diff options
199 files changed, 54010 insertions, 60 deletions
@@ -137,6 +137,9 @@ ScummVM Team      Parallaction:         peres +    Pegasus: +       Matthew Hoops +      Queen:         David Eriksson        - (retired)         Gregory Montoir       - (retired) @@ -597,3 +600,6 @@ Special thanks to     Broken Sword 2.5 team for providing sources of their engine and their     great support. +   Bob Bell, Michel Kripalani, Tommy Yune, from Presto Studios for providing +   the source code of The Journeyman Project: Pegasus Prime. + diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp index f94171646a..0ca5bbb059 100644 --- a/backends/events/sdl/sdl-events.cpp +++ b/backends/events/sdl/sdl-events.cpp @@ -191,6 +191,8 @@ void SdlEventSource::SDLModToOSystemKeyFlags(SDLMod mod, Common::Event &event) {  #endif  	if (mod & KMOD_CTRL)  		event.kbd.flags |= Common::KBD_CTRL; +	if (mod & KMOD_META) +		event.kbd.flags |= Common::KBD_META;  	// Sticky flags  	if (mod & KMOD_NUM) diff --git a/common/keyboard.h b/common/keyboard.h index e6db086598..64c6cc4d01 100644 --- a/common/keyboard.h +++ b/common/keyboard.h @@ -224,12 +224,13 @@ enum {  	KBD_CTRL  = 1 << 0,  	KBD_ALT   = 1 << 1,  	KBD_SHIFT = 1 << 2, -	KBD_NON_STICKY = (KBD_CTRL|KBD_ALT|KBD_SHIFT), +	KBD_META  = 1 << 3, +	KBD_NON_STICKY = (KBD_CTRL|KBD_ALT|KBD_SHIFT|KBD_META),  	// Sticky modifier flags -	KBD_NUM   = 1 << 3, -	KBD_CAPS  = 1 << 4, -	KBD_SCRL  = 1 << 5, +	KBD_NUM   = 1 << 4, +	KBD_CAPS  = 1 << 5, +	KBD_SCRL  = 1 << 6,  	KBD_STICKY = (KBD_NUM|KBD_CAPS|KBD_SCRL)  }; diff --git a/common/macresman.cpp b/common/macresman.cpp index 14bdfa7080..f2f020c6de 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -124,7 +124,7 @@ bool MacResManager::open(String filename) {  	File *file = new File();  	// First, let's try to see if the Mac converted name exists -	if (file->open("._" + filename) && loadFromAppleDouble(*file)) { +	if (file->open(constructAppleDoubleName(filename)) && loadFromAppleDouble(*file)) {  		_baseFileName = filename;  		return true;  	} @@ -185,7 +185,7 @@ bool MacResManager::open(FSNode path, String filename) {  #endif  	// First, let's try to see if the Mac converted name exists -	FSNode fsNode = path.getChild("._" + filename); +	FSNode fsNode = path.getChild(constructAppleDoubleName(filename));  	if (fsNode.exists() && !fsNode.isDirectory()) {  		SeekableReadStream *stream = fsNode.createReadStream();  		if (loadFromAppleDouble(*stream)) { @@ -253,7 +253,7 @@ bool MacResManager::exists(const String &filename) {  		return true;  	// Check if we have an AppleDouble file -	if (tempFile.open("._" + filename) && tempFile.readUint32BE() == 0x00051607) +	if (tempFile.open(constructAppleDoubleName(filename)) && tempFile.readUint32BE() == 0x00051607)  		return true;  	return false; @@ -574,4 +574,20 @@ void MacResManager::readMap() {  	}  } +Common::String MacResManager::constructAppleDoubleName(Common::String name) { +	// Insert "._" before the last portion of a path name +	for (int i = name.size() - 1; i >= 0; i--) { +		if (i == 0) { +			name.insertChar('_', 0); +			name.insertChar('.', 0); +		} else if (name[i] == '/') { +			name.insertChar('_', i + 1); +			name.insertChar('.', i + 1); +			break; +		} +	} + +	return name; +} +  } // End of namespace Common diff --git a/common/macresman.h b/common/macresman.h index 6820106925..f334405664 100644 --- a/common/macresman.h +++ b/common/macresman.h @@ -175,6 +175,8 @@ private:  	bool loadFromMacBinary(SeekableReadStream &stream);  	bool loadFromAppleDouble(SeekableReadStream &stream); +	static Common::String constructAppleDoubleName(Common::String name); +  	enum {  		kResForkNone = 0,  		kResForkRaw, diff --git a/common/rational.h b/common/rational.h index 45aa6a7a20..8270d2194e 100644 --- a/common/rational.h +++ b/common/rational.h @@ -80,6 +80,9 @@ public:  	double toDouble() const;  	frac_t toFrac() const; +	int getNumerator() const { return _num; } +	int getDenominator() const { return _denom; } +  	void debugPrint(int debuglevel = 0, const char *caption = "Rational:") const;  private: diff --git a/common/rect.h b/common/rect.h index 2bd3affafe..8d1243f7e4 100644 --- a/common/rect.h +++ b/common/rect.h @@ -170,6 +170,20 @@ struct Rect {  	}  	/** +	 * Find the intersecting rectangle between this rectangle and the given rectangle +	 * +	 * @param r the intersecting rectangle +	 * +	 * @return the intersection of the rectangles or an empty rectangle if not intersecting +	 */ +	Rect findIntersectingRect(const Rect &r) const { +		if (!intersects(r)) +			return Rect(); + +		return Rect(MAX(r.left, left), MAX(r.top, top), MIN(r.right, right), MIN(r.bottom, bottom)); +	} + +	/**  	 * Extend this rectangle so that it contains r  	 *  	 * @param r the rectangle to extend by diff --git a/devtools/credits.pl b/devtools/credits.pl index b3a506125f..db6975641a 100755 --- a/devtools/credits.pl +++ b/devtools/credits.pl @@ -608,6 +608,10 @@ begin_credits("Credits");  				add_person("", "peres", "");  			end_section(); +			begin_section("Pegasus"); +				add_person("Matthew Hoops", "clone2727", ""); +			end_section(); +  			begin_section("Queen");  				add_person("David Eriksson", "twogood", "(retired)");  				add_person("Gregory Montoir", "cyx", "(retired)"); @@ -1127,6 +1131,10 @@ begin_credits("Credits");      "Broken Sword 2.5 team for providing sources of their engine and their great ".      "support."); +	add_paragraph( +    "Bob Bell, Michel Kripalani, Tommy Yune, from Presto Studios for ". +    "providing the source code of The Journeyman Project: Pegasus Prime."); +  	end_section();  end_credits(); diff --git a/engines/configure.engines b/engines/configure.engines index 8eaee730cc..91e5e82506 100644 --- a/engines/configure.engines +++ b/engines/configure.engines @@ -27,6 +27,7 @@ add_engine cstime "Where in Time is Carmen Sandiego?" no  add_engine riven "Riven: The Sequel to Myst" no  add_engine myst "Myst" no  add_engine parallaction "Parallaction" yes +add_engine pegasus "The Journeyman Project: Pegasus Prime" no  add_engine queen "Flight of the Amazon Queen" yes  add_engine saga "SAGA" yes "ihnm saga2"  add_engine ihnm "IHNM" yes diff --git a/engines/engines.mk b/engines/engines.mk index 9939506b86..c7071f93c1 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -130,6 +130,11 @@ DEFINES += -DENABLE_PARALLACTION=$(ENABLE_PARALLACTION)  MODULES += engines/parallaction  endif +ifdef ENABLE_PEGASUS +DEFINES += -DENABLE_PEGASUS=$(ENABLE_PEGASUS) +MODULES += engines/pegasus +endif +  ifdef ENABLE_QUEEN  DEFINES += -DENABLE_QUEEN=$(ENABLE_QUEEN)  MODULES += engines/queen diff --git a/engines/pegasus/ai/ai_action.cpp b/engines/pegasus/ai/ai_action.cpp new file mode 100644 index 0000000000..38d639038f --- /dev/null +++ b/engines/pegasus/ai/ai_action.cpp @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" + +namespace Pegasus { + +AICompoundAction::~AICompoundAction() { +	for (AIActionList::iterator it = _compoundActions.begin(); it != _compoundActions.end(); it++) +		delete *it; +} + +void AICompoundAction::performAIAction(AIRule *rule) { +	for (AIActionList::iterator it = _compoundActions.begin(); it != _compoundActions.end(); it++) +		(*it)->performAIAction(rule); +} + +AIPlayMessageAction::AIPlayMessageAction(const Common::String &movieName, bool keepLastFrame, const InputBits interruptionFilter) { +	_movieName = movieName; +	_keepLastFrame = keepLastFrame; +	_interruptionFilter = interruptionFilter; +} + +void AIPlayMessageAction::performAIAction(AIRule *) { +	if (g_AIArea) { +		g_AIArea->checkMiddleArea(); +		g_AIArea->playAIMovie(kRightAreaSignature, _movieName, _keepLastFrame, _interruptionFilter); +	} +} + +AIStartTimerAction::AIStartTimerAction(AITimerCondition *timerCondition) { +	_timerCondition = timerCondition; +} + +void AIStartTimerAction::performAIAction(AIRule *) { +	_timerCondition->startTimer(); +} + +AIActivateRuleAction::AIActivateRuleAction(AIRule *rule) { +	_rule = rule; +} + +void AIActivateRuleAction::performAIAction(AIRule *) { +	_rule->activateRule(); +} + +AIDeactivateRuleAction::AIDeactivateRuleAction(AIRule *rule) { +	_rule = rule; +} + +void AIDeactivateRuleAction::performAIAction(AIRule *) { +	_rule->deactivateRule(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_action.h b/engines/pegasus/ai/ai_action.h new file mode 100644 index 0000000000..6eac976f9c --- /dev/null +++ b/engines/pegasus/ai/ai_action.h @@ -0,0 +1,136 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_AI_AIACTION_H +#define PEGASUS_AI_AIACTION_H + +#include "common/list.h" + +#include "pegasus/input.h" +#include "pegasus/types.h" + +namespace Pegasus { + +class AIRule; +class AITimerCondition; + +///////////////////////////////////////////// +// +// AIAction + +class AIAction { +friend class AIRule; +public: +	AIAction() { _actionCount = 1; } +	virtual ~AIAction() {} + +	virtual void performAIAction(AIRule *) = 0; + +	void setActionCount(const uint32 count) { _actionCount = count; } + +protected: +	uint32 _actionCount; +}; + +typedef Common::List<AIAction *> AIActionList; + +///////////////////////////////////////////// +// +// AICompoundAction + +class AICompoundAction : public AIAction { +public: +	AICompoundAction() {} +	virtual ~AICompoundAction(); + +	void addAction(AIAction *action) { _compoundActions.push_back(action); } + +	virtual void performAIAction(AIRule *); + +protected: +	AIActionList _compoundActions; +}; + +///////////////////////////////////////////// +// +// AIPlayMessageAction + +class AIPlayMessageAction : public AIAction { +public: +	AIPlayMessageAction(const Common::String &movieName, bool keepLastFrame, const InputBits = kWarningInterruption); + +	virtual void performAIAction(AIRule *); + +protected: +	Common::String _movieName; +	InputBits _interruptionFilter; +	bool _keepLastFrame; +}; + +///////////////////////////////////////////// +// +// AIStartTimerAction + +class AIStartTimerAction : public AIAction { +public: +	AIStartTimerAction(AITimerCondition *); + +	virtual void performAIAction(AIRule *); + +protected: +	AITimerCondition *_timerCondition; +}; + +///////////////////////////////////////////// +// +// AIActivateRuleAction + +class AIActivateRuleAction : public AIAction { +public: +	AIActivateRuleAction(AIRule *); + +	virtual void performAIAction(AIRule *); + +protected: +	AIRule *_rule; +}; + +///////////////////////////////////////////// +// +// AIDeactivateRuleAction + +class AIDeactivateRuleAction : public AIAction { +public: +	AIDeactivateRuleAction(AIRule *rule); + +	virtual void performAIAction(AIRule *); + +protected: +	AIRule *_rule; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_area.cpp b/engines/pegasus/ai/ai_area.cpp new file mode 100644 index 0000000000..e4d31049f2 --- /dev/null +++ b/engines/pegasus/ai/ai_area.cpp @@ -0,0 +1,613 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/memstream.h" + +#include "pegasus/cursor.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +AIArea *g_AIArea = 0; + +AIArea::AIArea(InputHandler *nextHandler) : InputHandler(nextHandler), _leftAreaMovie(kAILeftAreaID), +		_middleAreaMovie(kAIMiddleAreaID), _rightAreaMovie(kAIRightAreaID), _AIMovie(kAIMovieID) { +	g_AIArea = this; +	_leftAreaOwner = kNoClientSignature; +	_middleAreaOwner = kNoClientSignature; +	_rightAreaOwner = kNoClientSignature; +	_leftInventoryTime = 0xffffffff; +	_middleInventoryTime = 0xffffffff; +	_middleBiochipTime = 0xffffffff; +	_rightBiochipTime = 0xffffffff; +	_lockCount = 0; +	startIdling(); +} + +AIArea::~AIArea() {	 +	if (_middleAreaOwner == kBiochipSignature) { +		BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); +		if (currentBiochip && currentBiochip->isSelected()) +			currentBiochip->giveUpSharedArea(); +	} else if (_middleAreaOwner == kInventorySignature) { +		InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); +		if (currentItem && currentItem->isSelected()) +			currentItem->giveUpSharedArea(); +	} + +	stopIdling(); + +	for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) +		delete *it; + +	g_AIArea = 0; +} + +// Save last state of AI rules... +void AIArea::saveAIState() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	delete vm->_aiSaveStream; + +	Common::MemoryWriteStreamDynamic out; +	writeAIRules(&out); + +	vm->_aiSaveStream = new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES); +} +	 +void AIArea::restoreAIState() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	if (vm->_aiSaveStream) +		readAIRules(vm->_aiSaveStream); +} + +void AIArea::writeAIRules(Common::WriteStream *stream) { +	_AIRules.writeAIRules(stream); +} + +void AIArea::readAIRules(Common::ReadStream *stream) { +	_AIRules.readAIRules(stream); +} + +void AIArea::initAIArea() { +	allocateSurface(Common::Rect(0, 0, 384, 96)); +	 +	_leftAreaMovie.shareSurface(this); +	_leftAreaMovie.initFromMovieFile("Images/Items/Left Area Movie"); +	_leftAreaMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop); +	_leftAreaMovie.setDisplayOrder(kAILeftAreaOrder); +	_leftAreaMovie.startDisplaying(); +	_leftAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); +	 +	_middleAreaMovie.shareSurface(this); +	_middleAreaMovie.initFromMovieFile("Images/Items/Middle Area Movie"); +	_middleAreaMovie.moveElementTo(kAIMiddleAreaLeft, kAIMiddleAreaTop); +	_middleAreaMovie.moveMovieBoxTo(kAIMiddleAreaLeft - kAILeftAreaLeft, 0); +	_middleAreaMovie.setDisplayOrder(kAIMiddleAreaOrder); +	_middleAreaMovie.startDisplaying(); +	_middleAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); +	 +	_rightAreaMovie.shareSurface(this); +	_rightAreaMovie.initFromMovieFile("Images/Items/Right Area Movie"); +	_rightAreaMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop); +	_rightAreaMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0); +	_rightAreaMovie.setDisplayOrder(kAIRightAreaOrder); +	_rightAreaMovie.startDisplaying(); +	_rightAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); +	 +	_AIMovie.setDisplayOrder(kAIMovieOrder); +} + +void AIArea::setAIVolume(const uint16 volume) { +	_leftAreaMovie.setVolume(volume); +	_middleAreaMovie.setVolume(volume); +	_rightAreaMovie.setVolume(volume); +} + +// There are only so many legal combinations of client/area. +// Here is the list of supported pairs: +//      kInventorySignature     kLeftAreaSignature +//      kInventorySignature     kMiddleAreaSignature +//      kBiochipSignature       kMiddleAreaSignature +//      kBiochipSignature       kRightAreaSignature +//      kAISignature            kLeftAreaSignature +// Further, the kAISignature never sets a static frame time in the left area, +// but only plays a sequence. + +// If this function is called while a sequence is playing, it will just "remember" +// the time value, so that when the sequence finishes, the new time is asserted. + +void AIArea::setAIAreaToTime(const LowerClientSignature client, const LowerAreaSignature area, const TimeValue time) { +	switch (area) { +	case kLeftAreaSignature: +		// Only support kInventorySignature client, since AI never calls SetAIAreaToTime. +		_leftAreaMovie.setSegment(0, _leftAreaMovie.getDuration()); + +		if (time == 0xffffffff) { +			_leftAreaMovie.hide(); +			_leftAreaOwner = kNoClientSignature; +		} else { +			setLeftMovieTime(time); +		} +		break; +	case kMiddleAreaSignature: +		// Only support kInventorySignature and kBiochipSignature clients. +		_middleAreaMovie.stop(); +		_middleAreaMovie.setFlags(0); +		_middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration()); + +		if (time == 0xffffffff) { +			if (client == kInventorySignature) { +				if (_middleBiochipTime != 0xffffffff) { +					setMiddleMovieTime(kBiochipSignature, _middleBiochipTime); +				} else { +					_middleAreaMovie.hide(); +					_middleAreaOwner = kNoClientSignature; +				} +			} else {	// client == kBiochipSignature +				if (_middleInventoryTime != 0xffffffff) { +					setMiddleMovieTime(kInventorySignature, _middleInventoryTime); +				} else { +					_middleAreaMovie.hide(); +					_middleAreaOwner = kNoClientSignature; +				} +			} +		} else { +			setMiddleMovieTime(client, time); +		} +		break; +	case kRightAreaSignature: +		// Only support kBiochipSignature client. +		_rightAreaMovie.setSegment(0, _rightAreaMovie.getDuration()); + +		if (time == 0xffffffff) { +			_rightAreaMovie.hide(); +			_rightAreaOwner = kNoClientSignature; +		} else { +			setRightMovieTime(time); +		} +		break; +	} +} + +// Plays a sequence on an area. When the sequence ends, the previous image +// is restored. +// Also, is input disabled or not? +// Easy answer: yes. + +// There are only so many legal combinations of client/area. +// Here is the list of supported pairs: +//     kBiochipSignature        kMiddleAreaSignature +// 	   kBiochipSignature        kRightAreaSignature +// 	   kInventorySignature      kMiddleAreaSignature + +void AIArea::playAIAreaSequence(const LowerClientSignature, const LowerAreaSignature area, const TimeValue start, const TimeValue stop) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	lockAIOut(); + +	switch (area) { +	case kLeftAreaSignature: +		break; +	case kMiddleAreaSignature: +		if (_middleAreaOwner == kInventorySignature) +			_middleInventoryTime = _middleAreaMovie.getTime(); +		else if (_middleAreaOwner == kBiochipSignature) +			_middleBiochipTime = _middleAreaMovie.getTime(); + +		_middleAreaMovie.stop(); +		_middleAreaMovie.setFlags(0); +		_middleAreaMovie.setSegment(start, stop); +		_middleAreaMovie.setTime(start); +		_middleAreaMovie.show(); +		_middleAreaMovie.start(); +		vm->_cursor->hide(); + +		while (_middleAreaMovie.isRunning()) { +			vm->checkCallBacks(); +			vm->refreshDisplay(); +			g_system->delayMillis(10); +		} + +		_middleAreaMovie.stop(); +		vm->_cursor->hideUntilMoved(); + +		if (_middleAreaOwner == kInventorySignature) +			setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleInventoryTime); +		else if (_middleAreaOwner == kBiochipSignature) +			setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleBiochipTime); +		else +			setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, 0xffffffff); +		break; +	case kRightAreaSignature: +		_rightBiochipTime = _rightAreaMovie.getTime(); +		_rightAreaMovie.setSegment(start, stop); +		_rightAreaMovie.setTime(start); +		_rightAreaMovie.show(); +		_rightAreaMovie.start(); +		vm->_cursor->hide(); + +		while (_rightAreaMovie.isRunning()) { +			vm->checkCallBacks(); +			vm->refreshDisplay(); +			g_system->delayMillis(10); +		} + +		_rightAreaMovie.stop(); +		vm->_cursor->hideUntilMoved(); +		setAIAreaToTime(_rightAreaOwner, kRightAreaSignature, _rightBiochipTime); +		break; +	} + +	unlockAI(); +} + +bool AIArea::playAIMovie(const LowerAreaSignature area, const Common::String &movieName, bool keepLastFrame, const InputBits interruptFilter) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; +	 +	lockAIOut(); + +	InputDevice.waitInput(interruptFilter); +	if (_AIMovie.isMovieValid()) +		_AIMovie.releaseMovie(); + +	_AIMovie.shareSurface(this); +	_AIMovie.initFromMovieFile(movieName); + +	if (area == kLeftAreaSignature) { +		_AIMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop); +		_leftAreaMovie.hide(); +	} else { +		_AIMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop); +		_AIMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0); +		_rightAreaMovie.hide(); +	} + +	_AIMovie.setTime(0); +	_AIMovie.startDisplaying(); +	_AIMovie.show(); +	_AIMovie.redrawMovieWorld(); +	_AIMovie.setVolume(vm->getSoundFXLevel()); +	_AIMovie.start(); +	vm->_cursor->hide(); + +	bool result = true; +	bool saveAllowed = vm->swapSaveAllowed(false); +	bool openAllowed = vm->swapLoadAllowed(false); + +	while (_AIMovie.isRunning()) { +		Input input; +		InputDevice.getInput(input, interruptFilter); + +		if (input.anyInput() || vm->shouldQuit() || vm->saveRequested() || vm->loadRequested()) { +			result = false; +			break; +		} + +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	_AIMovie.stop(); + +	vm->swapSaveAllowed(saveAllowed); +	vm->swapLoadAllowed(openAllowed); + +	// This used to keep the last frame up even if the movie was interrupted. +	// However, this only occurs in the recalibration, where interruption means skip the +	// whole thing, so skipping causes the AI to go away even when keepLastFrame is true. + +	if (!(result && keepLastFrame)) { +		_AIMovie.stopDisplaying(); +		_AIMovie.releaseMovie(); + +		if (area == kLeftAreaSignature) { +			_leftAreaMovie.setTime(_leftInventoryTime); +			_leftAreaMovie.show(); +			_leftAreaMovie.redrawMovieWorld(); +		} else { +			_rightAreaMovie.setTime(_rightBiochipTime); +			_rightAreaMovie.show(); +			_rightAreaMovie.redrawMovieWorld(); +		} +	} + +	vm->_cursor->hideUntilMoved(); +	unlockAI(); +	return result; +} + +// Only implemented for kMiddleAreaSignature, kInventorySignature +void AIArea::loopAIAreaSequence(const LowerClientSignature owner, const LowerAreaSignature area, const TimeValue start, const TimeValue stop) { +	if (area == kMiddleAreaSignature && owner == kInventorySignature && owner == _middleAreaOwner) { +		_middleAreaMovie.stop(); +		_middleAreaMovie.setFlags(0); +		_middleAreaMovie.setSegment(start, stop); +		_middleAreaMovie.setFlags(kLoopTimeBase); +		_middleAreaMovie.setTime(start); +		_middleAreaMovie.show(); +		_middleAreaMovie.start(); +	} +} + +// Only called by kInventorySignature. +void AIArea::setLeftMovieTime(const TimeValue time) { +	if (!_AIMovie.isSurfaceValid()) { +		_leftAreaMovie.setTime(time); +		_leftAreaMovie.show(); +		_leftAreaMovie.redrawMovieWorld(); +	} + +	_leftAreaOwner = kInventorySignature; +	_leftInventoryTime = time; +} + +void AIArea::setMiddleMovieTime(const LowerClientSignature client, const TimeValue time) {	 +	if (client == kInventorySignature) { +		_middleInventoryTime = time; +		if (_middleAreaOwner == kBiochipSignature) { +			BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); +			if (currentBiochip && currentBiochip->isSelected()) +				currentBiochip->giveUpSharedArea(); +		} +	} else { +		_middleBiochipTime = time; +		if (_middleAreaOwner == kInventorySignature) { +			InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); +			if (currentItem && currentItem->isSelected()) +				currentItem->giveUpSharedArea(); +		} +	} + +	_middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration()); +	_middleAreaMovie.stop(); +	_middleAreaMovie.setFlags(0); +	_middleAreaMovie.setTime(time); +	_middleAreaMovie.show(); +	_middleAreaMovie.redrawMovieWorld(); +	_middleAreaOwner = client; +} + +// Only called by kBiochipSignature. +void AIArea::setRightMovieTime(const TimeValue time) { +	if (!_AIMovie.isSurfaceValid()) { +		// Can't do it when the AI movie is up... +		_rightAreaMovie.setTime(time); +		_rightAreaMovie.show(); +		_rightAreaMovie.redrawMovieWorld(); +	} + +	_rightAreaOwner = kBiochipSignature; +	_rightBiochipTime = time; +} + +void AIArea::handleInput(const Input &input, const Hotspot *cursorSpot) { +	if (JMPPPInput::isToggleAIMiddleInput(input)) +		toggleMiddleAreaOwner(); +	else +		InputHandler::handleInput(input, cursorSpot); +} + +void AIArea::toggleMiddleAreaOwner() {	 +	if (_middleAreaOwner == kInventorySignature) { +		BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); +		if (currentBiochip) { +			setMiddleMovieTime(kBiochipSignature, currentBiochip->getSharedAreaTime()); +			currentBiochip->takeSharedArea(); +		} +	} else if (_middleAreaOwner == kBiochipSignature) { +		InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); +		if (currentItem) { +			setMiddleMovieTime(kInventorySignature, currentItem->getSharedAreaTime()); +			currentItem->takeSharedArea(); +		} +	} +} + +void AIArea::activateHotspots() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; +	 +	if (_middleAreaOwner == kBiochipSignature) { +		BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); +		if (currentBiochip) +			switch (currentBiochip->getObjectID()) { +			case kAIBiochip: +				((AIChip *)currentBiochip)->activateAIHotspots(); +				break; +			case kPegasusBiochip: +				if (!vm->isDemo()) +					((PegasusChip *)currentBiochip)->activatePegasusHotspots(); +				break; +			case kOpticalBiochip: +				((OpticalChip *)currentBiochip)->activateOpticalHotspots(); +				break; +			} +	} else if (_middleAreaOwner == kInventorySignature) { +		InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); +		if (currentItem && currentItem->getObjectID() == kAirMask) +			((AirMask *)currentItem)->activateAirMaskHotspots(); +	} + +	InputHandler::activateHotspots(); +} + +void AIArea::clickInHotspot(const Input &input, const Hotspot *hotspot) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	bool handled = false; + +	if (_middleAreaOwner == kBiochipSignature) { +		BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + +		if (currentBiochip) { +			switch (currentBiochip->getObjectID()) { +			case kAIBiochip: +				if ((hotspot->getHotspotFlags() & kAIBiochipSpotFlag) != 0) { +					((AIChip *)currentBiochip)->clickInAIHotspot(hotspot->getObjectID()); +					handled = true; +				} +				break; +			case kPegasusBiochip: +				if (!vm->isDemo() && ((hotspot->getHotspotFlags() & kPegasusBiochipSpotFlag) != 0)) { +					((PegasusChip *)currentBiochip)->clickInPegasusHotspot(); +					handled = true; +				} +				break; +			case kOpticalBiochip: +				if ((hotspot->getHotspotFlags() & kOpticalBiochipSpotFlag) != 0) { +					((OpticalChip *)currentBiochip)->clickInOpticalHotspot(hotspot->getObjectID()); +					handled = true; +				} +				break; +			} +		} +	} else if (_middleAreaOwner == kInventorySignature) { +		InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + +		if (currentItem) { +			switch (currentItem->getObjectID()) { +			case kAirMask: +				if ((hotspot->getHotspotFlags() & kAirMaskSpotFlag) != 0) { +					((AirMask *)currentItem)->clickInAirMaskHotspot(); +					handled = true; +				} +				break; +			} +		} +	} + +	if (!handled) +		InputHandler::clickInHotspot(input, hotspot); +} + +void AIArea::lockAIOut() { +	if (_lockCount == 0) +		stopIdling(); + +	_lockCount++; +} + +void AIArea::unlockAI() { +	if (_lockCount > 0) { +		_lockCount--; +		if (_lockCount == 0) +			startIdling(); +	} +} + +void AIArea::forceAIUnlocked() { +	if (_lockCount > 0) { +		_lockCount = 1; +		unlockAI(); +	} +} + +void AIArea::checkRules() { +	if (_lockCount == 0 && ((PegasusEngine *)g_engine)->playerAlive()) +		for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) +			if ((*it)->fireRule()) +				break; +} + +void AIArea::useIdleTime() { +	checkRules(); +} + +void AIArea::addAIRule(AIRule *rule) { +	_AIRules.push_back(rule); +} + +void AIArea::removeAllRules() { +	for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) +		delete *it; + +	_AIRules.clear(); +} + +void AIArea::checkMiddleArea() { +	BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + +	if (currentBiochip) { +		if (_middleAreaOwner == kBiochipSignature) { +			switch (currentBiochip->getObjectID()) { +			case kAIBiochip: +				((AIChip *)currentBiochip)->setUpAIChip(); +				break; +			case kPegasusBiochip: +				((PegasusChip *)currentBiochip)->setUpPegasusChip(); +				break; +			} +		} else { +			switch (currentBiochip->getObjectID()) { +			case kAIBiochip: +				((AIChip *)currentBiochip)->setUpAIChipRude(); +				break; +			case kPegasusBiochip: +				((PegasusChip *)currentBiochip)->setUpPegasusChipRude(); +				break; +			} +		} +	} +} + +TimeValue AIArea::getBigInfoTime() { +	if (_middleAreaOwner == kInventorySignature) { +		InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); +		return currentItem->getInfoLeftTime(); +	} else if (_middleAreaOwner == kBiochipSignature) { +		BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); +		return currentBiochip->getInfoLeftTime(); +	} + +	return 0xffffffff; +} + +void AIArea::getSmallInfoSegment(TimeValue &start, TimeValue &stop) {	 +	if (_middleAreaOwner == kInventorySignature) { +		InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); +		currentItem->getInfoRightTimes(start, stop); +	} else if (_middleAreaOwner == kBiochipSignature) { +		BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); +		currentBiochip->getInfoRightTimes(start, stop); +	} else { +		start = 0xffffffff; +		stop = 0xffffffff; +	} +} + +LowerClientSignature AIArea::getMiddleAreaOwner() { +	return _middleAreaOwner; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_area.h b/engines/pegasus/ai/ai_area.h new file mode 100644 index 0000000000..10bfc933a1 --- /dev/null +++ b/engines/pegasus/ai/ai_area.h @@ -0,0 +1,172 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_AI_AIAREA_H +#define PEGASUS_AI_AIAREA_H + +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/timers.h" +#include "pegasus/ai/ai_rule.h" + +namespace Common { +	class ReadStream; +	class WriteStream; +} + +/* + +	The AI area is the area at the bottom of the screen. There are three areas within +	the AI area: +	1)	the inventory/AI help area +	2)	the middle area +	3)	the biochip display area +	 +	Area 1 is used for displaying the current inventory item. When the player changes the +	current item, either by selecting a new one in the inventory list or by picking +	up a new item, area 1 updates to show the new item. +	 +	If the AI decides to give a message, the AI's head temporarily takes over area 1 +	for the duration of the message, then goes away, returning the area to the current +	inventory item. +	 +	Area 2 is used to display the current inventory item's state, the current biochip's +	state, and any extra information from the AI. The contention for this area is +	resolved as follows: +	--	If the AI needs to use the area while giving a message in area 1, it takes over +		area 2 for the duration of its message. +*** This is not true. +	--	If the player selects a new inventory item, the inventory item's state gets +		displayed immediately. +	--	If the player selects a new biochip, the biochip's state info gets displayed +		immediately. +	--	If any auto drawing is to occur, it seizes the area as soon as the drawing is +		to occur. For example, the mapping biochip does auto drawing every time the +		player takes a step. The only exception to this rule is if the AI is presenting +		a warning. When the AI seizes areas 1 and 2, it preempts all other uses. +	Some inventory items and biochips can cause arbitrary drawing to occur in this area +	at arbitrary times. The map biochip is one example which causes drawing when the +	player takes a step. Another example is the poison gas canister, which flashes in +	this area to indicate a dangerous compound. +	 +	Area 3 is used to display the current biochip. When the player changes the current +	biochip, either by selecting a new one from the biochip list or by picking up a +	new one, area 3 updates to show the new item. In addition, some biochips can play +	animation in this area. + +*/ + +namespace Pegasus { + +class AIArea : public Surface, public Idler, public InputHandler { +public: +	AIArea(InputHandler *); +	virtual ~AIArea(); + +	void writeAIRules(Common::WriteStream *stream); +	void readAIRules(Common::ReadStream *stream); + +	void initAIArea(); + +	void saveAIState(); +	void restoreAIState(); +	 +	void handleInput(const Input &, const Hotspot *); +	void activateHotspots(); +	void clickInHotspot(const Input &, const Hotspot *); +	 +	void setAIVolume(const uint16); +	 +	// There are only so many legal combinations of client/area. +	// Here is the list of supported pairs: +	//      kInventorySignature     kLeftAreaSignature +	//      kInventorySignature     kMiddleAreaSignature +	//      kBiochipSignature       kMiddleAreaSignature +	//      kBiochipSignature       kRightAreaSignature +	//      kAISignature            kLeftAreaSignature +	// Further, the kAISignature never sets a static frame time in the left area, +	// but only plays a sequence from the AI movie. +	void setAIAreaToTime(const LowerClientSignature, const LowerAreaSignature, const TimeValue); +	 +	// The "Play" functions play the requested sequence synchronously. +	void playAIAreaSequence(const LowerClientSignature, const LowerAreaSignature, const TimeValue, const TimeValue); + +	// For PlayAIMovie, it is assumed that the client is the AI itself. +	// This is used to play AI messages as well as Optical Memory video. +	// Returns true if the movie played all the way through, false if it was interrupted. +	bool playAIMovie(const LowerAreaSignature, const Common::String &movieName, bool keepLastFrame, const InputBits); + +	// Loop the requested sequence indefinitely. +	void loopAIAreaSequence(const LowerClientSignature, const LowerAreaSignature, const TimeValue, const TimeValue); + +	void addAIRule(AIRule *); + +	// Remove and delete all rules. +	void removeAllRules(); + +	void lockAIOut(); +	void unlockAI(); +	void forceAIUnlocked(); + +	void checkMiddleArea(); +	void checkRules(); + +	LowerClientSignature getMiddleAreaOwner(); +	void toggleMiddleAreaOwner(); + +	TimeValue getBigInfoTime(); +	void getSmallInfoSegment(TimeValue &, TimeValue &); + +protected:	 +	void useIdleTime(); + +	void setLeftMovieTime(const TimeValue); +	void setMiddleMovieTime(const LowerClientSignature, const TimeValue); +	void setRightMovieTime(const TimeValue); + +	Movie _leftAreaMovie; +	Movie _middleAreaMovie; +	Movie _rightAreaMovie; +	Movie _AIMovie; + +	LowerClientSignature _leftAreaOwner; +	LowerClientSignature _middleAreaOwner; +	LowerClientSignature _rightAreaOwner; + +	TimeValue _leftInventoryTime; +	TimeValue _middleInventoryTime; +	TimeValue _middleBiochipTime; +	TimeValue _rightBiochipTime; + +	AIRuleList _AIRules; + +	uint _lockCount; +}; + +extern AIArea *g_AIArea; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_condition.cpp b/engines/pegasus/ai/ai_condition.cpp new file mode 100644 index 0000000000..df6d3227e5 --- /dev/null +++ b/engines/pegasus/ai/ai_condition.cpp @@ -0,0 +1,290 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/stream.h" + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +AIOneChildCondition::AIOneChildCondition(AICondition *child) { +	_child = child; +} + +AIOneChildCondition::~AIOneChildCondition() { +	delete _child; +} + +void AIOneChildCondition::writeAICondition(Common::WriteStream *stream) { +	if (_child) +		_child->writeAICondition(stream); +} + +void AIOneChildCondition::readAICondition(Common::ReadStream *stream) { +	if (_child) +		_child->readAICondition(stream); +} + +AITwoChildrenCondition::AITwoChildrenCondition(AICondition *leftChild, AICondition *rightChild) { +	_leftChild = leftChild; +	_rightChild = rightChild; +} + +AITwoChildrenCondition::~AITwoChildrenCondition() { +	delete _leftChild; +	delete _rightChild; +} + +void AITwoChildrenCondition::writeAICondition(Common::WriteStream *stream) { +	if (_leftChild) +		_leftChild->writeAICondition(stream); + +	if (_rightChild)  +		_rightChild->writeAICondition(stream); +} + +void AITwoChildrenCondition::readAICondition(Common::ReadStream *stream) { +	if (_leftChild) +		_leftChild->readAICondition(stream); + +	if (_rightChild) +		_rightChild->readAICondition(stream); +} + +AINotCondition::AINotCondition(AICondition* child) : AIOneChildCondition(child) { +} +	 +bool AINotCondition::fireCondition() { +	return _child && !_child->fireCondition(); +} + +AIAndCondition::AIAndCondition(AICondition *leftChild, AICondition *rightChild) : AITwoChildrenCondition(leftChild, rightChild) { +} +	 +bool AIAndCondition::fireCondition() { +	return _leftChild && _leftChild->fireCondition() && _rightChild && _rightChild->fireCondition(); +} + +AIOrCondition::AIOrCondition(AICondition *leftChild, AICondition *rightChild) : AITwoChildrenCondition(leftChild, rightChild) { +} + +bool AIOrCondition::fireCondition() { +	return (_leftChild && _leftChild->fireCondition()) || (_rightChild && _rightChild->fireCondition()); +} + +AITimerCondition::AITimerCondition(const TimeValue time, const TimeScale scale, const bool shouldStartTimer) { +	_timerFuse.primeFuse(time, scale); +	_timerFuse.setFunctionPtr((tFunctionPtr)&AITimerFunction, (void *)this); +	_fired = false; + +	if (shouldStartTimer) +		startTimer(); +} + +void AITimerCondition::startTimer() { +	_fired = false; +	_timerFuse.lightFuse(); +} + +void AITimerCondition::stopTimer() { +	_timerFuse.stopFuse(); +} + +void AITimerCondition::writeAICondition(Common::WriteStream *stream) { +	stream->writeByte(_timerFuse.isFuseLit()); +	stream->writeByte(_fired); +	stream->writeUint32BE(_timerFuse.getTimeRemaining()); +	stream->writeUint32BE(_timerFuse.getFuseScale()); +} + +void AITimerCondition::readAICondition(Common::ReadStream *stream) { +	bool running = stream->readByte(); +	_fired = stream->readByte(); +	TimeValue time = stream->readUint32BE(); +	TimeScale scale = stream->readUint32BE(); + +	_timerFuse.stopFuse(); +	_timerFuse.primeFuse(time, scale); + +	if (running) +		_timerFuse.lightFuse(); +} + +void AITimerCondition::AITimerFunction(FunctionPtr *, AITimerCondition *condition) { +	condition->_fired = true; +} + +bool AITimerCondition::fireCondition() { +	return _fired; +} + +AILocationCondition::AILocationCondition(uint32 maxLocations) { +	_numLocations = 0; +	_maxLocations = maxLocations; +	_locations = new RoomViewID[maxLocations]; +} + +AILocationCondition::~AILocationCondition() { +	delete[] _locations; +} + +void AILocationCondition::addLocation(const RoomViewID location) { +	if (_numLocations < _maxLocations) +		_locations[_numLocations++] = location; +} + +bool AILocationCondition::fireCondition() { +	RoomViewID test = GameState.getCurrentRoomAndView(), *p; +	uint32 i; + +	for (i = 0, p = _locations; i < _numLocations; i++, p++) { +		if (test == *p) { +			*p = MakeRoomView(kNoRoomID, kNoDirection); +			return true; +		} +	} + +	return false; +} + +void AILocationCondition::writeAICondition(Common::WriteStream *stream) { +	stream->writeUint32BE(_maxLocations); +	stream->writeUint32BE(_numLocations); + +	uint32 i; +	RoomViewID *p; +	for (i = 0, p = _locations; i < _numLocations; i++, p++) +		stream->writeUint32BE(*p); +} + +void AILocationCondition::readAICondition(Common::ReadStream *stream) { +	uint32 maxLocations = stream->readUint32BE(); + +	if (_maxLocations != maxLocations) { +		delete[] _locations; +		_locations = new RoomViewID[maxLocations]; +		_maxLocations = maxLocations; +	} + +	_numLocations = stream->readUint32BE(); + +	uint32 i; +	RoomViewID *p; +	for (i = 0, p = _locations; i < _numLocations; i++, p++) +		*p = stream->readUint32BE(); +} + +AIDoorOpenedCondition::AIDoorOpenedCondition(RoomViewID doorLocation) { +	_doorLocation = doorLocation; +} + +bool AIDoorOpenedCondition::fireCondition() { +	return GameState.getCurrentRoomAndView() == _doorLocation && GameState.isCurrentDoorOpen(); +} + +AIHasItemCondition::AIHasItemCondition(const ItemID item) { +	_item = item; +} + +bool AIHasItemCondition::fireCondition() { +	return _item == kNoItemID || GameState.isTakenItemID(_item); +} + +AIDoesntHaveItemCondition::AIDoesntHaveItemCondition(const ItemID item) { +	_item = item; +} + +bool AIDoesntHaveItemCondition::fireCondition() { +	return _item == kNoItemID || !GameState.isTakenItemID(_item); +} + +AICurrentItemCondition::AICurrentItemCondition(const ItemID item) { +	_item = item; +} +	 +bool AICurrentItemCondition::fireCondition() { +	InventoryItem *item = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + +	if (_item == kNoItemID) +		return item == 0; + +	return item != 0 && item->getObjectID() == _item; +} + +AICurrentBiochipCondition::AICurrentBiochipCondition(const ItemID biochip)  { +	_biochip = biochip; +} + +bool AICurrentBiochipCondition::fireCondition() { +	BiochipItem *biochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); +	 +	if (_biochip == kNoItemID) +		return biochip == 0; + +	return biochip != 0 && biochip->getObjectID() == _biochip; +} + +AIItemStateCondition::AIItemStateCondition(const ItemID item, const ItemState state) { +	_item = item; +	_state = state; +} + +bool AIItemStateCondition::fireCondition() { +	Item *item = g_allItems.findItemByID(_item); +	return item != 0 && item->getItemState() == _state; +} + +AIEnergyMonitorCondition::AIEnergyMonitorCondition(const int32 energyThreshold) { +	_energyThreshold = energyThreshold; +} + +bool AIEnergyMonitorCondition::fireCondition() { +	return g_energyMonitor != 0 && g_energyMonitor->getCurrentEnergy() < _energyThreshold; +} + +AILastExtraCondition::AILastExtraCondition(const ExtraID lastExtra) { +	_lastExtra = lastExtra; +} + +bool AILastExtraCondition::fireCondition() { +	return g_neighborhood && (ExtraID)g_neighborhood->getLastExtra() == _lastExtra; +} + +AICondition *makeLocationAndDoesntHaveItemCondition(const RoomID room, const DirectionConstant direction, const ItemID item) { +	AILocationCondition *location = new AILocationCondition(1); +	location->addLocation(MakeRoomView(room, direction)); +	 +	AIDoesntHaveItemCondition *doesntHaveItem = new AIDoesntHaveItemCondition(item); + +	return new AIAndCondition(location, doesntHaveItem); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_condition.h b/engines/pegasus/ai/ai_condition.h new file mode 100644 index 0000000000..2d93a52eab --- /dev/null +++ b/engines/pegasus/ai/ai_condition.h @@ -0,0 +1,287 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_AI_AICONDITION_H +#define PEGASUS_AI_AICONDITION_H + +#include "pegasus/timers.h" + +namespace Common { +	class ReadStream; +	class WriteStream; +} + +namespace Pegasus { + +///////////////////////////////////////////// +// +// AICondition + +class AICondition { +public: +	AICondition() {} +	virtual ~AICondition() {} + +	virtual bool fireCondition() = 0; + +	// Only need these for conditions that are dynamic, like timer conditions... +	// other conditions, like item related conditions, which don't change during +	// the run of an environment, are completely initted when the environment +	// is created. +	virtual void writeAICondition(Common::WriteStream *) {} +	virtual void readAICondition(Common::ReadStream *) {} +}; + +///////////////////////////////////////////// +// +// AIOneChildCondition + +class AIOneChildCondition : public AICondition { +public: +	AIOneChildCondition(AICondition *); +	virtual ~AIOneChildCondition(); +	 +	virtual void writeAICondition(Common::WriteStream *); +	virtual void readAICondition(Common::ReadStream *); + +protected: +	AICondition *_child; +}; + +///////////////////////////////////////////// +// +// AITwoChildrenCondition + +class AITwoChildrenCondition : public AICondition { +public: +	AITwoChildrenCondition(AICondition *, AICondition *); +	virtual ~AITwoChildrenCondition(); +	 +	virtual void writeAICondition(Common::WriteStream *); +	virtual void readAICondition(Common::ReadStream *); + +protected: +	AICondition *_leftChild, *_rightChild; +}; + +///////////////////////////////////////////// +// +// AINotCondition + +class AINotCondition : public AIOneChildCondition { +public: +	AINotCondition(AICondition *); + +	virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AIAndCondition + +class AIAndCondition : public AITwoChildrenCondition { +public: +	AIAndCondition(AICondition *, AICondition *); + +	virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AIOrCondition + +class AIOrCondition : public AITwoChildrenCondition { +public: +	AIOrCondition(AICondition *, AICondition *); + +	virtual bool fireCondition(); +}; + +///////////////////////////////////////////// +// +// AITimerCondition + +class AITimerCondition : public AICondition { +public: +	AITimerCondition(const TimeValue, const TimeScale, const bool); + +	void startTimer(); +	void stopTimer(); + +	virtual bool fireCondition(); +	 +	virtual void writeAICondition(Common::WriteStream *); +	virtual void readAICondition(Common::ReadStream *); + +protected: +	static void AITimerFunction(FunctionPtr *, AITimerCondition *); + +	FuseFunction _timerFuse; +	bool _fired; +}; + +///////////////////////////////////////////// +// +// AILocationCondition + +class AILocationCondition : public AICondition { +public: +	AILocationCondition(uint32); +	virtual ~AILocationCondition(); + +	void addLocation(RoomViewID); +	virtual bool fireCondition(); + +	virtual void writeAICondition(Common::WriteStream *); +	virtual void readAICondition(Common::ReadStream *); + +protected: +	uint32 _numLocations, _maxLocations; +	RoomViewID *_locations; +}; + +///////////////////////////////////////////// +// +// AIDoorOpenedCondition + +class AIDoorOpenedCondition : public AICondition { +public: +	AIDoorOpenedCondition(RoomViewID); +	virtual ~AIDoorOpenedCondition() {} + +	virtual bool fireCondition(); + +protected: +	RoomViewID _doorLocation; +}; + +///////////////////////////////////////////// +// +// AIHasItemCondition + +class AIHasItemCondition : public AICondition { +public: +	AIHasItemCondition(const ItemID); + +	virtual bool fireCondition(); + +protected: +	ItemID _item; +}; + +///////////////////////////////////////////// +// +// AIDoesntHaveItemCondition + +class AIDoesntHaveItemCondition : public AICondition { +public: +	AIDoesntHaveItemCondition(const ItemID); + +	virtual bool fireCondition(); + +protected: +	ItemID _item; +}; + +///////////////////////////////////////////// +// +// AICurrentItemCondition + +class AICurrentItemCondition : public AICondition { +public: +	AICurrentItemCondition(const ItemID); + +	virtual bool fireCondition(); + +protected: +	ItemID _item; +}; + +///////////////////////////////////////////// +// +// AICurrentBiochipCondition + +class AICurrentBiochipCondition : public AICondition { +public: +	AICurrentBiochipCondition(const ItemID); + +	virtual bool fireCondition(); + +protected: +	ItemID _biochip; +}; + +///////////////////////////////////////////// +// +// AIItemStateCondition + +class AIItemStateCondition : public AICondition { +public: +	AIItemStateCondition(const ItemID, const ItemState); + +	virtual bool fireCondition(); + +protected: +	ItemID _item; +	ItemState _state; +}; + +///////////////////////////////////////////// +// +// AIEnergyMonitorCondition + +class AIEnergyMonitorCondition : public AICondition { +public: +	AIEnergyMonitorCondition(const int32); + +	virtual bool fireCondition(); + +protected: +	int32 _energyThreshold; +}; + +///////////////////////////////////////////// +// +// AILastExtraCondition + +class AILastExtraCondition : public AICondition { +public: +	AILastExtraCondition(const ExtraID); + +	virtual bool fireCondition(); + +protected: +	ExtraID _lastExtra; +}; + +///////////////////////////////////////////// +// +// Helper functions + +AICondition *makeLocationAndDoesntHaveItemCondition(const RoomID room, const DirectionConstant direction, const ItemID item); + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/ai/ai_rule.cpp b/engines/pegasus/ai/ai_rule.cpp new file mode 100644 index 0000000000..3aaa530a4a --- /dev/null +++ b/engines/pegasus/ai/ai_rule.cpp @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/stream.h" + +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/ai/ai_rule.h" + +namespace Pegasus { + +bool AIRule::fireRule() { +	if (_ruleActive && _ruleCondition && _ruleAction && _ruleCondition->fireCondition()) { +		if (g_AIArea) +			g_AIArea->lockAIOut(); + +		_ruleAction->performAIAction(this); + +		if (--_ruleAction->_actionCount <= 0) +			deactivateRule(); + +		if (g_AIArea) +			g_AIArea->unlockAI(); + +		return true; +	} + +	return false; +} + +void AIRule::writeAIRule(Common::WriteStream *stream) { +	stream->writeByte(_ruleActive); + +	if (_ruleCondition) +		_ruleCondition->writeAICondition(stream); +} + +void AIRule::readAIRule(Common::ReadStream *stream) { +	_ruleActive = stream->readByte(); + +	if (_ruleCondition) +		_ruleCondition->readAICondition(stream); +} + +void AIRuleList::writeAIRules(Common::WriteStream *stream) { +	for (AIRuleList::iterator it = begin(); it != end(); it++) +		(*it)->writeAIRule(stream); +} + +void AIRuleList::readAIRules(Common::ReadStream *stream) { +	for (AIRuleList::iterator it = begin(); it != end(); it++) +		(*it)->readAIRule(stream); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/ai/ai_rule.h b/engines/pegasus/ai/ai_rule.h new file mode 100644 index 0000000000..bccd4ecb06 --- /dev/null +++ b/engines/pegasus/ai/ai_rule.h @@ -0,0 +1,86 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_AI_AIRULE_H +#define PEGASUS_AI_AIRULE_H + +#include "common/list.h" + +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_condition.h" + +namespace Common { +	class ReadStream; +	class WriteStream; +} + +namespace Pegasus { + +class AICondition; +class AIAction; + +class AIRule { +public: +	AIRule(AICondition *condition, AIAction *rule) { +		_ruleCondition = condition; +		_ruleAction = rule; +		_ruleActive = true; +	} +	 +	~AIRule() { +		if (_ruleCondition) +			delete _ruleCondition; + +		if (_ruleAction) +			delete _ruleAction; +	} + +	bool fireRule(); + +	void activateRule() { _ruleActive = true; } +	void deactivateRule() { _ruleActive = false; } +	bool isRuleActive() { return _ruleActive; } + +	void writeAIRule(Common::WriteStream *); +	void readAIRule(Common::ReadStream *); + +protected: +	AICondition *_ruleCondition; +	AIAction *_ruleAction; +	bool _ruleActive; +}; + +class AIRuleList : public Common::List<AIRule *> { +public: +	AIRuleList() {} +	~AIRuleList() {} + +	void writeAIRules(Common::WriteStream *); +	void readAIRules(Common::ReadStream *); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/compass.cpp b/engines/pegasus/compass.cpp new file mode 100644 index 0000000000..6ae4e11a0d --- /dev/null +++ b/engines/pegasus/compass.cpp @@ -0,0 +1,82 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/compass.h" + +namespace Pegasus { + +Compass *g_compass = 0; + +Compass::Compass() : FaderAnimation(kCompassID) { +	// Initialize it to east... +	setFaderValue(90); +	g_compass = this; +} + +Compass::~Compass() { +	g_compass = 0; +} + +void Compass::initCompass() { +	if (!isCompassValid()) { +		Common::Rect r; +		_compassImage.initFromPICTFile("Images/Compass/Compass"); +		_compassImage.getSurfaceBounds(r); +		r.right = kCompassWidth; +		setBounds(r); +	} +} + +void Compass::deallocateCompass() { +	_compassImage.deallocateSurface(); +} + +void Compass::setFaderValue(const int32 angle) { +	int16 a = angle % 360; + +	if (a < 0) +		a += 360; + +	FaderAnimation::setFaderValue(a); +} + +void Compass::draw(const Common::Rect &r1) {	 +	if (_compassImage.isSurfaceValid()) { +		Common::Rect bounds; +		getBounds(bounds); + +		Common::Rect r2; +		_compassImage.getSurfaceBounds(r2); + +		CoordType width = r2.width(); +		CoordType offsetH = width / 10 - bounds.width() / 2 + (getFaderValue() * width) / 450 - bounds.left; +		CoordType offsetV = -bounds.top; +		r2 = r1; +		r2.translate(offsetH, offsetV); +		_compassImage.drawImage(r2, r1); +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/compass.h b/engines/pegasus/compass.h new file mode 100644 index 0000000000..d66b432a38 --- /dev/null +++ b/engines/pegasus/compass.h @@ -0,0 +1,58 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_COMPASS_H +#define PEGASUS_COMPASS_H + +#include "pegasus/fader.h" +#include "pegasus/surface.h" + +namespace Pegasus { +  +// Compass is defined with 0 as north, 90 east, 180 south, 270 west. +// Clockwise rotation increases the angle, counterclockwise rotation decreases the angle. + +class Compass : public FaderAnimation { +public: +	Compass(); +	virtual ~Compass(); +	 +	void initCompass(); +	void deallocateCompass(); +	bool isCompassValid() const { return _compassImage.isSurfaceValid(); } +	 +	void setFaderValue(const int32); + +protected: +	void draw(const Common::Rect &); + +	Frame _compassImage; +}; + +extern Compass *g_compass; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/console.cpp b/engines/pegasus/console.cpp new file mode 100644 index 0000000000..64bd0ba5f2 --- /dev/null +++ b/engines/pegasus/console.cpp @@ -0,0 +1,102 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/console.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +PegasusConsole::PegasusConsole(PegasusEngine *vm) : GUI::Debugger(), _vm(vm) { +	DCmd_Register("die", WRAP_METHOD(PegasusConsole, Cmd_Die)); + +	// These functions are non-demo specific +	if (!_vm->isDemo()) +		DCmd_Register("jump", WRAP_METHOD(PegasusConsole, Cmd_Jump)); +} + +PegasusConsole::~PegasusConsole() { +} + +bool PegasusConsole::Cmd_Die(int argc, const char **argv) { +	if (argc == 1) { +		DebugPrintf("Usage: die <death reason>\n"); +		return true; +	} + +	int reason = atoi(argv[1]); + +	bool invalidReason = (reason == 0 || reason > kPlayerWonGame); + +	if (!invalidReason && _vm->isDemo()) +		invalidReason = (reason != kDeathFallOffCliff) && (reason != kDeathEatenByDinosaur) && +				(reason != kDeathStranded) && (reason != kPlayerWonGame); + + +	if (invalidReason) { +		DebugPrintf("Invalid death reason %d\n", reason); +		return true; +	} + +	_vm->die(atoi(argv[1])); +	return false; +} + +bool PegasusConsole::Cmd_Jump(int argc, const char **argv) { +	if (!g_interface) { +		// TODO +		DebugPrintf("Cannot jump without interface set up\n"); +		return true; +	} + +	// TODO: Default room/direction for each neighborhood + +	if (argc < 4) { +		DebugPrintf("Usage: jump <neighborhood> <room> <direction>\n"); +		return true; +	} + +	NeighborhoodID neighborhood = (NeighborhoodID)atoi(argv[1]); +	RoomID room = (RoomID)atoi(argv[2]); +	DirectionConstant direction = (DirectionConstant)atoi(argv[3]); + +	if ((neighborhood < kCaldoriaID || neighborhood > kNoradDeltaID || neighborhood == kFinalTSAID) && +			neighborhood != kNoradSubChaseID) { +		DebugPrintf("Invalid neighborhood %d", neighborhood); +		return true; +	} + +	// No real way to check room validity at this point + +	if (direction > kWest) { +		DebugPrintf("Invalid direction %d", direction); +		return true; +	} + +	// Here we go! +	// TODO: Can't clear menu since the engine is paused +	_vm->jumpToNewEnvironment(neighborhood, room, direction); +	return false; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/console.h b/engines/pegasus/console.h new file mode 100644 index 0000000000..f00ee25294 --- /dev/null +++ b/engines/pegasus/console.h @@ -0,0 +1,46 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_CONSOLE_H +#define PEGASUS_CONSOLE_H + +#include "gui/debugger.h" + +namespace Pegasus { + +class PegasusEngine; + +class PegasusConsole : public GUI::Debugger { +public: +	PegasusConsole(PegasusEngine *vm); +	virtual ~PegasusConsole(); + +private: +	bool Cmd_Die(int argc, const char **argv); +	bool Cmd_Jump(int argc, const char **argv); + +	PegasusEngine *_vm; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/constants.h b/engines/pegasus/constants.h new file mode 100644 index 0000000000..77a80256ac --- /dev/null +++ b/engines/pegasus/constants.h @@ -0,0 +1,729 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +  +#ifndef PEGASUS_CONSTANTS_H +#define PEGASUS_CONSTANTS_H + +#include "common/endian.h" +#include "common/rect.h" + +#include "pegasus/types.h" + +namespace Pegasus { + +// TODO: Organize these + +static const GameID kGameIDNothing = -1; + +static const ActorID kNoActorID = kGameIDNothing; +static const ActorID kPlayerID = 0; +static const ItemID kNoItemID = kGameIDNothing; +static const RoomID kNoRoomID = kGameIDNothing; +static const ExtraID kNoExtraID = 0xFFFFFFFF; +static const NeighborhoodID kNoNeighborhoodID = kGameIDNothing; +static const AlternateID kNoAlternateID = 0; +static const GameMenuCommand kMenuCmdNoCommand = 0; + +static const HotSpotActivationID kActivateHotSpotAlways = 0; +static const HotSpotActivationID kActivateHotSpotNever = -1; + +static const ItemState kNoItemState = -1; + +static const DirectionConstant kNoDirection = 0xFF; +static const DirectionConstant kNorth = 0; +static const DirectionConstant kSouth = 1; +static const DirectionConstant kEast = 2; +static const DirectionConstant kWest = 3; + +static const TurnDirection kNoTurn = 0xFF; +static const TurnDirection kTurnLeft = 0; +static const TurnDirection kTurnRight = 1; +static const TurnDirection kTurnUp = 2; +static const TurnDirection kTurnDown = 3; +static const TurnDirection kMaxTurns = 4; + +static const GameMode kNoMode = -1; +static const GameMode kModeNavigation = 0; +static const GameMode kLastGameShellMode = kModeNavigation; + +static const CanMoveForwardReason kCanMoveForward = 0; +static const CanMoveForwardReason kCantMoveBlocked = kCanMoveForward + 1; +static const CanMoveForwardReason kCantMoveDoorClosed = kCantMoveBlocked + 1; +static const CanMoveForwardReason kCantMoveDoorLocked = kCantMoveDoorClosed + 1; +static const CanMoveForwardReason kCantMoveLastReason = kCantMoveDoorLocked; + +static const CanTurnReason kCanTurn = 0; +static const CanTurnReason kCantTurnNoTurn = kCanTurn + 1; +static const CanTurnReason kCantTurnLastReason = kCantTurnNoTurn; + +static const CanOpenDoorReason kCanOpenDoor = 0; +static const CanOpenDoorReason kCantOpenNoDoor = kCanOpenDoor + 1; +static const CanOpenDoorReason kCantOpenLocked = kCantOpenNoDoor + 1; +static const CanOpenDoorReason kCantOpenAlreadyOpen = kCantOpenLocked + 1; +static const CanOpenDoorReason kCantOpenLastReason = kCantOpenAlreadyOpen; + +static const DisplayElementID kNoDisplayElement = -1; +static const DisplayElementID kHighestReservedElementID = -2; + +static const DisplayElementID kCursorID = kHighestReservedElementID; +static const DisplayElementID kLoadScreenID = kCursorID - 1; + +static const DisplayOrder kMinAvailableOrder = 0; +static const DisplayOrder kMaxAvailableOrder = 999998; +static const DisplayOrder kLoadScreenOrder = 900000; +static const DisplayOrder kCursorOrder = 1000000; + +static const HotSpotID kNoHotSpotID = -1; +static const HotSpotFlags kNoHotSpotFlags = 0; +static const HotSpotFlags kAllHotSpotFlags = ~kNoHotSpotFlags; + +static const NotificationFlags kNoNotificationFlags = 0; + +static const DisplayElementID kCurrentDragSpriteID = 1000; + +static const TimeScale kDefaultTimeScale = 600; + +// Ticks per second. + +static const TimeScale kOneTickPerSecond = 1; +static const TimeScale kTwoTicksPerSecond = 2; +static const TimeScale kFifteenTicksPerSecond = 15; +static const TimeScale kThirtyTicksPerSecond = 30; +static const TimeScale kSixtyTicksPerSecond = 60; +static const TimeScale kMovieTicksPerSecond = 600; + +// These times are in seconds. + +static const TimeValue kOneSecond = 1; +static const TimeValue kTwoSeconds = 2; +static const TimeValue kThreeSeconds = 3; +static const TimeValue kFourSeconds = 4; +static const TimeValue kFiveSeconds = 5; +static const TimeValue kSixSeconds = 6; +static const TimeValue kSevenSeconds = 7; +static const TimeValue kEightSeconds = 8; +static const TimeValue kNineSeconds = 9; +static const TimeValue kTenSeconds = 10; +static const TimeValue kElevenSeconds = 11; +static const TimeValue kTwelveSeconds = 12; +static const TimeValue kThirteenSeconds = 13; +static const TimeValue kFourteenSeconds = 14; +static const TimeValue kFifteenSeconds = 15; +static const TimeValue kSixteenSeconds = 16; +static const TimeValue kSeventeenSeconds = 17; +static const TimeValue kEighteenSeconds = 18; +static const TimeValue kNineteenSeconds = 19; +static const TimeValue kTwentySeconds = 20; +static const TimeValue kThirtySeconds = 30; +static const TimeValue kFortySeconds = 40; +static const TimeValue kFiftySeconds = 50; +static const TimeValue kSixtySeconds = 60; +static const TimeValue kOneMinute = 60; +static const TimeValue kTwoMinutes = kOneMinute * 2; +static const TimeValue kThreeMinutes = kOneMinute * 3; +static const TimeValue kFourMinutes = kOneMinute * 4; +static const TimeValue kFiveMinutes = kOneMinute * 5; +static const TimeValue kSixMinutes = kOneMinute * 6; +static const TimeValue kSevenMinutes = kOneMinute * 7; +static const TimeValue kEightMinutes = kOneMinute * 8; +static const TimeValue kNineMinutes = kOneMinute * 9; +static const TimeValue kTenMinutes = kOneMinute * 10; +static const TimeValue kElevenMinutes = kOneMinute * 11; +static const TimeValue kTwelveMinutes = kOneMinute * 12; +static const TimeValue kThirteenMinutes = kOneMinute * 13; +static const TimeValue kFourteenMinutes = kOneMinute * 14; +static const TimeValue kFifteenMinutes = kOneMinute * 15; +static const TimeValue kSixteenMinutes = kOneMinute * 16; +static const TimeValue kSeventeenMinutes = kOneMinute * 17; +static const TimeValue kEighteenMinutes = kOneMinute * 18; +static const TimeValue kNineteenMinutes = kOneMinute * 19; +static const TimeValue kTwentyMinutes = kOneMinute * 20; +static const TimeValue kThirtyMinutes = kOneMinute * 30; +static const TimeValue kFortyMinutes = kOneMinute * 40; +static const TimeValue kFiftyMinutes = kOneMinute * 50; +static const TimeValue kOneHour = kOneMinute * 60; +static const TimeValue kTwoHours = kOneHour * 2; + +// Common times. + +static const TimeValue kHalfSecondPerTwoTicks = kTwoTicksPerSecond / 2; +static const TimeValue kHalfSecondPerThirtyTicks = kThirtyTicksPerSecond / 2; +static const TimeValue kHalfSecondPerSixtyTicks = kSixtyTicksPerSecond / 2; + +static const TimeValue kOneSecondPerTwoTicks = kTwoTicksPerSecond; +static const TimeValue kOneSecondPerThirtyTicks = kThirtyTicksPerSecond; +static const TimeValue kOneSecondPerSixtyTicks = kSixtyTicksPerSecond; + +static const TimeValue kOneMinutePerFifteenTicks = kOneMinute * kFifteenTicksPerSecond; +static const TimeValue kFiveMinutesPerFifteenTicks = kFiveMinutes * kFifteenTicksPerSecond; +static const TimeValue kTenMinutesPerFifteenTicks = kTenMinutes * kFifteenTicksPerSecond; + +static const TimeValue kOneMinutePerThirtyTicks = kOneMinute * kThirtyTicksPerSecond; +static const TimeValue kFiveMinutesPerThirtyTicks = kFiveMinutes * kThirtyTicksPerSecond; +static const TimeValue kTenMinutesPerThirtyTicks = kTenMinutes * kThirtyTicksPerSecond; + +static const TimeValue kOneMinutePerSixtyTicks = kOneMinute * kSixtyTicksPerSecond; +static const TimeValue kFiveMinutesPerSixtyTicks = kFiveMinutes * kSixtyTicksPerSecond; +static const TimeValue kTenMinutesPerSixtyTicks = kTenMinutes * kSixtyTicksPerSecond; + +// Time in seconds you can hang around Caldoria without going to work... +static const TimeValue kLateWarning2TimeLimit = kFiveMinutes; +static const TimeValue kLateWarning3TimeLimit = kTenMinutes; + +static const TimeValue kSinclairShootsTimeLimit = kThreeMinutes; +static const TimeValue kCardBombCountDownTime = kTwelveSeconds; + +static const TimeValue kOxyMaskFullTime = kThirtyMinutes; + +static const TimeValue kTSAUncreatedTimeLimit = kFiveMinutes; +static const TimeValue kRipTimeLimit = kTenMinutesPerFifteenTicks; +static const TimeScale kRipTimeScale = kFifteenTicksPerSecond; + +static const TimeValue kIntroTimeOut = kThirtySeconds; + +static const TimeValue kMarsRobotPatienceLimit = kFifteenSeconds; +static const TimeValue kLockFreezeTimeLmit = kFifteenSeconds; +static const TimeValue kSpaceChaseTimeLimit = kTenMinutes; +static const TimeValue kVacuumSurvivalTimeLimit = kThirtySeconds; +static const TimeValue kColorMatchingTimeLimit = kFourMinutes; +static const TimeScale kJunkTimeScale = kFifteenTicksPerSecond; +static const TimeValue kJunkDropBaseTime = kFiveSeconds; +static const TimeValue kJunkDropSlopTime = kThreeSeconds; +static const TimeValue kJunkTravelTime = kTenSeconds * kJunkTimeScale; +static const TimeValue kCollisionReboundTime = kOneSecond * kJunkTimeScale; +static const TimeValue kWeaponReboundTime = kTwoSeconds * kJunkTimeScale; + +static const TimeValue kGawkAtRobotTime = kTenSeconds; +static const TimeValue kGawkAtRobotTime2 = kThirteenSeconds; +static const TimeValue kPlasmaImpactTime = kTwoSeconds; + +static const TimeValue kNoradAirMaskTimeLimit = kOneMinute + kFifteenSeconds; + +static const NotificationID kNeighborhoodNotificationID = 1; +static const NotificationID kLastNeighborhoodNotificationID = kNeighborhoodNotificationID; + +static const NotificationFlags kNeighborhoodMovieCompletedFlag = 1; +static const NotificationFlags kMoveForwardCompletedFlag = kNeighborhoodMovieCompletedFlag << 1; +static const NotificationFlags kStrideCompletedFlag = kMoveForwardCompletedFlag << 1; +static const NotificationFlags kTurnCompletedFlag = kStrideCompletedFlag << 1; +static const NotificationFlags kSpotCompletedFlag = kTurnCompletedFlag << 1; +static const NotificationFlags kDoorOpenCompletedFlag = kSpotCompletedFlag << 1; +static const NotificationFlags kExtraCompletedFlag = kDoorOpenCompletedFlag << 1; +static const NotificationFlags kSpotSoundCompletedFlag = kExtraCompletedFlag << 1; +static const NotificationFlags kDelayCompletedFlag = kSpotSoundCompletedFlag << 1; +static const NotificationFlags kActionRequestCompletedFlag = kDelayCompletedFlag << 1; +static const NotificationFlags kDeathExtraCompletedFlag = kActionRequestCompletedFlag << 1; +static const NotificationFlags kLastNeighborhoodNotificationFlag = kDeathExtraCompletedFlag; + +static const NotificationFlags kNeighborhoodFlags =	kNeighborhoodMovieCompletedFlag | +												kMoveForwardCompletedFlag | +												kStrideCompletedFlag | +												kTurnCompletedFlag | +												kSpotCompletedFlag | +												kDoorOpenCompletedFlag | +												kExtraCompletedFlag | +												kSpotSoundCompletedFlag | +												kDelayCompletedFlag | +												kActionRequestCompletedFlag | +												kDeathExtraCompletedFlag; + +static const uint32 kPegasusPrimeCreator = MKTAG('J', 'P', 'P', 'P'); +static const uint32 kPegasusPrimeContinueType = MKTAG('P', 'P', 'C', 'T'); + +static const uint32 kPegasusPrimeDisk1GameType = MKTAG('P', 'P', 'G', '1'); +static const uint32 kPegasusPrimeDisk2GameType = MKTAG('P', 'P', 'G', '2'); +static const uint32 kPegasusPrimeDisk3GameType = MKTAG('P', 'P', 'G', '3'); +static const uint32 kPegasusPrimeDisk4GameType = MKTAG('P', 'P', 'G', '4'); + +// We only support one of the save versions; the rest are from betas +// and we are not supporting them. +static const uint32 kPegasusPrimeVersion = 0x00009019; + +static const char kNormalSave = 0; +static const char kContinueSave = 1; + +// Display IDs. + +static const DisplayElementID kNavMovieID = 1; +static const DisplayElementID kTurnPushID = 2; + +static const DisplayElementID kMaxGameShellDisplayID = kTurnPushID; + +// Display ordering. + +static const DisplayOrder kNavLayer = 10000; +static const DisplayOrder kNavMovieOrder = kNavLayer; +static const DisplayOrder kTurnPushOrder = kNavMovieOrder + 1; + +///////////////////////////////////////////// +// +// Display IDs. + +static const DisplayElementID kScreenDimmerID = kMaxGameShellDisplayID + 1; +static const DisplayElementID kInterface1ID = kScreenDimmerID + 1; +static const DisplayElementID kInterface2ID = kInterface1ID + 1; +static const DisplayElementID kInterface3ID = kInterface2ID + 1; +static const DisplayElementID kInterface4ID = kInterface3ID + 1; +static const DisplayElementID kDateID = kInterface4ID + 1; +static const DisplayElementID kCompassID = kDateID + 1; +static const DisplayElementID kInventoryPushID = kCompassID + 1; +static const DisplayElementID kInventoryLidID = kInventoryPushID + 1; +static const DisplayElementID kBiochipPushID = kInventoryLidID + 1; +static const DisplayElementID kBiochipLidID = kBiochipPushID + 1; +static const DisplayElementID kEnergyBarID = kBiochipLidID + 1; +static const DisplayElementID kWarningLightID = kEnergyBarID + 1; +static const DisplayElementID kAILeftAreaID = kWarningLightID + 1; +static const DisplayElementID kAIMiddleAreaID = kAILeftAreaID + 1; +static const DisplayElementID kAIRightAreaID = kAIMiddleAreaID + 1; +static const DisplayElementID kAIMovieID = kAIRightAreaID + 1; +static const DisplayElementID kInventoryDropHighlightID = kAIMovieID + 1; +static const DisplayElementID kBiochipDropHighlightID = kInventoryDropHighlightID + 1; + +static const DisplayElementID kDraggingSpriteID = 1000; + +static const DisplayElementID kCroppedMovieID = 2000; + +static const DisplayElementID kNeighborhoodDisplayID = 3000; + +static const DisplayElementID kItemPictureBaseID = 5000; + +static const CoordType kNavAreaLeft = 64; +static const CoordType kNavAreaTop = 64; + +static const CoordType kBackground1Left = 0; +static const CoordType kBackground1Top = 64; + +static const CoordType kBackground2Left = 0; +static const CoordType kBackground2Top = 0; + +static const CoordType kBackground3Left = 576; +static const CoordType kBackground3Top = 64; + +static const CoordType kBackground4Left = 0; +static const CoordType kBackground4Top = 320; + +static const CoordType kOverviewControllerLeft = 540; +static const CoordType kOverviewControllerTop = 348; + +static const CoordType kSwapLeft = 194; +static const CoordType kSwapTop = 116; + +static const CoordType kSwapHiliteLeft = 200; +static const CoordType kSwapHiliteTop = 206; + +static const CoordType kDateLeft = 136; +static const CoordType kDateTop = 44; + +static const CoordType kCompassLeft = 222; +static const CoordType kCompassTop = 42; +static const CoordType kCompassWidth = 92; + +static const CoordType kInventoryPushLeft = 74; +static const CoordType kInventoryPushTop = 92; + +static const CoordType kInventoryLidLeft = 74; +static const CoordType kInventoryLidTop = 316; + +static const CoordType kBiochipPushLeft = 362; +static const CoordType kBiochipPushTop = 192; + +static const CoordType kBiochipLidLeft = 362; +static const CoordType kBiochipLidTop = 316; + +static const CoordType kInventoryDropLeft = 0; +static const CoordType kInventoryDropTop = 320; +static const CoordType kInventoryDropRight = 232; +static const CoordType kInventoryDropBottom = 480; + +static const CoordType kBiochipDropLeft = 302; +static const CoordType kBiochipDropTop = 320; +static const CoordType kBiochipDropRight = 640; +static const CoordType kBiochipDropBottom = 480; + +static const CoordType kFinalMessageLeft = kInventoryPushLeft + 1; +static const CoordType kFinalMessageTop = kInventoryPushTop + 24; + +///////////////////////////////////////////// +// +// Notifications. + +static const NotificationID kJMPDCShellNotificationID = kLastNeighborhoodNotificationID + 1; +static const NotificationID kInterfaceNotificationID = kJMPDCShellNotificationID + 1; +static const NotificationID kAINotificationID = kInterfaceNotificationID + 1; +static const NotificationID kNoradNotificationID = kAINotificationID + 1; +static const NotificationID kNoradECRNotificationID = kNoradNotificationID + 1; +static const NotificationID kNoradFillingStationNotificationID = kNoradECRNotificationID + 1; +static const NotificationID kNoradPressureNotificationID = kNoradFillingStationNotificationID + 1; +static const NotificationID kNoradUtilityNotificationID = kNoradPressureNotificationID + 1; +static const NotificationID kNoradElevatorNotificationID = kNoradUtilityNotificationID + 1; +static const NotificationID kNoradSubPlatformNotificationID = kNoradElevatorNotificationID + 1; +static const NotificationID kSubControlNotificationID = kNoradSubPlatformNotificationID + 1; +static const NotificationID kNoradGreenBallNotificationID = kSubControlNotificationID + 1; +static const NotificationID kNoradGlobeNotificationID = kNoradGreenBallNotificationID + 1; +static const NotificationID kCaldoriaVidPhoneNotificationID = kNoradGlobeNotificationID + 1; +static const NotificationID kCaldoriaMessagesNotificationID = kCaldoriaVidPhoneNotificationID + 1; +static const NotificationID kCaldoriaBombTimerNotificationID = kCaldoriaMessagesNotificationID + 1; + +// Sent to the shell by fShellNotification. +static const NotificationFlags kGameStartingFlag = 1; +static const NotificationFlags kNeedNewJumpFlag = kGameStartingFlag << 1; +static const NotificationFlags kPlayerDiedFlag = kNeedNewJumpFlag << 1; + +static const NotificationFlags kJMPShellNotificationFlags = kGameStartingFlag | +														kNeedNewJumpFlag | +														kPlayerDiedFlag; + +// Sent to the interface. +static const NotificationFlags kInventoryLidOpenFlag = 1; +static const NotificationFlags kInventoryLidClosedFlag = kInventoryLidOpenFlag << 1; +static const NotificationFlags kInventoryDrawerUpFlag = kInventoryLidClosedFlag << 1; +static const NotificationFlags kInventoryDrawerDownFlag = kInventoryDrawerUpFlag << 1; +static const NotificationFlags kBiochipLidOpenFlag = kInventoryDrawerDownFlag << 1; +static const NotificationFlags kBiochipLidClosedFlag = kBiochipLidOpenFlag << 1; +static const NotificationFlags kBiochipDrawerUpFlag = kBiochipLidClosedFlag << 1; +static const NotificationFlags kBiochipDrawerDownFlag = kBiochipDrawerUpFlag << 1; + +static const NotificationFlags kInterfaceNotificationFlags =	kInventoryLidOpenFlag | +														kInventoryLidClosedFlag | +														kInventoryDrawerUpFlag | +														kInventoryDrawerDownFlag | +														kBiochipLidOpenFlag | +														kBiochipLidClosedFlag | +														kBiochipDrawerUpFlag | +														kBiochipDrawerDownFlag; + +// Hot spots. + +// Neighborhood hot spots. + +static const HotSpotID kFirstNeighborhoodSpotID = 5000; + +// kShellSpotFlag is a flag which marks all hot spots which belong to the shell, like +// the current item and current biochip spots. +static const HotSpotFlags kShellSpotFlag = 1; +// kNeighborhoodSpotFlag is a flag which marks all hot spots which belong to a +// neighborhood, like buttons on walls and so on. +static const HotSpotFlags kNeighborhoodSpotFlag = kShellSpotFlag << 1; +// kZoomInSpotFlag is a flag which marks all hot spots which indicate a zoom. +static const HotSpotFlags kZoomInSpotFlag = kNeighborhoodSpotFlag << 1; +// kZoomOutSpotFlag is a flag which marks all hot spots which indicate a zoom. +static const HotSpotFlags kZoomOutSpotFlag = kZoomInSpotFlag << 1; + +static const HotSpotFlags kClickSpotFlag = kZoomOutSpotFlag << 1; +static const HotSpotFlags kPlayExtraSpotFlag = kClickSpotFlag << 1; +static const HotSpotFlags kPickUpItemSpotFlag = kPlayExtraSpotFlag << 1; +static const HotSpotFlags kDropItemSpotFlag = kPickUpItemSpotFlag << 1; +static const HotSpotFlags kOpenDoorSpotFlag = kDropItemSpotFlag << 1; + +static const HotSpotFlags kZoomSpotFlags = kZoomInSpotFlag | kZoomOutSpotFlag; + +static const HotSpotFlags kHighestGameShellSpotFlag = kOpenDoorSpotFlag; + +///////////////////////////////////////////// +// +// Hot spots. + +// Shell hot spots. +// The shell reserves all hot spot IDs from 0 to 999 + +static const HotSpotID kCurrentItemSpotID = 0; +static const HotSpotID kCurrentBiochipSpotID = kCurrentItemSpotID + 1; + +static const HotSpotID kInventoryDropSpotID = kCurrentBiochipSpotID + 1; +static const HotSpotID kBiochipDropSpotID = kInventoryDropSpotID + 1; + +static const HotSpotID kInfoReturnSpotID = kBiochipDropSpotID + 1; + +static const HotSpotID kAIHint1SpotID = kInfoReturnSpotID + 1; +static const HotSpotID kAIHint2SpotID = kAIHint1SpotID + 1; +static const HotSpotID kAIHint3SpotID = kAIHint2SpotID + 1; +static const HotSpotID kAISolveSpotID = kAIHint3SpotID + 1; +static const HotSpotID kAIBriefingSpotID = kAISolveSpotID + 1; +static const HotSpotID kAIScanSpotID = kAIBriefingSpotID + 1; + +static const HotSpotID kPegasusRecallSpotID = kAIScanSpotID + 1; + +static const HotSpotID kAriesSpotID = kPegasusRecallSpotID + 1; +static const HotSpotID kMercurySpotID = kAriesSpotID + 1; +static const HotSpotID kPoseidonSpotID = kMercurySpotID + 1; + +static const HotSpotID kAirMaskToggleSpotID = kPoseidonSpotID + 1; + +static const HotSpotID kShuttleEnergySpotID = kAirMaskToggleSpotID + 1; +static const HotSpotID kShuttleGravitonSpotID = kShuttleEnergySpotID + 1; +static const HotSpotID kShuttleTractorSpotID = kShuttleGravitonSpotID + 1; +static const HotSpotID kShuttleViewSpotID = kShuttleTractorSpotID + 1; +static const HotSpotID kShuttleTransportSpotID = kShuttleViewSpotID + 1; + +// Most of these are obsolete: + +// kInventoryDropSpotFlag is a flag which marks hot spots which are valid drop spots +// for inventory items. +// static const HotSpotFlags kInventoryDropSpotFlag = kHighestGameShellSpotFlag << 1; + +// kBiochipDropSpotFlag is a flag which marks hot spots which are valid drop spots +// for biochips. +// static const HotSpotFlags kBiochipDropSpotFlag = kInventoryDropSpotFlag << 1; + +// kInventorySpotFlag is a flag which marks hot spots which indicate inventory items +// in the environment. +// static const HotSpotFlags kInventorySpotFlag = kBiochipDropSpotFlag << 1; + +// kBiochipSpotFlag is a flag which marks hot spots which indicate biochips +// in the environment. +static const HotSpotFlags kPickUpBiochipSpotFlag = kHighestGameShellSpotFlag << 1; +static const HotSpotFlags kDropBiochipSpotFlag = kPickUpBiochipSpotFlag << 1; + +static const HotSpotFlags kInfoReturnSpotFlag = kDropBiochipSpotFlag << 1; + +// Biochip and inventory hot spot flags... + +static const HotSpotFlags kAIBiochipSpotFlag = kInfoReturnSpotFlag << 1; +static const HotSpotFlags kPegasusBiochipSpotFlag = kAIBiochipSpotFlag << 1; +static const HotSpotFlags kOpticalBiochipSpotFlag = kPegasusBiochipSpotFlag << 1; +static const HotSpotFlags kAirMaskSpotFlag = kOpticalBiochipSpotFlag << 1; + +static const HotSpotFlags kJMPClickingSpotFlags = kClickSpotFlag | +											kPlayExtraSpotFlag | +											kOpenDoorSpotFlag | +											kInfoReturnSpotFlag | +											kAIBiochipSpotFlag | +											kPegasusBiochipSpotFlag | +											kOpticalBiochipSpotFlag | +											kAirMaskSpotFlag; + +static const int32 kMainMenuID = 1; +static const int32 kPauseMenuID = 2; +static const int32 kCreditsMenuID = 3; +static const int32 kDeathMenuID = 4; + +///////////////////////////////////////////// +// +// Menu commands. + +static const GameMenuCommand kMenuCmdOverview = kMenuCmdNoCommand + 1; +static const GameMenuCommand kMenuCmdStartAdventure = kMenuCmdOverview + 1; +static const GameMenuCommand kMenuCmdStartWalkthrough = kMenuCmdStartAdventure + 1; +static const GameMenuCommand kMenuCmdRestore = kMenuCmdStartWalkthrough + 1; +static const GameMenuCommand kMenuCmdCredits = kMenuCmdRestore + 1; +static const GameMenuCommand kMenuCmdQuit = kMenuCmdCredits + 1; + +static const GameMenuCommand kMenuCmdDeathContinue = kMenuCmdQuit + 1; + +static const GameMenuCommand kMenuCmdDeathQuitDemo = kMenuCmdDeathContinue + 1; +static const GameMenuCommand kMenuCmdDeathMainMenuDemo = kMenuCmdDeathQuitDemo + 1; + +static const GameMenuCommand kMenuCmdDeathRestore = kMenuCmdDeathMainMenuDemo + 1; +static const GameMenuCommand kMenuCmdDeathMainMenu = kMenuCmdDeathRestore + 1; + +static const GameMenuCommand kMenuCmdPauseSave = kMenuCmdDeathMainMenu + 1; +static const GameMenuCommand kMenuCmdPauseContinue = kMenuCmdPauseSave + 1; +static const GameMenuCommand kMenuCmdPauseRestore = kMenuCmdPauseContinue + 1; +static const GameMenuCommand kMenuCmdPauseQuit = kMenuCmdPauseRestore + 1; + +static const GameMenuCommand kMenuCmdCreditsMainMenu = kMenuCmdPauseQuit + 1; + +static const GameMenuCommand kMenuCmdCancelRestart = kMenuCmdCreditsMainMenu + 1; +static const GameMenuCommand kMenuCmdEjectRestart = kMenuCmdCancelRestart + 1; + +static const TimeValue kMenuButtonHiliteTime = 20; +static const TimeScale kMenuButtonHiliteScale = kSixtyTicksPerSecond; + +// PICT resources: + +// Warning light PICTs: + +static const ResIDType kLightOffID = 128; +static const ResIDType kLightYellowID = 129; +static const ResIDType kLightOrangeID = 130; +static const ResIDType kLightRedID = 131; + +// Date PICTs: + +static const ResIDType kDatePrehistoricID = 138; +static const ResIDType kDate2112ID = 139; +static const ResIDType kDate2185ID = 140; +static const ResIDType kDate2310ID = 141; +static const ResIDType kDate2318ID = 142; + +///////////////////////////////////////////// +// +// Display Order + +static const DisplayOrder kCroppedMovieLayer = 11000; + +static const DisplayOrder kMonitorLayer = 12000; + +static const DisplayOrder kDragSpriteLayer = 15000; +static const DisplayOrder kDragSpriteOrder = kDragSpriteLayer; + +static const DisplayOrder kInterfaceLayer = 20000; +static const DisplayOrder kBackground1Order = kInterfaceLayer; +static const DisplayOrder kBackground2Order = kBackground1Order + 1; +static const DisplayOrder kBackground3Order = kBackground2Order + 1; +static const DisplayOrder kBackground4Order = kBackground3Order + 1; +static const DisplayOrder kDateOrder = kBackground4Order + 1; +static const DisplayOrder kCompassOrder = kDateOrder + 1; +static const DisplayOrder kEnergyBarOrder = kCompassOrder + 1; +static const DisplayOrder kEnergyLightOrder = kEnergyBarOrder + 1; + +static const DisplayOrder kAILayer = 22000; +static const DisplayOrder kAILeftAreaOrder = kAILayer; +static const DisplayOrder kAIMiddleAreaOrder = kAILeftAreaOrder + 1; +static const DisplayOrder kAIRightAreaOrder = kAIMiddleAreaOrder + 1; +static const DisplayOrder kAIMovieOrder = kAIRightAreaOrder + 1; + +static const DisplayOrder kHilitesLayer = 23000; +static const DisplayOrder kInventoryHiliteOrder = kHilitesLayer; +static const DisplayOrder kBiochipHiliteOrder = kInventoryHiliteOrder + 1; + +static const DisplayOrder kPanelsLayer = 25000; +static const DisplayOrder kInventoryPushOrder = kPanelsLayer; +static const DisplayOrder kInventoryLidOrder = kInventoryPushOrder + 1; +static const DisplayOrder kBiochipPushOrder = kInventoryLidOrder + 1; +static const DisplayOrder kBiochipLidOrder = kBiochipPushOrder + 1; +static const DisplayOrder kFinalMessageOrder = kBiochipLidOrder + 1; + +static const DisplayOrder kInfoLayer = 26000; +static const DisplayOrder kInfoBackgroundOrder = kInfoLayer; +static const DisplayOrder kInfoSpinOrder = kInfoBackgroundOrder + 1; + +static const DisplayOrder kScreenDimmerOrder = 30000; + +static const DisplayOrder kPauseScreenLayer = 31000; +static const DisplayOrder kPauseMenuOrder = kPauseScreenLayer; +static const DisplayOrder kSaveGameOrder = kPauseMenuOrder + 1; +static const DisplayOrder kContinueOrder = kSaveGameOrder + 1; +static const DisplayOrder kRestoreOrder = kContinueOrder + 1; +static const DisplayOrder kSoundFXOrder = kRestoreOrder + 1; +static const DisplayOrder kAmbienceOrder = kSoundFXOrder + 1; +static const DisplayOrder kWalkthruOrder = kAmbienceOrder + 1; +static const DisplayOrder kQuitToMainMenuOrder = kWalkthruOrder + 1; +static const DisplayOrder kPauseLargeHiliteOrder = kQuitToMainMenuOrder + 1; +static const DisplayOrder kPauseSmallHiliteOrder = kPauseLargeHiliteOrder + 1; + +///////////////////////////////////////////// +// +// Death reasons. +enum { +	// Caldoria +	kDeathUncreatedInCaldoria = 1, +	kDeathCardBomb, +	kDeathShotBySinclair, +	kDeathSinclairShotDelegate, +	kDeathNuclearExplosion, + +	// TSA +	kDeathUncreatedInTSA, +	kDeathShotByTSARobots, + +	// Prehistoric +	kDeathFallOffCliff, +	kDeathEatenByDinosaur, +	kDeathStranded, + +	// Norad +	kDeathGassedInNorad, +	kDeathArrestedInNorad, +	kDeathWokeUpNorad, +	kDeathSubDestroyed, // Unused +	kDeathRobotThroughNoradDoor, +	kDeathRobotSubControlRoom, + +	// Mars +	kDeathWrongShuttleLock, +	kDeathArrestedInMars, +	kDeathRunOverByPod, +	kDeathDidntGetOutOfWay, +	kDeathReactorBurn, +	kDeathDidntFindMarsBomb, +	kDeathDidntDisarmMarsBomb, +	kDeathNoMaskInMaze, +	kDeathNoAirInMaze, +	kDeathGroundByMazebot, +	kDeathMissedOreBucket, +	kDeathDidntLeaveBucket, +	kDeathRanIntoCanyonWall, // Unused +	kDeathRanIntoSpaceJunk, + +	// WSC +	kDeathDidntStopPoison, +	kDeathArrestedInWSC, +	kDeathHitByPlasma, +	kDeathShotOnCatwalk, + +	// Winning +	kPlayerWonGame +}; + +static const CoordType kAILeftAreaLeft = 76; +static const CoordType kAILeftAreaTop = 334; + +static const CoordType kAILeftAreaWidth = 96; +static const CoordType kAILeftAreaHeight = 96; + +static const CoordType kAIMiddleAreaLeft = 172; +static const CoordType kAIMiddleAreaTop = 334; + +static const CoordType kAIMiddleAreaWidth = 192; +static const CoordType kAIMiddleAreaHeight = 96; + +static const CoordType kAIRightAreaLeft = 364; +static const CoordType kAIRightAreaTop = 334; + +static const CoordType kAIRightAreaWidth = 96; +static const CoordType kAIRightAreaHeight = 96; + +enum { +	kTSAPlayerNotArrived,				// initial state, must be zero +	kTSAPlayerForcedReview,				// Player must watch TBP before rip occurs. +	kTSAPlayerDetectedRip,				// Player finished TBP, rip alarm just went off. +	kTSAPlayerNeedsHistoricalLog,		// Player is instructed to get historical log +	kTSAPlayerGotHistoricalLog, +	kTSAPlayerInstalledHistoricalLog, +	kTSABossSawHistoricalLog, +	kRobotsAtCommandCenter, +	kRobotsAtFrontDoor, +	kRobotsAtReadyRoom, +	kPlayerLockedInPegasus, +	kPlayerOnWayToPrehistoric, +	kPlayerWentToPrehistoric, +	kPlayerOnWayToNorad, +	kPlayerOnWayToMars, +	kPlayerOnWayToWSC, +	kPlayerFinishedWithTSA +}; + +///////////////////////////////////////////// +// +// Mode static constants. + +static const GameMode kModeInventoryPick = kLastGameShellMode + 1; +static const GameMode kModeBiochipPick = kModeInventoryPick + 1; +static const GameMode kModeInfoScreen = kModeBiochipPick + 1; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/cursor.cpp b/engines/pegasus/cursor.cpp new file mode 100644 index 0000000000..205336a00b --- /dev/null +++ b/engines/pegasus/cursor.cpp @@ -0,0 +1,213 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/events.h" +#include "common/stream.h" +#include "common/system.h" +#include "graphics/cursorman.h" +#include "graphics/surface.h" +#include "graphics/decoders/pict.h" + +#include "pegasus/cursor.h" +#include "pegasus/graphics.h" +#include "pegasus/pegasus.h" + +namespace Pegasus { + +Cursor::Cursor() { +	_cursorObscured = false; +	_index = -1; +	startIdling(); +} + +Cursor::~Cursor() { +	for (uint32 i = 0; i < _info.size(); i++) { +		if (_info[i].surface) { +			_info[i].surface->free(); +			delete _info[i].surface; +		} +		delete[] _info[i].palette; +	} + +	stopIdling(); +} + +void Cursor::addCursorFrames(uint16 id) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; +	Common::SeekableReadStream *cursStream = vm->_resFork->getResource(MKTAG('C', 'u', 'r', 's'), id); +	if (!cursStream) +		error("Could not load cursor frames set %d", id); + +	uint16 frameCount = cursStream->readUint16BE(); +	for (uint16 i = 0; i < frameCount; i++) { +		CursorInfo info; +		info.tag = cursStream->readUint16BE(); +		info.hotspot.x = cursStream->readUint16BE(); +		info.hotspot.y = cursStream->readUint16BE(); +		info.surface = 0; +		info.palette = 0; +		info.colorCount = 0; +		_info.push_back(info); +	} + +	delete cursStream; + +	setCurrentFrameIndex(0); +} + +void Cursor::setCurrentFrameIndex(int32 index) { +	if (_index != index) { +		_index = index; +		if (index != -1) { +			loadCursorImage(_info[index]); +			CursorMan.replaceCursorPalette(_info[index].palette, 0, _info[index].colorCount); +			CursorMan.replaceCursor((byte *)_info[index].surface->pixels, _info[index].surface->w, _info[index].surface->h, _info[index].hotspot.x, _info[index].hotspot.y, 0); +			((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); +		} +	} +} + +int32 Cursor::getCurrentFrameIndex() const { +	return _index; +} + +void Cursor::show() { +	if (!isVisible()) +		CursorMan.showMouse(true); + +	_cursorObscured = false; +	((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); +} + +void Cursor::hide() { +	CursorMan.showMouse(false); +	setCurrentFrameIndex(0); +	((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); +} + +void Cursor::hideUntilMoved() { +	if (!_cursorObscured) { +		hide(); +		_cursorObscured = true; +	} +} + +void Cursor::useIdleTime() { +	if (g_system->getEventManager()->getMousePos() != _cursorLocation) { +		_cursorLocation = g_system->getEventManager()->getMousePos(); +		if (_index != -1 && _cursorObscured) +			show(); +		((PegasusEngine *)g_engine)->_gfx->markCursorAsDirty(); +	} +} + +void Cursor::getCursorLocation(Common::Point &pt) const { +	pt = _cursorLocation; +} + +bool Cursor::isVisible() { +	return CursorMan.isVisible(); +} + +void Cursor::loadCursorImage(CursorInfo &cursorInfo) { +	if (cursorInfo.surface) +		return; + +	cursorInfo.surface = new Graphics::Surface(); + +	PegasusEngine *vm = (PegasusEngine *)g_engine; +	Common::SeekableReadStream *cicnStream = vm->_resFork->getResource(MKTAG('c', 'i', 'c', 'n'), cursorInfo.tag); + +	if (!cicnStream) +		error("Failed to find color icon %d", cursorInfo.tag); +	 +	// PixMap section +	Graphics::PICTDecoder::PixMap pixMap = Graphics::PICTDecoder::readPixMap(*cicnStream); +	 +	// Mask section +	cicnStream->readUint32BE(); // mask baseAddr +	uint16 maskRowBytes = cicnStream->readUint16BE(); // mask rowBytes +	cicnStream->skip(3 * 2); // mask rect +	/* uint16 maskHeight = */ cicnStream->readUint16BE(); + +	// Bitmap section +	cicnStream->readUint32BE(); // baseAddr +	uint16 rowBytes = cicnStream->readUint16BE(); +	cicnStream->readUint16BE(); // top +	cicnStream->readUint16BE(); // left +	uint16 height = cicnStream->readUint16BE(); // bottom +	cicnStream->readUint16BE(); // right +	 +	// Data section +	cicnStream->readUint32BE(); // icon handle +	cicnStream->skip(maskRowBytes * height); // FIXME: maskHeight doesn't work here, though the specs say it should +	cicnStream->skip(rowBytes * height); +	 +	// Palette section +	cicnStream->readUint32BE(); // always 0 +	cicnStream->readUint16BE(); // always 0 +	cursorInfo.colorCount = cicnStream->readUint16BE() + 1; +	 +	cursorInfo.palette = new byte[cursorInfo.colorCount * 3]; +	for (uint16 i = 0; i < cursorInfo.colorCount; i++) { +		cicnStream->readUint16BE(); +		cursorInfo.palette[i * 3] = cicnStream->readUint16BE() >> 8; +		cursorInfo.palette[i * 3 + 1] = cicnStream->readUint16BE() >> 8; +		cursorInfo.palette[i * 3 + 2] = cicnStream->readUint16BE() >> 8; +	} +	 +	// PixMap data +	if (pixMap.pixelSize == 8) { +		cursorInfo.surface->create(pixMap.rowBytes, pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8()); +		cicnStream->read(cursorInfo.surface->pixels, pixMap.rowBytes * pixMap.bounds.height()); + +		// While this looks sensible, it actually doesn't work for some cursors +		// (ie. the 'can grab' hand) +		//cursorInfo.surface->w = pixMap.bounds.width(); +	} else if (pixMap.pixelSize == 1) { +		cursorInfo.surface->create(pixMap.bounds.width(), pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8()); + +		for (int y = 0; y < pixMap.bounds.height(); y++) { +			byte *line = (byte *)cursorInfo.surface->getBasePtr(0, y); + +			for (int x = 0; x < pixMap.bounds.width();) { +				byte b = cicnStream->readByte(); + +				for (int i = 0; i < 8; i++) { +					*line++ = ((b & (1 << (7 - i))) != 0) ? 1 : 0; + +					if (++x == pixMap.bounds.width()) +						break; +				} +			} +		} +	} else { +		error("Unhandled %dbpp cicn images", pixMap.pixelSize); +	} + +	delete cicnStream; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/cursor.h b/engines/pegasus/cursor.h new file mode 100644 index 0000000000..ada82e3967 --- /dev/null +++ b/engines/pegasus/cursor.h @@ -0,0 +1,84 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_CURSOR_H +#define PEGASUS_CURSOR_H + +#include "common/array.h" +#include "common/rect.h" + +#include "pegasus/timers.h" + +namespace Graphics { +	struct Surface; +} + +namespace Pegasus { + +// The original cursor code was in the graphics code directly, +// unlike ScummVM where we have the cursor code separate. We're +// going to go with CursorManager here and therefore not inherit +// from the Sprite class. + +class Cursor : private Idler { +public: +	Cursor(); +	virtual ~Cursor(); + +	void addCursorFrames(uint16 id); + +	void setCurrentFrameIndex(int32 index); +	int32 getCurrentFrameIndex() const; + +	void show(); +	void hide(); +	void hideUntilMoved(); +	bool isVisible(); + +	void getCursorLocation(Common::Point &) const; + +protected: +	virtual void useIdleTime(); + +private: +	struct CursorInfo { +		uint16 tag; +		Common::Point hotspot; +		Graphics::Surface *surface; +		byte *palette; +		uint16 colorCount; +	}; + +	Common::Point _cursorLocation; +	Common::Array<CursorInfo> _info; +	bool _cursorObscured; +	int _index; + +	void loadCursorImage(CursorInfo &cursorInfo); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp new file mode 100644 index 0000000000..d9afacbf3e --- /dev/null +++ b/engines/pegasus/detection.cpp @@ -0,0 +1,157 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "base/plugins.h" + +#include "engines/advancedDetector.h" +#include "common/config-manager.h" +#include "common/file.h" +#include "common/savefile.h" + +#include "pegasus/pegasus.h" + +namespace Pegasus { + +struct PegasusGameDescription { +	ADGameDescription desc; +}; + +bool PegasusEngine::hasFeature(EngineFeature f) const { +	return +		(f == kSupportsRTL) +		|| (f == kSupportsLoadingDuringRuntime) +		|| (f == kSupportsSavingDuringRuntime); +} + +bool PegasusEngine::isDemo() const { +	return (_gameDescription->desc.flags & ADGF_DEMO) != 0; +} + +} // End of namespace Pegasus + +static const PlainGameDescriptor pegasusGames[] = { +	{"pegasus", "The Journeyman Project: Pegasus Prime"}, +	{0, 0} +}; + + +namespace Pegasus { + +static const PegasusGameDescription gameDescriptions[] = { +	{ +		{ +			"pegasus", +			"", +			AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 2009943), +			Common::EN_ANY, +			Common::kPlatformMacintosh, +			ADGF_MACRESFORK, +			GUIO0() +		}, +	}, + +	{ +		{ +			"pegasus", +			"Demo", +			AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 360129), +			Common::EN_ANY, +			Common::kPlatformMacintosh, +			ADGF_MACRESFORK|ADGF_DEMO, +			GUIO1(GUIO_NOLAUNCHLOAD) +		}, +	}, + +	{ AD_TABLE_END_MARKER } +}; + +} // End of namespace Pegasus + + +class PegasusMetaEngine : public AdvancedMetaEngine { +public: +	PegasusMetaEngine() : AdvancedMetaEngine(Pegasus::gameDescriptions, sizeof(Pegasus::PegasusGameDescription), pegasusGames) { +		_singleid = "pegasus"; +	} + +	virtual const char *getName() const { +		return "The Journeyman Project: Pegasus Prime"; +	} + +	virtual const char *getOriginalCopyright() const { +		return "The Journeyman Project: Pegasus Prime (C) Presto Studios"; +	} + +	virtual bool hasFeature(MetaEngineFeature f) const; +	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; +	virtual SaveStateList listSaves(const char *target) const; +	virtual int getMaximumSaveSlot() const { return 999; } +	virtual void removeSaveState(const char *target, int slot) const; +}; + +bool PegasusMetaEngine::hasFeature(MetaEngineFeature f) const { +	return +		(f == kSupportsListSaves) +		|| (f == kSupportsLoadingDuringStartup) +		|| (f == kSupportsDeleteSave); +} + +SaveStateList PegasusMetaEngine::listSaves(const char *target) const { +	// The original had no pattern, so the user must rename theirs +	// Note that we ignore the target because saves are compatible between +	// all versions +	Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav"); + +	SaveStateList saveList; +	for (uint32 i = 0; i < filenames.size(); i++) { +		// Isolate the description from the file name +		Common::String desc = filenames[i].c_str() + 8; +		for (int j = 0; j < 4; j++) +			desc.deleteLastChar(); +		 +		saveList.push_back(SaveStateDescriptor(i, desc)); +	} + +	return saveList; +} + +void PegasusMetaEngine::removeSaveState(const char *target, int slot) const { +	// See listSaves() for info on the pattern +	Common::StringArray filenames = g_system->getSavefileManager()->listSavefiles("pegasus-*.sav"); +	g_system->getSavefileManager()->removeSavefile(filenames[slot].c_str()); +} + +bool PegasusMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { +	const Pegasus::PegasusGameDescription *gd = (const Pegasus::PegasusGameDescription *)desc; + +	if (gd) +		*engine = new Pegasus::PegasusEngine(syst, gd); + +	return (gd != 0); +} + +#if PLUGIN_ENABLED_DYNAMIC(PEGASUS) +	REGISTER_PLUGIN_DYNAMIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine); +#else +	REGISTER_PLUGIN_STATIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine); +#endif + diff --git a/engines/pegasus/elements.cpp b/engines/pegasus/elements.cpp new file mode 100644 index 0000000000..0310d26292 --- /dev/null +++ b/engines/pegasus/elements.cpp @@ -0,0 +1,568 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/macresman.h" +#include "common/stream.h" + +#include "pegasus/elements.h" +#include "pegasus/graphics.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +DisplayElement::DisplayElement(const DisplayElementID id) : IDObject(id) { +	_elementIsDisplaying = false; +	_elementIsVisible = false; +	_elementOrder = 0; +	_triggeredElement = this; +	_nextElement = 0; +} + +DisplayElement::~DisplayElement() { +	if (isDisplaying()) +		((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); +} + +void DisplayElement::setDisplayOrder(const DisplayOrder order) { +	if (_elementOrder != order) { +		_elementOrder = order; +		if (isDisplaying()) { +			((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); +			((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this); +			triggerRedraw(); +		} +	} +} + +void DisplayElement::startDisplaying() { +	if (!isDisplaying()) { +		((PegasusEngine *)g_engine)->_gfx->addDisplayElement(this); +		triggerRedraw(); +	} +} + +void DisplayElement::stopDisplaying() { +	if (isDisplaying()) { +		triggerRedraw(); +		((PegasusEngine *)g_engine)->_gfx->removeDisplayElement(this); +	} +} + +void DisplayElement::setBounds(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) { +	setBounds(Common::Rect(left, top, right, bottom)); +} + +void DisplayElement::getBounds(Common::Rect &r) const { +	r = _bounds; +} + +void DisplayElement::sizeElement(const CoordType h, const CoordType v) { +	Common::Rect newBounds = _bounds; +	newBounds.right = _bounds.left + h; +	newBounds.bottom = _bounds.top + v; +	setBounds(newBounds); +} + +void DisplayElement::moveElementTo(const CoordType h, const CoordType v) { +	Common::Rect newBounds = _bounds; +	newBounds.moveTo(h, v); +	setBounds(newBounds); +} + +void DisplayElement::moveElement(const CoordType dh, const CoordType dv) { +	Common::Rect newBounds = _bounds; +	newBounds.translate(dh, dv); +	setBounds(newBounds); +} + +void DisplayElement::getLocation(CoordType &h, CoordType &v) const { +	h = _bounds.left; +	v = _bounds.top; +} + +void DisplayElement::centerElementAt(const CoordType h, const CoordType v) { +	Common::Rect newBounds = _bounds; +	newBounds.moveTo(h - (_bounds.width() / 2), v - (_bounds.height() / 2)); +	setBounds(newBounds); +} + +void DisplayElement::getCenter(CoordType &h, CoordType &v) const { +	h = (_bounds.left + _bounds.right) / 2; +	v = (_bounds.top + _bounds.bottom) / 2; +} + +void DisplayElement::setBounds(const Common::Rect &r) { +	if (r != _bounds) { +		triggerRedraw(); +		_bounds = r; +		triggerRedraw(); +	} +} + +void DisplayElement::hide() { +	if (_elementIsVisible) { +		triggerRedraw(); +		_elementIsVisible = false; +	} +} + +void DisplayElement::show() { +	if (!_elementIsVisible) { +		_elementIsVisible = true; +		triggerRedraw(); +	} +} + +// Only invalidates this element's bounding rectangle if all these conditions are true: +// -- The triggered element is this element. +// -- The element is displaying on the display list. +// -- The element is visible. +// -- The element is part of the active layer OR is one of the reserved items. +void DisplayElement::triggerRedraw() { +	GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + +	if (_triggeredElement == this) { +		if (validToDraw(gfx->getBackOfActiveLayer(), gfx->getFrontOfActiveLayer())) +			gfx->invalRect(_bounds); +	} else { +		_triggeredElement->triggerRedraw(); +	} +} + +void DisplayElement::setTriggeredElement(DisplayElement *element) { +	if (element) +		_triggeredElement = element; +	else +		_triggeredElement = this; +} + +bool DisplayElement::validToDraw(DisplayOrder backLayer, DisplayOrder frontLayer) { +	return	isDisplaying() && _elementIsVisible && +			(getObjectID() <= kHighestReservedElementID || +			(getDisplayOrder() >= backLayer && +			getDisplayOrder() <= frontLayer)); +} + +DropHighlight::DropHighlight(const DisplayElementID id) : DisplayElement(id) { +	_highlightColor = 0; +	_thickness = 2; +	_cornerDiameter = 0; +} + +void DropHighlight::draw(const Common::Rect &) { +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + +	// Since this is only used in two different ways, I'm only +	// going to implement it in those two ways. Deal with it. + +	Common::Rect rect = _bounds; +	rect.grow(-_thickness); +	screen->frameRect(rect, _highlightColor); +	rect.grow(1); +	screen->frameRect(rect, _highlightColor); + +	if (_cornerDiameter == 8 && _thickness == 4) { +		rect.grow(1); +		screen->frameRect(rect, _highlightColor); +		screen->hLine(rect.left + 1, rect.top - 1, rect.right - 2, _highlightColor); +		screen->hLine(rect.left + 1, rect.bottom, rect.right - 2, _highlightColor); +		screen->vLine(rect.left - 1, rect.top + 1, rect.bottom - 2, _highlightColor); +		screen->vLine(rect.right, rect.top + 1, rect.bottom - 2, _highlightColor); +	} +} + +IdlerAnimation::IdlerAnimation(const DisplayElementID id) : Animation(id) { +	_lastTime = 0xffffffff; +} + +void IdlerAnimation::startDisplaying() { +	if (!isDisplaying()) { +		Animation::startDisplaying(); +		startIdling(); +	} +} + +void IdlerAnimation::stopDisplaying() { +	if (isDisplaying()) { +		Animation::stopDisplaying(); +		stopIdling(); +	} +} + +void IdlerAnimation::useIdleTime() { +	uint32 currentTime = getTime(); + +	if (currentTime != _lastTime) { +		_lastTime = currentTime; +		timeChanged(_lastTime); +	} +} + +void IdlerAnimation::timeChanged(const TimeValue) { +	triggerRedraw(); +} + +FrameSequence::FrameSequence(const DisplayElementID id) : IdlerAnimation(id) { +	_duration = 0; +	_currentFrameNum = 0; +	_resFork = new Common::MacResManager(); +	_numFrames = 0; +} + +FrameSequence::~FrameSequence() { +	delete _resFork; +} + +void FrameSequence::useFileName(const Common::String &fileName) { +	_resFork->open(fileName); +} + +void FrameSequence::openFrameSequence() { +	if (!_resFork->hasResFork()) +		return; + +	Common::SeekableReadStream *res = _resFork->getResource(MKTAG('P', 'F', 'r', 'm'), 0x80); + +	if (!res) +		return; + +	uint32 scale = res->readUint32BE(); +	_bounds.top = res->readUint16BE(); +	_bounds.left = res->readUint16BE(); +	_bounds.bottom = res->readUint16BE(); +	_bounds.right = res->readUint16BE(); +	_numFrames = res->readUint16BE(); +	_duration = 0; + +	_frameTimes.clear(); +	for (uint32 i = 0; i < _numFrames; i++) { +		TimeValue time = res->readUint32BE(); +		_duration += time; +		_frameTimes.push_back(_duration); +	} + +	setScale(scale); +	setSegment(0, _duration); +	setTime(0); +	_currentFrameNum = 0; +	newFrame(_currentFrameNum); +	triggerRedraw(); + +	delete res; +} + +void FrameSequence::closeFrameSequence() { +	stop(); +	_resFork->close(); +	_duration = 0; +	_numFrames = 0; +	_frameTimes.clear(); +} + +void FrameSequence::timeChanged(const TimeValue time) { +	int16 frameNum = 0; +	for (int16 i = _numFrames - 1; i >= 0; i--) { +		if (_frameTimes[i] < time) { +			frameNum = i; +			break; +		} +	} + +	if (frameNum != _currentFrameNum) { +		_currentFrameNum = frameNum; +		newFrame(_currentFrameNum); +		triggerRedraw(); +	} +} + +void FrameSequence::setFrameNum(const int16 frameNum) { +	int16 f = CLIP<int>(frameNum, 0, _numFrames); + +	if (_currentFrameNum != f) { +		_currentFrameNum = f; +		setTime(_frameTimes[f]); +		newFrame(f); +		triggerRedraw(); +	} +} + +bool FrameSequence::isSequenceOpen() const { +	return _numFrames != 0; +} + +Sprite::Sprite(const DisplayElementID id) : DisplayElement(id) { +	_numFrames = 0; +	_currentFrameNum = 0xffffffff; +	_currentFrame = 0; +} + +Sprite::~Sprite() { +	discardFrames(); +} + +void Sprite::discardFrames() { +	if (!_frameArray.empty()) { +		for (uint32 i = 0; i < _numFrames; i++) { +			SpriteFrame *frame = _frameArray[i].frame; +			frame->_referenceCount--; +			if (frame->_referenceCount == 0) +				delete frame; +		} + +		_frameArray.clear(); +		_numFrames = 0; +		_currentFrame = 0; +		_currentFrameNum = 0xffffffff; +		setBounds(0, 0, 0, 0); +	} +} + +void Sprite::addPICTResourceFrame(const ResIDType pictID, bool transparent, const CoordType left, const CoordType top) { +	SpriteFrame *frame = new SpriteFrame(); +	frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, pictID, transparent); +	addFrame(frame, left, top); +} + +uint32 Sprite::addFrame(SpriteFrame *frame, const CoordType left, const CoordType top) { +	SpriteFrameRec frameRecord; +	frameRecord.frame = frame; +	frameRecord.frameLeft = left; +	frameRecord.frameTop = top; +	_frameArray.push_back(frameRecord); +	_numFrames++; +	frame->_referenceCount++; + +	Common::Rect frameBounds; +	frame->getSurfaceBounds(frameBounds); + +	// 9/3/96 +	// BB Should this be + left or - left? +	frameBounds.moveTo(_bounds.left + left, _bounds.top + top); + +	frameBounds.extend(_bounds); + +	if (_bounds != frameBounds) +		setBounds(frameBounds); + +	return _numFrames - 1; +} + +void Sprite::removeFrame(const uint32 frameNum) { +	_frameArray[frameNum].frame->_referenceCount--; +	if (_frameArray[frameNum].frame->_referenceCount == 0) +		delete _frameArray[frameNum].frame; + +	// Calculate the new bounds +	Common::Rect frameBounds; +	for (uint32 i = 0; i < _numFrames; i++) { +		if (i == frameNum) +			continue; + +		Common::Rect r; +		_frameArray[i].frame->getSurfaceBounds(r); +		r.translate(_frameArray[i].frameLeft, _frameArray[i].frameTop); +		frameBounds.extend(r); +	} + +	_frameArray.remove_at(frameNum); + +	frameBounds.moveTo(_bounds.left, _bounds.top); +	setBounds(frameBounds); + +	if (_currentFrameNum == frameNum) +		triggerRedraw(); +	else if (_currentFrameNum != 0xffffffff && _currentFrameNum > frameNum) +		--_currentFrameNum; +} + +void Sprite::setCurrentFrameIndex(const int32 frameNum) {	 +	if (frameNum < 0) { +		if (_currentFrameNum != 0xffffffff) { +			_currentFrameNum = 0xffffffff; +			_currentFrame = 0; +			triggerRedraw(); +		} +	} else if (_numFrames > 0) { +		uint32 f = frameNum % _numFrames; +		if (f != _currentFrameNum) { +			_currentFrameNum = f; +			_currentFrame = &_frameArray[f]; +			triggerRedraw(); +		} +	} +} + +SpriteFrame *Sprite::getFrame(const int32 index) { +	if (index < 0 || (uint32)index >= _numFrames) +		return 0; + +	return _frameArray[index].frame; +} + +void Sprite::draw(const Common::Rect &r) { +	if (_currentFrame) { +		Common::Rect frameBounds; +		_currentFrame->frame->getSurfaceBounds(frameBounds); + +		frameBounds.translate(_bounds.left + _currentFrame->frameLeft, _bounds.top + _currentFrame->frameTop); +		Common::Rect r1 = frameBounds.findIntersectingRect(r); + +		Common::Rect r2 = frameBounds; +		r2.translate(-_bounds.left - _currentFrame->frameLeft, -_bounds.top - _currentFrame->frameTop); + +		_currentFrame->frame->drawImage(r2, r1); +	} +} + +SpriteSequence::SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID) : +		FrameSequence(id), _sprite(spriteID), _transparent(false) { +} + +void SpriteSequence::openFrameSequence() {	 +	if (!isSequenceOpen()) { +		FrameSequence::openFrameSequence(); + +		if (isSequenceOpen()) { +			uint32 numFrames = getNumFrames(); + +			for (uint32 i = 0; i < numFrames; ++i) { +				SpriteFrame *frame = new SpriteFrame(); +				frame->initFromPICTResource(_resFork, i + 0x80, _transparent); +				_sprite.addFrame(frame, 0, 0); +			} + +			_sprite.setBounds(_bounds); +		} +	} +} + +void SpriteSequence::closeFrameSequence() { +	if (isSequenceOpen()) { +		FrameSequence::closeFrameSequence(); +		_sprite.discardFrames(); +	} +} + +void SpriteSequence::setBounds(const Common::Rect &bounds) { +	FrameSequence::setBounds(bounds); +	_sprite.setBounds(_bounds); +} + +void SpriteSequence::draw(const Common::Rect &r) { +	_sprite.draw(r); +} + +void SpriteSequence::newFrame(const uint16 frame) { +	_sprite.setCurrentFrameIndex(frame); +} + +#define DRAW_PIXEL() \ +	if (bytesPerPixel == 2) \ +		*((uint16 *)dst) = black; \ +	else \ +		*((uint32 *)dst) = black; \ +	dst += bytesPerPixel + +#define SKIP_PIXEL() \ +	dst += bytesPerPixel + +void ScreenDimmer::draw(const Common::Rect &r) { +	// We're going to emulate QuickDraw's srcOr+gray mode here +	// In this mode, every other y column is all black (odd-columns). +	// Basically, every row does three black and then one transparent +	// repeatedly. + +	// The output is identical to the original + +	uint32 black = g_system->getScreenFormat().RGBToColor(0, 0, 0); +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); +	byte bytesPerPixel = g_system->getScreenFormat().bytesPerPixel; + +	// We're currently doing it to the whole screen to simplify the code + +	for (int y = 0; y < 480; y++) { +		byte *dst = (byte *)screen->getBasePtr(0, y); + +		for (int x = 0; x < 640; x += 4) { +			if (y & 1) { +				DRAW_PIXEL(); +				DRAW_PIXEL(); +				SKIP_PIXEL(); +				DRAW_PIXEL(); +			} else { +				SKIP_PIXEL(); +				DRAW_PIXEL(); +				DRAW_PIXEL(); +				DRAW_PIXEL(); +			} +		} +	} +} + +#undef DRAW_PIXEL +#undef SKIP_PIXEL + +SoundLevel::SoundLevel(const DisplayElementID id) : DisplayElement(id) { +	_soundLevel = 0; +} + +void SoundLevel::incrementLevel() { +	if (_soundLevel < 12) { +		_soundLevel++; +		triggerRedraw(); +	} +} + +void SoundLevel::decrementLevel() { +	if (_soundLevel > 0) { +		_soundLevel--; +		triggerRedraw(); +	} +} + +uint16 SoundLevel::getSoundLevel() { +	return CLIP<int>(_soundLevel * 22, 0, 256); +} + +void SoundLevel::setSoundLevel(uint16 level) { +	uint16 newLevel = (level + 21) / 22; + +	if (newLevel != _soundLevel) { +		_soundLevel = newLevel; +		triggerRedraw(); +	} +} + +void SoundLevel::draw(const Common::Rect &r) { +	Common::Rect levelRect(_bounds.right + (8 * (_soundLevel - 12)), _bounds.top, _bounds.right, _bounds.bottom); +	levelRect = r.findIntersectingRect(levelRect); + +	if (!levelRect.isEmpty()) { +		Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); +		screen->fillRect(levelRect, g_system->getScreenFormat().RGBToColor(0, 0, 0)); +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/elements.h b/engines/pegasus/elements.h new file mode 100644 index 0000000000..d96d2173b1 --- /dev/null +++ b/engines/pegasus/elements.h @@ -0,0 +1,254 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ELEMENTS_H +#define PEGASUS_ELEMENTS_H + +#include "common/array.h" +#include "common/rect.h" +#include "common/str.h" +#include "common/system.h" +#include "graphics/surface.h" + +#include "pegasus/timers.h" +#include "pegasus/util.h" + +namespace Common { +	class MacResManager; +} + +namespace Pegasus { + +class DisplayElement : public IDObject { +friend class GraphicsManager; +public: +	DisplayElement(const DisplayElementID); +	virtual ~DisplayElement(); +	 +	void setDisplayOrder(const DisplayOrder); +	DisplayOrder getDisplayOrder() const { return _elementOrder; } +	 +	bool validToDraw(DisplayOrder, DisplayOrder); +	 +	virtual void draw(const Common::Rect&) {} +	bool isDisplaying() { return _elementIsDisplaying; } +	virtual void startDisplaying(); +	virtual void stopDisplaying(); +	 +	virtual void show(); +	virtual void hide(); +	bool isVisible() { return _elementIsVisible; } +	 +	// triggerRedraw only triggers a draw if the element is displaying and visible. +	void triggerRedraw(); +	void setTriggeredElement(DisplayElement *); +	 +	virtual void setBounds(const CoordType, const CoordType, const CoordType, const CoordType); +	virtual void setBounds(const Common::Rect &); +	virtual void getBounds(Common::Rect &) const; +	virtual void sizeElement(const CoordType, const CoordType); +	virtual void moveElementTo(const CoordType, const CoordType); +	virtual void moveElement(const CoordType, const CoordType); +	virtual void getLocation(CoordType &, CoordType &) const; +	virtual void getCenter(CoordType &, CoordType &) const; +	virtual void centerElementAt(const CoordType, const CoordType); + +protected: +	Common::Rect _bounds; +	bool _elementIsVisible; +	DisplayElement *_triggeredElement; + +	// Used only by PegasusEngine +	bool _elementIsDisplaying; +	DisplayOrder _elementOrder; +	DisplayElement *_nextElement; +}; + +// I'm using the proper "highlight" instead of the evil +// QuickDraw "hilite" :P (deal with it!) +class DropHighlight : public DisplayElement { +public: +	DropHighlight(const DisplayElementID); +	virtual ~DropHighlight() {} + +	void setHighlightColor(const uint32 &highlight) { _highlightColor = highlight; } +	void getHighlightColor(uint32 &highlight) const { highlight = _highlightColor; }  + +	void setHighlightThickness(const uint16 thickness) { _thickness = thickness; } +	uint16 getHighlightThickness() const { return _thickness; } + +	void setHighlightCornerDiameter(const uint16 diameter) { _cornerDiameter = diameter; } +	uint16 getHighlightCornerDiameter() const { return _cornerDiameter; } + +	virtual void draw(const Common::Rect&); + +protected: +	uint32 _highlightColor; +	uint16 _thickness; +	uint16 _cornerDiameter; +}; + +class Animation : public DisplayElement, public DynamicElement { +public: +	Animation(const DisplayElementID id) : DisplayElement(id) {} +}; + +class IdlerAnimation : public Animation, public Idler { +public: +	IdlerAnimation(const DisplayElementID); +	 +	virtual void startDisplaying(); +	virtual void stopDisplaying(); +	 +	TimeValue getLastTime() const { return _lastTime; } + +protected: +	virtual void useIdleTime(); +	virtual void timeChanged(const TimeValue); +	 +	TimeValue _lastTime; +}; + +// This class reads PICT resources and plays them like a movie. +// Assumes there is a resource of type 'PFrm' describing the time values for each +// PICT frame, as well as the total time in the movie. +// Assumes that PICT frames begin at PICT 128 + +class FrameSequence : public IdlerAnimation { +public: +	FrameSequence(const DisplayElementID); +	virtual ~FrameSequence(); + +	void useFileName(const Common::String &fileName); + +	virtual void openFrameSequence(); +	virtual void closeFrameSequence(); +	bool isSequenceOpen() const; +	 +	uint16 getNumFrames() const { return _numFrames; } +	virtual uint16 getFrameNum() const { return _currentFrameNum; } +	virtual void setFrameNum(const int16); + +protected:		 +	virtual void timeChanged(const TimeValue); +	virtual void newFrame(const uint16) {} + +	Common::MacResManager *_resFork; +	TimeValue _duration; + +	uint16 _numFrames; +	Common::Array<TimeValue> _frameTimes; + +	uint16 _currentFrameNum; +}; + +class SpriteFrame; + +class Sprite : public DisplayElement { +friend class SpriteFrame; +public: +	Sprite(const DisplayElementID); +	virtual ~Sprite(); + +	virtual void addPICTResourceFrame(const ResIDType, const bool, const CoordType, const CoordType); +	virtual uint32 addFrame(SpriteFrame *, const CoordType, const CoordType); +	virtual void removeFrame(const uint32); +	virtual void discardFrames(); +	 +	// Setting the current frame. +	// If the index is negative, sets the current frame to NULL and hides the sprite. +	// If the index is larger than the number of frames in the sprite, the number +	// is treated modulo the number of frames. +	virtual void setCurrentFrameIndex(const int32); +	virtual uint32 getCurrentFrameIndex() const { return _currentFrameNum; } +	 +	virtual SpriteFrame *getFrame(const int32); +	 +	virtual void draw(const Common::Rect &); +	 +	uint32 getNumFrames() const { return _numFrames; } + +protected: +	struct SpriteFrameRec { +		SpriteFrame *frame; +		CoordType frameLeft; +		CoordType frameTop; +	}; + +	uint32 _numFrames; +	uint32 _currentFrameNum; +	SpriteFrameRec *_currentFrame; +	Common::Array<SpriteFrameRec> _frameArray; +}; + +class SpriteSequence : public FrameSequence { +public: +	SpriteSequence(const DisplayElementID id, const DisplayElementID spriteID); +	virtual ~SpriteSequence() {} + +	void useTransparent(bool transparent) { _transparent = transparent; } + +	virtual void openFrameSequence(); +	virtual void closeFrameSequence(); + +	virtual void draw(const Common::Rect &); + +	virtual void setBounds(const Common::Rect &); + +protected: +	virtual void newFrame(const uint16); + +	bool _transparent; +	Sprite _sprite; +}; + +class ScreenDimmer : public DisplayElement { +public: +	ScreenDimmer() : DisplayElement(kScreenDimmerID) {} +	virtual ~ScreenDimmer() {} + +	virtual void draw(const Common::Rect &); +}; + +class SoundLevel : public DisplayElement { +public: +	SoundLevel(const DisplayElementID); +	virtual ~SoundLevel() {} +	 +	void incrementLevel(); +	void decrementLevel(); +	 +	uint16 getSoundLevel(); +	void setSoundLevel(uint16); +	 +	void draw(const Common::Rect &); + +protected: +	uint16 _soundLevel; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/energymonitor.cpp b/engines/pegasus/energymonitor.cpp new file mode 100644 index 0000000000..7a9ca7878a --- /dev/null +++ b/engines/pegasus/energymonitor.cpp @@ -0,0 +1,296 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +Blinker::Blinker() { +	_sprite = 0; +	_frame1 = -1; +	_frame2 = -1; +	_blinkDuration = 0; +} + +void Blinker::startBlinking(Sprite *sprite, int32 frame1, int32 frame2, uint32 numBlinks, TimeValue blinkDuration, TimeScale blinkScale) { +	stopBlinking(); +	_sprite = sprite; +	_frame1 = frame1; +	_frame2 = frame2; +	_blinkDuration = blinkDuration; +	setScale(blinkScale); +	setSegment(0, blinkDuration * numBlinks * 2, blinkScale); +	setTime(0); +	start(); +} + +void Blinker::stopBlinking() { +	if (_sprite) { +		_sprite->setCurrentFrameIndex(_frame2); +		_sprite = 0; +		stop(); +	} +} + +void Blinker::timeChanged(const TimeValue time) { +	if (_sprite && _blinkDuration != 0) { +		if (((time / _blinkDuration) & 1) != 0 || time == getDuration()) { +			_sprite->setCurrentFrameIndex(_frame2); +			if (!isRunning()) +				stopBlinking(); +		} else { +			_sprite->setCurrentFrameIndex(_frame1); +		} +	} +} + +static const NotificationFlags kEnergyExpiredFlag = 1; + +EnergyMonitor *g_energyMonitor = 0; + +EnergyMonitor::EnergyMonitor() : IdlerAnimation(kEnergyBarID), _energyLight(kWarningLightID) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	_stage = kStageNoStage; + +	_calibrating = false; +	_dontFlash = false; + +	setBounds(338, 48, 434, 54); + +	setDisplayOrder(kEnergyBarOrder); +	startDisplaying(); + +	SpriteFrame *frame = new SpriteFrame(); +	frame->initFromPICTResource(vm->_resFork, kLightOffID); +	_energyLight.addFrame(frame, 0, 0); + +	frame = new SpriteFrame(); +	frame->initFromPICTResource(vm->_resFork, kLightYellowID); +	_energyLight.addFrame(frame, 0, 0); + +	frame = new SpriteFrame(); +	frame->initFromPICTResource(vm->_resFork, kLightOrangeID); +	_energyLight.addFrame(frame, 0, 0); + +	frame = new SpriteFrame(); +	frame->initFromPICTResource(vm->_resFork, kLightRedID); +	_energyLight.addFrame(frame, 0, 0); + +	_energyLight.setBounds(540, 35, 600, 59); +	_energyLight.setDisplayOrder(kEnergyLightOrder); +	_energyLight.startDisplaying(); + +	setScale(1); +	setSegment(0, kMaxJMPEnergy); + +	setEnergyValue(kCasualEnergy); + +	g_energyMonitor = this; +} + +EnergyMonitor::~EnergyMonitor() { +	g_energyMonitor = 0; +} + +void EnergyMonitor::setEnergyValue(const uint32 value) { +	if (isRunning()) { +		stop(); +		setTime(getStop() - value); +		start(); +	} else { +		setTime(getStop() - value); +	} +} + +void EnergyMonitor::startEnergyDraining() { +	if (!isRunning()) { +		_energyLight.show(); +		start(); +		show(); +	} +} + +void EnergyMonitor::setEnergyDrainRate(Common::Rational rate) { +	setRate(rate); +} + +Common::Rational EnergyMonitor::getEnergyDrainRate() { +	return getRate(); +} + +void EnergyMonitor::stopEnergyDraining() { +	if (isRunning()) { +		stop(); +		_energyLight.hide(); +		hide(); +	} +} + +void EnergyMonitor::drainEnergy(const int32 delta) { +	setTime(getTime() + delta); +} + +int32 EnergyMonitor::getCurrentEnergy() { +	return kMaxJMPEnergy - getTime(); +} + +void EnergyMonitor::timeChanged(const TimeValue currentTime) { +	if (currentTime == getStop()) { +		PegasusEngine *vm = (PegasusEngine *)g_engine; +		if (vm->getEnergyDeathReason() != -1) +			vm->die(vm->getEnergyDeathReason()); +	} else { +		uint32 currentEnergy = kMaxJMPEnergy - currentTime; + +		EnergyStage newStage; +		if (currentEnergy > kWorriedEnergy) +			newStage = kStageCasual; +		else if (currentEnergy > kNervousEnergy) +			newStage = kStageWorried; +		else if (currentEnergy > kPanicStrickenEnergy) +			newStage = kStageNervous; +		else +			newStage = kStagePanicStricken; + +		if (_stage != newStage) { +			uint32 newFrame; + +			switch (newStage) { +			case kStageCasual: +				_barColor = g_system->getScreenFormat().RGBToColor(0x48, 0xB0, 0xD8); +				newFrame = kFrameLightOff; +				break; +			case kStageWorried: +				_barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0xC0, 0x30); +				newFrame = kFrameLightYellow; +				break; +			case kStageNervous: +				_barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0x78, 0x38); +				newFrame = kFrameLightOrange; +				break; +			case kStagePanicStricken: +				_barColor = g_system->getScreenFormat().RGBToColor(0xD8, 0x40, 0x38); +				newFrame = kFrameLightRed; +				break; +			default: +				error("no stage in energy monitor?"); +				break; +			} + +			_stage = newStage; +			uint32 oldFrame = _energyLight.getCurrentFrameIndex(); + +			if (!_calibrating) { +				if (oldFrame > newFrame || oldFrame == 0xffffffff || _dontFlash) { +					_energyLight.setCurrentFrameIndex(newFrame); +					_dontFlash = false; +				} else { +					_lightBlinker.startBlinking(&_energyLight, oldFrame, newFrame, 4, 1, 3); +					triggerRedraw(); +				} +			} +		} + +		Common::Rect r; +		calcLevelRect(r); +		if (r != _levelRect) { +			_levelRect = r; +			triggerRedraw(); +		} +	} +} + +void EnergyMonitor::calcLevelRect(Common::Rect &r) { +	if (getStop() == 0) { +		r = Common::Rect(); +	} else { +		getBounds(r); +		r.left = r.right - r.width() * (kMaxJMPEnergy - getTime()) / getStop(); +	} +} + +void EnergyMonitor::draw(const Common::Rect &r) { +	Common::Rect r2 = r.findIntersectingRect(_levelRect); + +	if (!r2.isEmpty()) { +		Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); +		screen->fillRect(r2, _barColor); +	} +} + +void EnergyMonitor::calibrateEnergyBar() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; +	 +	_calibrating = true; + +	vm->setEnergyDeathReason(-1); + +	uint32 numFrames = _energyLight.getNumFrames(); +	for (uint32 i = 1; i < numFrames; i++) { +		_energyLight.setCurrentFrameIndex(i); +		_energyLight.show(); +		vm->delayShell(1, 3); +		_energyLight.hide(); +		vm->delayShell(1, 3); +	} + +	_energyLight.setCurrentFrameIndex(0); +	_energyLight.hide(); + +	show(); +	setEnergyValue(0); +	setEnergyDrainRate(-(int32)kMaxJMPEnergy / 2); + +	// Make sure warning light is hidden... +	_energyLight.hide(); +	while (getCurrentEnergy() != (int32)kMaxJMPEnergy) { +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	vm->refreshDisplay(); +	setEnergyDrainRate(0); +	hide(); + +	_calibrating = false; +} + +void EnergyMonitor::restoreLastEnergyValue() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	_dontFlash = true; +	setEnergyValue(vm->getSavedEnergyValue()); +	vm->resetEnergyDeathReason(); +} + +void EnergyMonitor::saveCurrentEnergyValue() { +	((PegasusEngine *)g_engine)->setLastEnergyValue(getCurrentEnergy()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/energymonitor.h b/engines/pegasus/energymonitor.h new file mode 100644 index 0000000000..02377d515a --- /dev/null +++ b/engines/pegasus/energymonitor.h @@ -0,0 +1,111 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ENERGYMONITOR_H +#define PEGASUS_ENERGYMONITOR_H + +#include "pegasus/elements.h" + +namespace Pegasus { + +class Sprite; + +class Blinker : private IdlerTimeBase { +public: +	Blinker(); +	virtual ~Blinker() {} + +	void startBlinking(Sprite *sprite, int32 frame1, int32 frame2, uint32 numBlinks, TimeValue blinkDuration, TimeScale blinkScale); +	void stopBlinking(); + +protected: +	virtual void timeChanged(const TimeValue); + +	Sprite *_sprite; +	int32 _frame1; +	int32 _frame2; +	TimeValue _blinkDuration; +}; + +// Energy monitor constants. + +// These are in seconds. +// Max is two hours +static const uint32 kMaxJMPEnergy = 7200; + +static const uint32 kCasualEnergy = kMaxJMPEnergy * 100 / 100;      // 100% +static const uint32 kWorriedEnergy = kMaxJMPEnergy * 50 / 100;      // 50% +static const uint32 kNervousEnergy = kMaxJMPEnergy * 25 / 100;      // 25% +static const uint32 kPanicStrickenEnergy = kMaxJMPEnergy * 5 / 100; // 5% + +static const uint32 kFullEnergy = kCasualEnergy; + +static const uint32 kFrameLightOff = 0; +static const uint32 kFrameLightYellow = 1; +static const uint32 kFrameLightOrange = 2; +static const uint32 kFrameLightRed = 3; + +static const int kEnergyDrainNormal = 1; +static const int kMarsReactorEnergyDrainNoShield = 6; +static const int kMarsReactorEnergyDrainWithShield = 3; +static const int kWSCPoisonEnergyDrainWithDart = 20; +static const int kWSCPoisonEnergyDrainNoDart = 10; + +class EnergyMonitor : private IdlerAnimation { +public: +	EnergyMonitor(); +	virtual ~EnergyMonitor(); + +	void setEnergyValue(const uint32); +	void startEnergyDraining(); +	void setEnergyDrainRate(Common::Rational); +	Common::Rational getEnergyDrainRate(); +	void stopEnergyDraining(); +	void drainEnergy(const int32); +	int32 getCurrentEnergy(); + +	void restoreLastEnergyValue(); +	void saveCurrentEnergyValue(); + +	void calibrateEnergyBar(); + +protected: +	void timeChanged(const TimeValue); +	void calcLevelRect(Common::Rect &); +	void draw(const Common::Rect &); + +	uint32 _barColor; +	Common::Rect _levelRect; +	EnergyStage _stage; +	Sprite _energyLight; +	Blinker _lightBlinker; +	bool _calibrating, _dontFlash; +}; + +extern EnergyMonitor *g_energyMonitor; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/fader.cpp b/engines/pegasus/fader.cpp new file mode 100644 index 0000000000..77ad2cbe4e --- /dev/null +++ b/engines/pegasus/fader.cpp @@ -0,0 +1,218 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/fader.h" +#include "pegasus/pegasus.h" +#include "pegasus/sound.h" +#include "pegasus/util.h" + +namespace Pegasus { + +Fader::Fader() { +	_currentValue = 0; +	_currentFaderMove._numKnots = 0; +} + +void Fader::setFaderValue(const int32 newValue) { +	_currentValue = newValue; +} + +bool Fader::initFaderMove(const FaderMoveSpec &spec) {	 +	bool faderMoves = false; +	int32 value = 0; + +	if (spec._numKnots > 0) { +		stopFader(); +		value = spec._knots[0].knotValue; +		TimeValue startTime = spec._knots[0].knotTime; + +		if (startTime != 0xffffffff) { +			if (spec._numKnots > 1) { +				TimeValue stopTime = spec._knots[spec._numKnots - 1].knotTime; + +				if (spec._faderScale > 0) { +					if (stopTime > startTime) { +						for (uint32 i = 1; i < spec._numKnots; ++i) { +							if (spec._knots[i - 1].knotValue != spec._knots[i].knotValue) { +								faderMoves = true; +								break; +							} +						} + +						if (faderMoves) +							_currentFaderMove = spec; +					} else if (spec._knots[spec._numKnots - 1].knotValue != value) { +						value = spec._knots[spec._numKnots - 1].knotValue; +					} +				} +			} +		} +	} + +	setFaderValue(value); +	return faderMoves; +} + +void Fader::startFader(const FaderMoveSpec &spec) { +	if (initFaderMove(spec)) { +		setFlags(0); +		setScale(spec._faderScale); +		setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); +		setTime(spec._knots[0].knotTime); +		start(); +	} +} + +void Fader::startFaderSync(const FaderMoveSpec &spec) { +	if (initFaderMove(spec)) {	 +		setFlags(0); +		setScale(spec._faderScale); +		setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); +		setTime(spec._knots[0].knotTime); +		start(); + +		while (isFading()) { +			((PegasusEngine *)g_engine)->checkCallBacks(); +			useIdleTime(); +		} + +		// Once more, for good measure, to make sure that there are no boundary +		// condition problems. +		useIdleTime(); +		stopFader(); +	} +} + +void Fader::loopFader(const FaderMoveSpec &spec) { +	if (initFaderMove(spec)) { +		setFlags(kLoopTimeBase); +		setScale(spec._faderScale); +		setSegment(spec._knots[0].knotTime, spec._knots[spec._numKnots - 1].knotTime); +		setTime(spec._knots[0].knotTime); +		start(); +	} +} + +void Fader::stopFader() { +	stop(); +} + +void Fader::pauseFader() { +	stopFader(); +} + +void Fader::continueFader() { +	if (getTime() < getStop()) +		start(); +} + +void Fader::timeChanged(const TimeValue newTime) {	 +	if (_currentFaderMove._numKnots != 0) { +		uint32 i; +		for (i = 0; i < _currentFaderMove._numKnots; i++) +			if (_currentFaderMove._knots[i].knotTime > newTime) +				break; + +		int32 newValue; +		if (i == 0) +			newValue = _currentFaderMove._knots[0].knotValue; +		else if (i == _currentFaderMove._numKnots) +			newValue = _currentFaderMove._knots[i - 1].knotValue; +		else +			newValue = linearInterp(_currentFaderMove._knots[i - 1].knotTime, _currentFaderMove._knots[i].knotTime, newTime, _currentFaderMove._knots[i - 1].knotValue, _currentFaderMove._knots[i].knotValue); + +		if (newValue != _currentValue) +			setFaderValue(newValue); +	} +} + +void FaderMoveSpec::makeOneKnotFaderSpec(const int32 knotValue) { +	_numKnots = 1; +	_knots[0].knotTime = 0; +	_knots[0].knotValue = knotValue; +} + +void FaderMoveSpec::makeTwoKnotFaderSpec(const TimeScale faderScale, const TimeValue time1, const int32 value1, const TimeValue time2, const int32 value2) { +	_numKnots = 2; +	_faderScale = faderScale; +	_knots[0].knotTime = time1; +	_knots[0].knotValue = value1; +	_knots[1].knotTime = time2; +	_knots[1].knotValue = value2; +} + +void FaderMoveSpec::insertFaderKnot(const TimeValue knotTime, const int32 knotValue) {	 +	if (_numKnots != kMaxFaderKnots) { +		uint32 index; +		for (index = 0; index < _numKnots; index++) { +			if (knotTime == _knots[index].knotTime) { +				_knots[index].knotValue = knotValue; +				return; +			} else if (knotTime < _knots[index].knotTime) { +				break; +			} +		} + +		for (uint32 i = _numKnots; i > index; i--) +			_knots[i] = _knots[i - 1]; + +		_knots[index].knotTime = knotTime; +		_knots[index].knotValue = knotValue; +		_numKnots++; +	} +} + +void FaderAnimation::setFaderValue(const int32 newValue) { +	if (getFaderValue() != newValue) { +		Fader::setFaderValue(newValue); +		triggerRedraw(); +	} +} + +SoundFader::SoundFader() { +	_sound = 0; +	_masterVolume = 0xff; +} + +void SoundFader::attachSound(Sound *sound) { +	if (!sound && isFading()) +		stopFader(); + +	_sound = sound; +} + +void SoundFader::setFaderValue(const int32 newVolume) {	 +	if (_sound) +		_sound->setVolume((newVolume * _masterVolume) >> 8); + +	_currentValue = newVolume; +} + +void SoundFader::setMasterVolume(const uint16 masterVolume) { +	_masterVolume = masterVolume; +	setFaderValue(getFaderValue()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/fader.h b/engines/pegasus/fader.h new file mode 100644 index 0000000000..24ecae021b --- /dev/null +++ b/engines/pegasus/fader.h @@ -0,0 +1,130 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_FADER_H +#define PEGASUS_FADER_H + +#include "pegasus/elements.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Fader; + +class FaderMoveSpec { +friend class Fader; +public: +	FaderMoveSpec() { +		_faderScale = kDefaultTimeScale; +		_numKnots = 0; +	} + +	FaderMoveSpec(const TimeScale scale) { +		_faderScale = scale; +		_numKnots = 0; +	} + +	void setFaderScale(const TimeScale scale) { _faderScale = scale; } +	TimeScale getFaderScale() const { return _faderScale; } + +	void makeOneKnotFaderSpec(const int32); +	void makeTwoKnotFaderSpec(const TimeScale, const TimeValue, const int32, const TimeValue, const int32); + +	void insertFaderKnot(const TimeValue, const int32); + +	uint32 getNumKnots() const { return _numKnots; } +	TimeValue getNthKnotTime(const uint32 index) const { return _knots[index].knotTime; } +	int32 getNthKnotValue(const uint32 index) const { return _knots[index].knotValue; } + +protected: +	struct FaderKnot { +		TimeValue knotTime; +		int32 knotValue; +	}; +	 +	TimeScale _faderScale; +	uint32 _numKnots; + +	static const uint32 kMaxFaderKnots = 20; +	FaderKnot _knots[kMaxFaderKnots]; +}; + +class Fader : public IdlerTimeBase { +public: +	Fader(); +	virtual ~Fader() {} + +	virtual void setFaderValue(const int32); +	int32 getFaderValue() const { return _currentValue; } +	virtual void startFader(const FaderMoveSpec &); +	virtual void startFaderSync(const FaderMoveSpec &); +	virtual void loopFader(const FaderMoveSpec &); +	virtual void stopFader(); +	virtual bool isFading() { return isRunning(); } + +	void pauseFader(); +	void continueFader(); + +	void getCurrentFaderMove(FaderMoveSpec &spec) { spec = _currentFaderMove; } + +protected: +	bool initFaderMove(const FaderMoveSpec &); +	virtual void timeChanged(const TimeValue); +	 +	int32 _currentValue; +	FaderMoveSpec _currentFaderMove; +}; + +class FaderAnimation : public DisplayElement, public Fader { +public: +	FaderAnimation(const DisplayElementID id) : DisplayElement(id) {} +	virtual ~FaderAnimation() {} + +	void setFaderValue(const int32); +}; + +class Sound; + +class SoundFader : public Fader { +friend class Sound; +public: +	SoundFader(); +	virtual ~SoundFader() {} + +	void setFaderValue(const int32); + +	void setMasterVolume(const uint16); +	uint16 getMasterVolume() const { return _masterVolume; } + +protected: +	void attachSound(Sound *); + +	Sound *_sound; +	uint16 _masterVolume; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/gamestate.cpp b/engines/pegasus/gamestate.cpp new file mode 100644 index 0000000000..601960e5de --- /dev/null +++ b/engines/pegasus/gamestate.cpp @@ -0,0 +1,2359 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/error.h" +#include "common/stream.h" + +#include "pegasus/constants.h" +#include "pegasus/gamestate.h" +#include "pegasus/scoring.h" + +namespace Common { +DECLARE_SINGLETON(Pegasus::GameStateManager); +} + +namespace Pegasus { + +Common::Error GameStateManager::writeGameState(Common::WriteStream *stream) { +	stream->writeUint16BE(_currentNeighborhood); +	stream->writeUint16BE(_currentRoom); +	stream->writeByte(_currentDirection); +	stream->writeUint16BE(_nexNeighborhoodID); +	stream->writeUint16BE(_nextRoomID); +	stream->writeByte(_nextDirection); +	stream->writeUint16BE(_lastNeighborhood); +	stream->writeUint16BE(_lastRoom); +	stream->writeByte(_lastDirection); +	stream->writeUint16BE(_openDoorRoom); +	stream->writeByte(_openDoorDirection); + +	_globalFlags.writeToStream(stream); +	_scoringFlags.writeToStream(stream); +	_itemTakenFlags.writeToStream(stream); + +	writeCaldoriaState(stream); +	writeTSAState(stream); +	writePrehistoricState(stream); +	writeNoradState(stream); +	writeMarsState(stream); +	writeWSCState(stream); + +	if (stream->err()) +		return Common::kWritingFailed; +	 +	return Common::kNoError; +} + +Common::Error GameStateManager::readGameState(Common::ReadStream *stream) { +	_currentNeighborhood = stream->readUint16BE(); +	_currentRoom = stream->readUint16BE(); +	_currentDirection = stream->readByte(); +	_nexNeighborhoodID = stream->readUint16BE(); +	_nextRoomID = stream->readUint16BE(); +	_nextDirection = stream->readByte(); +	_lastNeighborhood = stream->readUint16BE(); +	_lastRoom = stream->readUint16BE(); +	_lastDirection = stream->readByte(); +	_openDoorRoom = stream->readUint16BE(); +	_openDoorDirection = stream->readByte();	 + +	_globalFlags.readFromStream(stream); +	_scoringFlags.readFromStream(stream); +	_itemTakenFlags.readFromStream(stream); +	 +	readCaldoriaState(stream); +	readTSAState(stream); +	readPrehistoricState(stream); +	readNoradState(stream); +	readMarsState(stream); +	readWSCState(stream); + +	if (stream->err()) +		return Common::kReadingFailed; +	 +	return Common::kNoError; +} + +void GameStateManager::resetGameState() { +	_currentNeighborhood = kNoNeighborhoodID; +	_currentRoom = kNoRoomID; +	_currentDirection = kNoDirection; +	_nexNeighborhoodID = kNoNeighborhoodID; +	_nextRoomID = kNoRoomID; +	_nextDirection = kNoDirection; +	_lastNeighborhood = kNoNeighborhoodID; +	_lastRoom = kNoRoomID; +	_lastDirection = kNoDirection; +	_openDoorRoom = kNoRoomID; +	_openDoorDirection = kNoDirection; + +	_globalFlags.clearAllFlags(); +	_scoringFlags.clearAllFlags(); +	_itemTakenFlags.clearAllFlags(); + +	resetCaldoriaState(); +	resetTSAState(); +	resetPrehistoricState(); +	resetNoradState(); +	resetMarsState(); +	resetWSCState(); +} + +void GameStateManager::getCurrentLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) { +	neighborhood = _currentNeighborhood; +	room = _currentRoom; +	direction = _currentDirection; +} + +void GameStateManager::setCurrentLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { +	_lastNeighborhood = _currentNeighborhood; +	_lastRoom = _currentRoom; +	_lastDirection = _currentDirection; +	_currentNeighborhood = neighborhood; +	_currentRoom = room; +	_currentDirection = direction; +} + +NeighborhoodID GameStateManager::getCurrentNeighborhood() { +	return _currentNeighborhood; +} + +void GameStateManager::setCurrentNeighborhood(const NeighborhoodID neighborhood) { +	_lastNeighborhood = _currentNeighborhood; +	_currentNeighborhood = neighborhood; +} + +RoomID GameStateManager::getCurrentRoom() { +	return _currentRoom; +} + +void GameStateManager::setCurrentRoom(const RoomID room) { +	_lastRoom = _currentRoom; +	_currentRoom = room; +} + +DirectionConstant GameStateManager::getCurrentDirection() { +	return _currentDirection; +} + +void GameStateManager::setCurrentDirection(const DirectionConstant direction) { +	_lastDirection = _currentDirection; +	_currentDirection = direction; +} + +RoomViewID GameStateManager::getCurrentRoomAndView() { +	return MakeRoomView(_currentRoom, _currentDirection); +} + +void GameStateManager::getNextLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) { +	neighborhood = _nexNeighborhoodID; +	room = _nextRoomID; +	direction = _nextDirection; +} + +void GameStateManager::setNextLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { +	_nexNeighborhoodID = neighborhood; +	_nextRoomID = room; +	_nextDirection = direction; +} + +NeighborhoodID GameStateManager::getNextNeighborhood() { +	return _nexNeighborhoodID; +} + +void GameStateManager::setNextNeighborhood(const NeighborhoodID neighborhood) { +	_nexNeighborhoodID = neighborhood; +} + +RoomID GameStateManager::getNextRoom() { +	return _nextRoomID; +} + +void GameStateManager::setNextRoom(const RoomID room) { +	_nextRoomID = room; +} + +DirectionConstant GameStateManager::getNextDirection() { +	return _nextDirection; +} + +void GameStateManager::setNextDirection(const DirectionConstant direction) { +	_nextDirection = direction; +} + +void GameStateManager::getLastLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) { +	neighborhood = _currentNeighborhood; +	room = _currentRoom; +	direction = _currentDirection; +} + +void GameStateManager::setLastLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { +	_currentNeighborhood = neighborhood; +	_currentRoom = room; +	_currentDirection = direction; +} + +NeighborhoodID GameStateManager::getLastNeighborhood() { +	return _lastNeighborhood; +} + +void GameStateManager::setLastNeighborhood(const NeighborhoodID neighborhood) { +	_lastNeighborhood = neighborhood; +} + +RoomID GameStateManager::getLastRoom() { +	return _lastRoom; +} + +void GameStateManager::setLastRoom(const RoomID room) { +	_lastRoom = room; +} + +DirectionConstant GameStateManager::getLastDirection() { +	return _lastDirection; +} + +void GameStateManager::setLastDirection(const DirectionConstant direction) { +	_lastDirection = direction; +} + +RoomViewID GameStateManager::getLastRoomAndView() { +	return MakeRoomView(_lastRoom, _lastDirection); +} + +void GameStateManager::getOpenDoorLocation(RoomID &room, DirectionConstant &direction) { +	room = _openDoorRoom; +	direction = _openDoorDirection; +} + +void GameStateManager::setOpenDoorLocation(const RoomID room, const DirectionConstant direction) { +	_openDoorRoom = room; +	_openDoorDirection = direction; +} + +RoomID GameStateManager::getOpenDoorRoom() { +	return _openDoorRoom; +} + +void GameStateManager::setOpenDoorRoom(const RoomID room) { +	_openDoorRoom = room; +} + +DirectionConstant GameStateManager::getOpenDoorDirection() { +	return _openDoorDirection; +} + +void GameStateManager::setOpenDoorDirection(const DirectionConstant direction) { +	_openDoorDirection = direction; +} + +RoomViewID GameStateManager::getDoorOpenRoomAndView() { +	return MakeRoomView(_openDoorRoom, _openDoorDirection); +} + +bool GameStateManager::isCurrentDoorOpen() { +	return _openDoorRoom == _currentRoom && _openDoorDirection == _currentDirection; +} + +GameScoreType GameStateManager::getCaldoriaTSAScore() { +	GameScoreType result = 0; + +	if (_scoringFlags.getFlag(kScoringSawINNFlag)) +		result += kSawINNScore; +	if (_scoringFlags.getFlag(kScoringTookShowerFlag)) +		result += kTookShowerScore; +	if (_scoringFlags.getFlag(kScoringFixedHairFlag)) +		result += kFixedHairScore; +	if (_scoringFlags.getFlag(kScoringGotKeyCardFlag)) +		result += kGotKeyCardScore; +	if (_scoringFlags.getFlag(kScoringReadPaperFlag)) +		result += kReadPaperScore; +	if (_scoringFlags.getFlag(kScoringLookThroughTelescopeFlag)) +		result += kLookThroughTelescopeScore; +	if (_scoringFlags.getFlag(kScoringSawCaldoriaKioskFlag)) +		result += kSawCaldoriaKioskScore; +	if (_scoringFlags.getFlag(kScoringGoToTSAFlag)) +		result += kGoToTSAScore; +	if (_scoringFlags.getFlag(kScoringEnterTSAFlag)) +		result += kEnterTSAScore; +	if (_scoringFlags.getFlag(kScoringSawBust1Flag)) +		result += kSawBust1Score; +	if (_scoringFlags.getFlag(kScoringSawBust2Flag)) +		result += kSawBust2Score; +	if (_scoringFlags.getFlag(kScoringSawBust3Flag)) +		result += kSawBust3Score; +	if (_scoringFlags.getFlag(kScoringSawBust4Flag)) +		result += kSawBust4Score; +	if (_scoringFlags.getFlag(kScoringSawBust5Flag)) +		result += kSawBust5Score; +	if (_scoringFlags.getFlag(kScoringSawBust6Flag)) +		result += kSawBust6Score; +	if (_scoringFlags.getFlag(kScoringSawTheoryFlag)) +		result += kSawTheoryScore; +	if (_scoringFlags.getFlag(kScoringSawBackgroundFlag)) +		result += kSawBackgroundScore; +	if (_scoringFlags.getFlag(kScoringSawProcedureFlag)) +		result += kSawProcedureScore; +	if (_scoringFlags.getFlag(kScoringGotJourneymanKeyFlag)) +		result += kGotJourneymanKeyScore; +	if (_scoringFlags.getFlag(kScoringGotPegasusBiochipFlag)) +		result += kGotPegasusBiochipScore; +	if (_scoringFlags.getFlag(kScoringGotBiosuitFlag)) +		result += kGotBiosuitScore; +	if (_scoringFlags.getFlag(kScoringGoToPrehistoricFlag)) +		result += kGoToPrehistoricScore; +	if (_scoringFlags.getFlag(kScoringPutLogInReaderFlag)) +		result += kPutLogInReaderScore; +	if (_scoringFlags.getFlag(kScoringSawCaldoriaNormalFlag)) +		result += kSawCaldoriaNormalScore; +	if (_scoringFlags.getFlag(kScoringSawCaldoriaAlteredFlag)) +		result += kSawCaldoriaAlteredScore; +	if (_scoringFlags.getFlag(kScoringSawNoradNormalFlag)) +		result += kSawNoradNormalScore; +	if (_scoringFlags.getFlag(kScoringSawNoradAlteredFlag)) +		result += kSawNoradAlteredScore; +	if (_scoringFlags.getFlag(kScoringSawMarsNormalFlag)) +		result += kSawMarsNormalScore; +	if (_scoringFlags.getFlag(kScoringSawMarsAlteredFlag)) +		result += kSawMarsAlteredScore; +	if (_scoringFlags.getFlag(kScoringSawWSCNormalFlag)) +		result += kSawWSCNormalScore; +	if (_scoringFlags.getFlag(kScoringSawWSCAlteredFlag)) +		result += kSawWSCAlteredScore; +	if (_scoringFlags.getFlag(kScoringWentToReadyRoom2Flag)) +		result += kWentToReadyRoom2Score; +	if (_scoringFlags.getFlag(kScoringWentAfterSinclairFlag)) +		result += kWentAfterSinclairScore; +	if (_scoringFlags.getFlag(kScoringUsedCardBombFlag)) +		result += kUsedCardBombScore; +	if (_scoringFlags.getFlag(kScoringShieldedCardBombFlag)) +		result += kShieldedCardBombScore; +	if (_scoringFlags.getFlag(kScoringStunnedSinclairFlag)) +		result += kStunnedSinclairScore; +	if (_scoringFlags.getFlag(kScoringDisarmedNukeFlag)) +		result += kDisarmedNukeScore; + +	return result; +} + +GameScoreType GameStateManager::getPrehistoricScore() { +	GameScoreType result = 0; + +	if (_scoringFlags.getFlag(kScoringThrewBreakerFlag)) +		result += kThrewBreakerScore; +	if (_scoringFlags.getFlag(kScoringExtendedBridgeFlag)) +		result += kExtendedBridgeScore; +	if (_scoringFlags.getFlag(kScoringGotHistoricalLogFlag)) +		result += kGotHistoricalLogScore; +	if (_scoringFlags.getFlag(kScoringFinishedPrehistoricFlag)) +		result += kFinishedPrehistoricScore; + +	return result; +} + +GameScoreType GameStateManager::getMarsScore() { +	GameScoreType result = 0; + +	if (_scoringFlags.getFlag(kScoringThrownByRobotFlag)) +		result += kThrownByRobotScore; +	if (_scoringFlags.getFlag(kScoringGotMarsCardFlag)) +		result += kGotMarsCardScore; +	if (_scoringFlags.getFlag(kScoringSawMarsKioskFlag)) +		result += kSawMarsKioskScore; +	if (_scoringFlags.getFlag(kScoringSawTransportMapFlag)) +		result += kSawTransportMapScore; +	if (_scoringFlags.getFlag(kScoringGotCrowBarFlag)) +		result += kGotCrowBarScore; +	if (_scoringFlags.getFlag(kScoringTurnedOnTransportFlag)) +		result += kTurnedOnTransportScore; +	if (_scoringFlags.getFlag(kScoringGotOxygenMaskFlag)) +		result += kGotOxygenMaskScore; +	if (_scoringFlags.getFlag(kScoringAvoidedRobotFlag)) +		result += kAvoidedRobotScore; +	if (_scoringFlags.getFlag(kScoringActivatedPlatformFlag)) +		result += kActivatedPlatformScore; +	if (_scoringFlags.getFlag(kScoringUsedLiquidNitrogenFlag)) +		result += kUsedLiquidNitrogenScore; +	if (_scoringFlags.getFlag(kScoringUsedCrowBarFlag)) +		result += kUsedCrowBarScore; +	if (_scoringFlags.getFlag(kScoringFoundCardBombFlag)) +		result += kFoundCardBombScore; +	if (_scoringFlags.getFlag(kScoringDisarmedCardBombFlag)) +		result += kDisarmedCardBombScore; +	if (_scoringFlags.getFlag(kScoringGotCardBombFlag)) +		result += kGotCardBombScore; +	if (_scoringFlags.getFlag(kScoringThreadedMazeFlag)) +		result += kThreadedMazeScore; +	if (_scoringFlags.getFlag(kScoringThreadedGearRoomFlag)) +		result += kThreadedGearRoomScore; +	if (_scoringFlags.getFlag(kScoringEnteredShuttleFlag)) +		result += kEnteredShuttleScore; +	if (_scoringFlags.getFlag(kScoringEnteredLaunchTubeFlag)) +		result += kEnteredLaunchTubeScore; +	if (_scoringFlags.getFlag(kScoringStoppedRobotsShuttleFlag)) +		result += kStoppedRobotsShuttleScore; +	if (_scoringFlags.getFlag(kScoringGotMarsOpMemChipFlag)) +		result += kGotMarsOpMemChipScore; +	if (_scoringFlags.getFlag(kScoringFinishedMarsFlag)) +		result += kFinishedMarsScore; + +	return result; +} + +GameScoreType GameStateManager::getNoradScore() { +	GameScoreType result = 0; + +	if (_scoringFlags.getFlag(kScoringSawSecurityMonitorFlag)) +		result += kSawSecurityMonitorScore; +	if (_scoringFlags.getFlag(kScoringFilledOxygenCanisterFlag)) +		result += kFilledOxygenCanisterScore; +	if (_scoringFlags.getFlag(kScoringFilledArgonCanisterFlag)) +		result += kFilledArgonCanisterScore; +	if (_scoringFlags.getFlag(kScoringSawUnconsciousOperatorFlag)) +		result += kSawUnconsciousOperatorScore; +	if (_scoringFlags.getFlag(kScoringWentThroughPressureDoorFlag)) +		result += kWentThroughPressureDoorScore; +	if (_scoringFlags.getFlag(kScoringPreppedSubFlag)) +		result += kPreppedSubScore; +	if (_scoringFlags.getFlag(kScoringEnteredSubFlag)) +		result += kEnteredSubScore; +	if (_scoringFlags.getFlag(kScoringExitedSubFlag)) +		result += kExitedSubScore; +	if (_scoringFlags.getFlag(kScoringSawRobotAt54NorthFlag)) +		result += kSawRobotAt54NorthScore; +	if (_scoringFlags.getFlag(kScoringPlayedWithClawFlag)) +		result += kPlayedWithClawScore; +	if (_scoringFlags.getFlag(kScoringUsedRetinalChipFlag)) +		result += kUsedRetinalChipScore; +	if (_scoringFlags.getFlag(kScoringFinishedGlobeGameFlag)) +		result += kFinishedGlobeGameScore; +	if (_scoringFlags.getFlag(kScoringStoppedNoradRobotFlag)) +		result += kStoppedNoradRobotScore; +	if (_scoringFlags.getFlag(kScoringGotNoradOpMemChipFlag)) +		result += kGotNoradOpMemChipScore; +	if (_scoringFlags.getFlag(kScoringFinishedNoradFlag)) +		result += kFinishedNoradScore; + +	return result; +} + +GameScoreType GameStateManager::getWSCScore() { +	GameScoreType result = 0; + +	if (_scoringFlags.getFlag(kScoringRemovedDartFlag)) +		result += kRemovedDartScore; +	if (_scoringFlags.getFlag(kScoringAnalyzedDartFlag)) +		result += kAnalyzedDartScore; +	if (_scoringFlags.getFlag(kScoringBuiltAntidoteFlag)) +		result += kBuiltAntidoteScore; +	if (_scoringFlags.getFlag(kScoringGotSinclairKeyFlag)) +		result += kGotSinclairKeyScore; +	if (_scoringFlags.getFlag(kScoringGotArgonCanisterFlag)) +		result += kGotArgonCanisterScore; +	if (_scoringFlags.getFlag(kScoringGotNitrogenCanisterFlag)) +		result += kGotNitrogenCanisterScore; +	if (_scoringFlags.getFlag(kScoringPlayedWithMessagesFlag)) +		result += kPlayedWithMessagesScore; +	if (_scoringFlags.getFlag(kScoringSawMorphExperimentFlag)) +		result += kSawMorphExperimentScore; +	if (_scoringFlags.getFlag(kScoringEnteredSinclairOfficeFlag)) +		result += kEnteredSinclairOfficeScore; +	if (_scoringFlags.getFlag(kScoringSawBrochureFlag)) +		result += kSawBrochureScore; +	if (_scoringFlags.getFlag(kScoringSawSinclairEntry1Flag)) +		result += kSawSinclairEntry1Score; +	if (_scoringFlags.getFlag(kScoringSawSinclairEntry2Flag)) +		result += kSawSinclairEntry2Score; +	if (_scoringFlags.getFlag(kScoringSawSinclairEntry3Flag)) +		result += kSawSinclairEntry3Score; +	if (_scoringFlags.getFlag(kScoringSawWSCDirectoryFlag)) +		result += kSawWSCDirectoryScore; +	if (_scoringFlags.getFlag(kScoringUsedCrowBarInWSCFlag)) +		result += kUsedCrowBarInWSCScore; +	if (_scoringFlags.getFlag(kScoringFinishedPlasmaDodgeFlag)) +		result += kFinishedPlasmaDodgeScore; +	if (_scoringFlags.getFlag(kScoringOpenedCatwalkFlag)) +		result += kOpenedCatwalkScore; +	if (_scoringFlags.getFlag(kScoringStoppedWSCRobotFlag)) +		result += kStoppedWSCRobotScore; +	if (_scoringFlags.getFlag(kScoringGotWSCOpMemChipFlag)) +		result += kGotWSCOpMemChipScore; +	if (_scoringFlags.getFlag(kScoringFinishedWSCFlag)) +		result += kFinishedWSCScore; + +	return result; +} + +GameScoreType GameStateManager::getGandhiScore() { +	GameScoreType result = 0; + +	if (_scoringFlags.getFlag(kScoringMarsGandhiFlag)) +		result += kMarsGandhiScore; +	if (_scoringFlags.getFlag(kScoringNoradGandhiFlag)) +		result += kNoradGandhiScore; +	if (_scoringFlags.getFlag(kScoringWSCGandhiFlag)) +		result += kWSCGandhiScore; + +	return result; +} + +GameScoreType GameStateManager::getTotalScore() { +	return	getCaldoriaTSAScore() + +			getPrehistoricScore() + +			getMarsScore() + +			getNoradScore() + +			getWSCScore() + +			getGandhiScore(); +} + +///////////////////////////////////////////// +// +// Caldoria data +	 +void GameStateManager::writeCaldoriaState(Common::WriteStream *stream) { +	_caldoriaFlags.writeToStream(stream); +	stream->writeUint32BE(_caldoriaFuseTimeLimit); +} + +void GameStateManager::readCaldoriaState(Common::ReadStream *stream) { +	_caldoriaFlags.readFromStream(stream); +	_caldoriaFuseTimeLimit = stream->readUint32BE(); +} + +void GameStateManager::resetCaldoriaState() { +	_caldoriaFlags.clearAllFlags(); +	_caldoriaFuseTimeLimit = 0; +} + +///////////////////////////////////////////// +// +// TSA data + +void GameStateManager::writeTSAState(Common::WriteStream *stream) { +	_TSAFlags.writeToStream(stream); +	stream->writeUint32BE(_TSARipTimerTime); +	stream->writeUint32BE(_TSAFuseTimeLimit); +	stream->writeByte(_TSAState); +	stream->writeByte(_T0BMonitorMode); +	stream->writeUint32BE(_T0BMonitorStart); +} + +void GameStateManager::readTSAState(Common::ReadStream *stream) { +	_TSAFlags.readFromStream(stream); +	_TSARipTimerTime = stream->readUint32BE(); +	_TSAFuseTimeLimit = stream->readUint32BE(); +	_TSAState = stream->readByte(); +	_T0BMonitorMode = stream->readByte(); +	_T0BMonitorStart = stream->readUint32BE(); +} + +void GameStateManager::resetTSAState() { +	_TSAFlags.clearAllFlags(); +	_TSAState = 0; +	_T0BMonitorMode = 0; +	_T0BMonitorStart = 0; +	_TSARipTimerTime = 0; +	_TSAFuseTimeLimit = kTSAUncreatedTimeLimit; +} + +///////////////////////////////////////////// +// +// Prehistoric data + +void GameStateManager::writePrehistoricState(Common::WriteStream *stream) { +	_prehistoricFlags.writeToStream(stream); +} + +void GameStateManager::readPrehistoricState(Common::ReadStream *stream) { +	_prehistoricFlags.readFromStream(stream); +} + +void GameStateManager::resetPrehistoricState() { +	_prehistoricFlags.clearAllFlags(); +} + +///////////////////////////////////////////// +// +// Norad data + +void GameStateManager::writeNoradState(Common::WriteStream *stream) { +	_noradFlags.writeToStream(stream); +	stream->writeUint16BE(_noradSubRoomPressure); +	stream->writeByte(_noradSubPrepState); +} + +void GameStateManager::readNoradState(Common::ReadStream *stream) { +	_noradFlags.readFromStream(stream); +	_noradSubRoomPressure = stream->readUint16BE(); +	_noradSubPrepState = (NoradSubPrepState)stream->readByte(); +} + +void GameStateManager::resetNoradState() { +	_noradFlags.clearAllFlags(); +	_noradSubRoomPressure = 9; +	_noradSubPrepState = kSubNotPrepped; +} + +///////////////////////////////////////////// +// +// Mars data + +void GameStateManager::writeMarsState(Common::WriteStream *stream) { +	_marsFlags.writeToStream(stream); +} + +void GameStateManager::readMarsState(Common::ReadStream *stream) { +	_marsFlags.readFromStream(stream); +} + +void GameStateManager::resetMarsState() { +	_marsFlags.clearAllFlags(); +} + +///////////////////////////////////////////// +// +// WSC data + +void GameStateManager::writeWSCState(Common::WriteStream *stream) { +	_WSCFlags.writeToStream(stream); +} + +void GameStateManager::readWSCState(Common::ReadStream *stream) { +	_WSCFlags.readFromStream(stream); +} + +void GameStateManager::resetWSCState() { +	_WSCFlags.clearAllFlags(); +} + +void GameStateManager::setScoringSawINN(const bool flag) { +	_scoringFlags.setFlag(kScoringSawINNFlag, flag); +} + +void GameStateManager::setScoringTookShower(const bool flag) { +	_scoringFlags.setFlag(kScoringTookShowerFlag, flag); +} + +void GameStateManager::setScoringFixedHair(const bool flag) { +	_scoringFlags.setFlag(kScoringFixedHairFlag, flag); +} + +void GameStateManager::setScoringGotKeyCard(const bool flag) { +	_scoringFlags.setFlag(kScoringGotKeyCardFlag, flag); +} + +void GameStateManager::setScoringReadPaper(const bool flag) { +	_scoringFlags.setFlag(kScoringReadPaperFlag, flag); +} + +void GameStateManager::setScoringLookThroughTelescope(const bool flag) { +	_scoringFlags.setFlag(kScoringLookThroughTelescopeFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaKiosk(const bool flag) { +	_scoringFlags.setFlag(kScoringSawCaldoriaKioskFlag, flag); +} + +void GameStateManager::setScoringGoToTSA(const bool flag) { +	_scoringFlags.setFlag(kScoringGoToTSAFlag, flag); +} + +void GameStateManager::setScoringEnterTSA(const bool flag) { +	_scoringFlags.setFlag(kScoringEnterTSAFlag, flag); +} + +void GameStateManager::setScoringSawBust1(const bool flag) { +	_scoringFlags.setFlag(kScoringSawBust1Flag, flag); +} + +void GameStateManager::setScoringSawBust2(const bool flag) { +	_scoringFlags.setFlag(kScoringSawBust2Flag, flag); +} + +void GameStateManager::setScoringSawBust3(const bool flag) { +	_scoringFlags.setFlag(kScoringSawBust3Flag, flag); +} + +void GameStateManager::setScoringSawBust4(const bool flag) { +	_scoringFlags.setFlag(kScoringSawBust4Flag, flag); +} + +void GameStateManager::setScoringSawBust5(const bool flag) { +	_scoringFlags.setFlag(kScoringSawBust5Flag, flag); +} + +void GameStateManager::setScoringSawBust6(const bool flag) { +	_scoringFlags.setFlag(kScoringSawBust6Flag, flag); +} + +void GameStateManager::setScoringSawTheory(const bool flag) { +	_scoringFlags.setFlag(kScoringSawTheoryFlag, flag); +} + +void GameStateManager::setScoringSawBackground(const bool flag) { +	_scoringFlags.setFlag(kScoringSawBackgroundFlag, flag); +} + +void GameStateManager::setScoringSawProcedure(const bool flag) { +	_scoringFlags.setFlag(kScoringSawProcedureFlag, flag); +} + +void GameStateManager::setScoringGotJourneymanKey(const bool flag) { +	_scoringFlags.setFlag(kScoringGotJourneymanKeyFlag, flag); +} + +void GameStateManager::setScoringGotPegasusBiochip(const bool flag) { +	_scoringFlags.setFlag(kScoringGotPegasusBiochipFlag, flag); +} + +void GameStateManager::setScoringGotBiosuit(const bool flag) { +	_scoringFlags.setFlag(kScoringGotBiosuitFlag, flag); +} + +void GameStateManager::setScoringGoToPrehistoric(const bool flag) { +	_scoringFlags.setFlag(kScoringGoToPrehistoricFlag, flag); +} + +void GameStateManager::setScoringPutLogInReader(const bool flag) { +	_scoringFlags.setFlag(kScoringPutLogInReaderFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaNormal(const bool flag) { +	_scoringFlags.setFlag(kScoringSawCaldoriaNormalFlag, flag); +} + +void GameStateManager::setScoringSawCaldoriaAltered(const bool flag) { +	_scoringFlags.setFlag(kScoringSawCaldoriaAlteredFlag, flag); +} + +void GameStateManager::setScoringSawNoradNormal(const bool flag) { +	_scoringFlags.setFlag(kScoringSawNoradNormalFlag, flag); +} + +void GameStateManager::setScoringSawNoradAltered(const bool flag) { +	_scoringFlags.setFlag(kScoringSawNoradAlteredFlag, flag); +} + +void GameStateManager::setScoringSawMarsNormal(const bool flag) { +	_scoringFlags.setFlag(kScoringSawMarsNormalFlag, flag); +} + +void GameStateManager::setScoringSawMarsAltered(const bool flag) { +	_scoringFlags.setFlag(kScoringSawMarsAlteredFlag, flag); +} + +void GameStateManager::setScoringSawWSCNormal(const bool flag) { +	_scoringFlags.setFlag(kScoringSawWSCNormalFlag, flag); +} + +void GameStateManager::setScoringSawWSCAltered(const bool flag) { +	_scoringFlags.setFlag(kScoringSawWSCAlteredFlag, flag); +} + +void GameStateManager::setScoringWentToReadyRoom2(const bool flag) { +	_scoringFlags.setFlag(kScoringWentToReadyRoom2Flag, flag); +} + +void GameStateManager::setScoringWentAfterSinclair(const bool flag) { +	_scoringFlags.setFlag(kScoringWentAfterSinclairFlag, flag); +} + +void GameStateManager::setScoringUsedCardBomb(const bool flag) { +	_scoringFlags.setFlag(kScoringUsedCardBombFlag, flag); +} + +void GameStateManager::setScoringShieldedCardBomb(const bool flag) { +	_scoringFlags.setFlag(kScoringShieldedCardBombFlag, flag); +} + +void GameStateManager::setScoringStunnedSinclair(const bool flag) { +	_scoringFlags.setFlag(kScoringStunnedSinclairFlag, flag); +} + +void GameStateManager::setScoringDisarmedNuke(const bool flag) { +	_scoringFlags.setFlag(kScoringDisarmedNukeFlag, flag); +} + +void GameStateManager::setScoringThrewBreaker(const bool flag) { +	_scoringFlags.setFlag(kScoringThrewBreakerFlag, flag); +} + +void GameStateManager::setScoringExtendedBridge(const bool flag) { +	_scoringFlags.setFlag(kScoringExtendedBridgeFlag, flag); +} + +void GameStateManager::setScoringGotHistoricalLog(const bool flag) { +	_scoringFlags.setFlag(kScoringGotHistoricalLogFlag, flag); +} + +void GameStateManager::setScoringFinishedPrehistoric(const bool flag) { +	_scoringFlags.setFlag(kScoringFinishedPrehistoricFlag, flag); +} + +void GameStateManager::setScoringThrownByRobot(const bool flag) { +	_scoringFlags.setFlag(kScoringThrownByRobotFlag, flag); +} + +void GameStateManager::setScoringGotMarsCard(const bool flag) { +	_scoringFlags.setFlag(kScoringGotMarsCardFlag, flag); +} + +void GameStateManager::setScoringSawMarsKiosk(const bool flag) { +	_scoringFlags.setFlag(kScoringSawMarsKioskFlag, flag); +} + +void GameStateManager::setScoringSawTransportMap(const bool flag) { +	_scoringFlags.setFlag(kScoringSawTransportMapFlag, flag); +} + +void GameStateManager::setScoringGotCrowBar(const bool flag) { +	_scoringFlags.setFlag(kScoringGotCrowBarFlag, flag); +} + +void GameStateManager::setScoringTurnedOnTransport(const bool flag) { +	_scoringFlags.setFlag(kScoringTurnedOnTransportFlag, flag); +} + +void GameStateManager::setScoringGotOxygenMask(const bool flag) { +	_scoringFlags.setFlag(kScoringGotOxygenMaskFlag, flag); +} + +void GameStateManager::setScoringAvoidedRobot(const bool flag) { +	_scoringFlags.setFlag(kScoringAvoidedRobotFlag, flag); +} + +void GameStateManager::setScoringActivatedPlatform(const bool flag) { +	_scoringFlags.setFlag(kScoringActivatedPlatformFlag, flag); +} + +void GameStateManager::setScoringUsedLiquidNitrogen(const bool flag) { +	_scoringFlags.setFlag(kScoringUsedLiquidNitrogenFlag, flag); +} + +void GameStateManager::setScoringUsedCrowBar(const bool flag) { +	_scoringFlags.setFlag(kScoringUsedCrowBarFlag, flag); +} + +void GameStateManager::setScoringFoundCardBomb(const bool flag) { +	_scoringFlags.setFlag(kScoringFoundCardBombFlag, flag); +} + +void GameStateManager::setScoringDisarmedCardBomb(const bool flag) { +	_scoringFlags.setFlag(kScoringDisarmedCardBombFlag, flag); +} + +void GameStateManager::setScoringGotCardBomb(const bool flag) { +	_scoringFlags.setFlag(kScoringGotCardBombFlag, flag); +} + +void GameStateManager::setScoringThreadedMaze(const bool flag) { +	_scoringFlags.setFlag(kScoringThreadedMazeFlag, flag); +} + +void GameStateManager::setScoringThreadedGearRoom(const bool flag) { +	_scoringFlags.setFlag(kScoringThreadedGearRoomFlag, flag); +} + +void GameStateManager::setScoringEnteredShuttle(const bool flag) { +	_scoringFlags.setFlag(kScoringEnteredShuttleFlag, flag); +} + +void GameStateManager::setScoringEnteredLaunchTube(const bool flag) { +	_scoringFlags.setFlag(kScoringEnteredLaunchTubeFlag, flag); +} + +void GameStateManager::setScoringStoppedRobotsShuttle(const bool flag) { +	_scoringFlags.setFlag(kScoringStoppedRobotsShuttleFlag, flag); +} + +void GameStateManager::setScoringGotMarsOpMemChip(const bool flag) { +	_scoringFlags.setFlag(kScoringGotMarsOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedMars(const bool flag) { +	_scoringFlags.setFlag(kScoringFinishedMarsFlag, flag); +} + +void GameStateManager::setScoringSawSecurityMonitor(const bool flag) { +	_scoringFlags.setFlag(kScoringSawSecurityMonitorFlag, flag); +} + +void GameStateManager::setScoringFilledOxygenCanister(const bool flag) { +	_scoringFlags.setFlag(kScoringFilledOxygenCanisterFlag, flag); +} + +void GameStateManager::setScoringFilledArgonCanister(const bool flag) { +	_scoringFlags.setFlag(kScoringFilledArgonCanisterFlag, flag); +} + +void GameStateManager::setScoringSawUnconsciousOperator(const bool flag) { +	_scoringFlags.setFlag(kScoringSawUnconsciousOperatorFlag, flag); +} + +void GameStateManager::setScoringWentThroughPressureDoor(const bool flag) { +	_scoringFlags.setFlag(kScoringWentThroughPressureDoorFlag, flag); +} + +void GameStateManager::setScoringPreppedSub(const bool flag) { +	_scoringFlags.setFlag(kScoringPreppedSubFlag, flag); +} + +void GameStateManager::setScoringEnteredSub(const bool flag) { +	_scoringFlags.setFlag(kScoringEnteredSubFlag, flag); +} + +void GameStateManager::setScoringExitedSub(const bool flag) { +	_scoringFlags.setFlag(kScoringExitedSubFlag, flag); +} + +void GameStateManager::setScoringSawRobotAt54North(const bool flag) { +	_scoringFlags.setFlag(kScoringSawRobotAt54NorthFlag, flag); +} + +void GameStateManager::setScoringPlayedWithClaw(const bool flag) { +	_scoringFlags.setFlag(kScoringPlayedWithClawFlag, flag); +} + +void GameStateManager::setScoringUsedRetinalChip(const bool flag) { +	_scoringFlags.setFlag(kScoringUsedRetinalChipFlag, flag); +} + +void GameStateManager::setScoringFinishedGlobeGame(const bool flag) { +	_scoringFlags.setFlag(kScoringFinishedGlobeGameFlag, flag); +} + +void GameStateManager::setScoringStoppedNoradRobot(const bool flag) { +	_scoringFlags.setFlag(kScoringStoppedNoradRobotFlag, flag); +} + +void GameStateManager::setScoringGotNoradOpMemChip(const bool flag) { +	_scoringFlags.setFlag(kScoringGotNoradOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedNorad(const bool flag) { +	_scoringFlags.setFlag(kScoringFinishedNoradFlag, flag); +} + +void GameStateManager::setScoringRemovedDart(const bool flag) { +	_scoringFlags.setFlag(kScoringRemovedDartFlag, flag); +} + +void GameStateManager::setScoringAnalyzedDart(const bool flag) { +	_scoringFlags.setFlag(kScoringAnalyzedDartFlag, flag); +} + +void GameStateManager::setScoringBuiltAntidote(const bool flag) { +	_scoringFlags.setFlag(kScoringBuiltAntidoteFlag, flag); +} + +void GameStateManager::setScoringGotSinclairKey(const bool flag) { +	_scoringFlags.setFlag(kScoringGotSinclairKeyFlag, flag); +} + +void GameStateManager::setScoringGotArgonCanister(const bool flag) { +	_scoringFlags.setFlag(kScoringGotArgonCanisterFlag, flag); +} + +void GameStateManager::setScoringGotNitrogenCanister(const bool flag) { +	_scoringFlags.setFlag(kScoringGotNitrogenCanisterFlag, flag); +} + +void GameStateManager::setScoringPlayedWithMessages(const bool flag) { +	_scoringFlags.setFlag(kScoringPlayedWithMessagesFlag, flag); +} + +void GameStateManager::setScoringSawMorphExperiment(const bool flag) { +	_scoringFlags.setFlag(kScoringSawMorphExperimentFlag, flag); +} + +void GameStateManager::setScoringEnteredSinclairOffice(const bool flag) { +	_scoringFlags.setFlag(kScoringEnteredSinclairOfficeFlag, flag); +} + +void GameStateManager::setScoringSawBrochure(const bool flag) { +	_scoringFlags.setFlag(kScoringSawBrochureFlag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry1(const bool flag) { +	_scoringFlags.setFlag(kScoringSawSinclairEntry1Flag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry2(const bool flag) { +	_scoringFlags.setFlag(kScoringSawSinclairEntry2Flag, flag); +} + +void GameStateManager::setScoringSawSinclairEntry3(const bool flag) { +	_scoringFlags.setFlag(kScoringSawSinclairEntry3Flag, flag); +} + +void GameStateManager::setScoringSawWSCDirectory(const bool flag) { +	_scoringFlags.setFlag(kScoringSawWSCDirectoryFlag, flag); +} + +void GameStateManager::setScoringUsedCrowBarInWSC(const bool flag) { +	_scoringFlags.setFlag(kScoringUsedCrowBarInWSCFlag, flag); +} + +void GameStateManager::setScoringFinishedPlasmaDodge(const bool flag) { +	_scoringFlags.setFlag(kScoringFinishedPlasmaDodgeFlag, flag); +} + +void GameStateManager::setScoringOpenedCatwalk(const bool flag) { +	_scoringFlags.setFlag(kScoringOpenedCatwalkFlag, flag); +} + +void GameStateManager::setScoringStoppedWSCRobot(const bool flag) { +	_scoringFlags.setFlag(kScoringStoppedWSCRobotFlag, flag); +} + +void GameStateManager::setScoringGotWSCOpMemChip(const bool flag) { +	_scoringFlags.setFlag(kScoringGotWSCOpMemChipFlag, flag); +} + +void GameStateManager::setScoringFinishedWSC(const bool flag) { +	_scoringFlags.setFlag(kScoringFinishedWSCFlag, flag); +} + +void GameStateManager::setScoringMarsGandhi(const bool flag) { +	_scoringFlags.setFlag(kScoringMarsGandhiFlag, flag); +} + +void GameStateManager::setScoringNoradGandhi(const bool flag) { +	_scoringFlags.setFlag(kScoringNoradGandhiFlag, flag); +} + +void GameStateManager::setScoringWSCGandhi(const bool flag) { +	_scoringFlags.setFlag(kScoringWSCGandhiFlag, flag); +} + +bool GameStateManager::getScoringSawINN() { +	return _scoringFlags.getFlag(kScoringSawINNFlag); +} + +bool GameStateManager::getScoringTookShower() { +	return _scoringFlags.getFlag(kScoringTookShowerFlag); +} + +bool GameStateManager::getScoringFixedHair() { +	return _scoringFlags.getFlag(kScoringFixedHairFlag); +} + +bool GameStateManager::getScoringGotKeyCard() { +	return _scoringFlags.getFlag(kScoringGotKeyCardFlag); +} + +bool GameStateManager::getScoringReadPaper() { +	return _scoringFlags.getFlag(kScoringReadPaperFlag); +} + +bool GameStateManager::getScoringLookThroughTelescope() { +	return _scoringFlags.getFlag(kScoringLookThroughTelescopeFlag); +} + +bool GameStateManager::getScoringSawCaldoriaKiosk() { +	return _scoringFlags.getFlag(kScoringSawCaldoriaKioskFlag); +} + +bool GameStateManager::getScoringGoToTSA() { +	return _scoringFlags.getFlag(kScoringGoToTSAFlag); +} + +bool GameStateManager::getScoringEnterTSA() { +	return _scoringFlags.getFlag(kScoringEnterTSAFlag); +} + +bool GameStateManager::getScoringSawBust1() { +	return _scoringFlags.getFlag(kScoringSawBust1Flag); +} + +bool GameStateManager::getScoringSawBust2() { +	return _scoringFlags.getFlag(kScoringSawBust2Flag); +} + +bool GameStateManager::getScoringSawBust3() { +	return _scoringFlags.getFlag(kScoringSawBust3Flag); +} + +bool GameStateManager::getScoringSawBust4() { +	return _scoringFlags.getFlag(kScoringSawBust4Flag); +} + +bool GameStateManager::getScoringSawBust5() { +	return _scoringFlags.getFlag(kScoringSawBust5Flag); +} + +bool GameStateManager::getScoringSawBust6() { +	return _scoringFlags.getFlag(kScoringSawBust6Flag); +} + +bool GameStateManager::getScoringSawTheory() { +	return _scoringFlags.getFlag(kScoringSawTheoryFlag); +} + +bool GameStateManager::getScoringSawBackground() { +	return _scoringFlags.getFlag(kScoringSawBackgroundFlag); +} + +bool GameStateManager::getScoringSawProcedure() { +	return _scoringFlags.getFlag(kScoringSawProcedureFlag); +} + +bool GameStateManager::getScoringGotJourneymanKey() { +	return _scoringFlags.getFlag(kScoringGotJourneymanKeyFlag); +} + +bool GameStateManager::getScoringGotPegasusBiochip() { +	return _scoringFlags.getFlag(kScoringGotPegasusBiochipFlag); +} + +bool GameStateManager::getScoringGotBiosuit() { +	return _scoringFlags.getFlag(kScoringGotBiosuitFlag); +} + +bool GameStateManager::getScoringGoToPrehistoric() { +	return _scoringFlags.getFlag(kScoringGoToPrehistoricFlag); +} + +bool GameStateManager::getScoringPutLogInReader() { +	return _scoringFlags.getFlag(kScoringPutLogInReaderFlag); +} + +bool GameStateManager::getScoringSawCaldoriaNormal() { +	return _scoringFlags.getFlag(kScoringSawCaldoriaNormalFlag); +} + +bool GameStateManager::getScoringSawCaldoriaAltered() { +	return _scoringFlags.getFlag(kScoringSawCaldoriaAlteredFlag); +} + +bool GameStateManager::getScoringSawNoradNormal() { +	return _scoringFlags.getFlag(kScoringSawNoradNormalFlag); +} + +bool GameStateManager::getScoringSawNoradAltered() { +	return _scoringFlags.getFlag(kScoringSawNoradAlteredFlag); +} + +bool GameStateManager::getScoringSawMarsNormal() { +	return _scoringFlags.getFlag(kScoringSawMarsNormalFlag); +} + +bool GameStateManager::getScoringSawMarsAltered() { +	return _scoringFlags.getFlag(kScoringSawMarsAlteredFlag); +} + +bool GameStateManager::getScoringSawWSCNormal() { +	return _scoringFlags.getFlag(kScoringSawWSCNormalFlag); +} + +bool GameStateManager::getScoringSawWSCAltered() { +	return _scoringFlags.getFlag(kScoringSawWSCAlteredFlag); +} + +bool GameStateManager::getScoringWentToReadyRoom2() { +	return _scoringFlags.getFlag(kScoringWentToReadyRoom2Flag); +} + +bool GameStateManager::getScoringWentAfterSinclair() { +	return _scoringFlags.getFlag(kScoringWentAfterSinclairFlag); +} + +bool GameStateManager::getScoringUsedCardBomb() { +	return _scoringFlags.getFlag(kScoringUsedCardBombFlag); +} + +bool GameStateManager::getScoringShieldedCardBomb() { +	return _scoringFlags.getFlag(kScoringShieldedCardBombFlag); +} + +bool GameStateManager::getScoringStunnedSinclair() { +	return _scoringFlags.getFlag(kScoringStunnedSinclairFlag); +} + +bool GameStateManager::getScoringDisarmedNuke() { +	return _scoringFlags.getFlag(kScoringDisarmedNukeFlag); +} + +bool GameStateManager::getScoringThrewBreaker() { +	return _scoringFlags.getFlag(kScoringThrewBreakerFlag); +} + +bool GameStateManager::getScoringExtendedBridge() { +	return _scoringFlags.getFlag(kScoringExtendedBridgeFlag); +} + +bool GameStateManager::getScoringGotHistoricalLog() { +	return _scoringFlags.getFlag(kScoringGotHistoricalLogFlag); +} + +bool GameStateManager::getScoringFinishedPrehistoric() { +	return _scoringFlags.getFlag(kScoringFinishedPrehistoricFlag); +} + +bool GameStateManager::getScoringThrownByRobot() { +	return _scoringFlags.getFlag(kScoringThrownByRobotFlag); +} + +bool GameStateManager::getScoringGotMarsCard() { +	return _scoringFlags.getFlag(kScoringGotMarsCardFlag); +} + +bool GameStateManager::getScoringSawMarsKiosk() { +	return _scoringFlags.getFlag(kScoringSawMarsKioskFlag); +} + +bool GameStateManager::getScoringSawTransportMap() { +	return _scoringFlags.getFlag(kScoringSawTransportMapFlag); +} + +bool GameStateManager::getScoringGotCrowBar() { +	return _scoringFlags.getFlag(kScoringGotCrowBarFlag); +} + +bool GameStateManager::getScoringTurnedOnTransport() { +	return _scoringFlags.getFlag(kScoringTurnedOnTransportFlag); +} + +bool GameStateManager::getScoringGotOxygenMask() { +	return _scoringFlags.getFlag(kScoringGotOxygenMaskFlag); +} + +bool GameStateManager::getScoringAvoidedRobot() { +	return _scoringFlags.getFlag(kScoringAvoidedRobotFlag); +} + +bool GameStateManager::getScoringActivatedPlatform() { +	return _scoringFlags.getFlag(kScoringActivatedPlatformFlag); +} + +bool GameStateManager::getScoringUsedLiquidNitrogen() { +	return _scoringFlags.getFlag(kScoringUsedLiquidNitrogenFlag); +} + +bool GameStateManager::getScoringUsedCrowBar() { +	return _scoringFlags.getFlag(kScoringUsedCrowBarFlag); +} + +bool GameStateManager::getScoringFoundCardBomb() { +	return _scoringFlags.getFlag(kScoringFoundCardBombFlag); +} + +bool GameStateManager::getScoringDisarmedCardBomb() { +	return _scoringFlags.getFlag(kScoringDisarmedCardBombFlag); +} + +bool GameStateManager::getScoringGotCardBomb() { +	return _scoringFlags.getFlag(kScoringGotCardBombFlag); +} + +bool GameStateManager::getScoringThreadedMaze() { +	return _scoringFlags.getFlag(kScoringThreadedMazeFlag); +} + +bool GameStateManager::getScoringThreadedGearRoom() { +	return _scoringFlags.getFlag(kScoringThreadedGearRoomFlag); +} + +bool GameStateManager::getScoringEnteredShuttle() { +	return _scoringFlags.getFlag(kScoringEnteredShuttleFlag); +} + +bool GameStateManager::getScoringEnteredLaunchTube() { +	return _scoringFlags.getFlag(kScoringEnteredLaunchTubeFlag); +} + +bool GameStateManager::getScoringStoppedRobotsShuttle() { +	return _scoringFlags.getFlag(kScoringStoppedRobotsShuttleFlag); +} + +bool GameStateManager::getScoringGotMarsOpMemChip() { +	return _scoringFlags.getFlag(kScoringGotMarsOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedMars() { +	return _scoringFlags.getFlag(kScoringFinishedMarsFlag); +} + +bool GameStateManager::getScoringSawSecurityMonitor() { +	return _scoringFlags.getFlag(kScoringSawSecurityMonitorFlag); +} + +bool GameStateManager::getScoringFilledOxygenCanister() { +	return _scoringFlags.getFlag(kScoringFilledOxygenCanisterFlag); +} + +bool GameStateManager::getScoringFilledArgonCanister() { +	return _scoringFlags.getFlag(kScoringFilledArgonCanisterFlag); +} + +bool GameStateManager::getScoringSawUnconsciousOperator() { +	return _scoringFlags.getFlag(kScoringSawUnconsciousOperatorFlag); +} + +bool GameStateManager::getScoringWentThroughPressureDoor() { +	return _scoringFlags.getFlag(kScoringWentThroughPressureDoorFlag); +} + +bool GameStateManager::getScoringPreppedSub() { +	return _scoringFlags.getFlag(kScoringPreppedSubFlag); +} + +bool GameStateManager::getScoringEnteredSub() { +	return _scoringFlags.getFlag(kScoringEnteredSubFlag); +} + +bool GameStateManager::getScoringExitedSub() { +	return _scoringFlags.getFlag(kScoringExitedSubFlag); +} + +bool GameStateManager::getScoringSawRobotAt54North() { +	return _scoringFlags.getFlag(kScoringSawRobotAt54NorthFlag); +} + +bool GameStateManager::getScoringPlayedWithClaw() { +	return _scoringFlags.getFlag(kScoringPlayedWithClawFlag); +} + +bool GameStateManager::getScoringUsedRetinalChip() { +	return _scoringFlags.getFlag(kScoringUsedRetinalChipFlag); +} + +bool GameStateManager::getScoringFinishedGlobeGame() { +	return _scoringFlags.getFlag(kScoringFinishedGlobeGameFlag); +} + +bool GameStateManager::getScoringStoppedNoradRobot() { +	return _scoringFlags.getFlag(kScoringStoppedNoradRobotFlag); +} + +bool GameStateManager::getScoringGotNoradOpMemChip() { +	return _scoringFlags.getFlag(kScoringGotNoradOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedNorad() { +	return _scoringFlags.getFlag(kScoringFinishedNoradFlag); +} + +bool GameStateManager::getScoringRemovedDart() { +	return _scoringFlags.getFlag(kScoringRemovedDartFlag); +} + +bool GameStateManager::getScoringAnalyzedDart() { +	return _scoringFlags.getFlag(kScoringAnalyzedDartFlag); +} + +bool GameStateManager::getScoringBuiltAntidote() { +	return _scoringFlags.getFlag(kScoringBuiltAntidoteFlag); +} + +bool GameStateManager::getScoringGotSinclairKey() { +	return _scoringFlags.getFlag(kScoringGotSinclairKeyFlag); +} + +bool GameStateManager::getScoringGotArgonCanister() { +	return _scoringFlags.getFlag(kScoringGotArgonCanisterFlag); +} + +bool GameStateManager::getScoringGotNitrogenCanister() { +	return _scoringFlags.getFlag(kScoringGotNitrogenCanisterFlag); +} + +bool GameStateManager::getScoringPlayedWithMessages() { +	return _scoringFlags.getFlag(kScoringPlayedWithMessagesFlag); +} + +bool GameStateManager::getScoringSawMorphExperiment() { +	return _scoringFlags.getFlag(kScoringSawMorphExperimentFlag); +} + +bool GameStateManager::getScoringEnteredSinclairOffice() { +	return _scoringFlags.getFlag(kScoringEnteredSinclairOfficeFlag); +} + +bool GameStateManager::getScoringSawBrochure() { +	return _scoringFlags.getFlag(kScoringSawBrochureFlag); +} + +bool GameStateManager::getScoringSawSinclairEntry1() { +	return _scoringFlags.getFlag(kScoringSawSinclairEntry1Flag); +} + +bool GameStateManager::getScoringSawSinclairEntry2() { +	return _scoringFlags.getFlag(kScoringSawSinclairEntry2Flag); +} + +bool GameStateManager::getScoringSawSinclairEntry3() { +	return _scoringFlags.getFlag(kScoringSawSinclairEntry3Flag); +} + +bool GameStateManager::getScoringSawWSCDirectory() { +	return _scoringFlags.getFlag(kScoringSawWSCDirectoryFlag); +} + +bool GameStateManager::getScoringUsedCrowBarInWSC() { +	return _scoringFlags.getFlag(kScoringUsedCrowBarInWSCFlag); +} + +bool GameStateManager::getScoringFinishedPlasmaDodge() { +	return _scoringFlags.getFlag(kScoringFinishedPlasmaDodgeFlag); +} + +bool GameStateManager::getScoringOpenedCatwalk() { +	return _scoringFlags.getFlag(kScoringOpenedCatwalkFlag); +} + +bool GameStateManager::getScoringStoppedWSCRobot() { +	return _scoringFlags.getFlag(kScoringStoppedWSCRobotFlag); +} + +bool GameStateManager::getScoringGotWSCOpMemChip() { +	return _scoringFlags.getFlag(kScoringGotWSCOpMemChipFlag); +} + +bool GameStateManager::getScoringFinishedWSC() { +	return _scoringFlags.getFlag(kScoringFinishedWSCFlag); +} + +bool GameStateManager::getScoringMarsGandhi() { +	return _scoringFlags.getFlag(kScoringMarsGandhiFlag); +} + +bool GameStateManager::getScoringNoradGandhi() { +	return _scoringFlags.getFlag(kScoringNoradGandhiFlag); +} + +bool GameStateManager::getScoringWSCGandhi() { +	return _scoringFlags.getFlag(kScoringWSCGandhiFlag); +} + +void GameStateManager::setWalkthroughMode(bool value) { +	_globalFlags.setFlag(kGlobalWalkthroughFlag, value); +} + +bool GameStateManager::getWalkthroughMode() { +	return _globalFlags.getFlag(kGlobalWalkthroughFlag); +} + +void GameStateManager::setShieldOn(bool value) { +	_globalFlags.setFlag(kGlobalShieldOnFlag, value); +} + +bool GameStateManager::getShieldOn() { +	return _globalFlags.getFlag(kGlobalShieldOnFlag); +} + +void GameStateManager::setEasterEgg(bool value) { +	_globalFlags.setFlag(kGlobalEasterEggFlag, value); +} + +bool GameStateManager::getEasterEgg() { +	return _globalFlags.getFlag(kGlobalEasterEggFlag); +} + +void GameStateManager::setBeenToWSC(bool value) { +	_globalFlags.setFlag(kGlobalBeenToWSCFlag, value); +} + +bool GameStateManager::getBeenToWSC() { +	return _globalFlags.getFlag(kGlobalBeenToWSCFlag); +} + +void GameStateManager::setBeenToMars(bool value) { +	_globalFlags.setFlag(kGlobalBeenToMarsFlag, value); +} + +bool GameStateManager::getBeenToMars() { +	return _globalFlags.getFlag(kGlobalBeenToMarsFlag); +} + +void GameStateManager::setBeenToNorad(bool value) { +	_globalFlags.setFlag(kGlobalBeenToNoradFlag, value); +} + +bool GameStateManager::getBeenToNorad() { +	return _globalFlags.getFlag(kGlobalBeenToNoradFlag); +} + +void GameStateManager::setWSCFinished(bool value) { +	_globalFlags.setFlag(kGlobalWSCFinishedFlag, value); +} + +bool GameStateManager::getWSCFinished() { +	return _globalFlags.getFlag(kGlobalWSCFinishedFlag); +} + +void GameStateManager::setMarsFinished(bool value) { +	_globalFlags.setFlag(kGlobalMarsFinishedFlag, value); +} + +bool GameStateManager::getMarsFinished() { +	return _globalFlags.getFlag(kGlobalMarsFinishedFlag); +} + +void GameStateManager::setNoradFinished(bool value) { +	_globalFlags.setFlag(kGlobalNoradFinishedFlag, value); +} + +bool GameStateManager::getNoradFinished() { +	return _globalFlags.getFlag(kGlobalNoradFinishedFlag); +} + +bool GameStateManager::allTimeZonesFinished() { +	return getWSCFinished() && getMarsFinished() && getNoradFinished(); +} + +void GameStateManager::setTakenItemID(ItemID id, bool value) { +	_itemTakenFlags.setFlag(id, value); +} + +bool GameStateManager::isTakenItemID(ItemID id) { +	return _itemTakenFlags.getFlag(id); +} + +void GameStateManager::setTakenItem(Item *item, bool value) { +	setTakenItemID(item->getObjectID(), value); +} + +bool GameStateManager::isTakenItem(Item *item) { +	return isTakenItemID(item->getObjectID()); +} + +void GameStateManager::setCaldoriaFuseTimeLimit(const TimeValue timeLimit) { +	_caldoriaFuseTimeLimit = timeLimit; +} + +TimeValue GameStateManager::getCaldoriaFuseTimeLimit() { +	return _caldoriaFuseTimeLimit; +} + +void GameStateManager::setCaldoriaSeenPullback(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaSeenPullbackFlag, value); +} + +bool GameStateManager::getCaldoriaSeenPullback() { +	return _caldoriaFlags.getFlag(kCaldoriaSeenPullbackFlag); +} + +void GameStateManager::setCaldoriaMadeOJ(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaMadeOJFlag, value); +} + +bool GameStateManager::getCaldoriaMadeOJ() { +	return _caldoriaFlags.getFlag(kCaldoriaMadeOJFlag); +} + +void GameStateManager::setCaldoriaWokenUp(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaWokenUpFlag, value); +} + +bool GameStateManager::getCaldoriaWokenUp() { +	return _caldoriaFlags.getFlag(kCaldoriaWokenUpFlag); +} + +void GameStateManager::setCaldoriaDidRecalibration(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaDidRecalibrationFlag, value); +} + +bool GameStateManager::getCaldoriaDidRecalibration() { +	return _caldoriaFlags.getFlag(kCaldoriaDidRecalibrationFlag); +} + +void GameStateManager::setCaldoriaSeenSinclairInElevator(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaSeenSinclairInElevatorFlag, value); +} + +bool GameStateManager::getCaldoriaSeenSinclairInElevator() { +	return _caldoriaFlags.getFlag(kCaldoriaSeenSinclairInElevatorFlag); +} + +void GameStateManager::setCaldoriaINNAnnouncing(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaINNAnnouncingFlag, value); +} + +bool GameStateManager::getCaldoriaINNAnnouncing() { +	return _caldoriaFlags.getFlag(kCaldoriaINNAnnouncingFlag); +} + +void GameStateManager::setCaldoriaSeenINN(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaSeenINNFlag, value); +} + +bool GameStateManager::getCaldoriaSeenINN() { +	return _caldoriaFlags.getFlag(kCaldoriaSeenINNFlag); +} + +void GameStateManager::setCaldoriaSeenMessages(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaSeenMessagesFlag, value); +} + +bool GameStateManager::getCaldoriaSeenMessages() { +	return _caldoriaFlags.getFlag(kCaldoriaSeenMessagesFlag); +} + +void GameStateManager::setCaldoriaSinclairShot(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaSinclairShotFlag, value); +} + +bool GameStateManager::getCaldoriaSinclairShot() { +	return _caldoriaFlags.getFlag(kCaldoriaSinclairShotFlag); +} + +void GameStateManager::setCaldoriaBombDisarmed(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaBombDisarmedFlag, value); +} + +bool GameStateManager::getCaldoriaBombDisarmed() { +	return _caldoriaFlags.getFlag(kCaldoriaBombDisarmedFlag); +} + +void GameStateManager::setCaldoriaRoofDoorOpen(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaRoofDoorOpenFlag, value); +} + +bool GameStateManager::getCaldoriaRoofDoorOpen() { +	return _caldoriaFlags.getFlag(kCaldoriaRoofDoorOpenFlag); +} + +void GameStateManager::setCaldoriaDoneHygiene(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaDoneHygieneFlag, value); +} + +bool GameStateManager::getCaldoriaDoneHygiene() { +	return _caldoriaFlags.getFlag(kCaldoriaDoneHygieneFlag); +} + +void GameStateManager::setCaldoriaSawVoiceAnalysis(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaSawVoiceAnalysisFlag, value); +} + +bool GameStateManager::getCaldoriaSawVoiceAnalysis() { +	return _caldoriaFlags.getFlag(kCaldoriaSawVoiceAnalysisFlag); +} + +void GameStateManager::setCaldoriaDoorBombed(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaDoorBombedFlag, value); +} + +bool GameStateManager::getCaldoriaDoorBombed() { +	return _caldoriaFlags.getFlag(kCaldoriaDoorBombedFlag); +} + +void GameStateManager::setCaldoriaGunAimed(bool value) { +	_caldoriaFlags.setFlag(kCaldoriaGunAimedFlag, value); +} + +bool GameStateManager::getCaldoriaGunAimed() { +	return _caldoriaFlags.getFlag(kCaldoriaGunAimedFlag); +} + +void GameStateManager::setRipTimerTime(TimeValue limit) { +	_TSARipTimerTime = limit; +} + +TimeValue GameStateManager::getRipTimerTime() { +	return _TSARipTimerTime; +} + +void GameStateManager::setTSAFuseTimeLimit(TimeValue limit) { +	_TSAFuseTimeLimit = limit; +} + +TimeValue GameStateManager::getTSAFuseTimeLimit() { +	return _TSAFuseTimeLimit; +} + +void GameStateManager::setTSAState(byte state) { +	_TSAState = state; +} + +byte GameStateManager::getTSAState() { +	return _TSAState; +} + +void GameStateManager::setT0BMonitorMode(byte mode) { +	_T0BMonitorMode = mode; +} + +byte GameStateManager::getT0BMonitorMode() { +	return _T0BMonitorMode; +} + +void GameStateManager::setT0BMonitorStart(TimeValue start) { +	_T0BMonitorStart = start; +} + +TimeValue GameStateManager::getT0BMonitorStart() { +	return _T0BMonitorStart; +} + +void GameStateManager::setTSAIDedAtDoor(bool value) { +	_TSAFlags.setFlag(kTSAIDedAtDoorFlag, value); +} + +bool GameStateManager::getTSAIDedAtDoor() { +	return _TSAFlags.getFlag(kTSAIDedAtDoorFlag); +} + +void GameStateManager::setTSA0BZoomedIn(bool value) { +	_TSAFlags.setFlag(kTSA0BZoomedInFlag, value); +} + +bool GameStateManager::getTSA0BZoomedIn() { +	return _TSAFlags.getFlag(kTSA0BZoomedInFlag); +} + +void GameStateManager::setTSAFrontDoorUnlockedOutside(bool value) { +	_TSAFlags.setFlag(kTSAFrontDoorUnlockedOutsideFlag, value); +} + +bool GameStateManager::getTSAFrontDoorUnlockedOutside() { +	return _TSAFlags.getFlag(kTSAFrontDoorUnlockedOutsideFlag); +} + +void GameStateManager::setTSAFrontDoorUnlockedInside(bool value) { +	_TSAFlags.setFlag(kTSAFrontDoorUnlockedInsideFlag, value); +} + +bool GameStateManager::getTSAFrontDoorUnlockedInside() { +	return _TSAFlags.getFlag(kTSAFrontDoorUnlockedInsideFlag); +} + +void GameStateManager::setTSASeenRobotGreeting(bool value) { +	_TSAFlags.setFlag(kTSASeenRobotGreetingFlag, value); +} + +bool GameStateManager::getTSASeenRobotGreeting() { +	return _TSAFlags.getFlag(kTSASeenRobotGreetingFlag); +} + +void GameStateManager::setTSASeenTheory(bool value) { +	_TSAFlags.setFlag(kTSASeenTheoryFlag, value); +} + +bool GameStateManager::getTSASeenTheory() { +	return _TSAFlags.getFlag(kTSASeenTheoryFlag); +} + +void GameStateManager::setTSASeenBackground(bool value) { +	_TSAFlags.setFlag(kTSASeenBackgroundFlag, value); +} + +bool GameStateManager::getTSASeenBackground() { +	return _TSAFlags.getFlag(kTSASeenBackgroundFlag); +} + +void GameStateManager::setTSASeenProcedure(bool value) { +	_TSAFlags.setFlag(kTSASeenProcedureFlag, value); +} + +bool GameStateManager::getTSASeenProcedure() { +	return _TSAFlags.getFlag(kTSASeenProcedureFlag); +} + +void GameStateManager::setTSASeenAgent3AtDoor(bool value) { +	_TSAFlags.setFlag(kTSASeenAgent3AtDoorFlag, value); +} + +bool GameStateManager::getTSASeenAgent3AtDoor() { +	return _TSAFlags.getFlag(kTSASeenAgent3AtDoorFlag); +} + +void GameStateManager::setTSACommandCenterLocked(bool value) { +	_TSAFlags.setFlag(kTSACommandCenterLockedFlag, value); +} + +bool GameStateManager::getTSACommandCenterLocked() { +	return _TSAFlags.getFlag(kTSACommandCenterLockedFlag); +} + +void GameStateManager::setTSASeenCaldoriaNormal(bool value) { +	_TSAFlags.setFlag(kTSASeenCaldoriaNormalFlag, value); +} + +bool GameStateManager::getTSASeenCaldoriaNormal() { +	return _TSAFlags.getFlag(kTSASeenCaldoriaNormalFlag); +} + +void GameStateManager::setTSASeenCaldoriaAltered(bool value) { +	_TSAFlags.setFlag(kTSASeenCaldoriaAlteredFlag, value); +} + +bool GameStateManager::getTSASeenCaldoriaAltered() { +	return _TSAFlags.getFlag(kTSASeenCaldoriaAlteredFlag); +} + +void GameStateManager::setTSASeenNoradNormal(bool value) { +	_TSAFlags.setFlag(kTSASeenNoradNormalFlag, value); +} + +bool GameStateManager::getTSASeenNoradNormal() { +	return _TSAFlags.getFlag(kTSASeenNoradNormalFlag); +} + +void GameStateManager::setTSASeenNoradAltered(bool value) { +	_TSAFlags.setFlag(kTSASeenNoradAlteredFlag, value); +} + +bool GameStateManager::getTSASeenNoradAltered() { +	return _TSAFlags.getFlag(kTSASeenNoradAlteredFlag); +} + +void GameStateManager::setTSASeenMarsNormal(bool value) { +	_TSAFlags.setFlag(kTSASeenMarsNormalFlag, value); +} + +bool GameStateManager::getTSASeenMarsNormal() { +	return _TSAFlags.getFlag(kTSASeenMarsNormalFlag); +} + +void GameStateManager::setTSASeenMarsAltered(bool value) { +	_TSAFlags.setFlag(kTSASeenMarsAlteredFlag, value); +} + +bool GameStateManager::getTSASeenMarsAltered() { +	return _TSAFlags.getFlag(kTSASeenMarsAlteredFlag); +} + +void GameStateManager::setTSASeenWSCNormal(bool value) { +	_TSAFlags.setFlag(kTSASeenWSCNormalFlag, value); +} + +bool GameStateManager::getTSASeenWSCNormal() { +	return _TSAFlags.getFlag(kTSASeenWSCNormalFlag); +} + +void GameStateManager::setTSASeenWSCAltered(bool value) { +	_TSAFlags.setFlag(kTSASeenWSCAlteredFlag, value); +} + +bool GameStateManager::getTSASeenWSCAltered() { +	return _TSAFlags.getFlag(kTSASeenWSCAlteredFlag); +} + +void GameStateManager::setTSABiosuitOn(bool value) { +	_TSAFlags.setFlag(kTSABiosuitOnFlag, value); +} + +bool GameStateManager::getTSABiosuitOn() { +	return _TSAFlags.getFlag(kTSABiosuitOnFlag); +} + +void GameStateManager::setPrehistoricTriedToExtendBridge(bool value) { +	_prehistoricFlags.setFlag(kPrehistoricTriedToExtendBridgeFlag, value); +} + +bool GameStateManager::getPrehistoricTriedToExtendBridge() { +	return _prehistoricFlags.getFlag(kPrehistoricTriedToExtendBridgeFlag); +} + +void GameStateManager::setPrehistoricSeenTimeStream(bool value) { +	_prehistoricFlags.setFlag(kPrehistoricSeenTimeStreamFlag, value); +} + +bool GameStateManager::getPrehistoricSeenTimeStream() { +	return _prehistoricFlags.getFlag(kPrehistoricSeenTimeStreamFlag); +} + +void GameStateManager::setPrehistoricSeenFlyer1(bool value) { +	_prehistoricFlags.setFlag(kPrehistoricSeenFlyer1Flag, value); +} + +bool GameStateManager::getPrehistoricSeenFlyer1() { +	return _prehistoricFlags.getFlag(kPrehistoricSeenFlyer1Flag); +} + +void GameStateManager::setPrehistoricSeenFlyer2(bool value) { +	_prehistoricFlags.setFlag(kPrehistoricSeenFlyer2Flag, value); +} + +bool GameStateManager::getPrehistoricSeenFlyer2() { +	return _prehistoricFlags.getFlag(kPrehistoricSeenFlyer2Flag); +} + +void GameStateManager::setPrehistoricSeenBridgeZoom(bool value) { +	_prehistoricFlags.setFlag(kPrehistoricSeenBridgeZoomFlag, value); +} + +bool GameStateManager::getPrehistoricSeenBridgeZoom() { +	return _prehistoricFlags.getFlag(kPrehistoricSeenBridgeZoomFlag); +} + +void GameStateManager::setPrehistoricBreakerThrown(bool value) { +	_prehistoricFlags.setFlag(kPrehistoricBreakerThrownFlag, value); +} + +bool GameStateManager::getPrehistoricBreakerThrown() { +	return _prehistoricFlags.getFlag(kPrehistoricBreakerThrownFlag); +} + +void GameStateManager::setNoradSeenTimeStream(bool value) { +	_noradFlags.setFlag(kNoradSeenTimeStreamFlag, value); +} + +bool GameStateManager::getNoradSeenTimeStream() { +	return _noradFlags.getFlag(kNoradSeenTimeStreamFlag); +} + +void GameStateManager::setNoradGassed(bool value) { +	_noradFlags.setFlag(kNoradGassedFlag, value); +} + +bool GameStateManager::getNoradGassed() { +	return _noradFlags.getFlag(kNoradGassedFlag); +} + +void GameStateManager::setNoradFillingStationOn(bool value) { +	_noradFlags.setFlag(kNoradFillingStationOnFlag, value); +} + +bool GameStateManager::getNoradFillingStationOn() { +	return _noradFlags.getFlag(kNoradFillingStationOnFlag); +} + +void GameStateManager::setNoradN22MessagePlayed(bool value) { +	_noradFlags.setFlag(kNoradN22MessagePlayedFlag, value); +} + +bool GameStateManager::getNoradN22MessagePlayed() { +	return _noradFlags.getFlag(kNoradN22MessagePlayedFlag); +} + +void GameStateManager::setNoradPlayedGlobeGame(bool value) { +	_noradFlags.setFlag(kNoradPlayedGlobeGameFlag, value); +} + +bool GameStateManager::getNoradPlayedGlobeGame() { +	return _noradFlags.getFlag(kNoradPlayedGlobeGameFlag); +} + +void GameStateManager::setNoradBeatRobotWithClaw(bool value) { +	_noradFlags.setFlag(kNoradBeatRobotWithClawFlag, value); +} + +bool GameStateManager::getNoradBeatRobotWithClaw() { +	return _noradFlags.getFlag(kNoradBeatRobotWithClawFlag); +} + +void GameStateManager::setNoradBeatRobotWithDoor(bool value) { +	_noradFlags.setFlag(kNoradBeatRobotWithDoorFlag, value); +} + +bool GameStateManager::getNoradBeatRobotWithDoor() { +	return _noradFlags.getFlag(kNoradBeatRobotWithDoorFlag); +} + +void GameStateManager::setNoradRetScanGood(bool value) { +	_noradFlags.setFlag(kNoradRetScanGoodFlag, value); +} + +bool GameStateManager::getNoradRetScanGood() { +	return _noradFlags.getFlag(kNoradRetScanGoodFlag); +} + +void GameStateManager::setNoradWaitingForLaser(bool value) { +	_noradFlags.setFlag(kNoradWaitingForLaserFlag, value); +} + +bool GameStateManager::getNoradWaitingForLaser() { +	return _noradFlags.getFlag(kNoradWaitingForLaserFlag); +} + +void GameStateManager::setNoradSubRoomPressure(uint16 pressure) { +	_noradSubRoomPressure = pressure; +} + +uint16 GameStateManager::getNoradSubRoomPressure() { +	return _noradSubRoomPressure; +} + +void GameStateManager::setNoradSubPrepState(NoradSubPrepState state) { +	_noradSubPrepState = state; +} + +NoradSubPrepState GameStateManager::getNoradSubPrepState() { +	return _noradSubPrepState; +} + +void GameStateManager::setNoradArrivedFromSub(bool value) { +	_noradFlags.setFlag(kNoradArrivedFromSubFlag, value); +} + +bool GameStateManager::getNoradArrivedFromSub() { +	return _noradFlags.getFlag(kNoradArrivedFromSubFlag); +} + +void GameStateManager::setMarsSeenTimeStream(bool value) { +	_marsFlags.setFlag(kMarsSeenTimeStreamFlag, value); +} + +bool GameStateManager::getMarsSeenTimeStream() { +	return _marsFlags.getFlag(kMarsSeenTimeStreamFlag); +} + +void GameStateManager::setMarsHeardUpperPodMessage(bool value) { +	_marsFlags.setFlag(kMarsHeardUpperPodMessageFlag, value); +} + +bool GameStateManager::getMarsHeardUpperPodMessage() { +	return _marsFlags.getFlag(kMarsHeardUpperPodMessageFlag); +} + +void GameStateManager::setMarsRobotThrownPlayer(bool value) { +	_marsFlags.setFlag(kMarsRobotThrownPlayerFlag, value); +} + +bool GameStateManager::getMarsRobotThrownPlayer() { +	return _marsFlags.getFlag(kMarsRobotThrownPlayerFlag); +} + +void GameStateManager::setMarsHeardCheckInMessage(bool value) { +	_marsFlags.setFlag(kMarsHeardCheckInMessageFlag, value); +} + +bool GameStateManager::getMarsHeardCheckInMessage() { +	return _marsFlags.getFlag(kMarsHeardCheckInMessageFlag); +} + +void GameStateManager::setMarsPodAtUpperPlatform(bool value) { +	_marsFlags.setFlag(kMarsPodAtUpperPlatformFlag, value); +} + +bool GameStateManager::getMarsPodAtUpperPlatform() { +	return _marsFlags.getFlag(kMarsPodAtUpperPlatformFlag); +} + +void GameStateManager::setMarsSeenThermalScan(bool value) { +	_marsFlags.setFlag(kMarsSeenThermalScanFlag, value); +} + +bool GameStateManager::getMarsSeenThermalScan() { +	return _marsFlags.getFlag(kMarsSeenThermalScanFlag); +} + +void GameStateManager::setMarsArrivedBelow(bool value) { +	_marsFlags.setFlag(kMarsArrivedBelowFlag, value); +} + +bool GameStateManager::getMarsArrivedBelow() { +	return _marsFlags.getFlag(kMarsArrivedBelowFlag); +} + +void GameStateManager::setMarsSeenRobotAtReactor(bool value) { +	_marsFlags.setFlag(kMarsSeenRobotAtReactorFlag, value); +} + +bool GameStateManager::getMarsSeenRobotAtReactor() { +	return _marsFlags.getFlag(kMarsSeenRobotAtReactorFlag); +} + +void GameStateManager::setMarsAvoidedReactorRobot(bool value) { +	_marsFlags.setFlag(kMarsAvoidedReactorRobotFlag, value); +} + +bool GameStateManager::getMarsAvoidedReactorRobot() { +	return _marsFlags.getFlag(kMarsAvoidedReactorRobotFlag); +} + +void GameStateManager::setMarsSecurityDown(bool value) { +	_marsFlags.setFlag(kMarsSecurityDownFlag, value); +} + +bool GameStateManager::getMarsSecurityDown() { +	return _marsFlags.getFlag(kMarsSecurityDownFlag); +} + +void GameStateManager::setMarsInAirlock(bool value) { +	_marsFlags.setFlag(kMarsInAirlockFlag, value); +} + +bool GameStateManager::getMarsInAirlock() { +	return _marsFlags.getFlag(kMarsInAirlockFlag); +} + +void GameStateManager::setMarsAirlockOpen(bool value) { +	_marsFlags.setFlag(kMarsAirlockOpenFlag, value); +} + +bool GameStateManager::getMarsAirlockOpen() { +	return _marsFlags.getFlag(kMarsAirlockOpenFlag); +} + +void GameStateManager::setMarsMaskOnFiller(bool value) { +	_marsFlags.setFlag(kMarsMaskOnFillerFlag, value); +} + +bool GameStateManager::getMarsMaskOnFiller() { +	return _marsFlags.getFlag(kMarsMaskOnFillerFlag); +} + +void GameStateManager::setMarsLockFrozen(bool value) { +	_marsFlags.setFlag(kMarsLockFrozenFlag, value); +} + +bool GameStateManager::getMarsLockFrozen() { +	return _marsFlags.getFlag(kMarsLockFrozenFlag); +} + +void GameStateManager::setMarsLockBroken(bool value) { +	_marsFlags.setFlag(kMarsLockBrokenFlag, value); +} + +bool GameStateManager::getMarsLockBroken() { +	return _marsFlags.getFlag(kMarsLockBrokenFlag); +} + +void GameStateManager::setMarsMazeDoorPair1(bool value) { +	_marsFlags.setFlag(kMarsMazeDoorPair1Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair1() { +	return _marsFlags.getFlag(kMarsMazeDoorPair1Flag); +} + +void GameStateManager::setMarsMazeDoorPair2(bool value) { +	_marsFlags.setFlag(kMarsMazeDoorPair2Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair2() { +	return _marsFlags.getFlag(kMarsMazeDoorPair2Flag); +} + +void GameStateManager::setMarsMazeDoorPair3(bool value) { +	_marsFlags.setFlag(kMarsMazeDoorPair3Flag, value); +} + +bool GameStateManager::getMarsMazeDoorPair3() { +	return _marsFlags.getFlag(kMarsMazeDoorPair3Flag); +} + +void GameStateManager::setMarsSawRobotLeave(bool value) { +	_marsFlags.setFlag(kMarsSawRobotLeaveFlag, value); +} + +bool GameStateManager::getMarsSawRobotLeave() { +	return _marsFlags.getFlag(kMarsSawRobotLeaveFlag); +} + +void GameStateManager::setMarsHitRobotWithCannon(bool flag) { +	_marsFlags.setFlag(kMarsHitRobotWithCannonFlag, flag); +} + +bool GameStateManager::getMarsHitRobotWithCannon() { +	return _marsFlags.getFlag(kMarsHitRobotWithCannonFlag); +} +	 +void GameStateManager::setMarsReadyForShuttleTransport(bool value) { +	_marsFlags.setFlag(kMarsReadyForShuttleTransportFlag, value); +} + +bool GameStateManager::getMarsReadyForShuttleTransport() { +	return _marsFlags.getFlag(kMarsReadyForShuttleTransportFlag); +} + +void GameStateManager::setMarsFinishedCanyonChase(bool flag) { +	_marsFlags.setFlag(kMarsFinishedCanyonChaseFlag, flag); +} + +bool GameStateManager::getMarsFinishedCanyonChase() { +	return _marsFlags.getFlag(kMarsFinishedCanyonChaseFlag); +} +	 +void GameStateManager::setMarsThreadedMaze(bool flag) { +	_marsFlags.setFlag(kMarsThreadedMazeFlag, flag); +} + +bool GameStateManager::getMarsThreadedMaze() { +	return _marsFlags.getFlag(kMarsThreadedMazeFlag); +} + +void GameStateManager::setWSCSeenTimeStream(bool value) { +	_WSCFlags.setFlag(kWSCSeenTimeStreamFlag, value); +} + +bool GameStateManager::getWSCSeenTimeStream() { +	return _WSCFlags.getFlag(kWSCSeenTimeStreamFlag); +} + +void GameStateManager::setWSCPoisoned(bool value) { +	_WSCFlags.setFlag(kWSCPoisonedFlag, value); +} + +bool GameStateManager::getWSCPoisoned() { +	return _WSCFlags.getFlag(kWSCPoisonedFlag); +} + +void GameStateManager::setWSCAnsweredAboutDart(bool value) { +	_WSCFlags.setFlag(kWSCAnsweredAboutDartFlag, value); +} + +bool GameStateManager::getWSCAnsweredAboutDart() { +	return _WSCFlags.getFlag(kWSCAnsweredAboutDartFlag); +} + +void GameStateManager::setWSCRemovedDart(bool value) { +	_WSCFlags.setFlag(kWSCRemovedDartFlag, value); +} + +bool GameStateManager::getWSCRemovedDart() { +	return _WSCFlags.getFlag(kWSCRemovedDartFlag); +} + +void GameStateManager::setWSCAnalyzerOn(bool value) { +	_WSCFlags.setFlag(kWSCAnalyzerOnFlag, value); +} + +bool GameStateManager::getWSCAnalyzerOn() { +	return _WSCFlags.getFlag(kWSCAnalyzerOnFlag); +} + +void GameStateManager::setWSCDartInAnalyzer(bool value) { +	_WSCFlags.setFlag(kWSCDartInAnalyzerFlag, value); +} + +bool GameStateManager::getWSCDartInAnalyzer() { +	return _WSCFlags.getFlag(kWSCDartInAnalyzerFlag); +} + +void GameStateManager::setWSCAnalyzedDart(bool value) { +	_WSCFlags.setFlag(kWSCAnalyzedDartFlag, value); +} + +bool GameStateManager::getWSCAnalyzedDart() { +	return _WSCFlags.getFlag(kWSCAnalyzedDartFlag); +} + +void GameStateManager::setWSCSawMorph(bool value) { +	_WSCFlags.setFlag(kWSCSawMorphFlag, value); +} + +bool GameStateManager::getWSCSawMorph() { +	return _WSCFlags.getFlag(kWSCSawMorphFlag); +} + +void GameStateManager::setWSCDesignedAntidote(bool value) { +	_WSCFlags.setFlag(kWSCDesignedAntidoteFlag, value); +} + +bool GameStateManager::getWSCDesignedAntidote() { +	return _WSCFlags.getFlag(kWSCDesignedAntidoteFlag); +} + +void GameStateManager::setWSCPickedUpAntidote(bool value) { +	_WSCFlags.setFlag(kWSCPickedUpAntidoteFlag, value); +} + +bool GameStateManager::getWSCPickedUpAntidote() { +	return _WSCFlags.getFlag(kWSCPickedUpAntidoteFlag); +} + +void GameStateManager::setWSCOfficeMessagesOpen(bool value) { +	_WSCFlags.setFlag(kWSCOfficeMessagesOpenFlag, value); +} + +bool GameStateManager::getWSCOfficeMessagesOpen() { +	return _WSCFlags.getFlag(kWSCOfficeMessagesOpenFlag); +} + +void GameStateManager::setWSCSeenNerd(bool value) { +	_WSCFlags.setFlag(kWSCSeenNerdFlag, value); +} + +bool GameStateManager::getWSCSeenNerd() { +	return _WSCFlags.getFlag(kWSCSeenNerdFlag); +} + +void GameStateManager::setWSCHeardPage1(bool value) { +	_WSCFlags.setFlag(kWSCHeardPage1Flag, value); +} + +bool GameStateManager::getWSCHeardPage1() { +	return _WSCFlags.getFlag(kWSCHeardPage1Flag); +} + +void GameStateManager::setWSCHeardPage2(bool value) { +	_WSCFlags.setFlag(kWSCHeardPage2Flag, value); +} + +bool GameStateManager::getWSCHeardPage2() { +	return _WSCFlags.getFlag(kWSCHeardPage2Flag); +} + +void GameStateManager::setWSCHeardCheckIn(bool value) { +	_WSCFlags.setFlag(kWSCHeardCheckInFlag, value); +} + +bool GameStateManager::getWSCHeardCheckIn() { +	return _WSCFlags.getFlag(kWSCHeardCheckInFlag); +} + +void GameStateManager::setWSCDidPlasmaDodge(bool value) { +	_WSCFlags.setFlag(kWSCDidPlasmaDodgeFlag, value); +} + +bool GameStateManager::getWSCDidPlasmaDodge() { +	return _WSCFlags.getFlag(kWSCDidPlasmaDodgeFlag); +} + +void GameStateManager::setWSCSeenSinclairLecture(bool value) { +	_WSCFlags.setFlag(kWSCSeenSinclairLectureFlag, value); +} + +bool GameStateManager::getWSCSeenSinclairLecture() { +	return _WSCFlags.getFlag(kWSCSeenSinclairLectureFlag); +} + +void GameStateManager::setWSCBeenAtWSC93(bool value) { +	_WSCFlags.setFlag(kWSCBeenAtWSC93Flag, value); +} + +bool GameStateManager::getWSCBeenAtWSC93() { +	return _WSCFlags.getFlag(kWSCBeenAtWSC93Flag); +} + +void GameStateManager::setWSCCatwalkDark(bool value) { +	_WSCFlags.setFlag(kWSCCatwalkDarkFlag, value); +} + +bool GameStateManager::getWSCCatwalkDark() { +	return _WSCFlags.getFlag(kWSCCatwalkDarkFlag); +} + +void GameStateManager::setWSCRobotDead(bool value) { +	_WSCFlags.setFlag(kWSCRobotDeadFlag, value); +} + +bool GameStateManager::getWSCRobotDead() { +	return _WSCFlags.getFlag(kWSCRobotDeadFlag); +} + +void GameStateManager::setWSCRobotGone(bool value) { +	_WSCFlags.setFlag(kWSCRobotGoneFlag, value); +} + +bool GameStateManager::getWSCRobotGone() { +	return _WSCFlags.getFlag(kWSCRobotGoneFlag); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/gamestate.h b/engines/pegasus/gamestate.h new file mode 100644 index 0000000000..7812003e15 --- /dev/null +++ b/engines/pegasus/gamestate.h @@ -0,0 +1,886 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_GAMESTATE_H +#define PEGASUS_GAMESTATE_H + +#include "common/singleton.h" +#include "common/util.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" +#include "pegasus/items/item.h" + +namespace Common { +	class Error; +	class ReadStream; +	class WriteStream; +} + +namespace Pegasus { + +// The only things saved in here are things which get written out to a saved game file... + +enum { +	kGlobalWalkthroughFlag, +	kGlobalShieldOnFlag, +	kGlobalEasterEggFlag, +	kGlobalBeenToWSCFlag, +	kGlobalBeenToMarsFlag, +	kGlobalBeenToNoradFlag, +	kGlobalWSCFinishedFlag, +	kGlobalMarsFinishedFlag, +	kGlobalNoradFinishedFlag, +	kNumGlobalFlags +}; + +enum { +	kScoringSawINNFlag, +	kScoringTookShowerFlag, +	kScoringFixedHairFlag, +	kScoringGotKeyCardFlag, +	kScoringReadPaperFlag, +	kScoringLookThroughTelescopeFlag, +	kScoringSawCaldoriaKioskFlag, +	kScoringGoToTSAFlag, +	kScoringEnterTSAFlag, +	kScoringSawBust1Flag, +	kScoringSawBust2Flag, +	kScoringSawBust3Flag, +	kScoringSawBust4Flag, +	kScoringSawBust5Flag, +	kScoringSawBust6Flag, +	kScoringSawTheoryFlag, +	kScoringSawBackgroundFlag, +	kScoringSawProcedureFlag, +	kScoringGotJourneymanKeyFlag, +	kScoringGotPegasusBiochipFlag, +	kScoringGotBiosuitFlag, +	kScoringGoToPrehistoricFlag, +	kScoringPutLogInReaderFlag, +	kScoringSawCaldoriaNormalFlag, +	kScoringSawCaldoriaAlteredFlag, +	kScoringSawNoradNormalFlag, +	kScoringSawNoradAlteredFlag, +	kScoringSawMarsNormalFlag, +	kScoringSawMarsAlteredFlag, +	kScoringSawWSCNormalFlag, +	kScoringSawWSCAlteredFlag, +	kScoringWentToReadyRoom2Flag, +	kScoringWentAfterSinclairFlag, +	kScoringUsedCardBombFlag, +	kScoringShieldedCardBombFlag, +	kScoringStunnedSinclairFlag, +	kScoringDisarmedNukeFlag, +	 +	kScoringThrewBreakerFlag, +	kScoringExtendedBridgeFlag, +	kScoringGotHistoricalLogFlag, +	kScoringFinishedPrehistoricFlag, +	 +	kScoringThrownByRobotFlag, +	kScoringGotMarsCardFlag, +	kScoringSawMarsKioskFlag, +	kScoringSawTransportMapFlag, +	kScoringGotCrowBarFlag, +	kScoringTurnedOnTransportFlag, +	kScoringGotOxygenMaskFlag, +	kScoringAvoidedRobotFlag, +	kScoringActivatedPlatformFlag, +	kScoringUsedLiquidNitrogenFlag, +	kScoringUsedCrowBarFlag, +	kScoringFoundCardBombFlag, +	kScoringDisarmedCardBombFlag, +	kScoringGotCardBombFlag, +	kScoringThreadedMazeFlag, +	kScoringThreadedGearRoomFlag, +	kScoringEnteredShuttleFlag, +	kScoringEnteredLaunchTubeFlag, +	kScoringStoppedRobotsShuttleFlag, +	kScoringGotMarsOpMemChipFlag, +	kScoringFinishedMarsFlag, +	 +	kScoringSawSecurityMonitorFlag, +	kScoringFilledOxygenCanisterFlag, +	kScoringFilledArgonCanisterFlag, +	kScoringSawUnconsciousOperatorFlag, +	kScoringWentThroughPressureDoorFlag, +	kScoringPreppedSubFlag, +	kScoringEnteredSubFlag, +	kScoringExitedSubFlag, +	kScoringSawRobotAt54NorthFlag, +	kScoringPlayedWithClawFlag, +	kScoringUsedRetinalChipFlag, +	kScoringFinishedGlobeGameFlag, +	kScoringStoppedNoradRobotFlag, +	kScoringGotNoradOpMemChipFlag, +	kScoringFinishedNoradFlag, +	 +	kScoringRemovedDartFlag, +	kScoringAnalyzedDartFlag, +	kScoringBuiltAntidoteFlag, +	kScoringGotSinclairKeyFlag, +	kScoringGotArgonCanisterFlag, +	kScoringGotNitrogenCanisterFlag, +	kScoringPlayedWithMessagesFlag, +	kScoringSawMorphExperimentFlag, +	kScoringEnteredSinclairOfficeFlag, +	kScoringSawBrochureFlag, +	kScoringSawSinclairEntry1Flag, +	kScoringSawSinclairEntry2Flag, +	kScoringSawSinclairEntry3Flag, +	kScoringSawWSCDirectoryFlag, +	kScoringUsedCrowBarInWSCFlag, +	kScoringFinishedPlasmaDodgeFlag, +	kScoringOpenedCatwalkFlag, +	kScoringStoppedWSCRobotFlag, +	kScoringGotWSCOpMemChipFlag, +	kScoringFinishedWSCFlag, +	 +	kScoringMarsGandhiFlag, +	kScoringNoradGandhiFlag, +	kScoringWSCGandhiFlag, +	 +	kNumScoringFlags +}; + +enum { +	kCaldoriaSeenPullbackFlag, +	kCaldoriaMadeOJFlag, +	kCaldoriaWokenUpFlag, +	kCaldoriaDidRecalibrationFlag, +	kCaldoriaSeenSinclairInElevatorFlag, +	kCaldoriaINNAnnouncingFlag, +	kCaldoriaSeenINNFlag, +	kCaldoriaSeenMessagesFlag, +	kCaldoriaSinclairShotFlag, +	kCaldoriaBombDisarmedFlag, +	kCaldoriaRoofDoorOpenFlag, +	kCaldoriaDoneHygieneFlag, +	kCaldoriaSawVoiceAnalysisFlag, +	kCaldoriaDoorBombedFlag, +	kCaldoriaGunAimedFlag, +	kNumCaldoriaFlags +}; + +enum { +	kCaldoriaNoFuseRunning, +	kCaldoriaDoorBombFuseRunning, +	kCaldoriaSinclairFuseRunning +}; + +enum { +	kTSAIDedAtDoorFlag, +	kTSA0BZoomedInFlag, +	kTSAFrontDoorUnlockedOutsideFlag, +	kTSAFrontDoorUnlockedInsideFlag, +	kTSASeenRobotGreetingFlag, +	kTSASeenTheoryFlag, +	kTSASeenBackgroundFlag, +	kTSASeenProcedureFlag, +	kTSASeenAgent3AtDoorFlag, +	kTSACommandCenterLockedFlag, +	kTSASeenCaldoriaNormalFlag, +	kTSASeenCaldoriaAlteredFlag, +	kTSASeenNoradNormalFlag, +	kTSASeenNoradAlteredFlag, +	kTSASeenMarsNormalFlag, +	kTSASeenMarsAlteredFlag, +	kTSASeenWSCNormalFlag, +	kTSASeenWSCAlteredFlag, +	kTSABiosuitOnFlag, +	kNumTSAFlags +}; + +enum { +	kPrehistoricTriedToExtendBridgeFlag, +	kPrehistoricSeenTimeStreamFlag, +	kPrehistoricSeenFlyer1Flag, +	kPrehistoricSeenFlyer2Flag, +	kPrehistoricSeenBridgeZoomFlag, +	kPrehistoricBreakerThrownFlag, +	kNumPrehistoricFlags +}; + +enum { +	kNoradSeenTimeStreamFlag, +	kNoradGassedFlag, +	kNoradFillingStationOnFlag, +	kNoradN22MessagePlayedFlag, +	kNoradArrivedFromSubFlag, +	kNoradWaitingForLaserFlag, +	kNoradRetScanGoodFlag, +	kNoradPlayedGlobeGameFlag, +	kNoradBeatRobotWithClawFlag, +	kNoradBeatRobotWithDoorFlag, +	kNumNoradFlags +}; + +enum { +	kMarsSeenTimeStreamFlag, +	kMarsHeardUpperPodMessageFlag, +	kMarsRobotThrownPlayerFlag, +	kMarsHeardCheckInMessageFlag, +	kMarsPodAtUpperPlatformFlag, +	kMarsSeenThermalScanFlag, +	kMarsArrivedBelowFlag, +	kMarsSeenRobotAtReactorFlag, +	kMarsAvoidedReactorRobotFlag, +	kMarsInAirlockFlag, +	kMarsAirlockOpenFlag, +	kMarsMaskOnFillerFlag, +	kMarsLockFrozenFlag, +	kMarsLockBrokenFlag, +	kMarsMazeDoorPair1Flag, +	kMarsMazeDoorPair2Flag, +	kMarsMazeDoorPair3Flag, +	kMarsSawRobotLeaveFlag, +	kMarsSecurityDownFlag, +	kMarsHitRobotWithCannonFlag, +	kMarsReadyForShuttleTransportFlag, +	kMarsFinishedCanyonChaseFlag, +	kMarsThreadedMazeFlag, +	kNumMarsFlags +}; + +enum { +	kWSCSeenTimeStreamFlag, +	kWSCPoisonedFlag, +	kWSCAnsweredAboutDartFlag, +	kWSCRemovedDartFlag, +	kWSCAnalyzerOnFlag, +	kWSCDartInAnalyzerFlag, +	kWSCAnalyzedDartFlag, +	kWSCSawMorphFlag, +	kWSCDesignedAntidoteFlag, +	kWSCPickedUpAntidoteFlag, +	kWSCOfficeMessagesOpenFlag, +	kWSCSeenNerdFlag, +	kWSCHeardPage1Flag, +	kWSCHeardPage2Flag, +	kWSCHeardCheckInFlag, +	kWSCDidPlasmaDodgeFlag, +	kWSCSeenSinclairLectureFlag, +	kWSCBeenAtWSC93Flag, +	kWSCCatwalkDarkFlag, +	kWSCRobotDeadFlag, +	kWSCRobotGoneFlag, +	kNumWSCFlags +}; + +class GameStateManager : public Common::Singleton<GameStateManager> { +public: +	GameStateManager() { resetGameState(); } + +	// Base game state +	Common::Error writeGameState(Common::WriteStream *stream); +	Common::Error readGameState(Common::ReadStream *stream); +	 +	void resetGameState(); +	 +	void getCurrentLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction); +	void setCurrentLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); +	 +	NeighborhoodID getCurrentNeighborhood(); +	void setCurrentNeighborhood(const NeighborhoodID neighborhood); +	RoomID getCurrentRoom(); +	void setCurrentRoom(const RoomID room); +	DirectionConstant getCurrentDirection(); +	void setCurrentDirection(const DirectionConstant direction); +	 +	RoomViewID getCurrentRoomAndView(); +	 +	void getNextLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction); +	void	setNextLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); + +	NeighborhoodID getNextNeighborhood(); +	void setNextNeighborhood(const NeighborhoodID neighborhood); +	RoomID getNextRoom(); +	void setNextRoom(const RoomID room); +	DirectionConstant getNextDirection(); +	void setNextDirection(const DirectionConstant direction); +	 +	void getLastLocation(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction); +	void setLastLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); +	 +	NeighborhoodID getLastNeighborhood(); +	void setLastNeighborhood(const NeighborhoodID neighborhood); +	RoomID getLastRoom(); +	void setLastRoom(const RoomID room); +	DirectionConstant getLastDirection(); +	void setLastDirection(const DirectionConstant direction); +	 +	RoomViewID getLastRoomAndView(); +	 +	void getOpenDoorLocation(RoomID &room, DirectionConstant &direction); +	void setOpenDoorLocation(const RoomID room, const DirectionConstant direction); +	RoomID getOpenDoorRoom(); +	void setOpenDoorRoom(const RoomID room); +	DirectionConstant getOpenDoorDirection(); +	void setOpenDoorDirection(const DirectionConstant direction); +	 +	RoomViewID getDoorOpenRoomAndView(); +	 +	bool isCurrentDoorOpen(); + +	// Pegasus Prime +	 +	// Scoring... +	// Scoring "Set" functions. +	// Caldoria/TSA scoring +	void setScoringSawINN(const bool = true); +	void setScoringTookShower(const bool = true); +	void setScoringFixedHair(const bool = true); +	void setScoringGotKeyCard(const bool = true); +	void setScoringReadPaper(const bool = true); +	void setScoringLookThroughTelescope(const bool = true); +	void setScoringSawCaldoriaKiosk(const bool = true); +	void setScoringGoToTSA(const bool = true); +	void setScoringEnterTSA(const bool = true); +	void setScoringSawBust1(const bool = true); +	void setScoringSawBust2(const bool = true); +	void setScoringSawBust3(const bool = true); +	void setScoringSawBust4(const bool = true); +	void setScoringSawBust5(const bool = true); +	void setScoringSawBust6(const bool = true); +	void setScoringSawTheory(const bool = true); +	void setScoringSawBackground(const bool = true); +	void setScoringSawProcedure(const bool = true); +	void setScoringGotJourneymanKey(const bool = true); +	void setScoringGotPegasusBiochip(const bool = true); +	void setScoringGotBiosuit(const bool = true); +	void setScoringGoToPrehistoric(const bool = true); +	void setScoringPutLogInReader(const bool = true); +	void setScoringSawCaldoriaNormal(const bool = true); +	void setScoringSawCaldoriaAltered(const bool = true); +	void setScoringSawNoradNormal(const bool = true); +	void setScoringSawNoradAltered(const bool = true); +	void setScoringSawMarsNormal(const bool = true); +	void setScoringSawMarsAltered(const bool = true); +	void setScoringSawWSCNormal(const bool = true); +	void setScoringSawWSCAltered(const bool = true); +	void setScoringWentToReadyRoom2(const bool = true); +	void setScoringWentAfterSinclair(const bool = true); +	void setScoringUsedCardBomb(const bool = true); +	void setScoringShieldedCardBomb(const bool = true); +	void setScoringStunnedSinclair(const bool = true); +	void setScoringDisarmedNuke(const bool = true); + +	// Prehistoric scoring +	void setScoringThrewBreaker(const bool = true); +	void setScoringExtendedBridge(const bool = true); +	void setScoringGotHistoricalLog(const bool = true); +	void setScoringFinishedPrehistoric(const bool = true); + +	// Mars scoring +	void setScoringThrownByRobot(const bool = true); +	void setScoringGotMarsCard(const bool = true); +	void setScoringSawMarsKiosk(const bool = true); +	void setScoringSawTransportMap(const bool = true); +	void setScoringGotCrowBar(const bool = true); +	void setScoringTurnedOnTransport(const bool = true); +	void setScoringGotOxygenMask(const bool = true); +	void setScoringAvoidedRobot(const bool = true); +	void setScoringActivatedPlatform(const bool = true); +	void setScoringUsedLiquidNitrogen(const bool = true); +	void setScoringUsedCrowBar(const bool = true); +	void setScoringFoundCardBomb(const bool = true); +	void setScoringDisarmedCardBomb(const bool = true); +	void setScoringGotCardBomb(const bool = true); +	void setScoringThreadedMaze(const bool = true); +	void setScoringThreadedGearRoom(const bool = true); +	void setScoringEnteredShuttle(const bool = true); +	void setScoringEnteredLaunchTube(const bool = true); +	void setScoringStoppedRobotsShuttle(const bool = true); +	void setScoringGotMarsOpMemChip(const bool = true); +	void setScoringFinishedMars(const bool = true); + +	// Norad scoring +	void setScoringSawSecurityMonitor(const bool = true); +	void setScoringFilledOxygenCanister(const bool = true); +	void setScoringFilledArgonCanister(const bool = true); +	void setScoringSawUnconsciousOperator(const bool = true); +	void setScoringWentThroughPressureDoor(const bool = true); +	void setScoringPreppedSub(const bool = true); +	void setScoringEnteredSub(const bool = true); +	void setScoringExitedSub(const bool = true); +	void setScoringSawRobotAt54North(const bool = true); +	void setScoringPlayedWithClaw(const bool = true); +	void setScoringUsedRetinalChip(const bool = true); +	void setScoringFinishedGlobeGame(const bool = true); +	void setScoringStoppedNoradRobot(const bool = true); +	void setScoringGotNoradOpMemChip(const bool = true); +	void setScoringFinishedNorad(const bool = true); + +	// WSC scoring +	void setScoringRemovedDart(const bool = true); +	void setScoringAnalyzedDart(const bool = true); +	void setScoringBuiltAntidote(const bool = true); +	void setScoringGotSinclairKey(const bool = true); +	void setScoringGotArgonCanister(const bool = true); +	void setScoringGotNitrogenCanister(const bool = true); +	void setScoringPlayedWithMessages(const bool = true); +	void setScoringSawMorphExperiment(const bool = true); +	void setScoringEnteredSinclairOffice(const bool = true); +	void setScoringSawBrochure(const bool = true); +	void setScoringSawSinclairEntry1(const bool = true); +	void setScoringSawSinclairEntry2(const bool = true); +	void setScoringSawSinclairEntry3(const bool = true); +	void setScoringSawWSCDirectory(const bool = true); +	void setScoringUsedCrowBarInWSC(const bool = true); +	void setScoringFinishedPlasmaDodge(const bool = true); +	void setScoringOpenedCatwalk(const bool = true); +	void setScoringStoppedWSCRobot(const bool = true); +	void setScoringGotWSCOpMemChip(const bool = true); +	void setScoringFinishedWSC(const bool = true); + +	// Gandhi scoring +	void setScoringMarsGandhi(const bool = true); +	void setScoringNoradGandhi(const bool = true); +	void setScoringWSCGandhi(const bool = true); +	 +	// Scoring "Get" functions. +	bool getScoringSawINN(); +	bool getScoringTookShower(); +	bool getScoringFixedHair(); +	bool getScoringGotKeyCard(); +	bool getScoringReadPaper(); +	bool getScoringLookThroughTelescope(); +	bool getScoringSawCaldoriaKiosk(); +	bool getScoringGoToTSA(); +	bool getScoringEnterTSA(); +	bool getScoringSawBust1(); +	bool getScoringSawBust2(); +	bool getScoringSawBust3(); +	bool getScoringSawBust4(); +	bool getScoringSawBust5(); +	bool getScoringSawBust6(); +	bool getScoringSawTheory(); +	bool getScoringSawBackground(); +	bool getScoringSawProcedure(); +	bool getScoringGotJourneymanKey(); +	bool getScoringGotPegasusBiochip(); +	bool getScoringGotBiosuit(); +	bool getScoringGoToPrehistoric(); +	bool getScoringPutLogInReader(); +	bool getScoringSawCaldoriaNormal(); +	bool getScoringSawCaldoriaAltered(); +	bool getScoringSawNoradNormal(); +	bool getScoringSawNoradAltered(); +	bool getScoringSawMarsNormal(); +	bool getScoringSawMarsAltered(); +	bool getScoringSawWSCNormal(); +	bool getScoringSawWSCAltered(); +	bool getScoringWentToReadyRoom2(); +	bool getScoringWentAfterSinclair(); +	bool getScoringUsedCardBomb(); +	bool getScoringShieldedCardBomb(); +	bool getScoringStunnedSinclair(); +	bool getScoringDisarmedNuke(); +	bool getScoringThrewBreaker(); +	bool getScoringExtendedBridge(); +	bool getScoringGotHistoricalLog(); +	bool getScoringFinishedPrehistoric(); +	bool getScoringThrownByRobot(); +	bool getScoringGotMarsCard(); +	bool getScoringSawMarsKiosk(); +	bool getScoringSawTransportMap(); +	bool getScoringGotCrowBar(); +	bool getScoringTurnedOnTransport(); +	bool getScoringGotOxygenMask(); +	bool getScoringAvoidedRobot(); +	bool getScoringActivatedPlatform(); +	bool getScoringUsedLiquidNitrogen(); +	bool getScoringUsedCrowBar(); +	bool getScoringFoundCardBomb(); +	bool getScoringDisarmedCardBomb(); +	bool getScoringGotCardBomb(); +	bool getScoringThreadedMaze(); +	bool getScoringThreadedGearRoom(); +	bool getScoringEnteredShuttle(); +	bool getScoringEnteredLaunchTube(); +	bool getScoringStoppedRobotsShuttle(); +	bool getScoringGotMarsOpMemChip(); +	bool getScoringFinishedMars(); +	bool getScoringSawSecurityMonitor(); +	bool getScoringFilledOxygenCanister(); +	bool getScoringFilledArgonCanister(); +	bool getScoringSawUnconsciousOperator(); +	bool getScoringWentThroughPressureDoor(); +	bool getScoringPreppedSub(); +	bool getScoringEnteredSub(); +	bool getScoringExitedSub(); +	bool getScoringSawRobotAt54North(); +	bool getScoringPlayedWithClaw(); +	bool getScoringUsedRetinalChip(); +	bool getScoringFinishedGlobeGame(); +	bool getScoringStoppedNoradRobot(); +	bool getScoringGotNoradOpMemChip(); +	bool getScoringFinishedNorad(); +	bool getScoringRemovedDart(); +	bool getScoringAnalyzedDart(); +	bool getScoringBuiltAntidote(); +	bool getScoringGotSinclairKey(); +	bool getScoringGotArgonCanister(); +	bool getScoringGotNitrogenCanister(); +	bool getScoringPlayedWithMessages(); +	bool getScoringSawMorphExperiment(); +	bool getScoringEnteredSinclairOffice(); +	bool getScoringSawBrochure(); +	bool getScoringSawSinclairEntry1(); +	bool getScoringSawSinclairEntry2(); +	bool getScoringSawSinclairEntry3(); +	bool getScoringSawWSCDirectory(); +	bool getScoringUsedCrowBarInWSC(); +	bool getScoringFinishedPlasmaDodge(); +	bool getScoringOpenedCatwalk(); +	bool getScoringStoppedWSCRobot(); +	bool getScoringGotWSCOpMemChip(); +	bool getScoringFinishedWSC(); +	bool getScoringMarsGandhi(); +	bool getScoringNoradGandhi(); +	bool getScoringWSCGandhi(); + +	GameScoreType getCaldoriaTSAScore(); +	GameScoreType getPrehistoricScore(); +	GameScoreType getMarsScore(); +	GameScoreType getNoradScore(); +	GameScoreType getWSCScore(); +	GameScoreType getGandhiScore(); +	GameScoreType getTotalScore(); + +	void writeCaldoriaState(Common::WriteStream *stream); +	void readCaldoriaState(Common::ReadStream *stream); +	void resetCaldoriaState(); +	 +	void writeTSAState(Common::WriteStream *stream); +	void readTSAState(Common::ReadStream *stream); +	void resetTSAState(); +	 +	void writePrehistoricState(Common::WriteStream *stream); +	void readPrehistoricState(Common::ReadStream *stream); +	void resetPrehistoricState(); +	 +	void writeNoradState(Common::WriteStream *stream); +	void readNoradState(Common::ReadStream *stream); +	void resetNoradState(); +	 +	void writeMarsState(Common::WriteStream *stream); +	void readMarsState(Common::ReadStream *stream); +	void resetMarsState(); +	 +	void writeWSCState(Common::WriteStream *stream); +	void readWSCState(Common::ReadStream *stream); +	void resetWSCState(); +	 +	// Globals. +	void setWalkthroughMode(bool); +	bool getWalkthroughMode(); +	void setShieldOn(bool); +	bool getShieldOn(); +	void setEasterEgg(bool); +	bool getEasterEgg(); +	void setBeenToWSC(bool value); +	bool getBeenToWSC(); +	void setBeenToMars(bool value); +	bool getBeenToMars(); +	void setBeenToNorad(bool value); +	bool getBeenToNorad(); +	void setWSCFinished(bool); +	bool getWSCFinished(); +	void setMarsFinished(bool); +	bool getMarsFinished(); +	void setNoradFinished(bool); +	bool getNoradFinished(); +	bool allTimeZonesFinished(); +	void setTakenItemID(ItemID, bool); +	bool isTakenItemID(ItemID); +	void setTakenItem(Item *, bool); +	bool isTakenItem(Item *); +	 +	// Caldoria +	void setCaldoriaFuseTimeLimit(const TimeValue); +	TimeValue getCaldoriaFuseTimeLimit(); +	void setCaldoriaSeenPullback(bool); +	bool getCaldoriaSeenPullback(); +	void setCaldoriaMadeOJ(bool); +	bool getCaldoriaMadeOJ(); +	void setCaldoriaWokenUp(bool); +	bool getCaldoriaWokenUp(); +	void setCaldoriaDidRecalibration(bool); +	bool getCaldoriaDidRecalibration(); +	void setCaldoriaSeenSinclairInElevator(bool); +	bool getCaldoriaSeenSinclairInElevator(); +	void setCaldoriaINNAnnouncing(bool); +	bool getCaldoriaINNAnnouncing(); +	void setCaldoriaSeenINN(bool); +	bool getCaldoriaSeenINN(); +	void setCaldoriaSeenMessages(bool); +	bool getCaldoriaSeenMessages(); +	void setCaldoriaSinclairShot(bool); +	bool getCaldoriaSinclairShot(); +	void setCaldoriaBombDisarmed(bool); +	bool getCaldoriaBombDisarmed(); +	void setCaldoriaRoofDoorOpen(bool); +	bool getCaldoriaRoofDoorOpen(); +	void setCaldoriaDoneHygiene(bool); +	bool getCaldoriaDoneHygiene(); +	void setCaldoriaSawVoiceAnalysis(bool); +	bool getCaldoriaSawVoiceAnalysis(); +	void setCaldoriaDoorBombed(bool); +	bool getCaldoriaDoorBombed(); +	void setCaldoriaGunAimed(bool); +	bool getCaldoriaGunAimed(); +	 +	// TSA +	void setRipTimerTime(TimeValue); +	TimeValue getRipTimerTime(); +	void setTSAFuseTimeLimit(TimeValue); +	TimeValue getTSAFuseTimeLimit(); +	void setT0BMonitorMode(byte); +	byte getT0BMonitorMode(); +	void setTSAState(byte); +	byte getTSAState(); +	void setT0BMonitorStart(TimeValue); +	TimeValue getT0BMonitorStart(); +	void setTSAIDedAtDoor(bool); +	bool getTSAIDedAtDoor(); +	void setTSA0BZoomedIn(bool); +	bool getTSA0BZoomedIn(); +	void setTSAFrontDoorUnlockedOutside(bool); +	bool getTSAFrontDoorUnlockedOutside(); +	void setTSAFrontDoorUnlockedInside(bool); +	bool getTSAFrontDoorUnlockedInside(); +	void setTSASeenRobotGreeting(bool); +	bool getTSASeenRobotGreeting(); +	void setTSASeenTheory(bool); +	bool getTSASeenTheory(); +	void setTSASeenBackground(bool); +	bool getTSASeenBackground(); +	void setTSASeenProcedure(bool); +	bool getTSASeenProcedure(); +	void setTSASeenAgent3AtDoor(bool); +	bool getTSASeenAgent3AtDoor(); +	void setTSACommandCenterLocked(bool); +	bool getTSACommandCenterLocked(); +	void setTSASeenCaldoriaNormal(bool); +	bool getTSASeenCaldoriaNormal(); +	void setTSASeenCaldoriaAltered(bool); +	bool getTSASeenCaldoriaAltered(); +	void setTSASeenNoradNormal(bool); +	bool getTSASeenNoradNormal(); +	void setTSASeenNoradAltered(bool); +	bool getTSASeenNoradAltered(); +	void setTSASeenMarsNormal(bool); +	bool getTSASeenMarsNormal(); +	void setTSASeenMarsAltered(bool); +	bool getTSASeenMarsAltered(); +	void setTSASeenWSCNormal(bool); +	bool getTSASeenWSCNormal(); +	void setTSASeenWSCAltered(bool); +	bool getTSASeenWSCAltered(); +	void setTSABiosuitOn(bool); +	bool getTSABiosuitOn(); +	 +	// Prehistoric +	void setPrehistoricTriedToExtendBridge(bool); +	bool getPrehistoricTriedToExtendBridge(); +	void setPrehistoricSeenTimeStream(bool); +	bool getPrehistoricSeenTimeStream(); +	void setPrehistoricSeenFlyer1(bool); +	bool getPrehistoricSeenFlyer1(); +	void setPrehistoricSeenFlyer2(bool); +	bool getPrehistoricSeenFlyer2(); +	void setPrehistoricSeenBridgeZoom(bool); +	bool getPrehistoricSeenBridgeZoom(); +	void setPrehistoricBreakerThrown(bool); +	bool getPrehistoricBreakerThrown(); +	 +	// Norad +	void setNoradSeenTimeStream(bool); +	bool getNoradSeenTimeStream(); +	void setNoradGassed(bool); +	bool getNoradGassed(); +	void setNoradFillingStationOn(bool); +	bool getNoradFillingStationOn(); +	void setNoradN22MessagePlayed(bool); +	bool getNoradN22MessagePlayed(); +	void setNoradPlayedGlobeGame(bool); +	bool getNoradPlayedGlobeGame(); +	void setNoradBeatRobotWithClaw(bool); +	bool getNoradBeatRobotWithClaw(); +	void setNoradBeatRobotWithDoor(bool); +	bool getNoradBeatRobotWithDoor(); +	void setNoradRetScanGood(bool); +	bool getNoradRetScanGood(); +	void setNoradWaitingForLaser(bool); +	bool getNoradWaitingForLaser(); +	void setNoradSubRoomPressure(uint16); +	uint16 getNoradSubRoomPressure(); +	void setNoradSubPrepState(NoradSubPrepState); +	NoradSubPrepState getNoradSubPrepState(); +	void setNoradArrivedFromSub(bool); +	bool getNoradArrivedFromSub(); +	 +	// Mars +	void setMarsSeenTimeStream(bool); +	bool getMarsSeenTimeStream();	 +	void setMarsHeardUpperPodMessage(bool); +	bool getMarsHeardUpperPodMessage(); +	void setMarsRobotThrownPlayer(bool); +	bool getMarsRobotThrownPlayer(); +	void setMarsHeardCheckInMessage(bool); +	bool getMarsHeardCheckInMessage(); +	void setMarsPodAtUpperPlatform(bool); +	bool getMarsPodAtUpperPlatform(); +	void setMarsSeenThermalScan(bool); +	bool getMarsSeenThermalScan(); +	void setMarsArrivedBelow(bool); +	bool getMarsArrivedBelow(); +	void setMarsSeenRobotAtReactor(bool); +	bool getMarsSeenRobotAtReactor(); +	void setMarsAvoidedReactorRobot(bool); +	bool getMarsAvoidedReactorRobot(); +	void setMarsInAirlock(bool); +	bool getMarsInAirlock(); +	void setMarsAirlockOpen(bool); +	bool getMarsAirlockOpen(); +	void setMarsMaskOnFiller(bool); +	bool getMarsMaskOnFiller(); +	void setMarsLockFrozen(bool); +	bool getMarsLockFrozen(); +	void setMarsLockBroken(bool); +	bool getMarsLockBroken(); +	void setMarsMazeDoorPair1(bool); +	bool getMarsMazeDoorPair1(); +	void setMarsMazeDoorPair2(bool); +	bool getMarsMazeDoorPair2(); +	void setMarsMazeDoorPair3(bool); +	bool getMarsMazeDoorPair3(); +	void setMarsSawRobotLeave(bool); +	bool getMarsSawRobotLeave(); +	void setMarsSecurityDown(bool); +	bool getMarsSecurityDown(); +	void setMarsFinishedCanyonChase(bool); +	bool getMarsFinishedCanyonChase(); +	void setMarsThreadedMaze(bool); +	bool getMarsThreadedMaze(); +	void setMarsHitRobotWithCannon(bool); +	bool getMarsHitRobotWithCannon(); +	void setMarsReadyForShuttleTransport(bool); +	bool getMarsReadyForShuttleTransport(); + +	// WSC +	void setWSCSeenTimeStream(bool); +	bool getWSCSeenTimeStream(); +	void setWSCPoisoned(bool); +	bool getWSCPoisoned(); +	void setWSCAnsweredAboutDart(bool); +	bool getWSCAnsweredAboutDart(); +	void setWSCRemovedDart(bool); +	bool getWSCRemovedDart(); +	void setWSCAnalyzerOn(bool); +	bool getWSCAnalyzerOn(); +	void setWSCDartInAnalyzer(bool); +	bool getWSCDartInAnalyzer(); +	void setWSCAnalyzedDart(bool); +	bool getWSCAnalyzedDart(); +	void setWSCSawMorph(bool); +	bool getWSCSawMorph(); +	void setWSCDesignedAntidote(bool); +	bool getWSCDesignedAntidote(); +	void setWSCPickedUpAntidote(bool); +	bool getWSCPickedUpAntidote(); +	void setWSCOfficeMessagesOpen(bool); +	bool getWSCOfficeMessagesOpen(); +	void setWSCSeenNerd(bool); +	bool getWSCSeenNerd(); +	void setWSCHeardPage1(bool); +	bool getWSCHeardPage1(); +	void setWSCHeardPage2(bool); +	bool getWSCHeardPage2(); +	void setWSCHeardCheckIn(bool); +	bool getWSCHeardCheckIn(); +	void setWSCDidPlasmaDodge(bool); +	bool getWSCDidPlasmaDodge(); +	void setWSCSeenSinclairLecture(bool); +	bool getWSCSeenSinclairLecture(); +	void setWSCBeenAtWSC93(bool); +	bool getWSCBeenAtWSC93(); +	void setWSCCatwalkDark(bool); +	bool getWSCCatwalkDark(); +	void setWSCRobotDead(bool); +	bool getWSCRobotDead(); +	void setWSCRobotGone(bool); +	bool getWSCRobotGone(); + +protected: +	friend class Common::Singleton<SingletonBaseType>; + +private: +	// Base +	NeighborhoodID _currentNeighborhood; +	RoomID _currentRoom; +	DirectionConstant _currentDirection; +	NeighborhoodID _nexNeighborhoodID; +	RoomID _nextRoomID; +	DirectionConstant _nextDirection; +	NeighborhoodID _lastNeighborhood; +	RoomID _lastRoom; +	DirectionConstant _lastDirection; +	RoomID _openDoorRoom; +	DirectionConstant _openDoorDirection; + +	// Pegasus Prime +	FlagsArray<byte, kNumGlobalFlags> _globalFlags; +	FlagsArray<byte, kNumScoringFlags> _scoringFlags; +	FlagsArray<uint32, kNumItems> _itemTakenFlags; +	 +	FlagsArray<byte, kNumCaldoriaFlags> _caldoriaFlags; +	TimeValue _caldoriaFuseTimeLimit; +	 +	TimeValue _TSARipTimerTime; +	TimeValue _TSAFuseTimeLimit; +	byte _TSAState; +	byte _T0BMonitorMode; +	TimeValue _T0BMonitorStart; +	FlagsArray<byte, kNumTSAFlags> _TSAFlags; +	 +	FlagsArray<byte, kNumPrehistoricFlags> _prehistoricFlags; +	 +	FlagsArray<byte, kNumNoradFlags> _noradFlags; +	uint16 _noradSubRoomPressure; +	NoradSubPrepState _noradSubPrepState; +	 +	FlagsArray<byte, kNumMarsFlags> _marsFlags; +	 +	FlagsArray<byte, kNumWSCFlags> _WSCFlags; +}; + +} // End of namespace Pegasus + +#define GameState (::Pegasus::GameStateManager::instance()) + +#endif diff --git a/engines/pegasus/graphics.cpp b/engines/pegasus/graphics.cpp new file mode 100644 index 0000000000..175f15e91d --- /dev/null +++ b/engines/pegasus/graphics.cpp @@ -0,0 +1,337 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/events.h" +#include "common/file.h" +#include "common/textconsole.h" +#include "engines/util.h" + +#include "pegasus/elements.h" +#include "pegasus/graphics.h" + +namespace Pegasus { + +GraphicsManager::GraphicsManager(PegasusEngine *vm) : _vm(vm) { +	initGraphics(640, 480, true, NULL); + +	if (_vm->_system->getScreenFormat().bytesPerPixel == 1) +		error("No true color mode available"); + +	_backLayer = kMinAvailableOrder; +	_frontLayer = kMaxAvailableOrder; +	_firstDisplayElement = _lastDisplayElement = 0; +	_workArea.create(640, 480, _vm->_system->getScreenFormat()); +	_modifiedScreen = false; +	_curSurface = &_workArea; +	_erase = false; +} +	 +GraphicsManager::~GraphicsManager() { +	_workArea.free(); +} + +void GraphicsManager::invalRect(const Common::Rect &rect) { +	// We're using a simpler algorithm for dirty rect handling than the original +	// The original was way too overcomplicated for what we need here now. + +	if (_dirtyRect.width() == 0 || _dirtyRect.height() == 0) { +		// We have no dirty rect, so this is now our dirty rect +		_dirtyRect = rect; +	} else { +		// Expand our dirty rect to include rect +		_dirtyRect.extend(rect); +	} + +	// Sanity check: clip our rect to the screen +	_dirtyRect.right = MIN<int>(640, _dirtyRect.right); +	_dirtyRect.bottom = MIN<int>(480, _dirtyRect.bottom); +} + +void GraphicsManager::addDisplayElement(DisplayElement *newElement) { +	newElement->_elementOrder = CLIP<int>(newElement->_elementOrder, kMinAvailableOrder, kMaxAvailableOrder); + +	if (_firstDisplayElement) { +		DisplayElement *runner = _firstDisplayElement; +		DisplayElement *lastRunner = 0; + +		// Search for first element whose display order is greater than +		// the new element's and add the new element just before it. +		while (runner) { +			if (newElement->_elementOrder < runner->_elementOrder) { +				if (lastRunner) { +					lastRunner->_nextElement = newElement; +					newElement->_nextElement = runner; +				} else { +					newElement->_nextElement = _firstDisplayElement; +					_firstDisplayElement = newElement; +				} +				break; +			} +			lastRunner = runner; +			runner = runner->_nextElement; +		} + +		// If got here and runner == NULL, we ran through the whole list without +		// inserting, so add at the end. +		if (!runner) { +			_lastDisplayElement->_nextElement = newElement; +			_lastDisplayElement = newElement; +		} +	} else { +		_firstDisplayElement = newElement; +		_lastDisplayElement = newElement; +	} + +	newElement->_elementIsDisplaying = true; +} + +void GraphicsManager::removeDisplayElement(DisplayElement *oldElement) { +	if (!_firstDisplayElement) +		return; + +	if (oldElement == _firstDisplayElement) { +		if (oldElement == _lastDisplayElement) { +			_firstDisplayElement = 0; +			_lastDisplayElement = 0; +		} else { +			_firstDisplayElement = oldElement->_nextElement; +		} + +		invalRect(oldElement->_bounds); +	} else { +		// Scan list for element. +		// If we get here, we know that the list has at least one item, and it +		// is not the first item, so we can skip it. +		DisplayElement *runner = _firstDisplayElement->_nextElement; +		DisplayElement *lastRunner = _firstDisplayElement; + +		while (runner) { +			if (runner == oldElement) { +				lastRunner->_nextElement = runner->_nextElement; + +				if (oldElement == _lastDisplayElement) +					_lastDisplayElement = lastRunner; + +				invalRect(oldElement->_bounds); +				break; +			} + +			lastRunner = runner; +			runner = runner->_nextElement; +		} +	} + +	oldElement->_nextElement = 0; +	oldElement->_elementIsDisplaying = false; +} + +void GraphicsManager::updateDisplay() { +	bool screenDirty = false; + +	if (!_dirtyRect.isEmpty()) { +		// Fill the dirty area with black if erase mode is enabled +		if (_erase) +			_workArea.fillRect(_dirtyRect, _workArea.format.RGBToColor(0, 0, 0)); + +		for (DisplayElement *runner = _firstDisplayElement; runner != 0; runner = runner->_nextElement) { +			Common::Rect bounds; +			runner->getBounds(bounds); + +			// TODO: Better logic; it does a bit more work than it probably needs to +			// but it should work fine for now. +			if (bounds.intersects(_dirtyRect) && runner->validToDraw(_backLayer, _frontLayer)) { +				runner->draw(bounds); +				screenDirty = true; +			} +		} + +		// Copy only the dirty rect to the screen +		if (screenDirty) +			g_system->copyRectToScreen((byte *)_workArea.getBasePtr(_dirtyRect.left, _dirtyRect.top), _workArea.pitch, _dirtyRect.left, _dirtyRect.top, _dirtyRect.width(), _dirtyRect.height()); + +		// Clear the dirty rect +		_dirtyRect = Common::Rect(); +	} + +	if (screenDirty || _modifiedScreen) +		g_system->updateScreen(); + +	_modifiedScreen = false; +} + +void GraphicsManager::clearScreen() { +	Graphics::Surface *screen = g_system->lockScreen(); +	screen->fillRect(Common::Rect(0, 0, 640, 480), g_system->getScreenFormat().RGBToColor(0, 0, 0)); +	g_system->unlockScreen(); +	_modifiedScreen = true; +} + +DisplayElement *GraphicsManager::findDisplayElement(const DisplayElementID id) { +	DisplayElement *runner = _firstDisplayElement; + +	while (runner) { +		if (runner->getObjectID() == id) +			return runner; +		runner = runner->_nextElement; +	} + +	return 0; +} + +void GraphicsManager::doFadeOutSync(const TimeValue, const TimeValue, uint32 color) { +	if (color == 0) +		color = g_system->getScreenFormat().RGBToColor(0, 0, 0); + +	// HACK: Until fading out is done, white-out the screen here +	Graphics::Surface *screen = g_system->lockScreen(); +	screen->fillRect(Common::Rect(0, 0, 640, 480), color); +	g_system->unlockScreen(); +	g_system->updateScreen(); +} + +void GraphicsManager::doFadeInSync(const TimeValue, const TimeValue, uint32) { +	// TODO +} + +void GraphicsManager::markCursorAsDirty() { +	_modifiedScreen = true; +} + +void GraphicsManager::newShakePoint(int32 index1, int32 index2, int32 maxRadius) { +	int32 index3 = (index1 + index2) >> 1; + +	if (maxRadius == 0) { +		_shakeOffsets[index3].x = ((_shakeOffsets[index1].x + _shakeOffsets[index2].x) >> 1); +		_shakeOffsets[index3].y = ((_shakeOffsets[index1].y + _shakeOffsets[index2].y) >> 1); +	} else { +		double angle = (int32)(_vm->getRandomNumber(360 - 1) * 3.1415926535 / 180); +		int32 radius = maxRadius; +		_shakeOffsets[index3].x = (int32)(((_shakeOffsets[index1].x + _shakeOffsets[index2].x) >> 1) + +				cos(angle) / 2 * radius); +		_shakeOffsets[index3].y = (int32)(((_shakeOffsets[index1].y + _shakeOffsets[index2].y) >> 1) + +				sin(angle) * radius); +	} + +	if (index1 < index3 - 1) +		newShakePoint(index1, index3, maxRadius * 2 / 3); + +	if (index3 < index2 - 1) +		newShakePoint(index3, index2, maxRadius * 2 / 3); +} + +void GraphicsManager::shakeTheWorld(TimeValue duration, TimeScale scale) { +	if (duration == 0 || scale == 0) +		return; + +	_shakeOffsets[0].x = 0; +	_shakeOffsets[0].y = 0; +	_shakeOffsets[(kMaxShakeOffsets - 1) / 4].x = 0; +	_shakeOffsets[(kMaxShakeOffsets - 1) / 4].y = 0; +	_shakeOffsets[(kMaxShakeOffsets - 1) / 2].x = 0; +	_shakeOffsets[(kMaxShakeOffsets - 1) / 2].y = 0; +	_shakeOffsets[(kMaxShakeOffsets - 1) * 3 / 4].x = 0; +	_shakeOffsets[(kMaxShakeOffsets - 1) * 3 / 4].y = 0; +	_shakeOffsets[kMaxShakeOffsets - 1].x = 0; +	_shakeOffsets[kMaxShakeOffsets - 1].y = 0; + +	newShakePoint(0, (kMaxShakeOffsets - 1) / 4, 8); +	newShakePoint((kMaxShakeOffsets - 1) / 4, (kMaxShakeOffsets - 1) / 2, 6); +	newShakePoint((kMaxShakeOffsets - 1) / 2, (kMaxShakeOffsets - 1) * 3 / 4, 4); +	newShakePoint((kMaxShakeOffsets - 1) * 3 / 4, kMaxShakeOffsets - 1, 3); + +	Common::Point lastOffset(0, 0); + +	// Store the current screen for later use +	Graphics::Surface oldScreen; +	Graphics::Surface *curScreen = g_system->lockScreen(); +	oldScreen.copyFrom(*curScreen); +	g_system->unlockScreen(); + +	// Convert to millis +	duration = duration * 1000 / scale; +	 +	uint32 startTime = g_system->getMillis(); + +	while (g_system->getMillis() < startTime + duration) { +		Common::Point thisOffset = _shakeOffsets[(g_system->getMillis() - startTime) * (kMaxShakeOffsets - 1) / duration]; +		if (thisOffset != lastOffset) { +			// Fill the screen with black +			Graphics::Surface *screen = g_system->lockScreen(); +			screen->fillRect(Common::Rect(0, 0, 640, 480), g_system->getScreenFormat().RGBToColor(0, 0, 0)); +			g_system->unlockScreen(); + +			// Calculate the src/dst offsets and the width/height +			int32 srcOffsetX, dstOffsetX, width; + +			if (thisOffset.x > 0) { +				srcOffsetX = 0; +				dstOffsetX = thisOffset.x; +				width = 640 - dstOffsetX; +			} else { +				srcOffsetX = -thisOffset.x; +				dstOffsetX = 0; +				width = 640 - srcOffsetX; +			} + +			int32 srcOffsetY, dstOffsetY, height; + +			if (thisOffset.y > 0) { +				srcOffsetY = 0; +				dstOffsetY = thisOffset.y; +				height = 480 - dstOffsetY; +			} else { +				srcOffsetY = -thisOffset.y; +				dstOffsetY = 0; +				height = 480 - srcOffsetY; +			} + +			// Now copy to the screen +			g_system->copyRectToScreen((byte *)oldScreen.getBasePtr(srcOffsetX, srcOffsetY), oldScreen.pitch, +					dstOffsetX, dstOffsetY, width, height); +			g_system->updateScreen(); + +			lastOffset = thisOffset; +		} + +		g_system->delayMillis(10); +	} + +	if (lastOffset.x != 0 || lastOffset.y != 0) { +		g_system->copyRectToScreen((byte *)oldScreen.pixels, oldScreen.pitch, 0, 0, 640, 480); +		g_system->updateScreen(); +	} + +	oldScreen.free(); +} + +void GraphicsManager::enableErase() { +	_erase = true; +} + +void GraphicsManager::disableErase() { +	_erase = false; +} +	 +} // End of namespace Pegasus diff --git a/engines/pegasus/graphics.h b/engines/pegasus/graphics.h new file mode 100644 index 0000000000..502304409a --- /dev/null +++ b/engines/pegasus/graphics.h @@ -0,0 +1,89 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_GRAPHICS_H +#define PEGASUS_GRAPHICS_H + +#include "common/rect.h" +#include "common/str.h" +#include "common/system.h" +#include "graphics/surface.h" + +#include "pegasus/constants.h" +#include "pegasus/pegasus.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class Cursor; +class DisplayElement; +class PegasusEngine; + +class GraphicsManager { +friend class Cursor; +public: +	GraphicsManager(PegasusEngine *vm); +	~GraphicsManager(); + +	void addDisplayElement(DisplayElement *element); +	void removeDisplayElement(DisplayElement *element); +	void invalRect(const Common::Rect &rect); +	DisplayOrder getBackOfActiveLayer() const { return _backLayer; } +	DisplayOrder getFrontOfActiveLayer() const { return _frontLayer; } +	void updateDisplay(); +	Graphics::Surface *getCurSurface() { return _curSurface; } +	void setCurSurface(Graphics::Surface *surface) { _curSurface = surface; } +	Graphics::Surface *getWorkArea() { return &_workArea; } +	void clearScreen(); +	DisplayElement *findDisplayElement(const DisplayElementID id); +	void shakeTheWorld(TimeValue time, TimeScale scale); +	void enableErase(); +	void disableErase(); + +	// These default to black +	void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, uint32 color = 0); +	void doFadeInSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, uint32 color = 0); + +protected: +	void markCursorAsDirty(); + +private:		 +	PegasusEngine *_vm; + +	bool _modifiedScreen, _erase; +	Common::Rect _dirtyRect; +	DisplayOrder _backLayer, _frontLayer; +	DisplayElement *_firstDisplayElement, *_lastDisplayElement; +	Graphics::Surface _workArea, *_curSurface; + +	// Shake Shake Shake! +	static const int kMaxShakeOffsets = 17; +	Common::Point _shakeOffsets[kMaxShakeOffsets]; +	void newShakePoint(int32 index1, int32 index2, int32 maxRadius); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/hotspot.cpp b/engines/pegasus/hotspot.cpp new file mode 100644 index 0000000000..84731b7a06 --- /dev/null +++ b/engines/pegasus/hotspot.cpp @@ -0,0 +1,327 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/stream.h" + +#include "pegasus/hotspot.h" + +namespace Pegasus { + +HotspotList g_allHotspots; + +Region::Region(Common::ReadStream *stream) { +	uint16 length = stream->readUint16BE(); + +	assert(length >= 10); + +	_bounds.top = stream->readUint16BE(); +	_bounds.left = stream->readUint16BE(); +	_bounds.bottom = stream->readUint16BE(); +	_bounds.right = stream->readUint16BE(); + +	_bounds.debugPrint(0, "Bounds:"); + +	if (length == 10) +		return; + +	length -= 10; + +	while (length > 0) { +		Vector v; +		v.y = stream->readUint16BE(); +		length -= 2; + +		if (v.y == 0x7fff) +			break; + +		debug(0, "y: %d", v.y); + +		// Normalize y to _bounds +		v.y -= _bounds.top; + +		while (length > 0) { +			Run run; +			run.start = stream->readUint16BE(); +			length -= 2; + +			if (run.start == 0x7fff) +				break; + +			run.end = stream->readUint16BE(); +			length -= 2; + +			debug(0, "\t[%d, %d)", run.start, run.end); + +			// Normalize to _bounds +			run.start -= _bounds.left; +			run.end -= _bounds.left; + +			v.push_back(run); +		} + +		_vectors.push_back(v); +	} +} + +Region::Region(const Common::Rect &rect) { +	_bounds = rect; +} + +bool Region::pointInRegion(const Common::Point &point) const { +	if (!_bounds.contains(point)) +		return false; + +	bool pixelActive = false; + +	// Normalize the points to _bounds +	uint16 x = point.x - _bounds.left; +	uint16 y = point.y - _bounds.top; + +	for (Common::List<Vector>::const_iterator v = _vectors.begin(); v != _vectors.end(); v++) { +		if (v->y > y) +			return pixelActive; + +		for (Vector::const_iterator run = v->begin(); run != v->end(); run++) { +			if (x >= run->start && x < run->end) { +				pixelActive = !pixelActive; +				break; +			} +		} +	} + +	// the case if the region is just a rect +	return true; +} + +void Region::moveTo(CoordType h, CoordType v) { +	_bounds.moveTo(h, v); +} + +void Region::moveTo(const Common::Point &point) { +	_bounds.moveTo(point); +} + +void Region::translate(CoordType h, CoordType v) { +	_bounds.translate(h, v); +} + +void Region::translate(const Common::Point &point) { +	_bounds.translate(point.x, point.y); +} + +void Region::getCenter(CoordType &h, CoordType &v) const { +	h = (_bounds.left + _bounds.right) / 2; +	v = (_bounds.top + _bounds.bottom) / 2; +} + +void Region::getCenter(Common::Point &point) const { +	getCenter(point.x, point.y); +} + +Hotspot::Hotspot(const HotSpotID id) : IDObject(id) { +	_spotFlags = kNoHotSpotFlags; +	_spotActive = false; +} + +Hotspot::~Hotspot() { +} + +void Hotspot::setArea(const Common::Rect &area) { +	_spotArea = Region(area); +} + +void Hotspot::setArea(const CoordType left, const CoordType top, const CoordType right, const CoordType bottom) { +	_spotArea = Region(Common::Rect(left, top, right, bottom)); +} + +void Hotspot::getBoundingBox(Common::Rect &r) const { +	r = _spotArea.getBoundingBox(); +} + +void Hotspot::getCenter(Common::Point &pt) const { +	_spotArea.getCenter(pt); +} + +void Hotspot::getCenter(CoordType &h, CoordType &v) const { +	_spotArea.getCenter(h, v); +} + +void Hotspot::setActive() { +	_spotActive = true; +} + +void Hotspot::setInactive() { +	_spotActive = false; +} + +void Hotspot::setHotspotFlags(const HotSpotFlags flags) { +	_spotFlags = flags; +} + +void Hotspot::setMaskedHotspotFlags(const HotSpotFlags flags, const HotSpotFlags mask) { +	_spotFlags = (_spotFlags & ~mask) | flags; +} + +bool Hotspot::isSpotActive() const { +	return _spotActive; +} + +void Hotspot::moveSpotTo(const CoordType h, const CoordType v) { +	_spotArea.moveTo(h, v); +} + +void Hotspot::moveSpotTo(const Common::Point pt) { +	_spotArea.moveTo(pt); +} + +void Hotspot::moveSpot(const CoordType h, const CoordType v) { +	_spotArea.translate(h, v); +} + +void Hotspot::moveSpot(const Common::Point pt) { +	_spotArea.translate(pt.x, pt.y); +} + +bool Hotspot::pointInSpot(const Common::Point where) const { +	return _spotActive && _spotArea.pointInRegion(where); +} + +HotSpotFlags Hotspot::getHotspotFlags() const { +	return _spotFlags; +} + +HotspotList::HotspotList() { +} + +HotspotList::~HotspotList() { +	// TODO: Should this call deleteHotspots()? +} + +void HotspotList::deleteHotspots() { +	for (HotspotIterator it = begin(); it != end(); it++) +		delete *it; + +	clear(); +} + +Hotspot *HotspotList::findHotspot(const Common::Point where) { +	for (HotspotIterator it = begin(); it != end(); it++) +		if ((*it)->pointInSpot(where)) +			return *it; + +	return 0; +} + +HotSpotID HotspotList::findHotspotID(const Common::Point where) { +	Hotspot *hotspot = findHotspot(where); +	return hotspot ? hotspot->getObjectID() : kNoHotSpotID; +} + +Hotspot *HotspotList::findHotspotByID(const HotSpotID id) { +	for (HotspotIterator it = begin(); it != end(); it++) +		if ((*it)->getObjectID() == id) +			return *it; + +	return 0; +} + +Hotspot *HotspotList::findHotspotByMask(const HotSpotFlags flags) { +	for (HotspotIterator it = begin(); it != end(); it++) +		if (((*it)->getHotspotFlags() & flags) == flags) +			return *it; + +	return 0; +} + +void HotspotList::activateMaskedHotspots(const HotSpotFlags flags) { +	for (HotspotIterator it = begin(); it != end(); it++) +		if (flags == kNoHotSpotFlags || ((*it)->getHotspotFlags() & flags) != 0) +			(*it)->setActive(); +} + +void HotspotList::deactivateAllHotspots() { +	for (HotspotIterator it = begin(); it != end(); it++) +		(*it)->setInactive(); +} + +void HotspotList::deactivateMaskedHotspots(const HotSpotFlags flags) { +	for (HotspotIterator it = begin(); it != end(); it++) +		if (((*it)->getHotspotFlags() & flags) != 0) +			(*it)->setInactive(); +} + +void HotspotList::activateOneHotspot(const HotSpotID id) { +	for (HotspotIterator it = begin(); it != end(); it++) { +		if ((*it)->getObjectID() == id) { +			(*it)->setActive(); +			return; +		} +	} +} + +void HotspotList::deactivateOneHotspot(const HotSpotID id) { +	for (HotspotIterator it = begin(); it != end(); it++) { +		if ((*it)->getObjectID() == id) { +			(*it)->setInactive(); +			return; +		} +	} +} + +void HotspotList::removeOneHotspot(const HotSpotID id) { +	for (HotspotIterator it = begin(); it != end(); it++) { +		if ((*it)->getObjectID() == id) { +			erase(it); +			return; +		} +	} +} + +void HotspotList::removeMaskedHotspots(const HotSpotFlags flags) { +	if (flags != kNoHotSpotFlags) { +		for (HotspotIterator it = begin(); it != end(); ) { +			if (((*it)->getHotspotFlags() & flags) != 0) +				it = erase(it); +			else +				it++; +		} +	} else { +		clear(); +	} +} + +void HotspotList::setHotspotRect(const HotSpotID id, const Common::Rect &r) { +	Hotspot *hotspot = findHotspotByID(id); +	if (hotspot) +		hotspot->setArea(r); +} + +void HotspotList::getHotspotRect(const HotSpotID id, Common::Rect &r) { +	Hotspot *hotspot = findHotspotByID(id); +	if (hotspot) +		hotspot->getBoundingBox(r); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/hotspot.h b/engines/pegasus/hotspot.h new file mode 100644 index 0000000000..bc1d054f0d --- /dev/null +++ b/engines/pegasus/hotspot.h @@ -0,0 +1,153 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_HOTSPOT_H +#define PEGASUS_HOTSPOT_H + +#include "common/list.h" +#include "common/rect.h" + +#include "pegasus/constants.h" +#include "pegasus/types.h" +#include "pegasus/util.h" + +/* + +	Hot spots combine a pixel area, an ID value and an active flag. +	 +	A point is considered in a hot spot if the point is in the hot spot's pixel area and +	the active flag is set. +	 +	In addition, hot spots have a 32 bit word of bit flags for filtering use. + +*/ + +namespace Common { +	class ReadStream; +} + +namespace Pegasus { + +// Our implementation of QuickDraw regions +class Region { +public: +	Region() {} +	Region(Common::ReadStream *stream); +	Region(const Common::Rect &rect); + +	Common::Rect getBoundingBox() const { return _bounds; } + +	bool pointInRegion(const Common::Point &point) const; + +	void moveTo(CoordType h, CoordType v); +	void moveTo(const Common::Point &point); +	void translate(CoordType h, CoordType v); +	void translate(const Common::Point &point); +	void getCenter(CoordType &h, CoordType &v) const; +	void getCenter(Common::Point &point) const; + +private: +	Common::Rect _bounds; + +	struct Run { +		uint16 start, end; +	}; + +	class Vector : public Common::List<Run> { +	public: +		uint16 y; +	}; + +	Common::List<Vector> _vectors; +}; + +class Hotspot : public IDObject { +public: +	Hotspot(const HotSpotID); +	virtual ~Hotspot(); + +	void setArea(const Region ®ion) { _spotArea = region; } +	void setArea(const Common::Rect &); +	void setArea(const CoordType, const CoordType, const CoordType, const CoordType); +	void getBoundingBox(Common::Rect &) const; +	void getArea(Region &) const; +	void getCenter(Common::Point&) const; +	void getCenter(CoordType&, CoordType&) const; +	 +	void moveSpotTo(const CoordType, const CoordType); +	void moveSpotTo(const Common::Point); +	void moveSpot(const CoordType, const CoordType); +	void moveSpot(const Common::Point); +	 +	bool pointInSpot(const Common::Point) const; +	 +	void setActive(); +	void setInactive(); +	bool isSpotActive() const; +	 +	HotSpotFlags getHotspotFlags() const; +	void setHotspotFlags(const HotSpotFlags); +	void setMaskedHotspotFlags(const HotSpotFlags flags, const HotSpotFlags mask); + +protected: +	Region _spotArea; +	HotSpotFlags _spotFlags; +	bool _spotActive; +}; + +class HotspotList : public Common::List<Hotspot *> { +public: +	HotspotList(); +	virtual ~HotspotList(); + +	void deleteHotspots(); + +	Hotspot *findHotspot(const Common::Point); +	HotSpotID findHotspotID(const Common::Point); +	Hotspot *findHotspotByID(const HotSpotID); +	Hotspot *findHotspotByMask(const HotSpotFlags); + +	void activateMaskedHotspots(const HotSpotFlags = kNoHotSpotFlags); +	void deactivateAllHotspots(); +	void deactivateMaskedHotspots(const HotSpotFlags); + +	void activateOneHotspot(const HotSpotID); +	void deactivateOneHotspot(const HotSpotID); +	 +	void removeOneHotspot(const HotSpotID); +	void removeMaskedHotspots(const HotSpotFlags = kNoHotSpotFlags); + +	void setHotspotRect(const HotSpotID, const Common::Rect&); +	void getHotspotRect(const HotSpotID, Common::Rect&); +}; + +typedef HotspotList::iterator HotspotIterator; + +// FIXME: Remove global construction +extern HotspotList g_allHotspots; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/input.cpp b/engines/pegasus/input.cpp new file mode 100644 index 0000000000..492b02b85e --- /dev/null +++ b/engines/pegasus/input.cpp @@ -0,0 +1,330 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/events.h" +#include "common/system.h" + +#include "pegasus/cursor.h" +#include "pegasus/input.h" +#include "pegasus/pegasus.h" + +namespace Common { +DECLARE_SINGLETON(Pegasus::InputDeviceManager); +} + +namespace Pegasus { + +InputDeviceManager::InputDeviceManager() { +	_lastRawBits = kAllUpBits; +} + +void InputDeviceManager::getInput(Input &input, const InputBits filter) { +	InputBits currentBits = 0; +	bool consoleRequested = false; +	bool altDown = false; + +	Common::Event event; +	while (g_system->getEventManager()->pollEvent(event)) { +		// We only care about key down here +		// We're mapping from ScummVM events to pegasus events, which +		// are based on pippin events. +		if (event.type == Common::EVENT_KEYDOWN) { +			switch (event.kbd.keycode) { +			case Common::KEYCODE_UP: +			case Common::KEYCODE_KP8: +				currentBits |= (kRawButtonDown << kUpButtonShift); +				break; +			case Common::KEYCODE_LEFT: +			case Common::KEYCODE_KP4: +				currentBits |= (kRawButtonDown << kLeftButtonShift); +				break; +			case Common::KEYCODE_DOWN: +			case Common::KEYCODE_KP5: +				currentBits |= (kRawButtonDown << kDownButtonShift); +				break; +			case Common::KEYCODE_RIGHT: +			case Common::KEYCODE_KP6: +				currentBits |= (kRawButtonDown << kRightButtonShift); +				break; +			case Common::KEYCODE_RETURN: +			case Common::KEYCODE_SPACE: +				currentBits |= (kRawButtonDown << kTwoButtonShift); +				break; +			case Common::KEYCODE_t: +			case Common::KEYCODE_KP_EQUALS: +				currentBits |= (kRawButtonDown << kThreeButtonShift); +				break; +			case Common::KEYCODE_i: +			case Common::KEYCODE_KP_DIVIDE: +				currentBits |= (kRawButtonDown << kFourButtonShift); +				break; +			case Common::KEYCODE_q: +				currentBits |= (kRawButtonDown << kMod1ButtonShift); +				break; +			case Common::KEYCODE_ESCAPE: +			case Common::KEYCODE_p: +				currentBits |= (kRawButtonDown << kMod3ButtonShift); +				break; +			case Common::KEYCODE_TILDE: +			case Common::KEYCODE_BACKQUOTE: +			case Common::KEYCODE_NUMLOCK: // Yes, the original uses Num Lock/Clear on the Mac... +				currentBits |= (kRawButtonDown << kLeftFireButtonShift); +				break; +			case Common::KEYCODE_BACKSPACE: +			case Common::KEYCODE_KP_MULTIPLY: +				currentBits |= (kRawButtonDown << kRightFireButtonShift); +				break; +			case Common::KEYCODE_d: +				if (event.kbd.flags & Common::KBD_CTRL) // Console! +					consoleRequested = true; +				break; +			case Common::KEYCODE_s: +				// We support meta where available and control elsewhere +				if (event.kbd.flags & (Common::KBD_CTRL|Common::KBD_META)) +					((PegasusEngine *)g_engine)->requestSave(); +				break; +			case Common::KEYCODE_o: // o for open (original) +			case Common::KEYCODE_l: // l for load (ScummVM terminology) +				// We support meta where available and control elsewhere +				if (event.kbd.flags & (Common::KBD_CTRL|Common::KBD_META)) +					((PegasusEngine *)g_engine)->requestLoad(); +				break; +			default: +				break; +			} + +			// WORKAROUND: The original had a specific key for this, but +			// pressing alt would count as an event (and mess up someone +			// trying to do alt+enter or something). Since it's only used +			// as an easter egg, I'm just going to handle it as a separate +			// bool value. +			// WORKAROUND x2: I'm also accepting control here since an +			// alt+click is often intercepted by the OS. +			if (event.kbd.flags & (Common::KBD_ALT|Common::KBD_CTRL)) +				altDown = true; +		} +	} + +	// Update mouse button state +	// Note that we don't use EVENT_LBUTTONUP/EVENT_LBUTTONDOWN because +	// they do not show if the button is being held down. We're treating +	// both mouse buttons as the same for ease of use. +	if (g_system->getEventManager()->getButtonState() != 0) +		currentBits |= (kRawButtonDown << kTwoButtonShift); + +	// Update the mouse position too +	input.setInputLocation(g_system->getEventManager()->getMousePos()); + +	// Set the outgoing bits +	InputBits filteredBits = currentBits & filter;	 +	input.setInputBits((filteredBits & kAllButtonDownBits) | (filteredBits & _lastRawBits & kAllAutoBits)); + +	// Update the last bits +	_lastRawBits = currentBits; + +	// Set the console to be requested or not +	input.setConsoleRequested(consoleRequested); + +	// Same for alt +	input.setAltDown(altDown); +} + +// Wait until the input device stops returning input allowed by filter... +void InputDeviceManager::waitInput(const InputBits filter) { +	if (filter != 0) { +		for (;;) { +			Input input; +			getInput(input, filter); +			if (!input.anyInput()) +				break; +		} +	} +} + +int operator==(const Input &arg1, const Input &arg2) { +	return arg1._inputState == arg2._inputState; +} + +int operator!=(const Input &arg1, const Input &arg2) { +	return !operator==(arg1, arg2); +} + +InputHandler *InputHandler::_inputHandler = 0; +bool InputHandler::_invalHotspots = false; +InputBits InputHandler::_lastFilter = kFilterNoInput; + +InputHandler *InputHandler::setInputHandler(InputHandler *currentHandler) { +	InputHandler *result = 0; + +	if (_inputHandler != currentHandler && (!_inputHandler || _inputHandler->releaseInputFocus())) { +		result = _inputHandler; +		_inputHandler = currentHandler; +		if (_inputHandler) +			_inputHandler->grabInputFocus(); +	} + +	return result; +} + +void InputHandler::pollForInput() { +	if (_inputHandler) { +		Input input; +		Hotspot *cursorSpot = 0; + +		InputHandler::getInput(input, cursorSpot); +		if (_inputHandler->isClickInput(input, cursorSpot)) +			_inputHandler->clickInHotspot(input, cursorSpot); +		else +			_inputHandler->handleInput(input, cursorSpot); +	} +} + +void InputHandler::getInput(Input &input, Hotspot *&cursorSpot) { +	Cursor *cursor = ((PegasusEngine *)g_engine)->_cursor; +	 +	if (_inputHandler) +		_lastFilter = _inputHandler->getInputFilter(); +	else +		_lastFilter = kFilterAllInput; + +	InputDevice.getInput(input, _lastFilter); + +	if (_inputHandler && _inputHandler->wantsCursor() && (_lastFilter & _inputHandler->getClickFilter()) != 0) { +		if (cursor->isVisible()) { +			g_allHotspots.deactivateAllHotspots(); +			_inputHandler->activateHotspots(); + +			Common::Point cursorLocation; +			cursor->getCursorLocation(cursorLocation); +			cursorSpot = g_allHotspots.findHotspot(cursorLocation); + +			if (_inputHandler) +				_inputHandler->updateCursor(cursorLocation, cursorSpot); +		} else { +			cursor->hideUntilMoved(); +		} +	} else { +		cursor->hide(); +	} +} + +void InputHandler::readInputDevice(Input &input) { +	InputDevice.getInput(input, kFilterAllInput); +} + +InputHandler::InputHandler(InputHandler *nextHandler) { +	_nextHandler = nextHandler; +	allowInput(true); +} + +InputHandler::~InputHandler() { +	if (_inputHandler == this) +		setInputHandler(_nextHandler); +} + +void InputHandler::handleInput(const Input &input, const Hotspot *cursorSpot) { +	if (_nextHandler) +		_nextHandler->handleInput(input, cursorSpot); +} + +void InputHandler::clickInHotspot(const Input &input, const Hotspot *cursorSpot) { +	if (_nextHandler) +		_nextHandler->clickInHotspot(input, cursorSpot); +} + +bool InputHandler::isClickInput(const Input &input, const Hotspot *cursorSpot) { +	if (_nextHandler) +		return _nextHandler->isClickInput(input, cursorSpot); + +	return false; +} + +void InputHandler::activateHotspots() { +	if (_nextHandler) +		_nextHandler->activateHotspots(); +} + +InputBits InputHandler::getInputFilter() { +	if (_allowInput) { +		if (_nextHandler) +			return _nextHandler->getInputFilter(); +		else +			return kFilterAllInput; +	} + +	return kFilterNoInput; +} + +InputBits InputHandler::getClickFilter() { +	if (_allowInput && _nextHandler) +		return _nextHandler->getClickFilter(); + +	return kFilterNoInput; +} + +void InputHandler::updateCursor(const Common::Point cursorLocation, const Hotspot *cursorSpot) { +	if (_nextHandler) +		_nextHandler->updateCursor(cursorLocation, cursorSpot); +} + +bool InputHandler::wantsCursor() { +	if (_allowInput) { +		if (_nextHandler) +			return _nextHandler->wantsCursor(); +		else +			return true; +	} + +	return false; +} + +Tracker *Tracker::_currentTracker = 0; + +void Tracker::handleInput(const Input &input, const Hotspot *) { +	if (stopTrackingInput(input)) +		stopTracking(input); +	else if (isTracking()) +		continueTracking(input); +} + +void Tracker::startTracking(const Input &) { +	if (!isTracking()) { +		_savedHandler = InputHandler::setInputHandler(this); +		_currentTracker = this; +	} +} + +void Tracker::stopTracking(const Input &) { +	if (isTracking()) { +		_currentTracker = NULL; +		InputHandler::setInputHandler(_savedHandler); +	} +} + +bool Tracker::isClickInput(const Input &input, const Hotspot *hotspot) { +	return !isTracking() && InputHandler::isClickInput(input, hotspot); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/input.h b/engines/pegasus/input.h new file mode 100644 index 0000000000..0ee01f1949 --- /dev/null +++ b/engines/pegasus/input.h @@ -0,0 +1,493 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_INPUT_H +#define PEGASUS_INPUT_H + +#include "common/rect.h" +#include "common/singleton.h" + +#include "pegasus/constants.h" +#include "pegasus/types.h" + +namespace Pegasus { + +class Hotspot; +class Input; + +class InputDeviceManager : public Common::Singleton<InputDeviceManager> { +public: +	InputDeviceManager(); +	~InputDeviceManager() {} + +	void getInput(Input &, const InputBits); + +	void waitInput(const InputBits); + +protected: +	friend class Common::Singleton<SingletonBaseType>; + +	InputBits _lastRawBits; +}; + +enum { +	kButtonDownBit = 0, +	kAutoButtonBit = 1, +	kBitsPerButton = 2, +	 +	kButtonDownMask = 1 << kButtonDownBit, +	kAutoButtonMask = 1 << kAutoButtonBit, +	 +	kButtonStateBits = kButtonDownMask | kAutoButtonMask, +	 +	kRawButtonUp = 0, +	kRawButtonDown = kButtonDownMask | kAutoButtonMask, +	 +	kButtonUp = 0, +	kButtonDown = kButtonDownMask, +	kButtonAutoUp = kAutoButtonMask, +	kButtonAutoDown = kButtonDownMask | kAutoButtonMask +}; + +enum { +	kUpButtonNum = 0, +	kLeftButtonNum = 1, +	kDownButtonNum = 2, +	kRightButtonNum = 3, +	kLeftFireButtonNum = 4, +	kRightFireButtonNum = 5, +	kOneButtonNum = 6, +	kTwoButtonNum = 7, +	kThreeButtonNum = 8, +	kFourButtonNum = 9, +	kMod1ButtonNum = 10, +	kMod2ButtonNum = 11, +	kMod3ButtonNum = 12 +}; + +enum { +	kUpButtonShift = kUpButtonNum * kBitsPerButton, +	kLeftButtonShift = kLeftButtonNum * kBitsPerButton, +	kDownButtonShift = kDownButtonNum * kBitsPerButton, +	kRightButtonShift = kRightButtonNum * kBitsPerButton, +	kLeftFireButtonShift = kLeftFireButtonNum * kBitsPerButton, +	kRightFireButtonShift = kRightFireButtonNum * kBitsPerButton, +	kOneButtonShift = kOneButtonNum * kBitsPerButton, +	kTwoButtonShift = kTwoButtonNum * kBitsPerButton, +	kThreeButtonShift = kThreeButtonNum * kBitsPerButton, +	kFourButtonShift = kFourButtonNum * kBitsPerButton, +	kMod1ButtonShift = kMod1ButtonNum * kBitsPerButton, +	kMod2ButtonShift = kMod2ButtonNum * kBitsPerButton, +	kMod3ButtonShift = kMod3ButtonNum * kBitsPerButton +}; + +enum { +	kAllUpBits =	(kButtonUp << kUpButtonShift) | +					(kButtonUp << kLeftButtonShift) | +					(kButtonUp << kDownButtonShift) | +					(kButtonUp << kRightButtonShift) | +					(kButtonUp << kLeftFireButtonShift) | +					(kButtonUp << kRightFireButtonShift) | +					(kButtonUp << kOneButtonShift) | +					(kButtonUp << kTwoButtonShift) | +					(kButtonUp << kThreeButtonShift) | +					(kButtonUp << kFourButtonShift) | +					(kButtonUp << kMod1ButtonShift) | +					(kButtonUp << kMod2ButtonShift) | +					(kButtonUp << kMod3ButtonShift), +	kDirectionBits =	(kButtonDownMask << kUpButtonShift) | +						(kButtonDownMask << kLeftButtonShift) | +						(kButtonDownMask << kDownButtonShift) | +						(kButtonDownMask << kRightButtonShift), +	kButtonBits =	(kButtonDownMask << kLeftFireButtonShift) | +					(kButtonDownMask << kRightFireButtonShift) | +					(kButtonDownMask << kOneButtonShift) | +					(kButtonDownMask << kTwoButtonShift) | +					(kButtonDownMask << kThreeButtonShift) | +					(kButtonDownMask << kFourButtonShift) | +					(kButtonDownMask << kMod1ButtonShift) | +					(kButtonDownMask << kMod2ButtonShift) | +					(kButtonDownMask << kMod3ButtonShift), +	kAllButtonDownBits = kDirectionBits | kButtonBits, +	kAllAutoBits =	(kAutoButtonMask << kUpButtonShift) | +					(kAutoButtonMask << kLeftButtonShift) | +					(kAutoButtonMask << kDownButtonShift) | +					(kAutoButtonMask << kRightButtonShift) | +					(kAutoButtonMask << kLeftFireButtonShift) | +					(kAutoButtonMask << kRightFireButtonShift) | +					(kAutoButtonMask << kOneButtonShift) | +					(kAutoButtonMask << kTwoButtonShift) | +					(kAutoButtonMask << kThreeButtonShift) | +					(kAutoButtonMask << kFourButtonShift) | +					(kAutoButtonMask << kMod1ButtonShift) | +					(kAutoButtonMask << kMod2ButtonShift) | +					(kAutoButtonMask << kMod3ButtonShift), +	 +	kFilterUpButton =			kButtonDownMask << kUpButtonShift, +	kFilterUpAuto =				kAutoButtonMask << kUpButtonShift, +	kFilterUpButtonAny =		kFilterUpButton | kFilterUpAuto, +	kFilterLeftButton =			kButtonDownMask << kLeftButtonShift, +	kFilterLeftAuto =			kAutoButtonMask << kLeftButtonShift, +	kFilterLeftButtonAny =		kFilterLeftButton | kFilterLeftAuto, +	kFilterDownButton =			kButtonDownMask << kDownButtonShift, +	kFilterDownAuto =			kAutoButtonMask << kDownButtonShift, +	kFilterDownButtonAny =		kFilterDownButton | kFilterDownAuto, +	kFilterRightButton =		kButtonDownMask << kRightButtonShift, +	kFilterRightAuto =			kAutoButtonMask << kRightButtonShift, +	kFilterRightButtonAny =		kFilterRightButton | kFilterRightAuto, +	kFilterLeftFireButton =		kButtonDownMask << kLeftFireButtonShift, +	kFilterLeftFireAuto =		kAutoButtonMask << kLeftFireButtonShift, +	kFilterLeftFireButtonAny =	kFilterLeftFireButton | kFilterLeftFireAuto, +	kFilterRightFireButton =	kButtonDownMask << kRightFireButtonShift, +	kFilterRightFireAuto =		kAutoButtonMask << kRightFireButtonShift, +	kFilterRightFireButtonAny =	kFilterRightFireButton | kFilterRightFireAuto, +	kFilterOneButton =			kButtonDownMask << kOneButtonShift, +	kFilterOneAuto =			kAutoButtonMask << kOneButtonShift, +	kFilterOneButtonAny =		kFilterOneButton | kFilterOneAuto, +	kFilterTwoButton =			kButtonDownMask << kTwoButtonShift, +	kFilterTwoAuto =			kAutoButtonMask << kTwoButtonShift, +	kFilterTwoButtonAny =		kFilterTwoButton | kFilterTwoAuto, +	kFilterThreeButton =		kButtonDownMask << kThreeButtonShift, +	kFilterThreeAuto =			kAutoButtonMask << kThreeButtonShift, +	kFilterThreeButtonAny =		kFilterThreeButton | kFilterThreeAuto, +	kFilterFourButton =			kButtonDownMask << kFourButtonShift, +	kFilterFourAuto =			kAutoButtonMask << kFourButtonShift, +	kFilterFourButtonAny =		kFilterFourButton | kFilterFourAuto, +	kFilterMod1Button =			kButtonDownMask << kMod1ButtonShift, +	kFilterMod1Auto =			kAutoButtonMask << kMod1ButtonShift, +	kFilterMod1ButtonAny =		kFilterMod1Button | kFilterMod1Auto, +	kFilterMod2Button =			kButtonDownMask << kMod2ButtonShift, +	kFilterMod2Auto =			kAutoButtonMask << kMod2ButtonShift, +	kFilterMod2ButtonAny =		kFilterMod2Button | kFilterMod2Auto, +	kFilterMod3Button =			kButtonDownMask << kMod3ButtonShift, +	kFilterMod3Auto =			kAutoButtonMask << kMod3ButtonShift, +	kFilterMod3ButtonAny =		kFilterMod3Button | kFilterMod3Auto, +	 +	kFilterNoInput =			0, +	kFilterAllInput =			kFilterUpButton | +								kFilterUpAuto | +								kFilterLeftButton | +								kFilterLeftAuto | +								kFilterDownButton | +								kFilterDownAuto | +								kFilterRightButton | +								kFilterRightAuto | +								kFilterLeftFireButton | +								kFilterLeftFireAuto | +								kFilterRightFireButton | +								kFilterRightFireAuto | +								kFilterOneButton | +								kFilterOneAuto | +								kFilterTwoButton | +								kFilterTwoAuto | +								kFilterThreeButton | +								kFilterThreeAuto | +								kFilterFourButton | +								kFilterFourAuto | +								kFilterMod1Button | +								kFilterMod1Auto | +								kFilterMod2Button | +								kFilterMod2Auto | +								kFilterMod3Button | +								kFilterMod3Auto, +	 +	kFilterAllDirections =		kFilterUpButton | +								kFilterUpAuto | +								kFilterLeftButton | +								kFilterLeftAuto | +								kFilterDownButton | +								kFilterDownAuto | +								kFilterRightButton | +								kFilterRightAuto, +	 +	kFilterButtons =			kFilterOneButton | +								kFilterOneAuto | +								kFilterTwoButton | +								kFilterTwoAuto | +								kFilterThreeButton | +								kFilterThreeAuto | +								kFilterFourButton | +								kFilterFourAuto, +	 +	kFilterFireButtons =		kFilterLeftFireButton | +								kFilterLeftFireAuto | +								kFilterRightFireButton | +								kFilterRightFireAuto, +	 +	kFilterAllButtons =			kFilterLeftFireButton | +								kFilterLeftFireAuto | +								kFilterRightFireButton | +								kFilterRightFireAuto | +								kFilterOneButton | +								kFilterOneAuto | +								kFilterTwoButton | +								kFilterTwoAuto | +								kFilterThreeButton | +								kFilterThreeAuto | +								kFilterFourButton | +								kFilterFourAuto | +								kFilterMod1Button | +								kFilterMod1Auto | +								kFilterMod2Button | +								kFilterMod2Auto | +								kFilterMod3Button | +								kFilterMod3Auto, +	 +	kFilterAllInputNoAuto =		kFilterUpButton | +								kFilterLeftButton | +								kFilterDownButton | +								kFilterRightButton | +								kFilterLeftFireButton | +								kFilterRightFireButton | +								kFilterOneButton | +								kFilterTwoButton | +								kFilterThreeButton | +								kFilterFourButton | +								kFilterMod1Button | +								kFilterMod2Button | +								kFilterMod3Button +}; + +static const InputBits kHintInterruption = kFilterAllInputNoAuto; +static const InputBits kWarningInterruption = kFilterNoInput; +static const InputBits kOpticalInterruption = kFilterAllInputNoAuto; + +/* + +	Buttons are defined as: +		up, left, down, right		direction buttons. +		fireLeft, fireRight:		fire buttons. +		mod1, mod2, mod3:			modifier buttons, similar to shift, control, etc. +		a, b, c, d:					general purpose buttons. +	 +	button state is held as bits in a long word, two bits per button. +	 +	Filter bits: +		for each button, two bits are assigned for filtering. If bit 0 is set, the +		corresponding button is available for "button down" input. If bit 1 is set, +		the corresponding button is available for "auto down" input. Note that bit +		1 is meaningful only if bit 0 is set. +	 +*/ + +class Input { +friend int operator==(const Input &, const Input &); +friend int operator!=(const Input &, const Input &); +friend class InputDeviceManager; + +public: +	Input() { clearInput(); } + +	bool upButtonDown() const { return (_inputState & (kButtonStateBits << kUpButtonShift)) == (kButtonDown << kUpButtonShift); } +	bool upButtonAutoDown() const { return (_inputState & (kButtonStateBits << kUpButtonShift)) == (kButtonAutoDown << kUpButtonShift); } +	bool upButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kUpButtonShift)) != 0; } +	 +	bool leftButtonDown() const { return (_inputState & (kButtonStateBits << kLeftButtonShift)) == (kButtonDown << kLeftButtonShift); } +	bool leftButtonAutoDown() const { return (_inputState & (kButtonStateBits << kLeftButtonShift)) == (kButtonAutoDown << kLeftButtonShift); } +	bool leftButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kLeftButtonShift)) != 0; } +	 +	bool downButtonDown() const { return (_inputState & (kButtonStateBits << kDownButtonShift)) == (kButtonDown << kDownButtonShift); } +	bool downButtonAutoDown() const { return (_inputState & (kButtonStateBits << kDownButtonShift)) == (kButtonAutoDown << kDownButtonShift); } +	bool downButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kDownButtonShift)) != 0; } +	 +	bool rightButtonDown() const { return (_inputState & (kButtonStateBits << kRightButtonShift)) == (kButtonDown << kRightButtonShift); } +	bool rightButtonAutoDown() const { return (_inputState & (kButtonStateBits << kRightButtonShift)) == (kButtonAutoDown << kRightButtonShift); } +	bool rightButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kRightButtonShift)) != 0; } +	 +	bool leftFireButtonDown() const { return (_inputState & (kButtonStateBits << kLeftFireButtonShift)) == (kButtonDown << kLeftFireButtonShift); } +	bool leftFireButtonAutoDown() const { return (_inputState & (kButtonStateBits << kLeftFireButtonShift)) == (kButtonAutoDown << kLeftFireButtonShift); } +	bool leftFireButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kLeftFireButtonShift)) != 0; } +	 +	bool rightFireButtonDown() const { return (_inputState & (kButtonStateBits << kRightFireButtonShift)) == (kButtonDown << kRightFireButtonShift); } +	bool rightFireButtonAutoDown() const { return (_inputState & (kButtonStateBits << kRightFireButtonShift)) == (kButtonAutoDown << kRightFireButtonShift); } +	bool rightFireButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kRightFireButtonShift)) != 0; } +	 +	bool oneButtonDown() const { return	(_inputState & (kButtonStateBits << kOneButtonShift)) == (kButtonDown << kOneButtonShift); } +	bool oneButtonAutoDown() const { return (_inputState & (kButtonStateBits << kOneButtonShift)) == (kButtonAutoDown << kOneButtonShift); } +	bool oneButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kOneButtonShift)) != 0; } + +	bool twoButtonDown() const { return (_inputState & (kButtonStateBits << kTwoButtonShift)) == (kButtonDown << kTwoButtonShift); } +	bool twoButtonAutoDown() const { return (_inputState & (kButtonStateBits << kTwoButtonShift)) == (kButtonAutoDown << kTwoButtonShift); } +	bool twoButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kTwoButtonShift)) != 0; } +	 +	bool threeButtonDown() const { return (_inputState & (kButtonStateBits << kThreeButtonShift)) == (kButtonDown << kThreeButtonShift); } +	bool threeButtonAutoDown() const { return (_inputState & (kButtonStateBits << kThreeButtonShift)) == (kButtonAutoDown << kThreeButtonShift); } +	bool threeButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kThreeButtonShift)) != 0; } +	 +	bool fourButtonDown() const { return (_inputState & (kButtonStateBits << kFourButtonShift)) == (kButtonDown << kFourButtonShift); } +	bool fourButtonAutoDown() const { return (_inputState & (kButtonStateBits << kFourButtonShift)) == (kButtonAutoDown << kFourButtonShift); } +	bool fourButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kFourButtonShift)) != 0; } +	 +	bool mod1ButtonDown() const { return (_inputState & (kButtonStateBits << kMod1ButtonShift)) == (kButtonDown << kMod1ButtonShift); } +	bool mod1ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod1ButtonShift)) == (kButtonAutoDown << kMod1ButtonShift); } +	bool mod1ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod1ButtonShift)) != 0; } +	 +	bool mod2ButtonDown() const { return (_inputState & (kButtonStateBits << kMod2ButtonShift)) == (kButtonDown << kMod2ButtonShift); } +	bool mod2ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod2ButtonShift)) == (kButtonAutoDown << kMod2ButtonShift); } +	bool mod2ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod2ButtonShift)) != 0; } +	 +	bool mod3ButtonDown() const { return (_inputState & (kButtonStateBits << kMod3ButtonShift)) == (kButtonDown << kMod3ButtonShift); } +	bool mod3ButtonAutoDown() const { return (_inputState & (kButtonStateBits << kMod3ButtonShift)) == (kButtonAutoDown << kMod3ButtonShift); } +	bool mod3ButtonAnyDown() const { return (_inputState & (kButtonAutoDown << kMod3ButtonShift)) != 0; } +	 +	bool allAutoInput() const { return (_inputState & kAllAutoBits) != 0; } +	bool anyDirectionInput() const { return (_inputState & kDirectionBits) != 0; } +	bool anyButtonInput() const { return (_inputState & kButtonBits) != 0; } +	bool anyInput() const { return _inputState != 0; } +	 +	void getInputLocation(Common::Point &where) const { where = _inputLocation; } +	 +	bool anyInputBitSet(const InputBits bits) const { return (_inputState & bits) != 0; } + +	bool isAltDown() const { return _altDown; } +	bool isConsoleRequested() const { return _consoleRequested; } + +	void clearInput() { +		_inputState = kAllUpBits; +		_inputLocation.x = 0; +		_inputLocation.y = 0; +		_consoleRequested = false; +		_altDown = false; +	} + +protected: +	void setInputBits(const InputBits state) { _inputState = state; } +	void setInputLocation(const Common::Point &where) { _inputLocation = where; } +	void setConsoleRequested(bool consoleRequested) { _consoleRequested = consoleRequested; } +	void setAltDown(bool altDown) { _altDown = altDown; } +	 +	InputBits _inputState; +	Common::Point _inputLocation; +	bool _consoleRequested; +	bool _altDown; +}; + +class InputHandler { +public: +	static InputHandler *setInputHandler(InputHandler*); +	static InputHandler *getCurrentHandler() { return _inputHandler; } +	static void pollForInput(); +	static void getInput(Input&, Hotspot*&); +	static void readInputDevice(Input&); +	static void invalHotspots() { _invalHotspots = true; } +	static InputBits getCurrentFilter() { return _lastFilter; } +		 +	InputHandler(InputHandler*); +	virtual ~InputHandler(); +	 +	virtual void setNextHandler(InputHandler *nextHandler) { _nextHandler = nextHandler; } +	virtual InputHandler *getNextHandler() { return _nextHandler; } +	 +	virtual void handleInput(const Input &, const Hotspot *); +	virtual void clickInHotspot(const Input &, const Hotspot *); +	 +	virtual void activateHotspots(); +	virtual void updateCursor(const Common::Point, const Hotspot *); +	virtual bool isClickInput(const Input &, const Hotspot *); +	virtual bool wantsCursor(); +	 +	virtual bool releaseInputFocus() { return true; } +	virtual void grabInputFocus() {} +	 +	// This returns bits set for what kinds of input to accept. +	virtual InputBits getInputFilter(); + +	// This returns bits defining what input constitutes a "click." +	virtual InputBits getClickFilter(); + +	virtual void allowInput(const bool allow) { _allowInput = allow; } + +protected: +	static InputHandler *_inputHandler; +	static bool _invalHotspots; +	static InputBits _lastFilter; +	 +	InputHandler *_nextHandler; +	bool _allowInput; +}; + + +/* + +	Tracker implements "dragging". A Tracker can receive a startTracking message, +	which causes it to be the current tracker, as well as setting it up as the current +	input handler. In addition, only one tracker can be tracking at a time, and no +	other handler can be set up as the current handler until the track finishes. By +	default, there is no next input handler for a Tracker, but this behavior can be +	overridden if desired. + +*/ + +class Tracker : public InputHandler { +public: +	Tracker() : InputHandler(0) {} +	virtual ~Tracker() {} + +	virtual void handleInput(const Input &, const Hotspot *); +	virtual bool stopTrackingInput(const Input &) { return false; } + +	virtual void startTracking(const Input &); +	virtual void stopTracking(const Input &); +	virtual void continueTracking(const Input &) {} + +	bool isTracking() { return this == _currentTracker; } +	bool isClickInput(const Input &, const Hotspot *); + +	bool releaseInputFocus() { return !isTracking(); } + +protected: +	static Tracker *_currentTracker; + +	InputHandler *_savedHandler; +}; + +class JMPPPInput { +public: +	static bool isMenuButtonPressInput(const Input &input) { return input.twoButtonDown(); } + +	static InputBits getClickInputFilter() { return kFilterTwoButton; } +	static bool isClickInput(const Input &input) { return input.twoButtonDown(); } +	static bool isDraggingInput(const Input &input) { return input.twoButtonAnyDown(); } +	static bool isPressingInput(const Input &input) { return input.twoButtonAnyDown(); } + +	static bool isRaiseInventoryInput(const Input &input) { return input.leftFireButtonDown(); } +	static bool isRaiseBiochipsInput(const Input &input) { return input.rightFireButtonDown(); } +	static InputBits getItemPanelsInputFilter() { return kFilterLeftFireButton | kFilterRightFireButton; } + +	static bool isToggleAIMiddleInput(const Input &input) { return input.threeButtonDown(); } + +	static bool isToggleInfoInput(const Input &input) { return input.fourButtonDown(); } + +	// Hmmmmm.... +	static bool isEasterEggModifierInput(const Input &input) { return input.isAltDown(); } +	 +	static bool isTogglePauseInput(const Input &input) { return input.mod3ButtonDown(); } +}; + +} // End of namespace Pegasus + +#define InputDevice (::Pegasus::InputDeviceManager::instance()) + +#endif diff --git a/engines/pegasus/interaction.h b/engines/pegasus/interaction.h new file mode 100644 index 0000000000..b1318563ac --- /dev/null +++ b/engines/pegasus/interaction.h @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_INTERACTION_H +#define PEGASUS_INTERACTION_H + +#include "pegasus/input.h" +#include "pegasus/util.h" + +namespace Pegasus { + +static const InteractionID kNoInteractionID = -1; + +class Neighborhood; + +class GameInteraction : public IDObject, public InputHandler { +public: +	GameInteraction(const InteractionID id, Neighborhood *nextHandler) : IDObject(id), InputHandler((InputHandler *)nextHandler) { +		_isInteracting = false; +		_savedHandler = 0; +		_owner = nextHandler; +	} + +	// If the interaction is open (_isInteracting == true), it's too late to do anything +	// about it here. +	virtual ~GameInteraction() {} + +	// startInteraction and stopInteraction are called by the outside world to +	// start and stop the interaction sequence. +	// isInteracting returns a bool indicating whether or not the interaction +	// is going. +	void startInteraction() { +		if (!isInteracting()) { +			openInteraction(); +			initInteraction(); +			_isInteracting = true; +			_savedHandler = InputHandler::setInputHandler(this); +		} +	} +	void stopInteraction() { +		if (isInteracting()) { +			closeInteraction(); +			_isInteracting = false; +			if (InputHandler::_inputHandler == this) +				InputHandler::setInputHandler(_savedHandler); +		} +	} +	void startOverInteraction() { +		if (isInteracting()) +			resetInteraction(); +	} +	bool isInteracting() const { return _isInteracting; } +	Neighborhood *getOwner() const { return _owner; } + +	virtual Common::String getBriefingMovie() { return ""; } +	virtual Common::String getEnvScanMovie() { return ""; } +	virtual long getNumHints() { return 0; } +	virtual Common::String getHintMovie(uint) { return ""; } +	virtual bool canSolve() { return false; } +	 +	virtual void setSoundFXLevel(const uint16) {} +	virtual void setAmbienceLevel(const uint16) {} +	 +	virtual void doSolve() {} + +protected: +	// Subclasses override openInteraction and closeInteraction to perform +	// specific initialization and cleanup. Override resetInteraction to +	// "start the interaction over." resetInteraction is called only when +	// the interaction is already open. +	// These functions are only called in pairs, never two opens or closes +	// in a row. +	virtual void openInteraction() {} +	virtual void initInteraction() {} +	virtual void closeInteraction() {} +	virtual void resetInteraction() {} +	 +	InputHandler *_savedHandler; +	Neighborhood *_owner; +	 +private: +	// Private so that only StartInteraction and StopInteraction can touch it. +	bool _isInteracting; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/interface.cpp b/engines/pegasus/interface.cpp new file mode 100644 index 0000000000..5e5188886e --- /dev/null +++ b/engines/pegasus/interface.cpp @@ -0,0 +1,667 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/compass.h" +#include "pegasus/energymonitor.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +Interface *g_interface = 0; + +Interface::Interface() : InputHandler(0), _interfaceNotification(kInterfaceNotificationID, (NotificationManager *)((PegasusEngine *)g_engine)), +			_currentItemSpot(kCurrentItemSpotID), _currentBiochipSpot(kCurrentBiochipSpotID), +			_background1(kInterface1ID), _background2(kInterface2ID), _background3(kInterface3ID), +			_background4(kInterface4ID), _datePicture(kDateID), _inventoryPush(kInventoryPushID), +			_inventoryLid(kInventoryLidID, kNoDisplayElement), +			_inventoryPanel(kNoDisplayElement, (InputHandler *)((PegasusEngine *)g_engine), ((PegasusEngine *)g_engine)->getItemsInventory()), +			_biochipPush(kBiochipPushID), _biochipLid(kBiochipLidID, kNoDisplayElement), +			_biochipPanel(kNoDisplayElement, (InputHandler *)((PegasusEngine *)g_engine), ((PegasusEngine *)g_engine)->getBiochipsInventory()) { +	g_energyMonitor = 0; +	_previousHandler = 0; +	_inventoryRaised = false; +	_biochipRaised = false; +	_playingEndMessage = false; +	g_interface = this; +} + +Interface::~Interface() { +	throwAwayInterface(); +	g_interface = 0; +} + +void Interface::throwAwayInterface() { +	g_allHotspots.removeOneHotspot(kCurrentItemSpotID); +	g_allHotspots.removeOneHotspot(kCurrentBiochipSpotID); + +	throwAwayBackground(); +	throwAwayDateMonitor(); +	throwAwayEnergyMonitor(); +	throwAwayAIArea(); +	throwAwayCompass(); +	throwAwayNotifications(); +	throwAwayInventoryPanel(); +	throwAwayBiochipPanel(); +} + +void Interface::validateBackground() { +	if (!_background1.isSurfaceValid()) { +		_background1.initFromPICTFile("Images/Interface/3DInterface Left"); +		_background2.initFromPICTFile("Images/Interface/3DInterface Top"); +		_background3.initFromPICTFile("Images/Interface/3DInterface Right"); +		_background4.initFromPICTFile("Images/Interface/3DInterface Bottom"); + +		_background1.setDisplayOrder(kBackground1Order); +		_background1.startDisplaying(); +		_background1.moveElementTo(kBackground1Left, kBackground1Top); + +		_background2.setDisplayOrder(kBackground2Order); +		_background2.startDisplaying(); +		_background2.moveElementTo(kBackground2Left, kBackground2Top); + +		_background3.setDisplayOrder(kBackground2Order); +		_background3.startDisplaying(); +		_background3.moveElementTo(kBackground3Left, kBackground3Top); + +		_background4.setDisplayOrder(kBackground4Order); +		_background4.startDisplaying(); +		_background4.moveElementTo(kBackground4Left, kBackground4Top); + +		_background1.show(); +		_background2.show(); +		_background3.show(); +		_background4.show(); +	} +} + +void Interface::throwAwayBackground() { +	_background1.stopDisplaying(); +	_background1.deallocateSurface(); +	_background2.stopDisplaying(); +	_background2.deallocateSurface(); +	_background3.stopDisplaying(); +	_background3.deallocateSurface(); +	_background4.stopDisplaying(); +	_background4.deallocateSurface(); +} + +void Interface::validateDateMonitor() { +	if (!_datePicture.isSurfaceValid()) { +		_datePicture.setDisplayOrder(kDateOrder); +		_datePicture.startDisplaying(); +		_datePicture.moveElementTo(kDateLeft, kDateTop); +		_datePicture.show(); +	} +} + +void Interface::throwAwayDateMonitor() { +	_datePicture.stopDisplaying(); +	_datePicture.deallocateSurface(); +} + +void Interface::setDate(const uint16 dateResID) { +	validateDateMonitor(); +	_datePicture.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, dateResID); +	_datePicture.triggerRedraw(); +} + +void Interface::validateCompass() { +	if (!g_compass) { +		new Compass(); +		g_compass->initCompass(); +		g_compass->setDisplayOrder(kCompassOrder); +		g_compass->startDisplaying(); +		g_compass->moveElementTo(kCompassLeft, kCompassTop); +		g_compass->show(); +	} +} + +void Interface::throwAwayCompass() { +	delete g_compass; +} + +void Interface::validateNotifications() { +	_interfaceNotification.notifyMe(this, kInterfaceNotificationFlags, kInterfaceNotificationFlags); +	_inventoryLidCallBack.setNotification(&_interfaceNotification); +	_inventoryPushCallBack.setNotification(&_interfaceNotification); +	_biochipLidCallBack.setNotification(&_interfaceNotification); +	_biochipPushCallBack.setNotification(&_interfaceNotification); +} + +void Interface::throwAwayNotifications() { +	_interfaceNotification.cancelNotification(this); +} + +void Interface::validateAIArea() { +	if (!g_AIArea) { +		new AIArea((InputHandler *)((PegasusEngine *)g_engine)); +		if (g_AIArea) +			g_AIArea->initAIArea(); +	} +} + +void Interface::throwAwayAIArea() { +	delete g_AIArea; +} + +void Interface::validateInventoryPanel() {	 +	if (!_inventoryPanel.isSurfaceValid()) { +		_inventoryPanel.initInventoryImage(&_inventoryPush); +		_inventoryPanel.moveElementTo(kInventoryPushLeft, kInventoryPushTop); +		_inventoryPush.setSlideDirection(kSlideUpMask); +		_inventoryPush.setInAndOutElements(&_inventoryPanel, 0); +		_inventoryPush.setDisplayOrder(kInventoryPushOrder); +		_inventoryPush.startDisplaying(); + +		_inventoryLid.useFileName("Images/Lids/Inventory Lid Sequence"); +		_inventoryLid.useTransparent(true); +		_inventoryLid.openFrameSequence(); +		_inventoryLid.moveElementTo(kInventoryLidLeft, kInventoryLidTop); +		_inventoryLid.setDisplayOrder(kInventoryLidOrder); +		_inventoryLid.startDisplaying(); + +		_inventoryPushCallBack.initCallBack(&_inventoryPush, kCallBackAtExtremes); +		_inventoryLidCallBack.initCallBack(&_inventoryLid, kCallBackAtExtremes); + +		_inventoryUp = false; +		_inventoryRaised = false; + +		Item *item = getCurrentInventoryItem(); +		if (item) +			item->select(); +	} +} + +void Interface::throwAwayInventoryPanel() { +	_inventoryPanel.stopDisplaying(); +	_inventoryPanel.throwAwayInventoryImage(); +	_inventoryPush.stopDisplaying(); +	_inventoryLid.stopDisplaying(); +	_inventoryLid.closeFrameSequence(); +	_inventoryPushCallBack.releaseCallBack(); +	_inventoryLidCallBack.releaseCallBack(); + +	Item *item = getCurrentInventoryItem(); +	if (item) +		item->deselect(); + +	_inventoryUp = false; +	_inventoryRaised = false; +} + +void Interface::validateBiochipPanel() {	 +	if (!_biochipPanel.isSurfaceValid()) { +		_biochipPanel.initInventoryImage(&_biochipPush); +		_biochipPanel.moveElementTo(kBiochipPushLeft, kBiochipPushTop); +		_biochipPush.setSlideDirection(kSlideUpMask); +		_biochipPush.setInAndOutElements(&_biochipPanel, 0); +		_biochipPush.setDisplayOrder(kBiochipPushOrder); +		_biochipPush.startDisplaying(); + +		_biochipLid.useFileName("Images/Lids/Biochip Lid Sequence"); +		_biochipLid.useTransparent(true); +		_biochipLid.openFrameSequence(); +		_biochipLid.moveElementTo(kBiochipLidLeft, kBiochipLidTop); +		_biochipLid.setDisplayOrder(kBiochipLidOrder); +		_biochipLid.startDisplaying(); + +		_biochipPushCallBack.initCallBack(&_biochipPush, kCallBackAtExtremes); +		_biochipLidCallBack.initCallBack(&_biochipLid, kCallBackAtExtremes); + +		_biochipUp = false; +		_biochipRaised = false; + +		Item *item = getCurrentBiochip(); +		if (item) +			item->select(); +	} +} + +void Interface::throwAwayBiochipPanel() {	 +	_biochipPanel.stopDisplaying(); +	_biochipPanel.throwAwayInventoryImage(); +	_biochipPush.stopDisplaying(); +	_biochipLid.stopDisplaying(); +	_biochipLid.closeFrameSequence(); +	_biochipPushCallBack.releaseCallBack(); +	_biochipLidCallBack.releaseCallBack(); +	 +	Item *item = getCurrentBiochip(); +	if (item) +		item->deselect(); +	 +	_biochipUp = false; +	_biochipRaised = false; +} + +void Interface::validateEnergyMonitor() { +	if (!g_energyMonitor) +		new EnergyMonitor(); +} + +void Interface::throwAwayEnergyMonitor() { +	delete g_energyMonitor; +} + +void Interface::createInterface() { +	validateBackground(); +	validateDateMonitor(); +	validateCompass(); +	validateNotifications(); +	validateAIArea(); +	validateBiochipPanel(); +	validateInventoryPanel(); +	validateEnergyMonitor(); + +	if (!g_allHotspots.findHotspotByID(kCurrentItemSpotID)) { +		_currentItemSpot.setArea(Common::Rect(76, 334, 172, 430)); +		_currentItemSpot.setHotspotFlags(kShellSpotFlag); +		_currentItemSpot.setActive(); +		g_allHotspots.push_back(&_currentItemSpot); +	} + +	if (!g_allHotspots.findHotspotByID(kCurrentBiochipSpotID)) { +		_currentBiochipSpot.setArea(Common::Rect(364, 334, 460, 430)); +		_currentBiochipSpot.setHotspotFlags(kShellSpotFlag); +		_currentBiochipSpot.setActive(); +		g_allHotspots.push_back(&_currentBiochipSpot); +	} +} + +InventoryResult Interface::addInventoryItem(InventoryItem *item) { +	return _inventoryPanel.addInventoryItem(item); +} + +InventoryResult Interface::removeInventoryItem(InventoryItem *item) { +	return _inventoryPanel.removeInventoryItem(item); +} + +void Interface::removeAllItemsFromInventory() { +	_inventoryPanel.removeAllItems(); +} + +InventoryItem *Interface::getCurrentInventoryItem() { +	return (InventoryItem *)_inventoryPanel.getCurrentItem(); +} + +void Interface::setCurrentInventoryItem(InventoryItem *item) { +	setCurrentInventoryItemID(item->getObjectID()); +} + +void Interface::setCurrentInventoryItemID(ItemID id) { +	_inventoryPanel.setCurrentItemID(id); +} + +InventoryResult Interface::addBiochip(BiochipItem *item) { +	return _biochipPanel.addInventoryItem(item); +} + +void Interface::removeAllItemsFromBiochips() { +	_biochipPanel.removeAllItems(); +} + +BiochipItem *Interface::getCurrentBiochip() { +	return (BiochipItem *)_biochipPanel.getCurrentItem(); +} + +void Interface::setCurrentBiochip(BiochipItem *item) { +	setCurrentBiochipID(item->getObjectID()); +} + +void Interface::setCurrentBiochipID(ItemID id) { +	_biochipPanel.setCurrentItemID(id); +} + +void Interface::receiveNotification(Notification *notification, const NotificationFlags flags) { +	if (notification == &_interfaceNotification) { +		switch (flags) { +		case kInventoryLidOpenFlag: +			inventoryLidOpen(true); +			break; +		case kInventoryLidClosedFlag: +			inventoryLidClosed(); +			break; +		case kInventoryDrawerUpFlag: +			inventoryDrawerUp(); +			break; +		case kInventoryDrawerDownFlag: +			inventoryDrawerDown(true); +			break; +		case kBiochipLidOpenFlag: +			biochipLidOpen(true); +			break; +		case kBiochipLidClosedFlag: +			biochipLidClosed(); +			break; +		case kBiochipDrawerUpFlag: +			biochipDrawerUp(); +			break; +		case kBiochipDrawerDownFlag: +			biochipDrawerDown(true); +			break; +		} +	} +} + +void Interface::raiseInventoryDrawer(const bool doCallBacks) {	 +	if (!_biochipUp) +		_previousHandler = InputHandler::getCurrentHandler(); + +	InputHandler::setInputHandler(&_inventoryPanel); +	_inventoryUp = true; +	_inventoryPanel.activateInventoryPicture(); + +	if (doCallBacks) { +		_inventoryLidCallBack.setCallBackFlag(kInventoryLidOpenFlag); +		_inventoryLidCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	} + +	_inventoryLid.show(); +	_inventoryPush.show(); +	_inventoryLid.start(); +} + +void Interface::playEndMessage() { +	raiseInventoryDrawerForMessage(); +	_playingEndMessage = true; +	_inventoryPanel.playEndMessage(&_inventoryPush); +	lowerInventoryDrawerForMessage(); +	_playingEndMessage = false; +} + +void Interface::raiseInventoryDrawerForMessage() { +	_inventoryPanel.disableLooping(); +	raiseInventoryDrawerSync(); +} + +void Interface::lowerInventoryDrawerForMessage() { +	lowerInventoryDrawerSync(); +} + +void Interface::inventoryLidOpen(const bool doCallBacks) { +	_inventoryLid.stop(); + +	if (doCallBacks) { +		_inventoryPushCallBack.setCallBackFlag(kInventoryDrawerUpFlag); +		_inventoryPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	} + +	FaderMoveSpec moveSpec; +	moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000); +	_inventoryPush.startFader(moveSpec); +} + +void Interface::inventoryDrawerUp() { +	_inventoryPush.stopFader(); +	_inventoryPanel.panelUp(); +	_inventoryRaised = true; +} + +bool Interface::isInventoryUp() { +	return _inventoryRaised; +} + +bool Interface::isInventoryDown() { +	return !_inventoryUp; +} + +void Interface::lowerInventoryDrawer(const bool doCallBacks) {	 +	if (_inventoryRaised) { +		_inventoryRaised = false; + +		if (!_playingEndMessage) +			_inventoryPanel.deactivateInventoryPicture(); + +		if (doCallBacks) { +			_inventoryPushCallBack.setCallBackFlag(kInventoryDrawerDownFlag); +			_inventoryPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +		} + +		FaderMoveSpec moveSpec; +		moveSpec.makeTwoKnotFaderSpec(60, 0, 1000, 15, 0); +		_inventoryPush.startFader(moveSpec); +	} +} + +void Interface::inventoryDrawerDown(const bool doCallBacks) { +	_inventoryPush.stopFader(); + +	if (doCallBacks) { +		_inventoryLidCallBack.setCallBackFlag(kInventoryLidClosedFlag); +		_inventoryLidCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); +	} + +	_inventoryLid.setRate(-1); +} + +void Interface::inventoryLidClosed() { +	_inventoryLid.stop(); + +	if (!_biochipUp) +		InputHandler::setInputHandler(_previousHandler); + +	_inventoryLid.hide(); +	_inventoryPush.hide(); +	_inventoryUp = false; +} + +void Interface::raiseBiochipDrawer(const bool doCallBacks) {	 +	if (!_inventoryUp) +		_previousHandler = InputHandler::getCurrentHandler(); + +	InputHandler::setInputHandler(&_biochipPanel); +	_biochipUp = true; +	_biochipPanel.activateInventoryPicture(); + +	if (doCallBacks) { +		_biochipLidCallBack.setCallBackFlag(kBiochipLidOpenFlag); +		_biochipLidCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	} + +	_biochipLid.show(); +	_biochipPush.show(); +	_biochipLid.start(); +} + +void Interface::biochipLidOpen(const bool doCallBacks) { +	_biochipLid.stop(); + +	if (doCallBacks) { +		_biochipPushCallBack.setCallBackFlag(kBiochipDrawerUpFlag); +		_biochipPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	} + +	FaderMoveSpec moveSpec; +	moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 9, 1000); +	_biochipPush.startFader(moveSpec); +} + +void Interface::biochipDrawerUp() { +	_biochipPush.stopFader(); +	_biochipPanel.panelUp(); +	_biochipRaised = true; +} + +void Interface::lowerBiochipDrawer(const bool doCallBacks) {	 +	if (_biochipRaised) { +		_biochipRaised = false; +		_biochipPanel.deactivateInventoryPicture(); + +		if (doCallBacks) { +			_biochipPushCallBack.setCallBackFlag(kBiochipDrawerDownFlag); +			_biochipPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +		} + +		FaderMoveSpec moveSpec; +		moveSpec.makeTwoKnotFaderSpec(60, 0, 1000, 9, 0); +		_biochipPush.startFader(moveSpec); +	} +} + +void Interface::biochipDrawerDown(const bool doCallBacks) { +	_biochipPush.stopFader(); + +	if (doCallBacks) { +		_biochipLidCallBack.setCallBackFlag(kBiochipLidClosedFlag); +		_biochipLidCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); +	} + +	_biochipLid.setRate(-1); +} + +void Interface::biochipLidClosed() { +	_biochipLid.stop(); + +	if (!_inventoryUp) +		InputHandler::setInputHandler(_previousHandler); + +	_biochipLid.hide(); +	_biochipPush.hide(); +	_biochipUp = false; +} + +void Interface::calibrateCompass() { +	uint32 currentValue = g_compass->getFaderValue(); +	FaderMoveSpec compassMove; +	compassMove.makeTwoKnotFaderSpec(15, 0, currentValue, 30, currentValue + 360); + +	g_compass->startFader(compassMove); + +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	while (g_compass->isFading()) { +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	vm->refreshDisplay(); +	g_compass->setFaderValue(currentValue); +} + +void Interface::calibrateEnergyBar() { +	g_energyMonitor->calibrateEnergyBar(); +} + +void Interface::raiseInventoryDrawerSync() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	raiseInventoryDrawer(false); + +	while (_inventoryLid.isRunning()) { +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	vm->refreshDisplay(); +	inventoryLidOpen(false); + +	while (_inventoryPush.isFading()) { +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	vm->refreshDisplay(); +	inventoryDrawerUp(); +} + +void Interface::lowerInventoryDrawerSync() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	lowerInventoryDrawer(false); + +	while (_inventoryPush.isFading()) { +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	vm->refreshDisplay(); +	inventoryDrawerDown(false); + +	while (_inventoryLid.isRunning()) { +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	vm->refreshDisplay(); +	inventoryLidClosed(); +} + +void Interface::raiseBiochipDrawerSync() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	raiseBiochipDrawer(false); + +	while (_biochipLid.isRunning()) { +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	vm->refreshDisplay(); +	biochipLidOpen(false); + +	while (_biochipPush.isFading()) { +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	vm->refreshDisplay(); +	biochipDrawerUp(); +} + +void Interface::lowerBiochipDrawerSync() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	lowerBiochipDrawer(false); + +	while (_biochipPush.isFading()) { +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} +	 +	vm->refreshDisplay(); +	biochipDrawerDown(false); + +	while (_biochipLid.isRunning()) { +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	vm->refreshDisplay(); +	biochipLidClosed(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/interface.h b/engines/pegasus/interface.h new file mode 100644 index 0000000000..a65d9a595a --- /dev/null +++ b/engines/pegasus/interface.h @@ -0,0 +1,148 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_INTERFACE_H +#define PEGASUS_INTERFACE_H + +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/notification.h" +#include "pegasus/surface.h" +#include "pegasus/transition.h" +#include "pegasus/items/inventorypicture.h" + +namespace Pegasus { + +class BiochipItem; +class InventoryItem; + +class Interface : public InputHandler, public NotificationReceiver { +public: +	Interface(); +	virtual ~Interface(); + +	void createInterface(); + +	// Recalibration functions... +	void calibrateCompass(); +	void calibrateEnergyBar(); +	void raiseInventoryDrawerSync(); +	void lowerInventoryDrawerSync(); +	void raiseBiochipDrawerSync(); +	void lowerBiochipDrawerSync(); + +	void raiseInventoryDrawer(const bool doCallBacks = true); +	void raiseBiochipDrawer(const bool doCallBacks = true); +	void lowerInventoryDrawer(const bool doCallBacks = true); +	void lowerBiochipDrawer(const bool doCallBacks = true); + +	void raiseInventoryDrawerForMessage(); +	void lowerInventoryDrawerForMessage(); +	bool isInventoryUp(); +	bool isInventoryDown(); + +	InventoryResult addInventoryItem(InventoryItem *); +	InventoryResult removeInventoryItem(InventoryItem *); +	void removeAllItemsFromInventory(); +	InventoryItem *getCurrentInventoryItem(); +	void setCurrentInventoryItem(InventoryItem *); +	void setCurrentInventoryItemID(ItemID); +	InventoryResult addBiochip(BiochipItem *); +	void removeAllItemsFromBiochips(); +	BiochipItem *getCurrentBiochip(); +	void setCurrentBiochip(BiochipItem *); +	void setCurrentBiochipID(ItemID); + +	void setDate(const uint16); + +	void playEndMessage(); + +	void throwAwayInterface(); + +protected: +	void validateBackground(); +	void validateDateMonitor(); +	void validateCompass(); +	void validateNotifications(); +	void validateAIArea(); +	void validateInventoryPanel(); +	void validateBiochipPanel(); +	void validateEnergyMonitor(); + +	void throwAwayBackground(); +	void throwAwayDateMonitor(); +	void throwAwayCompass(); +	void throwAwayNotifications(); +	void throwAwayAIArea(); +	void throwAwayInventoryPanel(); +	void throwAwayBiochipPanel(); +	void throwAwayEnergyMonitor(); + +	void receiveNotification(Notification *, const NotificationFlags); +	void inventoryLidOpen(const bool doCallBacks); +	void inventoryLidClosed(); +	void inventoryDrawerUp(); +	void inventoryDrawerDown(const bool doCallBacks); +	void biochipLidOpen(const bool doCallBacks); +	void biochipLidClosed(); +	void biochipDrawerUp(); +	void biochipDrawerDown(const bool doCallBacks); + +	Picture _background1; +	Picture _background2; +	Picture _background3; +	Picture _background4; + +	Picture _datePicture; + +	InputHandler *_previousHandler; + +	Push _inventoryPush; +	SpriteSequence _inventoryLid; +	NotificationCallBack _inventoryPushCallBack; +	NotificationCallBack _inventoryLidCallBack; +	InventoryItemsPicture _inventoryPanel; +	bool _inventoryUp, _inventoryRaised; + +	Push _biochipPush; +	SpriteSequence _biochipLid; +	NotificationCallBack _biochipPushCallBack; +	NotificationCallBack _biochipLidCallBack; +	BiochipPicture _biochipPanel; +	bool _biochipUp, _biochipRaised; + +	Hotspot _currentItemSpot; +	Hotspot _currentBiochipSpot; + +	Notification _interfaceNotification; + +	bool _playingEndMessage; +}; + +extern Interface *g_interface; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/autodragger.cpp b/engines/pegasus/items/autodragger.cpp new file mode 100644 index 0000000000..de03f8118f --- /dev/null +++ b/engines/pegasus/items/autodragger.cpp @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/elements.h" +#include "pegasus/items/autodragger.h" + +namespace Pegasus { + +AutoDragger::AutoDragger() { +	_draggingElement = NULL; +	_lastTime = 0; +	initCallBack(this, kCallBackAtExtremes); +} + +void AutoDragger::autoDrag(DisplayElement *dragElement, const Common::Point &startPoint, const Common::Point &stopPoint, +		TimeValue dragTime, TimeScale dragScale) { +	_draggingElement = dragElement; + +	if (_draggingElement) { +		_startLocation = startPoint; +		_stopLocation = stopPoint; +		_lastTime = 0; +		_done = false; +		_draggingElement->moveElementTo(_startLocation.x, _startLocation.y); +		setScale(dragScale); +		setSegment(0, dragTime); +		setTime(0); +		scheduleCallBack(kTriggerAtStop, 0, 0); +		startIdling(); +		start(); +	} else { +		stopDragging(); +	} +} + +void AutoDragger::stopDragging() { +	cancelCallBack(); +	stopIdling(); +	_draggingElement = 0; +	_startLocation = Common::Point(); +	_stopLocation = Common::Point(); +	_lastTime = 0; +	_done = true; +} + +bool AutoDragger::isDragging() { +	return isIdling(); +} + +void AutoDragger::useIdleTime() {	 +	TimeValue thisTime = getTime(); + +	if (thisTime != _lastTime) { +		int32 offsetX = (_stopLocation.x - _startLocation.x) * (int32)thisTime / (int32)getDuration(); +		int32 offsetY = (_stopLocation.y - _startLocation.y) * (int32)thisTime / (int32)getDuration(); +		_draggingElement->moveElementTo(_startLocation.x + offsetX, _startLocation.y + offsetY); +		_lastTime = thisTime; +	} + +	if (_done) +		stopDragging(); +} + +void AutoDragger::callBack() { +	if (isIdling()) +		_done = true; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/autodragger.h b/engines/pegasus/items/autodragger.h new file mode 100644 index 0000000000..6783fdf9a3 --- /dev/null +++ b/engines/pegasus/items/autodragger.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_AUTODRAGGER_H +#define PEGASUS_ITEMS_AUTODRAGGER_H + +#include "pegasus/timers.h" + +namespace Pegasus { + +class DisplayElement; + +class AutoDragger : private Idler, private TimeBase, private TimeBaseCallBack { +public: +	AutoDragger(); +	virtual ~AutoDragger() {} + +	void autoDrag(DisplayElement *, const Common::Point &, const Common::Point &, TimeValue, TimeScale); +	bool isDragging(); +	void stopDragging(); + +protected: +	void useIdleTime(); +	void callBack(); + +	DisplayElement *_draggingElement; +	Common::Point _startLocation, _stopLocation; +	TimeValue _lastTime; +	bool _done; +}; + +} // End of namespace Pegasus + +#endif + diff --git a/engines/pegasus/items/biochips/aichip.cpp b/engines/pegasus/items/biochips/aichip.cpp new file mode 100644 index 0000000000..5e9fc9f8c3 --- /dev/null +++ b/engines/pegasus/items/biochips/aichip.cpp @@ -0,0 +1,279 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +// indexed by [number of hints][number of solves (0, 1, or 2)][which button to highlight] +static const ItemState s_highlightState[4][3][7] = { +	{ +		{kAI000,	-1,			-1,			-1,			-1,			kAI005,		kAI006}, +		{kAI010,	-1,			-1,			-1,			-1,			kAI015,		kAI016}, +		{kAI020,	-1,			-1,			-1,			kAI024,		-1,			-1} +	}, +	{ +		{kAI100,	kAI101,		-1,			-1,			-1,			kAI105,		kAI106}, +		{kAI110,	kAI111,		-1,			-1,			-1,			kAI115,		kAI116}, +		{kAI120,	kAI121,		-1,			-1,			kAI124,		kAI125,		kAI126} +	}, +	{ +		{kAI200,	kAI201,		kAI202,		-1,			-1,			kAI205,		kAI206}, +		{kAI210,	kAI211,		kAI212,		-1,			-1,			kAI215,		kAI216}, +		{kAI220,	kAI221,		kAI222,		-1,			kAI224,		kAI225,		kAI226} +	}, +	{ +		{kAI300,	kAI301,		kAI302,		kAI303,		-1,			kAI305,		kAI306}, +		{kAI310,	kAI311,		kAI312,		kAI313,		-1,			kAI315,		kAI316}, +		{kAI320,	kAI321,		kAI322,		kAI323,		kAI324,		kAI325,		kAI326} +	} +}; + +AIChip *g_AIChip = 0; + +AIChip::AIChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +		BiochipItem(id, neighborhood, room, direction), _briefingSpot(kAIBriefingSpotID), _scanSpot(kAIScanSpotID), +		_hint1Spot(kAIHint1SpotID), _hint2Spot(kAIHint2SpotID), _hint3Spot(kAIHint3SpotID), _solveSpot(kAISolveSpotID) { +	_briefingSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 10, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 10 + 81, kAIMiddleAreaTop + 27 + 31)); +	_briefingSpot.setHotspotFlags(kAIBiochipSpotFlag); +	g_allHotspots.push_back(&_briefingSpot); +	 +	_scanSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 100, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 100 + 81, kAIMiddleAreaTop + 27 + 31)); +	_scanSpot.setHotspotFlags(kAIBiochipSpotFlag); +	g_allHotspots.push_back(&_scanSpot); +	 +	_hint1Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 70, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 70 + 21, kAIMiddleAreaTop + 67 + 21)); +	_hint1Spot.setHotspotFlags(kAIBiochipSpotFlag); +	g_allHotspots.push_back(&_hint1Spot); +	 +	_hint2Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 91, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 91 + 20, kAIMiddleAreaTop + 67 + 21)); +	_hint2Spot.setHotspotFlags(kAIBiochipSpotFlag); +	g_allHotspots.push_back(&_hint2Spot); +	 +	_hint3Spot.setArea(Common::Rect(kAIMiddleAreaLeft + 111, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 111 + 20, kAIMiddleAreaTop + 67 + 21)); +	_hint3Spot.setHotspotFlags(kAIBiochipSpotFlag); +	g_allHotspots.push_back(&_hint3Spot); +	 +	_solveSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 131, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 131 + 50, kAIMiddleAreaTop + 67 + 21)); +	_solveSpot.setHotspotFlags(kAIBiochipSpotFlag); +	g_allHotspots.push_back(&_solveSpot); +	 +	_playingMovie = false; +	setItemState(kAI000); +	 +	g_AIChip = this; +} + +AIChip::~AIChip() { +	g_AIChip = NULL; +	 +	g_allHotspots.removeOneHotspot(kAIBriefingSpotID); +	g_allHotspots.removeOneHotspot(kAIScanSpotID); +	g_allHotspots.removeOneHotspot(kAIHint1SpotID); +	g_allHotspots.removeOneHotspot(kAIHint2SpotID); +	g_allHotspots.removeOneHotspot(kAIHint3SpotID); +	g_allHotspots.removeOneHotspot(kAISolveSpotID); +} + +void AIChip::select() { +	BiochipItem::select(); +	setUpAIChip(); +} + +void AIChip::takeSharedArea() { +	setUpAIChip(); +} + +void AIChip::setUpAIChip() {	 +	if (!_playingMovie) { +		PegasusEngine *vm = (PegasusEngine *)g_engine; + +		uint numSolves; +		if (GameState.getWalkthroughMode()) { +			if (vm->canSolve()) +				numSolves = 2; +			else +				numSolves = 1; +		} else { +			numSolves = 0; +		} + +		setItemState(s_highlightState[vm->getNumHints()][numSolves][0]); +	} +} + +// Only does something when there are hints or solves available. +void AIChip::setUpAIChipRude() {	 +	if (!_playingMovie) { +		PegasusEngine *vm = (PegasusEngine *)g_engine; + +		uint numSolves; +		if (GameState.getWalkthroughMode()) { +			if (vm->canSolve()) +				numSolves = 2; +			else +				numSolves = 1; +		} else { +			numSolves = 0; +		} + +		uint numHints = vm->getNumHints(); +		if (numSolves == 2 || numHints != 0) +			setItemState(s_highlightState[numHints][numSolves][0]); +	} +} + +void AIChip::activateAIHotspots() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; +	_briefingSpot.setActive(); +	_scanSpot.setActive(); + +	switch (vm->getNumHints()) { +	case 3: +		_hint3Spot.setActive(); +		// fall through +	case 2: +		_hint2Spot.setActive(); +		// fall through +	case 1: +		_hint1Spot.setActive(); +		break; +	} + +	if (GameState.getWalkthroughMode() && vm->canSolve()) +		_solveSpot.setActive(); +} + +void AIChip::showBriefingClicked() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	_playingMovie = true; + +	uint numSolves; +	if (GameState.getWalkthroughMode()) { +		if (vm->canSolve()) +			numSolves = 2; +		else +			numSolves = 1; +	} else { +		numSolves = 0; +	} + +	ItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIBriefingSpotID - kAIHint1SpotID + 1]; +	if (newState != -1) +		setItemState(newState); +} + +void AIChip::showEnvScanClicked() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	_playingMovie = true; + +	uint numSolves; +	if (GameState.getWalkthroughMode()) { +		if (vm->canSolve()) +			numSolves = 2; +		else +			numSolves = 1; +	} else { +		numSolves = 0; +	} + +	ItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIScanSpotID - kAIHint1SpotID + 1]; + +	if (newState != -1) +		setItemState(newState); +} + +void AIChip::clearClicked() { +	_playingMovie = false; +	setUpAIChip(); +} + +void AIChip::clickInAIHotspot(HotSpotID id) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	Common::String movieName; + +	switch (id) { +	case kAIBriefingSpotID: +		movieName = vm->getBriefingMovie(); +		break; +	case kAIScanSpotID: +		movieName = vm->getEnvScanMovie(); +		break; +	case kAIHint1SpotID: +		movieName = vm->getHintMovie(1); +		break; +	case kAIHint2SpotID: +		movieName = vm->getHintMovie(2); +		break; +	case kAIHint3SpotID: +		movieName = vm->getHintMovie(3); +		break; +	case kAISolveSpotID: +		g_neighborhood->doSolve(); +		break; +	} + +	ItemState state = getItemState(); + +	if (!movieName.empty()) { +		_playingMovie = true; + +		uint numSolves; +		if (GameState.getWalkthroughMode()) { +			if (vm->canSolve()) +				numSolves = 2; +			else +				numSolves = 1; +		} else { +			numSolves = 0; +		} + +		ItemState newState = s_highlightState[vm->getNumHints()][numSolves][id - kAIHint1SpotID + 1]; + +		if (newState != -1) +			setItemState(newState); + +		if (g_AIArea) { +			vm->prepareForAIHint(movieName); +			g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kHintInterruption); +			vm->cleanUpAfterAIHint(movieName); +		} + +		if (newState != -1) +			setItemState(state); + +		_playingMovie = false; +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/aichip.h b/engines/pegasus/items/biochips/aichip.h new file mode 100644 index 0000000000..7a33953612 --- /dev/null +++ b/engines/pegasus/items/biochips/aichip.h @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_AICHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_AICHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class AIChip : public BiochipItem { +public: +	AIChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~AIChip(); + +	void select(); + +	void setUpAIChip(); + +	// Called to set up the AI chip when the AI chip is the current chip but does not +	// own the center area. +	void setUpAIChipRude(); +	void activateAIHotspots(); +	void clickInAIHotspot(HotSpotID); + +	void takeSharedArea(); + +	void showBriefingClicked(); +	void showEnvScanClicked(); +	void clearClicked(); + +protected: +	Hotspot _briefingSpot; +	Hotspot _scanSpot; +	Hotspot _hint1Spot; +	Hotspot _hint2Spot; +	Hotspot _hint3Spot; +	Hotspot _solveSpot; +	bool _playingMovie; +}; + +extern AIChip *g_AIChip; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/biochipitem.cpp b/engines/pegasus/items/biochips/biochipitem.cpp new file mode 100644 index 0000000000..5686948937 --- /dev/null +++ b/engines/pegasus/items/biochips/biochipitem.cpp @@ -0,0 +1,95 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include "common/stream.h" + +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +BiochipItem::BiochipItem(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +		Item(id, neighborhood, room, direction) { + +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	Common::SeekableReadStream *biochipInfo = vm->_resFork->getResource(MKTAG('B', 'i', 'o', 'I'), kItemBaseResID + id); +	if (biochipInfo) { +		_biochipInfoPanelTime = biochipInfo->readUint32BE(); +		delete biochipInfo; +	} else { +		_biochipInfoPanelTime = 0; +	} + +	Common::SeekableReadStream *rightInfo = vm->_resFork->getResource(MKTAG('R', 'g', 'h', 't'), kItemBaseResID + id); +	if (!rightInfo) +		error("Could not find right info for biochip %d", id); + +	_rightAreaInfo = readItemState(rightInfo); +	delete rightInfo; + +	setItemState(kNormalItem); +} + +BiochipItem::~BiochipItem() { +	delete[] _rightAreaInfo.entries; +} + +ItemType BiochipItem::getItemType() { +	return kBiochipItemType; +} + +TimeValue BiochipItem::getRightAreaTime() const { +	if (!_rightAreaInfo.entries) +		return 0xffffffff; + +	TimeValue time; +	ItemState state; + +	findItemStateEntryByState(_rightAreaInfo, _itemState, time); +	if (time == 0xffffffff) +		getItemStateEntry(_rightAreaInfo, 0, state, time); + +	return time; +} + +//	Must affect images in right area. +void BiochipItem::select() { +	Item::select(); + +	if (g_AIArea) +		g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, getRightAreaTime()); +} + +void BiochipItem::deselect() { +	Item::deselect(); + +	if (g_AIArea) +		g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, 0xffffffff); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/biochipitem.h b/engines/pegasus/items/biochips/biochipitem.h new file mode 100644 index 0000000000..24b9ae699a --- /dev/null +++ b/engines/pegasus/items/biochips/biochipitem.h @@ -0,0 +1,54 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_BIOCHIPITEM_H +#define PEGASUS_ITEMS_BIOCHIPS_BIOCHIPITEM_H + +#include "pegasus/items/item.h" + +namespace Pegasus { + +class BiochipItem : public Item { +public: +	BiochipItem(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~BiochipItem(); +	 +	virtual ItemType getItemType(); +	 +	TimeValue getPanelTime() const { return _biochipInfoPanelTime; } +	TimeValue getRightAreaTime() const; +	 +	// Must affect images in right area. +	virtual void select(); +	virtual void deselect(); + +protected: +	TimeValue _biochipInfoPanelTime; +	ItemStateInfo _rightAreaInfo; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/mapchip.cpp b/engines/pegasus/items/biochips/mapchip.cpp new file mode 100644 index 0000000000..69050d5193 --- /dev/null +++ b/engines/pegasus/items/biochips/mapchip.cpp @@ -0,0 +1,106 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/mapchip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +MapChip *g_map = 0; + +MapChip::MapChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +		BiochipItem(id, neighborhood, room, direction) { +	g_map = this; +	setItemState(kMapUnavailable); +} + +MapChip::~MapChip() { +	g_map = 0; +} + +void MapChip::writeToStream(Common::WriteStream *stream) { +	return _image.writeToStream(stream); +} + +void MapChip::readFromStream(Common::ReadStream *stream) { +	return _image.readFromStream(stream); +} + +void MapChip::select() { +	BiochipItem::select(); +	moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), GameState.getCurrentDirection()); +	_image.show(); +} + +void MapChip::takeSharedArea() { +	_image.show(); +} + +void MapChip::giveUpSharedArea() { +	_image.hide(); +} + +void MapChip::deselect() { +	BiochipItem::deselect(); +	_image.unloadImage(); +} + +void MapChip::moveToMapLocation(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant dir) { +	AirQuality airQuality; + +	if (g_neighborhood) +		airQuality = g_neighborhood->getAirQuality(room); +	else +		airQuality = kAirQualityGood; + +	switch (neighborhood) { +	case kMarsID: +		if (airQuality == kAirQualityVacuum) { +			if (room >= kMars35 && room <= kMars39) { +				setItemState(kMapEngaged); +				if (isSelected() && g_AIArea && g_AIArea->getMiddleAreaOwner() == kBiochipSignature) +					_image.loadGearRoomIfNecessary(); +			} else { +				setItemState(kMapEngaged); +				if (isSelected() && g_AIArea && g_AIArea->getMiddleAreaOwner() == kBiochipSignature) +					_image.loadMazeIfNecessary(); +			} + +			_image.moveToMapLocation(neighborhood, room, dir); +		} else { +			_image.unloadImage(); +			setItemState(kMapUnavailable); +		} +		break; +	default: +		_image.unloadImage(); +		setItemState(kMapUnavailable); +		break; +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/mapchip.h b/engines/pegasus/items/biochips/mapchip.h new file mode 100644 index 0000000000..6690090aa4 --- /dev/null +++ b/engines/pegasus/items/biochips/mapchip.h @@ -0,0 +1,64 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_MAPCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_MAPCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/mapimage.h" + +namespace Common { +	class ReadStream; +	class WriteStream; +} + +namespace Pegasus { + +class MapChip : public BiochipItem { +public: +	MapChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~MapChip(); + +	void select(); +	void deselect(); +	void takeSharedArea(); +	void giveUpSharedArea(); + +	void moveToMapLocation(const NeighborhoodID, const RoomID, const DirectionConstant); + +	void writeToStream(Common::WriteStream *); +	void readFromStream(Common::ReadStream *); + +	bool beenToMaze() { return _image.anyFlagSet(); } + +protected: +	MapImage _image; +}; + +extern MapChip *g_map; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/mapimage.cpp b/engines/pegasus/items/biochips/mapimage.cpp new file mode 100644 index 0000000000..bd7b8ff910 --- /dev/null +++ b/engines/pegasus/items/biochips/mapimage.cpp @@ -0,0 +1,443 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/biochips/mapimage.h" + +namespace Pegasus { + +#define FLAG_TO_INDEX(flag) ((flag) >> 2) +#define INDEX_TO_FLAG(index) ((index) << 2) + +#define ROOM_TO_INDEX(room) \ +	(((room) >= kMars35 && (room) <= kMars39) ? ((room) - kMars35) : \ +		(((room) == kMars60) ? (kMars39 - kMars35 + 1) : \ +			((room) - kMarsMaze004 + kMars39 - kMars35 + 2))) + +#define INDEX_TO_ROOM(index) \ +	(((index) <= ROOM_TO_INDEX(kMars39)) ? \ +	(((index) - ROOM_TO_INDEX(kMars35)) + kMars35) : \ +		((index) <= ROOM_TO_INDEX(kMars60,)) ? kMars60 : \ +			((((index) - ROOM_TO_INDEX(kMarsMaze004))) + kMarsMaze004)) + +#define ROOM_TO_FLAG(room, dir) (INDEX_TO_FLAG(ROOM_TO_INDEX(room)) | (dir)) + +#define FLAG_TO_ROOM(flag) (INDEX_TO_ROOM(FLAG_TO_INDEX(flag))) + +#define FLAG_TO_DIRECTION(flag) ((flag) & 3) + +static const int kGearRoomFlagLow = ROOM_TO_FLAG(kMars35, kNorth); +static const int kGearRoomFlagHigh = ROOM_TO_FLAG(kMars39, kWest); + +static const int kMazeFlagLow = ROOM_TO_FLAG(kMars60, kNorth); +static const int kMazeFlagHigh = ROOM_TO_FLAG(kMarsMaze200, kWest); + +static const CoordType kGearRoomScreenOffsetX = 49; +static const CoordType kGearRoomScreenOffsetY = 47; + +static const CoordType kGearRoomGridOriginX = 1; +static const CoordType kGearRoomGridOriginY = 4; + +static const CoordType kMazeScreenOffsetX = 16; +static const CoordType kMazeScreenOffsetY = 20; + +static const CoordType kMazeGridOriginX = 6; +static const CoordType kMazeGridOriginY = 1; + +static const CoordType kGridWidth = 4; +static const CoordType kGridHeight = 4; + +static const uint16 kMapOfMazePICTID = 906; +static const uint16 kMapOfGearRoomPICTID = 907; + +static const int s_mapCoords[MapImage::kNumMappingRooms][2] = { +	/* kMars35 */      { 0, 0 }, +	/* kMars36 */      { 1, 0 }, +	/* kMars37 */      { 2, 0 }, +	/* kMars38 */      { 3, 0 }, +	/* kMars39 */      { 4, 0 }, +	/* kMars60 */      { 19, 9 }, +	/* kMarsMaze004 */ { 18, 9 }, +	/* kMarsMaze005 */ { 18, 10 }, +	/* kMarsMaze006 */ { 17, 10 }, +	/* kMarsMaze007 */ { 16, 10 }, +	/* kMarsMaze008 */ { 15, 10 }, +	/* kMarsMaze009 */ { 14, 10 }, +	/* kMarsMaze010 */ { 14, 9 }, +	/* kMarsMaze011 */ { 14, 8 }, +	/* kMarsMaze012 */ { 14, 7 }, +	/* kMarsMaze015 */ { 16, 7 }, +	/* kMarsMaze016 */ { 14, 11 }, +	/* kMarsMaze017 */ { 14, 12 }, +	/* kMarsMaze018 */ { 15, 12 }, +	/* kMarsMaze019 */ { 16, 12 }, +	/* kMarsMaze020 */ { 16, 13 }, +	/* kMarsMaze021 */ { 16, 14 }, +	/* kMarsMaze022 */ { 16, 15 }, +	/* kMarsMaze023 */ { 17, 15 }, +	/* kMarsMaze024 */ { 18, 15 }, +	/* kMarsMaze025 */ { 18, 14 }, +	/* kMarsMaze026 */ { 18, 13 }, +	/* kMarsMaze027 */ { 18, 12 }, +	/* kMarsMaze028 */ { 18, 11 }, +	/* kMarsMaze031 */ { 19, 14 }, +	/* kMarsMaze032 */ { 20, 14 }, +	/* kMarsMaze033 */ { 20, 13 }, +	/* kMarsMaze034 */ { 20, 12 }, +	/* kMarsMaze035 */ { 20, 11 }, +	/* kMarsMaze036 */ { 21, 11 }, +	/* kMarsMaze037 */ { 15, 15 }, +	/* kMarsMaze038 */ { 14, 15 }, +	/* kMarsMaze039 */ { 13, 15 }, +	/* kMarsMaze042 */ { 10, 15 }, +	/* kMarsMaze043 */ { 9, 15 }, +	/* kMarsMaze044 */ { 8, 15 }, +	/* kMarsMaze045 */ { 7, 15 }, +	/* kMarsMaze046 */ { 6, 15 }, +	/* kMarsMaze047 */ { 5, 15 }, +	/* kMarsMaze049 */ { 13, 14 }, +	/* kMarsMaze050 */ { 12, 14 }, +	/* kMarsMaze051 */ { 11, 14 }, +	/* kMarsMaze052 */ { 10, 14 }, +	/* kMarsMaze053 */ { 10, 13 }, +	/* kMarsMaze054 */ { 9, 13 }, +	/* kMarsMaze055 */ { 8, 13 }, +	/* kMarsMaze056 */ { 8, 12 }, +	/* kMarsMaze057 */ { 7, 12 }, +	/* kMarsMaze058 */ { 12, 13 }, +	/* kMarsMaze059 */ { 12, 12 }, +	/* kMarsMaze060 */ { 12, 11 }, +	/* kMarsMaze061 */ { 12, 10 }, +	/* kMarsMaze063 */ { 12, 9 }, +	/* kMarsMaze064 */ { 12, 8 }, +	/* kMarsMaze065 */ { 12, 7 }, +	/* kMarsMaze066 */ { 13, 7 }, +	/* kMarsMaze067 */ { 15, 7 }, +	/* kMarsMaze068 */ { 17, 7 }, +	/* kMarsMaze069 */ { 18, 7 }, +	/* kMarsMaze070 */ { 19, 7 }, +	/* kMarsMaze071 */ { 20, 7 }, +	/* kMarsMaze072 */ { 20, 6 }, +	/* kMarsMaze074 */ { 20, 5 }, +	/* kMarsMaze076 */ { 20, 4 }, +	/* kMarsMaze078 */ { 20, 3 }, +	/* kMarsMaze079 */ { 20, 2 }, +	/* kMarsMaze081 */ { 20, 2 }, +	/* kMarsMaze083 */ { 20, 0 }, +	/* kMarsMaze084 */ { 19, 0 }, +	/* kMarsMaze085 */ { 18, 0 }, +	/* kMarsMaze086 */ { 17, 0 }, +	/* kMarsMaze087 */ { 16, 0 }, +	/* kMarsMaze088 */ { 15, 0 }, +	/* kMarsMaze089 */ { 14, 0 }, +	/* kMarsMaze090 */ { 13, 0 }, +	/* kMarsMaze091 */ { 12, 0 }, +	/* kMarsMaze092 */ { 11, 0 }, +	/* kMarsMaze093 */ { 10, 0 }, +	/* kMarsMaze098 */ { 10, 1 }, +	/* kMarsMaze099 */ { 8, 2 }, +	/* kMarsMaze100 */ { 9, 2 }, +	/* kMarsMaze101 */ { 10, 2 }, +	/* kMarsMaze104 */ { 13, 2 }, +	/* kMarsMaze105 */ { 13, 3 }, +	/* kMarsMaze106 */ { 13, 4 }, +	/* kMarsMaze107 */ { 13, 5 }, +	/* kMarsMaze108 */ { 14, 5 }, +	/* kMarsMaze111 */ { 15, 5 }, +	/* kMarsMaze113 */ { 16, 5 }, +	/* kMarsMaze114 */ { 17, 5 }, +	/* kMarsMaze115 */ { 18, 5 }, +	/* kMarsMaze116 */ { 18, 4 }, +	/* kMarsMaze117 */ { 18, 3 }, +	/* kMarsMaze118 */ { 19, 3 }, +	/* kMarsMaze119 */ { 18, 2 }, +	/* kMarsMaze120 */ { 17, 2 }, +	/* kMarsMaze121 */ { 16, 2 }, +	/* kMarsMaze122 */ { 15, 2 }, +	/* kMarsMaze123 */ { 15, 1 }, +	/* kMarsMaze124 */ { 12, 4 }, +	/* kMarsMaze125 */ { 11, 4 }, +	/* kMarsMaze126 */ { 10, 4 }, +	/* kMarsMaze127 */ { 10, 5 }, +	/* kMarsMaze128 */ { 10, 6 }, +	/* kMarsMaze129 */ { 9, 6 }, +	/* kMarsMaze130 */ { 8, 6 }, +	/* kMarsMaze131 */ { 7, 6 }, +	/* kMarsMaze132 */ { 7, 7 }, +	/* kMarsMaze133 */ { 7, 8 }, +	/* kMarsMaze136 */ { 7, 11 }, +	/* kMarsMaze137 */ { 6, 11 }, +	/* kMarsMaze138 */ { 5, 11 }, +	/* kMarsMaze139 */ { 5, 12 }, +	/* kMarsMaze140 */ { 4, 12 }, +	/* kMarsMaze141 */ { 5, 13 }, +	/* kMarsMaze142 */ { 5, 14 }, +	/* kMarsMaze143 */ { 4, 14 }, +	/* kMarsMaze144 */ { 3, 14 }, +	/* kMarsMaze145 */ { 3, 13 }, +	/* kMarsMaze146 */ { 2, 13 }, +	/* kMarsMaze147 */ { 1, 13 }, +	/* kMarsMaze148 */ { 1, 14 }, +	/* kMarsMaze149 */ { 1, 15 }, +	/* kMarsMaze152 */ { 1, 12 }, +	/* kMarsMaze153 */ { 1, 11 }, +	/* kMarsMaze154 */ { 1, 10 }, +	/* kMarsMaze155 */ { 1, 9 }, +	/* kMarsMaze156 */ { 1, 8 }, +	/* kMarsMaze157 */ { 2, 10 }, +	/* kMarsMaze159 */ { 2, 8 }, +	/* kMarsMaze160 */ { 2, 7 }, +	/* kMarsMaze161 */ { 2, 6 }, +	/* kMarsMaze162 */ { 3, 10 }, +	/* kMarsMaze163 */ { 3, 9 }, +	/* kMarsMaze164 */ { 3, 8 }, +	/* kMarsMaze165 */ { 4, 8 }, +	/* kMarsMaze166 */ { 5, 8 }, +	/* kMarsMaze167 */ { 6, 8 }, +	/* kMarsMaze168 */ { 3, 6 }, +	/* kMarsMaze169 */ { 4, 6 }, +	/* kMarsMaze170 */ { 5, 6 }, +	/* kMarsMaze171 */ { 5, 5 }, +	/* kMarsMaze172 */ { 5, 4 }, +	/* kMarsMaze173 */ { 4, 4 }, +	/* kMarsMaze174 */ { 3, 4 }, +	/* kMarsMaze175 */ { 3, 5 }, +	/* kMarsMaze177 */ { 8, 4 }, +	/* kMarsMaze178 */ { 8, 3 }, +	/* kMarsMaze179 */ { 7, 4 }, +	/* kMarsMaze180 */ { 6, 4 }, +	/* kMarsMaze181 */ { 6, 3 }, +	/* kMarsMaze182 */ { 6, 2 }, +	/* kMarsMaze183 */ { 6, 1 }, +	/* kMarsMaze184 */ { 6, 0 }, +	/* kMarsMaze187 */ { 3, 0 }, +	/* kMarsMaze188 */ { 2, 0 }, +	/* kMarsMaze189 */ { 1, 0 }, +	/* kMarsMaze190 */ { 1, 1 }, +	/* kMarsMaze191 */ { 1, 2 }, +	/* kMarsMaze192 */ { 5, 2 }, +	/* kMarsMaze193 */ { 4, 2 }, +	/* kMarsMaze194 */ { 3, 2 }, +	/* kMarsMaze195 */ { 3, 1 }, +	/* kMarsMaze198 */ { 1, 3 }, +	/* kMarsMaze199 */ { 1, 4 }, +	/* kMarsMaze200 */ { 0, 4 } +}; + +MapImage::MapImage() : DisplayElement(kNoDisplayElement) { +	_whichArea = kMapNoArea; +	setBounds(kAIMiddleAreaLeft, kAIMiddleAreaTop, kAIMiddleAreaLeft + kAIMiddleAreaWidth, kAIMiddleAreaTop + kAIMiddleAreaHeight); +	setDisplayOrder(kAIMiddleAreaOrder + 10); +	startDisplaying(); + +	_darkGreen = g_system->getScreenFormat().RGBToColor(64, 150, 10); +	_lightGreen = g_system->getScreenFormat().RGBToColor(102, 239, 0); +} + +void MapImage::writeToStream(Common::WriteStream *stream) { +	_mappedRooms.writeToStream(stream); +} + +void MapImage::readFromStream(Common::ReadStream *stream) { +	_mappedRooms.readFromStream(stream); +} + +void MapImage::loadGearRoomIfNecessary() {	 +	if (_whichArea != kMapGearRoom) { +		_mapImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMapOfGearRoomPICTID); + +		Common::Rect bounds; +		_mapImage.getSurfaceBounds(bounds); +		_mapMask.allocateSurface(bounds); +		_whichArea = kMapGearRoom; + +		GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; +		gfx->setCurSurface(_mapMask.getSurface()); + +		gfx->getCurSurface()->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + +		for (int i = kGearRoomFlagLow; i <= kGearRoomFlagHigh; i++) +			if (_mappedRooms.getFlag(i)) +				addFlagToMask(i); + +		gfx->setCurSurface(gfx->getWorkArea()); +		show(); +	} +} + +void MapImage::loadMazeIfNecessary() {	 +	if (_whichArea != kMapMaze) { +		_mapImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMapOfMazePICTID); + +		Common::Rect bounds; +		_mapImage.getSurfaceBounds(bounds); +		_mapMask.allocateSurface(bounds); +		_whichArea = kMapMaze; + +		GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; +		gfx->setCurSurface(_mapMask.getSurface()); + +		gfx->getCurSurface()->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + +		for (int i = kMazeFlagLow; i <= kMazeFlagHigh; i++) +			if (_mappedRooms.getFlag(i)) +				addFlagToMask(i); + +		gfx->setCurSurface(gfx->getWorkArea()); +		show(); +	} +} + +void MapImage::unloadImage() { +	_mapImage.deallocateSurface(); +	_mapMask.deallocateSurface(); +	hide(); +	_whichArea = kMapNoArea; +} + +void MapImage::moveToMapLocation(const NeighborhoodID, const RoomID room, const DirectionConstant dir) { +	GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; + +	int flag = ROOM_TO_FLAG(room, dir); + +	if (!_mappedRooms.getFlag(flag)) { +		_mappedRooms.setFlag(flag, true); + +		if (_mapMask.isSurfaceValid()) { +			gfx->setCurSurface(_mapMask.getSurface()); +			addFlagToMask(flag); +			gfx->setCurSurface(gfx->getWorkArea()); +		} +	} + +	if (isDisplaying()) +		triggerRedraw(); +} + +void MapImage::addFlagToMask(const int flag) { +	Common::Rect r1; +	getRevealedRects(flag, r1); +	((PegasusEngine *)g_engine)->_gfx->getCurSurface()->fillRect(r1, g_system->getScreenFormat().RGBToColor(0, 0, 0)); +} + +// This function can even be sensitive to open doors. +// clone2727 notices that it's not, though +void MapImage::getRevealedRects(const uint32 flag, Common::Rect &r1) {	 +	CoordType gridX, gridY; + +	switch (_whichArea) { +	case kMapMaze: +		gridX = kMazeGridOriginX; +		gridY = kMazeGridOriginY; +		break; +	case kMapGearRoom: +		gridX = kGearRoomGridOriginX; +		gridY = kGearRoomGridOriginY; +		break; +	default: +		return; +	} + +	int index = FLAG_TO_INDEX(flag); +	gridX += s_mapCoords[index][0] * kGridWidth; +	gridY += s_mapCoords[index][1] * kGridHeight; + +	r1 = Common::Rect(gridX - 1, gridY - 1, gridX + kGridWidth + 1, gridY + kGridHeight + 1); +} + +void MapImage::drawPlayer() { +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + +	CoordType gridX, gridY; + +	switch (_whichArea) { +	case kMapMaze: +		gridX = _bounds.left + kMazeScreenOffsetX + kMazeGridOriginX; +		gridY = _bounds.top + kMazeScreenOffsetY + kMazeGridOriginY; +		break; +	case kMapGearRoom: +		gridX = _bounds.left + kGearRoomScreenOffsetX + kGearRoomGridOriginX; +		gridY = _bounds.top + kGearRoomScreenOffsetY + kGearRoomGridOriginY; +		break; +	default: +		return; +	} + +	int index = ROOM_TO_INDEX(GameState.getCurrentRoom()); +	gridX += s_mapCoords[index][0] * kGridWidth; +	gridY += s_mapCoords[index][1] * kGridHeight; + +	// This was intended to make little arrows +	switch (GameState.getCurrentDirection()) { +	case kNorth: +		screen->drawLine(gridX + 1, gridY, gridX + 2, gridY, _darkGreen); +		screen->drawLine(gridX, gridY + 1, gridX + 3, gridY + 1, _darkGreen); +		screen->drawLine(gridX + 1, gridY + 1, gridX + 2, gridY + 1, _lightGreen); +		screen->drawLine(gridX, gridY + 2, gridX + 3, gridY + 2, _lightGreen); +		break; +	case kSouth: +		screen->drawLine(gridX + 1, gridY + 3, gridX + 2, gridY + 3, _darkGreen); +		screen->drawLine(gridX, gridY + 2, gridX + 3, gridY + 2, _darkGreen); +		screen->drawLine(gridX + 1, gridY + 2, gridX + 2, gridY + 2, _lightGreen); +		screen->drawLine(gridX, gridY + 1, gridX + 3, gridY + 1, _lightGreen); +		break; +	case kEast: +		screen->drawLine(gridX + 3, gridY + 1, gridX + 3, gridY + 2, _darkGreen); +		screen->drawLine(gridX + 2, gridY, gridX + 2, gridY + 3, _darkGreen); +		screen->drawLine(gridX + 2, gridY + 1, gridX + 2, gridY + 2, _lightGreen); +		screen->drawLine(gridX + 1, gridY, gridX + 1, gridY + 3, _lightGreen); +		break; +	case kWest: +		screen->drawLine(gridX, gridY + 1, gridX, gridY + 2, _darkGreen); +		screen->drawLine(gridX + 1, gridY, gridX + 1, gridY + 3, _darkGreen); +		screen->drawLine(gridX + 1, gridY + 1, gridX + 1, gridY + 2, _lightGreen); +		screen->drawLine(gridX + 2, gridY, gridX + 2, gridY + 3, _lightGreen); +		break; +	} +} + +void MapImage::draw(const Common::Rect &) { +	Common::Rect r1; +	_mapImage.getSurfaceBounds(r1); + +	Common::Rect r2 = r1; +	switch (_whichArea) { +	case kMapMaze: +		r2.moveTo(_bounds.left + kMazeScreenOffsetX, _bounds.top + kMazeScreenOffsetY); +		break; +	case kMapGearRoom: +		r2.moveTo(_bounds.left + kGearRoomScreenOffsetX, _bounds.top + kGearRoomScreenOffsetY); +		break; +	default: +		return; +	} + +	_mapImage.copyToCurrentPortMasked(r1, r2, &_mapMask); + +	drawPlayer(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/mapimage.h b/engines/pegasus/items/biochips/mapimage.h new file mode 100644 index 0000000000..d596b585c4 --- /dev/null +++ b/engines/pegasus/items/biochips/mapimage.h @@ -0,0 +1,84 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_MAPIMAGE_H +#define PEGASUS_ITEMS_BIOCHIPS_MAPIMAGE_H + +#include "pegasus/elements.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" +#include "pegasus/neighborhood/mars/constants.h" + +namespace Common { +	class ReadStream; +	class WriteStream; +} + +namespace Pegasus { + +class MapImage : public DisplayElement { +public: +	MapImage(); +	virtual ~MapImage() {} + +	void writeToStream(Common::WriteStream *); +	void readFromStream(Common::ReadStream *); + +	void loadGearRoomIfNecessary(); +	void loadMazeIfNecessary(); +	void unloadImage(); +	void moveToMapLocation(const NeighborhoodID, const RoomID, const DirectionConstant); +	 +	void draw(const Common::Rect &); +	 +	bool anyFlagSet() { return _mappedRooms.anyFlagSet(); } + +	static const uint32 kNumMappingRooms = (kMars39 - kMars35 + 1) + (kMars60 - kMars60 + 1) + +			(kMarsMaze200 - kMarsMaze004 + 1); +	static const uint32 kNumMappingFlags = kNumMappingRooms * 4; + +protected: +	enum MapArea { +		kMapNoArea, +		kMapMaze, +		kMapGearRoom +	}; + +	void addFlagToMask(const int flag); +	void getRevealedRects(const uint32, Common::Rect &); +	void drawPlayer(); + +	MapArea _whichArea; + +	FlagsArray<byte, kNumMappingFlags> _mappedRooms; + +	uint32 _darkGreen, _lightGreen; + +	Surface _mapImage, _mapMask; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/opticalchip.cpp b/engines/pegasus/items/biochips/opticalchip.cpp new file mode 100644 index 0000000000..6a9a41d573 --- /dev/null +++ b/engines/pegasus/items/biochips/opticalchip.cpp @@ -0,0 +1,189 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" + +namespace Pegasus { + +OpticalChip *g_opticalChip = 0; + +OpticalChip::OpticalChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +		BiochipItem(id, neighborhood, room, direction), _ariesHotspot(kAriesSpotID), _mercuryHotspot(kMercurySpotID), +		_poseidonHotspot(kPoseidonSpotID) { +	_ariesHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 27, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 27 + 20)); +	_ariesHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); +	g_allHotspots.push_back(&_ariesHotspot); + +	_mercuryHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 47, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 47 + 20)); +	_mercuryHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); +	g_allHotspots.push_back(&_mercuryHotspot); + +	_poseidonHotspot.setArea(Common::Rect(kAIMiddleAreaLeft + 60, kAIMiddleAreaTop + 67, kAIMiddleAreaLeft + 60 + 121, kAIMiddleAreaTop + 67 + 20)); +	_poseidonHotspot.setHotspotFlags(kOpticalBiochipSpotFlag); +	g_allHotspots.push_back(&_poseidonHotspot); + +	setItemState(kOptical000); + +	g_opticalChip = this; +} + +OpticalChip::~OpticalChip() { +	g_allHotspots.removeOneHotspot(kAriesSpotID); +	g_allHotspots.removeOneHotspot(kMercurySpotID); +	g_allHotspots.removeOneHotspot(kPoseidonSpotID); +} + +void OpticalChip::writeToStream(Common::WriteStream *stream) { +	BiochipItem::writeToStream(stream); +	_opticalFlags.writeToStream(stream); +} + +void OpticalChip::readFromStream(Common::ReadStream *stream) { +	BiochipItem::readFromStream(stream); +	_opticalFlags.readFromStream(stream); +} + +void OpticalChip::addAries() { +	_opticalFlags.setFlag(kOpticalAriesExposed, true); +	setUpOpticalChip(); +} + +void OpticalChip::addMercury() { +	_opticalFlags.setFlag(kOpticalMercuryExposed, true); +	setUpOpticalChip(); +} + +void OpticalChip::addPoseidon() { +	_opticalFlags.setFlag(kOpticalPoseidonExposed, true); +	setUpOpticalChip(); +} + +void OpticalChip::setUpOpticalChip() { +	if (_opticalFlags.getFlag(kOpticalAriesExposed)) { +		if (_opticalFlags.getFlag(kOpticalMercuryExposed)) { +			if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) +				setItemState(kOptical111); +			else +				setItemState(kOptical011); +		} else { +			if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) +				setItemState(kOptical101); +			else +				setItemState(kOptical001); +		} +	} else { +		if (_opticalFlags.getFlag(kOpticalMercuryExposed)) { +			if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) +				setItemState(kOptical110); +			else +				setItemState(kOptical010); +		} else { +			if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) +				setItemState(kOptical100); +			else +				setItemState(kOptical000); +		} +	} +} + +void OpticalChip::activateOpticalHotspots() { +	if (_opticalFlags.getFlag(kOpticalAriesExposed)) +		_ariesHotspot.setActive(); +	if (_opticalFlags.getFlag(kOpticalMercuryExposed)) +		_mercuryHotspot.setActive(); +	if (_opticalFlags.getFlag(kOpticalPoseidonExposed)) +		_poseidonHotspot.setActive(); +} + +void OpticalChip::clickInOpticalHotspot(HotSpotID id) { +	playOpMemMovie(id); +} + +void OpticalChip::playOpMemMovie(HotSpotID id) { +	Common::String movieName; +	switch (id) { +	case kAriesSpotID: +		movieName = "Images/AI/Globals/OMAI"; +		break; +	case kMercurySpotID: +		movieName = "Images/AI/Globals/OMMI"; +		break; +	case kPoseidonSpotID: +		movieName = "Images/AI/Globals/OMPI"; +		break; +	} + +	ItemState state = getItemState(), newState; +	switch (state) { +	case kOptical000: +		// Can never happen. +		break; +	case kOptical001: +		newState = kOptical002; +		break; +	case kOptical010: +		newState = kOptical020; +		break; +	case kOptical011: +		if (id == kAriesSpotID) +			newState = kOptical012; +		else +			newState = kOptical021; +		break; +	case kOptical100: +		newState = kOptical200; +		break; +	case kOptical101: +		if (id == kAriesSpotID) +			newState = kOptical102; +		else +			newState = kOptical201; +		break; +	case kOptical110: +		if (id == kMercurySpotID) +			newState = kOptical120; +		else +			newState = kOptical210; +		break; +	case kOptical111: +		if (id == kAriesSpotID) +			newState = kOptical112; +		else if (id == kMercurySpotID) +			newState = kOptical121; +		else +			newState = kOptical211; +		break; +	} + +	setItemState(newState); + +	if (g_AIArea) +		g_AIArea->playAIMovie(kRightAreaSignature, movieName, false, kOpticalInterruption); + +	setItemState(state); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/opticalchip.h b/engines/pegasus/items/biochips/opticalchip.h new file mode 100644 index 0000000000..b1ea87bffc --- /dev/null +++ b/engines/pegasus/items/biochips/opticalchip.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_OPTICALCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_OPTICALCHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/util.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class OpticalChip : public BiochipItem { +public: +	OpticalChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~OpticalChip(); + +	virtual void writeToStream(Common::WriteStream *); +	virtual void readFromStream(Common::ReadStream *); + +	void addAries(); +	void addMercury(); +	void addPoseidon(); + +	void activateOpticalHotspots(); +	void clickInOpticalHotspot(HotSpotID); +	void playOpMemMovie(HotSpotID); + +protected: +	enum { +		kOpticalAriesExposed, +		kOpticalMercuryExposed, +		kOpticalPoseidonExposed, +		kNumOpticalChipFlags +	}; +	 +	void setUpOpticalChip(); +	 +	FlagsArray<byte, kNumOpticalChipFlags> _opticalFlags; +	Hotspot _ariesHotspot; +	Hotspot _mercuryHotspot; +	Hotspot _poseidonHotspot; +}; + +extern OpticalChip *g_opticalChip; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/pegasuschip.cpp b/engines/pegasus/items/biochips/pegasuschip.cpp new file mode 100644 index 0000000000..320662af06 --- /dev/null +++ b/engines/pegasus/items/biochips/pegasuschip.cpp @@ -0,0 +1,190 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" + +namespace Pegasus { + +PegasusChip::PegasusChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +		BiochipItem(id, neighborhood, room, direction), _recallSpot(kPegasusRecallSpotID) { +	_recallSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 116, kAIMiddleAreaTop + 63, kAIMiddleAreaLeft + 184, kAIMiddleAreaTop + 91)); +	_recallSpot.setHotspotFlags(kPegasusBiochipSpotFlag); +	g_allHotspots.push_back(&_recallSpot); +	setItemState(kPegasusTSA00); +} + +PegasusChip::~PegasusChip() { +	g_allHotspots.removeOneHotspot(kPegasusRecallSpotID); +} + +void PegasusChip::select() { +	BiochipItem::select(); +	setUpPegasusChip(); +} + +void PegasusChip::setUpPegasusChip() { +	switch (GameState.getCurrentNeighborhood()) { +	case kCaldoriaID: +		setItemState(kPegasusCaldoria); +		break; +	case kFullTSAID: +	case kFinalTSAID: +	case kTinyTSAID: +		setItemState(kPegasusTSA10); +		break; +	case kPrehistoricID: +		if (((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog)) +			setItemState(kPegasusPrehistoric00); +		else +			setItemState(kPegasusPrehistoric10); +		break; +	case kMarsID: +		if (GameState.getMarsFinished()) +			setItemState(kPegasusMars00); +		else +			setItemState(kPegasusMars10); +		break; +	case kWSCID: +		if (GameState.getWSCFinished()) +			setItemState(kPegasusWSC00); +		else +			setItemState(kPegasusWSC10); +		break; +	case kNoradAlphaID: +	case kNoradDeltaID: +		if (GameState.getNoradFinished()) +			setItemState(kPegasusNorad00); +		else +			setItemState(kPegasusNorad10); +		break; +	} +} + +// Only does something if the chip should be announcing that the time zone is finished... +void PegasusChip::setUpPegasusChipRude() { +	switch (GameState.getCurrentNeighborhood()) { +	case kPrehistoricID: +		if (((PegasusEngine *)g_engine)->playerHasItemID(kHistoricalLog)) +			setItemState(kPegasusPrehistoric00); +		break; +	case kMarsID: +		if (GameState.getMarsFinished()) +			setItemState(kPegasusMars00); +		break; +	case kWSCID: +		if (GameState.getWSCFinished()) +			setItemState(kPegasusWSC00); +		break; +	case kNoradAlphaID: +	case kNoradDeltaID: +		if (GameState.getNoradFinished()) +			setItemState(kPegasusNorad00); +		break; +	} +} + +void PegasusChip::activatePegasusHotspots() { +	switch (GameState.getCurrentNeighborhood()) { +	case kPrehistoricID: +	case kMarsID: +	case kWSCID: +	case kNoradAlphaID: +	case kNoradDeltaID: +		_recallSpot.setActive(); +		break; +	} +} + +void PegasusChip::clickInPegasusHotspot() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	ItemState thisState = getItemState(); +	ItemState hiliteState; + +	switch (thisState) { +	case kPegasusPrehistoric00: +		hiliteState = kPegasusPrehistoric01; +		break; +	case kPegasusPrehistoric10: +		hiliteState = kPegasusPrehistoric11; +		break; +	case kPegasusMars00: +		hiliteState = kPegasusMars01; +		break; +	case kPegasusMars10: +		hiliteState = kPegasusMars11; +		break; +	case kPegasusNorad00: +		hiliteState = kPegasusNorad01; +		break; +	case kPegasusNorad10: +		hiliteState = kPegasusNorad11; +		break; +	case kPegasusWSC00: +		hiliteState = kPegasusWSC01; +		break; +	case kPegasusWSC10: +		hiliteState = kPegasusWSC11; +		break; +	} + +	// WORKAROUND: The original called setItemState() here. However, +	// since we're overriding select() to call setUpPegasusChip(), +	// the highlighted frame is never displayed! So, we're manually +	// setting the state and selecting the item. Also of note is that +	// setItemState() for this class is effectively useless since it +	// always gets overriden in the select() function. The only reason +	// that this doesn't end in infinite recursion is because setItemState() +	// has a check against the current state to make sure you don't call +	// select() again. </rant> +	_itemState = hiliteState; +	BiochipItem::select(); + +	uint32 time = g_system->getMillis(); +	while (g_system->getMillis() < time + 500) { +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	setItemState(thisState); + +	if (!((Neighborhood *)g_neighborhood)->okayToJump()) +		return; + +	if (g_energyMonitor) +		g_energyMonitor->stopEnergyDraining(); +	 +	if (GameState.getTSAState() == kPlayerWentToPrehistoric || GameState.allTimeZonesFinished()) +		vm->jumpToNewEnvironment(kFullTSAID, kTSA37, kNorth); +	else +		vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/pegasuschip.h b/engines/pegasus/items/biochips/pegasuschip.h new file mode 100644 index 0000000000..7597424821 --- /dev/null +++ b/engines/pegasus/items/biochips/pegasuschip.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_PEGASUSCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_PEGASUSCHIP_H + +#include "pegasus/hotspot.h" +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class PegasusChip : public BiochipItem { +public: +	PegasusChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~PegasusChip(); + +	void select(); + +	void setUpPegasusChip(); + +	// Called to set up the Pegasus chip when the Pegasus chip is the current chip but does not +	// own the center area. +	void setUpPegasusChipRude(); +	void activatePegasusHotspots(); +	void clickInPegasusHotspot(); + +protected: +	Hotspot _recallSpot; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/retscanchip.cpp b/engines/pegasus/items/biochips/retscanchip.cpp new file mode 100644 index 0000000000..84b74a63d2 --- /dev/null +++ b/engines/pegasus/items/biochips/retscanchip.cpp @@ -0,0 +1,49 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/retscanchip.h" + +namespace Pegasus { + +RetScanChip::RetScanChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +		BiochipItem(id, neighborhood, room, direction) { +} + +void RetScanChip::searchForLaser() { +	ItemExtraEntry entry; +	findItemExtra(kRetinalScanSearching, entry); + +	if (g_AIArea) +		g_AIArea->playAIAreaSequence(kBiochipSignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); + +	findItemExtra(kRetinalScanActivated, entry); +	if (g_AIArea) +		g_AIArea->playAIAreaSequence(kBiochipSignature, kRightAreaSignature, entry.extraStart, entry.extraStop); + +	setItemState(kRetinalSimulating); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/retscanchip.h b/engines/pegasus/items/biochips/retscanchip.h new file mode 100644 index 0000000000..e72eaeddaf --- /dev/null +++ b/engines/pegasus/items/biochips/retscanchip.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_RETSCANCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_RETSCANCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class RetScanChip : public BiochipItem { +public: +	RetScanChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~RetScanChip() {} +	 +	void searchForLaser(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/biochips/shieldchip.cpp b/engines/pegasus/items/biochips/shieldchip.cpp new file mode 100644 index 0000000000..58cbfcc4ec --- /dev/null +++ b/engines/pegasus/items/biochips/shieldchip.cpp @@ -0,0 +1,53 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +ShieldChip *g_shield = 0; + +ShieldChip::ShieldChip(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +		BiochipItem(id, neighborhood, room, direction) { +	g_shield = this; +} + +void ShieldChip::select() { +	BiochipItem::select(); +	GameState.setShieldOn(true); +	if (g_neighborhood) +		g_neighborhood->shieldOn(); +} + +void ShieldChip::deselect() { +	BiochipItem::deselect(); +	GameState.setShieldOn(false); +	if (g_neighborhood) +		g_neighborhood->shieldOff(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/shieldchip.h b/engines/pegasus/items/biochips/shieldchip.h new file mode 100644 index 0000000000..69c6369236 --- /dev/null +++ b/engines/pegasus/items/biochips/shieldchip.h @@ -0,0 +1,46 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_BIOCHIPS_SHIELDCHIP_H +#define PEGASUS_ITEMS_BIOCHIPS_SHIELDCHIP_H + +#include "pegasus/items/biochips/biochipitem.h" + +namespace Pegasus { + +class ShieldChip : public BiochipItem { +public: +	ShieldChip(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~ShieldChip() {} + +	void select(); +	void deselect(); +}; + +extern ShieldChip *g_shield; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory.cpp b/engines/pegasus/items/inventory.cpp new file mode 100644 index 0000000000..16cced3267 --- /dev/null +++ b/engines/pegasus/items/inventory.cpp @@ -0,0 +1,175 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/constants.h" +#include "pegasus/items/item.h" +#include "pegasus/items/inventory.h" + +namespace Pegasus { + +Inventory::Inventory() { +	_weightLimit = 100; +	_ownerID = kNoActorID; +	_referenceCount = 0; +} + +Inventory::~Inventory() { +} + +void Inventory::setWeightLimit(WeightType limit) { +	_weightLimit = limit; +	// *** What to do if the new weight limit is greater than the current weight? +} + +WeightType Inventory::getWeight() { +	WeightType	result = 0; + +	for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) +		result += (*it)->getItemWeight(); + +	return result; +} + +// If the item already belongs, just return kInventoryOK. +InventoryResult Inventory::addItem(Item *item) { +	if (itemInInventory(item)) +		return kInventoryOK; + +	if (getWeight() + item->getItemWeight() > _weightLimit) +		return kTooMuchWeight; + +	_inventoryList.push_back(item); +	item->setItemOwner(_ownerID); + +	++_referenceCount; +	return kInventoryOK; +} + +InventoryResult Inventory::removeItem(Item *item) { +	for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) { +		if (*it == item) { +			_inventoryList.erase(it); +			item->setItemOwner(kNoActorID); + +			++_referenceCount; +			return kInventoryOK; +		} +	} + +	return kItemNotInInventory; +} + +InventoryResult Inventory::removeItem(ItemID id) { +	Item *item = findItemByID(id); +	 +	if (item) { +		_inventoryList.remove(item); +		item->setItemOwner(kNoActorID); + +		++_referenceCount; +		return kInventoryOK; +	} + +	return kItemNotInInventory; +} + +void Inventory::removeAllItems() { +	_inventoryList.clear(); +	++_referenceCount; +} + +bool Inventory::itemInInventory(Item *item) { +	for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++) +		if (*it == item) +			return true; + +	return false; +} + +bool Inventory::itemInInventory(ItemID id) { +	return findItemByID(id) != NULL; +} + +Item *Inventory::getItemAt(int32 index) { +	int32 i = 0; +	for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) +		if (i == index) +			return *it; + +	return 0; +} + +ItemID Inventory::getItemIDAt(int32 index) { +	Item *item = getItemAt(index); + +	if (item) +		return item->getObjectID(); + +	return kNoItemID; +} + +Item *Inventory::findItemByID(ItemID id) { +	return _inventoryList.findItemByID(id); +} + +// Return -1 if not found. + +int32 Inventory::findIndexOf(Item *item) { +	uint32 i = 0; +	for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) +		if (*it == item) +			return i; + +	return -1; +} + +// Return -1 if not found. + +int32 Inventory::findIndexOf(ItemID id) { +	uint32 i = 0; +	for (ItemIterator it = _inventoryList.begin(); it != _inventoryList.end(); it++, i++) +		if ((*it)->getObjectID() == id) +			return i; + +	return -1; +} + +WeightType Inventory::getWeightLimit() { +	return _weightLimit; +} + +int32 Inventory::getNumItems() { +	return _inventoryList.size(); +} + +void Inventory::setOwnerID(const ActorID id) { +	_ownerID = id; +} + +ActorID Inventory::getOwnerID() const { +	return _ownerID; +} + +} // End of namespae Pegasus diff --git a/engines/pegasus/items/inventory.h b/engines/pegasus/items/inventory.h new file mode 100644 index 0000000000..099ba52ba7 --- /dev/null +++ b/engines/pegasus/items/inventory.h @@ -0,0 +1,80 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORY_H +#define PEGASUS_ITEMS_INVENTORY_H + +#include "pegasus/types.h" +#include "pegasus/items/itemlist.h" + +namespace Pegasus { + +class Item; + +// Inventories have a "current item". This item is the default item the player can +// use. In a text adventure system, the current item would be "it", as in +// "Hit the troll with it," where "it" would refer to some weapon which is the current +// item. In a graphic adventure, the current item would be the item the user selects +// to use with the mouse or other pointing device. + +class Inventory { +public: +	Inventory(); +	virtual ~Inventory(); +	 +	WeightType getWeightLimit(); +	void setWeightLimit(WeightType limit); +	WeightType getWeight(); +	 +	virtual InventoryResult addItem(Item *item); +	virtual InventoryResult removeItem(Item *item); +	virtual InventoryResult removeItem(ItemID id); +	virtual bool itemInInventory(Item *item); +	virtual bool itemInInventory(ItemID id); +	virtual Item *getItemAt(int32 index); +	virtual ItemID getItemIDAt(int32 index); +	virtual Item *findItemByID(ItemID id); +	virtual int32 findIndexOf(Item *item); +	virtual int32 findIndexOf(ItemID id); +	int32 getNumItems(); +	virtual void removeAllItems(); +	 +	void setOwnerID(const ActorID id); +	ActorID getOwnerID() const; +	 +	uint32 getReferenceCount() { return _referenceCount; } + +protected: +	WeightType _weightLimit; +	ActorID _ownerID; +	ItemList _inventoryList; + +private: +	uint32 _referenceCount; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/airmask.cpp b/engines/pegasus/items/inventory/airmask.cpp new file mode 100644 index 0000000000..56f9d6f62f --- /dev/null +++ b/engines/pegasus/items/inventory/airmask.cpp @@ -0,0 +1,248 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +AirMask *g_airMask = 0; + +//	Based on full == 100, which is scale used by GetAirLeft(). +static const TimeValue kOxygenLowThreshold = 25; + +void AirMask::airMaskTimerExpired(FunctionPtr *, void *) { +	if (g_neighborhood) +		g_neighborhood->checkAirMask(); +} + +AirMask::AirMask(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +		InventoryItem(id, neighborhood, room, direction), _toggleSpot(kAirMaskToggleSpotID) { +	g_airMask = this; +	_toggleSpot.setArea(Common::Rect(kAIMiddleAreaLeft + 10, kAIMiddleAreaTop + 17, kAIMiddleAreaLeft + 110, kAIMiddleAreaTop + 57)); +	_toggleSpot.setHotspotFlags(kAirMaskSpotFlag); +	g_allHotspots.push_back(&_toggleSpot); +	setItemState(kAirMaskEmptyOff); +	_oxygenTimer.primeFuse(0); +	_oxygenTimer.setFunctionPtr(&airMaskTimerExpired, 0); +} + +AirMask::~AirMask() { +	g_allHotspots.removeOneHotspot(kAirMaskToggleSpotID); +	g_airMask = 0; +} + +void AirMask::writeToStream(Common::WriteStream *stream) { +	InventoryItem::writeToStream(stream); +	stream->writeUint32BE(_oxygenTimer.getTimeRemaining()); +} + +void AirMask::readFromStream(Common::ReadStream *stream) {	 +	_oxygenTimer.stopFuse(); +	InventoryItem::readFromStream(stream); +	_oxygenTimer.primeFuse(stream->readUint32BE()); +} + +void AirMask::putMaskOn() { +	AirQuality airQuality; + +	if (g_neighborhood) +		airQuality = g_neighborhood->getAirQuality(GameState.getCurrentRoom()); +	else +		airQuality = kAirQualityGood; + +	uint airLevel = getAirLeft(); +	ItemState newState = getItemState(); +	ItemState oldState = newState; + +	if (airLevel == 0) { +		newState = kAirMaskEmptyFilter; +	} else if (airLevel <= kOxygenLowThreshold) { +		if (airQuality == kAirQualityVacuum) +			newState = kAirMaskLowOn; +		else +			newState = kAirMaskLowFilter; +	} else { +		if (airQuality == kAirQualityVacuum) +			newState = kAirMaskFullOn; +		else +			newState = kAirMaskFullFilter; +	} + +	if (newState != oldState) +		setItemState(newState); +} + +void AirMask::takeMaskOff() {	 +	uint airLevel = getAirLeft(); +	ItemState newState = getItemState(); +	ItemState oldState = newState; + +	if (airLevel == 0) +		newState = kAirMaskEmptyOff; +	else if (airLevel <= kOxygenLowThreshold) +		newState = kAirMaskLowOff; +	else +		newState = kAirMaskFullOff; + +	if (newState != oldState) +		setItemState(newState); +} + +void AirMask::toggleItemState() { +	if (isAirMaskInUse()) +		takeMaskOff(); +	else +		putMaskOn(); +} + +void AirMask::airQualityChanged() { +	if (isAirMaskInUse()) +		putMaskOn(); +	else +		takeMaskOff(); +} + +void AirMask::setItemState(const ItemState newState) { +	if (newState != getItemState()) { +		InventoryItem::setItemState(newState); + +		switch (newState) { +		case kAirMaskFullOn: +		case kAirMaskLowOn: +			if (!_oxygenTimer.isFuseLit()) { +				_oxygenTimer.lightFuse(); +				startIdling(); +			} +			break; +		default: +			if (_oxygenTimer.isFuseLit()) { +				_oxygenTimer.stopFuse(); +				stopIdling(); +			} +			break; +		} + +		if (g_neighborhood) +			g_neighborhood->checkAirMask(); + +		g_AIArea->checkMiddleArea(); +	} +} + +void AirMask::useIdleTime() { +	if (getAirLeft() == 0) +		setItemState(kAirMaskEmptyOff); +	else if (getAirLeft() <= kOxygenLowThreshold) +		setItemState(kAirMaskLowOn); +} + +void AirMask::refillAirMask() { +	switch (getItemState()) { +		case kAirMaskEmptyOff: +		case kAirMaskLowOff: +			setItemState(kAirMaskFullOff); +			break; +		case kAirMaskEmptyFilter: +		case kAirMaskLowFilter: +			setItemState(kAirMaskFullFilter); +			break; +		case kAirMaskLowOn: +			setItemState(kAirMaskFullOn); +			break; +	} + +	if (_oxygenTimer.isFuseLit()) { +		_oxygenTimer.stopFuse(); +		_oxygenTimer.primeFuse(kOxyMaskFullTime); +		_oxygenTimer.lightFuse(); +	} else { +		_oxygenTimer.primeFuse(kOxyMaskFullTime); +	} +} + +//	Doesn't return 0 until the timer is actually at 0. +uint AirMask::getAirLeft() { +	return CLIP<int>(((_oxygenTimer.getTimeRemaining() * 100) + kOxyMaskFullTime - 1) / kOxyMaskFullTime, 0, 100); +} + +bool AirMask::isAirMaskInUse() { +	switch (getItemState()) { +	case kAirMaskEmptyOff: +	case kAirMaskLowOff: +	case kAirMaskFullOff: +		return false; +		break; +	default: +		return true; +		break; +	} +} + +bool AirMask::isAirMaskOn() { +	switch (getItemState()) { +	case kAirMaskLowOn: +	case kAirMaskFullOn: +		return true; +		break; +	default: +		return false; +		break; +	} +} + +bool AirMask::isAirFilterOn() { +	switch (getItemState()) { +	case kAirMaskEmptyFilter: +	case kAirMaskLowFilter: +	case kAirMaskFullFilter: +		return true; +		break; +	default: +		return false; +		break; +	} +} + +void AirMask::addedToInventory() { +	GameState.setMarsMaskOnFiller(false); +} + +void AirMask::removedFromInventory() { +	if (isAirMaskInUse()) +		toggleItemState(); +} + +void AirMask::activateAirMaskHotspots() { +	_toggleSpot.setActive(); +} + +void AirMask::clickInAirMaskHotspot() { +	toggleItemState(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/airmask.h b/engines/pegasus/items/inventory/airmask.h new file mode 100644 index 0000000000..f207f3b82b --- /dev/null +++ b/engines/pegasus/items/inventory/airmask.h @@ -0,0 +1,76 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORY_AIRMASK_H +#define PEGASUS_ITEMS_INVENTORY_AIRMASK_H + +#include "pegasus/hotspot.h" +#include "pegasus/timers.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class AirMask : public InventoryItem, private Idler { +public: +	AirMask(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~AirMask(); + +	virtual void writeToStream(Common::WriteStream *); +	virtual void readFromStream(Common::ReadStream *); + +	virtual void setItemState(const ItemState); +	void putMaskOn(); +	void takeMaskOff(); +	void toggleItemState(); +	void airQualityChanged(); + +	bool isAirMaskInUse(); +	bool isAirMaskOn(); +	bool isAirFilterOn(); + +	void refillAirMask(); + +	// Returns a percentage +	uint getAirLeft(); + +	void activateAirMaskHotspots(); +	void clickInAirMaskHotspot(); + +protected: +	static void airMaskTimerExpired(FunctionPtr *, void *); + +	virtual void removedFromInventory(); +	virtual void addedToInventory(); +	void useIdleTime(); + +	Hotspot _toggleSpot; +	FuseFunction _oxygenTimer; +}; + +extern AirMask *g_airMask; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/gascanister.cpp b/engines/pegasus/items/inventory/gascanister.cpp new file mode 100644 index 0000000000..bf63cc6542 --- /dev/null +++ b/engines/pegasus/items/inventory/gascanister.cpp @@ -0,0 +1,46 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/gascanister.h" + +namespace Pegasus { + +GasCanister::GasCanister(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +		InventoryItem(id, neighborhood, room, direction) { +} + +void GasCanister::select() { +	InventoryItem::select(); +	takeSharedArea(); +} + +void GasCanister::takeSharedArea() { +	ItemExtraEntry entry; +	findItemExtra(kGasCanLoop, entry); +	g_AIArea->loopAIAreaSequence(kInventorySignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/gascanister.h b/engines/pegasus/items/inventory/gascanister.h new file mode 100644 index 0000000000..dae72c12cb --- /dev/null +++ b/engines/pegasus/items/inventory/gascanister.h @@ -0,0 +1,44 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORY_GASCANISTER_H +#define PEGASUS_ITEMS_INVENTORY_GASCANISTER_H + +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class GasCanister : public InventoryItem { +public: +	GasCanister(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~GasCanister() {} +	 +	void select(); +	void takeSharedArea(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/inventoryitem.cpp b/engines/pegasus/items/inventory/inventoryitem.cpp new file mode 100644 index 0000000000..4399708879 --- /dev/null +++ b/engines/pegasus/items/inventory/inventoryitem.cpp @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/stream.h" + +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +InventoryItem::InventoryItem(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +		Item(id, neighborhood, room, direction) { + +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	Common::SeekableReadStream *leftInfo = vm->_resFork->getResource(MKTAG('L', 'e', 'f', 't'), kItemBaseResID + id); +	if (leftInfo) { +		_leftAreaInfo = readItemState(leftInfo); +		delete leftInfo; +	} else { +		_leftAreaInfo.numEntries = 0; +		_leftAreaInfo.entries = 0; +	} + +	Common::SeekableReadStream *inventoryInfo = vm->_resFork->getResource(MKTAG('I', 'n', 'v', 'I'), kItemBaseResID + id); +	if (inventoryInfo) { +		_inventoryInfo.panelStart = inventoryInfo->readUint32BE(); +		_inventoryInfo.panelStop = inventoryInfo->readUint32BE(); +		delete inventoryInfo; +	} else { +		_inventoryInfo.panelStart = _inventoryInfo.panelStop = 0; +	} + +	_itemAnimationTime = 0; +} + +InventoryItem::~InventoryItem() { +	delete[] _leftAreaInfo.entries; +} + +ItemType InventoryItem::getItemType() { +	return kInventoryItemType; +} + +TimeValue InventoryItem::getLeftAreaTime() const { +	if (!_leftAreaInfo.entries) +		return 0xffffffff; + +	TimeValue time; +	ItemState state; + +	findItemStateEntryByState(_leftAreaInfo, _itemState, time); +	if (time == 0xffffffff) +		getItemStateEntry(_leftAreaInfo, 0, state, time); + +	return time; +} + +void InventoryItem::setAnimationTime(const TimeValue time) { +	_itemAnimationTime = time; +} + +TimeValue InventoryItem::getAnimationTime() const { +	return _itemAnimationTime; +} + +//	Must affect images in left area. +void InventoryItem::select() { +	Item::select(); + +	if (g_AIArea) +		g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, getLeftAreaTime()); +} + +void InventoryItem::deselect() { +	Item::deselect(); + +	if (g_AIArea) +		g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, 0xffffffff); +} + +void InventoryItem::getPanelTimes(TimeValue &start, TimeValue &stop) const { +	start = _inventoryInfo.panelStart; +	stop = _inventoryInfo.panelStop; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/inventoryitem.h b/engines/pegasus/items/inventory/inventoryitem.h new file mode 100644 index 0000000000..b526463b18 --- /dev/null +++ b/engines/pegasus/items/inventory/inventoryitem.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +  +#ifndef PEGASUS_ITEMS_INVENTORY_INVENTORYITEM_H +#define PEGASUS_ITEMS_INVENTORY_INVENTORYITEM_H + +#include "pegasus/items/item.h" + +namespace Pegasus { + +// JMPInventoryInfo contains the resource data used by InventoryItems. + +struct JMPInventoryInfo { +	TimeValue panelStart; +	TimeValue panelStop; +}; + +class InventoryItem : public Item { +public: +	InventoryItem(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~InventoryItem(); +	 +	virtual ItemType getItemType(); +	 +	void getPanelTimes(TimeValue &, TimeValue &) const; +	TimeValue getLeftAreaTime() const; +	 +	void setAnimationTime(const TimeValue); +	TimeValue getAnimationTime() const; +	 +	virtual void toggleItemState() {} +	 +	// Must affect images in left area. +	virtual void select(); +	virtual void deselect(); + +protected: +	JMPInventoryInfo _inventoryInfo; +	ItemStateInfo _leftAreaInfo; +	TimeValue _itemAnimationTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventory/keycard.cpp b/engines/pegasus/items/inventory/keycard.cpp new file mode 100644 index 0000000000..c818b6675b --- /dev/null +++ b/engines/pegasus/items/inventory/keycard.cpp @@ -0,0 +1,59 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/items/inventory/keycard.h" + +namespace Pegasus { + +KeyCard::KeyCard(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : +			InventoryItem(id, neighborhood, room, direction) { +	setItemState(kFlashlightOff); +} + +void KeyCard::toggleItemState() { +	if (getItemState() == kFlashlightOff) +		setItemState(kFlashlightOn); +	else +		setItemState(kFlashlightOff); +} + +void KeyCard::setItemState(const ItemState newState) { +	if (newState != getItemState()) { +		InventoryItem::setItemState(newState); +		((PegasusEngine *)g_engine)->checkFlashlight(); +	} +} + +bool KeyCard::isFlashlightOn() { +	return getItemState() == kFlashlightOn; +} + +void KeyCard::removedFromInventory() { +	if (isFlashlightOn()) +		setItemState(kFlashlightOff); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventory/keycard.h b/engines/pegasus/items/inventory/keycard.h new file mode 100644 index 0000000000..c30c789786 --- /dev/null +++ b/engines/pegasus/items/inventory/keycard.h @@ -0,0 +1,48 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORY_KEYCARD_H +#define PEGASUS_ITEMS_INVENTORY_KEYCARD_H + +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +class KeyCard : public InventoryItem { +public: +	KeyCard(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); +	virtual ~KeyCard() {} +	 +	virtual void toggleItemState(); +	virtual void setItemState(const ItemState); +	bool isFlashlightOn(); + +protected: +	virtual void removedFromInventory(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/inventorypicture.cpp b/engines/pegasus/items/inventorypicture.cpp new file mode 100644 index 0000000000..84999c0b22 --- /dev/null +++ b/engines/pegasus/items/inventorypicture.cpp @@ -0,0 +1,370 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/transition.h" +#include "pegasus/items/inventorypicture.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +InventoryPicture::InventoryPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : +		InputHandler(nextHandler), Picture(id), _panelMovie(kNoDisplayElement){ +	_inventory = inventory; +	_lastReferenceCount = 0xffffffff; + +	if (_inventory->getNumItems() > 0) { +		_currentItemIndex = 0; +		_currentItem = (Item *)_inventory->getItemAt(0); +	} else { +		_currentItemIndex = -1; +		_currentItem = 0; +	} + +	_active = false; +	_shouldDrawHighlight = true; +	_itemsPerRow = 1; +	_numberOfRows = 1; +	_itemWidth = 0; +	_itemHeight = 0; +	_itemX = 0; +	_itemY = 0; +} + +void InventoryPicture::initInventoryImage(Transition *transition) { +	initFromPICTFile(_pictName, true); +	_panelMovie.shareSurface(this); +	_panelMovie.initFromMovieFile(_movieName); +	_panelMovie.getBounds(_highlightBounds); +	_panelMovie.setTriggeredElement(transition); +	_highlightImage.initFromPICTFile(_highlightName, true); +} + +void InventoryPicture::throwAwayInventoryImage() { +	if (isSurfaceValid()) { +		_panelMovie.releaseMovie(); +		_highlightImage.deallocateSurface(); +		deallocateSurface(); +	} +} + +void InventoryPicture::getItemXY(uint32 index, CoordType &x, CoordType &y) { +	x = (index % _itemsPerRow) * _itemWidth + _itemX; +	y = (index / _itemsPerRow) * _itemHeight + _itemY; +} + +void InventoryPicture::drawItemHighlight(const Common::Rect &r) {	 +	if (_highlightImage.isSurfaceValid()) { +		Common::Rect r2 = _highlightBounds; +		Common::Rect bounds; +		getBounds(bounds); + +		r2.translate(bounds.left, bounds.top); +		r2 = r2.findIntersectingRect(r); +		if (!r2.isEmpty()) { +			Common::Rect r1 = r2; +			r1.translate(-bounds.left - _highlightBounds.left, -bounds.top - _highlightBounds.top); +			_highlightImage.drawImage(r1, r2); +		} +	} +} + +void InventoryPicture::draw(const Common::Rect &r) { +	Picture::draw(r); +	if (_inventory->getNumItems() != 0 && _shouldDrawHighlight) +		drawItemHighlight(r); +} + +// Assumes index >= 0. +void InventoryPicture::setCurrentItemIndex(int32 index) { +	if (index >= _inventory->getNumItems()) +		index = _inventory->getNumItems() - 1; + +	Item *currentItem = 0; +	if (index >= 0) +		currentItem = (Item *)_inventory->getItemAt(index); + +	if (currentItem != _currentItem) { +		if (_currentItem) { +			if (_currentItem->isSelected()) +				_currentItem->deselect(); + +			if (_active) +				unhighlightCurrentItem(); +		} + +		_currentItemIndex = index; +		_currentItem = currentItem; +		if (_currentItem) { +			_currentItem->select(); + +			if (_active) +				highlightCurrentItem(); +		} + +		if (_active) +			triggerRedraw(); +	} +} + +void InventoryPicture::setCurrentItemID(ItemID id) { +	int32 index = _inventory->findIndexOf(id); +	if (index >= 0) +		setCurrentItemIndex(index); +} + +InventoryResult InventoryPicture::addInventoryItem(Item *item) { +	InventoryResult result = _inventory->addItem(item); + +	if (result == kInventoryOK) +		setCurrentItemIndex(_inventory->findIndexOf(item)); + +	return result; +} + +InventoryResult InventoryPicture::removeInventoryItem(Item *item) {	 +	InventoryResult result = _inventory->removeItem(item); + +	if (result == kInventoryOK) +		setCurrentItemIndex(getCurrentItemIndex()); + +	return result; +} + +void InventoryPicture::removeAllItems() { +	_inventory->removeAllItems(); +	setCurrentItemIndex(0); +} + +bool InventoryPicture::itemInInventory(Item *item) { +	return _inventory->itemInInventory(item); +} + +bool InventoryPicture::itemInInventory(const ItemID id) { +	return _inventory->itemInInventory(id); +} + +void InventoryPicture::panelUp() { +	allowInput(true); +} + +// Must ensure that the picture matches the _inventory member variable. +void InventoryPicture::activateInventoryPicture() { +	if (_active) +		return; + +	allowInput(false); + +	if (_lastReferenceCount != _inventory->getReferenceCount()) { +		uint32 numItems = _inventory->getNumItems(); + +		CoordType x, y; +		getItemXY(0, x, y); +		_panelMovie.moveMovieBoxTo(x, y); +		_panelMovie.show(); + +		for (uint32 i = 0; i < numItems; i++) { +			Item *item = (Item *)_inventory->getItemAt(i); +			if (item == _currentItem) +				item->select(); + +			getItemXY(i, x, y); +			_panelMovie.moveMovieBoxTo(x, y); +			_panelMovie.setTime(getItemPanelTime(item)); +			_panelMovie.redrawMovieWorld(); +		} + +		uint32 numSlots = _itemsPerRow * _numberOfRows; + +		for (uint32 i = numItems; i < numSlots; i++) { +			getItemXY(i, x, y); +			_panelMovie.moveMovieBoxTo(x, y); +			_panelMovie.setTime(0); +			_panelMovie.redrawMovieWorld(); +		} + +		_lastReferenceCount = _inventory->getReferenceCount(); +	} + +	show(); // *** Do we really need this? +	if (_currentItem) +		highlightCurrentItem(); + +	_active = true; +} + +void InventoryPicture::deactivateInventoryPicture() { +	if (!_active) +		return; + +	_active = false; +	allowInput(false); +	_panelMovie.hide(); +	hide(); + +	if (_inventory->getNumItems() != 0) +		if (!_currentItem->isActive()) +			_currentItem->activate(); +} + +void InventoryPicture::handleInput(const Input &input, const Hotspot *cursorSpot) { +	if (_active) { +		if (input.upButtonDown()) { +			if (_currentItemIndex - _itemsPerRow >= 0) +				setCurrentItemIndex(_currentItemIndex - _itemsPerRow); +		} else if (input.downButtonDown()) { +			if (_currentItemIndex + _itemsPerRow < _inventory->getNumItems()) +				setCurrentItemIndex(_currentItemIndex + _itemsPerRow); +		} else if (input.leftButtonDown()) { +			if ((_currentItemIndex % _itemsPerRow) != 0) +				setCurrentItemIndex(_currentItemIndex - 1); +		} else if (input.rightButtonDown()) { +			if (((_currentItemIndex + 1) % _itemsPerRow) != 0 && _currentItemIndex + 1 < _inventory->getNumItems()) +				setCurrentItemIndex(_currentItemIndex + 1); +		} +	} + +	InputHandler::handleInput(input, cursorSpot); +} + +void InventoryPicture::highlightCurrentItem() { +	CoordType x, y; +	getItemXY(_currentItemIndex, x, y); +	_highlightBounds.moveTo(x, y); +} + +InventoryItemsPicture::InventoryItemsPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : +		InventoryPicture(id, nextHandler, inventory) { +	_pictName = "Images/Items/Inventory/Inventory Panel"; +	_movieName = "Images/Items/Inventory/Inventory Panel Movie"; +	_highlightName = "Images/Items/Inventory/Inventory Hilite"; + +	_itemsPerRow = 3; +	_numberOfRows = 3; +	_itemWidth = 88; +	_itemHeight = 64; +	_itemX = 8; +	_itemY = 26; +	_isLooping = true; +} + +void InventoryItemsPicture::loopCurrentItem() { +	if (_isLooping) { +		CoordType x, y; +		getItemXY(_currentItemIndex, x, y); +		_panelMovie.moveMovieBoxTo(x, y); +		_highlightBounds.moveTo(x, y); + +		TimeValue start, stop; +		((InventoryItem *)_currentItem)->getPanelTimes(start, stop); +		_panelMovie.stop(); +		_panelMovie.setFlags(0); +		_panelMovie.setSegment(start, stop); +		_panelMovie.setFlags(kLoopTimeBase); +		_panelMovie.setTime(((InventoryItem *)_currentItem)->getAnimationTime()); +		_panelMovie.start(); +	} +} + +void InventoryItemsPicture::highlightCurrentItem() { +	InventoryPicture::highlightCurrentItem(); +	loopCurrentItem(); +} + +void InventoryItemsPicture::unhighlightCurrentItem() { +	InventoryPicture::unhighlightCurrentItem(); +	_panelMovie.stop(); +	_panelMovie.setFlags(0); +	((InventoryItem *)_currentItem)->setAnimationTime(_panelMovie.getTime()); +} + +TimeValue InventoryItemsPicture::getItemPanelTime(Item *item) { +	TimeValue start, stop; +	((InventoryItem *)item)->getPanelTimes(start, stop); +	((InventoryItem *)item)->setAnimationTime(start); +	return start; +} + +void InventoryItemsPicture::deactivateInventoryPicture() { +	if (_active) { +		InventoryPicture::deactivateInventoryPicture(); +		_panelMovie.stop(); +		_panelMovie.setFlags(0); +		_panelMovie.setSegment(0, _panelMovie.getDuration()); +		_isLooping = true; +	} +} + +void InventoryItemsPicture::playEndMessage(DisplayElement *pushElement) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	Movie endMessage(0); + +	_shouldDrawHighlight = false; +	endMessage.shareSurface(this); +	endMessage.initFromMovieFile("Images/Caldoria/A56 Congrats"); +	endMessage.moveMovieBoxTo(kFinalMessageLeft - kInventoryPushLeft, kFinalMessageTop - kInventoryPushTop); +	endMessage.setTriggeredElement(pushElement); +	endMessage.start(); + +	while (endMessage.isRunning()) { +		vm->checkCallBacks(); +		vm->refreshDisplay(); +		g_system->delayMillis(10); +	} + +	endMessage.stop(); +} + +BiochipPicture::BiochipPicture(const DisplayElementID id, InputHandler *nextHandler, Inventory *inventory) : +		InventoryPicture(id, nextHandler, inventory) { +	_pictName = "Images/Items/Biochips/Biochip Panel"; +	_movieName = "Images/Items/Biochips/Biochip Panel Movie"; +	_highlightName = "Images/Items/Biochips/BioChip Hilite"; + +	_itemsPerRow = 4; +	_numberOfRows = 2; +	_itemWidth = 46; +	_itemHeight = 46; +	_itemX = 4; +	_itemY = 24; +} + +void BiochipPicture::unhighlightCurrentItem() { +	InventoryPicture::unhighlightCurrentItem(); +	CoordType x, y; +	getItemXY(_currentItemIndex, x, y); +	_panelMovie.show(); +	_panelMovie.moveMovieBoxTo(x, y); +	_panelMovie.setTime(getItemPanelTime(_currentItem)); +	_panelMovie.redrawMovieWorld(); +} + +TimeValue BiochipPicture::getItemPanelTime(Item *item) { +	return ((BiochipItem *)item)->getPanelTime(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/inventorypicture.h b/engines/pegasus/items/inventorypicture.h new file mode 100644 index 0000000000..18e85ef988 --- /dev/null +++ b/engines/pegasus/items/inventorypicture.h @@ -0,0 +1,125 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_INVENTORYPICTURE_H +#define PEGASUS_ITEMS_INVENTORYPICTURE_H + +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +class Inventory; +class Item; +class Input; +class Transition; + +class InventoryPicture : public InputHandler, public Picture { +public: +	InventoryPicture(const DisplayElementID, InputHandler *, Inventory *); +	virtual ~InventoryPicture() {} +	 +	void initInventoryImage(Transition *); +	void throwAwayInventoryImage(); +	 +	void panelUp(); +	void activateInventoryPicture(); +	void deactivateInventoryPicture(); +	void handleInput(const Input &, const Hotspot *); +	bool wantsCursor() { return false; } +	 +	InventoryResult addInventoryItem(Item *); +	InventoryResult removeInventoryItem(Item *); +	void removeAllItems(); +	Item *getCurrentItem() { return _currentItem; } +	void setCurrentItemIndex(int32); +	void setCurrentItemID(ItemID); +	int32 getCurrentItemIndex() { return _currentItemIndex; } +	bool itemInInventory(Item *); +	bool itemInInventory(const ItemID); + +protected: +	void getItemXY(uint32, CoordType &, CoordType &); +	void draw(const Common::Rect &); +	void drawItemHighlight(const Common::Rect &); +	virtual void highlightCurrentItem(); +	virtual void unhighlightCurrentItem() {} +	virtual TimeValue getItemPanelTime(Item *) = 0; +	 +	Inventory *_inventory; +	uint32 _lastReferenceCount; +	Frame _highlightImage; +	Movie _panelMovie; +	int32 _currentItemIndex; +	Item *_currentItem; +	Common::Rect _highlightBounds; +	bool _active, _shouldDrawHighlight; + +	Common::String _pictName; +	Common::String _movieName; +	Common::String _highlightName; +	uint16 _itemsPerRow; +	uint16 _numberOfRows; +	uint16 _itemWidth; +	uint16 _itemHeight; +	uint16 _itemX; +	uint16 _itemY; +}; + +class InventoryItemsPicture : public InventoryPicture { +public: +	InventoryItemsPicture(const DisplayElementID, InputHandler *, Inventory *); +	virtual ~InventoryItemsPicture() {} + +	void deactivateInventoryPicture(); + +	void disableLooping() { _isLooping = false; } + +	void playEndMessage(DisplayElement *); + +protected: +	virtual void highlightCurrentItem(); +	virtual void unhighlightCurrentItem(); +	virtual TimeValue getItemPanelTime(Item *); +	void loopCurrentItem(); + +	InputHandler *_previousHandler; +	bool _isLooping; +}; + +class BiochipPicture : public InventoryPicture { +public: +	BiochipPicture(const DisplayElementID, InputHandler *, Inventory *); +	virtual ~BiochipPicture() {} +	 +protected: +	virtual void unhighlightCurrentItem(); +	virtual TimeValue getItemPanelTime(Item *); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/item.cpp b/engines/pegasus/items/item.cpp new file mode 100644 index 0000000000..a49bb4a466 --- /dev/null +++ b/engines/pegasus/items/item.cpp @@ -0,0 +1,314 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/error.h" +#include "common/stream.h" + +#include "pegasus/constants.h" +#include "pegasus/elements.h" +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/item.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/inventory/inventoryitem.h" + +namespace Pegasus { + +Item::Item(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) : IDObject(id) { +	_itemNeighborhood = neighborhood; +	_itemRoom = room; +	_itemDirection = direction; +	_itemWeight = 1; +	_itemOwnerID = kNoActorID; +	_itemState = 0; + +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	Common::SeekableReadStream *info = vm->_resFork->getResource(kItemInfoResType, kItemBaseResID + id); +	if (info) { +		_itemInfo.infoLeftTime = info->readUint32BE(); +		_itemInfo.infoRightStart = info->readUint32BE(); +		_itemInfo.infoRightStop = info->readUint32BE(); +		_itemInfo.dragSpriteNormalID = info->readUint16BE(); +		_itemInfo.dragSpriteUsedID = info->readUint16BE(); + +		if (vm->isDemo()) { +			// Adjust info right times to account for the stuff that was chopped out of the +			// info right movies. +			// Assumes time scale of 600. + +			// Gap times in seconds +			static const TimeValue kGap1 = 24; +			static const TimeValue kGap2 = 34; +			static const TimeValue kGap3 = 4; +			static const TimeValue kGap4 = 4; + +			static const TimeValue kGapForGroup1 = kGap1; +			static const TimeValue kGapForGroup2 = kGapForGroup1 + kGap2; +			static const TimeValue kGapForGroup3 = kGapForGroup2 + kGap3; +			static const TimeValue kGapForGroup4 = kGapForGroup3 + kGap4; + +			switch (id) { +			case kHistoricalLog: +			case kJourneymanKey: +			case kKeyCard: +				_itemInfo.infoRightStart -= 600 * kGapForGroup1; +				_itemInfo.infoRightStop -= 600 * kGapForGroup1; +				break; +			case kAIBiochip: +				_itemInfo.infoRightStart -= 600 * kGapForGroup2; +				_itemInfo.infoRightStop -= 600 * kGapForGroup2; +				break; +			case kMapBiochip: +				_itemInfo.infoRightStart -= 600 * kGapForGroup3; +				_itemInfo.infoRightStop -= 600 * kGapForGroup3; +				break; +			case kPegasusBiochip: +				_itemInfo.infoRightStart -= 600 * kGapForGroup4; +				_itemInfo.infoRightStop -= 600 * kGapForGroup4; +				break; +			} +		} + +		delete info; +	} else { +		memset(&_itemInfo, 0, sizeof(_itemInfo)); +	} + +	Common::SeekableReadStream *middleAreaInfo = vm->_resFork->getResource(kMiddleAreaInfoResType, kItemBaseResID + id); +	if (middleAreaInfo) { +		_sharedAreaInfo = readItemState(middleAreaInfo); +		delete middleAreaInfo; +	} else { +		// Only kArgonPickup does not have this +		memset(&_sharedAreaInfo, 0, sizeof(_sharedAreaInfo)); +	} + +	Common::SeekableReadStream *extraInfo = vm->_resFork->getResource(kItemExtraInfoResType, kItemBaseResID + id); +	if (!extraInfo) +		error("Extra info not found for item %d", id); + +	_itemExtras.numEntries = extraInfo->readUint16BE(); +	_itemExtras.entries = new ItemExtraEntry[_itemExtras.numEntries]; +	for (uint16 i = 0; i < _itemExtras.numEntries; i++) { +		_itemExtras.entries[i].extraID = extraInfo->readUint32BE(); +		_itemExtras.entries[i].extraArea = extraInfo->readUint16BE(); +		_itemExtras.entries[i].extraStart = extraInfo->readUint32BE(); +		_itemExtras.entries[i].extraStop = extraInfo->readUint32BE(); +	} + +	delete extraInfo; + +	g_allItems.push_back(this); +} + +Item::~Item() { +	delete[] _sharedAreaInfo.entries; +	delete[] _itemExtras.entries; +} + +void Item::writeToStream(Common::WriteStream *stream) { +	stream->writeUint16BE(_itemNeighborhood); +	stream->writeUint16BE(_itemRoom); +	stream->writeByte(_itemDirection); +	stream->writeUint16BE(_itemOwnerID); +	stream->writeUint16BE(_itemState); +} + +void Item::readFromStream(Common::ReadStream *stream) { +	_itemNeighborhood = stream->readUint16BE(); +	_itemRoom = stream->readUint16BE(); +	_itemDirection = stream->readByte(); +	_itemOwnerID = stream->readUint16BE(); +	_itemState = stream->readUint16BE(); +} + +ActorID Item::getItemOwner() const { +	return _itemOwnerID; +} + +void Item::setItemOwner(const ActorID owner) { +	_itemOwnerID = owner; + +	if (owner == kNoActorID) { +		if (isSelected()) +			deselect(); +		removedFromInventory(); +	} else { +		addedToInventory(); +	} +} + +WeightType Item::getItemWeight() { +	return _itemWeight; +} + +ItemState Item::getItemState() const { +	return _itemState; +} + +void Item::setItemState(const ItemState state) { +	if (state != _itemState) { +		_itemState = state; + +		if (getItemType() == kInventoryItemType && ((PegasusEngine *)g_engine)->getCurrentInventoryItem() == (InventoryItem *)this) +			select(); +		else if (getItemType() == kBiochipItemType && ((PegasusEngine *)g_engine)->getCurrentBiochip() == (BiochipItem *)this) +			select(); +	} +} + +void Item::getItemRoom(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) const { +	neighborhood = _itemNeighborhood; +	room = _itemRoom; +	direction = _itemDirection; +} + +void Item::setItemRoom(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { +	_itemNeighborhood = neighborhood; +	_itemRoom = room; +	_itemDirection = direction; + +	if (neighborhood == kNoNeighborhoodID) +		pickedUp(); +	else +		dropped(); +} + +NeighborhoodID Item::getItemNeighborhood() const { +	return _itemNeighborhood; +} + +TimeValue Item::getSharedAreaTime() const { +	if (!_sharedAreaInfo.entries) +		return 0xffffffff; + +	TimeValue time; +	ItemState state; + +	findItemStateEntryByState(_sharedAreaInfo, _itemState, time); +	if (time == 0xffffffff) +		getItemStateEntry(_sharedAreaInfo, 0, state, time); + +	return time; +} + +// Must affect images in shared area. +void Item::select() { +	_isSelected = true; + +	if (g_AIArea) { +		if (getItemType() == kInventoryItemType) +			g_AIArea->setAIAreaToTime(kInventorySignature, kMiddleAreaSignature, getSharedAreaTime()); +		else +			g_AIArea->setAIAreaToTime(kBiochipSignature, kMiddleAreaSignature, getSharedAreaTime()); +	} +} + +void Item::deselect() { +	_isActive = false; +	_isSelected = false; + +	if (g_AIArea) { +		if (getItemType() == kInventoryItemType) +			g_AIArea->setAIAreaToTime(kInventorySignature, kMiddleAreaSignature, 0xffffffff); +		else +			g_AIArea->setAIAreaToTime(kBiochipSignature, kMiddleAreaSignature, 0xffffffff); +	} +} + +void Item::getItemStateEntry(ItemStateInfo info, uint32 index, ItemState &state, TimeValue &time) {	 +	if (index < info.numEntries) { +		state = info.entries[index].itemState; +		time = info.entries[index].itemTime; +	} else { +		state = kNoItemState; +		time = 0xffffffff; +	} +} + +void Item::findItemStateEntryByState(ItemStateInfo info, ItemState state, TimeValue &time) { +	for (uint16 i = 0; i < info.numEntries; i++) { +		if (info.entries[i].itemState == state) { +			time = info.entries[i].itemTime; +			return; +		} +	} + +	time = 0xffffffff; +} + +ItemStateInfo Item::readItemState(Common::SeekableReadStream *stream) { +	ItemStateInfo info; + +	info.numEntries = stream->readUint16BE(); +	info.entries = new ItemStateEntry[info.numEntries]; +	for (uint16 i = 0; i < info.numEntries; i++) { +		info.entries[i].itemState = stream->readSint16BE(); +		info.entries[i].itemTime = stream->readUint32BE(); +	} + +	return info; +} + +Sprite *Item::getDragSprite(const DisplayElementID id) const { +	PegasusEngine *vm = (PegasusEngine *)g_engine; +	Sprite *result = new Sprite(id); +	SpriteFrame *frame = new SpriteFrame(); + +	frame->initFromPICTResource(vm->_resFork, _itemInfo.dragSpriteNormalID, true); +	result->addFrame(frame, 0, 0); + +	if (_itemInfo.dragSpriteNormalID != _itemInfo.dragSpriteUsedID) { +		frame = new SpriteFrame(); +		frame->initFromPICTResource(vm->_resFork, _itemInfo.dragSpriteUsedID, true); +	} + +	result->addFrame(frame, 0, 0); +	result->setCurrentFrameIndex(0); +	return result; +} + +TimeValue Item::getInfoLeftTime() const { +	return _itemInfo.infoLeftTime; +} + +void Item::getInfoRightTimes(TimeValue &start, TimeValue &stop) const { +	start = _itemInfo.infoRightStart; +	stop = _itemInfo.infoRightStop; +} + +void Item::findItemExtra(const uint32 extraID, ItemExtraEntry &entry) { +	for (uint32 i = 0; i < _itemExtras.numEntries; i++) { +		if (_itemExtras.entries[i].extraID == extraID) { +			entry = _itemExtras.entries[i]; +			return; +		} +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/item.h b/engines/pegasus/items/item.h new file mode 100644 index 0000000000..efcf24100c --- /dev/null +++ b/engines/pegasus/items/item.h @@ -0,0 +1,363 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_ITEM_H +#define PEGASUS_ITEMS_ITEM_H + +#include "common/endian.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" + +namespace Common { +	class Error; +	class ReadStream; +	class WriteStream; +	class SeekableReadStream; +} + +namespace Pegasus { + +// JMPItemInfo contains resource data used by all Items. + +struct JMPItemInfo { +	TimeValue infoLeftTime; +	TimeValue infoRightStart; +	TimeValue infoRightStop; +	uint32 dragSpriteNormalID; +	uint32 dragSpriteUsedID; +}; + +// ItemStateEntry contains a single state/TimeValue pair. The TimeValue is +// the time value to set the shared area movie that corresponds with the given +// state of an inventory item. + +struct ItemStateEntry { +	ItemState itemState; +	TimeValue itemTime; +}; + +struct ItemStateInfo { +	uint16 numEntries; // For easy ResEdit access +	ItemStateEntry *entries; +}; + +// ItemExtraEntry + +static const short kLeftAreaExtra = 0; +static const short kMiddleAreaExtra = 1; +static const short kRightAreaExtra = 2; + +struct ItemExtraEntry { +	uint32 extraID; +	uint16 extraArea; +	TimeValue extraStart; +	TimeValue extraStop; +}; + +struct ItemExtraInfo { +	uint16 numEntries; // For easy ResEdit access +	ItemExtraEntry *entries; +}; + +// Inventory info resource type and ID: +// Individual inventory items are stored in these resource types. +// Resource ID is item ID + kItemBaseResID. + +static const uint32 kItemInfoResType = MKTAG('I', 't', 'e', 'm');       // JMPItemInfoHandle +static const uint32 kLeftAreaInfoResType = MKTAG('L', 'e', 'f', 't');   // ItemStateInfoHandle +static const uint32 kMiddleAreaInfoResType = MKTAG('M', 'i', 'd', 'l'); // ItemStateInfoHandle +static const uint32 kRightAreaInfoResType = MKTAG('R', 'g', 'h', 't');  // ItemStateInfoHandle +static const uint32 kItemExtraInfoResType = MKTAG('I', 'X', 't', 'r');	 // ItemExtraInfoHandle + +static const uint16 kItemBaseResID = 128; + +// Item IDs. + +static const ItemID kAirMask = 7; +static const ItemID kAntidote = 8; +static const ItemID kArgonCanister = 9; +static const ItemID kCardBomb = 10; +static const ItemID kCrowbar = 11; +static const ItemID kGasCanister = 12; +static const ItemID kHistoricalLog = 13; +static const ItemID kJourneymanKey = 14; +static const ItemID kKeyCard = 15; +static const ItemID kMachineGun = 16; +static const ItemID kMarsCard = 17; +static const ItemID kNitrogenCanister = 18; +static const ItemID kOrangeJuiceGlassFull = 19; +static const ItemID kOrangeJuiceGlassEmpty = 20; +static const ItemID kPoisonDart = 21; +static const ItemID kSinclairKey = 22; +static const ItemID kStunGun = 23; +static const ItemID kArgonPickup = 24; + +// Biochips. + +static const ItemID kAIBiochip = 0; +static const ItemID kInterfaceBiochip = 1; +static const ItemID kMapBiochip = 2; +static const ItemID kOpticalBiochip = 3; +static const ItemID kPegasusBiochip = 4; +static const ItemID kRetinalScanBiochip = 5; +static const ItemID kShieldBiochip = 6; + +static const ItemID kNumItems = 25; + +// Item States. + +static const ItemState kAI000 = 0; +static const ItemState kAI005 = 1; +static const ItemState kAI006 = 2; +static const ItemState kAI010 = 3; +static const ItemState kAI015 = 4; +static const ItemState kAI016 = 5; +static const ItemState kAI020 = 6; +static const ItemState kAI024 = 7; +static const ItemState kAI100 = 8; +static const ItemState kAI101 = 9; +static const ItemState kAI105 = 10; +static const ItemState kAI106 = 11; +static const ItemState kAI110 = 12; +static const ItemState kAI111 = 13; +static const ItemState kAI115 = 14; +static const ItemState kAI116 = 15; +static const ItemState kAI120 = 16; +static const ItemState kAI121 = 17; +static const ItemState kAI124 = 18; +static const ItemState kAI125 = 19; +static const ItemState kAI126 = 20; +static const ItemState kAI200 = 21; +static const ItemState kAI201 = 22; +static const ItemState kAI202 = 23; +static const ItemState kAI205 = 24; +static const ItemState kAI206 = 25; +static const ItemState kAI210 = 26; +static const ItemState kAI211 = 27; +static const ItemState kAI212 = 28; +static const ItemState kAI215 = 29; +static const ItemState kAI216 = 30; +static const ItemState kAI220 = 31; +static const ItemState kAI221 = 32; +static const ItemState kAI222 = 33; +static const ItemState kAI224 = 34; +static const ItemState kAI225 = 35; +static const ItemState kAI226 = 36; +static const ItemState kAI300 = 37; +static const ItemState kAI301 = 38; +static const ItemState kAI302 = 39; +static const ItemState kAI303 = 40; +static const ItemState kAI305 = 41; +static const ItemState kAI306 = 42; +static const ItemState kAI310 = 43; +static const ItemState kAI311 = 44; +static const ItemState kAI312 = 45; +static const ItemState kAI313 = 46; +static const ItemState kAI315 = 47; +static const ItemState kAI316 = 48; +static const ItemState kAI320 = 49; +static const ItemState kAI321 = 50; +static const ItemState kAI322 = 51; +static const ItemState kAI323 = 52; +static const ItemState kAI324 = 53; +static const ItemState kAI325 = 54; +static const ItemState kAI326 = 55; +static const ItemState kNormalItem = 56; +static const ItemState kMapUnavailable = 57; +static const ItemState kMapEngaged = 58; +static const ItemState kOptical000 = 59; +static const ItemState kOptical001 = 60; +static const ItemState kOptical002 = 61; +static const ItemState kOptical010 = 62; +static const ItemState kOptical011 = 63; +static const ItemState kOptical012 = 64; +static const ItemState kOptical020 = 65; +static const ItemState kOptical021 = 66; +static const ItemState kOptical100 = 67; +static const ItemState kOptical101 = 68; +static const ItemState kOptical102 = 69; +static const ItemState kOptical110 = 70; +static const ItemState kOptical111 = 71; +static const ItemState kOptical112 = 72; +static const ItemState kOptical120 = 73; +static const ItemState kOptical121 = 74; +static const ItemState kOptical200 = 75; +static const ItemState kOptical201 = 76; +static const ItemState kOptical210 = 77; +static const ItemState kOptical211 = 78; +static const ItemState kPegasusTSA00 = 79; +static const ItemState kPegasusTSA10 = 80; +static const ItemState kPegasusPrehistoric00 = 81; +static const ItemState kPegasusPrehistoric01 = 82; +static const ItemState kPegasusPrehistoric10 = 83; +static const ItemState kPegasusPrehistoric11 = 84; +static const ItemState kPegasusMars00 = 85; +static const ItemState kPegasusMars01 = 86; +static const ItemState kPegasusMars10 = 87; +static const ItemState kPegasusMars11 = 88; +static const ItemState kPegasusNorad00 = 89; +static const ItemState kPegasusNorad01 = 90; +static const ItemState kPegasusNorad10 = 91; +static const ItemState kPegasusNorad11 = 92; +static const ItemState kPegasusWSC00 = 93; +static const ItemState kPegasusWSC01 = 94; +static const ItemState kPegasusWSC10 = 95; +static const ItemState kPegasusWSC11 = 96; +static const ItemState kPegasusCaldoria = 97; +static const ItemState kRetinalSimulating = 98; +static const ItemState kShieldNormal = 99; +static const ItemState kShieldRadiation = 100; +static const ItemState kShieldPlasma = 101; +static const ItemState kShieldCardBomb = 102; +static const ItemState kShieldDraining = 103; +static const ItemState kAirMaskEmptyOff = 104; +static const ItemState kAirMaskEmptyFilter = 105; +static const ItemState kAirMaskLowOff = 106; +static const ItemState kAirMaskLowFilter = 107; +static const ItemState kAirMaskLowOn = 108; +static const ItemState kAirMaskFullOff = 109; +static const ItemState kAirMaskFullFilter = 110; +static const ItemState kAirMaskFullOn = 111; +static const ItemState kArgonEmpty = 112; +static const ItemState kArgonFull = 113; +static const ItemState kFlashlightOff = 114; +static const ItemState kFlashlightOn = 115; +static const ItemState kNitrogenEmpty = 116; +static const ItemState kNitrogenFull = 117; +static const ItemState kFullGlass = 118; + +// Extra IDs. + +static const uint32 kRetinalScanSearching = 0; +static const uint32 kRetinalScanActivated = 1; +static const uint32 kShieldIntro = 2; +static const uint32 kRemoveAirMask = 3; +static const uint32 kRemoveArgon = 4; +static const uint32 kRemoveCrowbar = 5; +static const uint32 kGasCanLoop = 6; +static const uint32 kRemoveJourneymanKey = 7; +static const uint32 kRemoveMarsCard = 8; +static const uint32 kRemoveNitrogen = 9; +static const uint32 kRemoveGlass = 10; +static const uint32 kRemoveDart = 11; +static const uint32 kRemoveSinclairKey = 12; + +enum ItemType { +	kInventoryItemType, +	kBiochipItemType +}; + +class Sprite; + +/* + +	Item is an object which can be picked up and carried around. +	Items have +		a location +		an ID. +		weight +		an owner (kNoActorID if no one is carrying the Item) + +*/ + +class Item : public IDObject { +public: +	Item(const ItemID id, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); +	virtual ~Item(); +	 +	// WriteToStream writes everything EXCEPT the item's ID. +	// It is assumed that the calling function will write and read the ID. +	virtual void writeToStream(Common::WriteStream *stream); +	virtual void readFromStream(Common::ReadStream *stream); +	 +	virtual ActorID getItemOwner() const; +	virtual void setItemOwner(const ActorID owner); +	 +	void getItemRoom(NeighborhoodID &neighborhood, RoomID &room, DirectionConstant &direction) const; +	void setItemRoom(const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction); +	NeighborhoodID getItemNeighborhood() const; +	 +	virtual WeightType getItemWeight(); +	 +	virtual void setItemState(const ItemState state); +	virtual ItemState getItemState() const; + +	virtual ItemType getItemType() = 0; +	 +	TimeValue getInfoLeftTime() const; +	void getInfoRightTimes(TimeValue &, TimeValue &) const; +	TimeValue getSharedAreaTime() const; + +	Sprite *getDragSprite(const DisplayElementID) const; +	 +	/* +		select		--	called when this item becomes current. Also called when the inventory +						panel holding this item is raised and this is the current item. +		deselect	--	called when this item is no longer current. +		activate	--	called on the current item when the panel is closed. +	*/ +	// In an override of these three member functions, you must call the inherited +	// member functions. +	virtual void select(); +	virtual void deselect(); +	virtual bool isSelected() { return _isSelected; } +	 +	virtual void activate() { _isActive = true; } +	virtual bool isActive() { return _isActive; } +	virtual void pickedUp() {} +	virtual void addedToInventory() {} +	virtual void removedFromInventory() {} +	virtual void dropped() {} + +	// Called when the shared area is taken by another item, but this item is still +	// selected. +	virtual void giveUpSharedArea() {} +	virtual void takeSharedArea() {} +	 +	void findItemExtra(const uint32 extraID, ItemExtraEntry &entry); + +protected: +	NeighborhoodID _itemNeighborhood; +	RoomID	_itemRoom; +	DirectionConstant _itemDirection; +	ActorID _itemOwnerID; +	WeightType _itemWeight; +	ItemState _itemState; + +	JMPItemInfo _itemInfo; +	ItemStateInfo _sharedAreaInfo; +	ItemExtraInfo _itemExtras; +	bool _isActive; +	bool _isSelected; + +	static void getItemStateEntry(ItemStateInfo, uint32, ItemState &, TimeValue &); +	static void findItemStateEntryByState(ItemStateInfo, ItemState, TimeValue &); +	static ItemStateInfo readItemState(Common::SeekableReadStream *stream); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/itemdragger.cpp b/engines/pegasus/items/itemdragger.cpp new file mode 100644 index 0000000000..9e77ad5745 --- /dev/null +++ b/engines/pegasus/items/itemdragger.cpp @@ -0,0 +1,193 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/elements.h" +#include "pegasus/hotspot.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/itemdragger.h" + +namespace Pegasus { + +SpriteDragger::SpriteDragger() { +	_draggingSprite = 0; +	_limitRect = Common::Rect(-30000, -30000, 30000, 30000); +	_slopRect = Common::Rect(-30000, -30000, 30000, 30000); +	_dragOffset.x = 0; +	_dragOffset.y = 0; +	_lastHotspot = 0; +} + +void SpriteDragger::setDragSprite(Sprite *newSprite) { +	if (!isTracking()) +		_draggingSprite = newSprite; +} + +void SpriteDragger::setDragConstraints(const Common::Rect &limitRect, const Common::Rect &slopRect) { +	if (!isTracking()) { +		_rawLimitRect = limitRect; +		_slopRect = slopRect; +	} +} + +void SpriteDragger::getDragConstraints(Common::Rect &limitRect, Common::Rect &slopRect) const { +	limitRect = _rawLimitRect; +	slopRect = _slopRect; +} + +void SpriteDragger::startTracking(const Input &input) { +	if (_draggingSprite) { +		Tracker::startTracking(input); + +		if (isTracking()) { +			input.getInputLocation(_startPoint); +			_lastRawPoint = _startRawPoint = _startPoint; + +			Common::Rect r; +			_draggingSprite->getBounds(r); +			_dragOffset.x = _startPoint.x - r.left; +			_dragOffset.y = _startPoint.y - r.top; + +			_limitRect = _rawLimitRect; +			_limitRect.left += _dragOffset.x; +			_limitRect.top += _dragOffset.y; +			_limitRect.right -= r.width() - _dragOffset.x; +			_limitRect.bottom -= r.height() - _dragOffset.y; +			pinPointInRect(_limitRect, _startPoint); + +			_lastPoint = _startPoint; +			if (_startPoint != _startRawPoint) { +				Common::Point pt = _startPoint - _dragOffset; +				_draggingSprite->moveElementTo(pt.x, pt.y); +			} + +			_lastHotspot = g_allHotspots.findHotspot(_lastRawPoint); +			if (_lastHotspot) +				enterHotspot(_lastHotspot); +		} +	} +} + +void SpriteDragger::continueTracking(const Input &input) {	 +	if (_draggingSprite) { +		Common::Point rawPoint; +		input.getInputLocation(rawPoint); + +		if (!_slopRect.contains(rawPoint)) +			rawPoint = _startRawPoint; +	 +		if (rawPoint != _lastRawPoint) { +			Common::Point newPoint = rawPoint; +			pinPointInRect(_limitRect, newPoint); +			newPoint -= _dragOffset; + +			if (newPoint != _lastPoint) { +				_draggingSprite->moveElementTo(newPoint.x, newPoint.y); +				_lastPoint = newPoint; +			} + +			Hotspot *newHotspot = g_allHotspots.findHotspot(rawPoint); +			if (newHotspot != _lastHotspot) { +				if (_lastHotspot) +					exitHotspot(_lastHotspot); +				if (newHotspot) +					enterHotspot(newHotspot); +				_lastHotspot = newHotspot; +			} + +			_lastRawPoint = rawPoint; +		} +	} +} + +void SpriteDragger::pinPointInRect(const Common::Rect &r, Common::Point &pt) { +	pt.x = CLIP<int>(pt.x, r.left, r.right - 1); +	pt.y = CLIP<int>(pt.y, r.top, r.bottom - 1); +} + +ItemDragger::ItemDragger(PegasusEngine *owner) : _inventoryDropSpot(kInventoryDropSpotID), _biochipDropSpot(kBiochipDropSpotID), +		_inventoryHighlight(kInventoryDropHighlightID), _biochipHighlight(kBiochipDropHighlightID) {	 +	_owner = owner; + +	Common::Rect r(kInventoryDropLeft, kInventoryDropTop, kInventoryDropRight, kInventoryDropBottom); +	_inventoryDropSpot.setArea(r); +	_inventoryDropSpot.setHotspotFlags(kDropItemSpotFlag); +	g_allHotspots.push_back(&_inventoryDropSpot); +	 +	r = Common::Rect(kBiochipDropLeft, kBiochipDropTop, kBiochipDropRight, kBiochipDropBottom); +	_biochipDropSpot.setArea(r); +	_biochipDropSpot.setHotspotFlags(kDropBiochipSpotFlag); +	g_allHotspots.push_back(&_biochipDropSpot); +} + +void ItemDragger::startTracking(const Input &input) { +	_inventoryHighlight.setDisplayOrder(kInventoryHiliteOrder); +	_inventoryHighlight.startDisplaying(); +	 +	_biochipHighlight.setDisplayOrder(kBiochipHiliteOrder); +	_biochipHighlight.startDisplaying(); +	 +	SpriteDragger::startTracking(input); +} + +void ItemDragger::stopTracking(const Input &input) { +	SpriteDragger::stopTracking(input); +	_inventoryHighlight.hide(); +	_biochipHighlight.hide(); +	_inventoryHighlight.stopDisplaying(); +	_biochipHighlight.stopDisplaying(); +	_owner->dragTerminated(input); +} + +bool ItemDragger::stopTrackingInput(const Input &input) { +	return !JMPPPInput::isDraggingInput(input); +} + +void ItemDragger::enterHotspot(Hotspot *spot) { +	if (spot->getObjectID() == kInventoryDropSpotID) +		_inventoryHighlight.show(); +	else if (spot->getObjectID() == kBiochipDropSpotID) +		_biochipHighlight.show(); +	else if ((spot->getHotspotFlags() & kDropItemSpotFlag) != 0) +		_draggingSprite->setCurrentFrameIndex(1); +} + +void ItemDragger::exitHotspot(Hotspot *spot) { +	if (spot->getObjectID() == kInventoryDropSpotID) +		_inventoryHighlight.hide(); +	else if (spot->getObjectID() == kBiochipDropSpotID) +		_biochipHighlight.hide(); +	else if ((spot->getHotspotFlags() & kDropItemSpotFlag) != 0) +		_draggingSprite->setCurrentFrameIndex(0); +} + +void ItemDragger::setHighlightBounds() { +	uint32 color = g_system->getScreenFormat().RGBToColor(0x48, 0x80, 0xD8); +	_inventoryHighlight.setBounds(Common::Rect(76, 334, 172, 430)); +	_inventoryHighlight.setHighlightColor(color); +	_biochipHighlight.setBounds(Common::Rect(364, 334, 460, 430)); +	_biochipHighlight.setHighlightColor(color); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/itemdragger.h b/engines/pegasus/items/itemdragger.h new file mode 100644 index 0000000000..69612316f3 --- /dev/null +++ b/engines/pegasus/items/itemdragger.h @@ -0,0 +1,96 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_ITEMDRAGGER_H +#define PEGASUS_ITEMS_ITEMDRAGGER_H + +#include "pegasus/elements.h" +#include "pegasus/input.h" + +namespace Pegasus { + +// TODO: Merge SpriteDragger and ItemDragger + +class Hotspot; +class Sprite; + +class SpriteDragger : public Tracker { +public: +	SpriteDragger(); +	virtual ~SpriteDragger() {} + +	void setDragSprite(Sprite *); +	Sprite *getDragSprite() { return _draggingSprite; } + +	void setDragConstraints(const Common::Rect &, const Common::Rect &); +	void getDragConstraints(Common::Rect &, Common::Rect &) const; +	 +	void startTracking(const Input &); +	void continueTracking(const Input&); +	 +	Hotspot *getLastHotspot() const { return _lastHotspot; } + +protected: +	virtual void enterHotspot(Hotspot *) {} +	virtual void exitHotspot(Hotspot *) {} +	 +	Sprite *_draggingSprite; +	Common::Point _startPoint, _lastPoint, _dragOffset; +	Common::Point _startRawPoint, _lastRawPoint; +	Common::Rect _rawLimitRect; +	Common::Rect _limitRect; +	Common::Rect _slopRect; +	Hotspot *_lastHotspot; + +	// This is a replica of QuickDraw's PinPointInRect function +	void pinPointInRect(const Common::Rect &, Common::Point &); +}; + +class PegasusEngine; + +class ItemDragger : public SpriteDragger { +public: +	ItemDragger(PegasusEngine *); +	virtual ~ItemDragger() {} + +	void setHighlightBounds(); +	void startTracking(const Input &); +	void stopTracking(const Input &); +	bool stopTrackingInput(const Input &); + +protected: +	virtual void enterHotspot(Hotspot *); +	virtual void exitHotspot(Hotspot *); +	 +	PegasusEngine *_owner; +	DropHighlight _inventoryHighlight; +	Hotspot _inventoryDropSpot; +	DropHighlight _biochipHighlight; +	Hotspot _biochipDropSpot; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/items/itemlist.cpp b/engines/pegasus/items/itemlist.cpp new file mode 100644 index 0000000000..4c30975589 --- /dev/null +++ b/engines/pegasus/items/itemlist.cpp @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/error.h" +#include "common/stream.h" + +#include "engines/pegasus/items/item.h" +#include "engines/pegasus/items/itemlist.h" + +namespace Pegasus { + +// TODO: Don't use global construction! +ItemList g_allItems; + +ItemList::ItemList() { +} + +ItemList::~ItemList() { +} + +void ItemList::writeToStream(Common::WriteStream *stream) { +	stream->writeUint32BE(size()); + +	for (ItemIterator it = begin(); it != end(); it++) { +		stream->writeUint16BE((*it)->getObjectID()); +		(*it)->writeToStream(stream); +	} +} + +void ItemList::readFromStream(Common::ReadStream *stream) { +	uint32 itemCount = stream->readUint32BE(); + +	for (uint32 i = 0; i < itemCount; i++) { +		ItemID itemID = stream->readUint16BE(); +		g_allItems.findItemByID(itemID)->readFromStream(stream); +	} +} + +Item *ItemList::findItemByID(const ItemID id) { +	for (ItemIterator it = begin(); it != end(); it++) +		if ((*it)->getObjectID() == id) +			return *it; + +	return 0; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/items/itemlist.h b/engines/pegasus/items/itemlist.h new file mode 100644 index 0000000000..b5a1d489be --- /dev/null +++ b/engines/pegasus/items/itemlist.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_ITEMS_ITEMLIST_H +#define PEGASUS_ITEMS_ITEMLIST_H + +#include "common/list.h" + +#include "pegasus/types.h" + +namespace Common { +	class ReadStream; +	class WriteStream; +} + +namespace Pegasus { + +class Item; + +class ItemList : public Common::List<Item *> { +public: +	ItemList(); +	virtual ~ItemList(); +	 +	virtual void writeToStream(Common::WriteStream *stream); +	virtual void readFromStream(Common::ReadStream *stream); +	 +	Item *findItemByID(const ItemID id); +}; + +typedef ItemList::iterator ItemIterator; + +// TODO: Don't use global construction! +extern ItemList g_allItems; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/menu.cpp b/engines/pegasus/menu.cpp new file mode 100644 index 0000000000..5d90f62b14 --- /dev/null +++ b/engines/pegasus/menu.cpp @@ -0,0 +1,1222 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/menu.h" +#include "pegasus/pegasus.h" +#include "pegasus/scoring.h" + +namespace Pegasus { + +GameMenu::GameMenu(const uint32 id) : IDObject(id), InputHandler((InputHandler *)((PegasusEngine *)g_engine)) { +	_previousHandler = 0; +	_lastCommand = kMenuCmdNoCommand; +} + +void GameMenu::becomeCurrentHandler() { +	_previousHandler = InputHandler::setInputHandler(this); +} + +void GameMenu::restorePreviousHandler() { +	InputHandler::setInputHandler(_previousHandler); +} + +void GameMenu::drawScore(GameScoreType score, GameScoreType total, const Common::Rect &scoreBounds, Surface *numbers) { +	CoordType x = scoreBounds.right; +	drawNumber(total, x, scoreBounds.top, numbers); + +	x -= 12; +	Common::Rect r1(120, 0, 132, 12); // The slash. +	Common::Rect r2 = r1; +	r2.moveTo(x, scoreBounds.top); +	numbers->copyToCurrentPort(r1, r2); +	drawNumber(score, x, scoreBounds.top, numbers); +} + +void GameMenu::drawNumber(GameScoreType number, CoordType &x, CoordType y, Surface *numbers) { +	Common::Rect r1(0, 0, 12, 12); // Width, height of one digit +	Common::Rect r2 = r1; +	r2.moveTo(x - 12, y); + +	do { +		uint16 digit = number % 10; +		number /= 10; +		r1.moveTo(digit * 12, 0); +		numbers->copyToCurrentPort(r1, r2); +		r2.translate(-12, 0); +	} while (number != 0); + +	x = r2.right; +} + +enum { +	kMainMenuStartDemo = 0, +	kMainMenuCreditsDemo, +	kMainMenuQuitDemo, +	kFirstSelectionDemo = kMainMenuStartDemo, +	kLastSelectionDemo = kMainMenuQuitDemo, + +	kMainMenuOverview = 0, +	kMainMenuStart, +	kMainMenuRestore, +	kMainMenuDifficulty, +	kMainMenuCredits, +	kMainMenuQuit, +	kFirstSelection = kMainMenuOverview, +	kLastSelection = kMainMenuQuit +}; + +static const CoordType kStartLeftDemo = 44; +static const CoordType kStartTopDemo = 336; + +static const CoordType kStartSelectLeftDemo = 40; +static const CoordType kStartSelectTopDemo = 331; + +static const CoordType kCreditsLeftDemo = 44; +static const CoordType kCreditsTopDemo = 372; + +static const CoordType kCreditsSelectLeftDemo = 40; +static const CoordType kCreditsSelectTopDemo = 367; + +static const CoordType kMainMenuQuitLeftDemo = 32; +static const CoordType kMainMenuQuitTopDemo = 412; + +static const CoordType kMainMenuQuitSelectLeftDemo = 28; +static const CoordType kMainMenuQuitSelectTopDemo = 408; + +static const CoordType kOverviewLeft = 200; +static const CoordType kOverviewTop = 208; + +static const CoordType kOverviewSelectLeft = 152; +static const CoordType kOverviewSelectTop = 204; + +static const CoordType kStartLeft = 212; +static const CoordType kStartTop = 256; + +static const CoordType kStartSelectLeft = 152; +static const CoordType kStartSelectTop = 252; + +static const CoordType kRestoreLeft = 212; +static const CoordType kRestoreTop = 296; + +static const CoordType kRestoreSelectLeft = 152; +static const CoordType kRestoreSelectTop = 292; + +static const CoordType kDifficultyLeft = 320; +static const CoordType kDifficultyTop = 340; + +static const CoordType kDifficultySelectLeft = 152; +static const CoordType kDifficultySelectTop = 336; + +static const CoordType kCreditsLeft = 212; +static const CoordType kCreditsTop = 388; + +static const CoordType kCreditsSelectLeft = 152; +static const CoordType kCreditsSelectTop = 384; + +static const CoordType kMainMenuQuitLeft = 212; +static const CoordType kMainMenuQuitTop = 428; + +static const CoordType kMainMenuQuitSelectLeft = 152; +static const CoordType kMainMenuQuitSelectTop = 424; + +// Never set the current input handler to the MainMenu. +MainMenu::MainMenu() : GameMenu(kMainMenuID), _menuBackground(0), _overviewButton(0), +		_restoreButton(0), _adventureButton(0), _walkthroughButton(0), _startButton(0), +		_creditsButton(0), _quitButton(0), _largeSelect(0), _smallSelect(0) { + +	bool isDemo = ((PegasusEngine *)g_engine)->isDemo(); + +	if (isDemo) +		_menuBackground.initFromPICTFile("Images/Demo/DemoMenu.pict"); +	else +		_menuBackground.initFromPICTFile("Images/Main Menu/MainMenu.mac"); +	_menuBackground.setDisplayOrder(0); +	_menuBackground.startDisplaying(); +	_menuBackground.show(); + +	if (!isDemo) { +		_overviewButton.initFromPICTFile("Images/Main Menu/pbOvervi.pict"); +		_overviewButton.setDisplayOrder(1); +		_overviewButton.moveElementTo(kOverviewLeft, kOverviewTop); +		_overviewButton.startDisplaying(); + +		_restoreButton.initFromPICTFile("Images/Main Menu/pbRestor.pict"); +		_restoreButton.setDisplayOrder(1); +		_restoreButton.moveElementTo(kRestoreLeft, kRestoreTop); +		_restoreButton.startDisplaying(); + +		_adventureButton.initFromPICTFile("Images/Main Menu/BtnAdv.pict"); +		_adventureButton.setDisplayOrder(1); +		_adventureButton.moveElementTo(kDifficultyLeft, kDifficultyTop); +		_adventureButton.startDisplaying(); + +		_walkthroughButton.initFromPICTFile("Images/Main Menu/BtnWlk.pict"); +		_walkthroughButton.setDisplayOrder(1); +		_walkthroughButton.moveElementTo(kDifficultyLeft, kDifficultyTop); +		_walkthroughButton.startDisplaying(); +	} + +	if (isDemo) +		_startButton.initFromPICTFile("Images/Demo/Start.pict"); +	else +		_startButton.initFromPICTFile("Images/Main Menu/pbStart.pict"); +	_startButton.setDisplayOrder(1); +	_startButton.moveElementTo(isDemo ? kStartLeftDemo : kStartLeft, isDemo ? kStartTopDemo : kStartTop); +	_startButton.startDisplaying(); + +	if (isDemo) +		_creditsButton.initFromPICTFile("Images/Demo/Credits.pict"); +	else +		_creditsButton.initFromPICTFile("Images/Main Menu/pbCredit.pict"); +	_creditsButton.setDisplayOrder(1); +	_creditsButton.moveElementTo(isDemo ? kCreditsLeftDemo : kCreditsLeft, isDemo ? kCreditsTopDemo : kCreditsTop); +	_creditsButton.startDisplaying(); + +	if (isDemo) +		_quitButton.initFromPICTFile("Images/Demo/Quit.pict"); +	else +		_quitButton.initFromPICTFile("Images/Main Menu/pbQuit.pict"); +	_quitButton.setDisplayOrder(1); +	_quitButton.moveElementTo(isDemo ? kMainMenuQuitLeftDemo : kMainMenuQuitLeft, isDemo ? kMainMenuQuitTopDemo : kMainMenuQuitTop); +	_quitButton.startDisplaying(); + +	if (isDemo) +		_largeSelect.initFromPICTFile("Images/Demo/SelectL.pict", true); +	else +		_largeSelect.initFromPICTFile("Images/Main Menu/SelectL.pict", true); +	_largeSelect.setDisplayOrder(1); +	_largeSelect.startDisplaying(); + +	if (isDemo) +		_smallSelect.initFromPICTFile("Images/Demo/SelectS.pict", true); +	else +		_smallSelect.initFromPICTFile("Images/Main Menu/SelectS.pict", true); +	_smallSelect.setDisplayOrder(1); +	_smallSelect.startDisplaying(); + +	_menuSelection = isDemo ? kFirstSelectionDemo : kFirstSelection; + +	_adventureMode = true; + +	_menuLoop.attachFader(&_menuFader); +	_menuLoop.initFromAIFFFile("Sounds/Main Menu.aiff"); + +	updateDisplay(); +} + +MainMenu::~MainMenu() { +	if (_menuLoop.isPlaying()) +		stopMainMenuLoop(); +} + +void MainMenu::startMainMenuLoop() { +	FaderMoveSpec spec; + +	_menuLoop.loopSound(); +	spec.makeTwoKnotFaderSpec(30, 0, 0, 30, 255); + +	// FIXME: Should be sync, but it's a pain to use the main menu right now +	// with this one. +	_menuFader.startFader(spec); +} + +void MainMenu::stopMainMenuLoop() { +	FaderMoveSpec spec; +	 +	spec.makeTwoKnotFaderSpec(30, 0, 255, 30, 0); +	_menuFader.startFaderSync(spec); +	_menuLoop.stopSound(); +} + +void MainMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; +	bool isDemo = vm->isDemo(); + +	if (input.upButtonDown()) { +		if (_menuSelection > (isDemo ? kFirstSelectionDemo : kFirstSelection)) { +			_menuSelection--; +			updateDisplay(); +		} +	} else if (input.downButtonDown()) { +		if (_menuSelection < (isDemo ? kLastSelectionDemo : kLastSelection)) { +			_menuSelection++; +			updateDisplay(); +		} +	} else if (!isDemo && (input.leftButtonDown() || input.rightButtonDown())) { +		if (_menuSelection == kMainMenuDifficulty) { +			_adventureMode = !_adventureMode; +			updateDisplay(); +		} +	} else if (JMPPPInput::isMenuButtonPressInput(input)) { +		if (isDemo) { +			switch (_menuSelection) { +			case kMainMenuCreditsDemo: +				_creditsButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_creditsButton.hide(); +				setLastCommand(kMenuCmdCredits); +				break; +			case kMainMenuStartDemo: +				_startButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_startButton.hide(); +				setLastCommand(kMenuCmdStartAdventure); +				break; +			case kMainMenuQuitDemo: +				_quitButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_quitButton.hide(); +				setLastCommand(kMenuCmdQuit); +				break; +			} +		} else { +			switch (_menuSelection) { +			case kMainMenuOverview: +				_overviewButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_overviewButton.hide(); +				setLastCommand(kMenuCmdOverview); +				break; +			case kMainMenuRestore: +				_restoreButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_restoreButton.hide(); +				setLastCommand(kMenuCmdRestore); +				break; +			case kMainMenuCredits: +				_creditsButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_creditsButton.hide(); +				setLastCommand(kMenuCmdCredits); +				break; +			case kMainMenuStart: +				_startButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_startButton.hide(); +				if (_adventureMode) +					setLastCommand(kMenuCmdStartAdventure); +				else +					setLastCommand(kMenuCmdStartWalkthrough); +				break; +			case kMainMenuDifficulty: +				_adventureMode = !_adventureMode; +				updateDisplay(); +				break; +			case kMainMenuQuit: +				_quitButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_quitButton.hide(); +				setLastCommand(kMenuCmdQuit); +				break; +			} +		} +	} + +	InputHandler::handleInput(input, cursorSpot); +} + +void MainMenu::updateDisplay() { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	if (vm->isDemo()) { +		switch (_menuSelection) { +		case kMainMenuStartDemo: +			_smallSelect.moveElementTo(kStartSelectLeftDemo, kStartSelectTopDemo); +			_smallSelect.show(); +			_largeSelect.hide(); +			break; +		case kMainMenuCreditsDemo: +			_smallSelect.moveElementTo(kCreditsSelectLeftDemo, kCreditsSelectTopDemo); +			_smallSelect.show(); +			_largeSelect.hide(); +			break; +		case kMainMenuQuitDemo: +			_largeSelect.moveElementTo(kMainMenuQuitSelectLeftDemo, kMainMenuQuitSelectTopDemo); +			_largeSelect.show(); +			_smallSelect.hide(); +			break; +		} +	} else { +		switch (_menuSelection) { +		case kMainMenuOverview: +			_largeSelect.moveElementTo(kOverviewSelectLeft, kOverviewSelectTop); +			_largeSelect.show(); +			_smallSelect.hide(); +			break; +		case kMainMenuRestore: +			_smallSelect.moveElementTo(kRestoreSelectLeft, kRestoreSelectTop); +			_smallSelect.show(); +			_largeSelect.hide(); +			break; +		case kMainMenuDifficulty: +			if (_adventureMode) { +				_adventureButton.show(); +				_walkthroughButton.hide(); +			} else { +				_walkthroughButton.show(); +				_adventureButton.hide(); +			} + +			_largeSelect.moveElementTo(kDifficultySelectLeft, kDifficultySelectTop); +			_largeSelect.show(); +			_smallSelect.hide(); +			break; +		case kMainMenuStart: +			_smallSelect.moveElementTo(kStartSelectLeft, kStartSelectTop); +			_smallSelect.show(); +			_largeSelect.hide(); +			break; +		case kMainMenuCredits: +			_smallSelect.moveElementTo(kCreditsSelectLeft, kCreditsSelectTop); +			_smallSelect.show(); +			_largeSelect.hide(); +			break; +		case kMainMenuQuit: +			_smallSelect.moveElementTo(kMainMenuQuitSelectLeft, kMainMenuQuitSelectTop); +			_smallSelect.show(); +			_largeSelect.hide(); +			break; +		} + +		vm->resetIntroTimer(); +	} +} + +enum { +	kCreditsMenuCoreTeam, +	kCreditsMenuSupportTeam, +	kCreditsMenuOriginalTeam, +	kCreditsMenuTalent, +	kCreditsMenuOtherTitles, +	kCreditsMenuMainMenu, +	 +	kCreditsFirstSelection = kCreditsMenuCoreTeam, +	kCreditsLastSelection = kCreditsMenuMainMenu +}; + +static const CoordType kCreditsMovieLeft = 288; +static const CoordType kCreditsMovieTop = 0; + +static const CoordType kCoreTeamSelectLeft = 40; +static const CoordType kCoreTeamSelectTop = 223; + +static const CoordType kSupportTeamSelectLeft = 40; +static const CoordType kSupportTeamSelectTop = 259; + +static const CoordType kOriginalTeamSelectLeft = 40; +static const CoordType kOriginalTeamSelectTop = 295; + +static const CoordType kTalentSelectLeft = 40; +static const CoordType kTalentSelectTop = 331; + +static const CoordType kOtherTitlesSelectLeft = 40; +static const CoordType kOtherTitlesSelectTop = 367; + +static const CoordType kCreditsMainMenuLeft = 32; +static const CoordType kCreditsMainMenuTop = 412; + +static const CoordType kCreditsMainMenuSelectLeft = 30; +static const CoordType kCreditsMainMenuSelectTop = 408; + +static const TimeValue kCoreTeamTime = 0; +static const TimeValue kSupportTeamTime = 1920; +static const TimeValue kOriginalTeamTime = 3000; +static const TimeValue kTalentTime = 4440; +static const TimeValue kOtherTitlesTime = 4680; + +static const TimeValue kFrameIncrement = 120; // Three frames... + +// Never set the current input handler to the CreditsMenu. +CreditsMenu::CreditsMenu() : GameMenu(kCreditsMenuID), _menuBackground(0), _creditsMovie(0), +		_mainMenuButton(0), _largeSelect(0), _smallSelect(0) { + +	_menuBackground.initFromPICTFile("Images/Credits/CredScrn.pict"); +	_menuBackground.setDisplayOrder(0); +	_menuBackground.startDisplaying(); +	_menuBackground.show(); + +	_creditsMovie.initFromMovieFile("Images/Credits/Credits.movie"); +	_creditsMovie.setDisplayOrder(1); +	_creditsMovie.moveElementTo(kCreditsMovieLeft, kCreditsMovieTop); +	_creditsMovie.startDisplaying(); +	_creditsMovie.show(); +	_creditsMovie.redrawMovieWorld(); + +	_mainMenuButton.initFromPICTFile("Images/Credits/MainMenu.pict"); +	_mainMenuButton.setDisplayOrder(1); +	_mainMenuButton.moveElementTo(kCreditsMainMenuLeft, kCreditsMainMenuTop); +	_mainMenuButton.startDisplaying(); + +	_largeSelect.initFromPICTFile("Images/Credits/SelectL.pict", true); +	_largeSelect.setDisplayOrder(2); +	_largeSelect.moveElementTo(kCreditsMainMenuSelectLeft, kCreditsMainMenuSelectTop); +	_largeSelect.startDisplaying(); + +	_smallSelect.initFromPICTFile("Images/Credits/SelectS.pict", true); +	_smallSelect.setDisplayOrder(2); +	_smallSelect.show(); +	_smallSelect.startDisplaying(); +	 +	_menuSelection = -1; +	 +	newMenuSelection(kCreditsMenuCoreTeam); +} + +// Assumes the new selection is never more than one away from the old... +void CreditsMenu::newMenuSelection(const int newSelection) { +	if (newSelection != _menuSelection) { +		switch (newSelection) { +		case kCreditsMenuCoreTeam: +			_smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop); +			_creditsMovie.setTime(kCoreTeamTime); +			_creditsMovie.redrawMovieWorld(); +			break; +		case kCreditsMenuSupportTeam: +			_smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop); +			_creditsMovie.setTime(kSupportTeamTime); +			_creditsMovie.redrawMovieWorld(); +			break; +		case kCreditsMenuOriginalTeam: +			_smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop); +			_creditsMovie.setTime(kOriginalTeamTime); +			_creditsMovie.redrawMovieWorld(); +			break; +		case kCreditsMenuTalent: +			_smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop); +			_creditsMovie.setTime(kTalentTime); +			_creditsMovie.redrawMovieWorld(); +			break; +		case kCreditsMenuOtherTitles: +			_smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop); +			_smallSelect.show(); +			_largeSelect.hide(); +			_creditsMovie.setTime(kOtherTitlesTime); +			_creditsMovie.redrawMovieWorld(); +			break; +		case kCreditsMenuMainMenu: +			_smallSelect.hide(); +			_largeSelect.show(); +			break; +		} + +		_menuSelection = newSelection; +	} +} + +void CreditsMenu::newMovieTime(const TimeValue newTime) { +	if (newTime < kSupportTeamTime) { +		_smallSelect.moveElementTo(kCoreTeamSelectLeft, kCoreTeamSelectTop); +		_menuSelection = kCreditsMenuCoreTeam; +	} else if (newTime < kOriginalTeamTime) { +		_smallSelect.moveElementTo(kSupportTeamSelectLeft, kSupportTeamSelectTop); +		_menuSelection = kCreditsMenuSupportTeam; +	} else if (newTime < kTalentTime) { +		_smallSelect.moveElementTo(kOriginalTeamSelectLeft, kOriginalTeamSelectTop); +		_menuSelection = kCreditsMenuOriginalTeam; +	} else if (newTime < kOtherTitlesTime) { +		_smallSelect.moveElementTo(kTalentSelectLeft, kTalentSelectTop); +		_smallSelect.show(); +		_largeSelect.hide(); +		_menuSelection = kCreditsMenuTalent; +	} else if ((int)newTime == -120) { +		// HACK: Avoid getting sent to the bottom button in the default case +		return; +	} else { +		_smallSelect.moveElementTo(kOtherTitlesSelectLeft, kOtherTitlesSelectTop); +		_smallSelect.show(); +		_largeSelect.hide(); +		_menuSelection = kCreditsMenuOtherTitles; +	} + +	_creditsMovie.setTime(newTime); +	_creditsMovie.redrawMovieWorld(); +} + +void CreditsMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { +	if (input.upButtonDown()) { +		if (_menuSelection > kCreditsFirstSelection) +			newMenuSelection(_menuSelection - 1); +	} else if (input.downButtonDown()) { +		if (_menuSelection < kCreditsLastSelection) +			newMenuSelection(_menuSelection + 1); +	} else if (input.leftButtonDown()) { +		newMovieTime(_creditsMovie.getTime() - kFrameIncrement); +	} else if (input.rightButtonDown()) { +		newMovieTime(_creditsMovie.getTime() + kFrameIncrement); +	} else if (JMPPPInput::isMenuButtonPressInput(input)) { +		if (_menuSelection == kCreditsMenuMainMenu) { +			_mainMenuButton.show(); +			((PegasusEngine *)g_engine)->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +			_mainMenuButton.hide(); +			setLastCommand(kMenuCmdCreditsMainMenu); +		} +	} + +	InputHandler::handleInput(input, cursorSpot); +} + +static const CoordType kContinueLeft = 44; +static const CoordType kContinueTop = 336; + +static const CoordType kContinueSelectLeft = 40; +static const CoordType kContinueSelectTopDemo = 331; +static const CoordType kContinueSelectTop = 332; + +static const CoordType kMainMenuLeftDemo = 44; +static const CoordType kMainMenuTopDemo = 372; + +static const CoordType kMainMenuSelectLeftDemo = 40; +static const CoordType kMainMenuSelectTopDemo = 367; + +static const CoordType kQuitLeftDemo = 32; +static const CoordType kQuitTopDemo = 412; + +static const CoordType kQuitSelectLeftDemo = 28; +static const CoordType kQuitSelectTopDemo = 408; + +static const CoordType kRestoreLeftDeath = 44; +static const CoordType kRestoreTopDeath = 372; + +static const CoordType kRestoreSelectLeftDeath = 40; +static const CoordType kRestoreSelectTopDeath = 368; + +static const CoordType kMainMenuLeft = 32; +static const CoordType kMainMenuTop = 412; + +static const CoordType kMainMenuSelectLeft = 28; +static const CoordType kMainMenuSelectTop = 408; + +enum { +	kDeathScreenContinueDemo = 0, +	kDeathScreenMainMenuDemo, +	kDeathScreenQuitDemo, +	 +	kFirstDeathSelectionDemo = kDeathScreenContinueDemo, +	kLastDeathSelectionDemo = kDeathScreenQuitDemo, + +	kDeathScreenContinue = 0, +	kDeathScreenRestore, +	kDeathScreenMainMenu, +	 +	kFirstDeathSelection = kDeathScreenContinue, +	kLastDeathSelection = kDeathScreenMainMenu +}; + +// Never set the current input handler to the DeathMenu. +DeathMenu::DeathMenu(const DeathReason deathReason) : GameMenu(kDeathMenuID), _deathBackground(0), _continueButton(0), +		_mainMenuButton(0), _quitButton(0), _restoreButton(0), _largeSelect(0), _smallSelect(0) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; +	bool isDemo = vm->isDemo(); + +	_playerWon = (deathReason == kPlayerWonGame); + +	Common::String prefix = "Images/"; +	Common::String imageName; +	if (isDemo) { +		prefix += "Demo/"; +		imageName = prefix; + +		switch (deathReason) { +		case kDeathFallOffCliff: +			imageName += "dPfall"; +			break; +		case kDeathEatenByDinosaur: +			imageName += "dPdino"; +			break; +		case kDeathStranded: +			imageName += "dPstuck"; +			break; +		default: +			imageName += "dPdemowin"; +			break; +		} + +		imageName += ".pict"; +	} else { +		prefix += "Death Screens/"; +		imageName = prefix; + +		static const char *fileNames[] = { +			"dAunmade", "dAbombed", "dAshot", "dAassass", "dAnuked", +			"dTunmade", "dTshot", "dPfall", "dPdino", "dPstuck", +			"dNchoke", "dNcaught", "dNcaught", "dNsub", "dNrobot1", +			"dNrobot2", "dMfall", "dMcaught", "dMtracks", "dMrobot", +			"dMtoast", "dMexplo1", "dMexplo2", "dMchoke1", "dMchoke2", +			"dMdroid", "dMfall", "dMgears", "dMshutt1", "dMshutt2", +			"dWpoison", "dWcaught", "dWplasma", "dWshot", "dAfinale" +		}; + +		imageName += fileNames[deathReason - 1]; +		imageName += ".pict"; +	} + +	_deathBackground.initFromPICTFile(imageName); +	_deathReason = deathReason; + +	if (!isDemo) { +		vm->_gfx->setCurSurface(_deathBackground.getSurface()); +		drawAllScores(); +		vm->_gfx->setCurSurface(vm->_gfx->getWorkArea()); +	} + +	_deathBackground.setDisplayOrder(0); +	_deathBackground.startDisplaying(); +	_deathBackground.show(); + +	if (isDemo) { +		if (_playerWon) // Make credits button... +			_continueButton.initFromPICTFile(prefix + "Credits.pict"); +		else            // Make continue button... +			_continueButton.initFromPICTFile(prefix + "Continue.pict"); + +		_mainMenuButton.initFromPICTFile(prefix + "MainMenu.pict"); +		_mainMenuButton.setDisplayOrder(1); +		_mainMenuButton.moveElementTo(kMainMenuLeftDemo, kMainMenuTopDemo); +		_mainMenuButton.startDisplaying(); + +		_quitButton.initFromPICTFile(prefix + "Quit.pict"); +		_quitButton.setDisplayOrder(1); +		_quitButton.moveElementTo(kQuitLeftDemo, kQuitTopDemo); +		_quitButton.startDisplaying(); + +		_menuSelection = kDeathScreenContinueDemo; +	} else { +		if (!_playerWon) { +			_mainMenuButton.initFromPICTFile(prefix + "MainMenu.pict"); +			_mainMenuButton.setDisplayOrder(1); +			_mainMenuButton.moveElementTo(kMainMenuLeft, kMainMenuTop); +			_mainMenuButton.startDisplaying(); + +			_restoreButton.initFromPICTFile(prefix + "Restore.pict"); +			_restoreButton.setDisplayOrder(1); +			_restoreButton.moveElementTo(kRestoreLeftDeath, kRestoreTopDeath); +			_restoreButton.startDisplaying(); +		} + +		_continueButton.initFromPICTFile(prefix + "Continue.pict"); + +		_menuSelection = kDeathScreenContinue; +	} + +	_smallSelect.initFromPICTFile(prefix + "SelectS.pict", true); +	_smallSelect.setDisplayOrder(2); +	_smallSelect.startDisplaying(); + +	_continueButton.setDisplayOrder(1); +	_continueButton.moveElementTo(kContinueLeft, kContinueTop); +	_continueButton.startDisplaying(); + +	if (isDemo || !_playerWon) { +		_largeSelect.initFromPICTFile(prefix + "SelectL.pict", true); +		_largeSelect.setDisplayOrder(2); +		_largeSelect.startDisplaying(); +	} else { +		_triumphSound.initFromQuickTime("Sounds/Caldoria/Galactic Triumph"); +		_triumphSound.playSound(); +	} + +	updateDisplay(); +} + +void DeathMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	if (input.upButtonDown()) { +		if (_menuSelection > (vm->isDemo() ? kFirstDeathSelectionDemo : kFirstDeathSelection)) { +			_menuSelection--; +			updateDisplay(); +		} +	} else if (input.downButtonDown() && (vm->isDemo() || !_playerWon)) { +		if (_menuSelection < (vm->isDemo() ? kLastDeathSelectionDemo : kLastDeathSelection)) { +			_menuSelection++; +			updateDisplay(); +		} +	} else if (JMPPPInput::isMenuButtonPressInput(input)) { +		if (vm->isDemo()) { +			switch (_menuSelection) { +			case kDeathScreenContinueDemo: +				_continueButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_continueButton.hide(); +				setLastCommand(kMenuCmdDeathContinue); +				break; +			case kDeathScreenQuitDemo: +				_quitButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_quitButton.hide(); +				setLastCommand(kMenuCmdDeathQuitDemo); +				break; +			case kDeathScreenMainMenuDemo: +				_mainMenuButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_mainMenuButton.hide(); +				setLastCommand(kMenuCmdDeathMainMenuDemo); +				break; +			} +		} else { +			switch (_menuSelection) { +			case kDeathScreenContinue: +				_continueButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_continueButton.hide(); +				setLastCommand(kMenuCmdDeathContinue); +				break; +			case kDeathScreenRestore: +				_restoreButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_restoreButton.hide(); +				setLastCommand(kMenuCmdDeathRestore); +				break; +			case kDeathScreenMainMenu: +				_mainMenuButton.show(); +				vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +				_mainMenuButton.hide(); +				setLastCommand(kMenuCmdDeathMainMenu); +				break; +			} +		} +	} + +	InputHandler::handleInput(input, cursorSpot); +} + +void DeathMenu::updateDisplay() { +	if (((PegasusEngine *)g_engine)->isDemo()) { +		switch (_menuSelection) { +		case kDeathScreenContinueDemo: +			_smallSelect.moveElementTo(kContinueSelectLeft, kContinueSelectTopDemo); +			_smallSelect.show(); +			_largeSelect.hide(); +			break; +		case kDeathScreenQuitDemo: +			_largeSelect.moveElementTo(kQuitSelectLeftDemo, kQuitSelectTopDemo); +			_largeSelect.show(); +			_smallSelect.hide(); +			break; +		case kDeathScreenMainMenuDemo: +			_smallSelect.moveElementTo(kMainMenuSelectLeftDemo, kMainMenuSelectTopDemo); +			_smallSelect.show(); +			_largeSelect.hide(); +			break; +		} +	} else { +		switch (_menuSelection) { +		case kDeathScreenContinue: +			_smallSelect.moveElementTo(kContinueSelectLeft, kContinueSelectTop); +			_smallSelect.show(); +			_largeSelect.hide(); +			break; +		case kDeathScreenRestore: +			_smallSelect.moveElementTo(kRestoreSelectLeftDeath, kRestoreSelectTopDeath); +			_smallSelect.show(); +			_largeSelect.hide(); +			break; +		case kDeathScreenMainMenu: +			_largeSelect.moveElementTo(kMainMenuSelectLeft, kMainMenuSelectTop); +			_largeSelect.show(); +			_smallSelect.hide(); +			break; +		} +	} +} + +void DeathMenu::drawAllScores() { +	Surface numbers; +	numbers.getImageFromPICTFile("Images/Death Screens/Numbers.pict"); + +	Common::Rect scoreBounds; +	GameScoreType caldoriaTotal = 0; + +	switch (_deathReason) { +	case kDeathCardBomb: +	case kDeathShotBySinclair: +	case kDeathSinclairShotDelegate: +	case kDeathNuclearExplosion: +	case kDeathGassedInNorad: +	case kDeathWokeUpNorad: +	case kDeathArrestedInNorad: +	case kDeathSubDestroyed: +	case kDeathRobotThroughNoradDoor: +	case kDeathRobotSubControlRoom: +	case kDeathWrongShuttleLock: +	case kDeathArrestedInMars: +	case kDeathRunOverByPod: +	case kDeathDidntGetOutOfWay: +	case kDeathReactorBurn: +	case kDeathDidntFindMarsBomb: +	case kDeathDidntDisarmMarsBomb: +	case kDeathNoMaskInMaze: +	case kDeathNoAirInMaze: +	case kDeathGroundByMazebot: +	case kDeathMissedOreBucket: +	case kDeathDidntLeaveBucket: +	case kDeathRanIntoCanyonWall: +	case kDeathRanIntoSpaceJunk: +	case kDeathDidntStopPoison: +	case kDeathArrestedInWSC: +	case kDeathHitByPlasma: +	case kDeathShotOnCatwalk: +	case kPlayerWonGame: +		caldoriaTotal += kMaxCaldoriaTSAScoreAfter; +		scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5, +				kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5 + kDeathScreenScoreHeight); +		drawScore(GameState.getGandhiScore(), kMaxGandhiScore, scoreBounds, &numbers); + +		scoreBounds.translate(0, kDeathScreenScoreSkipVert); +		drawScore(GameState.getWSCScore(), kMaxWSCScore, scoreBounds, &numbers); + +		scoreBounds.translate(0, kDeathScreenScoreSkipVert); +		drawScore(GameState.getNoradScore(), kMaxNoradScore, scoreBounds, &numbers); + +		scoreBounds.translate(0, kDeathScreenScoreSkipVert); +		drawScore(GameState.getMarsScore(), kMaxMarsScore, scoreBounds, &numbers); +		// fall through +	case kDeathFallOffCliff: +	case kDeathEatenByDinosaur: +	case kDeathStranded: +	case kDeathShotByTSARobots: +		scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert, +				kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert + kDeathScreenScoreHeight); +		drawScore(GameState.getPrehistoricScore(), kMaxPrehistoricScore, scoreBounds, &numbers); +		// fall through +	case kDeathUncreatedInCaldoria: +	case kDeathUncreatedInTSA: +		scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop, kDeathScreenScoreLeft + kDeathScreenScoreWidth, +				kDeathScreenScoreTop + kDeathScreenScoreHeight); +		caldoriaTotal += kMaxCaldoriaTSAScoreBefore; +		drawScore(GameState.getCaldoriaTSAScore(), caldoriaTotal, scoreBounds, &numbers); + +		scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6, +				kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6 + kDeathScreenScoreHeight); + +		drawScore(GameState.getTotalScore(), kMaxTotalScore, scoreBounds, &numbers); +		break; +	} +} + +enum { +	kPauseMenuSave, +	kPauseMenuContinue, +	kPauseMenuRestore, +	kPauseMenuSoundFX, +	kPauseMenuAmbience, +	kPauseMenuWalkthru, +	kPauseMenuQuitToMainMenu, +	 +	kFirstPauseSelection = kPauseMenuSave, +	kLastPauseSelection = kPauseMenuQuitToMainMenu +}; + +static const CoordType kPauseLeft = 194; +static const CoordType kPauseTop = 68; + +static const CoordType kSaveGameLeft = kPauseLeft + 6; +static const CoordType kSaveGameTop = kPauseTop + 56; + +static const CoordType kSaveGameSelectLeft = kPauseLeft - 44; +static const CoordType kSaveGameSelectTop = kPauseTop + 52; + +static const CoordType kPauseContinueLeft = kPauseLeft + 18; +static const CoordType kPauseContinueTop = kPauseTop + 100; + +static const CoordType kPauseContinueSelectLeft = kPauseLeft - 44; +static const CoordType kPauseContinueSelectTop = kPauseTop + 95; + +static const CoordType kPauseRestoreLeft = kPauseLeft + 18; +static const CoordType kPauseRestoreTop = kPauseTop + 136; + +static const CoordType kPauseRestoreSelectLeft = kPauseLeft - 44; +static const CoordType kPauseRestoreSelectTop = kPauseTop + 131; + +static const CoordType kSoundFXLeft = kPauseLeft + 128; +static const CoordType kSoundFXTop = kPauseTop + 187; +static const CoordType kSoundFXRight = kSoundFXLeft + 96; +static const CoordType kSoundFXBottom = kSoundFXTop + 14; + +static const CoordType kSoundFXSelectLeft = kPauseLeft - 44; +static const CoordType kSoundFXSelectTop = kPauseTop + 172; + +static const CoordType kAmbienceLeft = kPauseLeft + 128; +static const CoordType kAmbienceTop = kPauseTop + 227; +static const CoordType kAmbienceRight = kAmbienceLeft + 96; +static const CoordType kAmbienceBottom = kAmbienceTop + 14; + +static const CoordType kAmbienceSelectLeft = kPauseLeft - 44; +static const CoordType kAmbienceSelectTop = kPauseTop + 212; + +static const CoordType kWalkthruLeft = kPauseLeft + 128; +static const CoordType kWalkthruTop = kPauseTop + 264; + +static const CoordType kWalkthruSelectLeft = kPauseLeft - 44; +static const CoordType kWalkthruSelectTop = kPauseTop + 255; + +static const CoordType kQuitLeft = kPauseLeft + 18; +static const CoordType kQuitTop = kPauseTop + 302; + +static const CoordType kQuitSelectLeft = kPauseLeft - 44; +static const CoordType kQuitSelectTop = kPauseTop + 297; + +// These are relative to the pause background. +static const CoordType kPauseScoreLeft = 130; +static const CoordType kPauseScoreTop = 34; +static const CoordType kPauseScoreRight = kPauseScoreLeft + 108; +static const CoordType kPauseScoreBottom = kPauseScoreTop + 12; + +// Never set the current input handler to the CPauseMenu. +PauseMenu::PauseMenu() : GameMenu(kPauseMenuID), _pauseBackground(0), _saveButton(0), _restoreButton(0), +		_walkthroughButton(0), _continueButton(0), _soundFXLevel(0), _ambienceLevel(0), _quitButton(0), +		_largeSelect(0), _smallSelect(0) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	_pauseBackground.initFromPICTFile("Images/Pause Screen/PausScrn.pict", true); + +	if (!vm->isDemo()) { +		Surface numbers; +		numbers.getImageFromPICTFile("Images/Pause Screen/Numbers.pict"); +		vm->_gfx->setCurSurface(_pauseBackground.getSurface()); +		drawScore(GameState.getTotalScore(), kMaxTotalScore, +				Common::Rect(kPauseScoreLeft, kPauseScoreTop, kPauseScoreRight, kPauseScoreBottom), &numbers); +		vm->_gfx->setCurSurface(vm->_gfx->getWorkArea()); +	} + +	_pauseBackground.setDisplayOrder(kPauseMenuOrder); +	_pauseBackground.moveElementTo(kPauseLeft, kPauseTop); +	_pauseBackground.startDisplaying(); +	_pauseBackground.show(); + +	if (!vm->isDemo()) { +		_saveButton.initFromPICTFile("Images/Pause Screen/SaveGame.pict"); +		_saveButton.setDisplayOrder(kSaveGameOrder); +		_saveButton.moveElementTo(kSaveGameLeft, kSaveGameTop); +		_saveButton.startDisplaying(); + +		_restoreButton.initFromPICTFile("Images/Pause Screen/Restore.pict"); +		_restoreButton.setDisplayOrder(kRestoreOrder); +		_restoreButton.moveElementTo(kPauseRestoreLeft, kPauseRestoreTop); +		_restoreButton.startDisplaying(); + +		_walkthroughButton.initFromPICTFile("Images/Pause Screen/Walkthru.pict"); +		_walkthroughButton.setDisplayOrder(kWalkthruOrder); +		_walkthroughButton.moveElementTo(kWalkthruLeft, kWalkthruTop); +		_walkthroughButton.startDisplaying(); + +		if (GameState.getWalkthroughMode()) +			_walkthroughButton.show(); +	} + +	_continueButton.initFromPICTFile("Images/Pause Screen/Continue.pict"); +	_continueButton.setDisplayOrder(kContinueOrder); +	_continueButton.moveElementTo(kPauseContinueLeft, kPauseContinueTop); +	_continueButton.startDisplaying(); + +	_soundFXLevel.setDisplayOrder(kSoundFXOrder); +	_soundFXLevel.setBounds(Common::Rect(kSoundFXLeft, kSoundFXTop, kSoundFXRight, kSoundFXBottom)); +	_soundFXLevel.startDisplaying(); +	_soundFXLevel.show(); +	_soundFXLevel.setSoundLevel(vm->getSoundFXLevel()); + +	_ambienceLevel.setDisplayOrder(kAmbienceOrder); +	_ambienceLevel.setBounds(Common::Rect(kAmbienceLeft, kAmbienceTop, kAmbienceRight, kAmbienceBottom)); +	_ambienceLevel.startDisplaying(); +	_ambienceLevel.show(); +	_ambienceLevel.setSoundLevel(vm->getAmbienceLevel()); + +	_quitButton.initFromPICTFile("Images/Pause Screen/Quit2MM.pict"); +	_quitButton.setDisplayOrder(kQuitToMainMenuOrder); +	_quitButton.moveElementTo(kQuitLeft, kQuitTop); +	_quitButton.startDisplaying(); + +	_largeSelect.initFromPICTFile("Images/Pause Screen/SelectL.pict", true); +	_largeSelect.setDisplayOrder(kPauseLargeHiliteOrder); +	_largeSelect.startDisplaying(); + +	_smallSelect.initFromPICTFile("Images/Pause Screen/SelectS.pict", true); +	_smallSelect.setDisplayOrder(kPauseSmallHiliteOrder); +	_smallSelect.startDisplaying(); + +	_menuSelection = (vm->isDemo()) ? kPauseMenuContinue : kPauseMenuSave; + +	updateDisplay(); +} + +void PauseMenu::handleInput(const Input &input, const Hotspot *cursorSpot) { +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	if (input.upButtonDown()) { +		if (vm->isDemo()) { +			if (_menuSelection > kPauseMenuContinue) { +				switch (_menuSelection) { +				case kPauseMenuSoundFX: +					_menuSelection = kPauseMenuContinue; +					break; +				case kPauseMenuAmbience: +					_menuSelection = kPauseMenuSoundFX; +					break; +				case kPauseMenuQuitToMainMenu: +					_menuSelection = kPauseMenuAmbience; +					break; +				} +				updateDisplay(); +			} +		} else { +			if (_menuSelection > kFirstPauseSelection) { +				_menuSelection--; +				updateDisplay(); +			} +		} +	} else if (input.downButtonDown()) { +		if (vm->isDemo()) { +			if (_menuSelection < kPauseMenuQuitToMainMenu) { +				switch (_menuSelection) { +				case kPauseMenuContinue: +					_menuSelection = kPauseMenuSoundFX; +					break; +				case kPauseMenuSoundFX: +					_menuSelection = kPauseMenuAmbience; +					break; +				case kPauseMenuAmbience: +					_menuSelection = kPauseMenuQuitToMainMenu; +					break; +				} +				updateDisplay(); +			} +		} else { +			if (_menuSelection < kLastPauseSelection) { +				_menuSelection++; +				updateDisplay(); +			} +		} +	} else if (input.leftButtonDown()) { +		if (_menuSelection == kPauseMenuSoundFX) { +			_soundFXLevel.decrementLevel(); +			vm->setSoundFXLevel(_soundFXLevel.getSoundLevel()); +		} else if (_menuSelection == kPauseMenuAmbience) { +			_ambienceLevel.decrementLevel(); +			vm->setAmbienceLevel(_ambienceLevel.getSoundLevel()); +		} else if (!vm->isDemo() && _menuSelection == kPauseMenuWalkthru) { +			GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); +			if (GameState.getWalkthroughMode()) +				_walkthroughButton.show(); +			else +				_walkthroughButton.hide(); +		} +	} else if (input.rightButtonDown()) { +		if (_menuSelection == kPauseMenuSoundFX) { +			_soundFXLevel.incrementLevel(); +			vm->setSoundFXLevel(_soundFXLevel.getSoundLevel()); +		} else if (_menuSelection == kPauseMenuAmbience) { +			_ambienceLevel.incrementLevel(); +			vm->setAmbienceLevel(_ambienceLevel.getSoundLevel()); +		} else if (!vm->isDemo() && _menuSelection == kPauseMenuWalkthru) { +			GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); +			if (GameState.getWalkthroughMode()) +				_walkthroughButton.show(); +			else +				_walkthroughButton.hide(); +		} +	} else if (JMPPPInput::isMenuButtonPressInput(input)) { +		switch (_menuSelection) { +		case kPauseMenuSave: +			_saveButton.show(); +			vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +			_saveButton.hide(); +			setLastCommand(kMenuCmdPauseSave); +			break; +		case kPauseMenuRestore: +			_restoreButton.show(); +			vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +			_restoreButton.hide(); +			setLastCommand(kMenuCmdPauseRestore); +			break; +		case kPauseMenuContinue: +			_continueButton.show(); +			vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +			_continueButton.hide(); +			setLastCommand(kMenuCmdPauseContinue); +			break; +		case kPauseMenuWalkthru: +			GameState.setWalkthroughMode(!GameState.getWalkthroughMode()); +			if (GameState.getWalkthroughMode()) +				_walkthroughButton.show(); +			else +				_walkthroughButton.hide(); +			break; +		case kPauseMenuQuitToMainMenu: +			_quitButton.show(); +			vm->delayShell(kMenuButtonHiliteTime, kMenuButtonHiliteScale); +			_quitButton.hide(); +			setLastCommand(kMenuCmdPauseQuit); +			break; +		} +	} + +	InputHandler::handleInput(input, cursorSpot); +} + +void PauseMenu::updateDisplay() { +	switch (_menuSelection) { +	case kPauseMenuSave: +		_largeSelect.moveElementTo(kSaveGameSelectLeft, kSaveGameSelectTop); +		_largeSelect.show(); +		_smallSelect.hide(); +		break; +	case kPauseMenuContinue: +		_smallSelect.moveElementTo(kPauseContinueSelectLeft, kPauseContinueSelectTop); +		_smallSelect.show(); +		_largeSelect.hide(); +		break; +	case kPauseMenuRestore: +		_smallSelect.moveElementTo(kPauseRestoreSelectLeft, kPauseRestoreSelectTop); +		_smallSelect.show(); +		_largeSelect.hide(); +		break; +	case kPauseMenuSoundFX: +		_largeSelect.moveElementTo(kSoundFXSelectLeft, kSoundFXSelectTop); +		_largeSelect.show(); +		_smallSelect.hide(); +		break; +	case kPauseMenuAmbience: +		_largeSelect.moveElementTo(kAmbienceSelectLeft, kAmbienceSelectTop); +		_largeSelect.show(); +		_smallSelect.hide(); +		break; +	case kPauseMenuWalkthru: +		_largeSelect.moveElementTo(kWalkthruSelectLeft, kWalkthruSelectTop); +		_largeSelect.show(); +		_smallSelect.hide(); +		break; +	case kPauseMenuQuitToMainMenu: +		_smallSelect.moveElementTo(kQuitSelectLeft, kQuitSelectTop); +		_smallSelect.show(); +		_largeSelect.hide(); +		break; +	} + +	((PegasusEngine *)g_engine)->resetIntroTimer(); +} + + +} // End of namespace Pegasus diff --git a/engines/pegasus/menu.h b/engines/pegasus/menu.h new file mode 100644 index 0000000000..8b0169adaa --- /dev/null +++ b/engines/pegasus/menu.h @@ -0,0 +1,171 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_MENU_H +#define PEGASUS_MENU_H + +#include "pegasus/constants.h" +#include "pegasus/fader.h" +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/sound.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class GameMenu : public IDObject, public InputHandler { +public: +	GameMenu(const uint32); +	virtual ~GameMenu() {} +	 +	virtual void becomeCurrentHandler(); +	virtual void restorePreviousHandler(); +	 +	GameMenuCommand getLastCommand() { return _lastCommand; } +	void clearLastCommand() { _lastCommand = kMenuCmdNoCommand; } + +protected: +	void setLastCommand(const GameMenuCommand command) { _lastCommand = command; } + +	InputHandler *_previousHandler; +	GameMenuCommand _lastCommand; + +	void drawScore(GameScoreType, GameScoreType, const Common::Rect &, Surface *); + +private: +	void drawNumber(GameScoreType, CoordType &, CoordType, Surface *); +}; + +class Hotspot; + +class MainMenu : public GameMenu { +public: +	MainMenu(); +	virtual ~MainMenu(); + +	virtual void handleInput(const Input &input, const Hotspot *); +	void startMainMenuLoop(); +	void stopMainMenuLoop(); + +protected: +	void updateDisplay(); + +	uint32 _menuSelection; + +	// Full and Demo +	Picture _menuBackground; +	Picture _startButton; +	Picture _creditsButton; +	Picture _quitButton; +	Picture _largeSelect; +	Picture _smallSelect; + +	// Full only +	bool _adventureMode; +	Picture _overviewButton; +	Picture _restoreButton; +	Picture _adventureButton; +	Picture _walkthroughButton; + +	Sound _menuLoop; +	SoundFader _menuFader; +}; + +class CreditsMenu : public GameMenu { +public: +	CreditsMenu(); +	virtual ~CreditsMenu() {} +	 +	virtual void handleInput(const Input &input, const Hotspot *); + +protected: +	void newMenuSelection(const int); +	void newMovieTime(const TimeValue); +	 +	int _menuSelection; +	Picture _menuBackground; +	Movie _creditsMovie; +	Picture _mainMenuButton; +	Picture _largeSelect; +	Picture _smallSelect; +}; + +class DeathMenu : public GameMenu { +public: +	DeathMenu(const DeathReason); +	virtual ~DeathMenu() {} + +	virtual void handleInput(const Input &input, const Hotspot *); + +	bool playerWon() { return _playerWon; } + +protected: +	void drawAllScores(); + +	void updateDisplay(); + +	bool _playerWon; +	int _menuSelection; +	DeathReason _deathReason; + +	Picture _deathBackground; +	Picture _continueButton; +	Picture _restoreButton; +	Picture _mainMenuButton; +	Picture _quitButton; + +	Picture _largeSelect; +	Picture _smallSelect; + +	Sound _triumphSound; +}; + +class PauseMenu : public GameMenu { +public: +	PauseMenu(); +	virtual ~PauseMenu() {} + +	virtual void handleInput(const Input &input, const Hotspot *); + +protected: +	void updateDisplay(); + +	uint32 _menuSelection; +	Picture _pauseBackground; +	Picture _saveButton; +	Picture _restoreButton; +	Picture _walkthroughButton; +	Picture _continueButton; +	SoundLevel _soundFXLevel; +	SoundLevel _ambienceLevel; +	Picture _quitButton; +	Picture _largeSelect; +	Picture _smallSelect; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk new file mode 100644 index 0000000000..cb44a04171 --- /dev/null +++ b/engines/pegasus/module.mk @@ -0,0 +1,100 @@ +MODULE := engines/pegasus + +MODULE_OBJS = \ +	compass.o \ +	console.o \ +	cursor.o \ +	detection.o \ +	elements.o \ +	energymonitor.o \ +	fader.o \ +	gamestate.o \ +	graphics.o \ +	hotspot.o \ +	input.o \ +	interface.o \ +	menu.o \ +	movie.o \ +	notification.o \ +	pegasus.o \ +	sound.o \ +	surface.o \ +	timers.o \ +	transition.o \ +	util.o \ +	ai/ai_action.o \ +	ai/ai_area.o \ +	ai/ai_condition.o \ +	ai/ai_rule.o \ +	items/autodragger.o \ +	items/inventory.o \ +	items/inventorypicture.o \ +	items/item.o \ +	items/itemdragger.o \ +	items/itemlist.o \ +	items/biochips/aichip.o \ +	items/biochips/biochipitem.o \ +	items/biochips/mapchip.o \ +	items/biochips/mapimage.o \ +	items/biochips/opticalchip.o \ +	items/biochips/pegasuschip.o \ +	items/biochips/retscanchip.o \ +	items/biochips/shieldchip.o \ +	items/inventory/airmask.o \ +	items/inventory/gascanister.o \ +	items/inventory/inventoryitem.o \ +	items/inventory/keycard.o \ +	neighborhood/door.o \ +	neighborhood/exit.o \ +	neighborhood/extra.o \ +	neighborhood/hotspotinfo.o \ +	neighborhood/neighborhood.o \ +	neighborhood/spot.o \ +	neighborhood/turn.o \ +	neighborhood/view.o \ +	neighborhood/zoom.o \ +	neighborhood/caldoria/caldoria.o \ +	neighborhood/caldoria/caldoria4dsystem.o \ +	neighborhood/caldoria/caldoriabomb.o \ +	neighborhood/caldoria/caldoriamessages.o \ +	neighborhood/caldoria/caldoriamirror.o \ +	neighborhood/mars/energybeam.o \ +	neighborhood/mars/gravitoncannon.o \ +	neighborhood/mars/hermite.o \ +	neighborhood/mars/mars.o \ +	neighborhood/mars/planetmover.o \ +	neighborhood/mars/reactor.o \ +	neighborhood/mars/robotship.o \ +	neighborhood/mars/shuttleenergymeter.o \ +	neighborhood/mars/shuttlehud.o \ +	neighborhood/mars/shuttleweapon.o \ +	neighborhood/mars/spacechase3d.o \ +	neighborhood/mars/spacejunk.o \ +	neighborhood/mars/tractorbeam.o \ +	neighborhood/norad/norad.o \ +	neighborhood/norad/noradelevator.o \ +	neighborhood/norad/pressuredoor.o \ +	neighborhood/norad/pressuretracker.o \ +	neighborhood/norad/subcontrolroom.o \ +	neighborhood/norad/subplatform.o \ +	neighborhood/norad/alpha/ecrmonitor.o \ +	neighborhood/norad/alpha/fillingstation.o \ +	neighborhood/norad/alpha/noradalpha.o \ +	neighborhood/norad/alpha/panorama.o \ +	neighborhood/norad/alpha/panoramascroll.o \ +	neighborhood/norad/delta/globegame.o \ +	neighborhood/norad/delta/noraddelta.o \ +	neighborhood/prehistoric/prehistoric.o \ +	neighborhood/tsa/fulltsa.o \ +	neighborhood/tsa/tinytsa.o \ +	neighborhood/wsc/moleculebin.o \ +	neighborhood/wsc/wsc.o + + +# This module can be built as a plugin +ifeq ($(ENABLE_PEGASUS), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/pegasus/movie.cpp b/engines/pegasus/movie.cpp new file mode 100644 index 0000000000..9a13864cab --- /dev/null +++ b/engines/pegasus/movie.cpp @@ -0,0 +1,250 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" +#include "graphics/surface.h" +#include "video/qt_decoder.h" +#include "video/video_decoder.h" + +#include "pegasus/movie.h" + +namespace Pegasus { + +Movie::Movie(const DisplayElementID id) : Animation(id) { +	_video = 0; +	setScale(600); +} + +Movie::~Movie() { +	releaseMovie(); +} + +// *** Make sure this will stop displaying the movie. + +void Movie::releaseMovie() { +	if (_video) { +		delete _video; +		_video = 0; +		disposeAllCallBacks(); +		deallocateSurface(); +	} + +	setBounds(Common::Rect(0, 0, 0, 0)); +} + +void Movie::initFromMovieFile(const Common::String &fileName, bool transparent) { +	_transparent = transparent; + +	releaseMovie(); +	_video = new Video::QuickTimeDecoder(); +	if (!_video->loadFile(fileName)) { +		// Replace any colon with an underscore, since only Mac OS X +		// supports that. See PegasusEngine::detectOpeningClosingDirectory() +		// for more info. +		Common::String newName(fileName); +		if (newName.contains(':')) +			for (uint i = 0; i < newName.size(); i++) +				if (newName[i] == ':') +					newName.setChar('_', i); + +		if (!_video->loadFile(newName)) +			error("Could not load video '%s'", fileName.c_str()); +	} + +	_video->pauseVideo(true); + +	Common::Rect bounds(0, 0, _video->getWidth(), _video->getHeight()); +	sizeElement(_video->getWidth(), _video->getHeight()); +	_movieBox = bounds; + +	if (!isSurfaceValid()) +		allocateSurface(bounds); + +	setStart(0, getScale()); +	setStop(_video->getDuration() * getScale() / 1000, getScale()); +} + +void Movie::redrawMovieWorld() { +	if (_video && _video->needsUpdate()) { +		const Graphics::Surface *frame = _video->decodeNextFrame(); + +		if (!frame) +			return; + +		// Copy to the surface using _movieBox +		uint16 width = MIN<int>(frame->w, _movieBox.width()); +		uint16 height = MIN<int>(frame->h, _movieBox.height()); + +		for (uint16 y = 0; y < height; y++) +			memcpy((byte *)_surface->getBasePtr(_movieBox.left, _movieBox.top + y), (const byte *)frame->getBasePtr(0, y), width * frame->format.bytesPerPixel); + +		triggerRedraw(); +	} +} + +void Movie::draw(const Common::Rect &r) {	 +	Common::Rect worldBounds = _movieBox; +	Common::Rect elementBounds; +	getBounds(elementBounds); + +	worldBounds.moveTo(elementBounds.left, elementBounds.top); +	Common::Rect r1 = r.findIntersectingRect(worldBounds); + +	Common::Rect r2 = r1; +	r2.translate(_movieBox.left - elementBounds.left, _movieBox.top - elementBounds.top); +	drawImage(r2, r1); +} + +void Movie::moveMovieBoxTo(const CoordType h, const CoordType v) { +	_movieBox.moveTo(h, v); +} + +void Movie::setVolume(uint16 volume) { +	// TODO +} + +void Movie::setTime(const TimeValue time, const TimeScale scale) { +	if (_video) { +		// Don't go past the ends of the movie +		Common::Rational timeFrac = Common::Rational(time, ((scale == 0) ? getScale() : scale)); + +		if (timeFrac < Common::Rational(_startTime, _startScale)) +			timeFrac = Common::Rational(_startTime, _startScale); +		else if (timeFrac >= Common::Rational(_stopTime, _stopScale)) +			return; + +		_video->seekToTime(Audio::Timestamp(0, timeFrac.getNumerator(), timeFrac.getDenominator())); +		_time = timeFrac; +		_lastMillis = 0; +	} +} + +void Movie::setRate(const Common::Rational rate) { +	if (rate != 1 && rate != 0) { +		warning("Cannot set movie rate"); +		start(); +		return; +	} + +	TimeBase::setRate(rate); +} + +void Movie::start() { +	if (_video && _video->isPaused()) +		_video->pauseVideo(false); + +	TimeBase::start(); +} + +void Movie::stop() { +	if (_video && !_video->isPaused()) +		_video->pauseVideo(true); + +	TimeBase::stop(); +} + +void Movie::resume() { +	if (_video) +		_video->pauseVideo(false); + +	TimeBase::resume(); +} + +void Movie::pause() { +	if (_video) +		_video->pauseVideo(true); + +	TimeBase::pause(); +} + +TimeValue Movie::getDuration(const TimeScale scale) const { +	// Unlike TimeBase::getDuration(), this returns the whole duration of the movie +	// The original source has a TODO to make this behave like TimeBase::getDuration(), +	// but the problem is that too much code requires this function to behave this way... + +	if (_video) +		return _video->getDuration() * ((scale == 0) ? getScale() : scale) / 1000; + +	return 0; +} + +void Movie::updateTime() { +	// The reason why we overrode TimeBase's updateTime(): +	// Again, avoiding timers and handling it here +	if (_video && !_video->isPaused()) { +		redrawMovieWorld(); + +		uint32 startTime = _startTime * getScale() / _startScale; +		uint32 stopTime = _stopTime * getScale() / _stopScale; +		uint32 actualTime = CLIP<int>(_video->getElapsedTime() * getScale() / 1000, startTime, stopTime); +		_time = Common::Rational(actualTime, getScale()); +	} +} + +GlowingMovie::GlowingMovie(const DisplayElementID id) : Movie(id) { +	_glowing = false; +} + +void GlowingMovie::draw(const Common::Rect &r) { +	// Make sure the rectangles are clipped properly, OR guarantee that _bounds will +	// never fall off the screen. +	if (_glowing) { +		Common::Rect bounds; +		getBounds(bounds); + +		copyToCurrentPortTransparentGlow(_movieBox, bounds); +	} else { +		Movie::draw(r); +	} +} + +void GlowingMovie::setBounds(const Common::Rect &r) { +	Common::Rect bounds; +	getBounds(bounds); + +	if (r != bounds) { +		// Avoid Movie::setBounds. +		// clone2727 asks why, but goes along with it +		Animation::setBounds(r); +	} +} + +ScalingMovie::ScalingMovie(const DisplayElementID id) : GlowingMovie(id) { +} + +void ScalingMovie::draw(const Common::Rect &) { +	// Make sure the rectangles are clipped properly, OR guarantee that _bounds will +	// never fall off the screen. + +	Common::Rect bounds; +	getBounds(bounds); + +	if (_glowing) +		scaleTransparentCopyGlow(_movieBox, bounds); +	else +		scaleTransparentCopy(_movieBox, bounds); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/movie.h b/engines/pegasus/movie.h new file mode 100644 index 0000000000..593442fa44 --- /dev/null +++ b/engines/pegasus/movie.h @@ -0,0 +1,103 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_MOVIE_H +#define PEGASUS_MOVIE_H + +#include "common/str.h" + +#include "pegasus/elements.h" +#include "pegasus/surface.h" + +namespace Video { +	class SeekableVideoDecoder; +} + +namespace Pegasus { + +class Movie : public Animation, public PixelImage { +public: +	Movie(const DisplayElementID); +	virtual ~Movie(); + +	virtual void initFromMovieFile(const Common::String &fileName, bool transparent = false); + +	bool isMovieValid() { return _video != 0; } + +	virtual void releaseMovie(); + +	virtual void draw(const Common::Rect &); +	virtual void redrawMovieWorld(); + +	virtual void setTime(const TimeValue, const TimeScale = 0); + +	virtual void setRate(const Common::Rational); + +	virtual void start(); +	virtual void stop(); +	virtual void resume(); +	virtual void pause(); + +	virtual void moveMovieBoxTo(const CoordType, const CoordType); + +	virtual TimeValue getDuration(const TimeScale = 0) const; + +	// *** HACK ALERT +	Video::SeekableVideoDecoder *getMovie() { return _video; } +	void setVolume(uint16); + +protected: +	void updateTime(); + +	Video::SeekableVideoDecoder *_video; +	Common::Rect _movieBox; +}; + +class GlowingMovie : public Movie { +public: +	GlowingMovie(DisplayElementID); +	virtual ~GlowingMovie() {} + +	virtual void draw(const Common::Rect &); + +	void setBounds(const Common::Rect &); + +	void setGlowing(const bool glowing) { _glowing = glowing; } + +protected: +	bool _glowing; +}; + +class ScalingMovie : public GlowingMovie { +public: +	ScalingMovie(DisplayElementID); +	virtual ~ScalingMovie() {} + +	virtual void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.cpp b/engines/pegasus/neighborhood/caldoria/caldoria.cpp new file mode 100644 index 0000000000..dedfd88aa8 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria.cpp @@ -0,0 +1,1962 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" +#include "video/qt_decoder.h" + +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoria4dsystem.h" +#include "pegasus/neighborhood/caldoria/caldoriabomb.h" +#include "pegasus/neighborhood/caldoria/caldoriamessages.h" +#include "pegasus/neighborhood/caldoria/caldoriamirror.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" + +namespace Pegasus { + +static const int16 kVidPhoneAngle = 30; +static const int16 kReplicatorAngle = 50; +static const int16 kDrawersAngle = -30; +static const int16 kCaldoria53Angle = 45; +static const int16 kCaldoria55Angle = -45; + +static const TimeValue kSinclairInterruptionTime1 = 2955; +static const TimeValue kSinclairInterruptionTime2 = 6835; +static const TimeValue kSinclairInterruptionTime3 = 9835; +static const TimeValue kSinclairInterruptionTime4 = 12555; + +static const InputBits kPullbackInterruptFilter = kFilterAllInput; +static const InputBits kRecalibrationInterruptFilter = kFilterAllInput; + +static const TimeValue kCaldoriaReplicatorIntroIn = 4933; +static const TimeValue kCaldoriaReplicatorIntroOut = 6557; + +static const TimeValue kCaldoriaReplicatorWrongChoiceIn = 6557; +static const TimeValue kCaldoriaReplicatorWrongChoiceOut = 8586; + +static const TimeValue kCaldoriaReplicatorOJChoiceIn = 8586; +static const TimeValue kCaldoriaReplicatorOJChoiceOut = 11687; + +static const TimeValue kCaldoriaMessagesIntroIn = 11687; +static const TimeValue kCaldoriaMessagesIntroOut = 13641; + +static const TimeValue kCaldoriaFirstMessageIn = 13641; +static const TimeValue kCaldoriaFirstMessageOut = 14203; + +static const TimeValue kCaldoriaSecondMessageIn = 14203; +static const TimeValue kCaldoriaSecondMessageOut = 14750; + +static const TimeValue kCaldoriaDoorCloseIn = 14750; +static const TimeValue kCaldoriaDoorCloseOut = 15472; + +static const TimeValue kCaldoriaElevatorCloseIn = 15472; +static const TimeValue kCaldoriaElevatorCloseOut = 16336; + +static const TimeValue kCaldoriaShowerCloseIn = 16336; +static const TimeValue kCaldoriaShowerCloseOut = 17101; + +static const TimeValue kCaldoriaGTDoorCloseIn = 17101; +static const TimeValue kCaldoriaGTDoorCloseOut = 18523; + +static const TimeValue kCaldoriaNobodyHomeIn = 18523; +static const TimeValue kCaldoriaNobodyHomeOut = 21469; + +static const TimeValue kCaldoriaNoOtherFloorIn = 21469; +static const TimeValue kCaldoriaNoOtherFloorOut = 28013; + +static const TimeValue kCaldoria4DInstructionsIn = 28013; +static const TimeValue kCaldoria4DInstructionsOut = 29730; + +static const TimeValue kCaldoriaDrinkOJIn = 33910; +static const TimeValue kCaldoriaDrinkOJOut = 35846; + +static const TimeValue kCaldoriaNoOtherDestinationIn = 35846; +static const TimeValue kCaldoriaNoOtherDestinationOut = 37877; + +static const TimeValue kCaldoriaUhghIn = 37877; +static const TimeValue kCaldoriaUhghOut = 38025; + +static const TimeValue kCaldoriaSinclairShootsOSIn = 38025; +static const TimeValue kCaldoriaSinclairShootsOSOut = 40649; + +static const TimeValue kCaldoriaScreamingAfterIn = 40649; +static const TimeValue kCaldoriaScreamingAfterOut = 47661; + +static const TimeValue k4FloorTime = 0; + +static const TimeValue k4To1Start = 40; +static const TimeValue k4To1Stop = 7720; + +static const TimeValue k4To5Start = 7720; +static const TimeValue k4To5Stop = 10280; + +static const TimeValue k4To2Time = 10280; + +static const TimeValue k4To3Time = 10320; + +static const TimeValue k1FloorTime = 10360; + +static const TimeValue k1To4Start = 10400; +static const TimeValue k1To4Stop = 18080; + +static const TimeValue k1To5Start = 18080; +static const TimeValue k1To5Stop = 28320; + +static const TimeValue k1To2Time = 28320; + +static const TimeValue k1To3Time = 28360; + +static const TimeValue k5FloorTime = 28400; + +static const TimeValue k5To1Start = 28440; +static const TimeValue k5To1Stop = 38680; + +static const TimeValue k5To4Start = 38680; +static const TimeValue k5To4Stop = 41240; + +static const TimeValue k5To2Time = 41240; + +static const TimeValue k5To3Time = 41280; + +// FuseFunction functions... + +const NotificationFlags kSinclairLoopDoneFlag = kLastNeighborhoodNotificationFlag << 1; + +void doorBombTimerExpiredFunction(FunctionPtr *, void *caldoria) { +	((Caldoria *)caldoria)->doorBombTimerExpired(); +} + +void sinclairTimerExpiredFunction(FunctionPtr *, void *caldoria) { +	((Caldoria *)caldoria)->sinclairTimerExpired(); +} + +SinclairCallBack::SinclairCallBack(Caldoria *caldoria) { +	_caldoria = caldoria; +} + +void SinclairCallBack::callBack() { +	_caldoria->checkInterruptSinclair(); +} + +Caldoria::Caldoria(InputHandler* nextHandler, PegasusEngine *owner) +		: Neighborhood(nextHandler, owner, "Caldoria", kCaldoriaID), _sinclairInterrupt(this) { +	setIsItemTaken(kKeyCard); +	setIsItemTaken(kOrangeJuiceGlassEmpty); +	GameState.setTakenItemID(kOrangeJuiceGlassFull, GameState.isTakenItemID(kOrangeJuiceGlassEmpty)); +	_zoomOutSpot = 0; +	_gunSprite = 0; +} + +Caldoria::~Caldoria() { +	_sinclairInterrupt.releaseCallBack(); +} + +void Caldoria::init() { +	Neighborhood::init(); + +	// We need this notification flag as well. +	_neighborhoodNotification.notifyMe(this, kSinclairLoopDoneFlag, kSinclairLoopDoneFlag); + +	_sinclairInterrupt.initCallBack(&_navMovie, kCallBackAtTime); + +	forceStridingStop(kCaldoria55, kSouth, kAltCaldoriaSinclairDown); +	forceStridingStop(kCaldoria50, kNorth, kAltCaldoriaSinclairDown); +} + +void Caldoria::start() {	 +	g_energyMonitor->stopEnergyDraining(); + +	if (!GameState.getCaldoriaSeenPullback()) { +		_vm->_gfx->doFadeOutSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond); + +		g_system->delayMillis(2 * 1000); + +		Video::VideoDecoder *pullbackMovie = new Video::QuickTimeDecoder(); + +		if (!pullbackMovie->loadFile("Images/Caldoria/Pullback.movie")) +			error("Could not load pullback movie"); + +		bool skipped = false; +		Input input; + +		_vm->_gfx->doFadeInSync(kTwoSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond); + +		bool saveAllowed = _vm->swapSaveAllowed(false); +		bool openAllowed = _vm->swapLoadAllowed(false); + +		while (!_vm->shouldQuit() && !pullbackMovie->endOfVideo()) { +			if (pullbackMovie->needsUpdate()) { +				const Graphics::Surface *frame = pullbackMovie->decodeNextFrame(); + +				if (frame) { +					g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h); +					g_system->updateScreen(); +				} +			} + +			InputDevice.getInput(input, kPullbackInterruptFilter); +			if (input.anyInput() || _vm->saveRequested() || _vm->loadRequested()) { +				skipped = true; +				break; +			} + +			g_system->delayMillis(10); +		} + +		delete pullbackMovie; + +		_vm->swapSaveAllowed(saveAllowed); +		_vm->swapLoadAllowed(openAllowed); + +		ExtraTable::Entry entry; + +		if (!skipped) { +			uint32 white = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff); + +			_vm->_gfx->doFadeOutSync(kThreeSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond, white); + +			g_system->delayMillis(3 * 1000 / 2); + +			getExtraEntry(kCaldoria00WakeUp1, entry); +			_navMovie.setTime(entry.movieStart); +			_navMovie.redrawMovieWorld(); +			_navMovie.show(); +			_vm->refreshDisplay(); +			_vm->_gfx->doFadeInSync(kOneSecond * kFifteenTicksPerSecond, kFifteenTicksPerSecond, white); +		} else { +			getExtraEntry(kCaldoria00WakeUp1, entry); +			_navMovie.setTime(entry.movieStart); +			_navMovie.redrawMovieWorld(); +			_navMovie.show(); +		} + +		GameState.setCaldoriaSeenPullback(true); +	} + +	Neighborhood::start(); +} + +void Caldoria::flushGameState() { +	GameState.setCaldoriaFuseTimeLimit(_utilityFuse.getTimeRemaining()); +} + +class AIBombActiveCondition : public AICondition { +public: +	AIBombActiveCondition() {} + +	bool fireCondition(); +}; + +// Return true if player is on 53 east and Sinclair is shot. +bool AIBombActiveCondition::fireCondition() { +	return GameState.getCurrentRoom() == kCaldoria53 && GameState.getCurrentDirection() == kEast && +			GameState.getCaldoriaSinclairShot(); +} + +void Caldoria::setUpAIRules() { +	Neighborhood::setUpAIRules(); + +	if (g_AIArea) { +		if (GameState.allTimeZonesFinished()) { +			AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X49NB1", false); +			AILocationCondition *locCondition = new AILocationCondition(1); +			locCondition->addLocation(MakeRoomView(kCaldoria49, kNorth)); +			AIRule *rule = new AIRule(locCondition, messageAction); +			g_AIArea->addAIRule(rule); + +			messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X56EH1", false); +			AIBombActiveCondition *activeCondition = new AIBombActiveCondition(); +			rule = new AIRule(activeCondition, messageAction); +			g_AIArea->addAIRule(rule); +		} else { +			AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XAB2", false); +			AITimerCondition *timerCondition = new AITimerCondition(kLateWarning3TimeLimit, 1, true); +			AILocationCondition *locCondition = new AILocationCondition(1); +			locCondition->addLocation(MakeRoomView(kCaldoria44, kEast)); +			AINotCondition *notCondition = new AINotCondition(locCondition); +			AIAndCondition *andCondition = new AIAndCondition(timerCondition, notCondition); +			AIRule *rule = new AIRule(andCondition, messageAction); +			g_AIArea->addAIRule(rule); + +			messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XAB1", false); +			timerCondition = new AITimerCondition(kLateWarning2TimeLimit, 1, true); +			locCondition = new AILocationCondition(1); +			locCondition->addLocation(MakeRoomView(kCaldoria44, kEast)); +			notCondition = new AINotCondition(locCondition); +			andCondition = new AIAndCondition(timerCondition, notCondition); +			rule = new AIRule(andCondition, messageAction); +			g_AIArea->addAIRule(rule); + +			messageAction = new AIPlayMessageAction("Images/AI/Caldoria/XA44EB", false); +			locCondition = new AILocationCondition(3); +			locCondition->addLocation(MakeRoomView(kCaldoria01, kNorth)); +			locCondition->addLocation(MakeRoomView(kCaldoria01, kEast)); +			locCondition->addLocation(MakeRoomView(kCaldoria01, kSouth)); +			rule = new AIRule(locCondition, messageAction); +			g_AIArea->addAIRule(rule); +			 +			messageAction = new AIPlayMessageAction("Images/AI/Caldoria/X42WH1", false); +			AICondition *condition = makeLocationAndDoesntHaveItemCondition(kCaldoria44, kEast, kKeyCard); +			rule = new AIRule(condition, messageAction); +			g_AIArea->addAIRule(rule); +			 +			AIActivateRuleAction *ruleAction = new AIActivateRuleAction(rule); +			locCondition = new AILocationCondition(1); +			locCondition->addLocation(MakeRoomView(kCaldoria42, kEast)); +			rule = new AIRule(locCondition, ruleAction); +			g_AIArea->addAIRule(rule); +		} + 	} +} + +uint16 Caldoria::getDateResID() const { +	return kDate2318ID; +} + +TimeValue Caldoria::getViewTime(const RoomID room, const DirectionConstant direction) { +	ExtraTable::Entry extra;	 +	uint32 extraID = 0xffffffff; + +	switch (room) { +	case kCaldoria00: +		if (direction == kEast && _privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) +			extraID = k4DEnvironOpenView; +		break; +	case kCaldoriaDrawers: +		if (direction == kNorth && _privateFlags.getFlag(kCaldoriaPrivateRightDrawerOpenFlag)) { +			if (GameState.isTakenItemID(kKeyCard)) +				extraID = kRightDrawerOpenViewNoKeys; +			else +				extraID = kRightDrawerOpenViewWithKeys; +		} +		break; +	case kCaldoria16: +		if (direction == kSouth && GameState.getCaldoriaSeenSinclairInElevator()) +			extraID = kCaldoria16SouthViewWithElevator; +		break; +	case kCaldoriaReplicator: +		if (GameState.getCaldoriaMadeOJ() && !(GameState.isTakenItemID(kOrangeJuiceGlassEmpty) || GameState.isTakenItemID(kOrangeJuiceGlassFull))) +			extraID = kReplicatorNorthViewWithOJ; +		break; +	case kCaldoriaKiosk: +	case kCaldoriaBinoculars: +		return 0xffffffff; +	case kCaldoria48: +		if (direction == kNorth && GameState.getCaldoriaRoofDoorOpen()) +			extraID = kCa48NorthExplosion; +		break; +	} + +	if (extraID == 0xffffffff) +		return Neighborhood::getViewTime(room, direction); + +	getExtraEntry(extraID, extra); +	return extra.movieEnd - 1; +} + +void Caldoria::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kCaldoria13, kEast): +		if (!_privateFlags.getFlag(kCaldoriaPrivateSeen13CarFlag) && _vm->getRandomBit() == 0) { +			_privateFlags.setFlag(kCaldoriaPrivateSeen13CarFlag, true); +			Neighborhood::startSpotOnceOnly(startTime, stopTime); +		} +		break; +	case MakeRoomView(kCaldoria14, kEast): +		if (!_privateFlags.getFlag(kCaldoriaPrivateSeen14CarFlag) && _vm->getRandomBit() == 0) { +			_privateFlags.setFlag(kCaldoriaPrivateSeen14CarFlag, true); +			Neighborhood::startSpotOnceOnly(startTime, stopTime); +		} +		break; +	case MakeRoomView(kCaldoria18, kWest): +		if (!_privateFlags.getFlag(kCaldoriaPrivateSeen18CarFlag) && _vm->getRandomBit() == 0) { +			_privateFlags.setFlag(kCaldoriaPrivateSeen18CarFlag, true); +			Neighborhood::startSpotOnceOnly(startTime, stopTime); +		} +		break; +	case MakeRoomView(kCaldoria23, kSouth): +		if (!_privateFlags.getFlag(kCaldoriaPrivateSeen23CarFlag) && _vm->getRandomBit() == 0) { +			_privateFlags.setFlag(kCaldoriaPrivateSeen23CarFlag, true); +			Neighborhood::startSpotOnceOnly(startTime, stopTime); +		} +		break; +	case MakeRoomView(kCaldoria33, kSouth): +		if (!_privateFlags.getFlag(kCaldoriaPrivateSeen33CarFlag) && _vm->getRandomBit() == 0) { +			_privateFlags.setFlag(kCaldoriaPrivateSeen33CarFlag, true); +			Neighborhood::startSpotOnceOnly(startTime, stopTime); +		} +		break; +	case MakeRoomView(kCaldoria36, kNorth): +		if (!_privateFlags.getFlag(kCaldoriaPrivateSeen36CarFlag) && _vm->getRandomBit() == 0) { +			_privateFlags.setFlag(kCaldoriaPrivateSeen36CarFlag, true); +			Neighborhood::startSpotOnceOnly(startTime, stopTime); +		} +		break; +	case MakeRoomView(kCaldoria41, kNorth): +		if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41NorthCarFlag) && _vm->getRandomBit() == 0) { +			_privateFlags.setFlag(kCaldoriaPrivateSeen41NorthCarFlag, true); +			Neighborhood::startSpotOnceOnly(startTime, stopTime); +		} +		break; +	case MakeRoomView(kCaldoria41, kEast): +		if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41EastCarFlag) && _vm->getRandomBit() == 0) { +			_privateFlags.setFlag(kCaldoriaPrivateSeen41EastCarFlag, true); +			Neighborhood::startSpotOnceOnly(startTime, stopTime); +		} +		break; +	case MakeRoomView(kCaldoria41, kWest): +		if (!_privateFlags.getFlag(kCaldoriaPrivateSeen41WestCarFlag) && _vm->getRandomBit() == 0) { +			_privateFlags.setFlag(kCaldoriaPrivateSeen41WestCarFlag, true); +			Neighborhood::startSpotOnceOnly(startTime, stopTime); +		} +		break; +	default: +		Neighborhood::startSpotOnceOnly(startTime, stopTime); +		break; +	} +} + +void Caldoria::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) {	 +	Neighborhood::findSpotEntry(room, direction, flags, entry); + +	switch (room) { +	case kCaldoria00: +		if (direction == kEast && (!GameState.getCaldoriaINNAnnouncing() || GameState.getCaldoriaSeenINN())) +			entry.clear(); +		break; +	case kCaldoriaVidPhone: +		if (direction == kNorth && GameState.getCaldoriaSeenMessages()) +			entry.clear(); +		break; +	case kCaldoria44: +		if (direction == kEast && GameState.getLastRoom() != kCaldoria42) +			entry.clear(); +		break; +	} +} + +void Caldoria::startExitMovie(const ExitTable::Entry &exitEntry) { +	switch (GameState.getCurrentRoom()) { +	case kCaldoria05: +	case kCaldoria07: +		if (GameState.getCurrentDirection() == kWest) +			closeCroppedMovie(); +		// fall through +	case kCaldoria11: +		if (GameState.getCurrentDirection() == kEast) +			closeCroppedMovie(); +		break; +	case kCaldoria13: +	case kCaldoria14: +		if (GameState.getCurrentDirection() == kNorth) +			closeCroppedMovie(); +		break; +	} + +	Neighborhood::startExitMovie(exitEntry); +} + +void Caldoria::startZoomMovie(const ZoomTable::Entry &zoomEntry) { +	switch (GameState.getCurrentRoom()) { +	case kCaldoria12: +		if (GameState.getCurrentDirection() == kNorth) +			closeCroppedMovie(); +		break; +	} + +	Neighborhood::startZoomMovie(zoomEntry); +} + +void Caldoria::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { +	if (GameState.getCurrentRoom() == kCaldoria27 || GameState.getCurrentRoom() == kCaldoria28 || GameState.getCurrentRoom() == kCaldoria45) +		// Must be opening elevator door. +		closeCroppedMovie(); + +	if (GameState.getCurrentRoom() == kCaldoria44 && GameState.getLastRoom() != kCaldoria42) +		startExtraSequence(kArriveAtCaldoriaFromTSA, kDoorOpenCompletedFlag, false); +	else +		Neighborhood::startDoorOpenMovie(startTime, stopTime); +} + +void Caldoria::startTurnPush(const TurnDirection turnDirection, const TimeValue newViewTime, const DirectionConstant destDirection) { +	switch (GameState.getCurrentRoom()) { +	case kCaldoria05: +	case kCaldoria07: +		if (GameState.getCurrentDirection() == kWest) +			closeCroppedMovie(); +		break; +	case kCaldoria11: +		if (GameState.getCurrentDirection() == kEast) +			closeCroppedMovie(); +		break; +	case kCaldoria12: +	case kCaldoria13: +	case kCaldoria14: +	case kCaldoria27: +	case kCaldoria28: +	case kCaldoria45: +		if (GameState.getCurrentDirection() == kNorth) +			closeCroppedMovie(); +		break; +	case kCaldoria48: +		if (_croppedMovie.isSurfaceValid()) +			closeCroppedMovie(); +		break; +	} + +	Neighborhood::startTurnPush(turnDirection, newViewTime, destDirection); +} + +void Caldoria::bumpIntoWall() { +	requestSpotSound(kCaldoriaUhghIn, kCaldoriaUhghOut, kFilterNoInput, 0); +	Neighborhood::bumpIntoWall(); +} + +void Caldoria::closeDoorOffScreen(const RoomID room, const DirectionConstant direction) { +	switch (room) { +	case kCaldoria08: +		if (direction == kNorth) +			playSpotSoundSync(kCaldoriaShowerCloseIn, kCaldoriaShowerCloseOut); +		else +			playSpotSoundSync(kCaldoriaDoorCloseIn, kCaldoriaDoorCloseOut); +		break; +	case kCaldoria09: +		playSpotSoundSync(kCaldoriaShowerCloseIn, kCaldoriaShowerCloseOut); +		break; +	case kCaldoria16: +	case kCaldoria38: +	case kCaldoria46: +	case kCaldoria27: +	case kCaldoria28: +	case kCaldoria45: +		playSpotSoundSync(kCaldoriaElevatorCloseIn, kCaldoriaElevatorCloseOut); +		break; +	case kCaldoria44: +	case kCaldoria42: +		if (GameState.getCurrentRoom() == kCaldoria42) +			playSpotSoundSync(kCaldoriaGTDoorCloseIn, kCaldoriaGTDoorCloseOut); +		break; +	default: +		playSpotSoundSync(kCaldoriaDoorCloseIn, kCaldoriaDoorCloseOut); +		break; +	} +} + +int16 Caldoria::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { +	int16 result = Neighborhood::getStaticCompassAngle(room, dir); + +	switch (room) { +	case kCaldoriaVidPhone: +		result += kVidPhoneAngle; +		break; +	case kCaldoriaReplicator: +		result += kReplicatorAngle; +		break; +	case kCaldoriaDrawers: +		result += kDrawersAngle; +		break; +	case kCaldoria53: +		result += kCaldoria53Angle; +		break; +	case kCaldoria55: +		result += kCaldoria55Angle; +		break; +	} + +	return result; +} + +void Caldoria::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { +	Neighborhood::getExitCompassMove(exitEntry, compassMove); + +	switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { +	case MakeRoomView(kCaldoria08, kNorth): +	case MakeRoomView(kCaldoria09, kSouth): +		compassMove.insertFaderKnot((exitEntry.movieStart + exitEntry.movieEnd) >> 1, compassMove.getNthKnotValue(0) + 30); +		break; +	case MakeRoomView(kCaldoria10, kEast): +		compassMove.insertFaderKnot(exitEntry.movieStart + 4 * kCaldoriaFrameDuration, 90); +		compassMove.insertFaderKnot(exitEntry.movieStart + 19 * kCaldoriaFrameDuration, -90); +		break; +	case MakeRoomView(kCaldoria42, kWest): +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, -90, exitEntry.movieEnd, 90); +		compassMove.insertFaderKnot(exitEntry.movieStart + 3 * kCaldoriaFrameDuration, -90); +		compassMove.insertFaderKnot(exitEntry.movieStart + 33 * kCaldoriaFrameDuration, 90); +		break; +	case MakeRoomView(kCaldoria54, kEast): +		if (getCurrentAlternate() != kAltCaldoriaSinclairDown) { +			compassMove.insertFaderKnot(exitEntry.movieStart + 16 * kCaldoriaFrameDuration, 135); +			compassMove.insertFaderKnot(exitEntry.movieEnd, 135); +		} +		break; +	case MakeRoomView(kCaldoria55, kNorth): +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, 315, exitEntry.movieEnd, 270); +		break; +	} +} + +void Caldoria::getZoomCompassMove(const ZoomTable::Entry &zoomEntry, FaderMoveSpec &compassMove) { +	Neighborhood::getZoomCompassMove(zoomEntry, compassMove); + +	switch (zoomEntry.hotspot) { +	case kCaBathroomToiletSpotID: +		compassMove.insertFaderKnot(zoomEntry.movieStart + 4 * kCaldoriaFrameDuration, 90); +		compassMove.insertFaderKnot(zoomEntry.movieStart + 19 * kCaldoriaFrameDuration, -90); +		compassMove.insertFaderKnot(zoomEntry.movieEnd, -90); +		break; +	} +} + +void Caldoria::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { +	switch (entry.extra) { +	case kCaldoria00WakeUp1: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 180); +		compassMove.insertFaderKnot(entry.movieStart + 1000, 90); +		compassMove.insertFaderKnot(entry.movieStart + 1640, 120); +		compassMove.insertFaderKnot(entry.movieStart + 2240, 135); +		compassMove.insertFaderKnot(entry.movieStart + 2640, 180); +		break; +	case kCaldoria00WakeUp2: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 180, entry.movieEnd, 90); +		compassMove.insertFaderKnot(entry.movieStart + 560, 90); +		break; +	case kCaldoria56BombStage1: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 10); +		compassMove.insertFaderKnot(entry.movieStart + 31 * kCaldoriaFrameDuration, 60); +		compassMove.insertFaderKnot(entry.movieStart + 49 * kCaldoriaFrameDuration, 60); +		compassMove.insertFaderKnot(entry.movieStart + 66 * kCaldoriaFrameDuration, 10); +		break; +	case kCaldoria56BombStage7: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 10, entry.movieEnd, 90); +		compassMove.insertFaderKnot(entry.movieStart + 131 * kCaldoriaFrameDuration, 10); +		compassMove.insertFaderKnot(entry.movieStart + 148 * kCaldoriaFrameDuration, 60); +		compassMove.insertFaderKnot(entry.movieStart + 165 * kCaldoriaFrameDuration, 60); +		compassMove.insertFaderKnot(entry.movieEnd - 5 * kCaldoriaFrameDuration, 90); +		break; +	default: +		Neighborhood::getExtraCompassMove(entry, compassMove); +		break; +	} +} + +void Caldoria::loadAmbientLoops() { +	RoomID room = GameState.getCurrentRoom(); + +	if (room == kCaldoria00 && GameState.getCaldoriaWokenUp()) +		loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4); +	else if (room >= kCaldoria01 && room <= kCaldoria14) +		loadLoopSound1("Sounds/Caldoria/Apartment Music.AIFF", 0x100 / 4); +	else if (room == kCaldoria27 || room == kCaldoria28 || room == kCaldoria45) +		loadLoopSound1("Sounds/Caldoria/Elevator Loop.AIFF", 0x100 / 5); +	else if (room == kCaldoria44) +		loadLoopSound1("Sounds/Caldoria/TSA Hum Loop.AIFF"); +	else if (room >= kCaldoria15 && room <= kCaldoria48) +		loadLoopSound1("Sounds/Caldoria/Industrial Nuage.aiff", 2 * 0x100 / 3); +	else if (room >= kCaldoria49 && room <= kCaldoria56) +		loadLoopSound1("Sounds/Caldoria/A50NLB00.22K.AIFF", 0x100 / 4); +} + +void Caldoria::checkContinuePoint(const RoomID room, const DirectionConstant direction) { +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kCaldoria06, kSouth): +	case MakeRoomView(kCaldoria13, kNorth): +	case MakeRoomView(kCaldoria16, kSouth): +	case MakeRoomView(kCaldoria38, kEast): +	case MakeRoomView(kCaldoria38, kWest): +	case MakeRoomView(kCaldoria40, kNorth): +	case MakeRoomView(kCaldoria44, kEast): +	case MakeRoomView(kCaldoria48, kNorth): +	case MakeRoomView(kCaldoria49, kNorth): +		makeContinuePoint(); +		break; +	} +} + +void Caldoria::spotCompleted() { +	Neighborhood::spotCompleted(); +	if (GameState.getCurrentRoom() == kCaldoriaBinoculars) +		startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput); +} + +void Caldoria::arriveAt(const RoomID room, const DirectionConstant direction) { +	switch (room) { +	case kCaldoria56: +		if (!GameState.getCaldoriaGunAimed()) +			// Fall through... +	case kCaldoria49: +	case kCaldoria50: +	case kCaldoria51: +	case kCaldoria52: +	case kCaldoria53: +	case kCaldoria54: +	case kCaldoria55: +		if (GameState.getCaldoriaSinclairShot()) +			setCurrentAlternate(kAltCaldoriaSinclairDown); +		break; +	} + +	Neighborhood::arriveAt(room, direction); +	Input dummy; + +	switch (room) { +	case kCaldoria00: +		arriveAtCaldoria00(); +		break; +	case kCaldoria05: +		if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) +			loopCroppedMovie("Images/Caldoria/A05 Light Loop", kCaldoriaA05LightLoopLeft, kCaldoriaA05LightLoopTop); +		break; +	case kCaldoria07: +		if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) +			loopCroppedMovie("Images/Caldoria/A07 Light Loop", kCaldoriaA07LightLoopLeft, kCaldoriaA07LightLoopTop); +		break; +	case kCaldoria09: +		_lastExtra = 0xffffffff; +		break; +	case kCaldoriaToilet: +		GameState.setScoringReadPaper(true); +		break; +	case kCaldoriaReplicator: +		setCurrentActivation(kActivateReplicatorReady); +		requestSpotSound(kCaldoriaReplicatorIntroIn, kCaldoriaReplicatorIntroOut, kFilterNoInput, 0); +		break; +	case kCaldoria11: +		setCurrentAlternate(kAltCaldoriaNormal); +		if (direction == kEast && !GameState.getCaldoriaSeenMessages()) +			loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop); +		break; +	case kCaldoria12: +		if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) +			loopCroppedMovie("Images/Caldoria/A12 Message Machine Loop", kCaldoria12MessageLoopLeft, kCaldoria12MessageLoopTop); +		break; +	case kCaldoriaDrawers: +		setCurrentActivation(kActivateDrawersClosed); +		break; +	case kCaldoria13: +		GameState.setCaldoriaINNAnnouncing(true); +		if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) +			loopCroppedMovie("Images/Caldoria/A13 Message Machine Loop", kCaldoria13MessageLoopLeft, kCaldoria13MessageLoopTop); +		break; +	case kCaldoria14: +		if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) +			loopCroppedMovie("Images/Caldoria/A14 Message Machine Loop", kCaldoria14MessageLoopLeft, kCaldoria14MessageLoopTop); +		break; +	case kCaldoria08: +		if (direction == kWest) +			setCurrentActivation(kActivateMirrorReady); +		// Fall through... +	case kCaldoria15: +		GameState.setCaldoriaINNAnnouncing(true); +		break; +	case kCaldoria27: +	case kCaldoria28: +	case kCaldoria45: +		if (GameState.getCurrentDirection() == kNorth) +			openDoor(); +		break; +	case kCaldoriaBinoculars: +		GameState.setScoringLookThroughTelescope(true); +		break; +	case kCaldoriaKiosk: +		GameState.setScoringSawCaldoriaKiosk(true); +		startExtraSequenceSync(kCaldoriaKioskVideo, kFilterAllInput); +		downButton(dummy); +		break; +	case kCaldoria44: +		arriveAtCaldoria44(); +		break; +	case kCaldoria49: +		arriveAtCaldoria49(); +		break; +	case kCaldoria53: +		if (direction == kEast && !GameState.getCaldoriaSinclairShot()) +			zoomToSinclair(); +		break; +	case kCaldoria50: +		if (direction == kNorth && !GameState.getCaldoriaSinclairShot()) +			setUpSinclairLoops(); +		break; +	case kCaldoria54: +		if (direction == kSouth && !GameState.getCaldoriaSinclairShot()) +			setUpSinclairLoops(); +		break; +	case kCaldoria56: +		arriveAtCaldoria56(); +		break; +	case kCaldoriaDeathRoom: +		arriveAtCaldoriaDeath(); +		break; +	} + +	checkSinclairShootsOS(); +	setUpRoofTop(); +} + +void Caldoria::doAIRecalibration() { +	GameState.setCaldoriaDidRecalibration(true); + +	if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB1", true, kRecalibrationInterruptFilter)) +		return; + +	g_interface->calibrateEnergyBar(); +	if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB4", true, kRecalibrationInterruptFilter)) +		return; + +	g_interface->raiseInventoryDrawerSync(); +	if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB6", true, kRecalibrationInterruptFilter)) { +		g_interface->lowerInventoryDrawerSync(); +		return; +	} + +	g_interface->lowerInventoryDrawerSync(); +	g_interface->raiseBiochipDrawerSync(); + +	if (!g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB5", true, kRecalibrationInterruptFilter)) { +		g_interface->lowerBiochipDrawerSync(); +		return; +	} + +	g_interface->lowerBiochipDrawerSync(); + +	g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Caldoria/XA01EB8", false, kRecalibrationInterruptFilter); +} + +void Caldoria::arriveAtCaldoria00() { +	if (GameState.getCurrentDirection() == kEast) { +		if (GameState.getCaldoriaWokenUp()) { +			if (!GameState.getCaldoriaDidRecalibration()) +				doAIRecalibration(); +			setCurrentActivation(kActivate4DClosed); +		} else { +			// Good morning, sleeping beauty +			ExtraTable::Entry extra; +			getExtraEntry(kCaldoria00WakeUp1, extra); + +			if (_navMovie.getTime() != extra.movieStart) { +				_navMovie.setTime(extra.movieStart); +				_navMovie.redrawMovieWorld(); +			} + +			startExtraSequenceSync(kCaldoria00WakeUp1, kFilterNoInput); +			GameState.setCaldoriaWokenUp(true); +			playCroppedMovieOnce("Images/Caldoria/VidPhone.movie", kCaldoriaVidPhoneLeft, kCaldoriaVidPhoneTop, kFilterAllInput); +			startExtraSequence(kCaldoria00WakeUp2, kExtraCompletedFlag, kFilterNoInput); +		} +	} +} + +bool Caldoria::wantsCursor() { +	return GameState.getCaldoriaDidRecalibration(); +} + +void Caldoria::arriveAtCaldoria44() { +	if (GameState.getLastNeighborhood() != kCaldoriaID) { +		openDoor(); +	} else { +		setCurrentActivation(kActivateReadyForCard); +		loopExtraSequence(kCaldoriaTransporterArrowLoop, 0); +	} +} + +void Caldoria::arriveAtCaldoria49() { +	if (GameState.getLastRoom() == kCaldoria48) +		setCurrentAlternate(kAltCaldoriaNormal); + +	// Need to force the loop to play. +	if (GameState.getCurrentDirection() == kNorth) { +		GameState.setCaldoriaFuseTimeLimit(kSinclairShootsTimeLimit); +		startExtraSequence(kCa49NorthVoiceAnalysis, kExtraCompletedFlag, kFilterNoInput); +	} +} + +void Caldoria::arriveAtCaldoria56() { +	if (!GameState.getCaldoriaBombDisarmed()) { +		_privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, true); + +		if (GameState.getCurrentDirection() == kNorth) { +			turnRight(); +		} else if (GameState.getCurrentDirection() == kSouth) { +			turnLeft(); +		} else if (GameState.getCurrentDirection() == kEast) { +			_privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, false); +			newInteraction(kCaldoriaBombInteractionID); +		} +	} +} + +void Caldoria::arriveAtCaldoriaDeath() { +	if (GameState.getLastRoom() == kCaldoria49) { +		if (GameState.getCaldoriaSinclairShot()) { +			die(kDeathNuclearExplosion); +		} else { +			playSpotSoundSync(kCaldoriaSinclairShootsOSIn, kCaldoriaSinclairShootsOSOut); +			playSpotSoundSync(kCaldoriaScreamingAfterIn, kCaldoriaScreamingAfterOut); +			die(kDeathSinclairShotDelegate); +		} +	} else { +		die(kDeathShotBySinclair); +	} +} + +void Caldoria::setUpRoofTop() { +	switch (GameState.getCurrentRoom()) { +	case kCaldoria48: +		if (GameState.getCurrentDirection() == kNorth) { +			if (GameState.getCaldoriaRoofDoorOpen()) { +				setCurrentAlternate(kAltCaldoriaRoofDoorBlown); +			} else if (GameState.getCaldoriaDoorBombed()) { +				// Long enough for AI hints...? +				_utilityFuse.primeFuse(kCardBombCountDownTime); +				_utilityFuse.setFunctionPtr(&doorBombTimerExpiredFunction, (void *)this); +				_utilityFuse.lightFuse(); + +				loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop); +			} else { +				setCurrentActivation(kActivateRoofSlotEmpty); +			} +		} +		break; +	case kCaldoria56: +		if (GameState.getCurrentDirection() == kEast && GameState.getCaldoriaGunAimed()) +			startExtraSequence(kCa53EastShootSinclair, kExtraCompletedFlag, false); +		else +			// Fall through... +	case kCaldoria49: +	case kCaldoria50: +	case kCaldoria51: +	case kCaldoria52: +	case kCaldoria53: +	case kCaldoria54: +	case kCaldoria55: +		if (!GameState.getCaldoriaSinclairShot()) { +			if (GameState.getCaldoriaSawVoiceAnalysis() && !_utilityFuse.isFuseLit()) { +				_utilityFuse.primeFuse(GameState.getCaldoriaFuseTimeLimit()); +				_utilityFuse.setFunctionPtr(&sinclairTimerExpiredFunction, (void *)this); +				_utilityFuse.lightFuse(); +			} +		} else { +			setCurrentAlternate(kAltCaldoriaSinclairDown); +		} +		break; +	} +} + +void Caldoria::downButton(const Input &input) { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kCaldoria01, kEast): +		GameState.setCaldoriaWokenUp(true); +		startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput); +		break; +	default: +		Neighborhood::downButton(input); +		break; +	} +} + +void Caldoria::turnTo(const DirectionConstant direction) { +	Neighborhood::turnTo(direction); + +	switch (GameState.getCurrentRoom()) { +	case kCaldoria00: +		if (direction == kEast) +			setCurrentActivation(kActivate4DClosed); +		break; +	case kCaldoria01: +		if (direction == kEast) { +			GameState.setCaldoriaWokenUp(true); +			startExtraSequence(kCaldoria00SitDown, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kCaldoria05: +		if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) +			loopCroppedMovie("Images/Caldoria/A05 Light Loop", kCaldoriaA05LightLoopLeft, kCaldoriaA05LightLoopTop); +		break; +	case kCaldoria07: +		if (direction == kWest && GameState.getCaldoriaINNAnnouncing()) +			loopCroppedMovie("Images/Caldoria/A07 Light Loop", kCaldoriaA07LightLoopLeft, kCaldoriaA07LightLoopTop); +		break; +	case kCaldoria08: +		if (direction == kWest) +			setCurrentActivation(kActivateMirrorReady); +		break; +	case kCaldoria09: +		_lastExtra = 0xffffffff; +		break; +	case kCaldoria11: +		if (direction == kEast && !GameState.getCaldoriaSeenMessages()) +			loopCroppedMovie("Images/Caldoria/A11 Message Machine Loop", kCaldoria11MessageLoopLeft, kCaldoria11MessageLoopTop); +		break; +	case kCaldoria12: +		if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) +			loopCroppedMovie("Images/Caldoria/A12 Message Machine Loop", kCaldoria12MessageLoopLeft, kCaldoria12MessageLoopTop); +		break; +	case kCaldoria13: +		if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) +			loopCroppedMovie("Images/Caldoria/A13 Message Machine Loop", kCaldoria13MessageLoopLeft, kCaldoria13MessageLoopTop); +		break; +	case kCaldoria14: +		if (direction == kNorth && !GameState.getCaldoriaSeenMessages()) +			loopCroppedMovie("Images/Caldoria/A14 Message Machine Loop", kCaldoria14MessageLoopLeft, kCaldoria14MessageLoopTop); +		break; +	case kCaldoria27: +	case kCaldoria28: +	case kCaldoria45: +		if (direction == kNorth) +			openElevatorMovie(); +		else +			closeCroppedMovie(); +		break; +	case kCaldoria48: +		if (direction == kNorth && !GameState.getCaldoriaDoorBombed()) +			setCurrentActivation(kActivateRoofSlotEmpty); +		break; +	case kCaldoria53: +		if (GameState.getCurrentDirection() == kEast && !GameState.getCaldoriaSinclairShot()) +			zoomToSinclair(); +		break; +	case kCaldoria50: +		if (direction == kNorth && !GameState.getCaldoriaSinclairShot()) +			setUpSinclairLoops(); +		break; +	case kCaldoria54: +		if (direction == kSouth && !GameState.getCaldoriaSinclairShot()) +			setUpSinclairLoops(); +		break; +	case kCaldoria56: +		if (_privateFlags.getFlag(kCaldoriaPrivateZoomingToBombFlag)) { +			_privateFlags.setFlag(kCaldoriaPrivateZoomingToBombFlag, false); +			newInteraction(kCaldoriaBombInteractionID); +		} else if (GameState.getCaldoriaBombDisarmed()) { +			_vm->playEndMessage(); +		} +		break; +	} + +	checkSinclairShootsOS(); +} + +void Caldoria::zoomTo(const Hotspot *zoomOutSpot) { +	// Need to set _zoomOutSpot here because we may come through +	// this function another way, say by pressing the down arrow, +	// that doesn't involve the ClickInHotSpot function. +	_zoomOutSpot = zoomOutSpot; + +	if (zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) { +		if (_privateFlags.getFlag(kCaloriaPrivateLeftDrawerOpenFlag)) { +			_privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false); +			startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput); +		} else if (_privateFlags.getFlag(kCaldoriaPrivateRightDrawerOpenFlag)) { +			_privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); +			if (GameState.isTakenItemID(kKeyCard)) +				startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, false); +			else +				startExtraSequence(kRightDrawerCloseWithKeys, kExtraCompletedFlag, false); +		} else { +			Neighborhood::zoomTo(zoomOutSpot); +		} +	} else { +		Neighborhood::zoomTo(zoomOutSpot); +	} +} + +void Caldoria::setUpSinclairLoops() { +	_navMovie.stop(); +	scheduleNavCallBack(kSinclairLoopDoneFlag); +	_sinclairLoopCount = 0; +	_numSinclairLoops = 2; +	_navMovie.start(); +} + +void Caldoria::zoomToSinclair() {	 +	_utilityFuse.stopFuse(); +	_privateFlags.setFlag(kCaldoriaPrivateReadyToShootFlag, true); +	setCurrentActivation(kActivateZoomedOnSinclair); + +	ExtraTable::Entry entry; +	getExtraEntry(kCa53EastZoomToSinclair, entry); +	_sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime1, _navMovie.getScale()); +	startExtraSequence(kCa53EastZoomToSinclair, kExtraCompletedFlag, kFilterAllInput); +} + +void Caldoria::receiveNotification(Notification *notification, const NotificationFlags flags) {	 +	Neighborhood::receiveNotification(notification, flags); + +	if ((flags & kExtraCompletedFlag) != 0) { +		InventoryItem *item; +		_interruptionFilter = kFilterAllInput; + +		switch (_lastExtra) { +		case kCaldoria00WakeUp2: +			makeContinuePoint(); +			// Force ArriveAt to do its thing... +			GameState.setCurrentRoom(kNoRoomID); +			arriveAt(kCaldoria00, kEast); +			break; +		case k4DEnvironOpenToINN: +			GameState.setCaldoriaSeenINN(true); +			GameState.setScoringSawINN(true); +			// Fall through to k4DEnvironOpen... +		case k4DEnvironOpen: +			_privateFlags.setFlag(kCaldoriaPrivate4DSystemOpenFlag, true); +			setCurrentActivation(kActivate4DOpen); +			newInteraction(kCaldoria4DInteractionID); +			break; +		case kCaldoriaShowerUp: +			GameState.setScoringTookShower(true); +			GameState.setCaldoriaDoneHygiene(true); +			break; +		case kLeftDrawerClose: +		case kRightDrawerCloseNoKeys: +		case kRightDrawerCloseWithKeys: +			if (_zoomOutSpot && _zoomOutSpot->getObjectID() == kCaldoriaDrawersOutSpotID) { +				Input input; +				clickInHotspot(input, _zoomOutSpot); +			} +			break; +		case kCreateOrangeJuice: +			setCurrentActivation(kActivateOJOnThePad); +			requestSpotSound(kCaldoriaReplicatorOJChoiceIn, kCaldoriaReplicatorOJChoiceOut, kFilterNoInput, 0); +			break; +		case kCaldoria00SitDown: +			arriveAt(kCaldoria00, kEast); +			break; +		case kCaldoria16ElevatorUp: +			startExtraSequence(kCaldoria16ElevatorDown, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kCaldoria16ElevatorDown: +			GameState.setCaldoriaSeenSinclairInElevator(true); +			_privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); +			openDoor(); +			break; +		case kCaldoriaFourthToGround: +		case kCaldoriaRoofToGround: +			arriveAt(kCaldoria28, GameState.getCurrentDirection()); +			break; +		case kCaldoriaFourthToRoof: +		case kCaldoriaGroundToRoof: +			arriveAt(kCaldoria45, GameState.getCurrentDirection()); +			break; +		case kCaldoriaGroundToFourth: +		case kCaldoriaRoofToFourth: +			arriveAt(kCaldoria27, GameState.getCurrentDirection()); +			break; +		case kCaGTCardSwipe: +			item = (InventoryItem *)g_allItems.findItemByID(kKeyCard); +			_vm->addItemToInventory(item); +			setCurrentActivation(kActivateReadyToTransport); +			break; +		case kCaGTFryTheFly: +		case kCaGTGoToTSA: +			_vm->jumpToNewEnvironment(kFullTSAID, kTSA00, kNorth); +			break; +		case kCaGTGoToTokyo: +			playDeathExtra(kCaGTArriveAtTokyo, kDeathUncreatedInCaldoria); +			break; +		case kCaGTGoToBeach: +			playDeathExtra(kCaGTArriveAtBeach, kDeathUncreatedInCaldoria); +			break; +		case kCa48NorthExplosion: +			// Current biochip must be the shield if we got here. +			_vm->getCurrentBiochip()->setItemState(kShieldNormal); +			break; +		case kBinocularsZoomInOnShip: +			setCurrentActivation(kActivateFocusedOnShip); +			break; +		case kCa49NorthVoiceAnalysis: +			_utilityFuse.primeFuse(kSinclairShootsTimeLimit); +			_utilityFuse.setFunctionPtr(&sinclairTimerExpiredFunction, (void*) this); +			_utilityFuse.lightFuse(); +			GameState.setCaldoriaSawVoiceAnalysis(true); +			break; +		case kCa53EastZoomToSinclair: +			if (GameState.getCaldoriaSinclairShot()) { +				delete _gunSprite; +				_gunSprite = 0; +				startExtraSequence(kCa53EastShootSinclair, kExtraCompletedFlag, false); +			} else { +				playDeathExtra(kCa53EastDeath2, kDeathSinclairShotDelegate); +			} +			break; +		case kCa53EastShootSinclair: +			_vm->addItemToInventory((InventoryItem *)g_allItems.findItemByID(kStunGun)); +			startExtraSequence(kCa53EastZoomOutFromSinclair, kExtraCompletedFlag, false); +			GameState.setScoringStunnedSinclair(true); +			break; +		case kCa53EastZoomOutFromSinclair: +			setCurrentAlternate(kAltCaldoriaSinclairDown); +			updateViewFrame(); +			makeContinuePoint(); +			break; +		} +	} else if ((flags & kSpotSoundCompletedFlag) != 0) { +		switch (GameState.getCurrentRoom()) { +		case kCaldoria20: +		case kCaldoria21: +		case kCaldoria26: +		case kCaldoria29: +		case kCaldoria34: +		case kCaldoria35: +			updateViewFrame(); +			break; +		case kCaldoria27: +		case kCaldoria28: +		case kCaldoria45: +			updateElevatorMovie(); +			break; +		case kCaldoriaReplicator: +			emptyOJGlass(); +			break; +		} +	} else if ((flags & kSinclairLoopDoneFlag) != 0) { +		if (++_sinclairLoopCount == _numSinclairLoops) { +			switch (GameState.getCurrentRoom()) { +			case kCaldoria50: +				playDeathExtra(kCa50SinclairShoots, kDeathShotBySinclair); +				break; +			case kCaldoria54: +				playDeathExtra(kCa54SouthDeath, kDeathShotBySinclair); +				break; +			} +		} else { +			_navMovie.stop(); +			scheduleNavCallBack(kSinclairLoopDoneFlag); +			_navMovie.start(); +		} +	} + +	g_AIArea->checkMiddleArea(); +} + +InputBits Caldoria::getInputFilter() { +	InputBits result = Neighborhood::getInputFilter(); + +	switch (GameState.getCurrentRoom()) { +	case kCaldoria00: +		if (_privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) +			result &= ~kFilterAllDirections; +		break; +	case kCaldoriaBinoculars: +		if (getCurrentActivation() == kActivateNotFocusedOnShip) +			result &= ~(kFilterDownButton | kFilterDownAuto); +		break; +	case kCaldoria53: +		if (_privateFlags.getFlag(kCaldoriaPrivateReadyToShootFlag) && !GameState.getCaldoriaSinclairShot()) +			result &= ~kFilterAllDirections; +		break; +	case kCaldoria48: +		if (GameState.getCaldoriaDoorBombed()) +			result &= ~kFilterAllDirections; +	} + +	return result; +} + +void Caldoria::activateHotspots() { +	Neighborhood::activateHotspots(); + +	switch (GameState.getCurrentRoom()) { +	case kCaldoriaDrawers: +		if (getCurrentActivation() == kActivateRightOpen) { +			if (GameState.isTakenItemID(kKeyCard)) { +				g_allHotspots.activateOneHotspot(kCaldoriaRightDrawerNoKeysCloseSpotID); +				g_allHotspots.deactivateOneHotspot(kCaldoriaRightDrawerWithKeysCloseSpotID); +			} else { +				g_allHotspots.activateOneHotspot(kCaldoriaRightDrawerWithKeysCloseSpotID); +				g_allHotspots.deactivateOneHotspot(kCaldoriaRightDrawerNoKeysCloseSpotID); +			} +		} +	case kCaldoriaReplicator: +		if (GameState.getCaldoriaMadeOJ()) +			g_allHotspots.deactivateOneHotspot(kCaldoriaMakeOJSpotID); +		break; +	case kCaldoria27: +		if (GameState.isCurrentDoorOpen()) { +			g_allHotspots.deactivateOneHotspot(kCaldoriaFourthFloorElevator1); +			g_allHotspots.deactivateOneHotspot(kCaldoriaFourthFloorElevator2); +			g_allHotspots.deactivateOneHotspot(kCaldoriaFourthFloorElevator3); +			g_allHotspots.deactivateOneHotspot(kCaldoriaFourthFloorElevator4); +			g_allHotspots.deactivateOneHotspot(kCaldoriaFourthFloorElevator5); +		} +		break; +	case kCaldoria28: +		if (GameState.isCurrentDoorOpen()) { +			g_allHotspots.deactivateOneHotspot(kCaldoriaGroundElevator1); +			g_allHotspots.deactivateOneHotspot(kCaldoriaGroundElevator2); +			g_allHotspots.deactivateOneHotspot(kCaldoriaGroundElevator3); +			g_allHotspots.deactivateOneHotspot(kCaldoriaGroundElevator4); +			g_allHotspots.deactivateOneHotspot(kCaldoriaGroundElevator5); +		} +		break; +	case kCaldoria45: +		if (GameState.isCurrentDoorOpen()) { +			g_allHotspots.deactivateOneHotspot(kCaldoriaRoofElevator1); +			g_allHotspots.deactivateOneHotspot(kCaldoriaRoofElevator2); +			g_allHotspots.deactivateOneHotspot(kCaldoriaRoofElevator3); +			g_allHotspots.deactivateOneHotspot(kCaldoriaRoofElevator4); +			g_allHotspots.deactivateOneHotspot(kCaldoriaRoofElevator5); +		} +		break; +	} +} + +void Caldoria::clickInHotspot(const Input &input, const Hotspot *spot) { +	switch (spot->getObjectID()) { +	case kCa4DEnvironOpenSpotID: +		if (!GameState.getCaldoriaINNAnnouncing() || GameState.getCaldoriaSeenINN()) { +			startExtraSequence(k4DEnvironOpen, kExtraCompletedFlag, kFilterNoInput); +		} else { +			// This trick depends on the following sequences being in order in the +			// world movie: +			// k4DEnvironOpenToINN +			// k4DINNInterruption +			// k4DINNIntro +			// k4DINNMarkJohnson +			// k4DINNMeganLove +			// k4DINNFadeOut +			// k4DEnvironOpenFromINN +			loadLoopSound1(""); +			loadLoopSound2(""); +			startExtraLongSequence(k4DEnvironOpenToINN, k4DEnvironOpenFromINN, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kCa4DEnvironCloseSpotID: +		((Caldoria4DSystem *)_currentInteraction)->shutDown4DSystem(); +		break; +	case kCaBathroomMirrorSpotID: +		newInteraction(kCaldoriaMirrorInteractionID); +		break; +	case kCaShowerSpotID: +		requestExtraSequence(kCaldoriaShowerTitle, 0, kFilterNoInput); +		requestExtraSequence(kCaldoriaShowerButton, 0, kFilterNoInput); +		requestExtraSequence(kCaldoriaShowerDown, 0, kFilterNoInput); +		requestExtraSequence(kCaldoriaShowerUp, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaldoriaLeftDrawerOpenSpotID: +		_privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, true); +		setCurrentActivation(kActivateLeftOpen); +		startExtraSequence(kLeftDrawerOpen, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaldoriaLeftDrawerCloseSpotID: +		_privateFlags.setFlag(kCaloriaPrivateLeftDrawerOpenFlag, false); +		setCurrentActivation(kActivateDrawersClosed); +		startExtraSequence(kLeftDrawerClose, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaldoriaRightDrawerOpenSpotID: +		_privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, true); +		setCurrentActivation(kActivateRightOpen); +		if (GameState.isTakenItemID(kKeyCard)) +			startExtraSequence(kRightDrawerOpenNoKeys, kExtraCompletedFlag, kFilterNoInput); +		else +			startExtraSequence(kRightDrawerOpenWithKeys, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaldoriaRightDrawerWithKeysCloseSpotID: +		_privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); +		setCurrentActivation(kActivateDrawersClosed); +		startExtraSequence(kRightDrawerCloseWithKeys, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaldoriaRightDrawerNoKeysCloseSpotID: +		_privateFlags.setFlag(kCaldoriaPrivateRightDrawerOpenFlag, false); +		setCurrentActivation(kActivateDrawersClosed); +		startExtraSequence(kRightDrawerCloseNoKeys, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaldoriaMakeStickyBunsSpotID: +		requestSpotSound(kCaldoriaReplicatorWrongChoiceIn, kCaldoriaReplicatorWrongChoiceOut, kFilterNoInput, 0); +		break; +	case kCaldoriaMakeOJSpotID: +		GameState.setCaldoriaMadeOJ(true); +		startExtraSequence(kCreateOrangeJuice, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaBedroomVidPhoneActivationSpotID: +		newInteraction(kCaldoriaMessagesInteractionID); +		break; +	case kCaldoriaFourthFloorElevatorSpotID: +		if (!GameState.getCaldoriaSeenSinclairInElevator()) { +			startExtraSequence(kCaldoria16ElevatorUp, kExtraCompletedFlag, kFilterNoInput); +		} else { +			_privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); +			openDoor(); +		} +		break; +	case kCaldoriaGroundElevatorSpotID: +		_privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); +		openDoor(); +		break; +	case kCaldoriaRoofElevatorSpotID: +		_privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, true); +		openDoor(); +		break; +	case kCaldoriaFourthFloorElevator1: +	case kCaldoriaFourthFloorElevator2: +	case kCaldoriaFourthFloorElevator3: +	case kCaldoriaFourthFloorElevator4: +	case kCaldoriaFourthFloorElevator5: +		// Assumes that elevator hot spots are consecutive. +		takeElevator(4, spot->getObjectID() - kCaldoriaFourthFloorElevator1 + 1); +		break; +	case kCaldoriaGroundElevator1: +	case kCaldoriaGroundElevator2: +	case kCaldoriaGroundElevator3: +	case kCaldoriaGroundElevator4: +	case kCaldoriaGroundElevator5: +		// Assumes that elevator hot spots are consecutive. +		takeElevator(1, spot->getObjectID() - kCaldoriaGroundElevator1 + 1); +		break; +	case kCaldoriaRoofElevator1: +	case kCaldoriaRoofElevator2: +	case kCaldoriaRoofElevator3: +	case kCaldoriaRoofElevator4: +	case kCaldoriaRoofElevator5: +		// Assumes that elevator hot spots are consecutive. +		takeElevator(5, spot->getObjectID() - kCaldoriaRoofElevator1 + 1); +		break; +	case kCaldoriaGTTokyoSpotID: +		startExtraSequence(kCaGTGoToTokyo, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaldoriaGTTSASpotID: +		GameState.setScoringGoToTSA(true); +		startExtraLongSequence(kCaGTFryTheFly, kCaGTGoToTSA, kExtraCompletedFlag, false); +		break; +	case kCaldoriaGTBeachSpotID: +		startExtraSequence(kCaGTGoToBeach, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaldoriaGTOtherSpotID: +		showExtraView(kCaGTOtherChoice); +		playSpotSoundSync(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut); +		showExtraView(kCaGTCardSwipe); +		break; +	case kCaldoriaZoomInOnShipSpotID: +		startExtraSequence(kBinocularsZoomInOnShip, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaldoriaRoofDoorSpotID: +		startExtraSequence(kCa48NorthRooftopClosed, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaldoria20DoorbellSpotID: +	case kCaldoria21DoorbellSpotID: +	case kCaldoria26DoorbellSpotID: +	case kCaldoria29DoorbellSpotID: +	case kCaldoria34DoorbellSpotID: +	case kCaldoria35DoorbellSpotID: +		clickOnDoorbell(spot->getObjectID()); +		break; +	default: +		Neighborhood::clickInHotspot(input, spot); +		break; +	} +} + +void Caldoria::clickOnDoorbell(const HotSpotID doorBellSpotID) { +	uint32 extra; +	ExtraTable::Entry entry; + +	switch (doorBellSpotID) { +	case kCaldoria20DoorbellSpotID: +		extra = kCaldoria20Doorbell; +		break; +	case kCaldoria21DoorbellSpotID: +		extra = kCaldoria21Doorbell; +		break; +	case kCaldoria26DoorbellSpotID: +		extra = kCaldoria26Doorbell; +		break; +	case kCaldoria29DoorbellSpotID: +		extra = kCaldoria29Doorbell; +		break; +	case kCaldoria34DoorbellSpotID: +		extra = kCaldoria34Doorbell; +		break; +	case kCaldoria35DoorbellSpotID: +		extra = kCaldoria35Doorbell; +		break; +	} + +	getExtraEntry(extra, entry); +	showViewFrame(entry.movieStart); +	requestSpotSound(kCaldoriaNobodyHomeIn, kCaldoriaNobodyHomeOut, kFilterNoInput, kSpotSoundCompletedFlag); +} + +CanOpenDoorReason Caldoria::canOpenDoor(DoorTable::Entry &entry) { +	switch (GameState.getCurrentRoom()) { +	case kCaldoria16: +	case kCaldoria38: +	case kCaldoria46: +		if (GameState.getCurrentDirection() == kSouth && !_privateFlags.getFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag)) +			return kCantOpenLocked; +		break; +	} + +	return Neighborhood::canOpenDoor(entry); +} + +void Caldoria::doorOpened() { +	Neighborhood::doorOpened(); +	_privateFlags.setFlag(kCaldoriaPrivateCanOpenElevatorDoorFlag, false); +} + +GameInteraction *Caldoria::makeInteraction(const InteractionID interactionID) { +	switch (interactionID) { +	case kCaldoria4DInteractionID: +		return new Caldoria4DSystem(this); +	case kCaldoriaBombInteractionID: +		return new CaldoriaBomb(this, _vm); +	case kCaldoriaMessagesInteractionID: +		return new CaldoriaMessages(this, kCaldoriaMessagesNotificationID, _vm); +	case kCaldoriaMirrorInteractionID: +		return new CaldoriaMirror(this); +	} + +	return 0; +} + +void Caldoria::newInteraction(const InteractionID interactionID) { +	Neighborhood::newInteraction(interactionID); + +	if (!_currentInteraction) { +		if (_privateFlags.getFlag(kCaldoriaPrivate4DSystemOpenFlag)) { +			_privateFlags.setFlag(kCaldoriaPrivate4DSystemOpenFlag, false); +			setCurrentActivation(kActivate4DClosed); +			startExtraSequence(k4DEnvironClose, kExtraCompletedFlag, kFilterNoInput); +		} else if (GameState.getCaldoriaBombDisarmed()) { +			turnLeft(); +		} +	} +} + +// Only called when trying to pick up an item and the player can't (because +// the inventory is too full or because the player lets go of the item before +// dropping it into the inventory). +Hotspot *Caldoria::getItemScreenSpot(Item *item, DisplayElement *element) { +	HotSpotID destSpotID = kNoHotSpotID; +	 +	switch (item->getObjectID()) { +	case kKeyCard: +		destSpotID = kCaldoriaKeyCardSpotID; +		break; +	case kOrangeJuiceGlassEmpty: +	case kOrangeJuiceGlassFull: +		destSpotID = kCaldoriaOrangeJuiceSpotID; +		break; +	} + +	if (destSpotID == kNoHotSpotID) +		return Neighborhood::getItemScreenSpot(item, element); + +	return g_allHotspots.findHotspotByID(destSpotID); +} + +void Caldoria::pickedUpItem(Item *item) { +	switch (item->getObjectID()) { +	case kKeyCard: +		GameState.setScoringGotKeyCard(true); +		break; +	case kOrangeJuiceGlassFull: +		setCurrentActivation(kActivateReplicatorReady); +		requestSpotSound(kCaldoriaDrinkOJIn, kCaldoriaDrinkOJOut, kFilterNoInput, kSpotSoundCompletedFlag); +		break; +	case kStunGun: +		GameState.setCaldoriaGunAimed(false); +		break; +	} +} + +void Caldoria::dropItemIntoRoom(Item *item, Hotspot *dropSpot) {	 +	switch (item->getObjectID()) { +	case kKeyCard: +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		if (dropSpot->getObjectID() == kCaldoriaGTCardDropSpotID) +			startExtraSequence(kCaGTCardSwipe, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kOrangeJuiceGlassEmpty: +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		if (dropSpot->getObjectID() == kCaldoriaOrangeJuiceDropSpotID) { +			GameState.setCaldoriaMadeOJ(false); +			startExtraSequence(kDisposeOrangeJuice, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kCardBomb: +		GameState.setCaldoriaDoorBombed(true); +		setCurrentActivation(kActivateHotSpotAlways); +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		// Long enough for AI hints...? +		_utilityFuse.primeFuse(kCardBombCountDownTime); +		_utilityFuse.setFunctionPtr(&doorBombTimerExpiredFunction, (void *)this); +		_utilityFuse.lightFuse(); +		GameState.setCaldoriaFuseTimeLimit(kCardBombCountDownTime); +		loopCroppedMovie("Images/Caldoria/A48 Bomb Loop", kCaldoria48CardBombLoopLeft, kCaldoria48CardBombLoopTop); +		GameState.setScoringUsedCardBomb(true); +		break; +	case kStunGun: +		GameState.setCaldoriaGunAimed(true); +		GameState.setCaldoriaSinclairShot(true); +		_gunSprite = item->getDragSprite(0); +		_gunSprite->setCurrentFrameIndex(1); +		_gunSprite->setDisplayOrder(kDragSpriteOrder); +		_gunSprite->moveElementTo(kCaldoriaGunSpriteLeft, kCaldoriaGunSpriteTop); +		_gunSprite->startDisplaying(); +		_gunSprite->show(); +		break; +	default: +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		break; +	} +} + +void Caldoria::takeElevator(uint startFloor, uint endFloor) { +	_croppedMovie.stop(); +	_croppedMovie.setSegment(0, _croppedMovie.getDuration()); + +	switch (startFloor) { +	case 1: +		switch (endFloor) { +		case 1: +			// Do nothing. +			break; +		case 2: +			_croppedMovie.setTime(k1To2Time); +			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); +			break; +		case 3: +			_croppedMovie.setTime(k1To3Time); +			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); +			break; +		case 4: +			_croppedMovie.setSegment(k1To4Start, k1To4Stop); +			_croppedMovie.setTime(k1To4Start); +			startExtraSequence(kCaldoriaGroundToFourth, kExtraCompletedFlag, false); +			_croppedMovie.start(); +			break; +		case 5: +			_croppedMovie.setSegment(k1To5Start, k1To5Stop); +			_croppedMovie.setTime(k1To5Start); +			startExtraSequence(kCaldoriaGroundToRoof, kExtraCompletedFlag, false); +			_croppedMovie.start(); +			break; +		} +		break; +	case 4: +		switch (endFloor) { +		case 1: +			_croppedMovie.setSegment(k4To1Start, k4To1Stop); +			_croppedMovie.setTime(k4To1Start); +			startExtraSequence(kCaldoriaFourthToGround, kExtraCompletedFlag, false); +			_croppedMovie.start(); +			break; +		case 2: +			_croppedMovie.setTime(k4To2Time); +			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); +			break; +		case 3: +			_croppedMovie.setTime(k4To3Time); +			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); +			break; +		case 4: +			// Do nothing. +			break; +		case 5: +			_croppedMovie.setSegment(k4To5Start, k4To5Stop); +			_croppedMovie.setTime(k4To5Start); +			startExtraSequence(kCaldoriaFourthToRoof, kExtraCompletedFlag, false); +			_croppedMovie.start(); +			break; +		} +		break; +	case 5: +		switch (endFloor) { +		case 1: +			_croppedMovie.setSegment(k5To1Start, k5To1Stop); +			_croppedMovie.setTime(k5To1Start); +			startExtraSequence(kCaldoriaRoofToGround, kExtraCompletedFlag, false); +			_croppedMovie.start(); +			break; +		case 2: +			_croppedMovie.setTime(k5To2Time); +			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); +			break; +		case 3: +			_croppedMovie.setTime(k5To3Time); +			requestSpotSound(kCaldoriaNoOtherDestinationIn, kCaldoriaNoOtherDestinationOut, kFilterNoInput, kSpotSoundCompletedFlag); +			break; +		case 4: +			_croppedMovie.setSegment(k5To4Start, k5To4Stop); +			_croppedMovie.setTime(k5To4Start); +			startExtraSequence(kCaldoriaRoofToFourth, kExtraCompletedFlag, false); +			_croppedMovie.start(); +			break; +		case 5: +			// Do nothing. +			break; +		} +		break; +	}; +} + +void Caldoria::updateElevatorMovie() { +	TimeValue time = 0xffffffff; + +	if (GameState.getCurrentDirection() == kNorth) { +		switch (GameState.getCurrentRoom()) { +		case kCaldoria27: +			time = k4FloorTime; +			break; +		case kCaldoria28: +			time = k1FloorTime; +			break; +		case kCaldoria45: +			time = k5FloorTime; +			break; +		} +	} + +	_croppedMovie.stop(); + +	if (time == 0xffffffff) { +		_croppedMovie.hide(); +	} else { +		_croppedMovie.stop(); +		_croppedMovie.setSegment(0, _croppedMovie.getDuration()); +		_croppedMovie.setTime(time); +		_croppedMovie.redrawMovieWorld(); +		_croppedMovie.show(); +		 +		// *** Why do I need this? +		// clone2727: "don't ask me!" +		_navMovie.redrawMovieWorld(); +	} +} + +void Caldoria::openElevatorMovie() { +	if (!_croppedMovie.isSurfaceValid()) +		openCroppedMovie("Images/Caldoria/Caldoria Elevator.movie", kCaldoriaElevatorLeft, kCaldoriaElevatorTop); + +	updateElevatorMovie(); +} + +void Caldoria::emptyOJGlass() { +	GameState.setTakenItemID(kOrangeJuiceGlassFull, false); +	GameState.setTakenItemID(kOrangeJuiceGlassEmpty, true); +	_vm->removeItemFromInventory((InventoryItem *)g_allItems.findItemByID(kOrangeJuiceGlassFull)); +	_vm->addItemToInventory((InventoryItem *)g_allItems.findItemByID(kOrangeJuiceGlassEmpty)); +} + +void Caldoria::doorBombTimerExpired() { +	closeCroppedMovie(); + +	if (GameState.getShieldOn()) { +		_vm->getCurrentBiochip()->setItemState(kShieldCardBomb); +		setCurrentAlternate(kAltCaldoriaRoofDoorBlown); +		startExtraSequence(kCa48NorthExplosion, kExtraCompletedFlag, kFilterNoInput); +		GameState.setScoringShieldedCardBomb(true); +		GameState.setCaldoriaDoorBombed(false); +		GameState.setCaldoriaRoofDoorOpen(true); +	} else { +		playDeathExtra(kCa48NorthExplosionDeath, kDeathCardBomb); +	} +} + +void Caldoria::sinclairTimerExpired() { +	_privateFlags.setFlag(kCaldoriaPrivateSinclairTimerExpiredFlag, true); +	checkSinclairShootsOS(); +} + +void Caldoria::checkSinclairShootsOS() { +	if (_privateFlags.getFlag(kCaldoriaPrivateSinclairTimerExpiredFlag)) +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kCaldoria49, kNorth): +		case MakeRoomView(kCaldoria49, kSouth): +		case MakeRoomView(kCaldoria49, kEast): +		case MakeRoomView(kCaldoria49, kWest): +		case MakeRoomView(kCaldoria50, kSouth): +		case MakeRoomView(kCaldoria50, kEast): +		case MakeRoomView(kCaldoria50, kWest): +		case MakeRoomView(kCaldoria51, kNorth): +		case MakeRoomView(kCaldoria51, kSouth): +		case MakeRoomView(kCaldoria51, kWest): +		case MakeRoomView(kCaldoria52, kNorth): +		case MakeRoomView(kCaldoria52, kSouth): +		case MakeRoomView(kCaldoria52, kWest): +		case MakeRoomView(kCaldoria53, kNorth): +		case MakeRoomView(kCaldoria53, kSouth): +		case MakeRoomView(kCaldoria53, kWest): +		case MakeRoomView(kCaldoria54, kNorth): +		case MakeRoomView(kCaldoria54, kEast): +		case MakeRoomView(kCaldoria54, kWest): +			playSpotSoundSync(kCaldoriaSinclairShootsOSIn, kCaldoriaSinclairShootsOSOut); +			playSpotSoundSync(kCaldoriaScreamingAfterIn, kCaldoriaScreamingAfterOut); +			die(kDeathSinclairShotDelegate); +			break; +		} +} + +void Caldoria::checkInterruptSinclair() { +	if (GameState.getCaldoriaSinclairShot()) { +		_navMovie.stop(); +		_neighborhoodNotification.setNotificationFlags(kExtraCompletedFlag, kExtraCompletedFlag); +		g_AIArea->unlockAI(); +	} else { +		uint32 currentTime = _navMovie.getTime(); + +		ExtraTable::Entry entry; +		getExtraEntry(kCa53EastZoomToSinclair, entry); + +		if (currentTime < entry.movieStart + kSinclairInterruptionTime2) +			_sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime2, +					_navMovie.getScale()); +		else if (currentTime < entry.movieStart + kSinclairInterruptionTime3) +			_sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime3, +					_navMovie.getScale()); +		else if (currentTime < entry.movieStart + kSinclairInterruptionTime4) +			_sinclairInterrupt.scheduleCallBack(kTriggerTimeFwd, entry.movieStart + kSinclairInterruptionTime4, +					_navMovie.getScale()); +	} +} + +Common::String Caldoria::getBriefingMovie() { +	Common::String movieName = Neighborhood::getBriefingMovie(); + +	if (movieName.empty()) { +		if (GameState.allTimeZonesFinished()) +			return "Images/AI/Caldoria/XA02"; + +		return "Images/AI/Caldoria/XA01"; +	} + +	return movieName; +} + +Common::String Caldoria::getEnvScanMovie() {	 +	Common::String movieName = Neighborhood::getEnvScanMovie(); + +	if (movieName.empty()) { +		RoomID room = GameState.getCurrentRoom(); + +		if (room >= kCaldoria00 && room <= kCaldoria14) { +			// Inside apartment. +			if (GameState.getCaldoriaDoneHygiene()) +				return "Images/AI/Caldoria/XAE2"; + +			return "Images/AI/Caldoria/XAE1"; +		} else if (room >= kCaldoria15 && room <= kCaldoria48) { +			// Wandering the halls... +			return "Images/AI/Caldoria/XAE3"; +		} else { +			// Must be the roof. +			return "Images/AI/Caldoria/XAEH2"; +		} +	} + +	return movieName; +} + +uint Caldoria::getNumHints() { +	uint numHints = Neighborhood::getNumHints(); + +	if (numHints == 0) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kCaldoria44, kEast): +			if (!GameState.isTakenItemID(kKeyCard) && GameState.getOpenDoorRoom() == kNoRoomID) +				numHints = 1; +			break; +		case MakeRoomView(kCaldoria48, kNorth): +			if (!GameState.getCaldoriaRoofDoorOpen()) { +				if (_croppedMovie.isRunning()) // Bomb must be looping. +					numHints = 3; +				else if (GameState.isTakenItemID(kCardBomb)) +					numHints = 1; +			} +			break; +		case MakeRoomView(kCaldoria49, kEast): +		case MakeRoomView(kCaldoria54, kEast): +			numHints = 1; +			break; +		case MakeRoomView(kCaldoria49, kNorth): +			numHints = 1; +			break; +		} +	} +	 +	return numHints; +} + +Common::String Caldoria::getHintMovie(uint hintNum) { +	Common::String movieName = Neighborhood::getHintMovie(hintNum); + +	if (movieName.empty()) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kCaldoria44, kEast): +			return "Images/AI/Caldoria/X42WH2"; +		case MakeRoomView(kCaldoria48, kNorth): +			if (_croppedMovie.isRunning()) { // Bomb must be looping. +				if (hintNum == 1) +					return "Images/AI/Caldoria/X48ND1"; +				else if (hintNum == 2) +					return "Images/AI/Caldoria/X48ND2"; +				else if (GameState.isTakenItemID(kShieldBiochip)) +					return "Images/AI/Caldoria/X48ND3"; + +				// *** Doesn't work yet, need global movies. +				break; +			}  + +			return "Images/AI/Globals/XGLOB1A"; +		case MakeRoomView(kCaldoria49, kEast): +		case MakeRoomView(kCaldoria54, kEast): +			return "Images/AI/Caldoria/X49E"; +		case MakeRoomView(kCaldoria49, kNorth): +			return "Images/AI/Caldoria/X49NB2"; +		} +	} + +	return movieName; +} + +void Caldoria::updateCursor(const Common::Point where, const Hotspot *cursorSpot) { +	if (cursorSpot) { +		switch (cursorSpot->getObjectID()) { +		case kCa4DEnvironCloseSpotID: +			_vm->_cursor->setCurrentFrameIndex(2); +			return; +		case kCaldoriaKioskSpotID: +			_vm->_cursor->setCurrentFrameIndex(3); +			return; +		} +	} + +	Neighborhood::updateCursor(where, cursorSpot); +} + +Common::String Caldoria::getNavMovieName() { +	return "Images/Caldoria/Caldoria.movie"; +} + +Common::String Caldoria::getSoundSpotsName() { +	return "Sounds/Caldoria/Caldoria Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoria.h b/engines/pegasus/neighborhood/caldoria/caldoria.h new file mode 100644 index 0000000000..f02101ec3b --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria.h @@ -0,0 +1,525 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +static const TimeScale kCaldoriaMovieScale = 600; +static const TimeScale kCaldoriaFramesPerSecond = 15; +static const TimeScale kCaldoriaFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltCaldoriaNormal = 0; +static const AlternateID kAltCaldoriaRoofDoorBlown = 2; +static const AlternateID kAltCaldoriaSinclairDown = 3; + +// Room IDs. + +static const RoomID kCaldoria00 = 1; +static const RoomID kCaldoria01 = 2; +static const RoomID kCaldoria02 = 3; +static const RoomID kCaldoria03 = 4; +static const RoomID kCaldoria04 = 5; +static const RoomID kCaldoria05 = 6; +static const RoomID kCaldoria06 = 7; +static const RoomID kCaldoria07 = 8; +static const RoomID kCaldoria08 = 9; +static const RoomID kCaldoria09 = 10; +static const RoomID kCaldoria10 = 11; +static const RoomID kCaldoriaToilet = 12; +static const RoomID kCaldoria11 = 13; +static const RoomID kCaldoria12 = 14; +static const RoomID kCaldoriaVidPhone = 15; +static const RoomID kCaldoriaReplicator = 16; +static const RoomID kCaldoriaDrawers = 17; +static const RoomID kCaldoria13 = 18; +static const RoomID kCaldoria14 = 19; +static const RoomID kCaldoria15 = 20; +static const RoomID kCaldoria16 = 21; +static const RoomID kCaldoria17 = 22; +static const RoomID kCaldoria18 = 23; +static const RoomID kCaldoria19 = 24; +static const RoomID kCaldoria20 = 25; +static const RoomID kCaldoria21 = 26; +static const RoomID kCaldoria22 = 27; +static const RoomID kCaldoria23 = 28; +static const RoomID kCaldoria24 = 29; +static const RoomID kCaldoria25 = 30; +static const RoomID kCaldoria26 = 31; +static const RoomID kCaldoria27 = 32; +static const RoomID kCaldoria28 = 33; +static const RoomID kCaldoria29 = 34; +static const RoomID kCaldoria30 = 35; +static const RoomID kCaldoria31 = 36; +static const RoomID kCaldoria32 = 37; +static const RoomID kCaldoria33 = 38; +static const RoomID kCaldoria34 = 39; +static const RoomID kCaldoria35 = 40; +static const RoomID kCaldoria36 = 41; +static const RoomID kCaldoria37 = 42; +static const RoomID kCaldoria38 = 43; +static const RoomID kCaldoria39 = 44; +static const RoomID kCaldoria40 = 45; +static const RoomID kCaldoria41 = 46; +static const RoomID kCaldoriaBinoculars = 47; +static const RoomID kCaldoria42 = 48; +static const RoomID kCaldoriaKiosk = 49; +static const RoomID kCaldoria44 = 50; +static const RoomID kCaldoria45 = 51; +static const RoomID kCaldoria46 = 52; +static const RoomID kCaldoria47 = 53; +static const RoomID kCaldoria48 = 54; +static const RoomID kCaldoria49 = 55; +static const RoomID kCaldoria50 = 56; +static const RoomID kCaldoria51 = 57; +static const RoomID kCaldoria52 = 58; +static const RoomID kCaldoria53 = 59; +static const RoomID kCaldoria54 = 60; +static const RoomID kCaldoria55 = 61; +static const RoomID kCaldoria56 = 62; +static const RoomID kCaldoriaDeathRoom = 0; + +// Hot Spot Activation IDs. + +static const HotSpotActivationID kActivate4DClosed = 1; +static const HotSpotActivationID kActivate4DOpen = 2; +static const HotSpotActivationID kActivateMirrorReady = 3; +static const HotSpotActivationID kActivateStylistReady = 4; +static const HotSpotActivationID kActivateReplicatorReady = 5; +static const HotSpotActivationID kActivateOJOnThePad = 6; +static const HotSpotActivationID kActivateDrawersClosed = 7; +static const HotSpotActivationID kActivateRightOpen = 8; +static const HotSpotActivationID kActivateLeftOpen = 9; +static const HotSpotActivationID kActivateFocusedOnShip = 10; +static const HotSpotActivationID kActivateNotFocusedOnShip = 11; +static const HotSpotActivationID kActivateReadyForCard = 12; +static const HotSpotActivationID kActivateReadyToTransport = 13; +static const HotSpotActivationID kActivateRoofSlotEmpty = 14; +static const HotSpotActivationID kActivateZoomedOnSinclair = 15; + +// Hot Spot IDs. + +static const HotSpotID kCa4DEnvironOpenSpotID = 5000; +static const HotSpotID kCa4DEnvironCloseSpotID = 5001; +static const HotSpotID kCa4DVisualSpotID = 5002; +static const HotSpotID kCa4DAudioSpotID = 5003; +static const HotSpotID kCa4DChoice1SpotID = 5004; +static const HotSpotID kCa4DChoice2SpotID = 5005; +static const HotSpotID kCa4DChoice3SpotID = 5006; +static const HotSpotID kCa4DChoice4SpotID = 5007; +static const HotSpotID kCaBathroomMirrorSpotID = 5008; +static const HotSpotID kCaHairStyle1SpotID = 5009; +static const HotSpotID kCaHairStyle2SpotID = 5010; +static const HotSpotID kCaHairStyle3SpotID = 5011; +static const HotSpotID kCaShowerSpotID = 5012; +static const HotSpotID kCaBathroomToiletSpotID = 5013; +static const HotSpotID kCaldoriaVidPhoneSpotID = 5014; +static const HotSpotID kCaldoriaReplicatorSpotID = 5015; +static const HotSpotID kCaldoriaDrawersSpotID = 5016; +static const HotSpotID kCaldoriaVidPhoneOutSpotID = 5017; +static const HotSpotID kCaBedroomVidPhoneActivationSpotID = 5018; +static const HotSpotID kCaldoriaReplicatorOutSpotID = 5019; +static const HotSpotID kCaldoriaMakeOJSpotID = 5020; +static const HotSpotID kCaldoriaMakeStickyBunsSpotID = 5021; +static const HotSpotID kCaldoriaOrangeJuiceSpotID = 5022; +static const HotSpotID kCaldoriaOrangeJuiceDropSpotID = 5023; +static const HotSpotID kCaldoriaDrawersOutSpotID = 5024; +static const HotSpotID kCaldoriaLeftDrawerOpenSpotID = 5025; +static const HotSpotID kCaldoriaRightDrawerOpenSpotID = 5026; +static const HotSpotID kCaldoriaKeyCardSpotID = 5027; +static const HotSpotID kCaldoriaLeftDrawerCloseSpotID = 5028; +static const HotSpotID kCaldoriaRightDrawerWithKeysCloseSpotID = 5029; +static const HotSpotID kCaldoriaRightDrawerNoKeysCloseSpotID = 5030; +static const HotSpotID kCaldoriaFourthFloorElevatorSpotID = 5031; +static const HotSpotID kCaldoria20DoorbellSpotID = 5032; +static const HotSpotID kCaldoria21DoorbellSpotID = 5033; +static const HotSpotID kCaldoria26DoorbellSpotID = 5034; +static const HotSpotID kCaldoriaFourthFloorElevator1 = 5035; +static const HotSpotID kCaldoriaFourthFloorElevator2 = 5036; +static const HotSpotID kCaldoriaFourthFloorElevator3 = 5037; +static const HotSpotID kCaldoriaFourthFloorElevator4 = 5038; +static const HotSpotID kCaldoriaFourthFloorElevator5 = 5039; +static const HotSpotID kCaldoriaGroundElevator1 = 5040; +static const HotSpotID kCaldoriaGroundElevator2 = 5041; +static const HotSpotID kCaldoriaGroundElevator3 = 5042; +static const HotSpotID kCaldoriaGroundElevator4 = 5043; +static const HotSpotID kCaldoriaGroundElevator5 = 5044; +static const HotSpotID kCaldoria29DoorbellSpotID = 5045; +static const HotSpotID kCaldoria34DoorbellSpotID = 5046; +static const HotSpotID kCaldoria35DoorbellSpotID = 5047; +static const HotSpotID kCaldoriaGroundElevatorSpotID = 5048; +static const HotSpotID kCaldoriaBinocularZoomInSpotID = 5049; +static const HotSpotID kCaldoriaBinocularsOutSpotID = 5050; +static const HotSpotID kCaldoriaZoomInOnShipSpotID = 5051; +static const HotSpotID kCaldoriaKioskSpotID = 5052; +static const HotSpotID kCaldoriaKioskOutSpotID = 5053; +static const HotSpotID kCaldoriaKioskInfoSpotID = 5054; +static const HotSpotID kCaldoriaGTCardDropSpotID = 5055; +static const HotSpotID kCaldoriaGTTokyoSpotID = 5056; +static const HotSpotID kCaldoriaGTTSASpotID = 5057; +static const HotSpotID kCaldoriaGTBeachSpotID = 5058; +static const HotSpotID kCaldoriaGTOtherSpotID = 5059; +static const HotSpotID kCaldoriaRoofElevator1 = 5060; +static const HotSpotID kCaldoriaRoofElevator2 = 5061; +static const HotSpotID kCaldoriaRoofElevator3 = 5062; +static const HotSpotID kCaldoriaRoofElevator4 = 5063; +static const HotSpotID kCaldoriaRoofElevator5 = 5064; +static const HotSpotID kCaldoriaRoofElevatorSpotID = 5065; +static const HotSpotID kCaldoriaRoofDoorSpotID = 5066; +static const HotSpotID kCaldoriaRoofCardDropSpotID = 5067; +static const HotSpotID kCaldoria53EastSinclairTargetSpotID = 5068; + +// Extra sequence IDs. + +static const ExtraID kCaldoriaWakeUpView1 = 0; +static const ExtraID kCaldoria00WakeUp1 = 1; +static const ExtraID kCaldoria00WakeUp2 = 2; +static const ExtraID kCaldoria00SitDown = 3; +static const ExtraID k4DEnvironOpenToINN = 4; +static const ExtraID k4DINNInterruption = 5; +static const ExtraID k4DINNIntro = 6; +static const ExtraID k4DINNMarkJohnson = 7; +static const ExtraID k4DINNMeganLove = 8; +static const ExtraID k4DINNFadeOut = 9; +static const ExtraID k4DEnvironOpenFromINN = 10; +static const ExtraID k4DEnvironOpen = 11; +static const ExtraID k4DEnvironOpenView = 12; +static const ExtraID k4DEnvironClose = 13; +static const ExtraID k4DIslandLoop = 14; +static const ExtraID k4DDesertLoop = 15; +static const ExtraID k4DMountainLoop = 16; +static const ExtraID k4DIsland1ToIsland0 = 17; +static const ExtraID k4DIsland2ToIsland0 = 18; +static const ExtraID k4DIsland0ToDesert0 = 19; +static const ExtraID k4DIsland1ToDesert0 = 20; +static const ExtraID k4DIsland2ToDesert0 = 21; +static const ExtraID k4DIsland0ToMountain0 = 22; +static const ExtraID k4DIsland1ToMountain0 = 23; +static const ExtraID k4DIsland2ToMountain0 = 24; +static const ExtraID k4DDesert0ToIsland0 = 25; +static const ExtraID k4DDesert1ToIsland0 = 26; +static const ExtraID k4DDesert2ToIsland0 = 27; +static const ExtraID k4DDesert0ToMountain0 = 28; +static const ExtraID k4DDesert1ToMountain0 = 29; +static const ExtraID k4DDesert2ToMountain0 = 30; +static const ExtraID k4DMountain0ToIsland0 = 31; +static const ExtraID k4DMountain1ToIsland0 = 32; +static const ExtraID k4DMountain2ToIsland0 = 33; +static const ExtraID k4DMountain0ToDesert0 = 34; +static const ExtraID k4DMountain1ToDesert0 = 35; +static const ExtraID k4DMountain2ToDesert0 = 36; +static const ExtraID kCaBathroomGreeting = 37; +static const ExtraID kCaBathroomBodyFat = 38; +static const ExtraID kCaBathroomStylistIntro = 39; +static const ExtraID kCaBathroomRetrothrash = 40; +static const ExtraID kCaBathroomRetrothrashReturn = 41; +static const ExtraID kCaBathroomGeoWave = 42; +static const ExtraID kCaBathroomGeoWaveReturn = 43; +static const ExtraID kCaBathroomAgencyStandard = 44; +static const ExtraID kCaldoriaShowerTitle = 45; +static const ExtraID kCaldoriaShowerButton = 46; +static const ExtraID kCaldoriaShowerDown = 47; +static const ExtraID kCaldoriaShowerUp = 48; +static const ExtraID kCaBedroomVidPhone = 49; +static const ExtraID kCaBedroomMessage1 = 50; +static const ExtraID kCaBedroomMessage2 = 51; +static const ExtraID kCreateOrangeJuice = 52; +static const ExtraID kDisposeOrangeJuice = 53; +static const ExtraID kReplicatorNorthViewWithOJ = 54; +static const ExtraID kLeftDrawerOpen = 55; +static const ExtraID kLeftDrawerClose = 56; +static const ExtraID kRightDrawerOpenWithKeys = 57; +static const ExtraID kRightDrawerCloseWithKeys = 58; +static const ExtraID kRightDrawerOpenNoKeys = 59; +static const ExtraID kRightDrawerCloseNoKeys = 60; +static const ExtraID kRightDrawerOpenViewWithKeys = 61; +static const ExtraID kRightDrawerOpenViewNoKeys = 62; +static const ExtraID kCaldoria16ElevatorUp = 63; +static const ExtraID kCaldoria16ElevatorDown = 64; +static const ExtraID kCaldoria16SouthViewWithElevator = 65; +static const ExtraID kCaldoria20Doorbell = 66; +static const ExtraID kCaldoria21Doorbell = 67; +static const ExtraID kCaldoria26Doorbell = 68; +static const ExtraID kCaldoriaFourthToGround = 69; +static const ExtraID kCaldoriaRoofToFourth = 70; +static const ExtraID kCaldoriaRoofToGround = 71; +static const ExtraID kCaldoriaGroundToFourth = 72; +static const ExtraID kCaldoriaGroundToRoof = 73; +static const ExtraID kCaldoriaFourthToRoof = 74; +static const ExtraID kCaldoria29Doorbell = 75; +static const ExtraID kCaldoria34Doorbell = 76; +static const ExtraID kCaldoria35Doorbell = 77; +static const ExtraID kBinocularsZoomInOnShip = 78; +static const ExtraID kCaldoriaKioskVideo = 79; +static const ExtraID kCaldoriaTransporterArrowLoop = 80; +static const ExtraID kArriveAtCaldoriaFromTSA = 81; +static const ExtraID kCaGTOtherChoice = 82; +static const ExtraID kCaGTCardSwipe = 83; +static const ExtraID kCaGTSelectTSA = 84; +static const ExtraID kCaGTFryTheFly = 85; +static const ExtraID kCaGTGoToTSA = 86; +static const ExtraID kCaGTSelectBeach = 87; +static const ExtraID kCaGTGoToBeach = 88; +static const ExtraID kCaGTArriveAtBeach = 89; +static const ExtraID kCaGTSelectTokyo = 90; +static const ExtraID kCaGTGoToTokyo = 91; +static const ExtraID kCaGTArriveAtTokyo = 92; +static const ExtraID kCa48NorthRooftopClosed = 93; +static const ExtraID kCa48NorthExplosion = 94; +static const ExtraID kCa48NorthExplosionDeath = 95; +static const ExtraID kCa49NorthVoiceAnalysis = 96; +static const ExtraID kCa50SinclairShoots = 97; +static const ExtraID kCa53EastZoomToSinclair = 98; +static const ExtraID kCa53EastDeath2 = 99; +static const ExtraID kCa53EastShootSinclair = 100; +static const ExtraID kCa53EastZoomOutFromSinclair = 101; +static const ExtraID kCa54SouthDeath = 102; +static const ExtraID kCaldoria56BombStage1 = 103; +static const ExtraID kCaldoria56BombStage2 = 104; +static const ExtraID kCaldoria56BombStage3 = 105; +static const ExtraID kCaldoria56BombStage4 = 106; +static const ExtraID kCaldoria56BombStage5 = 107; +static const ExtraID kCaldoria56BombStage6 = 108; +static const ExtraID kCaldoria56BombStage7 = 109; +static const ExtraID kCaldoria56BombExplodes = 110; + +// Caldoria interactions. + +static const InteractionID kCaldoria4DInteractionID = 0; +static const InteractionID kCaldoriaBombInteractionID = 1; +static const InteractionID kCaldoriaMessagesInteractionID = 2; +static const InteractionID kCaldoriaMirrorInteractionID = 3; + +// Caldoria: + +static const DisplayOrder kVidPhoneOrder = kMonitorLayer; +static const DisplayOrder k4DSpritesOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaMessagesOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaElevatorOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaA05LightLoopOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaA07LightLoopOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaBombGridOrder = kMonitorLayer; +static const DisplayOrder kCaldoriaBombTimerOrder = kCaldoriaBombGridOrder + 1; + +///////////////////////////////////////////// +// +// Caldoria + +static const CoordType kCaldoriaVidPhoneLeft = kNavAreaLeft + 105; +static const CoordType kCaldoriaVidPhoneTop = kNavAreaTop + 28; + +static const CoordType kCaldoria4DSpritesLeft = kNavAreaLeft + 10; +static const CoordType kCaldoria4DSpritesTop = kNavAreaTop + 142; + +static const CoordType kCaldoriaMessageLeft = kNavAreaLeft + 202; +static const CoordType kCaldoriaMessageTop = kNavAreaTop + 26; + +static const CoordType kCaldoriaElevatorLeft = kNavAreaLeft + 407; +static const CoordType kCaldoriaElevatorTop = kNavAreaTop + 138; + +static const CoordType kCaldoriaA05LightLoopLeft = kNavAreaLeft + 213; +static const CoordType kCaldoriaA05LightLoopTop = kNavAreaTop + 215; + +static const CoordType kCaldoriaA07LightLoopLeft = kNavAreaLeft + 414; +static const CoordType kCaldoriaA07LightLoopTop = kNavAreaTop + 215; + +static const CoordType kCaldoriaGunSpriteLeft = kNavAreaLeft + 276; +static const CoordType kCaldoriaGunSpriteTop = kNavAreaTop + 115; + +static const CoordType kCaldoria11MessageLoopLeft = kNavAreaLeft + 135; +static const CoordType kCaldoria11MessageLoopTop = kNavAreaTop + 214; + +static const CoordType kCaldoria12MessageLoopLeft = kNavAreaLeft + 209; +static const CoordType kCaldoria12MessageLoopTop = kNavAreaTop + 170; + +static const CoordType kCaldoria13MessageLoopLeft = kNavAreaLeft + 480; +static const CoordType kCaldoria13MessageLoopTop = kNavAreaTop + 191; + +static const CoordType kCaldoria14MessageLoopLeft = kNavAreaLeft + 248; +static const CoordType kCaldoria14MessageLoopTop = kNavAreaTop + 191; + +static const CoordType kCaldoria48CardBombLoopLeft = kNavAreaLeft + 337; +static const CoordType kCaldoria48CardBombLoopTop = kNavAreaTop + 205; + +static const CoordType kCaldoriaBombGridLeft = kNavAreaLeft + 290; +static const CoordType kCaldoriaBombGridTop = kNavAreaTop + 58; + +static const CoordType kCaldoriaBombTimerLeft = kNavAreaLeft + 58; +static const CoordType kCaldoriaBombTimerTop = kNavAreaTop + 204; + +// Caldoria display IDs. + +static const DisplayElementID kCaldoriaVidPhoneID = kNeighborhoodDisplayID; +static const DisplayElementID kCaldoria4DSpritesID = kCaldoriaVidPhoneID + 1; +static const DisplayElementID kCaldoriaMessagesID = kCaldoria4DSpritesID + 1; +static const DisplayElementID kCaldoriaUtilityID = kCaldoriaMessagesID + 1; +static const DisplayElementID kCaldoriaBombGridID = kCaldoriaUtilityID + 1; +static const DisplayElementID kCaldoriaBombTimerID = kCaldoriaBombGridID + 1; + +static const TimeValue kCaldoria4DBlankChoiceIn = 29730; +static const TimeValue kCaldoria4DBlankChoiceOut = 33910; + +class Caldoria; + +class SinclairCallBack : public TimeBaseCallBack { +public: +	SinclairCallBack(Caldoria *); +	~SinclairCallBack() {} + +protected: +	virtual void callBack(); + +	Caldoria *_caldoria; +}; + +class Caldoria : public Neighborhood { +friend class SinclairCallBack; +friend void doorBombTimerExpiredFunction(FunctionPtr *, void *); +friend void sinclairTimerExpiredFunction(FunctionPtr *, void *); + +public: +	Caldoria(InputHandler *, PegasusEngine *); +	virtual ~Caldoria(); +	 +	virtual uint16 getDateResID() const; +	 +	void pickedUpItem(Item *); +	 +	virtual GameInteraction *makeInteraction(const InteractionID); +	 +	virtual Common::String getBriefingMovie(); +	virtual Common::String getEnvScanMovie(); +	virtual uint getNumHints(); +	virtual Common::String getHintMovie(uint); +	void loadAmbientLoops(); +	bool wantsCursor(); +	void flushGameState(); + +	void checkContinuePoint(const RoomID, const DirectionConstant); + +protected: +	enum { +		kCaldoriaPrivate4DSystemOpenFlag, +		kCaloriaPrivateLeftDrawerOpenFlag, +		kCaldoriaPrivateRightDrawerOpenFlag, +		kCaldoriaPrivateReadyToShootFlag, +		kCaldoriaPrivateZoomingToBombFlag, +		kCaldoriaPrivateCanOpenElevatorDoorFlag, +		kCaldoriaPrivateSinclairTimerExpiredFlag, +		kCaldoriaPrivateSeen13CarFlag, +		kCaldoriaPrivateSeen14CarFlag, +		kCaldoriaPrivateSeen18CarFlag, +		kCaldoriaPrivateSeen23CarFlag, +		kCaldoriaPrivateSeen33CarFlag, +		kCaldoriaPrivateSeen36CarFlag, +		kCaldoriaPrivateSeen41NorthCarFlag, +		kCaldoriaPrivateSeen41EastCarFlag, +		kCaldoriaPrivateSeen41WestCarFlag, +		kNumCaldoriaPrivateFlags +	}; +	 +	void init(); +	void start(); +	 +	void setUpRoofTop(); + +	void setUpAIRules(); +	void doAIRecalibration(); +	TimeValue getViewTime(const RoomID, const DirectionConstant); +	void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); +	void startSpotOnceOnly(TimeValue, TimeValue); +	void startExitMovie(const ExitTable::Entry &); +	void startZoomMovie(const ZoomTable::Entry &); +	void startDoorOpenMovie(const TimeValue, const TimeValue); +	void startTurnPush(const TurnDirection, const TimeValue, const DirectionConstant); +	void bumpIntoWall(); +	int16 getStaticCompassAngle(const RoomID, const DirectionConstant); +	void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); +	void getZoomCompassMove(const ZoomTable::Entry &, FaderMoveSpec &); +	void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); +	void spotCompleted(); +	void arriveAt(const RoomID, const DirectionConstant); +	void arriveAtCaldoria00(); +	void arriveAtCaldoriaToilet(); +	void arriveAtCaldoria44(); +	void arriveAtCaldoria49(); +	void arriveAtCaldoria56(); +	void arriveAtCaldoriaDeath(); +	void turnTo(const DirectionConstant); +	void zoomTo(const Hotspot *); +	void downButton(const Input &); +	void receiveNotification(Notification *, const NotificationFlags); +	InputBits getInputFilter(); +	void activateHotspots(); +	void clickInHotspot(const Input &, const Hotspot *); +	void newInteraction(const InteractionID); +	 +	void clickOnDoorbell(const HotSpotID); +	 +	Hotspot *getItemScreenSpot(Item *, DisplayElement *); +	void dropItemIntoRoom(Item *, Hotspot *); +	void takeElevator(uint, uint); +	void updateElevatorMovie(); +	void openElevatorMovie(); +	void emptyOJGlass(); +	void closeDoorOffScreen(const RoomID, const DirectionConstant); +	void doorBombTimerExpired(); +	void sinclairTimerExpired(); +	void checkSinclairShootsOS(); +	void setUpSinclairLoops(); +	void zoomToSinclair(); +	void playEndMessage(); +	void checkInterruptSinclair(); +	 +	CanOpenDoorReason canOpenDoor(DoorTable::Entry &); +	void doorOpened(); +	 +	void updateCursor(const Common::Point, const Hotspot *); + +	FlagsArray<uint16, kNumCaldoriaPrivateFlags> _privateFlags; + +	const Hotspot *_zoomOutSpot; +	 +	FuseFunction _utilityFuse; + +	long _sinclairLoopCount; +	long _numSinclairLoops; + +	Sprite *_gunSprite; + +	SinclairCallBack _sinclairInterrupt; + +	Common::String getSoundSpotsName(); +	Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp new file mode 100644 index 0000000000..d8ac3b08cb --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.cpp @@ -0,0 +1,369 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/ai/ai_area.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoria4dsystem.h" + +namespace Pegasus { + +static const TimeValue kSwitchableSlop = 3 * kCaldoriaFrameDuration; +// Two seconds - some slop +static const TimeValue kSwitchableDuration = kCaldoriaMovieScale * 2 - kSwitchableSlop; +// Twelve frames + some slop +static const TimeValue kNonswitchableDuration = kCaldoriaFrameDuration * 12 + kSwitchableSlop; + +static const TimeValue kSwitchable1Start = 0; +static const TimeValue kSwitchable1Stop = kSwitchable1Start + kSwitchableDuration; + +static const TimeValue kSwitchable2Start = kSwitchable1Stop + kNonswitchableDuration; +static const TimeValue kSwitchable2Stop = kSwitchable2Start + kSwitchableDuration; + +static const TimeValue kSwitchable3Start = kSwitchable2Stop + kNonswitchableDuration; +static const TimeValue kSwitchable3Stop = kSwitchable3Start + kSwitchableDuration; + +static const NotificationFlags kVidPhoneDoneFlag = 1; + +static const TimeValue kRockMusicLoopIn = 0; +static const TimeValue kRockMusicLoopOut = 2088; + +static const TimeValue kOrchestralMusicLoopIn = 2088; +static const TimeValue kOrchestralMusicLoopOut = 4985; + +static const TimeValue kRhythmsMusicLoopIn = 4985; +static const TimeValue kRhythmsMusicLoopOut = 6824; + +static const TimeValue kAcousticMusicLoopIn = 6824; +static const TimeValue kAcousticMusicLoopOut = 9387; + +enum { +	k4DVideoMenu, +	k4DAudioMenu, +	k4DShuttingDown, + +	// These constants are the exact frame numbers of the sprite movie. +	k4DRockChoice = 0, +	k4DOrchestralChoice, +	k4DRhythmsChoice, +	k4DAcousticChoice, +	k4DIslandChoice, +	k4DDesertChoice, +	k4DMountainChoice, + +	k4DFirstVideoChoice = k4DIslandChoice +}; + +static const ExtraID s_transitionExtras0[3][3] = { +	{ 0xffffffff, k4DIsland0ToDesert0, k4DIsland0ToMountain0 }, +	{ k4DDesert0ToIsland0, 0xffffffff, k4DDesert0ToMountain0 }, +	{ k4DMountain0ToIsland0, k4DMountain0ToDesert0, 0xffffffff } +}; + +static const ExtraID s_transitionExtras1[3][3] = { +	{ 0xffffffff, k4DIsland1ToDesert0, k4DIsland1ToMountain0 }, +	{ k4DDesert1ToIsland0, 0xffffffff, k4DDesert1ToMountain0 }, +	{ k4DMountain1ToIsland0, k4DMountain1ToDesert0, 0xffffffff } +}; + +static const ExtraID s_transitionExtras2[3][3] = { +	{ 0xffffffff, k4DIsland2ToDesert0, k4DIsland2ToMountain0 }, +	{ k4DDesert2ToIsland0, 0xffffffff, k4DDesert2ToMountain0 }, +	{ k4DMountain2ToIsland0, k4DMountain2ToDesert0, 0xffffffff } +}; + +static const ExtraID s_shutDownExtras[3][3] = { +	{ 0xffffffff, k4DIsland1ToIsland0, k4DIsland2ToIsland0 }, +	{ k4DDesert0ToIsland0, k4DDesert1ToIsland0, k4DDesert2ToIsland0 }, +	{ k4DMountain0ToIsland0, k4DMountain1ToIsland0, k4DMountain2ToIsland0 } +}; + +Caldoria4DSystem::Caldoria4DSystem(Neighborhood *owner) : GameInteraction(kCaldoria4DInteractionID, owner), +		_4DSpritesMovie(kCaldoria4DSpritesID) { +	g_AIArea->lockAIOut(); +} + +Caldoria4DSystem::~Caldoria4DSystem() { +	g_AIArea->unlockAI(); +} + +void Caldoria4DSystem::openInteraction() { +	_whichMenu = k4DVideoMenu; +	_videoChoice = k4DIslandChoice; +	_audioChoice = k4DRockChoice; +	_clickedHotspotID = kNoHotSpotID; + +	_4DSpritesMovie.initFromMovieFile("Images/Caldoria/4D Sprites", true); +	_4DSpritesMovie.moveElementTo(kCaldoria4DSpritesLeft, kCaldoria4DSpritesTop); +	_4DSpritesMovie.setDisplayOrder(k4DSpritesOrder); +	_4DSpritesMovie.startDisplaying(); +	_4DSpritesMovie.show(); +	 +	_4DSpritesScale = _4DSpritesMovie.getScale(); + +	_neighborhoodNotification = _owner->getNeighborhoodNotification(); +	_neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); + +	startIdling(); +} + +void Caldoria4DSystem::loopExtra(const ExtraID extraID) { +	ExtraTable::Entry extraEntry; +	 +	_owner->getExtraEntry(extraID, extraEntry); +	_loopStart = extraEntry.movieStart; +	_owner->loopExtraSequence(extraID); +} + +void Caldoria4DSystem::useIdleTime() { +	if (_whichMenu == k4DShuttingDown) { +		TimeValue movieTime = _owner->getNavMovie()->getTime() - _loopStart; +		ExtraID extraID; + +		if (movieTime < kSwitchable1Stop) +			extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][0]; +		else if (movieTime >= kSwitchable2Start && movieTime < kSwitchable2Stop) +			extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][1]; +		else if (movieTime >= kSwitchable3Start && movieTime < kSwitchable3Stop) +			extraID = s_shutDownExtras[_videoChoice - k4DFirstVideoChoice][2]; +		else +			extraID = 0xffffffff; + +		if (extraID != 0xffffffff) { +			setSpritesMovie(); +			_loopStart = 0; +			_owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); +		} +	} else if (_clickedHotspotID != kNoHotSpotID) { +		TimeValue movieTime = _owner->getNavMovie()->getTime() - _loopStart; +		ExtraID extraID; + +		if (movieTime < kSwitchable1Stop) { +			extraID = s_transitionExtras0[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; +			_clickedHotspotID = kNoHotSpotID; +		} else if (movieTime >= kSwitchable2Start && movieTime < kSwitchable2Stop) { +			extraID = s_transitionExtras1[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; +			_clickedHotspotID = kNoHotSpotID; +		} else if (movieTime >= kSwitchable3Start && movieTime < kSwitchable3Stop) { +			extraID = s_transitionExtras2[_videoChoice - k4DFirstVideoChoice][_clickedHotspotID - kCa4DChoice1SpotID]; +			_clickedHotspotID = kNoHotSpotID; +		} else +			extraID = 0xffffffff; + +		if (extraID != 0xffffffff) { +			switch (extraID) { +			case k4DDesert0ToIsland0: +			case k4DMountain0ToIsland0: +			case k4DDesert1ToIsland0: +			case k4DMountain1ToIsland0: +			case k4DDesert2ToIsland0: +			case k4DMountain2ToIsland0: +				_videoChoice = k4DIslandChoice; +				break; +			case k4DIsland0ToDesert0: +			case k4DMountain0ToDesert0: +			case k4DIsland1ToDesert0: +			case k4DMountain1ToDesert0: +			case k4DIsland2ToDesert0: +			case k4DMountain2ToDesert0: +				_videoChoice = k4DDesertChoice; +				break; +			case k4DDesert0ToMountain0: +			case k4DIsland0ToMountain0: +			case k4DIsland1ToMountain0: +			case k4DDesert1ToMountain0: +			case k4DIsland2ToMountain0: +			case k4DDesert2ToMountain0: +				_videoChoice = k4DMountainChoice; +				break; +			} + +			setSpritesMovie(); +			_loopStart = 0; +			_owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); +		} +	} +} + +void Caldoria4DSystem::initInteraction() { +	setSpritesMovie(); +	 +	_owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff"); +	loopExtra(k4DIslandLoop); +} + +void Caldoria4DSystem::closeInteraction() { +	stopIdling(); +	_neighborhoodNotification->cancelNotification(this); +	_4DSpritesMovie.releaseMovie(); +	_owner->loadAmbientLoops(); +} + +void Caldoria4DSystem::setSpritesMovie() { +	if (_whichMenu == k4DShuttingDown) +		_4DSpritesMovie.setTime(_4DSpritesScale * k4DIslandChoice); +	else if (_whichMenu == k4DVideoMenu) +		_4DSpritesMovie.setTime(_4DSpritesScale * _videoChoice); +	else if (_whichMenu == k4DAudioMenu) +		_4DSpritesMovie.setTime(_4DSpritesScale * _audioChoice); + +	_4DSpritesMovie.redrawMovieWorld(); +} + +void Caldoria4DSystem::handleInput(const Input &input, const Hotspot *cursorSpot) { +	if (input.downButtonAnyDown()) +		return; +	if (input.anyDirectionInput()) +		shutDown4DSystem(); +	else +		GameInteraction::handleInput(input, cursorSpot); +} + +void Caldoria4DSystem::activateHotspots() { +	GameInteraction::activateHotspots(); +	if (_whichMenu == k4DAudioMenu) +		g_allHotspots.activateOneHotspot(kCa4DChoice4SpotID); +} + +void Caldoria4DSystem::clickInHotspot(const Input &input, const Hotspot *spot) { +	switch (spot->getObjectID()) { +	case kCa4DVisualSpotID: +		if (_whichMenu == k4DAudioMenu) { +			_whichMenu = k4DVideoMenu; +			setSpritesMovie(); +		} +		break; +	case kCa4DAudioSpotID: +		if (_whichMenu == k4DVideoMenu) { +			_whichMenu = k4DAudioMenu; +			setSpritesMovie(); +		} +		break; +	case kCa4DChoice1SpotID: +		if (_whichMenu == k4DVideoMenu) +			makeIslandChoice(); +		else if (_whichMenu == k4DAudioMenu) +			makeRockChoice(); +		break; +	case kCa4DChoice2SpotID: +		if (_whichMenu == k4DVideoMenu) +			makeDesertChoice(); +		else if (_whichMenu == k4DAudioMenu) +			makeOrchestralChoice(); +		break; +	case kCa4DChoice3SpotID: +		if (_whichMenu == k4DVideoMenu) +			makeMountainChoice(); +		else if (_whichMenu == k4DAudioMenu) +			makeRhythmsChoice(); +		break; +	case kCa4DChoice4SpotID: +		if (_whichMenu == k4DAudioMenu) +			makeAcousticChoice(); +		else +			_owner->playSpotSoundSync(kCaldoria4DBlankChoiceIn, kCaldoria4DBlankChoiceOut); +		break; +	default: +		GameInteraction::clickInHotspot(input, spot); +	} +} + +void Caldoria4DSystem::receiveNotification(Notification *, const NotificationFlags) { +	if (_whichMenu == k4DShuttingDown) { +		_owner->requestDeleteCurrentInteraction(); +	} else { +		uint32 extraID; + +		switch (_videoChoice) { +		case k4DIslandChoice: +			extraID = k4DIslandLoop; +			break; +		case k4DDesertChoice: +			extraID = k4DDesertLoop; +			break; +		case k4DMountainChoice: +			extraID = k4DMountainLoop; +			break; +		default: +			extraID = 0xffffffff; +			break; +		} + +		if (extraID != 0xffffffff) +			loopExtra(extraID); +	} +} + +void Caldoria4DSystem::makeIslandChoice() { +	if (_videoChoice != k4DIslandChoice && _clickedHotspotID == kNoHotSpotID) +		_clickedHotspotID = kCa4DChoice1SpotID; +} + +void Caldoria4DSystem::makeDesertChoice() { +	if (_videoChoice != k4DDesertChoice && _clickedHotspotID == kNoHotSpotID) +		_clickedHotspotID = kCa4DChoice2SpotID; +} + +void Caldoria4DSystem::makeMountainChoice() { +	if (_videoChoice != k4DMountainChoice && _clickedHotspotID == kNoHotSpotID) +		_clickedHotspotID = kCa4DChoice3SpotID; +} + +void Caldoria4DSystem::makeRockChoice() { +	if (_audioChoice != k4DRockChoice) { +		_audioChoice = k4DRockChoice; +		setSpritesMovie(); +		_owner->loadLoopSound1("Sounds/Caldoria/Rock.aiff"); +	} +} + +void Caldoria4DSystem::makeOrchestralChoice() { +	if (_audioChoice != k4DOrchestralChoice) { +		_audioChoice = k4DOrchestralChoice; +		setSpritesMovie(); +		_owner->loadLoopSound1("Sounds/Caldoria/Orchestral.aiff"); +	} +} + +void Caldoria4DSystem::makeRhythmsChoice() { +	if (_audioChoice != k4DRhythmsChoice) { +		_audioChoice = k4DRhythmsChoice; +		setSpritesMovie(); +		_owner->loadLoopSound1("Sounds/Caldoria/Rhythms.aiff"); +	} +} + +void Caldoria4DSystem::makeAcousticChoice() { +	if (_audioChoice != k4DAcousticChoice) { +		_audioChoice = k4DAcousticChoice; +		setSpritesMovie(); +		_owner->loadLoopSound1("Sounds/Caldoria/Acoustic.aiff"); +	} +} + +void Caldoria4DSystem::shutDown4DSystem() { +	_whichMenu = k4DShuttingDown; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h new file mode 100644 index 0000000000..63de7e1c4e --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoria4dsystem.h @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA4DSYSTEM_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIA4DSYSTEM_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class Caldoria4DSystem : public GameInteraction, private Idler, public NotificationReceiver { +public: +	Caldoria4DSystem(Neighborhood *); +	virtual ~Caldoria4DSystem(); + +	void shutDown4DSystem(); + +protected: +	void openInteraction(); +	void initInteraction(); +	void closeInteraction(); +	 +	void handleInput(const Input &, const Hotspot *); +	void activateHotspots(); +	void clickInHotspot(const Input &, const Hotspot *); +	void receiveNotification(Notification *, const NotificationFlags); +	void setSpritesMovie(); +	void makeIslandChoice(); +	void makeRockChoice(); +	void makeMountainChoice(); +	void makeOrchestralChoice(); +	void makeDesertChoice(); +	void makeRhythmsChoice(); +	void makeAcousticChoice(); +	 +	void useIdleTime(); +	void loopExtra(const ExtraID); +	 +	Movie _4DSpritesMovie; +	TimeScale _4DSpritesScale; +	uint _whichMenu; +	uint _videoChoice; +	uint _audioChoice; +	Notification *_neighborhoodNotification; +	TimeValue _loopStart; +	HotSpotID _clickedHotspotID; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp b/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp new file mode 100644 index 0000000000..c9ee68aefb --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriabomb.cpp @@ -0,0 +1,1442 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriabomb.h" + +namespace Pegasus { + +// Bomb game PICTs: + +static const uint16 kYellowBombPICTBaseID = 700; +static const uint16 kRedBombPICTBaseID = 709; +static const uint16 kTimerLeftPICTID = 718; +static const uint16 kTimerRightPICTID = 719; + +static const uint32 kFlashOnTime = 20; +static const uint32 kFlashOffTime = 10; + +static const uint32 kOnTime1 = kFlashOnTime; +static const uint32 kOffTime1 = kOnTime1 + kFlashOffTime; +static const uint32 kOnTime2 = kOffTime1 + kFlashOnTime; +static const uint32 kOffTime2 = kOnTime2 + kFlashOffTime; +static const uint32 kOnTime3 = kOffTime2 + kFlashOnTime; +static const uint32 kOffTime3 = kOnTime3 + kFlashOffTime; +static const uint32 kOnTime4 = kOffTime3 + kFlashOnTime; + +static const HotSpotID kVertextHotSpotBaseID = 10000; + +static const CoordType kVertextHotSpotWidth = 24; +static const CoordType kVertextHotSpotHeight = 24; + +static const NotificationFlags kBombTimerExpiredFlag = 1; + +static const VertexType kBombLevelOne[] = { +	0, 1, 0, 1, 0,			// hot vertices first. +	1, 1, 0, 1, 1, +	1, 1, 0, 1, 0, +	1, 1, 0, 1, 1, +	0, 1, 0, 1, 0, +	 +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	 +	9,						// 9 edges in this level +	 +	kEdgeOneFourth, +	3, +	1, 2, 3, +	0, 0, +	 +	kEdgeOneFourth, +	5, +	5, 6, 7, 8, 9, +	0, 0, 0, 0, +	 +	kEdgeOneFourth, +	4, +	10, 11, 12, 13, +	0, 0, 0, +	 +	kEdgeOneFourth, +	5, +	15, 16, 17, 18, 19, +	0, 0, 0, 0, +	 +	kEdgeOneFourth, +	3, +	21, 22, 23, +	0, 0, +	 +	kEdgeOneHalf, +	3, +	5, 10, 15, +	0, 0, +	 +	kEdgeOneHalf, +	5, +	1, 6, 11, 16, 21, +	0, 0, 0, 0, +	 +	kEdgeOneHalf, +	5, +	3, 8, 13, 18, 23, +	0, 0, 0, 0, +	 +	kEdgeOneHalf, +	3, +	9, 14, 19, +	0, 0 +}; + +static const VertexType kBombLevelTwo[] = { +	0, 1, 0, 1, 0, +	1, 1, 1, 0, 1, +	0, 0, 0, 1, 0, +	1, 1, 1, 0, 1, +	0, 1, 0, 1, 0, +	 +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	 +	15, +	 +	kEdgeOneEighth, +	2, +	5, 1, +	0, +	 +	kEdgeOneEighth, +	3, +	17, 13, 9, +	0, 0, +	 +	kEdgeOneEighth, +	2, +	23, 19, +	0, +	 +	kEdgeThreeEighths, +	2, +	3, 9, +	0, +	 +	kEdgeThreeEighths, +	3, +	7, 13, 19, +	0, 0, +	 +	kEdgeThreeEighths, +	2, +	15, 21, +	0, +	 +	kEdgeOneFourth, +	3, +	1, 2, 3, +	0, 0, +	 +	kEdgeOneFourth, +	4, +	6, 7, 8, 9, +	0, 0, 0, +	 +	kEdgeOneFourth, +	4, +	16, 17, 18, 19, +	0, 0, 0, +	 +	kEdgeOneFourth, +	3, +	21, 22, 23, +	0, 0, +	 +	kEdgeOneHalf, +	3, +	5, 10, 15, +	0, 0, +	 +	kEdgeOneHalf, +	2, +	1, 6, +	0, +	 +	kEdgeOneHalf, +	3, +	7, 12, 17, +	0, 0, +	 +	kEdgeOneHalf, +	3, +	9, 14, 19, +	0, 0, +	 +	kEdgeOneHalf, +	2, +	16, 21, +	0 +}; + +static const VertexType kBombLevelThree[] = { +	0, 1, 0, 1, 0, +	1, 1, 1, 1, 1, +	0, 1, 1, 0, 0, +	1, 1, 1, 1, 1, +	0, 1, 0, 1, 0, +	 +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	 +	22, +	 +	kEdgeThreeSixteenths, +	3, +	15, 12, 9, +	0, 0, +	 +	kEdgeFiveSixteenths, +	3, +	5, 12, 19, +	0, 0, +	 +	kEdgeOneEighth, +	2, +	5, 1, +	0, +	 +	kEdgeOneEighth, +	2, +	7, 3, +	0, +	 +	kEdgeOneEighth, +	2, +	15, 11, +	0, +	 +	kEdgeOneEighth, +	2, +	21, 17, +	0, +	 +	kEdgeOneEighth, +	2, +	23, 19, +	0, +	 +	kEdgeThreeEighths, +	2, +	1, 7, +	0, +	 +	kEdgeThreeEighths, +	2, +	3, 9, +	0, +	 +	kEdgeThreeEighths, +	2, +	5, 11, +	0, +	 +	kEdgeThreeEighths, +	2, +	15, 21, +	0, +	 +	kEdgeThreeEighths, +	2, +	17, 23, +	0, +	 +	kEdgeOneFourth, +	3, +	1, 2, 3, +	0, 0, +	 +	kEdgeOneFourth, +	2, +	5, 6, +	0, +	 +	kEdgeOneFourth, +	2, +	8, 9, +	0, +	 +	kEdgeOneFourth, +	2, +	15, 16, +	0, +	 +	kEdgeOneFourth, +	2, +	18, 19, +	0, +	 +	kEdgeOneFourth, +	3, +	21, 22, 23, +	0, 0, +	 +	kEdgeOneHalf, +	2, +	1, 6, +	0, +	 +	kEdgeOneHalf, +	2, +	3, 8, +	0, +	 +	kEdgeOneHalf, +	2, +	16, 21, +	0, +	 +	kEdgeOneHalf, +	2, +	18, 23, +	0 +}; + +static const VertexType kBombLevelFour[] = { +	1, 1, 1, 1, 0, +	1, 1, 0, 1, 1, +	1, 0, 1, 0, 1, +	1, 1, 0, 1, 1, +	0, 1, 1, 1, 1, +	 +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	 +	19, +	 +	kEdgeOneEighth, +	2, +	5, 1, +	0, +	 +	kEdgeOneEighth, +	3, +	10, 6, 2, +	0, 0, +	 +	kEdgeOneEighth, +	3, +	16, 12, 8, +	0, 0, +	 +	kEdgeOneEighth, +	3, +	22, 18, 14, +	0, 0, +	 +	kEdgeOneEighth, +	2, +	23, 19, +	0, +	 +	kEdgeThreeEighths, +	3, +	2, 8, 14, +	0, 0, +	 +	kEdgeThreeEighths, +	3, +	10, 16, 22, +	0, 0, +	 +	kEdgeOneFourth, +	4, +	0, 1, 2, 3, +	0, 0, 0, +	 +	kEdgeOneFourth, +	2, +	5, 6, +	0, +	 +	kEdgeOneFourth, +	2, +	8, 9, +	0, +	 +	kEdgeOneFourth, +	2, +	15, 16, +	0, +	 +	kEdgeOneFourth, +	2, +	18, 19, +	0, +	 +	kEdgeOneFourth, +	4, +	21, 22, 23, 24, +	0, 0, 0, +	 +	kEdgeOneHalf, +	4, +	0, 5, 10, 15, +	0, 0, 0, +	 +	kEdgeOneHalf, +	2, +	1, 6, +	0, +	 +	kEdgeOneHalf, +	2, +	3, 8, +	0, +	 +	kEdgeOneHalf, +	4, +	9, 14, 19, 24, +	0, 0, 0, +	 +	kEdgeOneHalf, +	2, +	16, 21, +	0, +	 +	kEdgeOneHalf, +	2, +	18, 23, +	0 +}; + +static const VertexType kBombLevelFive[] = { +	0, 1, 0, 1, 0, +	1, 1, 1, 1, 1, +	0, 1, 1, 1, 0, +	1, 1, 1, 1, 1, +	0, 1, 0, 1, 0, +	 +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	 +	19, +	 +	kEdgeOneEighth, +	2, +	5, 1, +	0, +	 +	kEdgeOneEighth, +	2, +	7, 3, +	0, +	 +	kEdgeOneEighth, +	2, +	13, 9, +	0, +	 +	kEdgeOneEighth, +	2, +	15, 11, +	0, +	 +	kEdgeOneEighth, +	2, +	21, 17, +	0, +	 +	kEdgeOneEighth, +	2, +	23, 19, +	0, +	 +	kEdgeThreeEighths, +	2, +	1, 7, +	0, +	 +	kEdgeThreeEighths, +	4, +	5, 11, 17, 23, +	0, 0, 0, +	 +	kEdgeThreeEighths, +	3, +	6, 12, 18, +	0, 0, +	 +	kEdgeThreeEighths, +	2, +	13, 19, +	0, +	 +	kEdgeThreeEighths, +	2, +	15, 21, +	0, +	 +	kEdgeOneFourth, +	5, +	5, 6, 7, 8, 9, +	0, 0, 0, 0, +	 +	kEdgeOneFourth, +	3, +	15, 16, 17, +	0, 0, +	 +	kEdgeOneFourth, +	2, +	18, 19, +	0, +	 +	kEdgeOneFourth, +	3, +	21, 22, 23, +	0, 0, +	 +	kEdgeOneHalf, +	3, +	5, 10, 15, +	0, 0, +	 +	kEdgeOneHalf, +	2, +	1, 6, +	0, +	 +	kEdgeOneHalf, +	3, +	11, 16, 21, +	0, 0, +	 +	kEdgeOneHalf, +	5, +	3, 8, 13, 18, 23, +	0, 0, 0, 0 +}; + +static const VertexType kBombLevelSix[] = { +	0, 1, 1, 1, 0, +	1, 1, 1, 1, 1, +	1, 0, 0, 0, 1, +	1, 1, 1, 1, 1, +	0, 1, 1, 1, 0, +	 +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, +	 +	25, +	 +	kEdgeOneSixteenth, +	2, +	10, 1, +	0, +	 +	kEdgeOneSixteenth, +	2, +	23, 14, +	0, +	 +	kEdgeSevenSixteenths, +	2, +	3, 14, +	0, +	 +	kEdgeSevenSixteenths, +	2, +	10, 21, +	0, +	 +	kEdgeOneEighth, +	2, +	5, 1, +	0, +	 +	kEdgeOneEighth, +	3, +	10, 6, 2, +	0, 0, +	 +	kEdgeOneEighth, +	2, +	7, 3, +	0, +	 +	kEdgeOneEighth, +	2, +	21, 17, +	0, +	 +	kEdgeOneEighth, +	3, +	22, 18, 14, +	0, 0, +	 +	kEdgeOneEighth, +	2, +	23, 19, +	0, +	 +	kEdgeThreeEighths, +	2, +	1, 7, +	0, +	 +	kEdgeThreeEighths, +	3, +	2, 8, 14, +	0, 0, +	 +	kEdgeThreeEighths, +	2, +	3, 9, +	0, +	 +	kEdgeThreeEighths, +	3, +	10, 16, 22, +	0, 0, +	 +	kEdgeThreeEighths, +	2, +	15, 21, +	0, +	 +	kEdgeThreeEighths, +	2, +	17, 23, +	0, +	 +	kEdgeOneFourth, +	3, +	1, 2, 3, +	0, 0, +	 +	kEdgeOneFourth, +	3, +	6, 7, 8, +	0, 0, +	 +	kEdgeOneFourth, +	3, +	16, 17, 18, +	0, 0, +	 +	kEdgeOneFourth, +	3, +	21, 22, 23, +	0, 0, +	 +	kEdgeOneHalf, +	3, +	5, 10, 15, +	0, 0, +	 +	kEdgeOneHalf, +	3, +	6, 11, 16, +	0, 0, +	 +	kEdgeOneHalf, +	5, +	2, 7, 12, 17, 22, +	0, 0, 0, 0, +	 +	kEdgeOneHalf, +	3, +	8, 13, 18, +	0, 0, +	 +	kEdgeOneHalf, +	3, +	9, 14, 19, +	0, 0 +}; + +static const CoordType kBombGridWidth = 140; +static const CoordType kBombGridHeight = 140; + +static const CoordType kDotOriginX = 0; +static const CoordType kDotOriginY = 0; + +static const CoordType kVertOriginX = 2; +static const CoordType kVertOriginY = 6; + +static const CoordType kHorizOriginX = 6; +static const CoordType kHorizOriginY = 2; + +static const CoordType kDiagOriginX = 6; +static const CoordType kDiagOriginY = 6; + +static const int g_originsX[] = { +	kDiagOriginX, +	kDiagOriginX, +	kDiagOriginX, +	kHorizOriginX, +	kDiagOriginX, +	kDiagOriginX, +	kDiagOriginX, +	kVertOriginX +}; + +static const int g_originsY[] = { +	kDiagOriginY - 64, +	kDiagOriginY - 32, +	kDiagOriginY - 32, +	kHorizOriginY, +	kDiagOriginY, +	kDiagOriginY, +	kDiagOriginY, +	kVertOriginY +}; + +struct HotVerticesList { +	int numHotVerts; +	VertexType hotVerts[25]; +}; + +CoordType vertToX(VertexType vertex) { +	return (vertex % 5) * 32; +} + +CoordType vertToY(VertexType vertex) { +	return (vertex / 5) * 32; +} + +// This function returns the number of edges in the bomb edge list. +VertexType getNumEdges(BombEdgeList edges) { +	return edges[50]; +} + +// These four functions return pointers into the given edge list. + +// getFirstEdge and getNextEdge can be used to iterate across all edges +// in an edge list. These functions can be used to walk all the edges +// in a bomb edge list for drawing. +VertexType *getFirstEdge(BombEdgeList edges) { +	return &edges[51]; +} + +VertexType *getNextEdge(VertexType *anEdge) { +	return anEdge + *(anEdge + 1) * 2 + 1; +} + +// getVertices returns a pointer to all of the vertices that should are +// hot. These vertices indicate all the vertices that should be drawn in +// the game. +VertexType *getVertices(BombEdgeList edges) { +	return &edges[0]; +} + +// getUsedVertices returns a pointer to the "used" vertices area: the +// area that keeps track of which vertices have been set by the +// setVertexUsed used function. +VertexType *getUsedVertices(BombEdgeList edges) { +	return &edges[25]; +} + +// Useful for saving. Saving the state of the bomb game is as simple as writing +// out the edge list. +int getEdgeListSize(BombEdgeList edges) {	 +	VertexType numEdges = getNumEdges(edges); +	VertexType *anEdge = getFirstEdge(edges); + +	while (numEdges--) +		anEdge = getNextEdge(anEdge); + +	return anEdge - edges + 4; +} + +// Returns true if the given vertex lies on the given edge. +bool vertexOnEdge(VertexType *anEdge, VertexType whichVertex) {	 +	VertexType numVerts = *++anEdge; + +	while (numVerts--) +		if (*++anEdge == whichVertex) +			return true; + +	return false; +} + +// Given an edge list and a from vertex, this function constructs a list +// of all vertices that may be clicked on. +// if fromVertex == -1, all vertices are eligible. +// otherwise, only vertices on a line from fromVertex are eligible. +void makeHotVertexList(BombEdgeList edges, VertexType fromVertex, HotVerticesList &hotVertices) {	 +	hotVertices.numHotVerts = 0; + +	if (fromVertex == -1) { +		for (VertexType i = 0; i < 25; i++) +			if (edges[i]) +				hotVertices.hotVerts[hotVertices.numHotVerts++] = i; +	} else { +		VertexType numEdges = getNumEdges(edges); +		VertexType *anEdge = getFirstEdge(edges); +		hotVertices.hotVerts[hotVertices.numHotVerts++] = fromVertex; + +		while (numEdges--) { +			if (vertexOnEdge(anEdge, fromVertex)) { +				VertexType *p = anEdge + 1; +				VertexType numVerts = *p; + +				while (numVerts--) +					if (*++p != fromVertex) +						hotVertices.hotVerts[hotVertices.numHotVerts++] = *p; +			} + +			anEdge = getNextEdge(anEdge); +		} +	} +} + +// Set all edges in the edge list to the value passed in "edgeVal". +// For drawing purposes, 0 can mean don't draw, and 1 and higher can +// represent different colors. +void setAllEdgesUsed(BombEdgeList edges, VertexType used) {	 +	VertexType numEdges = getNumEdges(edges); +	VertexType *anEdge = getFirstEdge(edges); + +	while (numEdges--) { +		VertexType *p1 = anEdge + 1; +		VertexType numVerts = *p1; +		p1 += numVerts + 1; + +		while (--numVerts) +			*p1++ = used; + +		anEdge = getNextEdge(anEdge); +	} + +	VertexType *p1 = edges; +	VertexType *p2 = getUsedVertices(edges); + +	for (VertexType i = 0; i < 25; i++, p1++, p2++) +		if (*p1) +			*p2 = used; +} + +// Same as setAllEdgesUsed, but only affects edges that are already set +// to a non-zero value. +void setAllUsedEdgesUsed(BombEdgeList edges, VertexType used) { +	VertexType numEdges = getNumEdges(edges); +	VertexType *anEdge = getFirstEdge(edges); + +	while (numEdges--) { +		VertexType *p = anEdge + 1; +		VertexType numVerts = *p; +		p += numVerts + 1; + +		while (--numVerts) { +			if (*p) +				*p = used; +			++p; +		} + +		anEdge = getNextEdge(anEdge); +	} + +	VertexType *p = getUsedVertices(edges); +	for (VertexType i = 0; i < 25; i++, p++) +		if (*p) +			*p = used; +} + +// Replace all edges with value "value" with the new value "used". +void replaceUsedEdges(BombEdgeList edges, VertexType value, VertexType used) {	 +	VertexType numEdges = getNumEdges(edges); +	VertexType *anEdge = getFirstEdge(edges); + +	while (numEdges--) { +		VertexType *p = anEdge + 1; +		VertexType numVerts = *p; +		p += numVerts + 1; + +		while (--numVerts) { +			if (*p == value) +				*p = used; + +			p++; +		} + +		anEdge = getNextEdge(anEdge); +	} + +	VertexType *p = getUsedVertices(edges); +	for (VertexType i = 0; i < 25; i++, p++) +		if (*p == value) +			*p = used; +} + +// Set a vertex's value to "used". +void setVertexUsed(BombEdgeList edges, VertexType whichVertex, VertexType value) { +	*(getUsedVertices(edges) + whichVertex) = value; +} + +// Mark an edge in the given list between the two vertices as "used". This marks +// all inbetween vertices as well, even if the vertex is not marked as a "hot" +// vertex in the hot vertex section. Returns true if doing this operation +// crosses an already marked edge. +bool setEdgeUsed(BombEdgeList edges, VertexType fromVertex, VertexType toVertex) { +	VertexType numEdges = getNumEdges(edges); +	VertexType *anEdge = getFirstEdge(edges); +	bool crossed = false; + +	while (numEdges--) { +		VertexType *p = anEdge; +		VertexType numVerts = *++p; +		VertexType *fromPtr = 0; +		VertexType *toPtr = 0; +		VertexType i = numVerts; +		p++; + +		while (i--) { +			if (*p == fromVertex) +				fromPtr = p; +			else if (*p == toVertex) +				toPtr = p; + +			if (fromPtr && toPtr) { +				// Found the edge... +				if (fromPtr > toPtr) { +					p = fromPtr; +					fromPtr = toPtr; +					toPtr = p; +				} + +				p = fromPtr + numVerts; + +				for (i = toPtr - fromPtr; i > 0; i--, p++) { +					++(*p); + +					if (*p == 2) +						crossed = true; +				} + +				VertexType *verts = getVertices(edges); +				VertexType *usedVerts = getUsedVertices(edges); +				*(usedVerts + *fromPtr) = 1; + +				for (p = fromPtr + 1; p != toPtr; p++) +					if (*(verts + *p)) +						*(usedVerts + *p) = 1; + +				*(usedVerts + *toPtr) = 1; +				return crossed; +			} + +			p++; +		} + +		anEdge = getNextEdge(anEdge); +	} + +	return false; +} + +// Return true if all edges are used. Can be used to determine when the bomb +// game is over. +bool allEdgesUsed(BombEdgeList edges) {	 +	VertexType numEdges = getNumEdges(edges); +	VertexType *anEdge = getFirstEdge(edges); + +	while (numEdges--) { +		VertexType *p = anEdge + 1; +		VertexType numVerts = *p; +		p += numVerts + 1; + +		while (--numVerts) { +			if (!*p) +				return false; + +			++p; +		} + +		anEdge = getNextEdge(anEdge); +	} + +	return true; +} + +BombGrid::BombGrid(const DisplayElementID id) : Picture(id) { +	Common::Rect bounds(0, 0, kBombGridWidth, kBombGridHeight); + +	allocateSurface(bounds); +	setBounds(bounds); +	_surface->fillRect(bounds, g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + +	_transparent = true; + +	_yellowDot.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID, true); +	_yellowOneSixteenth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 1, true); +	_yellowOneEighth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 2, true); +	_yellowThreeSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 3, true); +	_yellowOneFourth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 4, true); +	_yellowFiveSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 5, true); +	_yellowThreeEighths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 6, true); +	_yellowSevenSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 7, true); +	_yellowOneHalf.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kYellowBombPICTBaseID + 8, true); +	 +	_redDot.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID, true); +	_redOneSixteenth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 1, true); +	_redOneEighth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 2, true); +	_redThreeSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 3, true); +	_redOneFourth.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 4, true); +	_redFiveSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 5, true); +	_redThreeEighths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 6, true); +	_redSevenSixteenths.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 7, true); +	_redOneHalf.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kRedBombPICTBaseID + 8, true); +} + +void BombGrid::drawEdges(BombEdgeList edges) { +	GraphicsManager *gfx = ((PegasusEngine *)g_engine)->_gfx; +	gfx->setCurSurface(_surface); + +	_surface->fillRect(Common::Rect(0, 0, kBombGridWidth, kBombGridHeight), g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff)); + +	Frame *yellowStuff = &_yellowDot; +	Frame *redStuff = &_redDot; +	VertexType numEdges = getNumEdges(edges); +	VertexType *anEdge = getFirstEdge(edges); +	VertexType i, *p; + +	Common::Rect bounds; +	getSurfaceBounds(bounds); + +	while (numEdges--) { +		p = anEdge; +		VertexType edgeDirection = *p++; +		VertexType numVerts = *p++; +		VertexType numSegs = numVerts - 1; + +		for (i = 0; i < numSegs; i++, p++) { +			if (*(p + numVerts) > 0 && *(p + numVerts) < 4) { +				Frame *drawStuff; + +				if (*(p + numVerts) == 2) +					drawStuff = redStuff; +				else +					drawStuff = yellowStuff; + +				int x = vertToX(*p) + g_originsX[edgeDirection]; +				int y = vertToY(*p) + g_originsY[edgeDirection]; + +				Common::Rect r1; +				drawStuff[edgeDirection + 1].getSurfaceBounds(r1); +				Common::Rect r2 = r1; +				r2.moveTo(x, y); +				drawStuff[edgeDirection + 1].drawImage(r1, r2); +			} +		} + +		anEdge = getNextEdge(anEdge); +	} + +	for (i = 0, p = getUsedVertices(edges); i < 25; i++, p++) { +		if (*p > 0 && *p < 4) { +			Frame *drawStuff; + +			if (*p == 2) +				drawStuff = redStuff; +			else +				drawStuff = yellowStuff; + +			int x = vertToX(i) + kDotOriginX; +			int y = vertToY(i) + kDotOriginY; + +			Common::Rect r1; +			drawStuff->getSurfaceBounds(r1); +			Common::Rect r2 = r1; +			r2.moveTo(x, y); +			drawStuff->drawImage(r1, r2); +		} +	} + +	triggerRedraw(); +	gfx->setCurSurface(gfx->getWorkArea()); +} + +BombTimer::BombTimer(const DisplayElementID id) : IdlerAnimation(id) {	 +	_middle = -1; +	_leftImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTimerLeftPICTID); +	_rightImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTimerRightPICTID); + +	Common::Rect r; +	_leftImage.getSurfaceBounds(r); +	setBounds(r); +} + +void BombTimer::draw(const Common::Rect &updateRect) { +	Common::Rect bounds; +	getBounds(bounds); + +	Common::Rect r1 = bounds; +	r1.right = _middle; +	r1 = r1.findIntersectingRect(updateRect); + +	if (!r1.isEmpty()) { +		Common::Rect r2 = r1; +		r2.moveTo(r1.left - bounds.left, r1.top - bounds.top); +		_leftImage.copyToCurrentPort(r2, r1); +	} + +	r1 = bounds; +	r1.left = _middle; +	r1 = r1.findIntersectingRect(updateRect); + +	if (!r1.isEmpty()) { +		Common::Rect r2 = r1; +		r2.moveTo(r1.left - bounds.left, r1.top - bounds.top); +		_rightImage.copyToCurrentPort(r2, r1); +	} +} + +void BombTimer::timeChanged(const TimeValue newTime) {	 +	Common::Rect bounds; +	getBounds(bounds); + +	int newMiddle = bounds.right - bounds.width() * newTime / getDuration(); +	if (newMiddle != _middle) { +		_middle = newMiddle; +		triggerRedraw(); +	} +} + +#define CREATE_BOMB_LEVEL(num, data) \ +	_bombLevel[num] = new VertexType[sizeof(data)]; \ +	memcpy(_bombLevel[num], data, sizeof(data)) + +CaldoriaBomb::CaldoriaBomb(Neighborhood *owner, NotificationManager *manager) : +		GameInteraction(kCaldoriaBombInteractionID, owner), _grid(kCaldoriaBombGridID), +		_timer(kCaldoriaBombTimerID), _timerNotification(kCaldoriaBombTimerNotificationID, manager) { +	CREATE_BOMB_LEVEL(0, kBombLevelOne); +	CREATE_BOMB_LEVEL(1, kBombLevelTwo); +	CREATE_BOMB_LEVEL(2, kBombLevelThree); +	CREATE_BOMB_LEVEL(3, kBombLevelFour); +	CREATE_BOMB_LEVEL(4, kBombLevelFive); +	CREATE_BOMB_LEVEL(5, kBombLevelSix); +	_currentLevel = 0; +} + +#undef CREATE_BOMB_LEVEL + +CaldoriaBomb::~CaldoriaBomb() { +	for (int i = 0; i < 6; i++) +		delete[] _bombLevel[i]; +} + +void CaldoriaBomb::openInteraction() {	 +	_grid.moveElementTo(kCaldoriaBombGridLeft, kCaldoriaBombGridTop); +	_grid.setDisplayOrder(kCaldoriaBombGridOrder); +	_grid.startDisplaying(); + +	_timer.moveElementTo(kCaldoriaBombTimerLeft, kCaldoriaBombTimerTop); +	_timer.setDisplayOrder(kCaldoriaBombTimerOrder); +	_timer.startDisplaying(); +	_timer.setSegment(0, kTenMinutesPerFifteenTicks, kFifteenTicksPerSecond); +	_timer.setTime(0); +	 +	_timerNotification.notifyMe(this, kBombTimerExpiredFlag, kBombTimerExpiredFlag); +	_timerCallBack.setNotification(&_timerNotification); +	_timerCallBack.initCallBack(&_timer, kCallBackAtExtremes); +	_timerCallBack.setCallBackFlag(kBombTimerExpiredFlag); +	 +	Common::Rect r(0, 0, kVertextHotSpotWidth, kVertextHotSpotHeight); + +	for (VertexType i = 0; i < 25; i++) { +		_vertexHotspot[i] = new Hotspot(i + kVertextHotSpotBaseID); +		r.moveTo(vertToX(i) + kCaldoriaBombGridLeft - kVertextHotSpotWidth / 2 + 6, +				vertToY(i) + kCaldoriaBombGridTop - kVertextHotSpotHeight / 2 + 6); +		_vertexHotspot[i]->setArea(r); +		_vertexHotspot[i]->setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +		g_allHotspots.push_back(_vertexHotspot[i]); +	} +	 +	_neighborhoodNotification = _owner->getNeighborhoodNotification(); +	_neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +} + +void CaldoriaBomb::initInteraction() { +	_owner->loadLoopSound1(""); +	_owner->startExtraSequence(kCaldoria56BombStage1, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaBomb::closeInteraction() { +	_timer.stop(); +	_timer.hide(); +	_timer.stopDisplaying(); +	_grid.hide(); +	_grid.stopDisplaying(); + +	// The original did not do this, but we need it here +	// Not sure why the original worked without this; probably +	// related to the way the List code worked in CodeWarrior. +	// If this is not here, the notifications will later attempt +	// to remove itself from this receiver causing a very nasty +	// crash. +	_timerNotification.cancelNotification(this); +	_neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaBomb::startBombAmbient(Common::String ambient) { +	_owner->loadLoopSound1(ambient); +} + +void CaldoriaBomb::receiveNotification(Notification *notification, const NotificationFlags) {	 +	if (notification == _neighborhoodNotification) { +		switch (_owner->getLastExtra()) { +		case kCaldoria56BombStage1: +			_grid.show(); +			_timer.show(); +			_timerCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +			_timer.start(); +			_currentLevel = 0; +			_lastVertex = -1; +			startBombAmbient("Sounds/Caldoria/BmbLoop1.22K.AIFF"); +			break; +		case kCaldoria56BombStage2: +		case kCaldoria56BombStage3: +		case kCaldoria56BombStage4: +		case kCaldoria56BombStage5: +		case kCaldoria56BombStage6: +			_grid.show(); +			_currentLevel++; +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -1; +			startBombAmbient(Common::String::format("Sounds/Caldoria/BmbLoop%d.22K.AIFF", _owner->getLastExtra() - kCaldoria56BombStage1 + 1)); +			break; +		case kCaldoria56BombStage7: +			_owner->requestDeleteCurrentInteraction(); +			GameState.setCaldoriaBombDisarmed(true); +			GameState.setScoringDisarmedNuke(true); +			_owner->loadAmbientLoops(); +			break; +		} +	} else if (notification == &_timerNotification) { +		_grid.hide(); +		_timer.stop(); +		_timer.hide(); +		_owner->loadLoopSound1(""); +		_owner->playDeathExtra(kCaldoria56BombExplodes, kDeathNuclearExplosion); +	} +} + +void CaldoriaBomb::activateHotspots() { +	GameInteraction::activateHotspots(); + +	if (_currentLevel != -1 && _lastVertex >= -1) { +		HotVerticesList hotVertices; +		makeHotVertexList(_bombLevel[_currentLevel], _lastVertex, hotVertices); + +		for (VertexType i = 0; i < hotVertices.numHotVerts; i++) +			g_allHotspots.activateOneHotspot(hotVertices.hotVerts[i] + kVertextHotSpotBaseID); +	} +} + +void CaldoriaBomb::clickInHotspot(const Input &input, const Hotspot *hotspot) { +	int clickedVertex = (int)hotspot->getObjectID() - (int)kVertextHotSpotBaseID; + +	if (clickedVertex >= 0 && clickedVertex < 25) { +		if (_lastVertex != -1 && setEdgeUsed(_bombLevel[_currentLevel], _lastVertex, clickedVertex)) { +			clickedVertex = -2; +			_flashTime = tickCount(); +		} else if (allEdgesUsed(_bombLevel[_currentLevel])) { +			setVertexUsed(_bombLevel[_currentLevel], clickedVertex, 1); +			clickedVertex = -20; +			_flashTime = tickCount(); +		} else { +			setVertexUsed(_bombLevel[_currentLevel], clickedVertex, 2); +		} + +		_grid.drawEdges(_bombLevel[_currentLevel]); +		_lastVertex = clickedVertex; +	} else { +		GameInteraction::clickInHotspot(input, hotspot); +	} +} + +InputBits CaldoriaBomb::getInputFilter() { +	// Disallow arrow buttons. +	return GameInteraction::getInputFilter() & kFilterAllButtons; +} + +void CaldoriaBomb::handleInput(const Input &input, const Hotspot *hotspot) { +	GameInteraction::handleInput(input, hotspot); + +	switch (_lastVertex) { +	case -2:			// Flash back to yellow. +		if (tickCount() > _flashTime + kOnTime1) { +			replaceUsedEdges(_bombLevel[_currentLevel], 2, 3); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -3; +		} +		break; +	case -3:			// Flash back to red. +		if (tickCount() > _flashTime + kOffTime1) { +			replaceUsedEdges(_bombLevel[_currentLevel], 3, 2); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -4; +		} +		break; +	case -4:			// Flash all to yellow. +		if (tickCount() > _flashTime + kOnTime2) { +			setAllUsedEdgesUsed(_bombLevel[_currentLevel], 1); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -5; +		} +		break; +	case -5:			// Flash all to red. +		if (tickCount() > _flashTime + kOffTime2) { +			setAllUsedEdgesUsed(_bombLevel[_currentLevel], 2); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -6; +		} +		break; +	case -6:			// Flash all to yellow. +		if (tickCount() > _flashTime + kOnTime3) { +			setAllUsedEdgesUsed(_bombLevel[_currentLevel], 1); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -7; +		} +		break; +	case -7:			// Flash all to red. +		if (tickCount() > _flashTime + kOffTime3) { +			setAllUsedEdgesUsed(_bombLevel[_currentLevel], 2); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -8; +		} +		break; +	case -8:			// Restore to normal. +		if (tickCount() > _flashTime + kOnTime4) { +			setAllEdgesUsed(_bombLevel[_currentLevel], 0); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -1; +		} +		break; + +	// Flash grid after success. +	case -20:			// Flash off. +		if (tickCount() > _flashTime + kOnTime1) { +			setAllEdgesUsed(_bombLevel[_currentLevel], 4); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -21; +		} +		break; +	case -21:			// Flash on. +		if (tickCount() > _flashTime + kOffTime1) { +			setAllEdgesUsed(_bombLevel[_currentLevel], 1); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -22; +		} +		break; +	case -22:			// Flash off. +		if (tickCount() > _flashTime + kOnTime2) { +			setAllEdgesUsed(_bombLevel[_currentLevel], 4); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -23; +		} +		break; +	case -23:			// Flash on. +		if (tickCount() > _flashTime + kOffTime2) { +			setAllEdgesUsed(_bombLevel[_currentLevel], 1); +			_grid.drawEdges(_bombLevel[_currentLevel]); +			_lastVertex = -24; +		} +		break; +	case -24: +		if (tickCount() > _flashTime + kOnTime3) { +			_grid.hide(); +			_lastVertex = -1; +			_owner->loadLoopSound1(""); + +			switch (_currentLevel) { +			case 0: +				_owner->startExtraSequence(kCaldoria56BombStage2, kExtraCompletedFlag, kFilterNoInput); +				break; +			case 1: +				_owner->startExtraSequence(kCaldoria56BombStage3, kExtraCompletedFlag, kFilterNoInput); +				break; +			case 2: +				_owner->startExtraSequence(kCaldoria56BombStage4, kExtraCompletedFlag, kFilterNoInput); +				break; +			case 3: +				_owner->startExtraSequence(kCaldoria56BombStage5, kExtraCompletedFlag, kFilterNoInput); +				break; +			case 4: +				_owner->startExtraSequence(kCaldoria56BombStage6, kExtraCompletedFlag, kFilterNoInput); +				break; +			case 5: +				_timer.stop(); +				_grid.hide(); +				_timer.hide(); +				_owner->startExtraSequence(kCaldoria56BombStage7, kExtraCompletedFlag, kFilterNoInput); +				break; +			} +		} +		break; +	} +} + +long CaldoriaBomb::getNumHints() { +	return 2; +} + +Common::String CaldoriaBomb::getHintMovie(uint hintNum) { +	return (hintNum == 1) ? "Images/AI/Caldoria/X56EH2" : "Images/AI/Caldoria/X56EH3"; +} + +bool CaldoriaBomb::canSolve() { +	return true; +} + +void CaldoriaBomb::doSolve() { +	_timer.stop(); +	_grid.hide(); +	_timer.hide(); +	_owner->loadLoopSound1(""); +	_owner->startExtraSequence(kCaldoria56BombStage7, kExtraCompletedFlag, kFilterNoInput); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriabomb.h b/engines/pegasus/neighborhood/caldoria/caldoriabomb.h new file mode 100644 index 0000000000..55d0409dec --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriabomb.h @@ -0,0 +1,156 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIABOMB_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIABOMB_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +/* +	Edge list is arranged as follows: +	 +	all values in the edge list are bytes. +	 +	all vertices are numbers between 0 and 24. x coordinate of vertex is vertex % 5, +	and y coordinate is vertex / 5. +	 +	an edge is +		a direction code +		a number of vertices in the edge +		an array of vertices -- all vertices along the edge, whether or not they're +			clickable. +		an array of bools (bytes) indicating that a portion of the edge is +			traversed (and should be drawn). the number of bools is one less than +			the number of vertices. +	 +	an edge list is +		an array of 25 bools indicating which vertex is clickable. +		an array of 25 bools indicating which vertex is used (drawn). +		a number of edges +		an array of edges. +	 +	a hot vertex list is +		a number of vertices +		an array of 25 vertices + +*/ + +typedef int8 VertexType; +typedef VertexType *BombEdgeList; + +static const VertexType kEdgeOneSixteenth = 0; +static const VertexType kEdgeOneEighth = 1; +static const VertexType kEdgeThreeSixteenths = 2; +static const VertexType kEdgeOneFourth = 3; +static const VertexType kEdgeFiveSixteenths = 4; +static const VertexType kEdgeThreeEighths = 5; +static const VertexType kEdgeSevenSixteenths = 6; +static const VertexType kEdgeOneHalf = 7; + +class BombTimer : public IdlerAnimation { +public: +	BombTimer(const DisplayElementID); +	virtual ~BombTimer() {} + +	void draw(const Common::Rect &); + +protected: +	void timeChanged(const TimeValue); + +	int _middle; +	Surface _leftImage, _rightImage; +}; + +class BombGrid : public Picture { +public: +	BombGrid(const DisplayElementID); +	virtual ~BombGrid() {} + +	void drawEdges(BombEdgeList); + +protected: +	Frame _yellowDot; +	Frame _yellowOneSixteenth; +	Frame _yellowOneEighth; +	Frame _yellowThreeSixteenths; +	Frame _yellowOneFourth; +	Frame _yellowFiveSixteenths; +	Frame _yellowThreeEighths; +	Frame _yellowSevenSixteenths; +	Frame _yellowOneHalf; +	Frame _redDot; +	Frame _redOneSixteenth; +	Frame _redOneEighth; +	Frame _redThreeSixteenths; +	Frame _redOneFourth; +	Frame _redFiveSixteenths; +	Frame _redThreeEighths; +	Frame _redSevenSixteenths; +	Frame _redOneHalf; +}; + +class Hotspot; + +class CaldoriaBomb : public GameInteraction, public NotificationReceiver { +public: +	CaldoriaBomb(Neighborhood *, NotificationManager *); +	virtual ~CaldoriaBomb(); + +	long getNumHints(); +	Common::String getHintMovie(uint); +	void doSolve(); +	bool canSolve(); + +protected: +	void openInteraction(); +	void initInteraction(); +	void closeInteraction(); +	void receiveNotification(Notification *, const NotificationFlags); +	void activateHotspots(); +	void clickInHotspot(const Input &, const Hotspot *); +	void handleInput(const Input &, const Hotspot *); +	InputBits getInputFilter(); +	void startBombAmbient(Common::String); +	 +	Notification *_neighborhoodNotification; +	BombGrid _grid; +	BombTimer _timer; +	BombEdgeList _bombLevel[6]; +	int _currentLevel, _flashTime; +	Hotspot *_vertexHotspot[25]; +	VertexType _lastVertex; +	Notification _timerNotification; +	NotificationCallBack _timerCallBack; + +	TimeValue _readTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp new file mode 100644 index 0000000000..5b9a823f7c --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamessages.cpp @@ -0,0 +1,115 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriamessages.h" + +namespace Pegasus { + +static const NotificationFlags kMessageDoneFlag = 1; + +CaldoriaMessages::CaldoriaMessages(Neighborhood *owner, const NotificationID id, NotificationManager *manager) : +		GameInteraction(kCaldoriaMessagesInteractionID, owner), Notification(id, manager), _messageMovie(kCaldoriaMessagesID) { +} + +void CaldoriaMessages::openInteraction() { +	_neighborhoodNotification = GameInteraction::_owner->getNeighborhoodNotification(); +	_neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +	_messageCallBack.setNotification(this); +	notifyMe(this, kMessageDoneFlag, kMessageDoneFlag); +	_messageCallBack.setCallBackFlag(kMessageDoneFlag); +	_messageNumber = 1; +} + +void CaldoriaMessages::initInteraction() { +	GameInteraction::_owner->startExtraSequence(kCaBedroomVidPhone, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaMessages::closeInteraction() { +	cancelNotification(this); +	_neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaMessages::receiveNotification(Notification *notification, const NotificationFlags) {	 +	if (notification == _neighborhoodNotification) { +		switch (GameInteraction::_owner->getLastExtra()) { +		case kCaBedroomVidPhone: +			GameInteraction::_owner->showExtraView(kCaBedroomMessage1); +			break; +		case kCaBedroomMessage1: +			play1Message(1); +			break; +		case kCaBedroomMessage2: +			play1Message(2); +			break; +		} +	} else { +		_messageCallBack.releaseCallBack(); +		_messageMovie.releaseMovie(); + +		uint32 extraID = (_messageNumber == 1) ? kCaBedroomMessage1 : kCaBedroomMessage2; +		GameInteraction::_owner->showExtraView(extraID); +		allowInput(true); +	} +} + +void CaldoriaMessages::clickInHotspot(const Input &input, const Hotspot *spot) { +	uint32 extraID; + +	switch (spot->getObjectID()) { +	case kCaBedroomVidPhoneActivationSpotID: +		extraID = (_messageNumber == 1) ? kCaBedroomMessage1 : kCaBedroomMessage2; +		GameInteraction::_owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterNoInput); +		break; +	default: +		GameInteraction::clickInHotspot(input, spot); +		break; +	} +} + +void CaldoriaMessages::play1Message(uint messageNumber) {	 +	if (messageNumber == 1) { +		_messageMovie.initFromMovieFile("Images/Caldoria/A12NVA.movie"); +		_messageNumber = 2; +	} else { +		_messageMovie.initFromMovieFile("Images/Caldoria/A12NVB.movie"); +		_messageNumber = 1; +		GameState.setCaldoriaSeenMessages(true); +	} + +	_messageMovie.moveElementTo(kCaldoriaMessageLeft, kCaldoriaMessageTop); +	_messageMovie.setDisplayOrder(kCaldoriaMessagesOrder); +	_messageMovie.startDisplaying(); +	_messageCallBack.initCallBack(&_messageMovie, kCallBackAtExtremes); +	_messageCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	allowInput(false); +	_messageMovie.show(); +	_messageMovie.redrawMovieWorld(); +	_messageMovie.start(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamessages.h b/engines/pegasus/neighborhood/caldoria/caldoriamessages.h new file mode 100644 index 0000000000..04079b52be --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamessages.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMESSAGES_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMESSAGES_H + +#include "pegasus/input.h" +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class CaldoriaMessages : public GameInteraction, public Notification, public NotificationReceiver { +public: +	CaldoriaMessages(Neighborhood *, const NotificationID, NotificationManager *); +	virtual ~CaldoriaMessages() {} + +protected: +	void openInteraction(); +	void initInteraction(); +	void closeInteraction(); +	void receiveNotification(Notification *, const NotificationFlags); +	void clickInHotspot(const Input &, const Hotspot *); +	void play1Message(uint); +	 +	Movie _messageMovie; +	NotificationCallBack _messageCallBack; +	Notification *_neighborhoodNotification; +	uint _messageNumber; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp new file mode 100644 index 0000000000..2b70ead442 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.cpp @@ -0,0 +1,134 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/caldoria/caldoriamirror.h" + +namespace Pegasus { + +CaldoriaMirror::CaldoriaMirror(Neighborhood *owner) : GameInteraction(kCaldoriaMirrorInteractionID, owner) { +} + +void CaldoriaMirror::openInteraction() { +	_neighborhoodNotification = _owner->getNeighborhoodNotification(); +	_neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +} + +void CaldoriaMirror::initInteraction() { +	_owner->setCurrentActivation(kActivateMirrorReady); +	_owner->startExtraSequence(kCaBathroomGreeting, kExtraCompletedFlag, kFilterNoInput); +} + +void CaldoriaMirror::closeInteraction() { +	_neighborhoodNotification->cancelNotification(this); +} + +void CaldoriaMirror::handleInput(const Input &input, const Hotspot *cursorSpot) { +	if (_owner->getLastExtra() == (uint32)kCaBathroomAgencyStandard || !input.anyDirectionInput()) +		GameInteraction::handleInput(input, cursorSpot); +} + +void CaldoriaMirror::activateHotspots() { +	GameInteraction::activateHotspots(); + +	switch (_owner->getLastExtra()) { +	case kCaBathroomGreeting: +	case kCaBathroomBodyFat: +	case kCaBathroomRetrothrash: +	case kCaBathroomGeoWave: +		g_allHotspots.activateOneHotspot(kCaBathroomMirrorSpotID); +		g_allHotspots.deactivateOneHotspot(kCaHairStyle1SpotID); +		g_allHotspots.deactivateOneHotspot(kCaHairStyle2SpotID); +		g_allHotspots.deactivateOneHotspot(kCaHairStyle3SpotID); +		break; +	case kCaBathroomStylistIntro: +	case kCaBathroomRetrothrashReturn: +	case kCaBathroomGeoWaveReturn: +		g_allHotspots.activateOneHotspot(kCaHairStyle1SpotID); +		g_allHotspots.activateOneHotspot(kCaHairStyle2SpotID); +		g_allHotspots.activateOneHotspot(kCaHairStyle3SpotID); +		g_allHotspots.deactivateOneHotspot(kCaBathroomMirrorSpotID); +		break; +	} +} + +void CaldoriaMirror::clickInHotspot(const Input &input, const Hotspot *spot) { +	switch (spot->getObjectID()) { +	case kCaBathroomMirrorSpotID: +		switch (_owner->getLastExtra()) { +		case kCaBathroomGreeting: +			_owner->startExtraSequence(kCaBathroomBodyFat, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kCaBathroomBodyFat: +			_owner->startExtraSequence(kCaBathroomStylistIntro, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kCaBathroomRetrothrash: +			_owner->startExtraSequence(kCaBathroomRetrothrashReturn, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kCaBathroomGeoWave: +			_owner->startExtraSequence(kCaBathroomGeoWaveReturn, kExtraCompletedFlag, kFilterNoInput); +			break; +		} +		break; +	case kCaHairStyle1SpotID: +		_owner->startExtraSequence(kCaBathroomRetrothrash, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaHairStyle2SpotID: +		_owner->startExtraSequence(kCaBathroomAgencyStandard, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kCaHairStyle3SpotID: +		_owner->startExtraSequence(kCaBathroomGeoWave, kExtraCompletedFlag, kFilterNoInput); +		break; +	default: +		GameInteraction::clickInHotspot(input, spot); +		break; +	} +} + +void CaldoriaMirror::receiveNotification(Notification *, const NotificationFlags) { +	switch (_owner->getLastExtra()) { +	case kCaBathroomRetrothrash: +	case kCaBathroomGeoWave: +		_owner->setCurrentActivation(kActivateMirrorReady); +		break; +	case kCaBathroomStylistIntro: +	case kCaBathroomRetrothrashReturn: +	case kCaBathroomGeoWaveReturn: +		_owner->setCurrentActivation(kActivateStylistReady); +		break; +	case kCaBathroomAgencyStandard: +		_owner->setCurrentActivation(kActivateHotSpotAlways); +		_owner->requestDeleteCurrentInteraction(); +		GameState.setScoringFixedHair(true); +		GameState.setCaldoriaDoneHygiene(true); +		break; +	} + +	allowInput(true); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/caldoria/caldoriamirror.h b/engines/pegasus/neighborhood/caldoria/caldoriamirror.h new file mode 100644 index 0000000000..1ca47ec774 --- /dev/null +++ b/engines/pegasus/neighborhood/caldoria/caldoriamirror.h @@ -0,0 +1,54 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMIRROR_H +#define PEGASUS_NEIGHBORHOOD_CALDORIA_CALDORIAMIRROR_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +class CaldoriaMirror : public GameInteraction, public NotificationReceiver { +public: +	CaldoriaMirror(Neighborhood *); +	virtual ~CaldoriaMirror() {} + +protected: +	void openInteraction(); +	void initInteraction(); +	void closeInteraction(); + +	void handleInput(const Input &, const Hotspot *); +	void activateHotspots(); +	void clickInHotspot(const Input &, const Hotspot *); +	void receiveNotification(Notification *, const NotificationFlags); + +	Notification *_neighborhoodNotification; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/door.cpp b/engines/pegasus/neighborhood/door.cpp new file mode 100644 index 0000000000..f7ec7559fc --- /dev/null +++ b/engines/pegasus/neighborhood/door.cpp @@ -0,0 +1,64 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/door.h" + +namespace Pegasus { + +void DoorTable::loadFromStream(Common::SeekableReadStream *stream) { +	uint32 count = stream->readUint32BE(); +	_entries.resize(count); + +	for (uint32 i = 0; i < count; i++) { +		_entries[i].room = stream->readUint16BE(); +		_entries[i].direction = stream->readByte(); +		_entries[i].altCode = stream->readByte(); +		_entries[i].movieStart = stream->readUint32BE(); +		_entries[i].movieEnd = stream->readUint32BE(); +		_entries[i].flags = stream->readByte(); +		stream->readByte(); // alignment +		debug(0, "Door[%d]: %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, +				_entries[i].altCode, _entries[i].movieStart, _entries[i].movieEnd, +				_entries[i].flags); +	} +} + +void DoorTable::clear() { +	_entries.clear(); +} + +DoorTable::Entry DoorTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) { +	for (uint32 i = 0; i < _entries.size(); i++) +		if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) +			return _entries[i]; + +	return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/door.h b/engines/pegasus/neighborhood/door.h new file mode 100644 index 0000000000..8ea757559a --- /dev/null +++ b/engines/pegasus/neighborhood/door.h @@ -0,0 +1,90 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_DOOR_H +#define PEGASUS_NEIGHBORHOOD_DOOR_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { +	class SeekableReadStream; +} + +namespace Pegasus { + +typedef byte DoorFlags; + +enum { +	kDoorPresentBit, // Bit set if there is a door here. +	kDoorLockedBit   // Bit set if door is locked, clear if unlocked. +}; + +static const DoorFlags kNoDoorFlags = 0; +static const DoorFlags kDoorPresentMask = 1 << kDoorPresentBit; +static const DoorFlags kDoorLockedMask = 1 << kDoorLockedBit; + +class DoorTable { +public: +	DoorTable() {} +	~DoorTable() {} + +	static uint32 getResTag() { return MKTAG('D', 'o', 'o', 'r'); } + +	void loadFromStream(Common::SeekableReadStream *stream); +	void clear(); + +	struct Entry { +		Entry() { clear(); } +		bool isEmpty() { return movieStart == 0xffffffff; } +		void clear() { +			room = kNoRoomID; +			direction = kNoDirection; +			altCode = kNoAlternateID; +			movieStart = 0xffffffff; +			movieEnd = 0xffffffff; +			flags = kNoDoorFlags; +		} + +		RoomID room; +		DirectionConstant direction; +		AlternateID altCode; +		TimeValue movieStart; +		TimeValue movieEnd; +		DoorFlags flags; +	}; + +	Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode); + +private: +	Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif + diff --git a/engines/pegasus/neighborhood/exit.cpp b/engines/pegasus/neighborhood/exit.cpp new file mode 100644 index 0000000000..f0dfff12d3 --- /dev/null +++ b/engines/pegasus/neighborhood/exit.cpp @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/exit.h" + +namespace Pegasus { + +void ExitTable::loadFromStream(Common::SeekableReadStream *stream) { +	uint32 count = stream->readUint32BE(); +	_entries.resize(count); + +	for (uint32 i = 0; i < count; i++) { +		_entries[i].room = stream->readUint16BE(); +		_entries[i].direction = stream->readByte(); +		_entries[i].altCode = stream->readByte(); +		_entries[i].movieStart = stream->readUint32BE(); +		_entries[i].movieEnd = stream->readUint32BE(); +		_entries[i].exitEnd = stream->readUint32BE(); +		_entries[i].exitLoop = stream->readUint32BE(); +		_entries[i].exitRoom = stream->readUint16BE(); +		_entries[i].exitDirection = stream->readByte(); +		stream->readByte(); // alignment + +		_entries[i].originalEnd = _entries[i].exitEnd; + +		debug(0, "Exit[%d]: %d %d %d %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, +				_entries[i].altCode, _entries[i].movieStart, _entries[i].movieEnd, _entries[i].exitEnd, +				_entries[i].exitLoop, _entries[i].exitRoom, _entries[i].exitDirection); +	} +} + +void ExitTable::clear() { +	_entries.clear(); +} + +ExitTable::Entry ExitTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) { +	for (uint32 i = 0; i < _entries.size(); i++) +		if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) +			return _entries[i]; + +	return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/exit.h b/engines/pegasus/neighborhood/exit.h new file mode 100644 index 0000000000..17150892f9 --- /dev/null +++ b/engines/pegasus/neighborhood/exit.h @@ -0,0 +1,93 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_EXIT_H +#define PEGASUS_NEIGHBORHOOD_EXIT_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { +	class SeekableReadStream; +} + +namespace Pegasus { + +class ExitTable { +public: +	ExitTable() {} +	~ExitTable() {} + +	static uint32 getResTag() { return MKTAG('E', 'x', 'i', 't'); } + +	void loadFromStream(Common::SeekableReadStream *stream); +	void clear(); + +	struct Entry { +		Entry() { clear(); } +		bool isEmpty() { return movieStart == 0xffffffff; } +		void clear() { +			room = kNoRoomID; +			direction = kNoDirection; +			altCode = kNoAlternateID; +			movieStart = 0xffffffff; +			movieEnd = 0xffffffff; +			exitEnd = 0xffffffff; +			originalEnd = 0xffffffff; +			exitLoop = 0xffffffff; +			exitRoom = kNoRoomID; +			exitDirection = kNoDirection; +		} + +		RoomID room; +		DirectionConstant direction; +		AlternateID altCode; +		TimeValue movieStart; +		TimeValue movieEnd; +		// exitEnd is the end of the optimized run of walks. +		TimeValue exitEnd; +		TimeValue originalEnd; +		// exitLoop is the loop start time of the optimized run of walks if the run +		// loops back on itself (so far, only in TSA). +		TimeValue exitLoop; +		RoomID exitRoom; +		DirectionConstant exitDirection; +	}; + +	Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode); + +	typedef Common::Array<Entry>::iterator iterator; +	iterator begin() { return _entries.begin(); } +	iterator end() { return _entries.end(); } + +private: +	Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/extra.cpp b/engines/pegasus/neighborhood/extra.cpp new file mode 100644 index 0000000000..b8c4e5b510 --- /dev/null +++ b/engines/pegasus/neighborhood/extra.cpp @@ -0,0 +1,58 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/extra.h" + +namespace Pegasus { + +void ExtraTable::loadFromStream(Common::SeekableReadStream *stream) { +	uint32 count = stream->readUint32BE(); +	_entries.resize(count); + +	for (uint32 i = 0; i < count; i++) { +		_entries[i].extra = stream->readUint32BE(); +		_entries[i].movieStart = stream->readUint32BE(); +		_entries[i].movieEnd = stream->readUint32BE(); +		debug(0, "Extra[%d]: %d %d %d", i, _entries[i].extra, _entries[i].movieStart, _entries[i].movieEnd); +	} +} + +void ExtraTable::clear() { +	_entries.clear(); +} + +ExtraTable::Entry ExtraTable::findEntry(ExtraID extra) { +	for (uint32 i = 0; i < _entries.size(); i++) +		if (_entries[i].extra == extra) +			return _entries[i]; + +	return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/extra.h b/engines/pegasus/neighborhood/extra.h new file mode 100644 index 0000000000..14fcff1009 --- /dev/null +++ b/engines/pegasus/neighborhood/extra.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_EXTRA_H +#define PEGASUS_NEIGHBORHOOD_EXTRA_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { +	class SeekableReadStream; +} + +namespace Pegasus { + +class ExtraTable { +public: +	ExtraTable() {} +	~ExtraTable() {} + +	static uint32 getResTag() { return MKTAG('X', 't', 'r', 'a'); } + +	void loadFromStream(Common::SeekableReadStream *stream); +	void clear(); + +	struct Entry { +		Entry() { movieStart = 0xffffffff; } +		bool isEmpty() { return movieStart == 0xffffffff; } + +		ExtraID extra; +		TimeValue movieStart; +		TimeValue movieEnd; +	}; + +	Entry findEntry(ExtraID extra); + +private: +	Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/hotspotinfo.cpp b/engines/pegasus/neighborhood/hotspotinfo.cpp new file mode 100644 index 0000000000..c7524f3a0f --- /dev/null +++ b/engines/pegasus/neighborhood/hotspotinfo.cpp @@ -0,0 +1,65 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/hotspotinfo.h" + +namespace Pegasus { + +void HotspotInfoTable::loadFromStream(Common::SeekableReadStream *stream) { +	uint32 count = stream->readUint32BE(); +	_entries.resize(count); + +	for (uint32 i = 0; i < count; i++) { +		_entries[i].hotspot = stream->readUint16BE(); +		_entries[i].hotspotActivation = stream->readSByte(); +		stream->readByte(); // alignment +		_entries[i].hotspotRoom = stream->readUint16BE(); +		_entries[i].hotspotDirection = stream->readByte(); +		stream->readByte(); // alignment +		_entries[i].hotspotExtra = stream->readUint32BE(); +		_entries[i].hotspotItem = stream->readUint16BE(); +		debug(0, "Hotspot[%d]: %d %d %d %d %d %d", i, _entries[i].hotspot, _entries[i].hotspotActivation, +				_entries[i].hotspotRoom, _entries[i].hotspotDirection, _entries[i].hotspotExtra, +				_entries[i].hotspotItem); +	} +} + +void HotspotInfoTable::clear() { +	_entries.clear(); +} + +HotspotInfoTable::Entry HotspotInfoTable::findEntry(HotSpotID hotspot) { +	for (uint32 i = 0; i < _entries.size(); i++) +		if (_entries[i].hotspot == hotspot) +			return _entries[i]; + +	return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/hotspotinfo.h b/engines/pegasus/neighborhood/hotspotinfo.h new file mode 100644 index 0000000000..965f445ba8 --- /dev/null +++ b/engines/pegasus/neighborhood/hotspotinfo.h @@ -0,0 +1,77 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_HOTSPOTINFO_H +#define PEGASUS_NEIGHBORHOOD_HOTSPOTINFO_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { +	class SeekableReadStream; +} + +namespace Pegasus { + +class HotspotInfoTable { +public: +	HotspotInfoTable() {} +	~HotspotInfoTable() {} + +	static uint32 getResTag() { return MKTAG('H', 'S', 'I', 'n'); } + +	void loadFromStream(Common::SeekableReadStream *stream); +	void clear(); + +	struct Entry { +		Entry() { hotspotRoom = kNoRoomID; } +		bool isEmpty() { return hotspotRoom == kNoRoomID; } + +		HotSpotID hotspot; +		HotSpotActivationID hotspotActivation; +		// Location hot spot lives in: +		RoomID hotspotRoom; +		DirectionConstant hotspotDirection; +		// Extra to play if this is a "play extra" hot spot. +		ExtraID hotspotExtra; +		// Item corresponding to this hot spot if it is an item-related hot spot. +		ItemID hotspotItem; +	}; + +	Entry findEntry(HotSpotID hotspot); + +	typedef Common::Array<Entry>::iterator iterator; +	iterator begin() { return _entries.begin(); } +	iterator end() { return _entries.end(); } + +private: +	Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/constants.h b/engines/pegasus/neighborhood/mars/constants.h new file mode 100644 index 0000000000..82a7f03b68 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/constants.h @@ -0,0 +1,941 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_CONSTANTS_H +#define PEGASUS_NEIGHBORHOOD_MARS_CONSTANTS_H + +#include "pegasus/constants.h" + +namespace Pegasus { + +// Element Coordinates + +static const CoordType kUndoHiliteLeft = kNavAreaLeft + 140; +static const CoordType kUndoHiliteTop = kNavAreaTop + 36; + +static const CoordType kCurrentGuessLeft = kNavAreaLeft + 146; +static const CoordType kCurrentGuessTop = kNavAreaTop + 90; + +static const CoordType kReactorChoiceHiliteLeft = kNavAreaLeft + 116; +static const CoordType kReactorChoiceHiliteTop = kNavAreaTop + 158; + +static const CoordType kReactorHistoryLeft = kNavAreaLeft + 302; +static const CoordType kReactorHistoryTop = kNavAreaTop + 39; + +static const CoordType kAnswerLeft = kNavAreaLeft + 304; +static const CoordType kAnswerTop = kNavAreaTop + 180; + +static const CoordType kShuttle1Left = 0; +static const CoordType kShuttle1Top = 0; + +static const CoordType kShuttle2Left = 0; +static const CoordType kShuttle2Top = 96; + +static const CoordType kShuttle3Left = 500; +static const CoordType kShuttle3Top = 96; + +static const CoordType kShuttle4Left = 0; +static const CoordType kShuttle4Top = 320; + +static const CoordType kShuttleWindowLeft = 140; +static const CoordType kShuttleWindowTop = 96; +static const CoordType kShuttleWindowWidth = 360; +static const CoordType kShuttleWindowHeight = 224; + +static const CoordType kShuttleWindowMidH = (kShuttleWindowLeft * 2 + kShuttleWindowWidth) / 2; +static const CoordType kShuttleWindowMidV = (kShuttleWindowTop * 2 + kShuttleWindowHeight) / 2; + +static const CoordType kShuttleLeftLeft = 0; +static const CoordType kShuttleLeftTop = 128; + +static const CoordType kShuttleRightLeft = 506; +static const CoordType kShuttleRightTop = 128; + +static const CoordType kShuttleLowerLeftLeft = 74; +static const CoordType kShuttleLowerLeftTop = 358; + +static const CoordType kShuttleLowerRightLeft = 486; +static const CoordType kShuttleLowerRightTop = 354; + +static const CoordType kShuttleCenterLeft = 260; +static const CoordType kShuttleCenterTop = 336; + +static const CoordType kShuttleUpperLeftLeft = 30; +static const CoordType kShuttleUpperLeftTop = 32; + +static const CoordType kShuttleUpperRightLeft = 506; +static const CoordType kShuttleUpperRightTop = 52; + +static const CoordType kShuttleLeftEnergyLeft = 110; +static const CoordType kShuttleLeftEnergyTop = 186; + +static const CoordType kShuttleRightEnergyLeft = 510; +static const CoordType kShuttleRightEnergyTop = 186; + +static const CoordType kShuttleEnergyLeft = 186; +static const CoordType kShuttleEnergyTop = 60; +static const CoordType kShuttleEnergyWidth = 252; +static const CoordType kShuttleEnergyHeight = 22; + +static const CoordType kPlanetStartLeft = kShuttleWindowLeft; +static const CoordType kPlanetStartTop = kShuttleWindowTop + kShuttleWindowHeight; + +static const CoordType kPlanetStopLeft = kShuttleWindowLeft; +static const CoordType kPlanetStopTop = kShuttleWindowTop + kShuttleWindowHeight - 100; + +static const CoordType kShuttleTractorLeft = kShuttleWindowLeft + 6; +static const CoordType kShuttleTractorTop = kShuttleWindowTop + 56; +static const CoordType kShuttleTractorWidth = 348; +static const CoordType kShuttleTractorHeight = 112; + +static const CoordType kShuttleJunkLeft = kShuttleWindowLeft + 6; +static const CoordType kShuttleJunkTop = kShuttleWindowTop + 6; + +static const DisplayOrder kShuttlePlanetOrder = kInterfaceLayer; +static const DisplayOrder kShuttleAlienShipOrder = kShuttlePlanetOrder + 1; +static const DisplayOrder kShuttleRobotShipOrder = kShuttleAlienShipOrder + 1; +static const DisplayOrder kShuttleTractorBeamMovieOrder = kShuttleRobotShipOrder + 1; +static const DisplayOrder kShuttleWeaponBackOrder = kShuttleTractorBeamMovieOrder + 1; +static const DisplayOrder kShuttleJunkOrder = kShuttleWeaponBackOrder + 1; +static const DisplayOrder kShuttleWeaponFrontOrder = kShuttleJunkOrder + 1; +static const DisplayOrder kShuttleTractorBeamOrder = kShuttleWeaponFrontOrder + 1; +static const DisplayOrder kShuttleHUDOrder = kShuttleTractorBeamOrder + 1; +static const DisplayOrder kShuttleBackgroundOrder = kShuttleHUDOrder + 1; +static const DisplayOrder kShuttleMonitorOrder = kShuttleBackgroundOrder + 1; +static const DisplayOrder kShuttleStatusOrder = kShuttleMonitorOrder + 1; + +static const TimeValue kShuttleSwingStart = 0; +static const TimeValue kShuttleSwingStop = 5 * 600; + +static const TimeValue kCanyonChaseStart = kShuttleSwingStop; +static const TimeValue kCanyonChaseStop = 60 * 600 + 43 * 600 + 14 * 40; + +static const TimeValue kLaunchTubeReachedTime = 60 * 600 + 38 * 600 - kCanyonChaseStart; +static const TimeValue kCanyonChaseFinishedTime = kCanyonChaseStop - kCanyonChaseStart - +											kLaunchTubeReachedTime; + +// Left shuttle. + +static const TimeValue kShuttleLeftIntroStart = 0; +static const TimeValue kShuttleLeftIntroStop = 400; + +static const TimeValue kShuttleLeftBlankTime = 400; + +static const TimeValue kShuttleLeftNormalTime = 440; + +static const TimeValue kShuttleLeftAutoTestTime = 480; + +static const TimeValue kShuttleLeftDamagedTime = 520; + +static const TimeValue kShuttleLeftDampingTime = 560; + +static const TimeValue kShuttleLeftGravitonTime = 600; + +static const TimeValue kShuttleLeftTractorTime = 640; + +// Right shuttle. + +static const TimeValue kShuttleRightIntroStart = 0; +static const TimeValue kShuttleRightIntroStop = 400; + +static const TimeValue kShuttleRightDestroyedStart = 400; +static const TimeValue kShuttleRightDestroyedStop = 840; + +static const TimeValue kShuttleRightBlankTime = 840; + +static const TimeValue kShuttleRightNormalTime = 880; + +static const TimeValue kShuttleRightDamagedTime = 920; + +static const TimeValue kShuttleRightTargetLockTime = 960; + +static const TimeValue kShuttleRightGravitonTime = 1000; + +static const TimeValue kShuttleRightOverloadTime = 1040; + +// Lower Left shuttle. + +static const TimeValue kShuttleLowerLeftCollisionTime = 0; + +static const TimeValue kShuttleLowerLeftTubeTime = 40; + +static const TimeValue kShuttleLowerLeftAutopilotTime = 80; + +// Lower Right shuttle. + +static const TimeValue kShuttleLowerRightOffTime = 0; + +static const TimeValue kShuttleLowerRightTrackingTime = 40; + +static const TimeValue kShuttleLowerRightTransportTime = 80; + +static const TimeValue kShuttleLowerRightTransportHiliteTime = 120; + +// Center shuttle. + +static const TimeValue kShuttleCenterBoardingTime = 0; + +static const TimeValue kShuttleCenterCheckTime = 40; + +static const TimeValue kShuttleCenterNavCompTime = 80; + +static const TimeValue kShuttleCenterCommTime = 120; + +static const TimeValue kShuttleCenterWeaponsTime = 160; + +static const TimeValue kShuttleCenterAllSystemsTime = 200; + +static const TimeValue kShuttleCenterSecureLooseTime = 240; + +static const TimeValue kShuttleCenterAutoTestTime = 280; + +static const TimeValue kShuttleCenterLaunchTime = 320; + +static const TimeValue kShuttleCenterEnterTubeTime = 360; + +static const TimeValue kShuttleCenterTargetSightedTime = 400; + +static const TimeValue kShuttleCenterVerifyingTime = 440; + +static const TimeValue kShuttleCenterScanningTime = 480; + +static const TimeValue kShuttleCenterSafeTime = 520; + +// Upper Left shuttle. + +static const TimeValue kShuttleUpperLeftDimTime = 0; + +static const TimeValue kShuttleUpperLeftDampingTime = 40; + +static const TimeValue kShuttleUpperLeftGravitonTime = 80; + +static const TimeValue kShuttleUpperLeftTractorTime = 120; + +// Upper Right shuttle. + +static const TimeValue kShuttleUpperRightLockedTime = 0; + +static const TimeValue kShuttleUpperRightArmedTime = 40; + +static const TimeValue kShuttleUpperRightAlienDestroyedTime = 80; + +static const TimeValue kShuttleUpperRightOverloadTime = 120; + +static const TimeValue kShuttleUpperRightTargetDestroyedTime = 160; + +// Shuttle distance + +static const int kShuttleDistance = 500; + +static const int kJunkMaxDistance = kShuttleDistance; +static const int kJunkMinDistance = 40; + +static const int kEnergyBeamMaxDistance = kShuttleDistance; +static const int kEnergyBeamMinDistance = 40; + +static const int kGravitonMaxDistance = kShuttleDistance; +static const int kGravitonMinDistance = 40; + +static const TimeValue kMarsOxyMaskOnIn = 0; +static const TimeValue kMarsOxyMaskOnOut = 1560; + +static const TimeValue kMarsAirlockButtonBeepIn = 1560; +static const TimeValue kMarsAirlockButtonBeepOut = 1620; + +static const TimeValue kMarsColorMatchingButtonBeepIn = 1620; +static const TimeValue kMarsColorMatchingButtonBeepOut = 1680; + +static const TimeValue kMarsKioskBeepIn = 1680; +static const TimeValue kMarsKioskBeepOut = 1740; + +static const TimeValue kMarsBumpIntoWallIn = 1740; +static const TimeValue kMarsBumpIntoWallOut = 1888; + +static const TimeValue kMarsGantryDoorCloseIn = 1888; +static const TimeValue kMarsGantryDoorCloseOut = 2866; + +static const TimeValue kMarsTransportDoorCloseIn = 2866; +static const TimeValue kMarsTransportDoorCloseOut = 3593; + +static const TimeValue kMarsAirlockPressurizeIn = 3593; +static const TimeValue kMarsAirlockPressurizeOut = 4766; + +static const TimeValue kMarsBigAirlockDoorCloseIn = 4766; +static const TimeValue kMarsBigAirlockDoorCloseOut = 7872; + +static const TimeValue kMarsSmallAirlockDoorCloseIn = 7872; +static const TimeValue kMarsSmallAirlockDoorCloseOut = 10000; + +static const TimeValue kMarsMazeDoorCloseIn = 10000; +static const TimeValue kMarsMazeDoorCloseOut = 10969; + +static const TimeValue kMarsRobotTakesTransportIn = 10969; +static const TimeValue kMarsRobotTakesTransportOut = 12802; + +static const TimeValue kMarsPodDepartedUpperPlatformIn = 12802; +static const TimeValue kMarsPodDepartedUpperPlatformOut = 15783; + +static const TimeValue kMarsPodDepartedLowerPlatformIn = 15783; +static const TimeValue kMarsPodDepartedLowerPlatformOut = 18736; + +static const TimeValue kMarsPodArrivedUpperPlatformIn = 18736; +static const TimeValue kMarsPodArrivedUpperPlatformOut = 21605; + +static const TimeValue kMarsCheckInRequiredIn = 21605; +static const TimeValue kMarsCheckInRequiredOut = 27463; + +static const TimeValue kMarsCantOpenShuttleIn = 27463; +static const TimeValue kMarsCantOpenShuttleOut = 29214; + +static const TimeValue kMarsShuttleLockOverrideIn = 29214; +static const TimeValue kMarsShuttleLockOverrideOut = 30330; + +static const TimeValue kMarsNoShuttleIn = 30330; +static const TimeValue kMarsNoShuttleOut = 31502; + +static const TimeValue kMustBeUnlockedIn = 31502; +static const TimeValue kMustBeUnlockedOut = 33960; + +static const TimeValue kColorMatchBlueIn = 33960; +static const TimeValue kColorMatchBlueOut = 34240; + +static const TimeValue kColorMatchRedIn = 34240; +static const TimeValue kColorMatchRedOut = 34538; + +static const TimeValue kColorMatchGreenIn = 34538; +static const TimeValue kColorMatchGreenOut = 34827; + +static const TimeValue kColorMatchYellowIn = 34827; +static const TimeValue kColorMatchYellowOut = 35162; + +static const TimeValue kColorMatchPurpleIn = 35162; +static const TimeValue kColorMatchPurpleOut = 35426; + +static const TimeValue kColorMatchZeroNodesIn = 35426; +static const TimeValue kColorMatchZeroNodesOut = 36376; + +static const TimeValue kColorMatchOneNodeIn = 36376; +static const TimeValue kColorMatchOneNodeOut = 37209; + +static const TimeValue kColorMatchTwoNodesIn = 37209; +static const TimeValue kColorMatchTwoNodesOut = 37983; + +static const TimeValue kColorMatchThreeNodesIn = 37983; +static const TimeValue kColorMatchThreeNodesOut = 38784; + +static const TimeValue kMarsShuttle1DepartedIn = 38784; +static const TimeValue kMarsShuttle1DepartedOut = 40323; + +static const TimeValue kMarsShuttle2DepartedIn = 40323; +static const TimeValue kMarsShuttle2DepartedOut = 41824; + +static const TimeValue kShuttleCockpitIn = 41824; +static const TimeValue kShuttleCockpitOut = 43126; + +static const TimeValue kShuttleOnboardIn = 43126; +static const TimeValue kShuttleOnboardOut = 44284; + +static const TimeValue kShuttleNavigationIn = 44284; +static const TimeValue kShuttleNavigationOut = 46049; + +static const TimeValue kShuttleCommunicationIn = 46049; +static const TimeValue kShuttleCommunicationOut = 47288; + +static const TimeValue kShuttleAutoTestingIn = 47288; +static const TimeValue kShuttleAutoTestingOut = 48179; + +static const TimeValue kMarsThrusterAutoTestIn = 48179; +static const TimeValue kMarsThrusterAutoTestOut = 49979; + +static const TimeValue kShuttleAllSystemsIn = 49979; +static const TimeValue kShuttleAllSystemsOut = 51065; + +static const TimeValue kShuttleSecureLooseIn = 51065; +static const TimeValue kShuttleSecureLooseOut = 52346; + +static const TimeValue kShuttlePrepareForDropIn = 52346; +static const TimeValue kShuttlePrepareForDropOut = 53216; + +static const TimeValue kShuttleAllClearIn = 53216; +static const TimeValue kShuttleAllClearOut = 54031; + +static const TimeValue kShuttleConfiguringIn = 54031; +static const TimeValue kShuttleConfiguringOut = 54994; + +static const TimeValue kShuttleGeneratingIn = 54994; +static const TimeValue kShuttleGeneratingOut = 56033; + +static const TimeValue kShuttleBreakawayIn = 56033; +static const TimeValue kShuttleBreakawayOut = 57346; + +static const TimeValue kMarsAtmosphericBreakawayIn = 57346; +static const TimeValue kMarsAtmosphericBreakawayOut = 59237; + +static const TimeValue kMarsCockpitChatterIn = 59237; +static const TimeValue kMarsCockpitChatterOut = 70344; + +static const TimeValue kShuttleDamperDescIn = 70344; +static const TimeValue kShuttleDamperDescOut = 73262; + +static const TimeValue kShuttleGravitonDescIn = 73262; +static const TimeValue kShuttleGravitonDescOut = 75296; + +static const TimeValue kShuttleTractorDescIn = 75296; +static const TimeValue kShuttleTractorDescOut = 78381; + +static const TimeValue kShuttleTargetSightedIn = 78381; +static const TimeValue kShuttleTargetSightedOut = 79074; + +static const TimeValue kShuttleAutopilotEngagedIn = 79074; +static const TimeValue kShuttleAutopilotEngagedOut = 80414; + +static const TimeValue kMarsEDBBlastIn = 80414; +static const TimeValue kMarsEDBBlastOut = 80705; + +static const TimeValue kMarsGravitonBlastIn = 80705; +static const TimeValue kMarsGravitonBlastOut = 81199; + +static const TimeValue kMarsJunkCollisionIn = 81199; +static const TimeValue kMarsJunkCollisionOut = 81961; + +static const TimeValue kShuttleGravitonIn = 81961; +static const TimeValue kShuttleGravitonOut = 82587; + +static const TimeValue kShuttleDampingBeamIn = 82587; +static const TimeValue kShuttleDampingBeamOut = 83331; + +static const TimeValue kShuttleTractorBeamIn = 83331; +static const TimeValue kShuttleTractorBeamOut = 83802; + +static const TimeValue kShuttleHullBreachIn = 83802; +static const TimeValue kShuttleHullBreachOut = 84721; + +static const TimeValue kShuttleWingDamageIn = 84721; +static const TimeValue kShuttleWingDamageOut = 85640; + +static const TimeValue kShuttleHullDamageIn = 85640; +static const TimeValue kShuttleHullDamageOut = 86513; + +static const TimeValue kShuttleEnergyTooLowIn = 86513; +static const TimeValue kShuttleEnergyTooLowOut = 87578; + +static const TimeValue kShuttleTractorLimitedIn = 87578; +static const TimeValue kShuttleTractorLimitedOut = 89164; + +static const TimeValue kShuttleCantHoldIn = 89164; +static const TimeValue kShuttleCantHoldOut = 90945; + +static const TimeValue kShuttleBrokeFreeIn = 90945; +static const TimeValue kShuttleBrokeFreeOut = 92322; + +static const TimeValue kShuttleDestroyedIn = 92322; +static const TimeValue kShuttleDestroyedOut = 93189; + +static const TimeValue kShuttleCoordinatesIn = 93189; +static const TimeValue kShuttleCoordinatesOut = 94018; + +static const TimeValue kShuttleScanningIn = 94018; +static const TimeValue kShuttleScanningOut = 94975; + +static const TimeValue kShuttleSafeIn = 94975; +static const TimeValue kShuttleSafeOut = 96176; + +static const TimeValue kShuttleOverloadedIn = 96176; +static const TimeValue kShuttleOverloadedOut = 101308; + +static const TimeScale kMarsMovieScale = 600; +static const TimeScale kMarsFramesPerSecond = 15; +static const TimeScale kMarsFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltMarsNormal = 0; +static const AlternateID kAltMarsPodAtMars34 = 1; +static const AlternateID kAltMarsTookCard = 2; +static const AlternateID kAltMars35AirlockEast = 3; +static const AlternateID kAltMars35AirlockWest = 4; +static const AlternateID kAltMarsPodAtMars45 = 5; +static const AlternateID kAltMarsTookMask = 6; +static const AlternateID kAltMarsMaskOnFiller = 7; +static const AlternateID kAltMars60AirlockEast = 8; +static const AlternateID kAltMars60AirlockWest = 9; + +// Room IDs. + +static const RoomID kMars0A = 0; +static const RoomID kMars00 = 1; +static const RoomID kMars01 = 2; +static const RoomID kMars02 = 3; +static const RoomID kMars03 = 4; +static const RoomID kMars04 = 5; +static const RoomID kMars05 = 6; +static const RoomID kMars06 = 7; +static const RoomID kMars07 = 8; +static const RoomID kMars08 = 9; +static const RoomID kMars09 = 10; +static const RoomID kMars10 = 11; +static const RoomID kMars11 = 12; +static const RoomID kMars12 = 13; +static const RoomID kMars13 = 14; +static const RoomID kMars14 = 15; +static const RoomID kMars15 = 16; +static const RoomID kMars16 = 17; +static const RoomID kMars17 = 18; +static const RoomID kMars18 = 19; +static const RoomID kMars19 = 20; +static const RoomID kMars20 = 21; +static const RoomID kMars21 = 22; +static const RoomID kMars22 = 23; +static const RoomID kMars23 = 24; +static const RoomID kMars24 = 25; +static const RoomID kMars25 = 26; +static const RoomID kMars26 = 27; +static const RoomID kMars27 = 28; +static const RoomID kMars28 = 29; +static const RoomID kMars29 = 30; +static const RoomID kMars30 = 31; +static const RoomID kMars31 = 32; +static const RoomID kMars31South = 33; +static const RoomID kMars32 = 34; +static const RoomID kMars33 = 35; +static const RoomID kMars33North = 36; +static const RoomID kMars34 = 37; +static const RoomID kMars35 = 38; +static const RoomID kMars36 = 39; +static const RoomID kMars37 = 40; +static const RoomID kMars38 = 41; +static const RoomID kMars39 = 42; +static const RoomID kMars41 = 43; +static const RoomID kMars42 = 44; +static const RoomID kMars43 = 45; +static const RoomID kMars44 = 46; +static const RoomID kMars45 = 47; +static const RoomID kMars46 = 48; +static const RoomID kMars47 = 49; +static const RoomID kMars48 = 50; +static const RoomID kMars49 = 51; +static const RoomID kMars50 = 52; +static const RoomID kMars51 = 53; +static const RoomID kMars52 = 54; +static const RoomID kMars54 = 55; +static const RoomID kMars56 = 56; +static const RoomID kMars58 = 57; +static const RoomID kMars60 = 58; +static const RoomID kMarsRobotShuttle = 59; +static const RoomID kMarsMaze004 = 60; +static const RoomID kMarsMaze005 = 61; +static const RoomID kMarsMaze006 = 62; +static const RoomID kMarsMaze007 = 63; +static const RoomID kMarsMaze008 = 64; +static const RoomID kMarsMaze009 = 65; +static const RoomID kMarsMaze010 = 66; +static const RoomID kMarsMaze011 = 67; +static const RoomID kMarsMaze012 = 68; +static const RoomID kMarsMaze015 = 69; +static const RoomID kMarsMaze016 = 70; +static const RoomID kMarsMaze017 = 71; +static const RoomID kMarsMaze018 = 72; +static const RoomID kMarsMaze019 = 73; +static const RoomID kMarsMaze020 = 74; +static const RoomID kMarsMaze021 = 75; +static const RoomID kMarsMaze022 = 76; +static const RoomID kMarsMaze023 = 77; +static const RoomID kMarsMaze024 = 78; +static const RoomID kMarsMaze025 = 79; +static const RoomID kMarsMaze026 = 80; +static const RoomID kMarsMaze027 = 81; +static const RoomID kMarsMaze028 = 82; +static const RoomID kMarsMaze031 = 83; +static const RoomID kMarsMaze032 = 84; +static const RoomID kMarsMaze033 = 85; +static const RoomID kMarsMaze034 = 86; +static const RoomID kMarsMaze035 = 87; +static const RoomID kMarsMaze036 = 88; +static const RoomID kMarsMaze037 = 89; +static const RoomID kMarsMaze038 = 90; +static const RoomID kMarsMaze039 = 91; +static const RoomID kMarsMaze042 = 92; +static const RoomID kMarsMaze043 = 93; +static const RoomID kMarsMaze044 = 94; +static const RoomID kMarsMaze045 = 95; +static const RoomID kMarsMaze046 = 96; +static const RoomID kMarsMaze047 = 97; +static const RoomID kMarsMaze049 = 98; +static const RoomID kMarsMaze050 = 99; +static const RoomID kMarsMaze051 = 100; +static const RoomID kMarsMaze052 = 101; +static const RoomID kMarsMaze053 = 102; +static const RoomID kMarsMaze054 = 103; +static const RoomID kMarsMaze055 = 104; +static const RoomID kMarsMaze056 = 105; +static const RoomID kMarsMaze057 = 106; +static const RoomID kMarsMaze058 = 107; +static const RoomID kMarsMaze059 = 108; +static const RoomID kMarsMaze060 = 109; +static const RoomID kMarsMaze061 = 110; +static const RoomID kMarsMaze063 = 111; +static const RoomID kMarsMaze064 = 112; +static const RoomID kMarsMaze065 = 113; +static const RoomID kMarsMaze066 = 114; +static const RoomID kMarsMaze067 = 115; +static const RoomID kMarsMaze068 = 116; +static const RoomID kMarsMaze069 = 117; +static const RoomID kMarsMaze070 = 118; +static const RoomID kMarsMaze071 = 119; +static const RoomID kMarsMaze072 = 120; +static const RoomID kMarsMaze074 = 121; +static const RoomID kMarsMaze076 = 122; +static const RoomID kMarsMaze078 = 123; +static const RoomID kMarsMaze079 = 124; +static const RoomID kMarsMaze081 = 125; +static const RoomID kMarsMaze083 = 126; +static const RoomID kMarsMaze084 = 127; +static const RoomID kMarsMaze085 = 128; +static const RoomID kMarsMaze086 = 129; +static const RoomID kMarsMaze087 = 130; +static const RoomID kMarsMaze088 = 131; +static const RoomID kMarsMaze089 = 132; +static const RoomID kMarsMaze090 = 133; +static const RoomID kMarsMaze091 = 134; +static const RoomID kMarsMaze092 = 135; +static const RoomID kMarsMaze093 = 136; +static const RoomID kMarsMaze098 = 137; +static const RoomID kMarsMaze099 = 138; +static const RoomID kMarsMaze100 = 139; +static const RoomID kMarsMaze101 = 140; +static const RoomID kMarsMaze104 = 141; +static const RoomID kMarsMaze105 = 142; +static const RoomID kMarsMaze106 = 143; +static const RoomID kMarsMaze107 = 144; +static const RoomID kMarsMaze108 = 145; +static const RoomID kMarsMaze111 = 146; +static const RoomID kMarsMaze113 = 147; +static const RoomID kMarsMaze114 = 148; +static const RoomID kMarsMaze115 = 149; +static const RoomID kMarsMaze116 = 150; +static const RoomID kMarsMaze117 = 151; +static const RoomID kMarsMaze118 = 152; +static const RoomID kMarsMaze119 = 153; +static const RoomID kMarsMaze120 = 154; +static const RoomID kMarsMaze121 = 155; +static const RoomID kMarsMaze122 = 156; +static const RoomID kMarsMaze123 = 157; +static const RoomID kMarsMaze124 = 158; +static const RoomID kMarsMaze125 = 159; +static const RoomID kMarsMaze126 = 160; +static const RoomID kMarsMaze127 = 161; +static const RoomID kMarsMaze128 = 162; +static const RoomID kMarsMaze129 = 163; +static const RoomID kMarsMaze130 = 164; +static const RoomID kMarsMaze131 = 165; +static const RoomID kMarsMaze132 = 166; +static const RoomID kMarsMaze133 = 167; +static const RoomID kMarsMaze136 = 168; +static const RoomID kMarsMaze137 = 169; +static const RoomID kMarsMaze138 = 170; +static const RoomID kMarsMaze139 = 171; +static const RoomID kMarsMaze140 = 172; +static const RoomID kMarsMaze141 = 173; +static const RoomID kMarsMaze142 = 174; +static const RoomID kMarsMaze143 = 175; +static const RoomID kMarsMaze144 = 176; +static const RoomID kMarsMaze145 = 177; +static const RoomID kMarsMaze146 = 178; +static const RoomID kMarsMaze147 = 179; +static const RoomID kMarsMaze148 = 180; +static const RoomID kMarsMaze149 = 181; +static const RoomID kMarsMaze152 = 182; +static const RoomID kMarsMaze153 = 183; +static const RoomID kMarsMaze154 = 184; +static const RoomID kMarsMaze155 = 185; +static const RoomID kMarsMaze156 = 186; +static const RoomID kMarsMaze157 = 187; +static const RoomID kMarsMaze159 = 188; +static const RoomID kMarsMaze160 = 189; +static const RoomID kMarsMaze161 = 190; +static const RoomID kMarsMaze162 = 191; +static const RoomID kMarsMaze163 = 192; +static const RoomID kMarsMaze164 = 193; +static const RoomID kMarsMaze165 = 194; +static const RoomID kMarsMaze166 = 195; +static const RoomID kMarsMaze167 = 196; +static const RoomID kMarsMaze168 = 197; +static const RoomID kMarsMaze169 = 198; +static const RoomID kMarsMaze170 = 199; +static const RoomID kMarsMaze171 = 200; +static const RoomID kMarsMaze172 = 201; +static const RoomID kMarsMaze173 = 202; +static const RoomID kMarsMaze174 = 203; +static const RoomID kMarsMaze175 = 204; +static const RoomID kMarsMaze177 = 205; +static const RoomID kMarsMaze178 = 206; +static const RoomID kMarsMaze179 = 207; +static const RoomID kMarsMaze180 = 208; +static const RoomID kMarsMaze181 = 209; +static const RoomID kMarsMaze182 = 210; +static const RoomID kMarsMaze183 = 211; +static const RoomID kMarsMaze184 = 212; +static const RoomID kMarsMaze187 = 213; +static const RoomID kMarsMaze188 = 214; +static const RoomID kMarsMaze189 = 215; +static const RoomID kMarsMaze190 = 216; +static const RoomID kMarsMaze191 = 217; +static const RoomID kMarsMaze192 = 218; +static const RoomID kMarsMaze193 = 219; +static const RoomID kMarsMaze194 = 220; +static const RoomID kMarsMaze195 = 221; +static const RoomID kMarsMaze198 = 222; +static const RoomID kMarsMaze199 = 223; +static const RoomID kMarsMaze200 = 224; +static const RoomID kMarsDeathRoom = 225; + +// Hot Spot Activation IDs. + +static const HotSpotActivationID kActivationReadyForKiosk = 1; +static const HotSpotActivationID kActivationKioskChoice = 2; +static const HotSpotActivationID kActivationTunnelMapReady = 3; +static const HotSpotActivationID kActivateMarsPodClosed = 4; +static const HotSpotActivationID kActivateMarsPodOpen = 5; +static const HotSpotActivationID kActivateReadyToPressurizeAirlock = 6; +static const HotSpotActivationID kActivateAirlockPressurized = 7; +static const HotSpotActivationID kActivateMaskOnHolder = 8; +static const HotSpotActivationID kActivateMaskOnFiller = 9; +static const HotSpotActivationID kActivateReactorPlatformOut = 10; +static const HotSpotActivationID kActivateReactorPlatformIn = 11; +static const HotSpotActivationID kActivateReactorAskLowerScreen = 12; +static const HotSpotActivationID kActivateReactorReadyForNitrogen = 13; +static const HotSpotActivationID kActivateReactorReadyForCrowBar = 14; +static const HotSpotActivationID kActivateReactorAskOperation = 15; +static const HotSpotActivationID kActivateReactorRanEvaluation = 16; +static const HotSpotActivationID kActivateReactorRanDiagnostics = 17; +static const HotSpotActivationID kActivateReactorAnalyzed = 18; +static const HotSpotActivationID kActivateReactorInstructions = 19; +static const HotSpotActivationID kActivateReactorInGame = 20; +static const HotSpotActivationID kActivateReactorBombSafe = 21; +static const HotSpotActivationID kActivateReactorBombExposed = 22; +static const HotSpotActivationID kActivationRobotHeadClosed = 23; +static const HotSpotActivationID kActivationRobotHeadOpen = 24; + +// Hot Spot IDs. + +static const HotSpotID kMars11NorthKioskSpotID = 5000; +static const HotSpotID kMars11NorthKioskSightsSpotID = 5001; +static const HotSpotID kMars11NorthKioskColonySpotID = 5002; +static const HotSpotID kMars12NorthKioskSpotID = 5003; +static const HotSpotID kMars12NorthKioskSightsSpotID = 5004; +static const HotSpotID kMars12NorthKioskColonySpotID = 5005; +static const HotSpotID kMars31SouthSpotID = 5006; +static const HotSpotID kMars31SouthOutSpotID = 5007; +static const HotSpotID kMars31SouthCardSpotID = 5008; +static const HotSpotID kMars33NorthSpotID = 5009; +static const HotSpotID kMars33NorthOutSpotID = 5010; +static const HotSpotID kMars33NorthMonitorSpotID = 5011; +static const HotSpotID kMars34NorthCardDropSpotID = 5012; +static const HotSpotID kMars34SouthOpenStorageSpotID = 5013; +static const HotSpotID kMars34SouthCloseStorageSpotID = 5014; +static const HotSpotID kMars34SouthCrowbarSpotID = 5015; +static const HotSpotID kMars35EastPressurizeSpotID = 5016; +static const HotSpotID kMars35EastSpinSpotID = 5017; +static const HotSpotID kMars35WestPressurizeSpotID = 5018; +static const HotSpotID kMars35WestSpinSpotID = 5019; +static const HotSpotID kMars45NorthOpenStorageSpotID = 5020; +static const HotSpotID kMars45NorthCloseStorageSpotID = 5021; +static const HotSpotID kMars45NorthCrowbarSpotID = 5022; +static const HotSpotID kAttackRobotHotSpotID = 5023; +static const HotSpotID kMars49AirMaskSpotID = 5024; +static const HotSpotID kMars49AirMaskFilledSpotID = 5025; +static const HotSpotID kMars49AirFillingDropSpotID = 5026; +static const HotSpotID kMars52MoveLeftSpotID = 5027; +static const HotSpotID kMars52MoveRightSpotID = 5028; +static const HotSpotID kMars52ExtractSpotID = 5029; +static const HotSpotID kMars53RetractSpotID = 5030; +static const HotSpotID kMars54MoveLeftSpotID = 5031; +static const HotSpotID kMars54MoveRightSpotID = 5032; +static const HotSpotID kMars54ExtractSpotID = 5033; +static const HotSpotID kMars55RetractSpotID = 5034; +static const HotSpotID kMars56MoveLeftSpotID = 5035; +static const HotSpotID kMars56MoveRightSpotID = 5036; +static const HotSpotID kMars56ExtractSpotID = 5037; +static const HotSpotID kMars57RetractSpotID = 5038; +static const HotSpotID kMars57LowerScreenSpotID = 5039; +static const HotSpotID kMars57Retract2SpotID = 5040; +static const HotSpotID kMars57DropNitrogenSpotID = 5041; +static const HotSpotID kMars57DropCrowBarSpotID = 5042; +static const HotSpotID kMars57CantOpenPanelSpotID = 5043; +static const HotSpotID kMars57ShieldEvaluationSpotID = 5044; +static const HotSpotID kMars57MeasureOutputSpotID = 5045; +static const HotSpotID kMars57RunDiagnosticsSpotID = 5046; +static const HotSpotID kMars57BackToOperationMenuSpotID = 5047; +static const HotSpotID kMars57AnalyzeObjectSpotID = 5048; +static const HotSpotID kMars57RemoveObjectMenuSpotID = 5049; +static const HotSpotID kMars57CircuitLinkSpotID = 5050; +static const HotSpotID kMars57CancelCircuitLinkSpotID = 5051; +static const HotSpotID kMars57GameInstructionsSpotID = 5052; +static const HotSpotID kMars57UndoMoveSpotID = 5053; +static const HotSpotID kMars57RedMoveSpotID = 5054; +static const HotSpotID kMars57YellowMoveSpotID = 5055; +static const HotSpotID kMars57GreenMoveSpotID = 5056; +static const HotSpotID kMars57BlueMoveSpotID = 5057; +static const HotSpotID kMars57PurpleMoveSpotID = 5058; +static const HotSpotID kMars57LowerScreenSafelySpotID = 5059; +static const HotSpotID kMars57GrabBombSpotID = 5060; +static const HotSpotID kMars58MoveLeftSpotID = 5061; +static const HotSpotID kMars58MoveRightSpotID = 5062; +static const HotSpotID kMars58ExtractSpotID = 5063; +static const HotSpotID kMars59RetractSpotID = 5064; +static const HotSpotID kMars60EastPressurizeSpotID = 5065; +static const HotSpotID kMars60EastSpinSpotID = 5066; +static const HotSpotID kMars60WestPressurizeSpotID = 5067; +static const HotSpotID kMars60WestSpinSpotID = 5068; +static const HotSpotID kRobotShuttleOpenHeadSpotID = 5069; +static const HotSpotID kRobotShuttleMapChipSpotID = 5070; +static const HotSpotID kRobotShuttleOpticalChipSpotID = 5071; +static const HotSpotID kRobotShuttleShieldChipSpotID = 5072; + +// Extra sequence IDs. + +static const ExtraID kMarsArrivalFromTSA = 0; +static const ExtraID kMars0AWatchShuttleDepart = 1; +static const ExtraID kRobotThrowsPlayer = 2; +static const ExtraID kMarsInfoKioskIntro = 3; +static const ExtraID kMarsColonyInfo = 4; +static const ExtraID kMarsSightsInfo = 5; +static const ExtraID kRobotOnWayToShuttle = 6; +static const ExtraID kMars31SouthZoomInNoCard = 7; +static const ExtraID kMars31SouthViewNoCard = 8; +static const ExtraID kMars31SouthZoomOutNoCard = 9; +static const ExtraID kMars31SouthZoomViewNoCard = 10; +static const ExtraID kMars33SlideShow1 = 11; +static const ExtraID kMars33SlideShow2 = 12; +static const ExtraID kMars33SlideShow3 = 13; +static const ExtraID kMars33SlideShow4 = 14; +static const ExtraID kMars34SpotOpenWithBar = 15; +static const ExtraID kMars34SpotCloseWithBar = 16; +static const ExtraID kMars34SpotOpenNoBar = 17; +static const ExtraID kMars34SpotCloseNoBar = 18; +static const ExtraID kMars34ViewOpenWithBar = 19; +static const ExtraID kMars34ViewOpenNoBar = 20; +static const ExtraID kMars34NorthPodGreeting = 21; +static const ExtraID kMarsTurnOnPod = 22; +static const ExtraID kMarsTakePodToMars45 = 23; +static const ExtraID kMars35WestSpinAirlockToEast = 24; +static const ExtraID kMars35EastSpinAirlockToWest = 25; +static const ExtraID kMars45SpotOpenWithBar = 26; +static const ExtraID kMars45SpotCloseWithBar = 27; +static const ExtraID kMars45SpotOpenNoBar = 28; +static const ExtraID kMars45SpotCloseNoBar = 29; +static const ExtraID kMars45ViewOpenWithBar = 30; +static const ExtraID kMars45ViewOpenNoBar = 31; +static const ExtraID kMars48RobotApproaches = 32; +static const ExtraID kMars48RobotKillsPlayer = 33; +static const ExtraID kMars48RobotLoops = 34; +static const ExtraID kMars48RobotView = 35; +static const ExtraID kMars48RobotDefends = 36; +static const ExtraID kMars49SouthViewMaskFilling = 37; +static const ExtraID kMars52SpinLeft = 38; +static const ExtraID kMars52SpinRight = 39; +static const ExtraID kMars52Extend = 40; +static const ExtraID kMars53Retract = 41; +static const ExtraID kMars54SpinLeft = 42; +static const ExtraID kMars54SpinRight = 43; +static const ExtraID kMars54Extend = 44; +static const ExtraID kMars55Retract = 45; +static const ExtraID kMars56SpinLeft = 46; +static const ExtraID kMars56SpinRight = 47; +static const ExtraID kMars56ExtendWithBomb = 48; +static const ExtraID kMars56ExtendNoBomb = 49; +static const ExtraID kMars57RetractWithBomb = 50; +static const ExtraID kMars57RetractNoBomb = 51; +static const ExtraID kMars57LowerScreenClosed = 52; +static const ExtraID kMars57CantOpenPanel = 53; +static const ExtraID kMars57FreezeLock = 54; +static const ExtraID kMars57BreakLock = 55; +static const ExtraID kMars57LockFrozenView = 56; +static const ExtraID kMars57ThawLock = 57; +static const ExtraID kMars57OpenPanel = 58; +static const ExtraID kMars57OpenPanelChoices = 59; +static const ExtraID kMars57ShieldEvaluation = 60; +static const ExtraID kMars57MeasureOutput = 61; +static const ExtraID kMars57ShieldOkayLoop = 62; +static const ExtraID kMars57RunDiagnostics = 63; +static const ExtraID kMars57BombExplodes = 64; +static const ExtraID kMars57BombAnalysis = 65; +static const ExtraID kMars57DontLink = 66; +static const ExtraID kMars57CircuitLink = 67; +static const ExtraID kMars57GameLevel1 = 68; +static const ExtraID kMars57GameLevel2 = 69; +static const ExtraID kMars57GameLevel3 = 70; +static const ExtraID kMars57BombExplodesInGame = 71; +static const ExtraID kMars57GameSolved = 72; +static const ExtraID kMars57ExposeBomb = 73; +static const ExtraID kMars57BackToNormal = 74; +static const ExtraID kMars57ViewOpenNoBomb = 75; +static const ExtraID kMars58SpinLeft = 76; +static const ExtraID kMars58SpinRight = 77; +static const ExtraID kMars58Extend = 78; +static const ExtraID kMars59Retract = 79; +static const ExtraID kMars60WestSpinAirlockToEast = 80; +static const ExtraID kMars60EastSpinAirlockToWest = 81; +static const ExtraID kMarsRobotHeadOpen = 82; +static const ExtraID kMarsRobotHeadClose = 83; +static const ExtraID kMarsRobotHead000 = 84; +static const ExtraID kMarsRobotHead001 = 85; +static const ExtraID kMarsRobotHead010 = 86; +static const ExtraID kMarsRobotHead011 = 87; +static const ExtraID kMarsRobotHead100 = 88; +static const ExtraID kMarsRobotHead101 = 89; +static const ExtraID kMarsRobotHead110 = 90; +static const ExtraID kMarsRobotHead111 = 91; +static const ExtraID kMarsMaze007RobotApproach = 92; +static const ExtraID kMarsMaze007RobotLoop = 93; +static const ExtraID kMarsMaze007RobotDeath = 94; +static const ExtraID kMarsMaze015SouthRobotApproach = 95; +static const ExtraID kMarsMaze015SouthRobotLoop = 96; +static const ExtraID kMarsMaze015SouthRobotDeath = 97; +static const ExtraID kMarsMaze101EastRobotApproach = 98; +static const ExtraID kMarsMaze101EastRobotLoop = 99; +static const ExtraID kMarsMaze101EastRobotDeath = 100; +static const ExtraID kMarsMaze104WestLoop = 101; +static const ExtraID kMarsMaze104WestDeath = 102; +static const ExtraID kMarsMaze133SouthApproach = 103; +static const ExtraID kMarsMaze133SouthLoop = 104; +static const ExtraID kMarsMaze133SouthDeath = 105; +static const ExtraID kMarsMaze136NorthApproach = 106; +static const ExtraID kMarsMaze136NorthLoop = 107; +static const ExtraID kMarsMaze136NorthDeath = 108; +static const ExtraID kMarsMaze184WestLoop = 109; +static const ExtraID kMarsMaze184WestDeath = 110; +static const ExtraID kMars200DeathInBucket = 111; + +static const ResIDType kReactorUndoHilitePICTID = 900; + +static const int16 kMars52Compass = 90; +static const int16 kMars54Compass = 180; +static const int16 kMars56Compass = 270; +static const int16 kMars58Compass = 0; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/energybeam.cpp b/engines/pegasus/neighborhood/mars/energybeam.cpp new file mode 100644 index 0000000000..964c8ba381 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/energybeam.cpp @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/energybeam.h" + +namespace Pegasus { + +static const TimeValue kEnergyBeamTime = kOneSecond * kShuttleWeaponScale / 2; + +static const CoordType kEnergyBeamOriginH = kShuttleWindowMidH; +static const CoordType kEnergyBeamOriginV = kShuttleWindowTop + kShuttleWindowHeight; + +static const float kBeamXOrigin = convertScreenHToSpaceX(kEnergyBeamOriginH, kEnergyBeamMinDistance); +static const float kBeamYOrigin = convertScreenVToSpaceY(kEnergyBeamOriginV, kEnergyBeamMinDistance); +static const float kBeamZOrigin = kEnergyBeamMinDistance; + +EnergyBeam::EnergyBeam() { +	_weaponDuration = kEnergyBeamTime; +	setSegment(0, kEnergyBeamTime); +	_weaponOrigin = Point3D(kBeamXOrigin, kBeamYOrigin, kBeamZOrigin); +} + +void EnergyBeam::draw(const Common::Rect &) { +	static const int kBeamColorRed1 = 224; +	static const int kBeamColorRed2 = 64; + +	Graphics::Surface *surface = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + +	byte red = linearInterp(0, kEnergyBeamTime, _lastTime, kBeamColorRed1, kBeamColorRed2); +	uint32 color = surface->format.RGBToColor(red, 0, 0); + +	Point3D startPoint; +	if (_weaponTime < 0.1) +		startPoint = _weaponOrigin; +	else +		linearInterp(_weaponOrigin, _weaponTarget, _weaponTime - 0.1, startPoint); + +	Common::Point lineStart; +	project3DTo2D(startPoint, lineStart); + +	Common::Point lineEnd; +	project3DTo2D(_weaponLocation, lineEnd); + +	surface->drawThickLine(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y, 2, 1, color); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/energybeam.h b/engines/pegasus/neighborhood/mars/energybeam.h new file mode 100644 index 0000000000..715ed4b01d --- /dev/null +++ b/engines/pegasus/neighborhood/mars/energybeam.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_ENERGYBEAM_H +#define PEGASUS_NEIGHBORHOOD_MARS_ENERGYBEAM_H + +#include "pegasus/neighborhood/mars/shuttleweapon.h" + +namespace Pegasus { + +class EnergyBeam : public ShuttleWeapon { +public: +	EnergyBeam(); +	virtual ~EnergyBeam() {} + +	void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/gravitoncannon.cpp b/engines/pegasus/neighborhood/mars/gravitoncannon.cpp new file mode 100644 index 0000000000..13dc6dfc77 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/gravitoncannon.cpp @@ -0,0 +1,134 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/mars/gravitoncannon.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +static const TimeValue kGravitonTime = kOneSecond * kShuttleWeaponScale; + +static const CoordType kGravitonOriginH = kShuttleWindowLeft - 1; +static const CoordType kGravitonOriginV = kShuttleWindowMidV; + +static const float kGravitonXOrigin = convertScreenHToSpaceX(kGravitonOriginH, kGravitonMinDistance); +static const float kGravitonYOrigin = convertScreenVToSpaceY(kGravitonOriginV, kGravitonMinDistance); +static const float kGravitonZOrigin = kGravitonMinDistance; + +// Width of graviton sprite... +static const CoordType kGravitonMaxScreenWidth = 78; +static const CoordType kGravitonMaxScreenHeight = 46; + +static const float kGravitonWidth = convertScreenHToSpaceX(kShuttleWindowMidH + kGravitonMaxScreenWidth / 2, kGravitonMinDistance) +		- convertScreenHToSpaceX(kShuttleWindowMidH - kGravitonMaxScreenWidth / 2, kGravitonMinDistance); +static const float kGravitonHeight = convertScreenVToSpaceY(kShuttleWindowMidV - kGravitonMaxScreenHeight / 2, kGravitonMinDistance) +		- convertScreenVToSpaceY(kShuttleWindowMidV + kGravitonMaxScreenHeight / 2, kGravitonMinDistance); + +GravitonCannon::GravitonCannon() { +	_weaponDuration = kGravitonTime; +	setSegment(0, kGravitonTime); +	_weaponOrigin = Point3D(kGravitonXOrigin, kGravitonYOrigin, kGravitonZOrigin); +	_rightOrigin = Point3D(-kGravitonXOrigin, kGravitonYOrigin, kGravitonZOrigin); +} + +void GravitonCannon::initShuttleWeapon() { +	ShuttleWeapon::initShuttleWeapon(); +	_gravitonImage.getImageFromPICTFile("Images/Mars/Graviton Cannon"); +	_gravitonImage.getSurfaceBounds(_gravitonBounds); +} + +void GravitonCannon::cleanUpShuttleWeapon() { +	_gravitonImage.deallocateSurface(); +	ShuttleWeapon::cleanUpShuttleWeapon(); +} + +void GravitonCannon::draw(const Common::Rect &) {	 +	// Left graviton... +	Point3D pt3D = _weaponLocation; +	pt3D.translate(-kGravitonWidth / 2, kGravitonHeight / 2, 0); +	Common::Point pt2D; +	project3DTo2D(pt3D, pt2D); +	Common::Rect gravitonRect; +	gravitonRect.left = pt2D.x; +	gravitonRect.top = pt2D.y; +	 +	pt3D.translate(kGravitonWidth, -kGravitonHeight, 0); +	project3DTo2D(pt3D, pt2D); +	gravitonRect.right = pt2D.x; +	gravitonRect.bottom = pt2D.y; + +	_gravitonImage.scaleTransparentCopy(_gravitonBounds, gravitonRect); +	 +	// Right graviton... +	pt3D = _rightLocation; +	pt3D.translate(-kGravitonWidth / 2, kGravitonHeight / 2, 0); +	project3DTo2D(pt3D, pt2D); +	gravitonRect.left = pt2D.x; +	gravitonRect.top = pt2D.y; +	 +	pt3D.translate(kGravitonWidth, -kGravitonHeight, 0); +	project3DTo2D(pt3D, pt2D); +	gravitonRect.right = pt2D.x; +	gravitonRect.bottom = pt2D.y; + +	_gravitonImage.scaleTransparentCopy(_gravitonBounds, gravitonRect); +} + +void GravitonCannon::updateWeaponPosition() { +	ShuttleWeapon::updateWeaponPosition(); +	if (_weaponTime != 1.0) +		linearInterp(_rightOrigin, _weaponTarget, _weaponTime, _rightLocation); +} + +bool GravitonCannon::collisionWithJunk(Common::Point &impactPoint) {	 +	if (getDisplayOrder() == kShuttleWeaponFrontOrder) { +		Point3D junkPosition; +		g_spaceJunk->getJunkPosition(junkPosition); + +		if (junkPosition.z < _weaponLocation.z) { +			setDisplayOrder(kShuttleWeaponBackOrder); +			project3DTo2D(_weaponLocation, impactPoint); + +			if (g_spaceJunk->pointInJunk(impactPoint)) +				return true; + +			project3DTo2D(_rightLocation, impactPoint); +			return g_spaceJunk->pointInJunk(impactPoint); +		} +	} + +	return false; +} + +void GravitonCannon::hitJunk(Common::Point impactPoint) { +	g_spaceJunk->hitByGravitonCannon(impactPoint); +} + +void GravitonCannon::hitShuttle(Common::Point impactPoint) { +	g_robotShip->hitByGravitonCannon(impactPoint); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/gravitoncannon.h b/engines/pegasus/neighborhood/mars/gravitoncannon.h new file mode 100644 index 0000000000..b94fd55e5b --- /dev/null +++ b/engines/pegasus/neighborhood/mars/gravitoncannon.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_GRAVITONCANNON_H +#define PEGASUS_NEIGHBORHOOD_MARS_GRAVITONCANNON_H + +#include "pegasus/surface.h" +#include "pegasus/neighborhood/mars/shuttleweapon.h" + +namespace Pegasus { + +class GravitonCannon : public ShuttleWeapon { +public: +	GravitonCannon(); +	virtual ~GravitonCannon() {} + +	void initShuttleWeapon(); +	void cleanUpShuttleWeapon(); + +	void draw(const Common::Rect &); + +protected: +	virtual void updateWeaponPosition(); +	virtual bool collisionWithJunk(Common::Point &impactPoint); +	virtual void hitJunk(Common::Point impactPoint); +	virtual void hitShuttle(Common::Point impactPoint); + +	Surface _gravitonImage; +	Common::Rect _gravitonBounds; +	Point3D _rightOrigin, _rightLocation; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/hermite.cpp b/engines/pegasus/neighborhood/mars/hermite.cpp new file mode 100644 index 0000000000..dc4a2c5fc2 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/hermite.cpp @@ -0,0 +1,76 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/mars/hermite.h" + +namespace Pegasus { + +CoordType hermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 time, int32 duration) {	 +	float t = (float)time / duration; +	float tsq = t * t; +	float tcu = t * tsq; +	float tcu2 = tcu + tcu; +	float tsq2 = tsq + tsq; +	float tsq3 = tsq2 + tsq; +	return (CoordType)((tcu2 - tsq3 + 1) * p1 + (tsq3 - tcu2) * p4 + (tcu - tsq2 + t) * r1 + (tcu - tsq) * r4); +} + +CoordType dHermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 time, int32 duration) {	 +	float t = (float)time / duration; +	float t2 = t + t; +	float t4 = t2 + t2; +	float t6 = t4 + t2; +	float tsq = t * t; +	float tsq3 = tsq + tsq + tsq; +	float tsq6 = tsq3 + tsq3; +	return (CoordType)((tsq6 - t6) * p1 + (t6 - tsq6) * p4 + (tsq3 - t4 + 1) * r1 + (tsq3 - t2) * r4); +} + +void hermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 time, int32 duration, Common::Point &result) {	 +	float t = (float)time / duration; +	float tsq = t * t; +	float tcu = t * tsq; +	float tcu2 = tcu + tcu; +	float tsq2 = tsq + tsq; +	float tsq3 = tsq2 + tsq; + +	result.x = (int16)((tcu2 - tsq3 + 1) * p1.x + (tsq3 - tcu2) * p4.x + (tcu - tsq2 + t) * r1.x + (tcu - tsq) * r4.x); +	result.y = (int16)((tcu2 - tsq3 + 1) * p1.y + (tsq3 - tcu2) * p4.y + (tcu - tsq2 + t) * r1.y + (tcu - tsq) * r4.y); +} + +void dHermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 time, int32 duration, Common::Point &result) {	 +	float t = (float)time / duration; +	float t2 = t + t; +	float t4 = t2 + t2; +	float t6 = t4 + t2; +	float tsq = t * t; +	float tsq3 = tsq + tsq + tsq; +	float tsq6 = tsq3 + tsq3; + +	result.x = (int16)((tsq6 - t6) * p1.x + (t6 - tsq6) * p4.x + (tsq3 - t4 + 1) * r1.x + (tsq3 - t2) * r4.x); +	result.y = (int16)((tsq6 - t6) * p1.y + (t6 - tsq6) * p4.y + (tsq3 - t4 + 1) * r1.y + (tsq3 - t2) * r4.y); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/hermite.h b/engines/pegasus/neighborhood/mars/hermite.h new file mode 100644 index 0000000000..44cb3a5a11 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/hermite.h @@ -0,0 +1,41 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_HERMITE_H +#define PEGASUS_NEIGHBORHOOD_MARS_HERMITE_H + +#include "common/rect.h" +#include "pegasus/types.h" + +namespace Pegasus { + +CoordType hermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 t, int32 duration); +CoordType dHermite(CoordType p1, CoordType p4, CoordType r1, CoordType r4, int32 t, int32 duration); +void hermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 t, int32 duration, Common::Point &result); +void dHermite(Common::Point p1, Common::Point p4, Common::Point r1, Common::Point r4, int32 t, int32 duration, Common::Point &result); + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/mars.cpp b/engines/pegasus/neighborhood/mars/mars.cpp new file mode 100644 index 0000000000..c950a85897 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/mars.cpp @@ -0,0 +1,3749 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/events.h" +#include "video/qt_decoder.h" + +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/mars/mars.h" + +namespace Pegasus { + +// This should really be 22.5. +// Probably no one will know the difference. +static const int16 kMarsShieldPanelOffsetAngle = 22; + +static const CanMoveForwardReason kCantMoveRobotBlocking = kCantMoveLastReason + 1; + +static const NotificationFlags kTimeForCanyonChaseFlag = kLastNeighborhoodNotificationFlag << 1; +static const NotificationFlags kExplosionFinishedFlag = kTimeForCanyonChaseFlag << 1; +static const NotificationFlags kTimeToTransportFlag = kExplosionFinishedFlag << 1; + +static const NotificationFlags kMarsNotificationFlags = kTimeForCanyonChaseFlag | +													kExplosionFinishedFlag | +													kTimeToTransportFlag; + +static const TimeValue kLittleExplosionStart = 0 * 40; +static const TimeValue kLittleExplosionStop = 24 * 40; + +static const TimeValue kBigExplosionStart = 24 * 40; +static const TimeValue kBigExplosionStop = 62 * 40; + +enum { +	kMaze007RobotLoopingEvent, +	kMaze015RobotLoopingEvent, +	kMaze101RobotLoopingEvent, +	kMaze104RobotLoopingEvent, +	kMaze133RobotLoopingEvent, +	kMaze136RobotLoopingEvent, +	kMaze184RobotLoopingEvent +}; + +enum { +	kMaze007RobotLoopingTime = (64 + 96) * kMarsFrameDuration, +	kMaze015RobotLoopingTime = (64 + 93) * kMarsFrameDuration, +	kMaze101RobotLoopingTime = (64 + 45) * kMarsFrameDuration, +	kMaze104RobotLoopingTime = 96 * kMarsFrameDuration, +	kMaze133RobotLoopingTime = (64 + 96) * kMarsFrameDuration, +	kMaze136RobotLoopingTime = (64 + 96) * kMarsFrameDuration, +	kMaze184RobotLoopingTime = 96 * kMarsFrameDuration +}; + +// I've made a couple macros for these rects so we don't +// have to globally construct them or whatnot +#define kShuttleEnergyBeamBounds Common::Rect(24, 27, 24 + 112, 27 + 46) +#define kShuttleGravitonBounds Common::Rect(24, 73, 24 + 112, 73 + 30) +#define kShuttleTractorBounds Common::Rect(24, 103, 24 + 112, 103 + 30) +#define kShuttleTransportBounds Common::Rect(484, 353, 89 + 484, 79 + 353) + +void robotTimerExpiredFunction(FunctionPtr *, void *mars) { +	((Mars *)mars)->robotTiredOfWaiting(); +} + +void lockThawTimerExpiredFunction(FunctionPtr *, void *mars) { +	((Mars *)mars)->lockThawed(); +} + +void bombTimerExpiredFunction(FunctionPtr *, void *mars) { +	((Mars *)mars)->didntFindBomb(); +} + +void bombTimerExpiredInGameFunction(FunctionPtr *, void *mars) { +	((Mars *)mars)->bombExplodesInGame(); +} + +void airStageExpiredFunction(FunctionPtr *, void *mars) { +	((Mars *)mars)->airStageExpired(); +} + +void marsTimerFunction(FunctionPtr *, void *event) { +	((MarsTimerEvent *)event)->mars->marsTimerExpired(*(MarsTimerEvent *)event); +} + +Mars::Mars(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Mars", kMarsID), +		_guessObject(kNoDisplayElement), _undoPict(kNoDisplayElement), _guessHistory(kNoDisplayElement), +		_choiceHighlight(kNoDisplayElement), _shuttleInterface1(kNoDisplayElement), _shuttleInterface2(kNoDisplayElement), +		_shuttleInterface3(kNoDisplayElement), _shuttleInterface4(kNoDisplayElement), _canyonChaseMovie(kNoDisplayElement), +		_leftShuttleMovie(kNoDisplayElement), _rightShuttleMovie(kNoDisplayElement), _lowerLeftShuttleMovie(kNoDisplayElement), +		_lowerRightShuttleMovie(kNoDisplayElement), _centerShuttleMovie(kNoDisplayElement), +		_upperLeftShuttleMovie(kNoDisplayElement), _upperRightShuttleMovie(kNoDisplayElement), +		_leftDamageShuttleMovie(kNoDisplayElement), _rightDamageShuttleMovie(kNoDisplayElement), _explosions(kNoDisplayElement), +		_planetMovie(kNoDisplayElement), _junk(kNoDisplayElement), _energyChoiceSpot(kShuttleEnergySpotID), +		_gravitonChoiceSpot(kShuttleGravitonSpotID), _tractorChoiceSpot(kShuttleTractorSpotID), +		_shuttleViewSpot(kShuttleViewSpotID), _shuttleTransportSpot(kShuttleTransportSpotID) { +	_noAirFuse.setFunctionPtr(&airStageExpiredFunction, this); +	setIsItemTaken(kMarsCard); +	setIsItemTaken(kAirMask); +	setIsItemTaken(kCrowbar); +	setIsItemTaken(kCardBomb); +} + +Mars::~Mars() { +	g_allHotspots.remove(&_energyChoiceSpot); +	g_allHotspots.remove(&_gravitonChoiceSpot); +	g_allHotspots.remove(&_tractorChoiceSpot); +	g_allHotspots.remove(&_shuttleViewSpot); +	g_allHotspots.remove(&_shuttleTransportSpot); +} + +void Mars::init() {	 +	Neighborhood::init(); +	 +	Hotspot *attackSpot = g_allHotspots.findHotspotByID(kAttackRobotHotSpotID); +	attackSpot->setMaskedHotspotFlags(kDropItemSpotFlag, kDropItemSpotFlag); +	_attackingItem = NULL; +	 +	forceStridingStop(kMars08, kNorth, kAltMarsNormal); + +	_neighborhoodNotification.notifyMe(this, kMarsNotificationFlags, kMarsNotificationFlags); + +	_explosionCallBack.setNotification(&_neighborhoodNotification); +	_explosionCallBack.setCallBackFlag(kExplosionFinishedFlag); +} + +void Mars::flushGameState() { +	g_energyMonitor->saveCurrentEnergyValue(); +} + +void Mars::start() { +	g_energyMonitor->stopEnergyDraining(); +	g_energyMonitor->restoreLastEnergyValue(); +	_vm->resetEnergyDeathReason(); +	g_energyMonitor->startEnergyDraining(); +	Neighborhood::start(); +} + +class AirMaskCondition : public AICondition { +public: +	AirMaskCondition(const uint32); +	 +	virtual bool fireCondition(); + +protected: +	uint32 _airThreshold; +	uint32 _lastAirLevel; +}; + +AirMaskCondition::AirMaskCondition(const uint32 airThreshold) { +	_airThreshold = airThreshold; +	_lastAirLevel = g_airMask->getAirLeft(); +} + +bool AirMaskCondition::fireCondition() { +	bool result = g_airMask && g_airMask->isAirMaskOn() && +			g_airMask->getAirLeft() <= _airThreshold && _lastAirLevel > _airThreshold; + +	_lastAirLevel = g_airMask->getAirLeft(); +	return result; +} + +void Mars::setUpAIRules() {	 +	Neighborhood::setUpAIRules(); + +	// Don't add these rules if we're going to the robot's shuttle... +	if (g_AIArea && !GameState.getMarsReadyForShuttleTransport()) { +		AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB1E", false); +		AILocationCondition *locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kMars47, kSouth)); +		AIRule *rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); + +		messageAction = new AIPlayMessageAction("Images/AI/Mars/XM27NB", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kMars27, kNorth)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); + +		messageAction = new AIPlayMessageAction("Images/AI/Mars/XM27NB", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kMars28, kNorth)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); + +		messageAction = new AIPlayMessageAction("Images/AI/Mars/XM41ED", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kMars19, kEast)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); + +		AIDeactivateRuleAction *deactivate = new AIDeactivateRuleAction(rule); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kMars35, kWest)); +		rule = new AIRule(locCondition, deactivate); +		g_AIArea->addAIRule(rule); + +		messageAction = new AIPlayMessageAction("Images/AI/Mars/XM41ED", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kMars48, kWest)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); + +		AirMaskCondition *airMask50Condition = new AirMaskCondition(50); +		messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB1", false); +		AIRule *rule50 = new AIRule(airMask50Condition, messageAction); + +		AirMaskCondition *airMask25Condition = new AirMaskCondition(25); +		AICompoundAction *compound = new AICompoundAction(); +		messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB2", false); +		compound->addAction(messageAction); +		deactivate = new AIDeactivateRuleAction(rule50); +		compound->addAction(deactivate); +		AIRule *rule25 = new AIRule(airMask25Condition, compound); + +		AirMaskCondition *airMask5Condition = new AirMaskCondition(5); +		compound = new AICompoundAction; +		messageAction = new AIPlayMessageAction("Images/AI/Mars/XMMAZB3", false); +		compound->addAction(messageAction); +		deactivate = new AIDeactivateRuleAction(rule50); +		compound->addAction(deactivate); +		deactivate = new AIDeactivateRuleAction(rule25); +		compound->addAction(deactivate); +		AIRule *rule5 = new AIRule(airMask5Condition, compound); + +		g_AIArea->addAIRule(rule5); +		g_AIArea->addAIRule(rule25); +		g_AIArea->addAIRule(rule50); + +		messageAction = new AIPlayMessageAction("Images/AI/Mars/XM51ND", false); +		AIDoorOpenedCondition *doorOpen = new AIDoorOpenedCondition(MakeRoomView(kMars51, kEast)); +		rule = new AIRule(doorOpen, messageAction); +		g_AIArea->addAIRule(rule); +	} +} + +uint16 Mars::getDateResID() const { +	return kDate2185ID; +} + +TimeValue Mars::getViewTime(const RoomID room, const DirectionConstant direction) { +	ExtraTable::Entry extra; +	SpotTable::Entry spotEntry; +	uint32 extraID = 0xffffffff; + +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kMars0A, kNorth): +		if (!GameState.getMarsSeenTimeStream()) { +			getExtraEntry(kMarsArrivalFromTSA, extra); +			return extra.movieStart; +		} +		break; +	case MakeRoomView(kMars31South, kSouth): +		if (GameState.isTakenItemID(kMarsCard)) +			extraID = kMars31SouthZoomViewNoCard; +		break; +	case MakeRoomView(kMars31, kSouth): +		if (GameState.isTakenItemID(kMarsCard)) +			extraID = kMars31SouthViewNoCard; +		break; +	case MakeRoomView(kMars34, kSouth): +		if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { +			if (GameState.isTakenItemID(kCrowbar)) +				extraID = kMars34ViewOpenNoBar; +			else +				extraID = kMars34ViewOpenWithBar; +		} +		break; +	case MakeRoomView(kMars36, kSouth): +	case MakeRoomView(kMars37, kSouth): +	case MakeRoomView(kMars38, kSouth): +		findSpotEntry(room, direction, kSpotOnTurnMask | kSpotLoopsMask, spotEntry); +		return spotEntry.movieStart; +	case MakeRoomView(kMars45, kNorth): +		if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { +			if (GameState.isTakenItemID(kCrowbar)) +				extraID = kMars45ViewOpenNoBar; +			else +				extraID = kMars45ViewOpenWithBar; +		} +		break; +	case MakeRoomView(kMars48, kEast): +		if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) +			extraID = kMars48RobotView; +		break; +	case MakeRoomView(kMars56, kEast): +		if (_privateFlags.getFlag(kMarsPrivateBombExposedFlag)) { +			if (_privateFlags.getFlag(kMarsPrivateDraggingBombFlag)) +				extraID = kMars57ViewOpenNoBomb; +			else +				extraID = kMars57ExposeBomb; +		} else if (GameState.getMarsLockBroken()) { +			extraID = kMars57OpenPanelChoices; +		} else if (GameState.getMarsLockFrozen()) { +			extraID = kMars57LockFrozenView; +		} +		break; +	case MakeRoomView(kMarsRobotShuttle, kEast): +		if (getCurrentActivation() == kActivationRobotHeadOpen) { +			extraID = kMarsRobotHead111; + +			if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag)) +				extraID -= 1; +			if (_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) +				extraID -= 2; +			if (_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag)) +				extraID -= 4; +		} +		break; +	} + +	if (extraID == 0xffffffff) +		return Neighborhood::getViewTime(room, direction); + +	getExtraEntry(extraID, extra); +	return extra.movieEnd - 1; +} + +void Mars::getZoomEntry(const HotSpotID spotID, ZoomTable::Entry &entry) { +	Neighborhood::getZoomEntry(spotID, entry); + +	uint32 extraID = 0xffffffff; + +	switch (spotID) { +	case kMars31SouthSpotID: +		if (GameState.getCurrentDirection() == kSouth && GameState.isTakenItemID(kMarsCard)) +			extraID = kMars31SouthZoomInNoCard; +		break; +	case kMars31SouthOutSpotID: +		if (GameState.getCurrentDirection() == kSouth && GameState.isTakenItemID(kMarsCard)) +			extraID = kMars31SouthZoomOutNoCard; +		break; +	} + +	if (extraID != 0xffffffff) { +		ExtraTable::Entry extra; +		getExtraEntry(extraID, extra); +		entry.movieStart = extra.movieStart; +		entry.movieEnd = extra.movieEnd; +	} +} + +void Mars::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { +	Neighborhood::findSpotEntry(room, direction, flags, entry); + +	if ((flags & (kSpotOnArrivalMask | kSpotOnTurnMask)) != 0) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kMars27, kNorth): +			if (GameState.getMarsSeenThermalScan()) +				entry.clear(); +			else +				GameState.setMarsSeenThermalScan(true); +			break; +		case MakeRoomView(kMars28, kNorth): +			if (GameState.getMarsSeenThermalScan()) +				entry.clear(); +			else +				GameState.setMarsSeenThermalScan(true); +			break; +		} +	} +} + +CanMoveForwardReason Mars::canMoveForward(ExitTable::Entry &entry) { +	CanMoveForwardReason reason = Neighborhood::canMoveForward(entry); + +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kMars48, kEast): +		if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) +			reason = kCantMoveRobotBlocking; +		break; +	case MakeRoomView(kMars48, kSouth): +		if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) +			_utilityFuse.stopFuse(); +		break; +	} + +	return reason; +} + +void Mars::cantMoveThatWay(CanMoveForwardReason reason) { +	if (reason == kCantMoveRobotBlocking) { +		startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput); +		loadLoopSound2(""); +	} else { +		Neighborhood::cantMoveThatWay(reason); +	} +} + +void Mars::moveForward() { +	if (GameState.getCurrentRoom() == kMars02 || (GameState.getCurrentRoom() >= kMars05 && GameState.getCurrentRoom() <= kMars08)) +		loadLoopSound2(""); + +	Neighborhood::moveForward(); +} + +void Mars::bumpIntoWall() { +	requestSpotSound(kMarsBumpIntoWallIn, kMarsBumpIntoWallOut, kFilterNoInput, 0); +	Neighborhood::bumpIntoWall(); +} + +CanOpenDoorReason Mars::canOpenDoor(DoorTable::Entry &entry) { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kMars05, kEast): +	case MakeRoomView(kMars06, kEast): +	case MakeRoomView(kMars07, kEast): +		if (!GameState.getMarsSecurityDown()) +			return kCantOpenLocked; +		break; +	case MakeRoomView(kMarsMaze037, kWest): +	case MakeRoomView(kMarsMaze038, kEast): +		if (GameState.getMarsMazeDoorPair1()) +			return kCantOpenLocked; +		break; +	case MakeRoomView(kMarsMaze050, kNorth): +	case MakeRoomView(kMarsMaze058, kSouth): +		if (!GameState.getMarsMazeDoorPair1()) +			return kCantOpenLocked; +		break; +	case MakeRoomView(kMarsMaze047, kNorth): +	case MakeRoomView(kMarsMaze142, kSouth): +		if (GameState.getMarsMazeDoorPair2()) +			return kCantOpenLocked; +		break; +	case MakeRoomView(kMarsMaze057, kNorth): +	case MakeRoomView(kMarsMaze136, kSouth): +		if (!GameState.getMarsMazeDoorPair2()) +			return kCantOpenLocked; +		break; +	case MakeRoomView(kMarsMaze120, kWest): +	case MakeRoomView(kMarsMaze121, kEast): +		if (GameState.getMarsMazeDoorPair3()) +			return kCantOpenLocked; +		break; +	case MakeRoomView(kMarsMaze081, kNorth): +	case MakeRoomView(kMarsMaze083, kSouth): +		if (!GameState.getMarsMazeDoorPair3()) +			return kCantOpenLocked; +		break; +	} + +	return Neighborhood::canOpenDoor(entry); +} + +void Mars::cantOpenDoor(CanOpenDoorReason reason) { +	switch (GameState.getCurrentRoom()) { +	case kMars05: +	case kMars06: +	case kMars07: +		playSpotSoundSync(kMarsCantOpenShuttleIn, kMarsCantOpenShuttleOut); +		break; +	default: +		Neighborhood::cantOpenDoor(reason); +		break; +	} +} + +void Mars::openDoor() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kMars06, kEast): +	case MakeRoomView(kMars07, kEast): +		if (GameState.getMarsSecurityDown()) +			playSpotSoundSync(kMarsNoShuttleIn, kMarsNoShuttleOut); +		break; +	case MakeRoomView(kMars47, kSouth): +		if (GameState.isTakenItemID(kAirMask)) +			setCurrentAlternate(kAltMarsTookMask); +		else +			setCurrentAlternate(kAltMarsNormal); +		break; +	case MakeRoomView(kMars48, kNorth): +		if (GameState.getMarsPodAtUpperPlatform()) +			setCurrentAlternate(kAltMarsNormal); +		else +			setCurrentAlternate(kAltMarsPodAtMars45); +		break; +	case MakeRoomView(kMars48, kEast): +		if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) { +			die(kDeathDidntGetOutOfWay); +			return; +		} +		break; +	} + +	Neighborhood::openDoor(); +} + +void Mars::doorOpened() { +	switch (GameState.getCurrentRoom()) { +	case kMars27: +	case kMars28: +		if (GameState.getCurrentDirection() == kNorth) +			_vm->die(kDeathArrestedInMars); +		else +			Neighborhood::doorOpened(); +		break; +	case kMars41: +	case kMars42: +		if (GameState.getCurrentDirection() == kEast) +			_vm->die(kDeathWrongShuttleLock); +		else +			Neighborhood::doorOpened(); +		break; +	case kMars51: +		Neighborhood::doorOpened(); +		setUpReactorEnergyDrain(); + +		if (g_AIArea) +			g_AIArea->checkRules(); +		break; +	case kMars19: +		if (GameState.getCurrentDirection() == kEast) +			GameState.setMarsAirlockOpen(true); + +		Neighborhood::doorOpened(); +		break; +	case kMars48: +		if (GameState.getCurrentDirection() == kWest) +			GameState.setMarsAirlockOpen(true); + +		Neighborhood::doorOpened(); +		break; +	default: +		Neighborhood::doorOpened(); +		break; +	} +} + +void Mars::setUpReactorEnergyDrain() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kMars51, kEast): +		if (GameState.isCurrentDoorOpen()) { +			if (g_energyMonitor->getEnergyDrainRate() == kEnergyDrainNormal) { +				if (GameState.getShieldOn()) { +					g_shield->setItemState(kShieldRadiation); +					g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainWithShield); +				} else { +					g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainNoShield); +				} +				_vm->setEnergyDeathReason(kDeathReactorBurn); +			} +		} else { +			if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) { +				if (GameState.getShieldOn()) +					g_shield->setItemState(kShieldNormal); +				g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); +				_vm->resetEnergyDeathReason(); +			} +		} +		break; +	case MakeRoomView(kMars52, kNorth): +	case MakeRoomView(kMars52, kSouth): +	case MakeRoomView(kMars52, kEast): +	case MakeRoomView(kMars52, kWest): +	case MakeRoomView(kMars54, kNorth): +	case MakeRoomView(kMars54, kSouth): +	case MakeRoomView(kMars54, kEast): +	case MakeRoomView(kMars54, kWest): +	case MakeRoomView(kMars56, kNorth): +	case MakeRoomView(kMars56, kSouth): +	case MakeRoomView(kMars56, kEast): +	case MakeRoomView(kMars56, kWest): +	case MakeRoomView(kMars58, kNorth): +	case MakeRoomView(kMars58, kSouth): +	case MakeRoomView(kMars58, kEast): +	case MakeRoomView(kMars58, kWest): +		if (g_energyMonitor->getEnergyDrainRate() == kEnergyDrainNormal) { +			if (GameState.getShieldOn()) { +				g_shield->setItemState(kShieldRadiation); +				g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainWithShield); +			} else { +				g_energyMonitor->setEnergyDrainRate(kMarsReactorEnergyDrainNoShield); +			} +			_vm->setEnergyDeathReason(kDeathReactorBurn); +		} +		break; +	default: +		if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) { +			if (GameState.getShieldOn()) +				g_shield->setItemState(kShieldNormal); +			g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); +			_vm->resetEnergyDeathReason(); +		} +		break; +	} +} + +void Mars::closeDoorOffScreen(const RoomID room, const DirectionConstant direction) { +	switch (room) { +	case kMars51: +		playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); +		if (GameState.getShieldOn()) +			g_shield->setItemState(kShieldNormal); +		g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); +		_vm->resetEnergyDeathReason(); +		break; +	case kMars05: +	case kMars06: +	case kMars07: +	case kMars13: +	case kMars22: +	case kMars47: +	case kMars52: +		playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); +		break; +	case kMars18: +	case kMars32: +		playSpotSoundSync(kMarsTransportDoorCloseIn, kMarsTransportDoorCloseOut); +		break; +	case kMars19: +		if (GameState.getCurrentRoom() != kMars35) { +			playSpotSoundSync(kMarsBigAirlockDoorCloseIn, kMarsBigAirlockDoorCloseOut); +			GameState.setMarsAirlockOpen(false); +		} +		break; +	case kMars36: +		if (GameState.getCurrentRoom() != kMars35) +			playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut); +		break; +	case kMars48: +		if (direction == kWest) { +			if (GameState.getCurrentRoom() != kMars60) { +				playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut); +				GameState.setMarsAirlockOpen(false); +			} +		} else { +			playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); +		} +		break; +	case kMars41: +	case kMars42: +	case kMars43: +		if (direction == kWest) +			playSpotSoundSync(kMarsGantryDoorCloseIn, kMarsGantryDoorCloseOut); +		break; +	case kMarsMaze037: +	case kMarsMaze038: +	case kMarsMaze012: +	case kMarsMaze066: +	case kMarsMaze050: +	case kMarsMaze058: +	case kMarsMaze057: +	case kMarsMaze136: +	case kMarsMaze047: +	case kMarsMaze142: +	case kMarsMaze133: +	case kMarsMaze132: +	case kMarsMaze113: +	case kMarsMaze114: +	case kMarsMaze120: +	case kMarsMaze121: +	case kMarsMaze081: +	case kMarsMaze083: +	case kMarsMaze088: +	case kMarsMaze089: +	case kMarsMaze179: +	case kMarsMaze180: +		playSpotSoundSync(kMarsMazeDoorCloseIn, kMarsMazeDoorCloseOut); +		break; +	} +} + +void Mars::checkAirlockDoors() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kMars19, kWest): +	case MakeRoomView(kMars18, kWest): +	case MakeRoomView(kMars17, kWest): +	case MakeRoomView(kMars16, kWest): +	case MakeRoomView(kMars15, kWest): +	case MakeRoomView(kMars14, kWest): +	case MakeRoomView(kMars12, kWest): +	case MakeRoomView(kMars11, kWest): +	case MakeRoomView(kMars10, kWest): +		if (GameState.getMarsInAirlock()) { +			playSpotSoundSync(kMarsBigAirlockDoorCloseIn, kMarsBigAirlockDoorCloseOut); +			GameState.setMarsInAirlock(false); +		} +		break; +	case MakeRoomView(kMars36, kEast): +	case MakeRoomView(kMars37, kEast): +	case MakeRoomView(kMars38, kEast): +	case MakeRoomView(kMars39, kEast): +	case MakeRoomView(kMars48, kEast): +	case MakeRoomView(kMars50, kEast): +	case MakeRoomView(kMars51, kEast): +	case MakeRoomView(kMars52, kEast): +		if (GameState.getMarsInAirlock()) { +			playSpotSoundSync(kMarsSmallAirlockDoorCloseIn, kMarsSmallAirlockDoorCloseOut); +			GameState.setMarsInAirlock(false); +		} +		break; +	case MakeRoomView(kMars35, kWest): +	case MakeRoomView(kMars35, kEast): +	case MakeRoomView(kMars60, kWest): +	case MakeRoomView(kMars60, kEast): +		GameState.setMarsInAirlock(true); +		break; +	default: +		GameState.setMarsInAirlock(false); +		break; +	} +} + +int16 Mars::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { +	int16 angle = Neighborhood::getStaticCompassAngle(room, dir); + +	switch (MakeRoomView(room, dir)) { +	case MakeRoomView(kMars0A, kNorth): +		angle -= 20; +		break; +	case MakeRoomView(kMars23, kNorth): +	case MakeRoomView(kMars23, kSouth): +	case MakeRoomView(kMars23, kEast): +	case MakeRoomView(kMars23, kWest): +	case MakeRoomView(kMars26, kNorth): +	case MakeRoomView(kMars26, kSouth): +	case MakeRoomView(kMars26, kEast): +	case MakeRoomView(kMars26, kWest): +		angle += 30; +		break; +	case MakeRoomView(kMars24, kNorth): +	case MakeRoomView(kMars24, kSouth): +	case MakeRoomView(kMars24, kEast): +	case MakeRoomView(kMars24, kWest): +	case MakeRoomView(kMars25, kNorth): +	case MakeRoomView(kMars25, kSouth): +	case MakeRoomView(kMars25, kEast): +	case MakeRoomView(kMars25, kWest): +		angle -= 30; +		break; +	case MakeRoomView(kMars54, kNorth): +	case MakeRoomView(kMars54, kSouth): +	case MakeRoomView(kMars54, kEast): +	case MakeRoomView(kMars54, kWest): +		angle += 90; +		break; +	case MakeRoomView(kMars56, kNorth): +	case MakeRoomView(kMars56, kSouth): +	case MakeRoomView(kMars56, kEast): +	case MakeRoomView(kMars56, kWest): +		angle += 180; +		break; +	case MakeRoomView(kMars58, kNorth): +	case MakeRoomView(kMars58, kSouth): +	case MakeRoomView(kMars58, kEast): +	case MakeRoomView(kMars58, kWest): +		angle -= 90; +		break; +	} + +	return angle; +} + +void Mars::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { +	Neighborhood::getExitCompassMove(exitEntry, compassMove); + +	if (exitEntry.room == kMars43 && exitEntry.direction == kEast) { +		compassMove.insertFaderKnot(exitEntry.movieStart + 16 * kMarsFrameDuration, 90); +		compassMove.insertFaderKnot(exitEntry.movieStart + 32 * kMarsFrameDuration, 270); +	} else if (exitEntry.room == kMars46 && exitEntry.direction == kWest && exitEntry.altCode != kAltMarsPodAtMars45) { +		compassMove.makeTwoKnotFaderSpec(kMarsMovieScale, exitEntry.movieStart, 270, exitEntry.movieEnd, 360); +		compassMove.insertFaderKnot(exitEntry.movieStart + 43 * kMarsFrameDuration, 270); +		compassMove.insertFaderKnot(exitEntry.movieStart + 58 * kMarsFrameDuration, 360); +	} +} + +void Mars::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { +	switch (entry.extra) { +	case kMarsTakePodToMars45: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 0, entry.movieEnd, 180); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 3), 30); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 11), 10); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 14), 40); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 16), 30); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 23), 100); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 31), 70); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 34), 100); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 37), 85); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 42), 135); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 44), 125); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 46), 145); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 49), 160); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * (kMarsFramesPerSecond * 51), 180); +		break; +	case kMars35WestSpinAirlockToEast: +	case kMars60WestSpinAirlockToEast: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 90, entry.movieEnd, 270); +		compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale, 90); +		compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale * 3, 270); +		break; +	case kMars35EastSpinAirlockToWest: +	case kMars60EastSpinAirlockToWest: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, 270, entry.movieEnd, 90); +		compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale, 270); +		compassMove.insertFaderKnot(entry.movieStart + kMarsMovieScale * 3, 90); +		break; +	case kMars52SpinLeft: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, entry.movieEnd, kMars54Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars54Compass); +		break; +	case kMars52SpinRight: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, entry.movieEnd, kMars58Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars58Compass); +		break; +	case kMars52Extend: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars52Compass, +				entry.movieEnd, kMars52Compass + kMarsShieldPanelOffsetAngle); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars52Compass + kMarsShieldPanelOffsetAngle); +		break; +	case kMars53Retract: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, +				kMars52Compass + kMarsShieldPanelOffsetAngle, entry.movieEnd, kMars52Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars52Compass + kMarsShieldPanelOffsetAngle); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars52Compass); +		break; +	case kMars56ExtendWithBomb: +	case kMars56ExtendNoBomb: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass, +				entry.movieEnd, kMars56Compass - kMarsShieldPanelOffsetAngle); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars56Compass - kMarsShieldPanelOffsetAngle); +		break; +	case kMars57RetractWithBomb: +	case kMars57RetractNoBomb: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, +				kMars56Compass - kMarsShieldPanelOffsetAngle, entry.movieEnd, kMars56Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass - kMarsShieldPanelOffsetAngle); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 60, kMars56Compass); +		break; +	case kMars54SpinLeft: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars54Compass, entry.movieEnd, kMars56Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars54Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars56Compass); +		break; +	case kMars54SpinRight: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars54Compass, entry.movieEnd, kMars52Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars54Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars52Compass); +		break; +	case kMars56SpinLeft: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass, +				entry.movieEnd, kMars58Compass + 360); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars58Compass + 360); +		break; +	case kMars56SpinRight: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars56Compass, entry.movieEnd, kMars54Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars56Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars54Compass); +		break; +	case kMars58SpinLeft: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, kMars58Compass, +				entry.movieEnd, kMars52Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars58Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars52Compass); +		break; +	case kMars58SpinRight: +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), entry.movieStart, +				kMars58Compass + 360, entry.movieEnd, kMars56Compass); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 10, kMars58Compass + 360); +		compassMove.insertFaderKnot(entry.movieStart + kMarsFrameDuration * 110, kMars56Compass); +		break; +	default: +		Neighborhood::getExtraCompassMove(entry, compassMove); +	} +} + +void Mars::loadAmbientLoops() { +	RoomID room = GameState.getCurrentRoom(); + +	if ((room >= kMars0A && room <= kMars21) || (room >= kMars41 && room <= kMars43)) { +		if (GameState.getMarsSeenTimeStream()) +			loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF"); +	} else if (room >= kMars22 && room <= kMars31South) { +		loadLoopSound1("Sounds/Mars/Reception.02.22K.8.AIFF", 0x100 / 4); +	} else if (room >= kMars32 && room <= kMars34) { +		loadLoopSound1("Sounds/Mars/Pod Room Ambient.22K.8.AIFF"); +	} else if (room == kMars35) { +		if (getAirQuality(room) == kAirQualityVacuum) +			loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF"); +		else +			loadLoopSound1("Sounds/Mars/Gantry Ambient.22K.8.AIFF", 0x100 / 2); +	} else if (room >= kMars36 && room <= kMars39) { +		loadLoopSound1("Sounds/Mars/Gear Room Ambient.22K.8.AIFF"); +	} else if (room >= kMars45 && room <= kMars51) { +		loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF"); +	} else if (room >= kMars52 && room <= kMars58) { +		loadLoopSound1("Sounds/Mars/ReactorLoop.22K.8.AIFF"); +	} else if (room == kMars60) { +		if (getAirQuality(room) == kAirQualityVacuum) +			loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF"); +		else +			loadLoopSound1("Sounds/Mars/Lower Mars Ambient.22K.8.AIFF", 0x100 / 2); +	} else if (room >= kMarsMaze004 && room <= kMarsMaze200) { +		loadLoopSound1("Sounds/Mars/Mars Maze Ambient.22K.8.AIFF"); +	} else if (room == kMarsRobotShuttle) { +		loadLoopSound1("Sounds/Mars/Robot Shuttle.22K.8.AIFF"); +	} +	 +	if (!_noAirFuse.isFuseLit()) { +		switch (room) { +		case kMars02: +		case kMars05: +		case kMars06: +		case kMars07: +		case kMars08: +			loadLoopSound2("Sounds/Mars/Gantry Loop.aiff", 0x100, 0, 0); +			break; +		// Robot at maze 48 +		case kMarsMaze037: +			if (GameState.isCurrentDoorOpen()) +				loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); +			else +				loadLoopSound2(""); +			break; +		case kMarsMaze038: +		case kMarsMaze039: +		case kMarsMaze049: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); +			break; +		case kMarsMaze050: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); +			break; +		case kMarsMaze051: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); +			break; +		case kMarsMaze052: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); +			break; +		case kMarsMaze042: +		case kMarsMaze053: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 8); +			break; +		case kMarsMaze058: +			if (GameState.isCurrentDoorOpen()) +				loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); +			else +				loadLoopSound2(""); +			break; +		// Robot at 151 +		case kMarsMaze148: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); +			break; +		case kMarsMaze147: +		case kMarsMaze149: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); +			break; +		case kMarsMaze146: +		case kMarsMaze152: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); +			break; +		case kMarsMaze145: +		case kMarsMaze153: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); +			break; +		// Robots at 80 and 82. +		case kMarsMaze079: +		case kMarsMaze081: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); +			break; +		case kMarsMaze078: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); +			break; +		case kMarsMaze083: +			if (GameState.isCurrentDoorOpen()) +				loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); +			else +				loadLoopSound2(""); +			break; +		case kMarsMaze118: +		case kMarsMaze076: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); +			break; +		case kMarsMaze074: +		case kMarsMaze117: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); +			break; +		// Robot at 94 +		case kMarsMaze093: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); +			break; +		case kMarsMaze091: +		case kMarsMaze092: +		case kMarsMaze098: +		case kMarsMaze101: +		case kMarsMaze100: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); +			break; +		case kMarsMaze090: +		case kMarsMaze099: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); +			break; +		case kMarsMaze089: +			if (GameState.isCurrentDoorOpen()) +				loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); +			break; +		case kMarsMaze178: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 4); +			break; +		// Robot at 197 +		case kMarsMaze191: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100); +			break; +		case kMarsMaze190: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 * 3 / 4); +			break; +		case kMarsMaze198: +		case kMarsMaze189: +			loadLoopSound2("Sounds/Mars/Maze Sparks.22K.AIFF", 0x100 / 2); +			break; +		default: +			loadLoopSound2(""); +			break; +		} +	} +} + +void Mars::checkContinuePoint(const RoomID room, const DirectionConstant direction) { +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kMars02, kSouth): +	case MakeRoomView(kMars19, kEast): +	case MakeRoomView(kMars22, kNorth): +	case MakeRoomView(kMars43, kEast): +	case MakeRoomView(kMars51, kEast): +	case MakeRoomView(kMars56, kEast): +	case MakeRoomView(kMars60, kWest): +	case MakeRoomView(kMarsMaze004, kWest): +	case MakeRoomView(kMarsMaze009, kWest): +	case MakeRoomView(kMarsMaze012, kWest): +	case MakeRoomView(kMarsMaze037, kWest): +	case MakeRoomView(kMarsMaze047, kNorth): +	case MakeRoomView(kMarsMaze052, kWest): +	case MakeRoomView(kMarsMaze057, kNorth): +	case MakeRoomView(kMarsMaze071, kWest): +	case MakeRoomView(kMarsMaze081, kNorth): +	case MakeRoomView(kMarsMaze088, kWest): +	case MakeRoomView(kMarsMaze093, kWest): +	case MakeRoomView(kMarsMaze115, kNorth): +	case MakeRoomView(kMarsMaze120, kWest): +	case MakeRoomView(kMarsMaze126, kEast): +	case MakeRoomView(kMarsMaze133, kNorth): +	case MakeRoomView(kMarsMaze144, kNorth): +	case MakeRoomView(kMarsMaze156, kEast): +	case MakeRoomView(kMarsMaze162, kNorth): +	case MakeRoomView(kMarsMaze177, kWest): +	case MakeRoomView(kMarsMaze180, kNorth): +	case MakeRoomView(kMarsMaze187, kWest): +	case MakeRoomView(kMarsMaze199, kWest): +		makeContinuePoint(); +		break; +	case MakeRoomView(kMars05, kEast): +	case MakeRoomView(kMars06, kEast): +	case MakeRoomView(kMars07, kEast): +		if (GameState.getMarsSecurityDown()) +			makeContinuePoint(); +		break; +	case MakeRoomView(kMars46, kSouth): +		if (!GameState.getMarsSeenRobotAtReactor()) +			makeContinuePoint(); +		break; +	case MakeRoomView(kMars46, kWest): +		if (GameState.getMarsAvoidedReactorRobot()) +			makeContinuePoint(); +		break; +	} +} + +void Mars::launchMaze007Robot() { +	startExtraLongSequence(kMarsMaze007RobotApproach, kMarsMaze007RobotDeath, kExtraCompletedFlag, kFilterAllInput); +	scheduleEvent(kMaze007RobotLoopingTime, kMarsMovieScale, kMaze007RobotLoopingEvent); +} + +void Mars::launchMaze015Robot() { +	startExtraLongSequence(kMarsMaze015SouthRobotApproach, kMarsMaze015SouthRobotDeath, kExtraCompletedFlag, kFilterAllInput); +	scheduleEvent(kMaze015RobotLoopingTime, kMarsMovieScale, kMaze015RobotLoopingEvent); +} + +void Mars::launchMaze101Robot() { +	startExtraLongSequence(kMarsMaze101EastRobotApproach, kMarsMaze101EastRobotDeath, kExtraCompletedFlag, kFilterAllInput); +	scheduleEvent(kMaze101RobotLoopingTime, kMarsMovieScale, kMaze101RobotLoopingEvent); +} + +void Mars::launchMaze104Robot() { +	startExtraLongSequence(kMarsMaze104WestLoop, kMarsMaze104WestDeath, kExtraCompletedFlag, kFilterAllInput); +	scheduleEvent(kMaze104RobotLoopingTime, kMarsMovieScale, kMaze104RobotLoopingEvent); +} + +void Mars::launchMaze133Robot() { +	startExtraLongSequence(kMarsMaze133SouthApproach, kMarsMaze133SouthDeath, kExtraCompletedFlag, kFilterAllInput); +	scheduleEvent(kMaze133RobotLoopingTime, kMarsMovieScale, kMaze133RobotLoopingEvent); +} + +void Mars::launchMaze136Robot() { +	startExtraLongSequence(kMarsMaze136NorthApproach, kMarsMaze136NorthDeath, kExtraCompletedFlag, kFilterAllInput); +	scheduleEvent(kMaze136RobotLoopingTime, kMarsMovieScale, kMaze136RobotLoopingEvent); +} + +void Mars::launchMaze184Robot() { +	startExtraLongSequence(kMarsMaze184WestLoop, kMarsMaze184WestDeath, kExtraCompletedFlag, kFilterAllInput); +	scheduleEvent(kMaze184RobotLoopingTime, kMarsMovieScale, kMaze184RobotLoopingEvent); +} + +void Mars::timerExpired(const uint32 eventType) { +	switch (eventType) { +	case kMaze007RobotLoopingEvent: +	case kMaze015RobotLoopingEvent: +	case kMaze101RobotLoopingEvent: +	case kMaze104RobotLoopingEvent: +	case kMaze133RobotLoopingEvent: +	case kMaze136RobotLoopingEvent: +	case kMaze184RobotLoopingEvent: +		_interruptionFilter = kFilterNoInput; +		break; +	} +} + +void Mars::arriveAt(const RoomID room, const DirectionConstant direction) { +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kMars18, kNorth): +		if (GameState.getMarsPodAtUpperPlatform()) +			setCurrentAlternate(kAltMarsPodAtMars34); +		break; +	case MakeRoomView(kMars27, kEast): +	case MakeRoomView(kMars29, kEast): +		if (GameState.isTakenItemID(kMarsCard)) +			setCurrentAlternate(kAltMarsTookCard); +		else +			setCurrentAlternate(kAltMarsNormal); +		break; +	case MakeRoomView(kMars35, kEast): +	case MakeRoomView(kMars35, kWest): +		if (GameState.getMarsAirlockOpen()) +			setCurrentAlternate(kAltMars35AirlockWest); +		else +			setCurrentAlternate(kAltMars35AirlockEast); +		break; +	case MakeRoomView(kMars60, kEast): +	case MakeRoomView(kMars60, kWest): +		if (GameState.getMarsAirlockOpen()) +			setCurrentAlternate(kAltMars60AirlockEast); +		else +			setCurrentAlternate(kAltMars60AirlockWest); +		break; +	case MakeRoomView(kMars45, kNorth): +	case MakeRoomView(kMars45, kSouth): +	case MakeRoomView(kMars45, kEast): +	case MakeRoomView(kMars45, kWest): +		GameState.setMarsPodAtUpperPlatform(false); +		setCurrentAlternate(kAltMarsPodAtMars45); +		break; +	case MakeRoomView(kMars46, kNorth): +	case MakeRoomView(kMars46, kSouth): +	case MakeRoomView(kMars46, kEast): +	case MakeRoomView(kMars46, kWest): +	case MakeRoomView(kMars47, kNorth): +	case MakeRoomView(kMars47, kSouth): +	case MakeRoomView(kMars47, kEast): +	case MakeRoomView(kMars47, kWest): +		if (GameState.getMarsPodAtUpperPlatform()) +			setCurrentAlternate(kAltMarsNormal); +		else +			setCurrentAlternate(kAltMarsPodAtMars45); +		break; +	case MakeRoomView(kMars48, kNorth): +	case MakeRoomView(kMars48, kSouth): +	case MakeRoomView(kMars48, kEast): +	case MakeRoomView(kMars48, kWest): +	case MakeRoomView(kMars49, kNorth): +	case MakeRoomView(kMars49, kEast): +	case MakeRoomView(kMars49, kWest): +		if (GameState.isTakenItemID(kAirMask)) +			setCurrentAlternate(kAltMarsTookMask); +		else +			setCurrentAlternate(kAltMarsNormal); +		break; +	case MakeRoomView(kMars49, kSouth): +		if (GameState.getMarsMaskOnFiller()) +			setCurrentAlternate(kAltMarsMaskOnFiller); +		else if (GameState.isTakenItemID(kAirMask)) +			setCurrentAlternate(kAltMarsTookMask); +		else +			setCurrentAlternate(kAltMarsNormal); +		break; +	} + +	Neighborhood::arriveAt(room, direction); +	checkAirlockDoors(); +	setUpReactorEnergyDrain(); + +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kMars0A, kNorth): +		if (!GameState.getMarsSeenTimeStream()) +			startExtraLongSequence(kMarsArrivalFromTSA, kMars0AWatchShuttleDepart, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kMars07, kSouth): +	case MakeRoomView(kMars13, kNorth): +		if (!GameState.getMarsHeardCheckInMessage()) { +			playSpotSoundSync(kMarsCheckInRequiredIn, kMarsCheckInRequiredOut); +			GameState.setMarsHeardCheckInMessage(true); +		} +		break; +	case MakeRoomView(kMars44, kWest): +		if (GameState.getMarsReadyForShuttleTransport()) +			startUpFromFinishedSpaceChase(); +		else if (GameState.getMarsFinishedCanyonChase()) +			startUpFromSpaceChase(); +		else +			_neighborhoodNotification.setNotificationFlags(kTimeForCanyonChaseFlag, kTimeForCanyonChaseFlag); +		break; +	case MakeRoomView(kMars10, kNorth): +		if (!GameState.getMarsRobotThrownPlayer()) +			startExtraSequence(kRobotThrowsPlayer, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kMars11, kSouth): +	case MakeRoomView(kMars12, kSouth): +		setCurrentActivation(kActivationReadyForKiosk); +		break; +	case MakeRoomView(kMars15, kWest): +		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) { +			playSpotSoundSync(kMarsShuttle2DepartedIn, kMarsShuttle2DepartedOut); +			restoreStriding(kMars17, kWest, kAltMarsNormal); +			GameState.setMarsSecurityDown(true); +		} +		break; +	case MakeRoomView(kMars17, kNorth): +	case MakeRoomView(kMars17, kSouth): +	case MakeRoomView(kMars17, kEast): +	case MakeRoomView(kMars17, kWest): +		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) +			forceStridingStop(kMars17, kWest, kAltMarsNormal); + +		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) { +			startExtraSequence(kRobotOnWayToShuttle, kExtraCompletedFlag, kFilterNoInput); +			restoreStriding(kMars19, kWest, kAltMarsNormal); +			GameState.setMarsSawRobotLeave(true); +		} +		break; +	case MakeRoomView(kMars19, kNorth): +	case MakeRoomView(kMars19, kSouth): +	case MakeRoomView(kMars19, kWest): +		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) +			forceStridingStop(kMars19, kWest, kAltMarsNormal); + +		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) +			forceStridingStop(kMars17, kWest, kAltMarsNormal); +		break; +	case MakeRoomView(kMars19, kEast): +		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSawRobotLeave()) +			forceStridingStop(kMars19, kWest, kAltMarsNormal); + +		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) +			forceStridingStop(kMars17, kWest, kAltMarsNormal); +		break; +	case MakeRoomView(kMars32, kNorth): +		if (!GameState.getMarsPodAtUpperPlatform()) { +			playSpotSoundSync(kMarsPodArrivedUpperPlatformIn, kMarsPodArrivedUpperPlatformOut); +			GameState.setMarsPodAtUpperPlatform(true); +		} +		break; +	case MakeRoomView(kMars33North, kNorth): +		setCurrentActivation(kActivationTunnelMapReady); +		// Fall through... +	case MakeRoomView(kMars33, kSouth): +	case MakeRoomView(kMars33, kEast): +	case MakeRoomView(kMars33, kWest): +	case MakeRoomView(kMars32, kSouth): +	case MakeRoomView(kMars32, kEast): +	case MakeRoomView(kMars32, kWest): +		if (!GameState.getMarsPodAtUpperPlatform()) +			GameState.setMarsPodAtUpperPlatform(true); +		break; +	case MakeRoomView(kMars34, kNorth): +		startExtraSequence(kMars34NorthPodGreeting, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kMars34, kSouth): +	case MakeRoomView(kMars45, kNorth): +		setCurrentActivation(kActivateMarsPodClosed); +		break; +	case MakeRoomView(kMars35, kWest): +		if (GameState.getMarsThreadedMaze() && !GameState.getMarsSecurityDown()) +			forceStridingStop(kMars19, kWest, kAltMarsNormal); +		// Fall through... +	case MakeRoomView(kMars60, kEast): +		if (!GameState.getMarsAirlockOpen()) +			setCurrentActivation(kActivateReadyToPressurizeAirlock); +		break; +	case MakeRoomView(kMars35, kEast): +	case MakeRoomView(kMars60, kWest): +		if (GameState.getMarsAirlockOpen()) +			setCurrentActivation(kActivateReadyToPressurizeAirlock); +		break; +	case MakeRoomView(kMars39, kWest): +		if (GameState.getLastRoom() == kMarsMaze200) +			GameState.setMarsPodAtUpperPlatform(false); +		break; +	case MakeRoomView(kMars45, kSouth): +		// Set up maze doors here. +		// Doing it here makes sure that it will be the same if the player comes +		// back out of the maze and goes back in, but will vary if +		// the player comes back down to the maze a second time. +		GameState.setMarsMazeDoorPair1(_vm->getRandomBit()); +		GameState.setMarsMazeDoorPair2(_vm->getRandomBit()); +		GameState.setMarsMazeDoorPair3(_vm->getRandomBit()); +		GameState.setMarsArrivedBelow(true); +		break; +	case MakeRoomView(kMars48, kEast): +		if (!GameState.getMarsSeenRobotAtReactor()) { +			// Preload the looping sound... +			loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0, 0, 0); +			startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput); +		} else if (!GameState.getMarsAvoidedReactorRobot()) { +			loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); +			loopExtraSequence(kMars48RobotLoops); +			_utilityFuse.primeFuse(kMarsRobotPatienceLimit); +			_utilityFuse.setFunctionPtr(&robotTimerExpiredFunction, (void *)this); +			_utilityFuse.lightFuse(); +		} +		break; +	case MakeRoomView(kMars48, kSouth): +		if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) { +			loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); +			_utilityFuse.primeFuse(kMarsRobotPatienceLimit); +			_utilityFuse.setFunctionPtr(&robotTimerExpiredFunction, (void *)this); +			_utilityFuse.lightFuse(); +		} +		break; +	case MakeRoomView(kMars49, kSouth): +		if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) { +			playSpotSoundSync(kMarsRobotTakesTransportIn, kMarsRobotTakesTransportOut); +			playSpotSoundSync(kMarsPodDepartedLowerPlatformIn, kMarsPodDepartedLowerPlatformOut); +			GameState.setMarsAvoidedReactorRobot(true); +			GameState.setMarsPodAtUpperPlatform(true); +			GameState.getScoringAvoidedRobot(); +		} + +		if (GameState.isTakenItemID(kAirMask)) +			setCurrentActivation(kActivateHotSpotAlways); +		else if (GameState.getMarsMaskOnFiller()) +			setCurrentActivation(kActivateMaskOnFiller); +		else +			setCurrentActivation(kActivateMaskOnHolder); +		break; +	case MakeRoomView(kMars51, kWest): +	case MakeRoomView(kMars50, kWest): +	case MakeRoomView(kMars48, kWest): +		if (GameState.getShieldOn()) +			g_shield->setItemState(kShieldNormal); +		g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); +		_vm->resetEnergyDeathReason(); +		break; +	case MakeRoomView(kMars52, kNorth): +	case MakeRoomView(kMars52, kSouth): +	case MakeRoomView(kMars52, kEast): +	case MakeRoomView(kMars52, kWest): +	case MakeRoomView(kMars54, kNorth): +	case MakeRoomView(kMars54, kSouth): +	case MakeRoomView(kMars54, kEast): +	case MakeRoomView(kMars54, kWest): +	case MakeRoomView(kMars56, kNorth): +	case MakeRoomView(kMars56, kSouth): +	case MakeRoomView(kMars56, kWest): +	case MakeRoomView(kMars58, kNorth): +	case MakeRoomView(kMars58, kSouth): +	case MakeRoomView(kMars58, kEast): +	case MakeRoomView(kMars58, kWest): +		setCurrentActivation(kActivateReactorPlatformOut); +		break; +	case MakeRoomView(kMars56, kEast): +		if (GameState.getMarsLockBroken()) { +			setCurrentActivation(kActivateReactorAskOperation); +			_privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); +		} else if (GameState.getMarsLockFrozen()) { +			setCurrentActivation(kActivateReactorReadyForCrowBar); +			_privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); +			_utilityFuse.primeFuse(kLockFreezeTimeLmit); +			_utilityFuse.setFunctionPtr(&lockThawTimerExpiredFunction, (void *)this); +			_utilityFuse.lightFuse(); +		} else { +			setCurrentActivation(kActivateReactorPlatformOut); +		} +		break; +	case MakeRoomView(kMarsRobotShuttle, kEast): +		setCurrentActivation(kActivationRobotHeadClosed); +		break; +	case MakeRoomView(kMarsMaze007, kNorth): +		launchMaze007Robot(); +		break; +	case MakeRoomView(kMarsMaze015, kSouth): +		launchMaze015Robot(); +		break; +	case MakeRoomView(kMarsMaze101, kEast): +		launchMaze101Robot(); +		break; +	case MakeRoomView(kMarsMaze104, kWest): +		launchMaze104Robot(); +		break; +	case MakeRoomView(kMarsMaze133, kSouth): +		launchMaze133Robot(); +		break; +	case MakeRoomView(kMarsMaze136, kNorth): +		launchMaze136Robot(); +		break; +	case MakeRoomView(kMarsMaze184, kWest): +		launchMaze184Robot(); +		break; +	case MakeRoomView(kMarsMaze199, kSouth): +		GameState.setScoringThreadedMaze(); +		GameState.setMarsThreadedMaze(true); +		break; +	case MakeRoomView(kMarsDeathRoom, kNorth): +	case MakeRoomView(kMarsDeathRoom, kSouth): +	case MakeRoomView(kMarsDeathRoom, kEast): +	case MakeRoomView(kMarsDeathRoom, kWest): +		switch (GameState.getLastRoom()) { +		case kMars39: +			die(kDeathDidntLeaveBucket); +			break; +		case kMars46: +			die(kDeathRunOverByPod); +			break; +		} +		break; +	} + +	checkAirMask(); +} + +void Mars::shieldOn() { +	setUpReactorEnergyDrain(); +} + +void Mars::shieldOff() { +	setUpReactorEnergyDrain(); +} + +void Mars::turnTo(const DirectionConstant direction) { +	switch (MakeRoomView(GameState.getCurrentRoom(), direction)) { +	case MakeRoomView(kMars27, kNorth): +	case MakeRoomView(kMars27, kSouth): +	case MakeRoomView(kMars27, kEast): +	case MakeRoomView(kMars29, kNorth): +	case MakeRoomView(kMars29, kSouth): +	case MakeRoomView(kMars29, kEast): +		if (GameState.isTakenItemID(kMarsCard)) +			setCurrentAlternate(kAltMarsTookCard); +		break; +	case MakeRoomView(kMars35, kNorth): +	case MakeRoomView(kMars35, kSouth): +	case MakeRoomView(kMars60, kNorth): +	case MakeRoomView(kMars60, kSouth): +		if (getCurrentActivation() == kActivateAirlockPressurized) +			playSpotSoundSync(kMarsAirlockPressurizeIn, kMarsAirlockPressurizeOut); +		break; +	} + +	Neighborhood::turnTo(direction); + +	switch (MakeRoomView(GameState.getCurrentRoom(), direction)) { +	case MakeRoomView(kMars11, kSouth): +	case MakeRoomView(kMars12, kSouth): +		setCurrentActivation(kActivationReadyForKiosk); +		break; +	case MakeRoomView(kMars18, kNorth): +		if (GameState.getMarsPodAtUpperPlatform()) +			setCurrentAlternate(kAltMarsPodAtMars34); +		break; +	case MakeRoomView(kMars22, kSouth): +		if (!GameState.getMarsHeardCheckInMessage()) { +			playSpotSoundSync(kMarsCheckInRequiredIn, kMarsCheckInRequiredOut); +			GameState.setMarsHeardCheckInMessage(true); +		} +		break; +	case MakeRoomView(kMars34, kSouth): +	case MakeRoomView(kMars45, kNorth): +		setCurrentActivation(kActivateMarsPodClosed); +		break; +	case MakeRoomView(kMars34, kNorth): +		startExtraSequence(kMars34NorthPodGreeting, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kMars35, kEast): +	case MakeRoomView(kMars60, kWest): +		if (GameState.getMarsAirlockOpen()) +			setCurrentActivation(kActivateReadyToPressurizeAirlock); +		break; +	case MakeRoomView(kMars60, kEast): +		if (!GameState.getMarsAirlockOpen()) +			setCurrentActivation(kActivateReadyToPressurizeAirlock); +		break; +	case MakeRoomView(kMars35, kWest): +		if (!GameState.getMarsAirlockOpen()) +			setCurrentActivation(kActivateReadyToPressurizeAirlock); +		 +		// Do this here because this will be called after spinning the airlock after +		// going through the gear room. +		if (GameState.getMarsThreadedMaze()) +			GameState.setScoringThreadedGearRoom(); +		break; +	case MakeRoomView(kMars48, kNorth): +		if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) +			die(kDeathDidntGetOutOfWay); +		break; +	case MakeRoomView(kMars48, kEast): +		if (!GameState.getMarsSeenRobotAtReactor()) { +			// Preload the looping sound... +			loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0, 0, 0); +			startExtraSequence(kMars48RobotApproaches, kExtraCompletedFlag, kFilterNoInput); +		} else if (!GameState.getMarsAvoidedReactorRobot()) { +			loopExtraSequence(kMars48RobotLoops); +		} else if (GameState.isTakenItemID(kAirMask)) { +			setCurrentAlternate(kAltMarsTookMask); +		} else { +			setCurrentAlternate(kAltMarsNormal); +		} +		break; +	case MakeRoomView(kMars48, kWest): +		if (GameState.getMarsSeenRobotAtReactor() && !GameState.getMarsAvoidedReactorRobot()) +			die(kDeathDidntGetOutOfWay); +		else if (GameState.isTakenItemID(kAirMask)) +			setCurrentAlternate(kAltMarsTookMask); +		else +			setCurrentAlternate(kAltMarsNormal); +		break; +	case MakeRoomView(kMars49, kSouth): +		if (GameState.isTakenItemID(kAirMask)) +			setCurrentActivation(kActivateHotSpotAlways); +		else +			setCurrentActivation(kActivateMaskOnHolder); +		break; +	case MakeRoomView(kMars52, kNorth): +	case MakeRoomView(kMars52, kSouth): +	case MakeRoomView(kMars52, kEast): +	case MakeRoomView(kMars52, kWest): +	case MakeRoomView(kMars54, kNorth): +	case MakeRoomView(kMars54, kSouth): +	case MakeRoomView(kMars54, kEast): +	case MakeRoomView(kMars54, kWest): +	case MakeRoomView(kMars56, kNorth): +	case MakeRoomView(kMars56, kSouth): +	case MakeRoomView(kMars56, kEast): +	case MakeRoomView(kMars56, kWest): +	case MakeRoomView(kMars58, kNorth): +	case MakeRoomView(kMars58, kSouth): +	case MakeRoomView(kMars58, kEast): +	case MakeRoomView(kMars58, kWest): +		setCurrentActivation(kActivateReactorPlatformOut); +		break; +	case MakeRoomView(kMarsMaze007, kNorth): +		launchMaze007Robot(); +		break; +	case MakeRoomView(kMarsMaze015, kSouth): +		launchMaze015Robot(); +		break; +	case MakeRoomView(kMarsMaze101, kEast): +		launchMaze101Robot(); +		break; +	case MakeRoomView(kMarsMaze104, kWest): +		launchMaze104Robot(); +		break; +	case MakeRoomView(kMarsMaze133, kSouth): +		launchMaze133Robot(); +		break; +	case MakeRoomView(kMarsMaze136, kNorth): +		launchMaze136Robot(); +		break; +	case MakeRoomView(kMarsMaze184, kWest): +		launchMaze184Robot(); +		break; +	} +} + +void Mars::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) { +	switch (hotspot->getObjectID()) { +	case kMars57RedMoveSpotID: +	case kMars57YellowMoveSpotID: +	case kMars57GreenMoveSpotID: +		if (!_choiceHighlight.choiceHighlighted(hotspot->getObjectID() - kMars57RedMoveSpotID)) +			hotspot->setActive(); +		break; +	case kMars57BlueMoveSpotID: +		if (_reactorStage >= 2 && !_choiceHighlight.choiceHighlighted(3)) +			hotspot->setActive(); +		break; +	case kMars57PurpleMoveSpotID: +		if (_reactorStage == 3 && !_choiceHighlight.choiceHighlighted(4)) +			hotspot->setActive(); +		break; +	default: +		Neighborhood::activateOneHotspot(entry, hotspot); +		break; +	} +} + +void Mars::activateHotspots() { +	InventoryItem *item; + +	Neighborhood::activateHotspots(); + +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kMars48, kEast): +		if ((_navMovie.getFlags() & kLoopTimeBase) != 0 && _vm->getDragType() == kDragInventoryUse) +			g_allHotspots.activateOneHotspot(kAttackRobotHotSpotID); +		break; +	case MakeRoomView(kMars56, kEast): +		switch (getCurrentActivation()) { +		case kActivateReactorReadyForNitrogen: +			item = (InventoryItem *)g_allItems.findItemByID(kNitrogenCanister); +			if (item->getItemState() != kNitrogenFull) +				g_allHotspots.deactivateOneHotspot(kMars57DropNitrogenSpotID); +			// Fall through... +		case kActivateReactorReadyForCrowBar: +			g_allHotspots.activateOneHotspot(kMars57CantOpenPanelSpotID); +			break; +		} +		break; +	case MakeRoomView(kMarsRobotShuttle, kEast): +		if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag)) +			g_allHotspots.deactivateOneHotspot(kRobotShuttleMapChipSpotID); +		else +			g_allHotspots.activateOneHotspot(kRobotShuttleMapChipSpotID); + +		if (_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) +			g_allHotspots.deactivateOneHotspot(kRobotShuttleOpticalChipSpotID); +		else +			g_allHotspots.activateOneHotspot(kRobotShuttleOpticalChipSpotID); + +		if (_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag)) +			g_allHotspots.deactivateOneHotspot(kRobotShuttleShieldChipSpotID); +		else +			g_allHotspots.activateOneHotspot(kRobotShuttleShieldChipSpotID); +		break; +	default: +		if (_privateFlags.getFlag(kMarsPrivateInSpaceChaseFlag)) { +			if (GameState.getMarsReadyForShuttleTransport()) { +				_shuttleTransportSpot.setActive(); +			} else { +				_energyChoiceSpot.setActive(); +				_gravitonChoiceSpot.setActive(); +				_tractorChoiceSpot.setActive(); +				if (_weaponSelection != kNoWeapon) +					_shuttleViewSpot.setActive(); +			} +		} +		break; +	} +} + +void Mars::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { +	switch (clickedSpot->getObjectID()) { +	case kMars11NorthKioskSpotID: +	case kMars12NorthKioskSpotID: +		playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut); +		Neighborhood::clickInHotspot(input, clickedSpot); +		break; +	case kMars11NorthKioskSightsSpotID: +	case kMars12NorthKioskSightsSpotID: +		playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut); +		if (!startExtraSequenceSync(kMarsSightsInfo, kFilterAllInput)) +			showExtraView(kMarsInfoKioskIntro); +		break; +	case kMars11NorthKioskColonySpotID: +	case kMars12NorthKioskColonySpotID: +		playSpotSoundSync(kMarsKioskBeepIn, kMarsKioskBeepOut); +		if (!startExtraSequenceSync(kMarsColonyInfo, kFilterAllInput)) +			showExtraView(kMarsInfoKioskIntro); +		break; +	case kMars33NorthMonitorSpotID: +		switch (_lastExtra) { +		case kMars33SlideShow1: +			startExtraSequence(kMars33SlideShow2, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kMars33SlideShow2: +			startExtraSequence(kMars33SlideShow3, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kMars33SlideShow3: +			startExtraSequence(kMars33SlideShow4, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kMars33SlideShow4: +			// Should never happen... +		default: +			startExtraSequence(kMars33SlideShow1, kExtraCompletedFlag, kFilterNoInput); +			break; +		} +		break; +	case kMars34SouthOpenStorageSpotID: +		if (GameState.isTakenItemID(kCrowbar)) +			startExtraSequence(kMars34SpotOpenNoBar, kExtraCompletedFlag, kFilterNoInput); +		else +			startExtraSequence(kMars34SpotOpenWithBar, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kMars34SouthCloseStorageSpotID: +		if (GameState.isTakenItemID(kCrowbar)) +			startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); +		else +			startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kMars35WestPressurizeSpotID: +	case kMars35EastPressurizeSpotID: +	case kMars60WestPressurizeSpotID: +	case kMars60EastPressurizeSpotID: +		playSpotSoundSync(kMarsAirlockButtonBeepIn, kMarsAirlockButtonBeepOut); +		playSpotSoundSync(kMarsAirlockPressurizeIn, kMarsAirlockPressurizeOut); +		setCurrentActivation(kActivateAirlockPressurized); +		break; +	case kMars45NorthOpenStorageSpotID: +		if (GameState.isTakenItemID(kCrowbar)) +			startExtraSequence(kMars45SpotOpenNoBar, kExtraCompletedFlag, kFilterNoInput); +		else +			startExtraSequence(kMars45SpotOpenWithBar, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kMars45NorthCloseStorageSpotID: +		if (GameState.isTakenItemID(kCrowbar)) +			startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); +		else +			startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kMars56ExtractSpotID: +		if (GameState.isTakenItemID(kCardBomb)) { +			startExtraSequence(kMars56ExtendNoBomb, kExtraCompletedFlag, kFilterNoInput); +			setCurrentActivation(kActivateReactorPlatformIn); +		} else { +			startExtraSequence(kMars56ExtendWithBomb, kExtraCompletedFlag, kFilterNoInput); +			setCurrentActivation(kActivateReactorAskLowerScreen); +		} +		break; +	case kMars57UndoMoveSpotID: +		playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); +		doUndoOneGuess(); +		break; +	case kMars57RedMoveSpotID: +		playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); +		doReactorGuess(0); +		break; +	case kMars57YellowMoveSpotID: +		playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); +		doReactorGuess(1); +		break; +	case kMars57GreenMoveSpotID: +		playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); +		doReactorGuess(2); +		break; +	case kMars57BlueMoveSpotID: +		playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); +		doReactorGuess(3); +		break; +	case kMars57PurpleMoveSpotID: +		playSpotSoundSync(kMarsColorMatchingButtonBeepIn, kMarsColorMatchingButtonBeepOut); +		doReactorGuess(4); +		break; +	case kShuttleEnergySpotID: +	case kShuttleGravitonSpotID: +	case kShuttleTractorSpotID: +	case kShuttleViewSpotID: +	case kShuttleTransportSpotID: +		spaceChaseClick(input, clickedSpot->getObjectID()); +		break; +	default: +		Neighborhood::clickInHotspot(input, clickedSpot); +		break; +	} +} + +InputBits Mars::getInputFilter() { +	InputBits result = Neighborhood::getInputFilter(); + +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kMars49, kSouth): +		if (GameState.getMarsMaskOnFiller()) +			// Can't move when mask is on filler. +			result &= ~kFilterAllDirections; +		break; +	case MakeRoomView(kMars52, kNorth): +	case MakeRoomView(kMars52, kSouth): +	case MakeRoomView(kMars52, kEast): +	case MakeRoomView(kMars52, kWest): +	case MakeRoomView(kMars54, kNorth): +	case MakeRoomView(kMars54, kSouth): +	case MakeRoomView(kMars54, kEast): +	case MakeRoomView(kMars54, kWest): +	case MakeRoomView(kMars56, kNorth): +	case MakeRoomView(kMars56, kSouth): +	case MakeRoomView(kMars56, kEast): +	case MakeRoomView(kMars56, kWest): +	case MakeRoomView(kMars58, kNorth): +	case MakeRoomView(kMars58, kSouth): +	case MakeRoomView(kMars58, kEast): +	case MakeRoomView(kMars58, kWest): +		if (_privateFlags.getFlag(kMarsPrivatePlatformZoomedInFlag)) +			// Can't move when platform is extended. +			result &= ~kFilterAllDirections; +		break; +	case MakeRoomView(kMars44, kWest): +		if (_canyonChaseMovie.isMovieValid() && _canyonChaseMovie.isRunning()) +			result &= ~kFilterAllDirections; +		break; +	} + +	return result; +} + +// Only called when trying to pick up an item and the player can't (because +// the inventory is too full or because the player lets go of the item before +// dropping it into the inventory). +Hotspot *Mars::getItemScreenSpot(Item *item, DisplayElement *element) { +	HotSpotID destSpotID; + +	switch (item->getObjectID()) { +	case kCardBomb: +		destSpotID = kMars57GrabBombSpotID; +		break; +	case kMarsCard: +		destSpotID = kMars31SouthCardSpotID; +		break; +	case kAirMask: +		if (GameState.getMarsMaskOnFiller()) +			destSpotID = kMars49AirFillingDropSpotID; +		else +			destSpotID = kMars49AirMaskSpotID; +		break; +	case kCrowbar: +		if (GameState.getCurrentRoom() == kMars34) +			destSpotID = kMars34SouthCrowbarSpotID; +		else +			destSpotID = kMars45NorthCrowbarSpotID; +		break; +	case kMapBiochip: +		destSpotID = kRobotShuttleMapChipSpotID; +		break; +	case kOpticalBiochip: +		destSpotID = kRobotShuttleOpticalChipSpotID; +		break; +	case kShieldBiochip: +		destSpotID = kRobotShuttleShieldChipSpotID; +		break; +	default: +		destSpotID = kNoHotSpotID; +		break; +	} + +	if (destSpotID == kNoHotSpotID) +		return Neighborhood::getItemScreenSpot(item, element); + +	return g_allHotspots.findHotspotByID(destSpotID); +} + +void Mars::takeItemFromRoom(Item *item) { +	switch (item->getObjectID()) { +	case kAirMask: +		setCurrentAlternate(kAltMarsTookMask); +		break; +	case kCardBomb: +		_privateFlags.setFlag(kMarsPrivateDraggingBombFlag, true); +		break; +	case kMapBiochip: +		_privateFlags.setFlag(kMarsPrivateGotMapChipFlag, true); +		break; +	case kShieldBiochip: +		_privateFlags.setFlag(kMarsPrivateGotShieldChipFlag, true); +		break; +	case kOpticalBiochip: +		_privateFlags.setFlag(kMarsPrivateGotOpticalChipFlag, true); +		break; +	} + +	Neighborhood::takeItemFromRoom(item); +} + +void Mars::pickedUpItem(Item *item) { +	switch (item->getObjectID()) { +	case kAirMask: +		setCurrentActivation(kActivateHotSpotAlways); +		if (!GameState.getScoringGotOxygenMask()) { +			g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM48SB", false, kWarningInterruption); +			GameState.setScoringGotOxygenMask(); +		} +		break; +	case kCrowbar: +		GameState.setScoringGotCrowBar(); +		g_AIArea->checkMiddleArea(); +		break; +	case kMarsCard: +		GameState.setScoringGotMarsCard(); +		g_AIArea->checkMiddleArea(); +		break; +	case kCardBomb: +		GameState.setScoringGotCardBomb(); +		if (GameState.getMarsLockBroken()) { +			startExtraSequence(kMars57BackToNormal, kExtraCompletedFlag, kFilterNoInput); +			GameState.setMarsLockBroken(false); +		} + +		_privateFlags.setFlag(kMarsPrivateDraggingBombFlag, false); +		break; +	case kMapBiochip: +		if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) && +				_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) && +				_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) { +			GameState.setMarsFinished(true); +			GameState.setScoringMarsGandhi(); +			startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kShieldBiochip: +		if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) && +				_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) && +				_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) { +			GameState.setMarsFinished(true); +			GameState.setScoringMarsGandhi(); +			startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kOpticalBiochip: +		g_opticalChip->addAries(); +		GameState.setScoringGotMarsOpMemChip(); +			 +		if (_privateFlags.getFlag(kMarsPrivateGotMapChipFlag) && +				_privateFlags.getFlag(kMarsPrivateGotShieldChipFlag) && +				_privateFlags.getFlag(kMarsPrivateGotOpticalChipFlag)) { +			GameState.setMarsFinished(true); +			GameState.setScoringMarsGandhi(); +			startExtraSequence(kMarsRobotHeadClose, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	} +} + +void Mars::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { +	if (dropSpot->getObjectID() == kAttackRobotHotSpotID) { +		_attackingItem = (InventoryItem *)item; +		startExtraSequence(kMars48RobotDefends, kExtraCompletedFlag, kFilterNoInput); +		loadLoopSound2(""); +	} else { +		switch (item->getObjectID()) { +		case kMarsCard: +			Neighborhood::dropItemIntoRoom(item, dropSpot); +			if (dropSpot && dropSpot->getObjectID() == kMars34NorthCardDropSpotID) +				startExtraSequence(kMarsTurnOnPod, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kNitrogenCanister: +			Neighborhood::dropItemIntoRoom(item, dropSpot); +			if (dropSpot && dropSpot->getObjectID() == kMars57DropNitrogenSpotID) +				startExtraSequence(kMars57FreezeLock, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kCrowbar: +			_utilityFuse.stopFuse(); +			Neighborhood::dropItemIntoRoom(item, dropSpot); +			if (dropSpot && dropSpot->getObjectID() == kMars57DropCrowBarSpotID) +				startExtraSequence(kMars57BreakLock, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kAirMask: +			if (dropSpot) { +				if (dropSpot->getObjectID() == kMars49AirFillingDropSpotID) { +					if (!GameState.getMarsMaskOnFiller()) { +						Neighborhood::dropItemIntoRoom(item, dropSpot); +						startExtraSequence(kMars49SouthViewMaskFilling, kExtraCompletedFlag, kFilterNoInput); +					} else { +						setCurrentActivation(kActivateMaskOnFiller); +						setCurrentAlternate(kAltMarsMaskOnFiller); +						Neighborhood::dropItemIntoRoom(item, dropSpot); +					} +				} else if (dropSpot->getObjectID() == kMars49AirMaskSpotID) { +					setCurrentAlternate(kAltMarsNormal); +					setCurrentActivation(kActivateMaskOnHolder); +					Neighborhood::dropItemIntoRoom(item, dropSpot); +				} +			} +			break; +		case kCardBomb: +			_privateFlags.setFlag(kMarsPrivateDraggingBombFlag, false); +			Neighborhood::dropItemIntoRoom(item, dropSpot); +			break; +		case kMapBiochip: +			_privateFlags.setFlag(kMarsPrivateGotMapChipFlag, false); +			Neighborhood::dropItemIntoRoom(item, dropSpot); +			break; +		case kShieldBiochip: +			_privateFlags.setFlag(kMarsPrivateGotShieldChipFlag, false); +			Neighborhood::dropItemIntoRoom(item, dropSpot); +			break; +		case kOpticalBiochip: +			_privateFlags.setFlag(kMarsPrivateGotOpticalChipFlag, false); +			Neighborhood::dropItemIntoRoom(item, dropSpot); +			break; +		default: +			Neighborhood::dropItemIntoRoom(item, dropSpot); +			break; +		} +	} +} + +void Mars::robotTiredOfWaiting() { +	if (GameState.getCurrentRoomAndView() == MakeRoomView(kMars48, kEast)) { +		if (_attackingItem) { +			startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput); +			loadLoopSound2(""); +		} else { +			_privateFlags.setFlag(kMarsPrivateRobotTiredOfWaitingFlag, true); +		} +	} else { +		die(kDeathDidntGetOutOfWay); +	} +} + +void Mars::turnLeft() { +	if (isEventTimerRunning()) +		cancelEvent(); + +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kMars34, kSouth): +		if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { +			_privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, true); +			if (GameState.isTakenItemID(kCrowbar)) +				startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); +		} else { +			Neighborhood::turnLeft(); +		} +		break; +	case MakeRoomView(kMars45, kNorth): +		if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { +			_privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, true); +			if (GameState.isTakenItemID(kCrowbar)) +				startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); +		} else { +			Neighborhood::turnLeft(); +		} +		break; +	default: +		Neighborhood::turnLeft(); +		break; +	} +} + +void Mars::turnRight() { +	if (isEventTimerRunning()) +		cancelEvent(); + +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kMars34, kSouth): +		if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { +			_privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, true); +			if (GameState.isTakenItemID(kCrowbar)) +				startExtraSequence(kMars34SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kMars34SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); +		} else { +			Neighborhood::turnRight(); +		} +		break; +	case MakeRoomView(kMars45, kNorth): +		if (_privateFlags.getFlag(kMarsPrivatePodStorageOpenFlag)) { +			_privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, true); +			if (GameState.isTakenItemID(kCrowbar)) +				startExtraSequence(kMars45SpotCloseNoBar, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kMars45SpotCloseWithBar, kExtraCompletedFlag, kFilterNoInput); +		} else { +			Neighborhood::turnRight(); +		} +		break; +	default: +		Neighborhood::turnRight(); +		break; +	} +} + +void Mars::receiveNotification(Notification *notification, const NotificationFlags flag) { +	InventoryItem *item; +	 +	Neighborhood::receiveNotification(notification, flag); + +	if ((flag & kExtraCompletedFlag) != 0) { +		_interruptionFilter = kFilterAllInput; + +		switch (_lastExtra) { +		case kMarsArrivalFromTSA: +			GameState.setMarsSeenTimeStream(true); +			loadAmbientLoops(); +			playSpotSoundSync(kMarsShuttle1DepartedIn, kMarsShuttle1DepartedOut); +			makeContinuePoint(); +			break; +		case kRobotThrowsPlayer: +			GameState.setMarsRobotThrownPlayer(true); +			GameState.setScoringThrownByRobot(); +			restoreStriding(kMars08, kNorth, kAltMarsNormal); +			arriveAt(kMars08, kNorth); +			if (!GameState.getMarsHeardUpperPodMessage()) { +				playSpotSoundSync(kMarsPodDepartedUpperPlatformIn, +											kMarsPodDepartedUpperPlatformOut); +				GameState.setMarsHeardUpperPodMessage(true); +			} +			break; +		case kMarsInfoKioskIntro: +			GameState.setScoringSawMarsKiosk(); +			setCurrentActivation(kActivationKioskChoice); +			break; +		case kMars33SlideShow4: +			GameState.setScoringSawTransportMap(); +			setCurrentActivation(kActivateHotSpotAlways); +			break; +		case kMars34SpotOpenNoBar: +		case kMars34SpotOpenWithBar: +		case kMars45SpotOpenNoBar: +		case kMars45SpotOpenWithBar: +			_privateFlags.setFlag(kMarsPrivatePodStorageOpenFlag, true); +			setCurrentActivation(kActivateMarsPodOpen); +			break; +		case kMars34SpotCloseNoBar: +		case kMars34SpotCloseWithBar: +		case kMars45SpotCloseNoBar: +		case kMars45SpotCloseWithBar: +			_privateFlags.setFlag(kMarsPrivatePodStorageOpenFlag, false); +			setCurrentActivation(kActivateMarsPodClosed); +			if (_privateFlags.getFlag(kMarsPrivatePodTurnLeftFlag)) { +				_privateFlags.setFlag(kMarsPrivatePodTurnLeftFlag, false); +				turnLeft(); +			} else if (_privateFlags.getFlag(kMarsPrivatePodTurnRightFlag)) { +				_privateFlags.setFlag(kMarsPrivatePodTurnRightFlag, false); +				turnRight(); +			} +			break; +		case kMarsTurnOnPod: +			item = (InventoryItem *)g_allItems.findItemByID(kMarsCard); +			_vm->addItemToInventory(item); +			GameState.setScoringTurnedOnTransport(); +			loadLoopSound1(""); +			loadLoopSound2(""); +			startExtraSequence(kMarsTakePodToMars45, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kMarsTakePodToMars45: +			arriveAt(kMars45, kSouth); +			break; +		case kMars35WestSpinAirlockToEast: +			GameState.setMarsAirlockOpen(false); +			setCurrentAlternate(kAltMars35AirlockEast); +			turnTo(kWest); +			setCurrentActivation(kActivateReadyToPressurizeAirlock); +			g_airMask->airQualityChanged(); +			checkAirMask(); +			loadAmbientLoops(); +			break; +		case kMars35EastSpinAirlockToWest: +			GameState.setMarsAirlockOpen(true); +			setCurrentAlternate(kAltMars35AirlockWest); +			turnTo(kEast); +			setCurrentActivation(kActivateReadyToPressurizeAirlock); +			g_airMask->airQualityChanged(); +			checkAirMask(); +			loadAmbientLoops(); +			break; +		case kMars48RobotApproaches: +			loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); +			GameState.setMarsSeenRobotAtReactor(true); +			loopExtraSequence(kMars48RobotLoops); +			_utilityFuse.primeFuse(kMarsRobotPatienceLimit); +			_utilityFuse.setFunctionPtr(&robotTimerExpiredFunction, (void *)this); +			_utilityFuse.lightFuse(); +			break; +		case kMars48RobotDefends: +			_vm->addItemToInventory(_attackingItem); +			_attackingItem = 0; +			if (_privateFlags.getFlag(kMarsPrivateRobotTiredOfWaitingFlag)) { +				startExtraSequence(kMars48RobotKillsPlayer, kExtraCompletedFlag, kFilterNoInput); +				loadLoopSound2("", 0x100, 0, 0); +			} else { +				loadLoopSound2("Sounds/Mars/Robot Loop.aiff", 0x100, 0, 0); +				loopExtraSequence(kMars48RobotLoops, kExtraCompletedFlag); +			} +			break; +		case kMars48RobotKillsPlayer: +			loadLoopSound2(""); +			die(kDeathDidntGetOutOfWay); +			break; +		case kMars49SouthViewMaskFilling: +			setCurrentActivation(kActivateMaskOnFiller); +			setCurrentAlternate(kAltMarsMaskOnFiller); +			GameState.setMarsMaskOnFiller(true); +			break; +		case kMars58SpinLeft: +		case kMars54SpinRight: +			GameState.setScoringActivatedPlatform(); +			arriveAt(kMars52, kEast); +			break; +		case kMars52SpinLeft: +		case kMars56SpinRight: +			GameState.setScoringActivatedPlatform(); +			arriveAt(kMars54, kEast); +			break; +		case kMars54SpinLeft: +		case kMars58SpinRight: +			GameState.setScoringActivatedPlatform(); +			arriveAt(kMars56, kEast); +			break; +		case kMars56SpinLeft: +		case kMars52SpinRight: +			GameState.setScoringActivatedPlatform(); +			arriveAt(kMars58, kEast); +			break; +		case kMars52Extend: +		case kMars54Extend: +		case kMars56ExtendNoBomb: +		case kMars58Extend: +			GameState.setScoringActivatedPlatform(); +			setCurrentActivation(kActivateReactorPlatformIn); +			_privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); +			break; +		case kMars53Retract: +		case kMars55Retract: +		case kMars57RetractWithBomb: +		case kMars57RetractNoBomb: +		case kMars59Retract: +			GameState.setScoringActivatedPlatform(); +			setCurrentActivation(kActivateReactorPlatformOut); +			_privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, false); +			break; +		case kMars56ExtendWithBomb: +			playSpotSoundSync(kMustBeUnlockedIn, kMustBeUnlockedOut); +			GameState.setScoringActivatedPlatform(); +			_privateFlags.setFlag(kMarsPrivatePlatformZoomedInFlag, true); +			break; +		case kMars57CantOpenPanel: +			GameState.setScoringActivatedPlatform(); +			setCurrentActivation(kActivateReactorAskLowerScreen); +			break; +		case kMars57LowerScreenClosed: +		case kMars57ThawLock: +			setCurrentActivation(kActivateReactorReadyForNitrogen); +			GameState.setMarsLockFrozen(false); +			break; +		case kMars57FreezeLock: +			item = (InventoryItem *)g_allItems.findItemByID(kNitrogenCanister); +			item->setItemState(kNitrogenEmpty); +			_vm->addItemToInventory(item); +			setCurrentActivation(kActivateReactorReadyForCrowBar); +			GameState.setScoringUsedLiquidNitrogen(); +			GameState.setMarsLockFrozen(true); +			showExtraView(kMars57LockFrozenView); +			_utilityFuse.primeFuse(kLockFreezeTimeLmit); +			_utilityFuse.setFunctionPtr(&lockThawTimerExpiredFunction, (void *)this); +			_utilityFuse.lightFuse(); +			break; +		case kMars57BreakLock: +			item = (InventoryItem *)g_allItems.findItemByID(kCrowbar); +			_vm->addItemToInventory(item); +			GameState.setScoringUsedCrowBar(); +			GameState.setMarsLockBroken(true); +			GameState.setMarsLockFrozen(false); +			startExtraLongSequence(kMars57OpenPanel, kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kMars57OpenPanel: +		case kMars57OpenPanelChoices: +			setCurrentActivation(kActivateReactorAskOperation); +			break; +		case kMars57ShieldEvaluation: +		case kMars57MeasureOutput: +			setCurrentActivation(kActivateReactorRanEvaluation); +			loopExtraSequence(kMars57ShieldOkayLoop); +			break; +		case kMars57RunDiagnostics: +			setCurrentActivation(kActivateReactorRanDiagnostics); +			GameState.setScoringFoundCardBomb(); +			break; +		case kMars57BombExplodes: +		case kMars57BombExplodesInGame: +			die(kDeathDidntDisarmMarsBomb); +			break; +		case kMars57BombAnalysis: +			setCurrentActivation(kActivateReactorAnalyzed); +			break; +		case kMars57DontLink: +			startExtraSequence(kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kMars57CircuitLink: +			setCurrentActivation(kActivateReactorInstructions); +			break; +		case kMars57GameLevel1: +			setUpReactorLevel1(); +			break; +		case kMars57GameLevel2: +		case kMars57GameLevel3: +			setUpNextReactorLevel(); +			break; +		case kMars57GameSolved: +			setCurrentActivation(kActivateReactorBombSafe); +			break; +		case kMars57ExposeBomb: +			setCurrentActivation(kActivateReactorBombExposed); +			_privateFlags.setFlag(kMarsPrivateBombExposedFlag, true); +			break; +		case kMars57BackToNormal: +			setCurrentActivation(kActivateReactorPlatformIn); +			_privateFlags.setFlag(kMarsPrivateBombExposedFlag, false); +			g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XM51SW", false, kWarningInterruption); +			break; +		case kMars60WestSpinAirlockToEast: +			GameState.setMarsAirlockOpen(true); +			setCurrentAlternate(kAltMars60AirlockEast); +			turnTo(kWest); +			setCurrentActivation(kActivateReadyToPressurizeAirlock); +			g_airMask->airQualityChanged(); +			checkAirMask(); +			loadAmbientLoops(); +			break; +		case kMars60EastSpinAirlockToWest: +			GameState.setMarsAirlockOpen(false); +			setCurrentAlternate(kAltMars60AirlockWest); +			turnTo(kEast); +			setCurrentActivation(kActivateReadyToPressurizeAirlock); +			g_airMask->airQualityChanged(); +			checkAirMask(); +			loadAmbientLoops(); +			break; +		case kMarsRobotHeadOpen: +			setCurrentActivation(kActivationRobotHeadOpen); +			break; +		case kMarsRobotHeadClose: +			recallToTSASuccess(); +			break; +		case kMarsMaze007RobotApproach: +		case kMarsMaze015SouthRobotApproach: +		case kMarsMaze101EastRobotApproach: +		case kMarsMaze104WestLoop: +		case kMarsMaze133SouthApproach: +		case kMarsMaze136NorthApproach: +		case kMarsMaze184WestLoop: +			die(kDeathGroundByMazebot); +			break; +		} +	} else if ((flag & kTimeForCanyonChaseFlag) != 0) { +		doCanyonChase(); +	} else if ((flag & kExplosionFinishedFlag) != 0) { +		_explosions.stop(); +		_explosions.hide(); +		if (g_robotShip->isDead()) { +			GameState.setMarsFinished(true); +			_centerShuttleMovie.hide(); +			_upperRightShuttleMovie.show(); +			_upperRightShuttleMovie.setTime(kShuttleUpperRightTargetDestroyedTime); +			_upperRightShuttleMovie.redrawMovieWorld(); +			_rightDamageShuttleMovie.hide(); +			playMovieSegment(&_rightShuttleMovie, kShuttleRightDestroyedStart, kShuttleRightDestroyedStop); +			playSpotSoundSync(kShuttleDestroyedIn, kShuttleDestroyedOut); +			throwAwayMarsShuttle(); +			reinstateMonocleInterface(); +			recallToTSASuccess(); +		} +	} else if ((flag & kTimeToTransportFlag) != 0) { +		transportToRobotShip(); +	} + +	if (g_AIArea) +		g_AIArea->checkMiddleArea(); +} + +void Mars::spotCompleted() { +	Neighborhood::spotCompleted(); + +	if (GameState.getCurrentRoom() == kMarsRobotShuttle) +		g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Mars/XN59WD", false, kWarningInterruption); +} + +void Mars::doCanyonChase() {	 +	GameState.setScoringEnteredShuttle(); +	setNextHandler(_vm); +	throwAwayInterface(); + +	_vm->_cursor->hide(); +	 +	// Open the spot sounds movie again... +	_spotSounds.initFromQuickTime(getSoundSpotsName()); +	_spotSounds.setVolume(_vm->getSoundFXLevel()); + +	Video::VideoDecoder *video = new Video::QuickTimeDecoder(); +	if (!video->loadFile("Images/Mars/M44ESA.movie")) +		error("Could not load interface->shuttle transition video"); + +	while (!_vm->shouldQuit() && !video->endOfVideo()) { +		if (video->needsUpdate()) { +			const Graphics::Surface *frame = video->decodeNextFrame(); + +			if (frame) +				_vm->drawScaledFrame(frame, 0, 0); +		} + +		Common::Event event; +		while (g_system->getEventManager()->pollEvent(event)) +			; + +		g_system->delayMillis(10); +	} + +	delete video; + +	if (_vm->shouldQuit()) +		return; + +	initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left, +							kShuttle1Top, true); +	initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left, +							kShuttle2Top, true); +	initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left, +							kShuttle3Top, true); +	initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left, +							kShuttle4Top, true); + +	initOneMovie(&_canyonChaseMovie, "Images/Mars/Canyon.movie", +						kShuttleMonitorOrder, kShuttleWindowLeft, kShuttleWindowTop, true); +	_canyonChaseMovie.setVolume(_vm->getSoundFXLevel()); + +	loadLoopSound1("Sounds/Mars/Inside Cockpit.22K.8.AIFF"); + +	// Swing shuttle around... +	playMovieSegment(&_canyonChaseMovie, kShuttleSwingStart, kShuttleSwingStop); + +	initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie", +			kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false); +	 +	initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie", +			kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false); +	 +	initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder, +			kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false); +	 +	initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder, +			kShuttleLowerRightLeft, kShuttleLowerRightTop, false); +	 +	initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie", +			kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false); +	 +	initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder, +			kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false); +	 +	initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder, +			kShuttleUpperRightLeft, kShuttleUpperRightTop, false); +	 +	initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie", +			kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false); +	 +	initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie", +			kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false); +	 +	_centerShuttleMovie.show(); +	_centerShuttleMovie.setTime(kShuttleCenterBoardingTime); +	playSpotSoundSync(kShuttleCockpitIn, kShuttleCockpitOut); +	 +	_centerShuttleMovie.setTime(kShuttleCenterCheckTime); +	playSpotSoundSync(kShuttleOnboardIn, kShuttleOnboardOut); +	 +	_shuttleEnergyMeter.initShuttleEnergyMeter(); +	_shuttleEnergyMeter.powerUpMeter(); +	while (_shuttleEnergyMeter.isFading()) { +		_vm->checkCallBacks(); +		_vm->refreshDisplay(); +		g_system->updateScreen(); +	} + +	_leftShuttleMovie.show(); +	playMovieSegment(&_leftShuttleMovie, kShuttleLeftIntroStart, kShuttleLeftIntroStop); + +	_leftShuttleMovie.setTime(kShuttleLeftNormalTime); +	_leftShuttleMovie.redrawMovieWorld(); +	 +	_leftDamageShuttleMovie.show(); +	playMovieSegment(&_leftDamageShuttleMovie); + +	// Take it down a tick initially. This sets the time to the time of the last tick, +	// so that subsequence drops will drop it down a tick. +	_leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getTime() - 40); +	_leftDamageShuttleMovie.redrawMovieWorld(); +	 +	_lowerRightShuttleMovie.show(); +	_lowerRightShuttleMovie.setTime(kShuttleLowerRightOffTime); +	_lowerRightShuttleMovie.redrawMovieWorld(); +	_centerShuttleMovie.setTime(kShuttleCenterNavCompTime); +	_centerShuttleMovie.redrawMovieWorld(); +	playSpotSoundSync(kShuttleNavigationIn, kShuttleNavigationOut); + +	_centerShuttleMovie.setTime(kShuttleCenterCommTime); +	_centerShuttleMovie.redrawMovieWorld(); +	playSpotSoundSync(kShuttleCommunicationIn, kShuttleCommunicationOut); + +	_centerShuttleMovie.setTime(kShuttleCenterAllSystemsTime); +	_centerShuttleMovie.redrawMovieWorld(); +	playSpotSoundSync(kShuttleAllSystemsIn, kShuttleAllSystemsOut); + +	_centerShuttleMovie.setTime(kShuttleCenterSecureLooseTime); +	_centerShuttleMovie.redrawMovieWorld(); +	playSpotSoundSync(kShuttleSecureLooseIn, kShuttleSecureLooseOut); + +	_centerShuttleMovie.setTime(kShuttleCenterAutoTestTime); +	_centerShuttleMovie.redrawMovieWorld(); +	playSpotSoundSync(kShuttleAutoTestingIn, kShuttleAutoTestingOut); + +	_leftShuttleMovie.setTime(kShuttleLeftAutoTestTime); +	_leftShuttleMovie.redrawMovieWorld(); +	playSpotSoundSync(kMarsThrusterAutoTestIn, kMarsThrusterAutoTestOut); +	_leftShuttleMovie.setTime(kShuttleLeftNormalTime); +	_leftShuttleMovie.redrawMovieWorld(); + +	_centerShuttleMovie.setTime(kShuttleCenterLaunchTime); +	_centerShuttleMovie.redrawMovieWorld(); +	playSpotSoundSync(kShuttlePrepareForDropIn, kShuttlePrepareForDropOut); + +	playSpotSoundSync(kShuttleAllClearIn, kShuttleAllClearOut); + +	_centerShuttleMovie.setTime(kShuttleCenterEnterTubeTime); +	_centerShuttleMovie.redrawMovieWorld(); + +	_lowerLeftShuttleMovie.show(); +	_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftCollisionTime); + +	loadLoopSound1(""); + +	_canyonChaseMovie.setSegment(kCanyonChaseStart, kCanyonChaseStop); +	_canyonChaseMovie.start(); + +	startMarsTimer(kLaunchTubeReachedTime, kMovieTicksPerSecond, kMarsLaunchTubeReached); +} + +void Mars::startUpFromFinishedSpaceChase() { +	setNextHandler(_vm); +	throwAwayInterface(); + +	initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left, +							kShuttle1Top, true); +	initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left, +							kShuttle2Top, true); +	initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left, +							kShuttle3Top, true); +	initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left, +							kShuttle4Top, true); +	 +	initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie", +			kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false); +	 +	initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie", +			kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false); +	 +	initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder, +			kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false); +	 +	initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder, +			kShuttleLowerRightLeft, kShuttleLowerRightTop, false); +	 +	initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie", +			kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false); +	 +	initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder, +			kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false); +	 +	initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder, +			kShuttleUpperRightLeft, kShuttleUpperRightTop, false); +	 +	initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie", +			kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false); +	 +	initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie", +			kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false); + +	_centerShuttleMovie.show(); + +	_shuttleEnergyMeter.initShuttleEnergyMeter(); +	_shuttleEnergyMeter.setEnergyValue(kFullShuttleEnergy); + +	_leftShuttleMovie.show(); +	_leftShuttleMovie.setTime(kShuttleLeftNormalTime); +	_leftShuttleMovie.redrawMovieWorld(); + +	_leftDamageShuttleMovie.show(); +	_leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getDuration() - 40); +	_leftDamageShuttleMovie.redrawMovieWorld(); + +	_lowerRightShuttleMovie.show(); + +	_lowerLeftShuttleMovie.show(); + +	loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF"); + +	initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft, +			kShuttleJunkTop, false); + +	initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false); +	_explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes); + +	_energyBeam.initShuttleWeapon(); +	_gravitonCannon.initShuttleWeapon(); + +	_upperLeftShuttleMovie.show(); +	_upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime); +	_upperLeftShuttleMovie.redrawMovieWorld(); + +	_rightShuttleMovie.show(); +	_rightShuttleMovie.setTime(kShuttleRightIntroStop - 1); +	_rightShuttleMovie.redrawMovieWorld(); + +	_rightDamageShuttleMovie.show(); +	_rightDamageShuttleMovie.setTime(40); +	_rightDamageShuttleMovie.redrawMovieWorld(); + +	_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime); +	_lowerLeftShuttleMovie.redrawMovieWorld(); +	 +	_shuttleTransportSpot.setArea(kShuttleTransportBounds); +	_shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +	g_allHotspots.push_back(&_shuttleTransportSpot); + +	_privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true); + +	_upperRightShuttleMovie.show(); +	_upperRightShuttleMovie.setTime(kShuttleUpperRightOverloadTime); +	_upperRightShuttleMovie.redrawMovieWorld(); + +	_centerShuttleMovie.setTime(kShuttleCenterSafeTime); +	_centerShuttleMovie.redrawMovieWorld(); + +	_lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportTime); +	_lowerRightShuttleMovie.redrawMovieWorld(); + +	initOneMovie(&_canyonChaseMovie, "Images/Mars/M98EAS.movie", kShuttleTractorBeamMovieOrder, +			kShuttleWindowLeft, kShuttleWindowTop, true); +	_canyonChaseMovie.setTime(_canyonChaseMovie.getDuration()); +	_canyonChaseMovie.redrawMovieWorld(); +} + +void Mars::startUpFromSpaceChase() { +	setNextHandler(_vm); +	throwAwayInterface(); +	 +	// Open the spot sounds movie again... +	_spotSounds.initFromQuickTime(getSoundSpotsName()); +	_spotSounds.setVolume(_vm->getSoundFXLevel());; +	 +	initOnePicture(&_shuttleInterface1, "Images/Mars/MCmain1.pict", kShuttleBackgroundOrder, kShuttle1Left, +							kShuttle1Top, true); +	initOnePicture(&_shuttleInterface2, "Images/Mars/MCmain2.pict", kShuttleBackgroundOrder, kShuttle2Left, +							kShuttle2Top, true); +	initOnePicture(&_shuttleInterface3, "Images/Mars/MCmain3.pict", kShuttleBackgroundOrder, kShuttle3Left, +							kShuttle3Top, true); +	initOnePicture(&_shuttleInterface4, "Images/Mars/MCmain4.pict", kShuttleBackgroundOrder, kShuttle4Left, +							kShuttle4Top, true); +	 +	initOneMovie(&_leftShuttleMovie, "Images/Mars/Left Shuttle.movie", +			kShuttleMonitorOrder, kShuttleLeftLeft, kShuttleLeftTop, false); +	 +	initOneMovie(&_rightShuttleMovie, "Images/Mars/Right Shuttle.movie", +			kShuttleMonitorOrder, kShuttleRightLeft, kShuttleRightTop, false); +	 +	initOneMovie(&_lowerLeftShuttleMovie, "Images/Mars/Lower Left Shuttle.movie", kShuttleMonitorOrder, +			kShuttleLowerLeftLeft, kShuttleLowerLeftTop, false); +	 +	initOneMovie(&_lowerRightShuttleMovie, "Images/Mars/Lower Right Shuttle.movie", kShuttleMonitorOrder, +			kShuttleLowerRightLeft, kShuttleLowerRightTop, false); +	 +	initOneMovie(&_centerShuttleMovie, "Images/Mars/Center Shuttle.movie", +			kShuttleMonitorOrder, kShuttleCenterLeft, kShuttleCenterTop, false); +	 +	initOneMovie(&_upperLeftShuttleMovie, "Images/Mars/Upper Left Shuttle.movie", kShuttleMonitorOrder, +			kShuttleUpperLeftLeft, kShuttleUpperLeftTop, false); +	 +	initOneMovie(&_upperRightShuttleMovie, "Images/Mars/Upper Right Shuttle.movie", kShuttleMonitorOrder, +			kShuttleUpperRightLeft, kShuttleUpperRightTop, false); +	 +	initOneMovie(&_leftDamageShuttleMovie, "Images/Mars/Left Damage Shuttle.movie", +			kShuttleStatusOrder, kShuttleLeftEnergyLeft, kShuttleLeftEnergyTop, false); +	 +	initOneMovie(&_rightDamageShuttleMovie, "Images/Mars/Right Damage Shuttle.movie", +			kShuttleStatusOrder, kShuttleRightEnergyLeft, kShuttleRightEnergyTop, false); + +	_centerShuttleMovie.show(); + +	_shuttleEnergyMeter.initShuttleEnergyMeter(); +	_shuttleEnergyMeter.setEnergyValue(kFullShuttleEnergy); + +	_leftShuttleMovie.show(); +	_leftShuttleMovie.setTime(kShuttleLeftNormalTime); +	_leftShuttleMovie.redrawMovieWorld(); +	 +	_leftDamageShuttleMovie.show(); +	_leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getDuration() - 40); +	_leftDamageShuttleMovie.redrawMovieWorld(); + +	_lowerRightShuttleMovie.show(); + +	_lowerLeftShuttleMovie.show(); + +	loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF"); +	 +	initOneMovie(&_planetMovie, "Images/Mars/Planet.movie", kShuttlePlanetOrder, +			kPlanetStartLeft, kPlanetStartTop, true); +	_planetMovie.setFlags(kLoopTimeBase); + +	initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft, +			kShuttleJunkTop, false); +	 +	initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false); +	_explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes); + +	_energyBeam.initShuttleWeapon(); +	_gravitonCannon.initShuttleWeapon(); + +	_upperLeftShuttleMovie.show(); + +	_robotShip.initRobotShip(); + +	_planetMovie.start(); +	_planetMover.startMoving(&_planetMovie); + +	_upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime); +	_upperLeftShuttleMovie.redrawMovieWorld(); + +	_centerShuttleMovie.setTime(kShuttleCenterTargetSightedTime); +	_centerShuttleMovie.redrawMovieWorld(); + +	_lowerRightShuttleMovie.setTime(kShuttleLowerRightTrackingTime); +	_lowerRightShuttleMovie.redrawMovieWorld(); + +	_rightShuttleMovie.show(); +	_rightShuttleMovie.setTime(kShuttleRightIntroStop - 1); +	_rightShuttleMovie.redrawMovieWorld(); + +	_rightDamageShuttleMovie.show(); +	_rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getDuration() - 40); +	_rightDamageShuttleMovie.redrawMovieWorld(); + +	_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime); +	_lowerLeftShuttleMovie.redrawMovieWorld(); + +	_robotShip.startMoving(); + +	_shuttleHUD.initShuttleHUD(); + +	_tractorBeam.startDisplaying(); + +	_energyChoiceSpot.setArea(kShuttleEnergyBeamBounds); +	_energyChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +	g_allHotspots.push_back(&_energyChoiceSpot); +	_gravitonChoiceSpot.setArea(kShuttleGravitonBounds); +	_gravitonChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +	g_allHotspots.push_back(&_gravitonChoiceSpot); +	_tractorChoiceSpot.setArea(kShuttleTractorBounds); +	_tractorChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +	g_allHotspots.push_back(&_tractorChoiceSpot); +	_shuttleViewSpot.setArea(kShuttleWindowLeft, kShuttleWindowTop, +			kShuttleWindowLeft + kShuttleWindowWidth, kShuttleWindowTop + kShuttleWindowHeight); +	_shuttleViewSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +	g_allHotspots.push_back(&_shuttleViewSpot); +	_shuttleTransportSpot.setArea(kShuttleTransportBounds); +	_shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +	g_allHotspots.push_back(&_shuttleTransportSpot); + +	_privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true); + +	startMarsTimer(kSpaceChaseTimeLimit, kOneTickPerSecond, kMarsSpaceChaseFinished); +} + +void Mars::setSoundFXLevel(const uint16 level) { +	Neighborhood::setSoundFXLevel(level); + +	if (_canyonChaseMovie.isMovieValid()) +		_canyonChaseMovie.setVolume(level); + +	if (_explosions.isMovieValid()) +		_explosions.setVolume(level); +} + +void Mars::startMarsTimer(TimeValue time, TimeScale scale, MarsTimerCode code) { +	_utilityFuse.primeFuse(time, scale); +	_marsEvent.mars = this; +	_marsEvent.event = code; +	_utilityFuse.setFunctionPtr(&marsTimerFunction, (void *)&_marsEvent); +	_utilityFuse.lightFuse(); +} + +void Mars::marsTimerExpired(MarsTimerEvent &event) { +	Common::Rect r; +	uint16 x, y; + +	switch (event.event) { +	case kMarsLaunchTubeReached: +		_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftTubeTime); +		_lowerLeftShuttleMovie.redrawMovieWorld(); +		startMarsTimer(kCanyonChaseFinishedTime, kMovieTicksPerSecond, kMarsCanyonChaseFinished); +		break; +	case kMarsCanyonChaseFinished: +		GameState.setScoringEnteredLaunchTube(); + +		while (_canyonChaseMovie.isRunning()) { +			_vm->checkCallBacks(); +			_vm->refreshDisplay(); +			_vm->_system->delayMillis(10); +		} + +		_canyonChaseMovie.stop(); +		_canyonChaseMovie.stopDisplaying(); +		_canyonChaseMovie.releaseMovie(); + +		_vm->_gfx->enableErase(); + +		loadLoopSound1("Sounds/Mars/Space Ambient.22K.8.AIFF"); + +		playSpotSoundSync(kShuttleConfiguringIn, kShuttleConfiguringOut); +		playSpotSoundSync(kShuttleGeneratingIn, kShuttleGeneratingOut); +		playSpotSoundSync(kShuttleBreakawayIn, kShuttleBreakawayOut); +		playSpotSoundSync(kMarsAtmosphericBreakawayIn, kMarsAtmosphericBreakawayOut); + +		initOneMovie(&_planetMovie, "Images/Mars/Planet.movie", kShuttlePlanetOrder, kPlanetStartLeft, kPlanetStartTop, true); +		_planetMovie.setFlags(kLoopTimeBase); + +		initOneMovie(&_junk, "Images/Mars/Junk.movie", kShuttleJunkOrder, kShuttleJunkLeft, kShuttleJunkTop, false); + +		initOneMovie(&_explosions, "Images/Mars/Explosions.movie", kShuttleWeaponFrontOrder, 0, 0, false); +		_explosionCallBack.initCallBack(&_explosions, kCallBackAtExtremes); + +		_energyBeam.initShuttleWeapon(); +		_gravitonCannon.initShuttleWeapon(); + +		_centerShuttleMovie.setTime(kShuttleCenterWeaponsTime); +		_centerShuttleMovie.redrawMovieWorld(); + +		_upperLeftShuttleMovie.show(); +		_upperLeftShuttleMovie.setTime(kShuttleUpperLeftDampingTime); +		_upperLeftShuttleMovie.redrawMovieWorld(); + +		_robotShip.initRobotShip(); + +		_planetMovie.start(); +		_planetMover.startMoving(&_planetMovie); + +		playSpotSoundSync(kShuttleDamperDescIn, kShuttleDamperDescOut); +		_upperLeftShuttleMovie.setTime(kShuttleUpperLeftGravitonTime); +		_upperLeftShuttleMovie.redrawMovieWorld(); + +		playSpotSoundSync(kShuttleGravitonDescIn, kShuttleGravitonDescOut); +		_upperLeftShuttleMovie.setTime(kShuttleUpperLeftTractorTime); +		_upperLeftShuttleMovie.redrawMovieWorld(); + +		playSpotSoundSync(kShuttleTractorDescIn, kShuttleTractorDescOut); +		_upperLeftShuttleMovie.setTime(kShuttleUpperLeftDimTime); +		_upperLeftShuttleMovie.redrawMovieWorld(); +			 +		_centerShuttleMovie.setTime(kShuttleCenterTargetSightedTime); +		_centerShuttleMovie.redrawMovieWorld(); +		playSpotSoundSync(kShuttleTargetSightedIn, kShuttleTargetSightedOut); + +		_lowerRightShuttleMovie.setTime(kShuttleLowerRightTrackingTime); +		_lowerRightShuttleMovie.redrawMovieWorld(); +		_rightShuttleMovie.show(); +		playMovieSegment(&_rightShuttleMovie, kShuttleRightIntroStart, kShuttleRightIntroStop); + +		_rightDamageShuttleMovie.show(); +		playMovieSegment(&_rightDamageShuttleMovie); + +		// Take it down a tick initially. This sets the time to the time of the last tick, +		// so that subsequence drops will drop it down a tick. +		_rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getTime() - 40); +		_rightDamageShuttleMovie.redrawMovieWorld(); + +		_lowerLeftShuttleMovie.setTime(kShuttleLowerLeftAutopilotTime); +		_lowerLeftShuttleMovie.redrawMovieWorld(); +		playSpotSoundSync(kShuttleAutopilotEngagedIn, kShuttleAutopilotEngagedOut); + +		_robotShip.startMoving(); + +		_shuttleHUD.initShuttleHUD(); + +		_tractorBeam.startDisplaying(); + +		_energyChoiceSpot.setArea(kShuttleEnergyBeamBounds); +		_energyChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +		g_allHotspots.push_back(&_energyChoiceSpot); +		_gravitonChoiceSpot.setArea(kShuttleGravitonBounds); +		_gravitonChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +		g_allHotspots.push_back(&_gravitonChoiceSpot); +		_tractorChoiceSpot.setArea(kShuttleTractorBounds); +		_tractorChoiceSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +		g_allHotspots.push_back(&_tractorChoiceSpot); +		_shuttleViewSpot.setArea(kShuttleWindowLeft, kShuttleWindowTop, +				kShuttleWindowLeft + kShuttleWindowWidth, kShuttleWindowTop + kShuttleWindowHeight); +		_shuttleViewSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +		g_allHotspots.push_back(&_shuttleViewSpot); +		_shuttleTransportSpot.setArea(kShuttleTransportBounds); +		_shuttleTransportSpot.setHotspotFlags(kNeighborhoodSpotFlag | kClickSpotFlag); +		g_allHotspots.push_back(&_shuttleTransportSpot); + +		_privateFlags.setFlag(kMarsPrivateInSpaceChaseFlag, true); + +		playSpotSoundSync(kMarsCockpitChatterIn, kMarsCockpitChatterOut); + +		GameState.setMarsFinishedCanyonChase(true); + +		startMarsTimer(kSpaceChaseTimeLimit, kOneTickPerSecond, kMarsSpaceChaseFinished); + +		_vm->_cursor->hideUntilMoved(); +		break;			 +	case kMarsSpaceChaseFinished: +		// Player failed to stop the robot in time... +		_interruptionFilter = kFilterNoInput; + +		_rightShuttleMovie.setTime(kShuttleRightTargetLockTime); +		_rightShuttleMovie.redrawMovieWorld(); + +		_upperRightShuttleMovie.show(); +		_upperRightShuttleMovie.setTime(kShuttleUpperRightLockedTime); +		_upperRightShuttleMovie.redrawMovieWorld(); + +		_rightShuttleMovie.setTime(kShuttleRightGravitonTime); +		_rightShuttleMovie.redrawMovieWorld(); +		_upperRightShuttleMovie.setTime(kShuttleUpperRightArmedTime); +		_upperRightShuttleMovie.redrawMovieWorld(); + +		_vm->delayShell(3, 1); + +		x = _vm->getRandomNumber(19); +		y = _vm->getRandomNumber(19); + +		r = Common::Rect(kShuttleWindowMidH - x, kShuttleWindowMidV - y, +				kShuttleWindowMidH - x + 20, kShuttleWindowMidV - y + 20); +		showBigExplosion(r, kShuttleAlienShipOrder); +			 +		while (_explosions.isRunning()) { +			_vm->checkCallBacks(); +			_vm->refreshDisplay(); +			g_system->delayMillis(10); +		} + +		throwAwayMarsShuttle(); +		reinstateMonocleInterface(); +		recallToTSAFailure(); +		break; +	default: +		break; +	} + +	_interruptionFilter = kFilterAllInput; +} + +void Mars::throwAwayMarsShuttle() { +	_shuttleInterface1.deallocateSurface(); +	_shuttleInterface1.stopDisplaying(); +	_shuttleInterface2.deallocateSurface(); +	_shuttleInterface2.stopDisplaying(); +	_shuttleInterface3.deallocateSurface(); +	_shuttleInterface3.stopDisplaying(); +	_shuttleInterface4.deallocateSurface(); +	_shuttleInterface4.stopDisplaying(); + +	_spotSounds.disposeSound(); +	 +	_canyonChaseMovie.releaseMovie(); +	_canyonChaseMovie.stopDisplaying(); +	_leftShuttleMovie.releaseMovie(); +	_leftShuttleMovie.stopDisplaying(); +	_rightShuttleMovie.releaseMovie(); +	_rightShuttleMovie.stopDisplaying(); +	_lowerLeftShuttleMovie.releaseMovie(); +	_lowerLeftShuttleMovie.stopDisplaying(); +	_lowerRightShuttleMovie.releaseMovie(); +	_lowerRightShuttleMovie.stopDisplaying(); +	_centerShuttleMovie.releaseMovie(); +	_centerShuttleMovie.stopDisplaying(); +	_upperLeftShuttleMovie.releaseMovie(); +	_upperLeftShuttleMovie.stopDisplaying(); +	_upperRightShuttleMovie.releaseMovie(); +	_upperRightShuttleMovie.stopDisplaying(); +	_leftDamageShuttleMovie.releaseMovie(); +	_leftDamageShuttleMovie.stopDisplaying(); +	_rightDamageShuttleMovie.releaseMovie(); +	_rightDamageShuttleMovie.stopDisplaying(); + +	_shuttleEnergyMeter.disposeShuttleEnergyMeter(); +	_robotShip.cleanUpRobotShip(); +	_shuttleHUD.cleanUpShuttleHUD(); +	_tractorBeam.stopDisplaying(); +	_junk.releaseMovie(); +	_junk.stopDisplaying(); +	_energyBeam.cleanUpShuttleWeapon(); +	_gravitonCannon.cleanUpShuttleWeapon(); +	g_allHotspots.remove(&_energyChoiceSpot); +	g_allHotspots.remove(&_gravitonChoiceSpot); +	g_allHotspots.remove(&_tractorChoiceSpot); +	g_allHotspots.remove(&_shuttleViewSpot); +	g_allHotspots.remove(&_shuttleTransportSpot); +	_explosions.releaseMovie(); +	_explosions.stopDisplaying(); + +	loadLoopSound1(""); +} + +void Mars::transportToRobotShip() { +	throwAwayMarsShuttle(); + +	Video::VideoDecoder *video = new Video::QuickTimeDecoder(); +	if (!video->loadFile("Images/Mars/M98EAE.movie")) +		error("Could not load shuttle->interface transition video"); + +	while (!_vm->shouldQuit() && !video->endOfVideo()) { +		if (video->needsUpdate()) { +			const Graphics::Surface *frame = video->decodeNextFrame(); + +			if (frame) +				_vm->drawScaledFrame(frame, 0, 0); +		} + +		Common::Event event; +		while (g_system->getEventManager()->pollEvent(event)) +			; + +		g_system->delayMillis(10); +	} + +	delete video; + +	if (_vm->shouldQuit()) +		return; + +	reinstateMonocleInterface(); + +	g_energyMonitor->stopEnergyDraining(); +	g_energyMonitor->restoreLastEnergyValue(); +	_vm->resetEnergyDeathReason(); +	g_energyMonitor->startEnergyDraining(); + +	arriveAt(kMarsRobotShuttle, kEast); + +	_navMovie.stop(); +	_navMovie.setTime(_navMovie.getStart()); +	_navMovie.start(); +} + +const int kRobotTooStrong = 1; +const int kTractorTooWeak = 2; +const int kCapturedRobotShip = 3; + +void Mars::spaceChaseClick(const Input &input, const HotSpotID id) { +	Common::Point pt; + +	switch (id) { +	case kShuttleEnergySpotID: +		_upperLeftShuttleMovie.setTime(kShuttleUpperLeftDampingTime); +		_upperLeftShuttleMovie.redrawMovieWorld(); +		_leftShuttleMovie.setTime(kShuttleLeftDampingTime); +		_leftShuttleMovie.redrawMovieWorld(); +		_shuttleHUD.hide(); +		_weaponSelection = kEnergyBeam; +		playSpotSoundSync(kShuttleDampingBeamIn, kShuttleDampingBeamOut); +		break; +	case kShuttleGravitonSpotID: +		_upperLeftShuttleMovie.setTime(kShuttleUpperLeftGravitonTime); +		_upperLeftShuttleMovie.redrawMovieWorld(); +		_leftShuttleMovie.setTime(kShuttleLeftGravitonTime); +		_leftShuttleMovie.redrawMovieWorld(); +		_shuttleHUD.hide(); +		_weaponSelection = kGravitonCannon; +		playSpotSoundSync(kShuttleGravitonIn, kShuttleGravitonOut); +		break; +	case kShuttleTractorSpotID: +		_upperLeftShuttleMovie.setTime(kShuttleUpperLeftTractorTime); +		_upperLeftShuttleMovie.redrawMovieWorld(); +		_leftShuttleMovie.setTime(kShuttleLeftTractorTime); +		_leftShuttleMovie.redrawMovieWorld(); +		_shuttleHUD.show(); +		_weaponSelection = kTractorBeam; +		playSpotSoundSync(kShuttleTractorBeamIn, kShuttleTractorBeamOut); +		break; +	case kShuttleViewSpotID: +		switch (_weaponSelection) { +		case kEnergyBeam: +			if (_shuttleEnergyMeter.getEnergyValue() < kMinDampingEnergy) { +				playSpotSoundSync(kShuttleEnergyTooLowIn, kShuttleEnergyTooLowOut); +			} else { +				if (_energyBeam.canFireWeapon()) { +					_shuttleEnergyMeter.dropEnergyValue(kMinDampingEnergy); +					input.getInputLocation(pt); +					_energyBeam.fireWeapon(pt.x, pt.y); +					playSpotSoundSync(kMarsEDBBlastIn, kMarsEDBBlastOut); +				} +			} +			break; +		case kGravitonCannon: +			if (_shuttleEnergyMeter.getEnergyValue() < kMinGravitonEnergy) { +				playSpotSoundSync(kShuttleEnergyTooLowIn, kShuttleEnergyTooLowOut); +			} else { +				if (_gravitonCannon.canFireWeapon()) { +					_shuttleEnergyMeter.dropEnergyValue(kMinGravitonEnergy); +					input.getInputLocation(pt); +					_gravitonCannon.fireWeapon(pt.x, pt.y); +					playSpotSoundSync(kMarsGravitonBlastIn, kMarsGravitonBlastOut); +				} +			} +			break; +		case kTractorBeam: +			if (_shuttleHUD.isTargetLocked()) { +				// play tractor beam sound? +				_utilityFuse.stopFuse(); + +				_tractorBeam.show(); + +				int capture; +				if (_rightDamageShuttleMovie.getTime() > 40) { +					capture = kRobotTooStrong; +				} else if (!_shuttleEnergyMeter.enoughEnergyForTractorBeam()) { +					capture = kTractorTooWeak; +				} else { +					_robotShip.snareByTractorBeam(); +					capture = kCapturedRobotShip; +					_planetMover.dropPlanetOutOfSight(); +				} + +				_shuttleEnergyMeter.drainForTractorBeam(); + +				while (_shuttleEnergyMeter.isFading()) { +					_vm->checkCallBacks(); +					_vm->refreshDisplay(); +					_vm->_system->delayMillis(10); +				} + +				_shuttleEnergyMeter.setEnergyValue(_shuttleEnergyMeter.getEnergyValue()); + +				switch (capture) { +				case kRobotTooStrong: +					_tractorBeam.hide(); +					playSpotSoundSync(kShuttleBrokeFreeIn, kShuttleBrokeFreeOut); +					_utilityFuse.lightFuse(); +					break; +				case kTractorTooWeak: +					playSpotSoundSync(kShuttleCantHoldIn, kShuttleCantHoldOut); +					_tractorBeam.hide(); +					_utilityFuse.lightFuse(); +					break; +				case kCapturedRobotShip: +					_tractorBeam.hide(); +					_shuttleHUD.hide(); +					_robotShip.cleanUpRobotShip(); +					_planetMovie.stop(); +					_planetMovie.stopDisplaying(); +					_planetMovie.releaseMovie(); + +					// Shameless reuse of a variable :P +					initOneMovie(&_canyonChaseMovie, "Images/Mars/M98EAS.movie", kShuttleTractorBeamMovieOrder, +							kShuttleWindowLeft, kShuttleWindowTop, true); +					_canyonChaseMovie.redrawMovieWorld(); +					playMovieSegment(&_canyonChaseMovie, 0, _canyonChaseMovie.getDuration()); + +					// wait here until any junk clears... +					while (_junk.junkFlying()) { +						_vm->checkCallBacks(); +						_vm->refreshDisplay(); +						_vm->_system->delayMillis(10); +					} + +					_upperRightShuttleMovie.show(); +					_upperRightShuttleMovie.setTime(kShuttleUpperRightOverloadTime); +					_upperRightShuttleMovie.redrawMovieWorld(); + +					playSpotSoundSync(kShuttleOverloadedIn, kShuttleOverloadedOut); +					_centerShuttleMovie.setTime(kShuttleCenterVerifyingTime); +					_centerShuttleMovie.redrawMovieWorld(); + +					playSpotSoundSync(kShuttleCoordinatesIn, kShuttleCoordinatesOut); +					_centerShuttleMovie.setTime(kShuttleCenterScanningTime); +					_centerShuttleMovie.redrawMovieWorld(); + +					playSpotSoundSync(kShuttleScanningIn, kShuttleScanningOut); +					_centerShuttleMovie.setTime(kShuttleCenterSafeTime); +					_centerShuttleMovie.redrawMovieWorld(); + +					playSpotSoundSync(kShuttleSafeIn, kShuttleSafeOut); +					_lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportTime); +					_lowerRightShuttleMovie.redrawMovieWorld(); +					GameState.setMarsReadyForShuttleTransport(true); +					break; +				} +			} else { +				playSpotSoundSync(kShuttleTractorLimitedIn, kShuttleTractorLimitedOut); +			} +			break; +		default: +			break; +		} +		break; +	case kShuttleTransportSpotID: +		_lowerRightShuttleMovie.setTime(kShuttleLowerRightTransportHiliteTime); +		_lowerRightShuttleMovie.redrawMovieWorld(); +		_neighborhoodNotification.setNotificationFlags(kTimeToTransportFlag, kTimeToTransportFlag); +		break; +	} +} + +void Mars::showBigExplosion(const Common::Rect &r, const DisplayOrder order) { +	if (_explosions.isMovieValid()) { +		_explosions.setDisplayOrder(order); + +		Common::Rect r2 = r; +		int dx = r.width() / 2; +		int dy = r.height() / 2; +		r2.left -= dx; +		r2.right += dx; +		r2.top -= dy; +		r2.bottom += dy;		 + +		_explosions.setBounds(r2); +		_explosions.show(); +		_explosions.stop(); +		_explosions.setSegment(kBigExplosionStart, kBigExplosionStop); +		_explosions.setTime(kBigExplosionStart); +		_explosionCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +		_explosions.start(); +	} +} + +void Mars::showLittleExplosion(const Common::Rect &r, const DisplayOrder order) {	 +	if (_explosions.isMovieValid()) { +		_explosions.setDisplayOrder(order); + +		Common::Rect r2 = r; +		int dx = r.width() / 2; +		int dy = r.height() / 2; +		r2.left -= dx; +		r2.right += dx; +		r2.top -= dy; +		r2.bottom += dy; +		_explosions.setBounds(r2); + +		_explosions.show(); +		_explosions.stop(); +		_explosions.setSegment(kLittleExplosionStart, kLittleExplosionStop); +		_explosions.setTime(kLittleExplosionStart); +		_explosionCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +		_explosions.start(); +	} +} + +void Mars::hitByJunk() {	 +	_leftDamageShuttleMovie.setTime(_leftDamageShuttleMovie.getTime() - 40); +	_leftDamageShuttleMovie.redrawMovieWorld(); + +	playSpotSoundSync(kMarsJunkCollisionIn, kMarsJunkCollisionOut); + +	if (_leftDamageShuttleMovie.getTime() == 0) { +		die(kDeathRanIntoSpaceJunk); +	} else { +		TimeValue t = _leftDamageShuttleMovie.getTime() / 40; + +		if (t == 1) +			playSpotSoundSync(kShuttleHullBreachIn, kShuttleHullBreachOut); + +		t = _leftShuttleMovie.getTime(); +		_leftShuttleMovie.setTime(kShuttleLeftDamagedTime); +		_leftShuttleMovie.redrawMovieWorld(); +		_vm->delayShell(1, 3); +		_leftShuttleMovie.setTime(t); +		_leftShuttleMovie.redrawMovieWorld(); +	} +} + +void Mars::setUpNextDropTime() { +	_robotShip.setUpNextDropTime(); +} + +void Mars::decreaseRobotShuttleEnergy(const int delta, Common::Point impactPoint) {	 +	_rightDamageShuttleMovie.setTime(_rightDamageShuttleMovie.getTime() - 40 * delta); +	_rightDamageShuttleMovie.redrawMovieWorld(); + +	if (_rightDamageShuttleMovie.getTime() == 0) { +		Common::Rect r; +		_robotShip.getShuttleBounds(r); +		int size = MAX(r.width(), r.height()); +		r = Common::Rect::center(impactPoint.x, impactPoint.y, size, size); +		_robotShip.killRobotShip(); +		showBigExplosion(r, kShuttleRobotShipOrder); +	} else if (delta > 1) { +		Common::Rect r; +		_robotShip.getShuttleBounds(r); +		int size = MIN(r.width(), r.height()); +		r = Common::Rect::center(impactPoint.x, impactPoint.y, size, size); +		showLittleExplosion(r, kShuttleWeaponBackOrder); +		TimeValue t = _rightShuttleMovie.getTime(); +		_rightShuttleMovie.setTime(kShuttleRightDamagedTime); +		_rightShuttleMovie.redrawMovieWorld(); +		_vm->delayShell(1, 3); +		_rightShuttleMovie.setTime(t); +		_rightShuttleMovie.redrawMovieWorld(); +	} + +	if (_rightDamageShuttleMovie.getTime() <= 40) { +		GameState.setScoringStoppedRobotsShuttle(); +		if (!GameState.getMarsHitRobotWithCannon()) +			GameState.setScoringMarsGandhi(); +	} +} + +void Mars::updateCursor(const Common::Point cursorLocation, const Hotspot *cursorSpot) { +	if (cursorSpot && cursorSpot->getObjectID() == kShuttleViewSpotID) { +		if (_weaponSelection != kNoWeapon) +			_vm->_cursor->setCurrentFrameIndex(6); +		else +			_vm->_cursor->setCurrentFrameIndex(0); +	} else { +		Neighborhood::updateCursor(cursorLocation, cursorSpot); +	} +} + +AirQuality Mars::getAirQuality(const RoomID room) { +	if ((room >= kMars36 && room <= kMars39) || (room >= kMarsMaze004 && room <= kMarsMaze200)) +		return kAirQualityVacuum; +	if (room == kMars35 && !GameState.getMarsAirlockOpen()) +		return kAirQualityVacuum; +	if (room == kMars60 && !GameState.getMarsAirlockOpen()) +		return kAirQualityVacuum; + +	return Neighborhood::getAirQuality(room); +} + +// Start up panting sound if necessary. + +void Mars::checkAirMask() { +	Neighborhood::checkAirMask(); + +	if (getAirQuality(GameState.getCurrentRoom()) == kAirQualityVacuum) { +		if (g_airMask->isAirMaskOn()) { +			if (_noAirFuse.isFuseLit()) { +				_noAirFuse.stopFuse(); +				loadLoopSound2(""); +				loadAmbientLoops(); +				playSpotSoundSync(kMarsOxyMaskOnIn, kMarsOxyMaskOnOut); +			} +		} else { +			if (!_noAirFuse.isFuseLit()) { +				loadLoopSound2("Sounds/Mars/SukWind1.22K.AIFF"); +				_noAirFuse.primeFuse(kVacuumSurvivalTimeLimit); +				_noAirFuse.lightFuse(); +			} +		} +	} else { +		if (_noAirFuse.isFuseLit()) { +			_noAirFuse.stopFuse(); +			loadLoopSound2(""); +			loadAmbientLoops(); +		} +	} +} + +void Mars::airStageExpired() { +	if (((PegasusEngine *)g_engine)->playerHasItemID(kAirMask)) +		die(kDeathNoAirInMaze); +	else +		die(kDeathNoMaskInMaze); +} + +void Mars::lockThawed() { +	startExtraSequence(kMars57ThawLock, kExtraCompletedFlag, kFilterNoInput); +} + +void Mars::setUpReactorLevel1() { +	_reactorStage = 1; +	makeColorSequence(); +	_guessObject.initReactorGuess(); +	_undoPict.initFromPICTResource(_vm->_resFork, kReactorUndoHilitePICTID); +	_undoPict.setDisplayOrder(kMonitorLayer); +	_undoPict.moveElementTo(kUndoHiliteLeft, kUndoHiliteTop); +	_undoPict.startDisplaying(); +	_guessHistory.initReactorHistory(); +	_choiceHighlight.initReactorChoiceHighlight(); +	setCurrentActivation(kActivateReactorInGame); +	_bombFuse.primeFuse(kColorMatchingTimeLimit); +	_bombFuse.setFunctionPtr(&bombTimerExpiredInGameFunction, (void *)this); +	_bombFuse.lightFuse(); +} + +void Mars::setUpNextReactorLevel() { +	_guessObject.show(); +	_guessHistory.show(); +	_guessHistory.clearHistory(); +	_choiceHighlight.show(); +	_reactorStage++; +	makeColorSequence(); +} + +void Mars::makeColorSequence() { +	int32 code[5]; +	int32 highest = _reactorStage + 2; + +	for (int32 i = 0; i < highest; i++) +		code[i] = i; + +	_vm->shuffleArray(code, highest); +	_currentGuess[0] = -1; +	_currentGuess[1] = -1; +	_currentGuess[2] = -1; +	_nextGuess = 0; +	_guessObject.setGuess(-1, -1, -1); +	_guessHistory.setAnswer(code[0], code[1], code[2]); +} + +void Mars::doUndoOneGuess() { +	if (_nextGuess > 0) { +		_undoPict.show(); +		_vm->delayShell(1, 2); +		_undoPict.hide(); +		_nextGuess--; +		_currentGuess[_nextGuess] = -1; +		_guessObject.setGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]); +		_choiceHighlight.resetHighlight(); + +		if (_currentGuess[0] != -1) { +			_choiceHighlight.highlightChoice(_currentGuess[0]); + +			if (_currentGuess[1] != -1) { +				_choiceHighlight.highlightChoice(_currentGuess[1]); + +				if (_currentGuess[2] != -1) +					_choiceHighlight.highlightChoice(_currentGuess[2]); +			} +		} +	} +} + +void Mars::doReactorGuess(int32 guess) { +	_choiceHighlight.highlightChoice(guess); +	_currentGuess[_nextGuess] = guess; +	_guessObject.setGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]); + +	switch (guess) { +	case 0: +		playSpotSoundSync(kColorMatchRedIn, kColorMatchRedOut); +		break; +	case 1: +		playSpotSoundSync(kColorMatchYellowIn, kColorMatchYellowOut); +		break; +	case 2: +		playSpotSoundSync(kColorMatchGreenIn, kColorMatchGreenOut); +		break; +	case 3: +		playSpotSoundSync(kColorMatchBlueIn, kColorMatchBlueOut); +		break; +	case 4: +		playSpotSoundSync(kColorMatchPurpleIn, kColorMatchPurpleOut); +		break; +	} + +	_nextGuess++; + +	if (_nextGuess == 3) { +		_vm->delayShell(1, 2); +		_nextGuess = 0; +		_guessHistory.addGuess(_currentGuess[0], _currentGuess[1], _currentGuess[2]); + +		switch (_guessHistory.getCurrentNumCorrect()) { +		case 0: +			playSpotSoundSync(kColorMatchZeroNodesIn, kColorMatchZeroNodesOut); +			break; +		case 1: +			playSpotSoundSync(kColorMatchOneNodeIn, kColorMatchOneNodeOut); +			break; +		case 2: +			playSpotSoundSync(kColorMatchTwoNodesIn, kColorMatchTwoNodesOut); +			break; +		case 3: +			playSpotSoundSync(kColorMatchThreeNodesIn, kColorMatchThreeNodesOut); +			break; +		} + +		_currentGuess[0] = -1; +		_currentGuess[1] = -1; +		_currentGuess[2] = -1; +		_guessObject.setGuess(-1, -1, -1); +		_choiceHighlight.resetHighlight(); + +		if (_guessHistory.isSolved()) { +			_guessHistory.showAnswer(); +			_vm->delayShell(1, 2); +			_guessObject.hide(); +			_guessHistory.hide(); +			_choiceHighlight.hide(); + +			switch (_reactorStage) { +			case 1: +				startExtraSequence(kMars57GameLevel2, kExtraCompletedFlag, kFilterNoInput); +				break; +			case 2: +				startExtraSequence(kMars57GameLevel3, kExtraCompletedFlag, kFilterNoInput); +				break; +			case 3: +				_bombFuse.stopFuse(); +				_guessObject.disposeReactorGuess(); +				_undoPict.deallocateSurface(); +				_guessHistory.disposeReactorHistory(); +				_choiceHighlight.disposeReactorChoiceHighlight(); +				GameState.setScoringDisarmedCardBomb(); +				startExtraSequence(kMars57GameSolved, kExtraCompletedFlag, kFilterNoInput); +				break; +			} +		} else if (_guessHistory.getNumGuesses() >= 5) { +			_vm->delayShell(2, 1); +			bombExplodesInGame(); +		} +	} +} + +void Mars::bombExplodesInGame() { +	_guessObject.disposeReactorGuess(); +	_undoPict.deallocateSurface(); +	_guessHistory.disposeReactorHistory(); +	_choiceHighlight.disposeReactorChoiceHighlight(); +	startExtraSequence(kMars57BombExplodesInGame, kExtraCompletedFlag, kFilterNoInput); +} + +void Mars::didntFindBomb() { +	die(kDeathDidntFindMarsBomb); +} + +Common::String Mars::getBriefingMovie() { +	Common::String movieName = Neighborhood::getBriefingMovie(); + +	if (!movieName.empty()) +		return movieName; + +	return "Images/AI/Mars/XM01"; +} + +Common::String Mars::getEnvScanMovie() { +	Common::String movieName = Neighborhood::getEnvScanMovie(); + +	if (movieName.empty()) { +		RoomID room = GameState.getCurrentRoom(); + +		if (room >= kMars0A && room <= kMars21) +			return "Images/AI/Mars/XME1"; +		else if (room >= kMars22 && room <= kMars31South) +			return "Images/AI/Mars/XME2"; +		else if (room >= kMars52 && room <= kMars58) +			return "Images/AI/Mars/XMREACE"; + +		return "Images/AI/Mars/XME3"; +	} + +	return movieName; +} + +uint Mars::getNumHints() { +	uint numHints = Neighborhood::getNumHints(); + +	if (numHints == 0) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kMars27, kNorth): +		case MakeRoomView(kMars28, kNorth): +		case MakeRoomView(kMars49, kSouth): +			numHints = 1; +			break; +		case MakeRoomView(kMars31, kSouth): +		case MakeRoomView(kMars31South, kSouth): +			if (!GameState.isTakenItemID(kMarsCard)) +				numHints = 1; +			break; +		case MakeRoomView(kMars34, kNorth): +			if (!GameState.isTakenItemID(kMarsCard)) +				numHints = 2; +			break; +		case MakeRoomView(kMars34, kSouth): +		case MakeRoomView(kMars45, kNorth): +			if (!GameState.isTakenItemID(kCrowbar)) +				numHints = 1; +			break; +		case MakeRoomView(kMars51, kEast): +			if (GameState.isCurrentDoorOpen() && !GameState.getShieldOn()) { +				if (GameState.isTakenItemID(kShieldBiochip)) +					numHints = 1; +				else +					numHints = 2; +			} +			break; +		case MakeRoomView(kMars52, kNorth): +		case MakeRoomView(kMars52, kSouth): +		case MakeRoomView(kMars52, kEast): +		case MakeRoomView(kMars52, kWest): +		case MakeRoomView(kMars54, kNorth): +		case MakeRoomView(kMars54, kSouth): +		case MakeRoomView(kMars54, kEast): +		case MakeRoomView(kMars54, kWest): +		case MakeRoomView(kMars56, kNorth): +		case MakeRoomView(kMars56, kSouth): +		case MakeRoomView(kMars56, kWest): +		case MakeRoomView(kMars58, kNorth): +		case MakeRoomView(kMars58, kSouth): +		case MakeRoomView(kMars58, kEast): +		case MakeRoomView(kMars58, kWest): +			if (!GameState.getShieldOn()) { +				if (GameState.isTakenItemID(kShieldBiochip)) +					numHints = 1; +				else +					numHints = 2; +			} +			break; +		case MakeRoomView(kMars56, kEast): +			if (getCurrentActivation() == kActivateReactorReadyForNitrogen) { +				if ((ExtraID)_lastExtra == kMars57LowerScreenClosed) +					numHints = 3; +			} else if (getCurrentActivation() == kActivateReactorPlatformOut) { +				if (!GameState.getShieldOn()) { +					if (GameState.isTakenItemID(kShieldBiochip)) +						numHints = 1; +					else +						numHints = 2; +				} +			} +			break; +		} +	} + +	return numHints; +} + +Common::String Mars::getHintMovie(uint hintNum) { +	Common::String movieName = Neighborhood::getHintMovie(hintNum); + +	if (movieName.empty()) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kMars27, kNorth): +		case MakeRoomView(kMars28, kNorth): +			return "Images/AI/Globals/XGLOB5C"; +		case MakeRoomView(kMars31, kSouth): +		case MakeRoomView(kMars31South, kSouth): +		case MakeRoomView(kMars34, kSouth): +		case MakeRoomView(kMars45, kNorth): +			return "Images/AI/Globals/XGLOB1C"; +		case MakeRoomView(kMars34, kNorth): +			if (hintNum == 1) +				return "Images/AI/Globals/XGLOB2C"; + +			return "Images/AI/Globals/XGLOB3G"; +		case MakeRoomView(kMars49, kSouth): +			if (GameState.isTakenItemID(kAirMask)) +				return "Images/AI/Globals/XGLOB3E"; + +			return "Images/AI/Globals/XGLOB1C"; +		case MakeRoomView(kMars51, kEast): +			if (GameState.isTakenItemID(kShieldBiochip)) +				return "Images/AI/Mars/XM52NW"; + +			if (hintNum == 1) +				return "Images/AI/Globals/XGLOB2D"; + +			return "Images/AI/Globals/XGLOB3F"; +		case MakeRoomView(kMars52, kNorth): +		case MakeRoomView(kMars52, kSouth): +		case MakeRoomView(kMars52, kEast): +		case MakeRoomView(kMars52, kWest): +		case MakeRoomView(kMars54, kNorth): +		case MakeRoomView(kMars54, kSouth): +		case MakeRoomView(kMars54, kEast): +		case MakeRoomView(kMars54, kWest): +		case MakeRoomView(kMars56, kNorth): +		case MakeRoomView(kMars56, kSouth): +		case MakeRoomView(kMars56, kWest): +		case MakeRoomView(kMars58, kNorth): +		case MakeRoomView(kMars58, kSouth): +		case MakeRoomView(kMars58, kEast): +		case MakeRoomView(kMars58, kWest): +			if (hintNum == 1) { +				if (GameState.isTakenItemID(kShieldBiochip)) +					return "Images/AI/Mars/XM52NW"; + +				return "Images/AI/Globals/XGLOB2D"; +			} + +			return "Images/AI/Globals/XGLOB3F"; +		case MakeRoomView(kMars56, kEast): +			if (getCurrentActivation() == kActivateReactorReadyForNitrogen) +				return Common::String::format("Images/AI/Mars/XM57SD%d", hintNum); + +			if (hintNum == 1) { +				if (GameState.isTakenItemID(kShieldBiochip)) +					return "Images/AI/Mars/XM52NW"; + +				return "Images/AI/Globals/XGLOB2D"; +			} + +			return "Images/AI/Globals/XGLOB3F"; +		} +	} + +	return movieName; +} + +bool Mars::inColorMatchingGame() { +	return _guessObject.isDisplaying(); +} + +bool Mars::canSolve() { +	return GameState.getCurrentRoomAndView() == MakeRoomView(kMars56, kEast) && (getCurrentActivation() == kActivateReactorReadyForNitrogen || +			getCurrentActivation() == kActivateReactorReadyForCrowBar || inColorMatchingGame()); +} + +void Mars::doSolve() { +	if (getCurrentActivation() == kActivateReactorReadyForNitrogen || getCurrentActivation() == kActivateReactorReadyForCrowBar) { +		_utilityFuse.stopFuse(); +		GameState.setMarsLockBroken(true); +		GameState.setMarsLockFrozen(false); +		startExtraLongSequence(kMars57OpenPanel, kMars57OpenPanelChoices, kExtraCompletedFlag, kFilterNoInput); +	} else if (inColorMatchingGame()) { +		_bombFuse.stopFuse(); +		_guessObject.disposeReactorGuess(); +		_undoPict.deallocateSurface(); +		_guessHistory.disposeReactorHistory(); +		_choiceHighlight.disposeReactorChoiceHighlight(); +		startExtraSequence(kMars57GameSolved, kExtraCompletedFlag, kFilterNoInput); +	} +} + +Common::String Mars::getSoundSpotsName() { +	return "Sounds/Mars/Mars Spots"; +} + +Common::String Mars::getNavMovieName() { +	return "Images/Mars/Mars.movie"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/mars.h b/engines/pegasus/neighborhood/mars/mars.h new file mode 100644 index 0000000000..9aca10a703 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/mars.h @@ -0,0 +1,242 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_MARS_H +#define PEGASUS_NEIGHBORHOOD_MARS_MARS_H + +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/energybeam.h" +#include "pegasus/neighborhood/mars/gravitoncannon.h" +#include "pegasus/neighborhood/mars/planetmover.h" +#include "pegasus/neighborhood/mars/reactor.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/shuttleenergymeter.h" +#include "pegasus/neighborhood/mars/shuttlehud.h" +#include "pegasus/neighborhood/mars/spacejunk.h" +#include "pegasus/neighborhood/mars/tractorbeam.h" + +namespace Pegasus { + +class InventoryItem; +class Mars; + +enum MarsTimerCode { +	kMarsLaunchTubeReached, +	kMarsCanyonChaseFinished, +	kMarsSpaceChaseFinished // Player ran out of time... +}; + +struct MarsTimerEvent { +	Mars *mars; +	MarsTimerCode event; +}; + +enum ShuttleWeaponSelection { +	kNoWeapon, +	kEnergyBeam, +	kGravitonCannon, +	kTractorBeam +}; + +class Mars : public Neighborhood { +friend void robotTimerExpiredFunction(FunctionPtr *, void *); +friend void lockThawTimerExpiredFunction(FunctionPtr *, void *); +friend void bombTimerExpiredFunction(FunctionPtr *, void *); +friend void bombTimerExpiredInGameFunction(FunctionPtr *, void *); +friend void airStageExpiredFunction(FunctionPtr *, void *); +friend void marsTimerFunction(FunctionPtr *, void *); + +public: +	Mars(InputHandler *, PegasusEngine *); +	virtual ~Mars(); + +	void flushGameState(); + +	virtual uint16 getDateResID() const; + +	virtual AirQuality getAirQuality(const RoomID); + +	void checkAirMask(); + +	void showBigExplosion(const Common::Rect &, const DisplayOrder); +	void showLittleExplosion(const Common::Rect &, const DisplayOrder); +	void hitByJunk(); +	void decreaseRobotShuttleEnergy(const int, Common::Point impactPoint); +	void setUpNextDropTime(); + +	Common::String getBriefingMovie(); +	Common::String getEnvScanMovie(); +	uint getNumHints(); +	Common::String getHintMovie(uint); + +	virtual void shieldOn(); +	virtual void shieldOff(); + +	void checkContinuePoint(const RoomID, const DirectionConstant); + +	void setSoundFXLevel(const uint16); + +	bool canSolve(); +	void doSolve(); + +	bool inColorMatchingGame(); + +protected: +	enum { +		kMarsPrivatePodStorageOpenFlag, +		kMarsPrivatePodTurnLeftFlag, +		kMarsPrivatePodTurnRightFlag, +		kMarsPrivateRobotTiredOfWaitingFlag, +		kMarsPrivatePlatformZoomedInFlag, +		kMarsPrivateBombExposedFlag, +		kMarsPrivateDraggingBombFlag, +		kMarsPrivateInSpaceChaseFlag, +		kMarsPrivateGotMapChipFlag, +		kMarsPrivateGotOpticalChipFlag, +		kMarsPrivateGotShieldChipFlag, +		kNumMarsPrivateFlags +	}; +	 +	void init(); +	void start(); +	void setUpAIRules(); +	void arriveAt(const RoomID, const DirectionConstant); +	void takeItemFromRoom(Item *); +	void dropItemIntoRoom(Item *, Hotspot *); +	void activateHotspots(); +	void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *); +	void clickInHotspot(const Input &, const Hotspot *); +	InputBits getInputFilter(); +	 +	TimeValue getViewTime(const RoomID, const DirectionConstant); +	void getZoomEntry(const HotSpotID, ZoomTable::Entry &); +	void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); +	CanOpenDoorReason canOpenDoor(DoorTable::Entry &); +	void openDoor(); +	void closeDoorOffScreen(const RoomID, const DirectionConstant); +	int16 getStaticCompassAngle(const RoomID, const DirectionConstant); +	void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); +	void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); +	void turnTo(const DirectionConstant); +	void receiveNotification(Notification *, const NotificationFlags); +	void doorOpened(); +	void setUpReactorEnergyDrain(); +	Hotspot *getItemScreenSpot(Item *, DisplayElement *); +	void lockThawed(); +	void robotTiredOfWaiting(); +	 +	void setUpReactorLevel1(); +	void setUpNextReactorLevel(); +	void makeColorSequence(); +	void doUndoOneGuess(); +	void doReactorGuess(int32 guess); +	void bombExplodesInGame(); +	void didntFindBomb(); +	CanMoveForwardReason canMoveForward(ExitTable::Entry &); +	void cantMoveThatWay(CanMoveForwardReason); +	void moveForward(); +	void bumpIntoWall(); +	void turnLeft(); +	void turnRight(); +	void airStageExpired(); +	void loadAmbientLoops(); +	void checkAirlockDoors(); +	void pickedUpItem(Item *item); +	void cantOpenDoor(CanOpenDoorReason); +	void launchMaze007Robot(); +	void launchMaze015Robot(); +	void launchMaze101Robot(); +	void launchMaze104Robot(); +	void launchMaze133Robot(); +	void launchMaze136Robot(); +	void launchMaze184Robot(); +	void timerExpired(const uint32); +	void spotCompleted(); + +	void doCanyonChase(void); +	void startMarsTimer(TimeValue, TimeScale, MarsTimerCode); +	void marsTimerExpired(MarsTimerEvent &); +	void throwAwayMarsShuttle(); +	void startUpFromFinishedSpaceChase(); +	void startUpFromSpaceChase(); +	void transportToRobotShip(); +	void spaceChaseClick(const Input &, const HotSpotID); +	void updateCursor(const Common::Point, const Hotspot *); + +	Common::String getSoundSpotsName(); +	Common::String getNavMovieName(); +	 +	InventoryItem *_attackingItem; +	FuseFunction _bombFuse; +	FuseFunction _noAirFuse; +	FuseFunction _utilityFuse; +	FlagsArray<byte, kNumMarsPrivateFlags> _privateFlags; +	uint _reactorStage, _nextGuess; +	int32 _currentGuess[3]; +	ReactorGuess _guessObject; +	Picture _undoPict; +	ReactorHistory _guessHistory; +	ReactorChoiceHighlight _choiceHighlight; + +	Picture _shuttleInterface1; +	Picture _shuttleInterface2; +	Picture _shuttleInterface3; +	Picture _shuttleInterface4; +	Movie _canyonChaseMovie; + +	MarsTimerEvent _marsEvent; + +	Movie _leftShuttleMovie; +	Movie _rightShuttleMovie; +	Movie _lowerLeftShuttleMovie; +	Movie _lowerRightShuttleMovie; +	Movie _centerShuttleMovie; +	Movie _upperLeftShuttleMovie; +	Movie _upperRightShuttleMovie; +	Movie _leftDamageShuttleMovie; +	Movie _rightDamageShuttleMovie; +	ShuttleEnergyMeter _shuttleEnergyMeter; +	Movie _planetMovie; +	PlanetMover _planetMover; +	RobotShip _robotShip; +	ShuttleHUD _shuttleHUD; +	TractorBeam _tractorBeam; +	SpaceJunk _junk; +	EnergyBeam _energyBeam; +	GravitonCannon _gravitonCannon; +	Hotspot _energyChoiceSpot; +	Hotspot _gravitonChoiceSpot; +	Hotspot _tractorChoiceSpot; +	Hotspot _shuttleViewSpot; +	Hotspot _shuttleTransportSpot; +	ShuttleWeaponSelection _weaponSelection; +	ScalingMovie _explosions; +	NotificationCallBack _explosionCallBack; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/planetmover.cpp b/engines/pegasus/neighborhood/mars/planetmover.cpp new file mode 100644 index 0000000000..ef26a7b573 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/planetmover.cpp @@ -0,0 +1,104 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/movie.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/hermite.h" +#include "pegasus/neighborhood/mars/planetmover.h" +#include "pegasus/neighborhood/mars/shuttleenergymeter.h" + +namespace Pegasus { + +static const TimeScale kRovingScale = kTractorBeamScale; +static const TimeValue kRovingTime = kTenSeconds * kRovingScale; +static const TimeValue kRovingSlop = kTwoSeconds * kRovingScale; + +static const CoordType kMaxVelocity = 20; + +PlanetMover::PlanetMover() { +	setScale(kRovingScale); +	_dropping = false; +	_planetMovie = 0; +} + +void PlanetMover::startMoving(Movie *planetMovie) { +	_planetMovie = planetMovie; +	_p4 = kPlanetStartTop; +	_r4 = ((PegasusEngine *)g_engine)->getRandomNumber(kMaxVelocity - 1); +	if (_r4 + _p4 < kPlanetStopTop) +		_r4 = kPlanetStopTop - _p4; +	newDestination(); +} + +void PlanetMover::stopMoving() { +	stop(); +} + +void PlanetMover::dropPlanetOutOfSight() {	 +	stop(); +	CoordType currentLoc = hermite(_p1, _p4, _r1, _r4, _lastTime, _duration); +	CoordType currentV = dHermite(_p1, _p4, _r1, _r4, _lastTime, _duration); +	_p1 = currentLoc; +	_r1 = currentV; +	_p4 = kPlanetStartTop; +	_r4 = 0; +	_duration = kTractorBeamTime - kTractorBeamScale; +	_dropping = true; +	setSegment(0, _duration); +	setTime(0); +	start(); +} + +void PlanetMover::newDestination() { +	_p1 = _p4; +	_r1 = _r4; + +	_p4 = kPlanetStopTop + ((PegasusEngine *)g_engine)->getRandomNumber(kPlanetStartTop - kPlanetStopTop - 1); +	_r4 = ((PegasusEngine *)g_engine)->getRandomNumber(kMaxVelocity - 1); + +	if (_r4 + _p4 < kPlanetStopTop) +		_r4 = kPlanetStopTop - _p4; + +	stop(); +	_duration = kRovingTime + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingSlop - 1); +	setSegment(0, _duration); +	setTime(0); +	start(); +} + +void PlanetMover::timeChanged(const TimeValue) { +	if (_planetMovie) { +		_planetMovie->moveElementTo(kPlanetStartLeft, hermite(_p1, _p4, _r1, _r4, _lastTime, _duration)); +		if (_lastTime == _duration) { +			if (_dropping) +				stop(); +			else +				newDestination(); +		} +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/planetmover.h b/engines/pegasus/neighborhood/mars/planetmover.h new file mode 100644 index 0000000000..cc2c412548 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/planetmover.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_PLANETMOVER_H +#define PEGASUS_NEIGHBORHOOD_MARS_PLANETMOVER_H + +#include "pegasus/timers.h" + +namespace Pegasus { + +class Movie; + +class PlanetMover : IdlerTimeBase { +public: +	PlanetMover(); +	virtual ~PlanetMover() {} + +	void startMoving(Movie *); +	void stopMoving(); + +	void dropPlanetOutOfSight(); + +protected: +	void newDestination(); +	virtual void timeChanged(const TimeValue); +	 +	Movie *_planetMovie; +	CoordType _p1, _p4, _r1, _r4; +	TimeValue _duration; +	bool _dropping; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/reactor.cpp b/engines/pegasus/neighborhood/mars/reactor.cpp new file mode 100644 index 0000000000..478a01c155 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/reactor.cpp @@ -0,0 +1,297 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * aint32 with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/reactor.h" + +namespace Pegasus { + +static const CoordType kCurrentGuessWidth = 121; +static const CoordType kCurrentGuessHeight = 23; + +static const CoordType kOneGuessWidth = 25; +static const CoordType kOneGuessHeight = 23; + +static const ResIDType kReactorChoicesPICTID = 905; + +static const CoordType kCurrentGuessLeft = kNavAreaLeft + 146; +static const CoordType kCurrentGuessTop = kNavAreaTop + 90; + +ReactorGuess::ReactorGuess(const DisplayElementID id) : DisplayElement(id) { +	setBounds(kCurrentGuessLeft, kCurrentGuessTop, kCurrentGuessLeft + kCurrentGuessWidth, +			kCurrentGuessTop + kCurrentGuessHeight); +	setDisplayOrder(kMonitorLayer); +	_currentGuess[0] = -1; +	_currentGuess[1] = -1; +	_currentGuess[2] = -1; +} + +void ReactorGuess::initReactorGuess() { +	_colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorChoicesPICTID); +	startDisplaying(); +	show(); +} + +void ReactorGuess::disposeReactorGuess() { +	stopDisplaying(); +	_colors.deallocateSurface(); +} + +void ReactorGuess::setGuess(int32 a, int32 b, int32 c) { +	_currentGuess[0] = a; +	_currentGuess[1] = b; +	_currentGuess[2] = c; +	triggerRedraw(); +} + +void ReactorGuess::draw(const Common::Rect &) {	 +	if (_colors.isSurfaceValid()) { +		Common::Rect r1(0, 0, kOneGuessWidth, kOneGuessHeight); +		Common::Rect r2 = r1; + +		for (int i = 0; i < 3; i++) { +			if (_currentGuess[i] >= 0) { +				r1.moveTo(kOneGuessWidth * _currentGuess[i], 0); +				r2.moveTo(kCurrentGuessLeft + 48 * i, kCurrentGuessTop); +				_colors.copyToCurrentPortTransparent(r1, r2); +			} +		} +	} +} + +static const CoordType kReactorChoiceHiliteWidth = 166; +static const CoordType kReactorChoiceHiliteHeight = 26; + +static const CoordType kChoiceHiliteLefts[6] = { +	0, +	34, +	34 + 34, +	34 + 34 + 32, +	34 + 34 + 32 + 34, +	34 + 34 + 32 + 34 + 32 +}; + +static const ResIDType kReactorChoiceHilitePICTID = 901; + +static const CoordType kReactorChoiceHiliteLeft = kNavAreaLeft + 116; +static const CoordType kReactorChoiceHiliteTop = kNavAreaTop + 158; + +ReactorChoiceHighlight::ReactorChoiceHighlight(const DisplayElementID id) : DisplayElement(id) { +	setBounds(kReactorChoiceHiliteLeft, kReactorChoiceHiliteTop, kReactorChoiceHiliteLeft + kReactorChoiceHiliteWidth, +			kReactorChoiceHiliteTop + kReactorChoiceHiliteHeight); +	setDisplayOrder(kMonitorLayer); +} + +void ReactorChoiceHighlight::initReactorChoiceHighlight() { +	_colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorChoiceHilitePICTID); +	startDisplaying(); +	show(); +} + +void ReactorChoiceHighlight::disposeReactorChoiceHighlight() { +	stopDisplaying(); +	_colors.deallocateSurface(); +} + +void ReactorChoiceHighlight::draw(const Common::Rect &) {	 +	if (_colors.isSurfaceValid()) { +		for (int i = 0; i < 5; ++i) { +			if (_choices.getFlag(i)) { +				Common::Rect r1(0, 0, kChoiceHiliteLefts[i + 1] - kChoiceHiliteLefts[i], kReactorChoiceHiliteHeight); +				Common::Rect r2 = r1; +				r1.moveTo(kChoiceHiliteLefts[i], 0); +				r2.moveTo(kReactorChoiceHiliteLeft + kChoiceHiliteLefts[i], kReactorChoiceHiliteTop); +				_colors.copyToCurrentPort(r1, r2); +			} +		} +	} +} + +static const CoordType kReactorHistoryWidth = 128; +static const CoordType kReactorHistoryHeight = 168; + +static const CoordType kColorWidths[5] = { 24, 25, 25, 26, 27 }; +static const CoordType kColorHeights[5] = { 14, 15, 17, 17, 19}; + +static const CoordType kHistoryLefts[5][3] = { +	{ 302 + kNavAreaLeft, 329 + kNavAreaLeft, 357 + kNavAreaLeft }, +	{ 302 + kNavAreaLeft, 331 + kNavAreaLeft, 360 + kNavAreaLeft }, +	{ 303 + kNavAreaLeft, 333 + kNavAreaLeft, 363 + kNavAreaLeft }, +	{ 304 + kNavAreaLeft, 335 + kNavAreaLeft, 366 + kNavAreaLeft }, +	{ 305 + kNavAreaLeft, 337 + kNavAreaLeft, 369 + kNavAreaLeft } +}; + +static const CoordType kHistoryTops[5] = { +	39 + kNavAreaTop, +	61 + kNavAreaTop, +	84 + kNavAreaTop, +	110 + kNavAreaTop, +	137 + kNavAreaTop +}; + +static const CoordType kOneAnswerWidth = 35; +static const CoordType kOneAnswerHeight = 27; + +static const CoordType kDigitWidth = 16; +static const CoordType kDigitHeight = 12; + +static const CoordType kCorrectCountLefts[5] = { +	388 + kNavAreaLeft, +	392 + kNavAreaLeft, +	398 + kNavAreaLeft, +	402 + kNavAreaLeft, +	406 + kNavAreaLeft +}; + +static const CoordType kCorrectCountTops[5] = { +	40 + kNavAreaTop, +	62 + kNavAreaTop, +	86 + kNavAreaTop, +	112 + kNavAreaTop, +	140 + kNavAreaTop +}; + +static const ResIDType kReactorDigitsPICTID = 902; +static const ResIDType kReactorHistoryPICTID = 903; +static const ResIDType kReactorAnswerPICTID = 904; + +static const CoordType kReactorHistoryLeft = kNavAreaLeft + 302; +static const CoordType kReactorHistoryTop = kNavAreaTop + 39; + +static const CoordType kAnswerLeft = kNavAreaLeft + 304; +static const CoordType kAnswerTop = kNavAreaTop + 180; + +ReactorHistory::ReactorHistory(const DisplayElementID id) : DisplayElement(id) { +	setBounds(kReactorHistoryLeft, kReactorHistoryTop, kReactorHistoryLeft + kReactorHistoryWidth, +			kReactorHistoryTop + kReactorHistoryHeight); +	setDisplayOrder(kMonitorLayer); +	_numGuesses = 0; +	_answer[0] = -1; +	_answer[1] = -1; +	_answer[2] = -1; +	_showAnswer = false; +} + +void ReactorHistory::initReactorHistory() { +	_colors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorHistoryPICTID); +	_digits.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorDigitsPICTID); +	_answerColors.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kReactorAnswerPICTID); +	startDisplaying(); +	show(); +} + +void ReactorHistory::disposeReactorHistory() { +	stopDisplaying(); +	_colors.deallocateSurface(); +} + +void ReactorHistory::addGuess(int32 a, int32 b, int32 c) { +	_history[_numGuesses][0] = a; +	_history[_numGuesses][1] = b; +	_history[_numGuesses][2] = c; +	_numGuesses++; +	triggerRedraw(); +} + +void ReactorHistory::clearHistory() { +	_numGuesses = 0; +	_showAnswer = false; +	triggerRedraw(); +} + +void ReactorHistory::setAnswer(int32 a, int32 b, int32 c) { +	_answer[0] = a; +	_answer[1] = b; +	_answer[2] = c; +} + +void ReactorHistory::showAnswer() { +	_showAnswer = true; +	triggerRedraw(); +} + +bool ReactorHistory::isSolved() {	 +	for (int i = 0; i < _numGuesses; i++) +		if (_history[i][0] == _answer[0] && _history[i][1] == _answer[1] && _history[i][2] == _answer[2]) +			return true; + +	return false; +} + +void ReactorHistory::draw(const Common::Rect &) { +	static const CoordType kColorTops[5] = { +		0, +		kColorHeights[0], +		kColorHeights[0] + kColorHeights[1], +		kColorHeights[0] + kColorHeights[1] + kColorHeights[2], +		kColorHeights[0] + kColorHeights[1] + kColorHeights[2] + kColorHeights[3], +	}; + +	if (_colors.isSurfaceValid() && _digits.isSurfaceValid()) { +		for (int i = 0; i < _numGuesses; ++i) { +			Common::Rect r1(0, 0, kColorWidths[i], kColorHeights[i]); +			Common::Rect r2 = r1; +			Common::Rect r3(0, 0, kDigitWidth, kDigitHeight); +			Common::Rect r4 = r3; +			int correct = 0; + +			for (int j = 0; j < 3; ++j) { +				r1.moveTo(kColorWidths[i] * _history[i][j], kColorTops[i]); +				r2.moveTo(kHistoryLefts[i][j], kHistoryTops[i]); +				_colors.copyToCurrentPortTransparent(r1, r2); + +				if (_history[i][j] == _answer[j]) +					correct++; +			} + +			r3.moveTo(kDigitWidth * correct, 0); +			r4.moveTo(kCorrectCountLefts[i], kCorrectCountTops[i]); +			_digits.copyToCurrentPort(r3, r4); +		} + +		if (_showAnswer && _answerColors.isSurfaceValid()) { +			Common::Rect r1(0, 0, kOneAnswerWidth, kOneAnswerHeight); +			Common::Rect r2 = r1; + +			for (int i = 0; i < 3; i++) { +				r1.moveTo(kOneAnswerWidth * _answer[i], 0); +				r2.moveTo(kAnswerLeft + 34 * i, kAnswerTop); +				_answerColors.copyToCurrentPortTransparent(r1, r2); +			} +		} +	} +} + +int32 ReactorHistory::getCurrentNumCorrect() {	 +	int correct = 0; + +	for (int i = 0; i < 3; i++) +		if (_history[_numGuesses - 1][i] == _answer[i]) +			correct++; + +	return correct; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/reactor.h b/engines/pegasus/neighborhood/mars/reactor.h new file mode 100644 index 0000000000..86338f8266 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/reactor.h @@ -0,0 +1,108 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_REACTOR_H +#define PEGASUS_NEIGHBORHOOD_MARS_REACTOR_H + +#include "pegasus/elements.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class ReactorGuess : public DisplayElement { +public: +	ReactorGuess(const DisplayElementID); +	virtual ~ReactorGuess() {} + +	void initReactorGuess(); +	void disposeReactorGuess(); + +	void setGuess(int32, int32, int32); + +	void draw(const Common::Rect &); + +protected: +	int32 _currentGuess[3]; + +	Surface _colors; +}; + +class ReactorChoiceHighlight : public DisplayElement { +public: +	ReactorChoiceHighlight(const DisplayElementID); +	virtual ~ReactorChoiceHighlight() {} + +	void initReactorChoiceHighlight(); +	void disposeReactorChoiceHighlight(); + +	void resetHighlight() { +		_choices.clearAllFlags(); +		triggerRedraw(); +	} + +	bool choiceHighlighted(uint32 whichChoice) { return _choices.getFlag(whichChoice); } + +	void draw(const Common::Rect &); + +	void highlightChoice(uint32 whichChoice) { +		_choices.setFlag(whichChoice); +		triggerRedraw(); +	} + +protected: +	Surface _colors; +	FlagsArray<byte, 5> _choices; +}; + +class ReactorHistory : public DisplayElement { +public: +	ReactorHistory(const DisplayElementID); +	virtual ~ReactorHistory() {} + +	void initReactorHistory(); +	void disposeReactorHistory(); + +	void draw(const Common::Rect &); + +	void addGuess(int32, int32, int32); +	int32 getNumGuesses() { return _numGuesses; } +	void clearHistory(); +	void setAnswer(int32, int32, int32); +	void showAnswer(); +	bool isSolved(); +	int32 getCurrentNumCorrect(); + +protected: +	Surface _colors, _digits, _answerColors; +	int32 _answer[3]; +	int32 _history[5][3]; +	int32 _numGuesses; +	bool _showAnswer; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/robotship.cpp b/engines/pegasus/neighborhood/mars/robotship.cpp new file mode 100644 index 0000000000..a0ff749910 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/robotship.cpp @@ -0,0 +1,270 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/hermite.h" +#include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/spacechase3d.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +static const TimeScale kRovingScale = kTractorBeamScale; +static const TimeValue kRovingTime = kSixSeconds * kRovingScale; +static const TimeValue kRovingSlop = kThreeSeconds * kRovingScale; + +static const int kNumSpriteColumns = 15; +static const int kNumSpriteRows = 16; + +static const CoordType kInitialLocationLeft = kShuttleWindowLeft - 50; +static const CoordType kInitialLocationTop = kShuttleWindowTop - 50; +static const CoordType kInitialLocationWidth = kShuttleWindowWidth + 100; +static const CoordType kInitialLocationHeight = kShuttleWindowHeight + 100; + +static const CoordType kVelocityVectorLength = 100; +static const CoordType kVelocityVectorSlop = 50; + +static const CoordType kRovingLeft = kShuttleWindowLeft + 20; +static const CoordType kRovingTop = kShuttleWindowTop + 20; +static const CoordType kRovingWidth = kShuttleWindowMidH - kRovingLeft; +static const CoordType kRovingHeight = kShuttleWindowMidV - kRovingTop; + +RobotShip* g_robotShip = 0; + +void timeToDropJunkFunction(FunctionPtr *, void *robotShip) { +	((RobotShip *)robotShip)->timeToDropJunk(); +} + +RobotShip::RobotShip() : _spritesMovie(kNoDisplayElement) { +	g_robotShip = this; +	_shipRange = Common::Rect(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth, +			kShuttleWindowTop + kShuttleWindowHeight); +	setScale(kRovingScale); +	_currentLocation.x = 0; +	_currentLocation.y = 0; +	_snaring = false; +	_dropJunkFuse.setFunctionPtr(&timeToDropJunkFunction, (void *)this); +} + +RobotShip::~RobotShip() { +	g_robotShip = 0; +} + +void RobotShip::initRobotShip() {	 +	_spritesMovie.initFromMovieFile("Images/Mars/Ship.movie", true); +	_spritesMovie.setDisplayOrder(kShuttleRobotShipOrder); +	_spritesMovie.moveElementTo(kPlanetStartLeft, kPlanetStartTop); +	_spritesMovie.startDisplaying(); +	_spritesMovie.show(); + +	Common::Rect r; +	_spritesMovie.getBounds(r); +	_shipWidth = r.width(); +	_shipHeight = r.height(); +	_dead = false; +} + +void RobotShip::cleanUpRobotShip() { +	_dropJunkFuse.stopFuse(); +	_spritesMovie.stopDisplaying(); +	_spritesMovie.releaseMovie(); +} + +void RobotShip::startMoving() { +	if (((PegasusEngine *)g_engine)->getRandomBit()) { +		_p4.x = kInitialLocationLeft + ((PegasusEngine *)g_engine)->getRandomNumber(kInitialLocationWidth - 1); +		if (((PegasusEngine *)g_engine)->getRandomBit()) +			_p4.y = kInitialLocationTop; +		else +			_p4.y = kInitialLocationTop + kInitialLocationHeight; +	} else { +		_p4.y = kInitialLocationTop + ((PegasusEngine *)g_engine)->getRandomNumber(kInitialLocationHeight - 1); +		if (((PegasusEngine *)g_engine)->getRandomBit()) +			_p4.x = kInitialLocationLeft; +		else +			_p4.x = kInitialLocationLeft + kInitialLocationWidth; +	} + +	makeVelocityVector(_p4.x, _p4.y, kShuttleWindowLeft + kShuttleWindowWidth / 2, +			kShuttleWindowTop + kShuttleWindowHeight / 2, _r4); +	newDestination(); +	setUpNextDropTime(); +} + +void RobotShip::killRobotShip() { +	cleanUpRobotShip(); +	_dead = true; +} + +void RobotShip::setUpNextDropTime() { +	if (!isSnared()) { +		_dropJunkFuse.primeFuse(kJunkDropBaseTime + ((PegasusEngine *)g_engine)->getRandomNumber(kJunkDropSlopTime)); +		_dropJunkFuse.lightFuse(); +	} +} + +void RobotShip::timeToDropJunk() {	 +	if (g_spaceJunk) { +		CoordType x, y; +		_spritesMovie.getCenter(x, y); +		g_spaceJunk->launchJunk(((PegasusEngine *)g_engine)->getRandomNumber(24), x, y); +	} +} + +void RobotShip::newDestination() { +	_p1 = _p4; +	_r1 = _r4; + +	_p4.x = kRovingLeft + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingWidth - 1); +	_p4.y = kRovingTop + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingHeight - 1); + +	if (((PegasusEngine *)g_engine)->getRandomNumber(7) < 6) { +		if (!sameSign(_p4.x - kShuttleWindowMidH, kShuttleWindowMidH - _p1.x)) { +			if (sign(_p4.x - kShuttleWindowMidH) > 0) +				_p4.x -= kRovingWidth; +			else +				_p4.x += kRovingWidth; +		} +	} + +	if (((PegasusEngine *)g_engine)->getRandomNumber(7) < 6) { +		if (!sameSign(_p4.y - kShuttleWindowMidV, kShuttleWindowMidV - _p1.y)) { +			if (sign(_p4.y - kShuttleWindowMidV) > 0) +				_p4.y -= kRovingHeight; +			else +				_p4.y += kRovingHeight; +		} +	} +	 +	makeVelocityVector(_p4.x, _p4.y, kShuttleWindowLeft + kShuttleWindowWidth / 2, +			kShuttleWindowTop + kShuttleWindowHeight / 2, _r4); +	stop(); +	_duration = kRovingTime + ((PegasusEngine *)g_engine)->getRandomNumber(kRovingSlop - 1); +	setSegment(0, _duration); +	setTime(0); +	start(); +} + +void RobotShip::moveRobotTo(CoordType x, CoordType y) { +	_currentLocation.x = x; +	_currentLocation.y = y; + +	if (_spritesMovie.isMovieValid()) { +		_spritesMovie.moveElementTo(x - (_shipWidth >> 1), y - (_shipHeight >> 1)); + +		if (x < _shipRange.left) +			x = 0; +		else if (x > _shipRange.right - 1) +			x = _shipRange.width() - 1; +		else +			x -= _shipRange.left; + +		if (y < _shipRange.top) +			y = 0; +		else if (y > _shipRange.bottom - 1) +			y = _shipRange.height() - 1; +		else +			y -= _shipRange.top; + +		x = kNumSpriteColumns * x / _shipRange.width(); +		y = kNumSpriteRows * y / _shipRange.height(); + +		_spritesMovie.setTime(40 * (x + y * kNumSpriteColumns)); +		_spritesMovie.redrawMovieWorld(); +	} +} + +bool RobotShip::pointInShuttle(Common::Point &pt) { +	Common::Rect r; +	_spritesMovie.getBounds(r); + +	int dx = r.width() / 4; +	int dy = r.height() / 6; + +	r.left += dx; +	r.right -= dx; +	r.top += dy; +	r.bottom -= dy; + +	return r.contains(pt); +} + +void RobotShip::hitByEnergyBeam(Common::Point impactPoint) { +	((Mars *)g_neighborhood)->decreaseRobotShuttleEnergy(1, impactPoint); +	setGlowing(true); +	((PegasusEngine *)g_engine)->delayShell(1, 3); +	setGlowing(false); +} + +void RobotShip::hitByGravitonCannon(Common::Point impactPoint) { +	GameState.setMarsHitRobotWithCannon(true); +	((Mars *)g_neighborhood)->decreaseRobotShuttleEnergy(6, impactPoint); +} + +void RobotShip::snareByTractorBeam() {	 +	_dropJunkFuse.stopFuse(); +	stop(); + +	Common::Point currentV; +	dHermite(_p1, _p4, _r1, _r4, _lastTime, _duration, currentV); + +	_p1 = _currentLocation; +	_r1 = currentV; +	_p4.x = kShuttleWindowMidH; +	_p4.y = kShuttleWindowMidV; +	_r4.x = 0; +	_r4.y = 0; +	_duration = kTractorBeamTime; +	_snaring = true; +	setSegment(0, _duration); +	setTime(0); +	start(); +} + +void RobotShip::timeChanged(const TimeValue) { +	Common::Point newLocation; +	hermite(_p1, _p4, _r1, _r4, _lastTime, _duration, newLocation); +	moveRobotTo(newLocation.x, newLocation.y); + +	if (_lastTime == _duration) { +		if (_snaring) +			stop(); +		else +			newDestination(); +	} +} + +void RobotShip::makeVelocityVector(CoordType x1, CoordType y1, CoordType x2, CoordType y2, Common::Point &vector) {	 +	CoordType length = ((PegasusEngine *)g_engine)->getRandomNumber(kVelocityVectorSlop - 1) + kVelocityVectorLength; +	vector.x = x2 - x1; +	vector.y = y2 - y1; +	float oldLength = sqrt((float)(vector.x * vector.x + vector.y * vector.y)); +	vector.x = (int)(vector.x * length / oldLength); +	vector.y = (int)(vector.y * length / oldLength); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/robotship.h b/engines/pegasus/neighborhood/mars/robotship.h new file mode 100644 index 0000000000..b668e8f154 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/robotship.h @@ -0,0 +1,86 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_ROBOTSHIP_H +#define PEGASUS_NEIGHBORHOOD_MARS_ROBOTSHIP_H + +#include "pegasus/movie.h" + +namespace Pegasus { + +static const CoordType kShuttleMovieWidth = 114; +static const CoordType kShuttleMovieHeight = 42; + +class RobotShip : IdlerTimeBase { +friend void timeToDropJunkFunction(FunctionPtr *, void *); + +public: +	RobotShip(); +	virtual ~RobotShip(); + +	void initRobotShip(); +	void cleanUpRobotShip(); + +	void startMoving(); + +	void killRobotShip(); + +	bool pointInShuttle(Common::Point&); + +	void hitByEnergyBeam(Common::Point impactPoint); +	void hitByGravitonCannon(Common::Point impactPoint); + +	void getShuttleBounds(Common::Rect &r) { _spritesMovie.getBounds(r); } + +	void setGlowing(const bool glowing) { _spritesMovie.setGlowing(glowing); } + +	void snareByTractorBeam(); +	bool isSnared() { return _snaring && getTime() == _duration; } + +	bool isDead() { return _dead; } + +	void setUpNextDropTime(); + +protected: +	void newDestination(); +	void moveRobotTo(CoordType, CoordType); +	void timeToDropJunk(); +	virtual void timeChanged(const TimeValue); +	void makeVelocityVector(CoordType, CoordType, CoordType, CoordType, Common::Point &); + +	GlowingMovie _spritesMovie; +	Common::Rect _shipRange; +	int _shipWidth, _shipHeight; +	Common::Point _p1, _p4, _r1, _r4, _currentLocation; +	FuseFunction _dropJunkFuse; +	TimeValue _duration; +	bool _snaring, _dead; +}; + +extern RobotShip *g_robotShip; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp b/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp new file mode 100644 index 0000000000..21bb1fb700 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleenergymeter.cpp @@ -0,0 +1,116 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/shuttleenergymeter.h" + +namespace Pegasus { + +ShuttleEnergyMeter::ShuttleEnergyMeter() : FaderAnimation(kNoDisplayElement) { +	setBounds(kShuttleEnergyLeft, kShuttleEnergyTop, kShuttleEnergyLeft + kShuttleEnergyWidth, +			kShuttleEnergyTop + kShuttleEnergyHeight); +	setDisplayOrder(kShuttleStatusOrder); +	setFaderValue(0); +} + +void ShuttleEnergyMeter::initShuttleEnergyMeter() { +	_meterImage.getImageFromPICTFile("Images/Mars/Shuttle Energy.pict"); +	_lowWarning.getImageFromPICTFile("Images/Mars/Shuttle Low Energy.pict"); +	startDisplaying(); +	show(); +} + +void ShuttleEnergyMeter::disposeShuttleEnergyMeter() { +	stopFader(); +	hide(); +	stopDisplaying(); +	_meterImage.deallocateSurface(); +	_lowWarning.deallocateSurface(); +} + +void ShuttleEnergyMeter::draw(const Common::Rect &) { +	int32 currentValue = getFaderValue(); + +	Common::Rect r1, r2, bounds; +	getBounds(bounds); + +	if (currentValue < kLowShuttleEnergy) { +		_lowWarning.getSurfaceBounds(r1); +		r2 = r1; +		r2.moveTo(bounds.left, bounds.top); +		_lowWarning.copyToCurrentPort(r1, r2); +	} + +	_meterImage.getSurfaceBounds(r1); +	r1.right = r1.left + r1.width() * currentValue / kFullShuttleEnergy; +	r2 = r1; +	r2.moveTo(bounds.left + 102, bounds.top + 6); +	_meterImage.copyToCurrentPort(r1, r2); +} + +void ShuttleEnergyMeter::powerUpMeter() { +	FaderMoveSpec moveSpec; +	moveSpec.makeTwoKnotFaderSpec(kThirtyTicksPerSecond, 0, 0, 45, kFullShuttleEnergy); +	startFader(moveSpec); +} + +void ShuttleEnergyMeter::setEnergyValue(const int32 value) { +	stopFader(); +	FaderMoveSpec moveSpec; +	moveSpec.makeTwoKnotFaderSpec(kFifteenTicksPerSecond, value * 3, value, kFullShuttleEnergy * 3, kFullShuttleEnergy); +	startFader(moveSpec); +} + +void ShuttleEnergyMeter::drainForTractorBeam() {	 +	stopFader(); +	TimeValue startTime = 0, stopTime; +	int32 startValue = getFaderValue(), stopValue; + +	if (startValue < kTractorBeamEnergy) { +		stopTime = startValue * kTractorBeamTime / kTractorBeamEnergy; +		stopValue = 0; +	} else { +		stopTime = kTractorBeamTime; +		stopValue = startValue - kTractorBeamEnergy; +	} + +	FaderMoveSpec moveSpec; +	moveSpec.makeTwoKnotFaderSpec(kTractorBeamScale, startTime, startValue, stopTime, stopValue); +	startFader(moveSpec); +} + +int32 ShuttleEnergyMeter::getEnergyValue() const { +	return getFaderValue(); +} + +void ShuttleEnergyMeter::dropEnergyValue(const int32 delta) { +	setEnergyValue(getFaderValue() - delta); +} + +bool ShuttleEnergyMeter::enoughEnergyForTractorBeam() const { +	return getEnergyValue() >= kTractorBeamEnergy; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/shuttleenergymeter.h b/engines/pegasus/neighborhood/mars/shuttleenergymeter.h new file mode 100644 index 0000000000..51161e094e --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleenergymeter.h @@ -0,0 +1,73 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEENERGYMETER_H +#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEENERGYMETER_H + +#include "pegasus/fader.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +static const int32 kFullShuttleEnergy = 100; +// Low is 20 percent +static const int32 kLowShuttleEnergy = kFullShuttleEnergy * 20 / 100; + +static const int32 kMinDampingEnergy = 15; +static const int32 kMinGravitonEnergy = 63; + +static const TimeScale kTractorBeamScale = kFifteenTicksPerSecond; +static const TimeValue kTractorBeamTime = kFiveSeconds * kTractorBeamScale; +static const int32 kTractorBeamEnergy = kLowShuttleEnergy; + +class ShuttleEnergyMeter : public FaderAnimation { +public: +	ShuttleEnergyMeter(); +	~ShuttleEnergyMeter() {} + +	void initShuttleEnergyMeter(); +	void disposeShuttleEnergyMeter(); + +	void powerUpMeter(); + +	void setEnergyValue(const int32); +	int32 getEnergyValue() const; + +	void dropEnergyValue(const int32); + +	void drainForTractorBeam(); + +	bool enoughEnergyForTractorBeam() const; + +	void draw(const Common::Rect &); + +protected: +	Surface _meterImage; +	Surface _lowWarning; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/shuttlehud.cpp b/engines/pegasus/neighborhood/mars/shuttlehud.cpp new file mode 100644 index 0000000000..14f5b88319 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttlehud.cpp @@ -0,0 +1,246 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/shuttlehud.h" + +namespace Pegasus { + +static const CoordType kHUDTargetGridLeft = kShuttleWindowLeft + 16; +static const CoordType kHUDTargetGridTop = kShuttleWindowTop + 8; +static const CoordType kHUDTargetGridWidth = 328; +static const CoordType kHUDTargetGridHeight = 206; + +static const CoordType kHUDRS232Left = kHUDTargetGridLeft + 264; +static const CoordType kHUDRS232Top = kHUDTargetGridTop + 2; + +static const CoordType kHUDLockLeft = kShuttleWindowLeft + 101; +static const CoordType kHUDLockTop = kShuttleWindowTop + 49; +static const CoordType kHUDLockWidth = 145; +static const CoordType kHUDLockHeight = 124; + +static const CoordType kTractorLockWidth = 50; +static const CoordType kTractorLockHeight = 30; + +static const CoordType kTractorLockLeft = kShuttleWindowMidH - kTractorLockWidth / 2; +static const CoordType kTractorLockTop = kShuttleWindowMidV - kTractorLockHeight / 2; +static const CoordType kTractorLockRight = kTractorLockLeft + kTractorLockWidth; +static const CoordType kTractorLockBottom = kTractorLockTop + kTractorLockHeight; + +static const uint16 s_RS232Data[] = { +	0xF0E1, 0xCE70, +	0xF9E1, 0xEF78, +	0x4900, 0x2108, +	0x79C0, 0xE738, +	0x70E1, 0xC770, +	0x5821, 0x0140, +	0x4DE1, 0xEF78, +	0x45C1, 0xEE78 +}; + +static const uint16 s_lockData[] = { +	0xE007, 0xFE1F, 0xF8E0, 0x7000, +	0xE00F, 0xFF3F, 0xFCE0, 0xE000, +	0xE00E, 0x0738, 0x1CE1, 0xC000, +	0xE00E, 0x0738, 0x00FF, 0x8000, +	0xE00E, 0x0738, 0x00FF, 0x0000, +	0xE00E, 0x0738, 0x00E3, 0x8000, +	0xE00E, 0x0738, 0x1CE1, 0xC000, +	0xFFCF, 0xFF3F, 0xFCE0, 0xE000, +	0xFFC7, 0xFE1F, 0xF8E0, 0x7000 +}; + +#define drawHUDLockLine(x1, y1, x2, y2, penX, penY, color) \ +	screen->drawThickLine((x1) + kHUDLockLeft, (y1) + kHUDLockTop, \ +			(x2) + kHUDLockLeft, (y2) + kHUDLockTop, penX, penY, color) + +#define drawHUDLockArrows(offset, color) \ +	drawHUDLockLine(63, 0 + (offset), 68, 5 + (offset), 1, 3, color); \ +	drawHUDLockLine(71, 8 + (offset), 77, 14 + (offset), 1, 3, color); \ +	drawHUDLockLine(78, 14 + (offset), 84, 8 + (offset), 1, 3, color); \ +	drawHUDLockLine(87, 5 + (offset), 92, 0 + (offset), 1, 3, color); \ +	drawHUDLockLine(63, 121 - (offset), 68, 116 - (offset), 1, 3, color); \ +	drawHUDLockLine(71, 113 - (offset), 77, 107 - (offset), 1, 3, color); \ +	drawHUDLockLine(78, 107 - (offset), 84, 113 - (offset), 1, 3, color); \ +	drawHUDLockLine(87, 116 - (offset), 92, 121 - (offset), 1, 3, color); \ +\ +	drawHUDLockLine(13 + (offset), 47, 18 + (offset), 52, 3, 1, color); \ +	drawHUDLockLine(21 + (offset), 55, 27 + (offset), 61, 3, 1, color); \ +	drawHUDLockLine(27 + (offset), 62, 21 + (offset), 68, 3, 1, color); \ +	drawHUDLockLine(18 + (offset), 71, 13 + (offset), 76, 3, 1, color); \ +	drawHUDLockLine(142 - (offset), 47, 137 - (offset), 52, 3, 1, color); \ +	drawHUDLockLine(134 - (offset), 55, 128 - (offset), 61, 3, 1, color); \ +	drawHUDLockLine(128 - (offset), 62, 134 - (offset), 68, 3, 1, color); \ +	drawHUDLockLine(137 - (offset), 71, 142 - (offset), 76, 3, 1, color) + +ShuttleHUD::ShuttleHUD() : DisplayElement(kNoDisplayElement) { +	_lightGreen = g_system->getScreenFormat().RGBToColor(0, 204, 0); +	_gridDarkGreen = g_system->getScreenFormat().RGBToColor(0, 85, 0); +	_lockDarkGreen1 = g_system->getScreenFormat().RGBToColor(0, 68, 0); +	_lockDarkGreen2 = g_system->getScreenFormat().RGBToColor(0, 65, 0); + +	_targetLocked = false; +	setBounds(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth, +			kShuttleWindowTop + kShuttleWindowHeight); +	setDisplayOrder(kShuttleHUDOrder); +} + +void ShuttleHUD::initShuttleHUD() { +	startDisplaying(); +	startIdling(); +} + +void ShuttleHUD::cleanUpShuttleHUD() { +	stopIdling(); +	stopDisplaying(); +} + +void ShuttleHUD::showTargetGrid() { +	show(); +} + +void ShuttleHUD::hideTargetGrid() { +	hide(); +	unlockOnTarget(); +} + +void ShuttleHUD::useIdleTime() {	 +	if (isVisible()) { +		Common::Rect r; +		g_robotShip->getShuttleBounds(r); +		if (r.left < kTractorLockRight && r.right > kTractorLockLeft && r.top < kTractorLockBottom && r.bottom > kTractorLockTop) +			lockOnTarget(); +		else +			unlockOnTarget(); +	} +} + +void ShuttleHUD::lockOnTarget() { +	if (!_targetLocked) { +		_targetLocked = true; +		triggerRedraw(); +	} +} + +void ShuttleHUD::unlockOnTarget() { +	if (_targetLocked) { +		_targetLocked = false; +		triggerRedraw(); +	} +} + +void ShuttleHUD::draw(const Common::Rect &) { +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + +	for (int y = 0; y < 35; y++) { +		Common::Rect r; + +		if (y & 1) { +			if (y == 17) { +				r = Common::Rect(0, 0, 4, 2); +				r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _gridDarkGreen); +				r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 12, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _gridDarkGreen); + +				r = Common::Rect(0, 0, 6, 2); +				r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _lightGreen); +				r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 8, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _lightGreen); + +				r = Common::Rect(0, 0, 23, 2); +				r.moveTo(kHUDTargetGridLeft + 12, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _lightGreen); +				r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 35, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _lightGreen); +			} else if (y == 1 || y == 15 || y == 19 || y == 33) { +				r = Common::Rect(0, 0, 4, 2); +				r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _gridDarkGreen); +				r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 6, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _gridDarkGreen); + +				r = Common::Rect(0, 0, 15, 2); +				r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _gridDarkGreen); +				r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 23, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _gridDarkGreen); +			} else { +				r = Common::Rect(0, 0, 4, 2); +				r.moveTo(kHUDTargetGridLeft + 2, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _gridDarkGreen); +				r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 6, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _gridDarkGreen); + +				r = Common::Rect(0, 0, 10, 2); +				r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _gridDarkGreen); +				r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 18, y * 6 + kHUDTargetGridTop); +				screen->fillRect(r, _gridDarkGreen); +			} +		} else { +			r = Common::Rect(0, 0, 2, 2); +			r.moveTo(kHUDTargetGridLeft, y * 6 + kHUDTargetGridTop); +			screen->fillRect(r, _gridDarkGreen); +			r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 2, y * 6 + kHUDTargetGridTop); +			screen->fillRect(r, _gridDarkGreen); + +			r = Common::Rect(0, 0, 4, 2); +			r.moveTo(kHUDTargetGridLeft + 8, y * 6 + kHUDTargetGridTop); +			screen->fillRect(r, _gridDarkGreen); +			r.moveTo(kHUDTargetGridLeft + kHUDTargetGridWidth - 12, y * 6 + kHUDTargetGridTop); +			screen->fillRect(r, _gridDarkGreen); +		} +	} + +	drawOneBitImageOr(screen, s_RS232Data, 2, Common::Rect(kHUDRS232Left, kHUDRS232Top, +			kHUDRS232Left + 29, kHUDRS232Top + 8), _gridDarkGreen); + +	if (_targetLocked) { +		drawHUDLockArrows(0, _lockDarkGreen2); +		drawHUDLockArrows(12, _lockDarkGreen1); +		drawHUDLockArrows(24, _lightGreen); +		drawOneBitImageOr(screen, s_lockData, 4, Common::Rect(kHUDLockLeft, kHUDLockTop + 115, +				kHUDLockLeft + 52, kHUDLockTop + 115 + 9), _lightGreen); +	} +} + +void ShuttleHUD::drawOneBitImageOr(Graphics::Surface *screen, const uint16 *data, int pitch, const Common::Rect &bounds, uint32 color) { +	for (int y = 0; y < bounds.height(); y++) { +		for (int x = 0; x < bounds.width(); x++) { +			if ((data[y * pitch + x / 16] & (1 << (15 - (x % 16)))) != 0) { +				if (screen->format.bytesPerPixel == 2) +					WRITE_UINT16((byte *)screen->getBasePtr(x + bounds.left, y + bounds.top), color); +				else +					WRITE_UINT32((byte *)screen->getBasePtr(x + bounds.left, y + bounds.top), color); +			} +		} +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/shuttlehud.h b/engines/pegasus/neighborhood/mars/shuttlehud.h new file mode 100644 index 0000000000..dc1c7598b5 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttlehud.h @@ -0,0 +1,61 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEHUD_H +#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEHUD_H + +#include "pegasus/elements.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class ShuttleHUD : public DisplayElement, public Idler { +public: +	ShuttleHUD(); +	virtual ~ShuttleHUD() {} + +	void showTargetGrid(); +	void hideTargetGrid(); + +	void initShuttleHUD(); +	void cleanUpShuttleHUD(); + +	bool isTargetLocked() { return _targetLocked; } + +	void draw(const Common::Rect &); + +protected: +	void useIdleTime(); +	void lockOnTarget(); +	void unlockOnTarget(); +	void drawOneBitImageOr(Graphics::Surface *, const uint16 *, int, const Common::Rect &, uint32); + +	bool _targetLocked; +	uint32 _lightGreen, _gridDarkGreen, _lockDarkGreen1, _lockDarkGreen2; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/shuttleweapon.cpp b/engines/pegasus/neighborhood/mars/shuttleweapon.cpp new file mode 100644 index 0000000000..0151a26f29 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleweapon.cpp @@ -0,0 +1,129 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/robotship.h" +#include "pegasus/neighborhood/mars/shuttleweapon.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +ShuttleWeapon::ShuttleWeapon() : IdlerAnimation(kNoDisplayElement) { +	setScale(kShuttleWeaponScale); +	_weaponDuration = kShuttleWeaponScale * 2; +	setSegment(0, _weaponDuration); +	setBounds(kShuttleWindowLeft, kShuttleWindowTop, kShuttleWindowLeft + kShuttleWindowWidth, +			kShuttleWindowTop + kShuttleWindowHeight); +	setDisplayOrder(kShuttleWeaponFrontOrder); +} + +void ShuttleWeapon::initShuttleWeapon() { +	startDisplaying(); +} + +void ShuttleWeapon::cleanUpShuttleWeapon() { +	stop(); +	hide(); +	stopDisplaying(); +} + +bool ShuttleWeapon::canFireWeapon() { +	return !isRunning(); +} + +void ShuttleWeapon::fireWeapon(const CoordType hStop, const CoordType vStop) {	 +	if (!isRunning()) { +		stop(); +		setTime(0); +		show(); + +		Common::Point pt2D(hStop, vStop); +		project2DTo3D(pt2D, kShuttleDistance, _weaponTarget); +		_weaponTime = 0; +		setDisplayOrder(kShuttleWeaponFrontOrder); +		start(); +	} +} + +void ShuttleWeapon::updateWeaponPosition() { +	_weaponTime = (float)_lastTime / _weaponDuration; +	linearInterp(_weaponOrigin, _weaponTarget, _weaponTime, _weaponLocation); + +	if (_weaponTime == 1.0) { +		stop(); +		hide(); +	} else { +		triggerRedraw(); +	} +} + +void ShuttleWeapon::timeChanged(const TimeValue) { +	updateWeaponPosition(); + +	bool hit = false; +	Common::Point impactPoint; + +	if (g_spaceJunk->isJunkFlying()) { +		hit = collisionWithJunk(impactPoint); +		if (hit) { +			stop(); +			hide(); +			hitJunk(impactPoint); +		} +	} + +	if (!hit && _weaponTime == 1.0 && collisionWithShuttle(impactPoint)) +		hitShuttle(impactPoint); +} + +bool ShuttleWeapon::collisionWithJunk(Common::Point &impactPoint) { +	if (getDisplayOrder() == kShuttleWeaponFrontOrder) { +		Point3D junkPosition; +		g_spaceJunk->getJunkPosition(junkPosition); + +		if (junkPosition.z < _weaponLocation.z) { +			setDisplayOrder(kShuttleWeaponBackOrder); +			project3DTo2D(_weaponLocation, impactPoint); +			return g_spaceJunk->pointInJunk(impactPoint); +		} +	} + +	return false; +} + +bool ShuttleWeapon::collisionWithShuttle(Common::Point &impactPoint) { +	project3DTo2D(_weaponLocation, impactPoint); +	return g_robotShip->pointInShuttle(impactPoint); +} + +void ShuttleWeapon::hitJunk(Common::Point impactPoint) { +	g_spaceJunk->hitByEnergyBeam(impactPoint); +} + +void ShuttleWeapon::hitShuttle(Common::Point impactPoint) { +	g_robotShip->hitByEnergyBeam(impactPoint); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/shuttleweapon.h b/engines/pegasus/neighborhood/mars/shuttleweapon.h new file mode 100644 index 0000000000..38529c8919 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/shuttleweapon.h @@ -0,0 +1,68 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEWEAPON_H +#define PEGASUS_NEIGHBORHOOD_MARS_SHUTTLEWEAPON_H + +#include "pegasus/elements.h" +#include "pegasus/neighborhood/mars/spacechase3d.h" + +namespace Pegasus { + +// Can fire multiple times? +// For now, no... +// clone2727 adds: And now forever + +static const TimeScale kShuttleWeaponScale = kFifteenTicksPerSecond; + +class ShuttleWeapon : public IdlerAnimation { +public: +	ShuttleWeapon(); +	virtual ~ShuttleWeapon() {} + +	virtual void initShuttleWeapon(); +	virtual void cleanUpShuttleWeapon(); + +	virtual void fireWeapon(const CoordType, const CoordType); + +	bool canFireWeapon(); + +protected: +	void timeChanged(const TimeValue); +	virtual void updateWeaponPosition(); +	virtual bool collisionWithJunk(Common::Point &impactPoint); +	bool collisionWithShuttle(Common::Point &impactPoint); +	virtual void hitJunk(Common::Point impactPoint); +	virtual void hitShuttle(Common::Point impactPoint); + +	Point3D _weaponOrigin, _weaponTarget; +	Point3D _weaponLocation; +	float _weaponTime; +	TimeValue _weaponDuration; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/spacechase3d.cpp b/engines/pegasus/neighborhood/mars/spacechase3d.cpp new file mode 100644 index 0000000000..05f8233763 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacechase3d.cpp @@ -0,0 +1,106 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/mars/spacechase3d.h" + +namespace Pegasus { + +void project3DTo2D(const Point3D &pt3D, Common::Point &pt2D) { +	pt2D.x = (int)convertSpaceXToScreenH(pt3D.x, pt3D.z); +	pt2D.y = (int)convertSpaceYToScreenV(pt3D.y, pt3D.z); +} + +void project2DTo3D(const Common::Point &pt2D, const float screenDistance, Point3D &pt3D) { +	pt3D.x = convertScreenHToSpaceX(pt2D.x, screenDistance); +	pt3D.y = convertScreenVToSpaceY(pt2D.y, screenDistance); +	pt3D.z = screenDistance; +} + +void linearInterp(const Point3D &pt1, const Point3D &pt2, const float t, Point3D &pt3) { +	pt3.x = pt1.x + (pt2.x - pt1.x) * t; +	pt3.y = pt1.y + (pt2.y - pt1.y) * t; +	pt3.z = pt1.z + (pt2.z - pt1.z) * t; +} + +void linearInterp(const Point3D &pt1, const float x2, const float y2, const float z2, const float t, Point3D &pt3) { +	pt3.x = pt1.x + (x2 - pt1.x) * t; +	pt3.y = pt1.y + (y2 - pt1.y) * t; +	pt3.z = pt1.z + (z2 - pt1.z) * t; +} + +void linearInterp(const float x1, const float y1, const float z1, const Point3D &pt2, const float t, Point3D &pt3) { +	pt3.x = x1 + (pt2.x - x1) * t; +	pt3.y = y1 + (pt2.y - y1) * t; +	pt3.z = z1 + (pt2.z - z1) * t; +} + +void linearInterp(const float x1, const float y1, const float z1, const float x2, const float y2, const float z2, +		const float t, Point3D &pt3) { +	pt3.x = x1 + (x2 - x1) * t; +	pt3.y = y1 + (y2 - y1) * t; +	pt3.z = z1 + (z2 - z1) * t; +} + +void linearInterp(const Common::Point &pt1, const Common::Point &pt2, const float t, Common::Point &pt3) { +	pt3.x = (int)(pt1.x + (pt2.x - pt1.x) * t); +	pt3.y = (int)(pt1.y + (pt2.y - pt1.y) * t); +} + +void linearInterp(const Common::Point &pt1, const float h2, const float v2, const float t, Common::Point &pt3) { +	pt3.x = (int)(pt1.x + (h2 - pt1.x) * t); +	pt3.y = (int)(pt1.y + (v2 - pt1.y) * t); +} + +void linearInterp(const float h1, const float v1, const Common::Point &pt2, const float t, Common::Point &pt3) { +	pt3.x = (int)(h1 + (pt2.x - h1) * t); +	pt3.y = (int)(v1 + (pt2.y - v1) * t); +} + +void linearInterp(const float h1, const float v1, const float h2, const float v2, const float t, Common::Point &pt3) { +	pt3.x = (int)(h1 + (h2 - h1) * t); +	pt3.y = (int)(v1 + (v2 - v1) * t); +} + +float linearInterp(const float arg1, const float arg2, const float t) { +	return arg1 + (arg2 - arg1) * t; +} + +bool isNegative(int a) { +	return a < 0; +} + +bool isPositive(int a) { +	return a > 0; +} + +int sign(int a) { +	return isNegative(a) ? -1 : isPositive(a) ? 1 : 0; +} + +bool sameSign(int a, int b) { +	return sign(a) == sign(b); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/spacechase3d.h b/engines/pegasus/neighborhood/mars/spacechase3d.h new file mode 100644 index 0000000000..f6815e69bd --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacechase3d.h @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_SPACECHASE3D_H +#define PEGASUS_NEIGHBORHOOD_MARS_SPACECHASE3D_H + +#include "pegasus/neighborhood/mars/constants.h" + +namespace Pegasus { + +// This is approximately right for a field of view of 72 degrees +// (Should be set to the tangent of FOV). +//static const float kTangentFOV = 0.76254; +static const float kTangentFOV = 1.0; + +// Define these as macros and they can be used to define constants... +#define convertSpaceXToScreenH(x, z) \ +	((x) / (z) * (kScreenWidth / (2 * kTangentFOV)) + kShuttleWindowMidH) + +#define convertSpaceYToScreenV(y, z) \ +	(kShuttleWindowMidV - (y) / (z) * (kScreenWidth / (2 * kTangentFOV))) + +#define convertScreenHToSpaceX(x, d) \ +	(((2.0 * kTangentFOV) / kScreenWidth) * ((float)(x) - kShuttleWindowMidH) * (d)) + +#define convertScreenVToSpaceY(y, d) \ +	(((2.0 * kTangentFOV) / kScreenWidth) *	((float)kShuttleWindowMidV - (y)) * (d)) + +struct Point3D { +	float x, y, z; + +	Point3D() : x(0), y(0), z(0) {} +	Point3D(float x1, float y1, float z1) : x(x1), y(y1), z(z1) {} +	bool operator==(const Point3D &p) const { return x == p.x && y == p.y && z == p.z; } +	bool operator!=(const Point3D &p) const { return x != p.x || y != p.y || z != p.z; } + +	void translate(float dx, float dy, float dz) { +		x += dx; +		y += dy; +		z += dz; +	} +}; + +static const int kScreenWidth = kShuttleWindowWidth; + +bool isNegative(int a); +bool isPositive(int a); +int sign(int a); +bool sameSign(int a, int b); + +void project3DTo2D(const Point3D &pt3D, Common::Point &pt2D); +void project2DTo3D(const Common::Point &pt2D, const float screenDistance, Point3D &pt3D); + +void linearInterp(const Point3D &pt1, const Point3D &pt2, const float t, Point3D &pt3); +void linearInterp(const Point3D &pt1, const float x2, const float y2, const float z2, const float t, Point3D &pt3); +void linearInterp(const float x1, const float y1, const float z1, const Point3D &pt2, const float t, Point3D &pt3); +void linearInterp(const float x1, const float y1, const float z1, const float x2, +		const float y2, const float z2, const float t, Point3D &pt3); + +void linearInterp(const Common::Point &pt1, const Common::Point &pt2, const float t, Common::Point &pt3); +void linearInterp(const Common::Point &pt1, const float h2, const float v2, const float t, Common::Point &pt3); +void linearInterp(const float h1, const float v1, const Common::Point &pt2, const float t, Common::Point &pt3); +void linearInterp(const float h1, const float v1, const float h2, const float v2, const float t, Common::Point &pt3); + +float linearInterp(const float arg1, const float arg2, const float t); + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/spacejunk.cpp b/engines/pegasus/neighborhood/mars/spacejunk.cpp new file mode 100644 index 0000000000..ac8b1a23cc --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacejunk.cpp @@ -0,0 +1,212 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/mars/spacejunk.h" + +namespace Pegasus { + +static const CoordType kMaxBounceSize = 90; +static const CoordType kBounceTargetHRange = 640 - kMaxBounceSize - 2; +static const CoordType kBounceTargetVRange = 480 - kMaxBounceSize - 2; + +static const float kJunkXTarget = 0; +static const float kJunkYTarget = 0; +static const float kJunkZTarget = kJunkMinDistance; + +SpaceJunk *g_spaceJunk = 0; + +SpaceJunk::SpaceJunk(const DisplayElementID id) : ScalingMovie(id) { +	_timer.setScale(kJunkTimeScale); +	_bouncing = false; +	g_spaceJunk = this; +} + +SpaceJunk::~SpaceJunk() { +	g_spaceJunk = 0; +} + +void SpaceJunk::launchJunk(int16 whichJunk, CoordType xOrigin, CoordType yOrigin) {	 +	_bouncing = false; +	TimeValue startTime = whichJunk * 16 * 40; +	TimeValue stopTime = startTime + 16 * 40; + +	_launchPoint = Point3D(convertScreenHToSpaceX(xOrigin, kJunkMaxDistance), +			convertScreenVToSpaceY(yOrigin, kJunkMaxDistance), kJunkMaxDistance); +	startIdling(); +	stop(); +	setFlags(0); +	setSegment(startTime, stopTime); +	setFlags(kLoopTimeBase); +	setTime(startTime); +	start(); +	show(); +	_timer.stop(); +	_timer.setSegment(0, kJunkTravelTime); +	_timer.setTime(0); + +	// Force it to set up correctly from the get-go +	useIdleTime(); + +	_timer.start(); +} + +void SpaceJunk::setCenter(const CoordType centerX, const CoordType centerY) { +	_center.x = centerX; +	_center.y = centerY; + +	Common::Rect r; +	getBounds(r); +	r.moveTo(CLIP<int>(centerX - (r.width() >> 1), 0, 640 - r.width()), CLIP<int>(centerY - (r.height() >> 1), 0, 480 - r.height())); +	setBounds(r); +} + +void SpaceJunk::setScaleSize(const CoordType size) { +	Common::Rect r; +	r.left = _center.x - (size >> 1); +	r.top = _center.y - (size >> 1); +	r.right = r.left + size; +	r.bottom = r.top + size; +	setBounds(r); +} + +void SpaceJunk::useIdleTime() {	 +	if (_bouncing) { +		TimeValue time = _timer.getTime(); +		Common::Point pt; +		pt.x = linearInterp(0, _bounceTime, time, _bounceStart.x, _bounceStop.x); +		pt.y = linearInterp(0, _bounceTime, time, _bounceStart.y, _bounceStop.y); +		CoordType size = linearInterp(0, _bounceTime, time, _bounceSizeStart, _bounceSizeStop); +		setCenter(pt.x, pt.y); +		setScaleSize(size); + +		if (time == _bounceTime) { +			stop(); +			stopIdling(); +			hide(); +			((Mars *)g_neighborhood)->setUpNextDropTime(); +		} +	} else { +		float t = (float)_timer.getTime() / kJunkTravelTime; +		linearInterp(_launchPoint, kJunkXTarget, kJunkYTarget, kJunkZTarget, t, _junkPosition); + +		Common::Point pt2D; +		project3DTo2D(_junkPosition, pt2D); +		setCenter(pt2D.x, pt2D.y); +		setScaleSize((int)(convertSpaceYToScreenV(_junkPosition.y - kJunkSize / 2, _junkPosition.z) -  +				convertSpaceYToScreenV(_junkPosition.y + kJunkSize / 2, _junkPosition.z))); + +		if (t == 1.0) { +			rebound(kCollisionReboundTime); +			((Mars *)g_neighborhood)->hitByJunk(); +		} +	} +} + +bool SpaceJunk::pointInJunk(const Common::Point &pt) { +	Common::Rect r; +	getBounds(r); + +	int dx = r.width() / 4; +	int dy = r.height() / 4; + +	r.left += dx; +	r.right -= dx; +	r.top += dy; +	r.top -= dy; + +	return r.contains(pt); +} + +void SpaceJunk::rebound(const TimeValue reboundTime) { +	Common::Rect bounds; +	getBounds(bounds); + +	_bounceStart.x = (bounds.left + bounds.right) >> 1; +	_bounceStart.y = (bounds.top + bounds.bottom) >> 1; + +	PegasusEngine *vm = (PegasusEngine *)g_engine; + +	switch (vm->getRandomNumber(3)) { +	case 0: +		_bounceStop.x = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetHRange - 1); +		_bounceStop.y = kMaxBounceSize / 2 + 1; +		break; +	case 1: +		_bounceStop.x = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetHRange - 1); +		_bounceStop.y = 480 - kMaxBounceSize / 2 + 1; +		break; +	case 2: +		_bounceStop.x = kMaxBounceSize / 2 + 1; +		_bounceStop.y = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetVRange - 1); +		break; +	case 3: +		_bounceStop.x = 640 - kMaxBounceSize / 2 + 1; +		_bounceStop.y = kMaxBounceSize / 2 + 1 + vm->getRandomNumber(kBounceTargetVRange - 1); +		break; +	} + +	_bounceSizeStart = bounds.width(); +	_bounceSizeStop = MIN(_bounceSizeStart, kMaxBounceSize); + +	_timer.stop(); +	_timer.setSegment(0, reboundTime); +	_bounceTime = reboundTime; +	_timer.setTime(0); +	_timer.start(); + +	_bouncing = true; +} + +void SpaceJunk::hitByEnergyBeam(Common::Point) { +	rebound(kWeaponReboundTime); +	setGlowing(true); +	((PegasusEngine *)g_engine)->delayShell(1, 3); +	setGlowing(false); +} + +void SpaceJunk::hitByGravitonCannon(Common::Point impactPoint) {	 +	stop(); +	stopIdling(); +	hide(); + +	Common::Rect r; +	getBounds(r); +	r = Common::Rect::center(impactPoint.x, impactPoint.y, r.width(), r.height()); + +	((Mars *)g_neighborhood)->showBigExplosion(r, kShuttleJunkOrder); +	((Mars *)g_neighborhood)->setUpNextDropTime(); +} + +void SpaceJunk::getJunkPosition(Point3D &position) { +	position = _junkPosition; +} + +bool SpaceJunk::isJunkFlying() { +	return isIdling(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/spacejunk.h b/engines/pegasus/neighborhood/mars/spacejunk.h new file mode 100644 index 0000000000..f2dbf9a838 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/spacejunk.h @@ -0,0 +1,78 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_SPACEJUNK_H +#define PEGASUS_NEIGHBORHOOD_MARS_SPACEJUNK_H + +#include "pegasus/movie.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/spacechase3d.h" + +namespace Pegasus { + +static const CoordType kJunkMaxScreenSize = 250; + +static const float kJunkSize = convertScreenVToSpaceY(kShuttleWindowMidV - kJunkMaxScreenSize / 2, kJunkMinDistance) - +		convertScreenVToSpaceY(kShuttleWindowMidV + kJunkMaxScreenSize / 2, kJunkMinDistance); + +class SpaceJunk : public ScalingMovie, public Idler { +public: +	SpaceJunk(const DisplayElementID); +	virtual ~SpaceJunk(); + +	void setCenter(const CoordType, const CoordType); +	void setScaleSize(const CoordType); + +	void useIdleTime(); +	 +	void launchJunk(int16, CoordType, CoordType); +	 +	void getJunkPosition(Point3D &); +	bool isJunkFlying(); + +	bool pointInJunk(const Common::Point &); + +	void hitByEnergyBeam(Common::Point impactPoint); +	void hitByGravitonCannon(Common::Point impactPoint); + +	bool junkFlying() { return _timer.isRunning(); } + +protected: +	void rebound(const TimeValue); + +	TimeBase _timer; +	Point3D _launchPoint, _junkPosition; +	Common::Point _center; +	bool _bouncing; +	Common::Point _bounceStart, _bounceStop; +	CoordType _bounceSizeStart, _bounceSizeStop; +	TimeValue _bounceTime; +}; + +extern SpaceJunk *g_spaceJunk; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/mars/tractorbeam.cpp b/engines/pegasus/neighborhood/mars/tractorbeam.cpp new file mode 100644 index 0000000000..81c96299cf --- /dev/null +++ b/engines/pegasus/neighborhood/mars/tractorbeam.cpp @@ -0,0 +1,139 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/mars/tractorbeam.h" + +namespace Pegasus { + +TractorBeam::TractorBeam() : DisplayElement(kNoDisplayElement) { +	setBounds(kShuttleTractorLeft, kShuttleTractorTop, kShuttleTractorLeft + kShuttleTractorWidth, +			kShuttleTractorTop + kShuttleTractorHeight); +	setDisplayOrder(kShuttleTractorBeamOrder); + +} + +static const int kHalfWidth = kShuttleTractorWidth >> 1; +static const int kHalfHeight = kShuttleTractorHeight >> 1; + +static const int kW3Vert = kHalfHeight * kHalfHeight * kHalfHeight; +static const int kW3Div2Vert = kW3Vert >> 1; + +static const int kW3Horiz = kHalfWidth * kHalfWidth * kHalfWidth; +static const int kW3Div2Horiz = kW3Horiz >> 1; + +static const int kMaxLevel = 50; + +static const int kAVert = -2 * kMaxLevel; +static const int kBVert = 3 * kMaxLevel * kHalfHeight; + +#define READ_PIXEL(ptr) \ +	if (screen->format.bytesPerPixel == 2) \ +		color = READ_UINT16(ptr); \ +	else \ +		color = READ_UINT32(ptr); \ +	screen->format.colorToRGB(color, r, g, b) + +#define WRITE_PIXEL(ptr) \ +	color = screen->format.RGBToColor(r, g, b); \ +	if (screen->format.bytesPerPixel == 2) \ +		WRITE_UINT16(ptr, color); \ +	else \ +		WRITE_UINT32(ptr, color) + +#define DO_BLEND(ptr) \ +	READ_PIXEL(ptr); \ +	g += (((0xff - g) * blendHoriz) >> 8); \ +	b += (((0xff - b) * blendHoriz) >> 8); \ +	WRITE_PIXEL(ptr) + +void TractorBeam::draw(const Common::Rect &) { +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + +	// Set up vertical DDA. +	int blendVert = 0; +	int dVert = 0; +	int d1Vert = kAVert + kBVert; +	int d2Vert = 6 * kAVert + 2 * kBVert; +	int d3Vert = 6 * kAVert; + +	byte *rowPtrTop = (byte *)screen->getBasePtr(_bounds.left, _bounds.top); +	byte *rowPtrBottom = (byte *)screen->getBasePtr(_bounds.left, _bounds.top + ((kHalfHeight << 1) - 1)); + +	for (int y = kHalfHeight; y > 0; y--) { +		// Set up horizontal DDA +		int A = -2 * blendVert; +		int B = 3 * blendVert * kHalfWidth; +		int blendHoriz = 0; +		int dHoriz = 0; +		int d1Horiz = A + B; +		int d2Horiz = 6 * A + 2 * B; +		int d3Horiz = 6 * A; + +		byte *pTopLeft = rowPtrTop; +		byte *pTopRight = rowPtrTop + (kHalfWidth * 2 - 1) * screen->format.bytesPerPixel; +		byte *pBottomLeft = rowPtrBottom; +		byte *pBottomRight = rowPtrBottom + (kHalfWidth * 2 - 1) * screen->format.bytesPerPixel; +		 +		for (int x = kHalfWidth; x > 0; x--) { +			byte r, g, b; +			uint32 color; + +			DO_BLEND(pTopLeft); +			DO_BLEND(pTopRight); +			DO_BLEND(pBottomLeft); +			DO_BLEND(pBottomRight); +			 +			pTopLeft += screen->format.bytesPerPixel; +			pBottomLeft += screen->format.bytesPerPixel; +			pTopRight -= screen->format.bytesPerPixel; +			pBottomRight -= screen->format.bytesPerPixel; +			 +			while (dHoriz > kW3Div2Horiz) { +				blendHoriz++; +				dHoriz -= kW3Horiz; +			} + +			dHoriz += d1Horiz; +			d1Horiz += d2Horiz; +			d2Horiz += d3Horiz; +		} +		 +		rowPtrTop += screen->pitch; +		rowPtrBottom -= screen->pitch; +		 +		while (dVert > kW3Div2Vert) { +			blendVert++; +			dVert -= kW3Vert; +		} + +		dVert += d1Vert; +		d1Vert += d2Vert; +		d2Vert += d3Vert; +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/mars/tractorbeam.h b/engines/pegasus/neighborhood/mars/tractorbeam.h new file mode 100644 index 0000000000..cd87992d11 --- /dev/null +++ b/engines/pegasus/neighborhood/mars/tractorbeam.h @@ -0,0 +1,43 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_MARS_TRACTORBEAM_H +#define PEGASUS_NEIGHBORHOOD_MARS_TRACTORBEAM_H + +#include "pegasus/elements.h" + +namespace Pegasus { + +class TractorBeam : public DisplayElement { +public: +	TractorBeam(); +	virtual ~TractorBeam() {} + +	void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/neighborhood.cpp b/engines/pegasus/neighborhood/neighborhood.cpp new file mode 100644 index 0000000000..3378459a5d --- /dev/null +++ b/engines/pegasus/neighborhood/neighborhood.cpp @@ -0,0 +1,1774 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" + +#include "pegasus/compass.h" +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/graphics.h" +#include "pegasus/input.h" +#include "pegasus/interaction.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/mapchip.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" + +namespace Pegasus { + +StriderCallBack::StriderCallBack(Neighborhood *neighborhood) { +	_neighborhood = neighborhood; +} + +void StriderCallBack::callBack() { +	_neighborhood->checkStriding(); +} + +static const TimeValue kStridingSlop = 39; + +Neighborhood *g_neighborhood = 0; + +Neighborhood::Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id) +		: InputHandler(nextHandler), IDObject(id), _vm(vm), _resName(resName), _navMovie(kNavMovieID), _stridingCallBack(this), +		_neighborhoodNotification(kNeighborhoodNotificationID, (NotificationManager *)vm), _pushIn(kNoDisplayElement), +		_turnPush(kTurnPushID), _croppedMovie(kCroppedMovieID) { +	GameState.setOpenDoorLocation(kNoRoomID, kNoDirection); +	_currentAlternate = 0; +	_currentActivation = kActivateHotSpotAlways; +	_interruptionFilter = kFilterAllInput; +	allowInput(true); +	resetLastExtra(); +	g_neighborhood = this; +	_currentInteraction = 0; +	_doneWithInteraction = false; +	_croppedMovie.setDisplayOrder(kCroppedMovieLayer); +} + +Neighborhood::~Neighborhood() { +	for (HotspotIterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++) +		g_allHotspots.remove(*it); + +	_neighborhoodHotspots.deleteHotspots(); +	g_neighborhood = 0; + +	loadLoopSound1(""); +	loadLoopSound2(""); +	newInteraction(kNoInteractionID); + +	if (g_AIArea) +		g_AIArea->removeAllRules(); +} + +void Neighborhood::init() { +	_neighborhoodNotification.notifyMe(this, kNeighborhoodFlags, kNeighborhoodFlags); +	_navMovieCallBack.setNotification(&_neighborhoodNotification); +	_turnPushCallBack.setNotification(&_neighborhoodNotification); +	_delayCallBack.setNotification(&_neighborhoodNotification); +	_spotSoundCallBack.setNotification(&_neighborhoodNotification); + +	debug(0, "Loading '%s' neighborhood resources", _resName.c_str()); + +	Common::SeekableReadStream *stream = _vm->_resFork->getResource(_doorTable.getResTag(), _resName); +	if (!stream) +		error("Failed to load doors"); +	_doorTable.loadFromStream(stream); +	delete stream; + +	stream = _vm->_resFork->getResource(_exitTable.getResTag(), _resName); +	if (!stream) +		error("Failed to load exits"); +	_exitTable.loadFromStream(stream); +	delete stream; + +	stream = _vm->_resFork->getResource(_extraTable.getResTag(), _resName); +	if (!stream) +		error("Failed to load extras"); +	_extraTable.loadFromStream(stream); +	delete stream; + +	stream = _vm->_resFork->getResource(_hotspotInfoTable.getResTag(), _resName); +	if (!stream) +		error("Failed to load hotspot info"); +	_hotspotInfoTable.loadFromStream(stream); +	delete stream; + +	stream = _vm->_resFork->getResource(_spotTable.getResTag(), _resName); +	if (!stream) +		error("Failed to load spots"); +	_spotTable.loadFromStream(stream); +	delete stream; + +	stream = _vm->_resFork->getResource(_turnTable.getResTag(), _resName); +	if (!stream) +		error("Failed to load turns"); +	_turnTable.loadFromStream(stream); +	delete stream; + +	stream = _vm->_resFork->getResource(_viewTable.getResTag(), _resName); +	if (!stream) +		error("Failed to load views"); +	_viewTable.loadFromStream(stream); +	delete stream; + +	stream = _vm->_resFork->getResource(_zoomTable.getResTag(), _resName); +	if (!stream) +		error("Failed to load zooms"); +	_zoomTable.loadFromStream(stream); +	delete stream; + +	createNeighborhoodSpots(); + +	_navMovie.initFromMovieFile(getNavMovieName()); +	_navMovie.setVolume(_vm->getSoundFXLevel()); + +	Common::String soundSpotsName = getSoundSpotsName(); +	if (soundSpotsName.empty()) { +		_spotSounds.disposeSound(); +	} else { +		_spotSounds.initFromQuickTime(getSoundSpotsName()); +		_spotSounds.setVolume(_vm->getSoundFXLevel()); +	} + +	_navMovie.setDisplayOrder(kNavMovieOrder); +	_navMovie.startDisplaying(); + +	Common::Rect bounds; +	_navMovie.getBounds(bounds); +	_pushIn.allocateSurface(bounds); + +	_turnPush.setInAndOutElements(&_pushIn, &_navMovie); +	_turnPush.setDisplayOrder(kTurnPushOrder); +	_turnPush.startDisplaying(); +	_navMovieCallBack.initCallBack(&_navMovie, kCallBackAtExtremes); +	_stridingCallBack.initCallBack(&_navMovie, kCallBackAtTime); +	_turnPushCallBack.initCallBack(&_turnPush, kCallBackAtExtremes); +	_delayCallBack.initCallBack(&_delayTimer, kCallBackAtExtremes); +	_spotSoundCallBack.initCallBack(&_spotSounds, kCallBackAtExtremes); + +	setUpAIRules(); + +	if (g_compass) +		g_compass->setFaderValue(getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection())); + +	_soundLoop1.attachFader(&_loop1Fader); +	_soundLoop2.attachFader(&_loop2Fader); +	startIdling(); +} + +void Neighborhood::start() { +	GameState.setCurrentRoom(GameState.getLastRoom()); +	GameState.setCurrentDirection(GameState.getLastDirection()); +	arriveAt(GameState.getNextRoom(), GameState.getNextDirection()); +} + +void Neighborhood::receiveNotification(Notification *, const NotificationFlags flags) { +	if ((flags & (kNeighborhoodMovieCompletedFlag | kTurnCompletedFlag)) != 0 && g_AIArea) +		g_AIArea->unlockAI(); +	if (flags & kMoveForwardCompletedFlag) +		arriveAt(GameState.getNextRoom(), GameState.getNextDirection()); +	if (flags & kTurnCompletedFlag) +		turnTo(GameState.getNextDirection()); +	if (flags & kSpotCompletedFlag) +		spotCompleted(); +	if (flags & kDoorOpenCompletedFlag) +		doorOpened(); +	if (flags & kActionRequestCompletedFlag) +		popActionQueue(); +	if (flags & kDeathExtraCompletedFlag) +		die(_extraDeathReason); +} + +void Neighborhood::arriveAt(const RoomID room, const DirectionConstant direction) { +	if (g_map) +		g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), room, direction); + +	GameState.setCurrentNeighborhood(getObjectID()); + +	_currentActivation = kActivateHotSpotAlways; +	_interruptionFilter = kFilterAllInput; + +	if (room != GameState.getCurrentRoom() || direction != GameState.getCurrentDirection()) { +		GameState.setCurrentRoom(room); +		GameState.setCurrentDirection(direction); +		loadAmbientLoops(); +		activateCurrentView(room, direction, kSpotOnArrivalMask); +	} else { +		loadAmbientLoops(); +		showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); +	} + +	if (GameState.getOpenDoorRoom() != kNoRoomID) { +		// Arriving always closes a door. +		loadAmbientLoops(); +		closeDoorOffScreen(GameState.getOpenDoorRoom(), GameState.getOpenDoorDirection()); +		GameState.setOpenDoorLocation(kNoRoomID, kNoDirection); +	} + +	if (g_compass) +		g_compass->setFaderValue(getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection())); + +	if (g_AIArea) +		g_AIArea->checkMiddleArea(); + +	checkContinuePoint(room, direction); +} + +// These functions can be overridden to tweak the exact frames used. + +void Neighborhood::getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry) { +	entry = _exitTable.findEntry(room, direction, _currentAlternate); + +	if (entry.isEmpty()) +		entry = _exitTable.findEntry(room, direction, kNoAlternateID); +} + +TimeValue Neighborhood::getViewTime(const RoomID room, const DirectionConstant direction) { +	if (GameState.getOpenDoorRoom() == room && GameState.getOpenDoorDirection() == direction) { +		// If we get here, the door entry for this location must exist. +		DoorTable::Entry doorEntry = _doorTable.findEntry(room, direction, _currentAlternate); + +		if (doorEntry.isEmpty()) +			doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID); + +		return doorEntry.movieEnd - 1; +	} + +	ViewTable::Entry viewEntry = _viewTable.findEntry(room, direction, _currentAlternate); + +	if (viewEntry.isEmpty()) +		viewEntry = _viewTable.findEntry(room, direction, kNoAlternateID); + +	return viewEntry.time; +} + +void Neighborhood::getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &doorEntry) { +	doorEntry = _doorTable.findEntry(room, direction, _currentAlternate); + +	if (doorEntry.isEmpty()) +		doorEntry = _doorTable.findEntry(room, direction, kNoAlternateID); +} + +DirectionConstant Neighborhood::getTurnEntry(const RoomID room, const DirectionConstant direction, const TurnDirection turnDirection) { +	TurnTable::Entry turnEntry = _turnTable.findEntry(room, direction, turnDirection, _currentAlternate); + +	if (turnEntry.isEmpty()) +		turnEntry = _turnTable.findEntry(room, direction, turnDirection, kNoAlternateID); + +	return turnEntry.endDirection; +} + +void Neighborhood::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) { +	spotEntry = _spotTable.findEntry(room, direction, flags, _currentAlternate); + +	if (spotEntry.isEmpty()) +		spotEntry = _spotTable.findEntry(room, direction, flags, kNoAlternateID); +} + +void Neighborhood::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) { +	zoomEntry = _zoomTable.findEntry(id); +} + +void Neighborhood::getHotspotEntry(const HotSpotID id, HotspotInfoTable::Entry &hotspotEntry) { +	hotspotEntry = _hotspotInfoTable.findEntry(id); +} + +void Neighborhood::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) { +	extraEntry = _extraTable.findEntry(id); +} + +///////////////////////////////////////////// +// +// "Can" functions: Called to see whether or not a user is allowed to do something + +CanMoveForwardReason Neighborhood::canMoveForward(ExitTable::Entry &entry) { +	DoorTable::Entry door; +	 +	getExitEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), entry); +	getDoorEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), door); + +	// Fixed this so that doors that don't lead anywhere can be opened, but not walked +	// through. +	if (door.flags & kDoorPresentMask) { +		if (GameState.isCurrentDoorOpen()) { +			if (entry.exitRoom == kNoRoomID) +				return kCantMoveBlocked; +			else +				return kCanMoveForward; +		} else if (door.flags & kDoorLockedMask) { +			return kCantMoveDoorLocked; +		} else { +			return kCantMoveDoorClosed; +		} +	} else if (entry.exitRoom == kNoRoomID) { +		return kCantMoveBlocked; +	} + +	return kCanMoveForward; +} + +CanTurnReason Neighborhood::canTurn(TurnDirection turnDirection, DirectionConstant &nextDir) { +	nextDir = getTurnEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), turnDirection); + +	if (nextDir == kNoDirection) +		return kCantTurnNoTurn; + +	return kCanTurn; +} + +CanOpenDoorReason Neighborhood::canOpenDoor(DoorTable::Entry &entry) { +	getDoorEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), entry); + +	if (entry.flags & kDoorPresentMask) { +		if (GameState.isCurrentDoorOpen()) +			return kCantOpenAlreadyOpen; + +		if (entry.flags & kDoorLockedMask) +			return kCantOpenLocked; + +		return kCanOpenDoor; +	} + +	return kCantOpenNoDoor; +} + +void Neighborhood::createNeighborhoodSpots() { +	Common::SeekableReadStream *hotspotList = _vm->_resFork->getResource(MKTAG('H', 'S', 'L', 's'), _resName); +	if (!hotspotList) +		error("Could not load neighborhood hotspots"); + +	uint32 hotspotCount = hotspotList->readUint32BE(); + +	while (hotspotCount--) { +		uint16 id = hotspotList->readUint16BE(); +		uint32 flags = hotspotList->readUint32BE(); +		uint32 rgnSize = hotspotList->readUint32BE(); + +		int32 startPos = hotspotList->pos(); + +		debug(0, "Hotspot %d:", id); +		Region region(hotspotList); + +		hotspotList->seek(startPos + rgnSize); + +		Hotspot *hotspot = new Hotspot(id); +		hotspot->setHotspotFlags(flags); +		hotspot->setArea(region); + +		g_allHotspots.push_back(hotspot); +		_neighborhoodHotspots.push_back(hotspot); +	} + +	delete hotspotList; +} + +void Neighborhood::popActionQueue() {	 +	if (!_actionQueue.empty()) { +		QueueRequest topRequest = _actionQueue.pop(); + +		switch (topRequest.requestType) { +		case kNavExtraRequest: +			_navMovie.stop(); +			break; +		case kSpotSoundRequest: +			_spotSounds.stopSound(); +			break; +		case kDelayRequest: +			_delayTimer.stop(); +			break; +		} + +		serviceActionQueue(); +	} +} + +void Neighborhood::serviceActionQueue() { +	if (!_actionQueue.empty()) { +		QueueRequest &topRequest = _actionQueue.front(); + +		if (!topRequest.playing) { +			topRequest.playing = true; +			switch (topRequest.requestType) { +			case kNavExtraRequest: +				startExtraSequence(topRequest.extra, topRequest.flags, topRequest.interruptionFilter); +				break; +			case kSpotSoundRequest: +				_spotSounds.stopSound(); +				_spotSounds.playSoundSegment(topRequest.start, topRequest.stop); +				_interruptionFilter = topRequest.interruptionFilter; +				_spotSoundCallBack.setCallBackFlag(topRequest.flags); +				_spotSoundCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +				break; +			case kDelayRequest: +				_delayTimer.stop(); +				_delayCallBack.setCallBackFlag(topRequest.flags); +				_delayTimer.setSegment(0, topRequest.start, topRequest.stop); +				_delayTimer.setTime(0); +				_delayCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +				_interruptionFilter = topRequest.interruptionFilter; +				_delayTimer.start(); +				break; +			} +		} +	} else { +		_interruptionFilter = kFilterAllInput; +	} +} + +void Neighborhood::requestAction(const QueueRequestType requestType, const ExtraID extra, const TimeValue in, const TimeValue out, +		const InputBits interruptionFilter, const NotificationFlags flags) { + +	QueueRequest request; +	 +	request.requestType = requestType; +	request.extra = extra; +	request.start = in; +	request.stop = out; +	request.interruptionFilter = interruptionFilter; +	request.playing = false; +	request.flags = flags | kActionRequestCompletedFlag; +	request.notification = &_neighborhoodNotification; +	_actionQueue.push(request); +	if (_actionQueue.size() == 1) +		serviceActionQueue(); +} + +void Neighborhood::requestExtraSequence(const ExtraID whichExtra, const NotificationFlags flags, const InputBits interruptionFilter) { +	requestAction(kNavExtraRequest, whichExtra, 0, 0, interruptionFilter, flags); +} + +void Neighborhood::requestSpotSound(const TimeValue in, const TimeValue out, const InputBits interruptionFilter, const NotificationFlags flags) { +	requestAction(kSpotSoundRequest, 0xFFFFFFFF, in, out, interruptionFilter, flags); +} + +void Neighborhood::playSpotSoundSync(const TimeValue in, const TimeValue out) { +	// Let the action queue play out first... +	while (!actionQueueEmpty()) { +		_vm->checkCallBacks(); +		_vm->refreshDisplay(); +		_vm->checkNotifications(); +		_vm->_system->delayMillis(10); +	} + +	_spotSounds.stopSound(); +	_spotSounds.playSoundSegment(in, out); + +	while (_spotSounds.isPlaying()) { +		_vm->checkCallBacks(); +		_vm->refreshDisplay(); +		_vm->_system->delayMillis(10); +	} +} + +void Neighborhood::requestDelay(const TimeValue delayDuration, const TimeScale delayScale, const InputBits interruptionFilter, const NotificationFlags flags) { +	requestAction(kDelayRequest, 0xFFFFFFFF, delayDuration, delayScale, interruptionFilter, flags); +} + +bool operator==(const QueueRequest &arg1, const QueueRequest &arg2) { +	return arg1.requestType == arg2.requestType && arg1.extra == arg2.extra && +			arg1.start == arg2.start && arg1.stop == arg2.stop; +} + +bool operator!=(const QueueRequest &arg1, const QueueRequest &arg2) { +	return !operator==(arg1, arg2); +} + +Common::String Neighborhood::getBriefingMovie() { +	if (_currentInteraction) +		return _currentInteraction->getBriefingMovie(); + +	return Common::String(); +} + +Common::String Neighborhood::getEnvScanMovie() { +	if (_currentInteraction) +		return _currentInteraction->getEnvScanMovie(); + +	return Common::String(); +} + +uint Neighborhood::getNumHints() { +	if (_currentInteraction) +		return _currentInteraction->getNumHints(); + +	return 0; +} + +Common::String Neighborhood::getHintMovie(uint hintNum) { +	if (_currentInteraction) +		return _currentInteraction->getHintMovie(hintNum); + +	return Common::String(); +} + +bool Neighborhood::canSolve() { +	if (_currentInteraction) +		return _currentInteraction->canSolve(); + +	return false; +} + +void Neighborhood::doSolve() { +	if (_currentInteraction) +		_currentInteraction->doSolve(); +} + +bool Neighborhood::okayToJump() { +	return !_vm->playerHasItemID(kGasCanister) && !_vm->playerHasItemID(kMachineGun); +} + +AirQuality Neighborhood::getAirQuality(const RoomID) { +	return kAirQualityGood; +} + +void Neighborhood::checkStriding() { +	if (stillMoveForward()) { +		ExitTable::Entry nextExit; +		getExitEntry(GameState.getNextRoom(), GameState.getNextDirection(), nextExit); +		keepStriding(nextExit); +	} else { +		stopStriding(); +	} +} + +bool Neighborhood::stillMoveForward() { +	Input input; + +	InputHandler::readInputDevice(input); +	return input.upButtonAnyDown(); +} + +void Neighborhood::keepStriding(ExitTable::Entry &nextExitEntry) { +	FaderMoveSpec compassMove; + +	if (g_map) +		g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getNextRoom(), GameState.getNextDirection()); + +	if (g_compass) +		getExitCompassMove(nextExitEntry, compassMove); + +	GameState.setCurrentRoom(GameState.getNextRoom()); +	GameState.setCurrentDirection(GameState.getNextDirection()); +	GameState.setNextRoom(nextExitEntry.exitRoom); +	GameState.setNextDirection(nextExitEntry.exitDirection); + +	if (nextExitEntry.movieEnd == nextExitEntry.exitEnd) +		scheduleNavCallBack(kNeighborhoodMovieCompletedFlag | kMoveForwardCompletedFlag); +	else +		scheduleStridingCallBack(nextExitEntry.movieEnd - kStridingSlop, kStrideCompletedFlag); + +	if (g_compass) +		g_compass->startFader(compassMove); +} + +void Neighborhood::stopStriding() { +	_navMovie.stop(); +	_neighborhoodNotification.setNotificationFlags(kNeighborhoodMovieCompletedFlag | +			kMoveForwardCompletedFlag, kNeighborhoodMovieCompletedFlag | kMoveForwardCompletedFlag); +} + +// Compass support +int16 Neighborhood::getStaticCompassAngle(const RoomID, const DirectionConstant dir) { +	// North, south, east, west +	static const int16 compassAngles[] = { 0, 180, 90, 270 }; +	return compassAngles[dir]; +} + +void Neighborhood::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) {	 +	int32 startAngle = getStaticCompassAngle(exitEntry.room, exitEntry.direction); +	int32 stopAngle = getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection); + +	if (startAngle > stopAngle) { +		if (stopAngle + 180 < startAngle) +			stopAngle += 360; +	} else { +		if (startAngle + 180 < stopAngle) +			startAngle += 360; +	} + +	compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), exitEntry.movieStart, startAngle, exitEntry.movieEnd, stopAngle); +} + +void Neighborhood::scheduleNavCallBack(NotificationFlags flags) { +	_navMovieCallBack.cancelCallBack(); + +	if (flags != 0) { +		_navMovieCallBack.setCallBackFlag(flags); +		_navMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	} +} + +void Neighborhood::scheduleStridingCallBack(const TimeValue strideStop, NotificationFlags flags) {	 +	_stridingCallBack.cancelCallBack(); + +	if (flags != 0) +		_stridingCallBack.scheduleCallBack(kTriggerTimeFwd, strideStop, _navMovie.getScale()); +} + +void Neighborhood::moveNavTo(const CoordType h, const CoordType v) { +	CoordType oldH, oldV; +	_navMovie.getLocation(oldH, oldV); + +	CoordType offH = h - oldH; +	CoordType offV = v - oldV; + +	_navMovie.moveElementTo(h, v); +	_turnPush.moveElementTo(h, v); + +	if (offH != 0 || offV != 0) +		for (HotspotList::iterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++) +			if ((*it)->getHotspotFlags() & kNeighborhoodSpotFlag) +				(*it)->moveSpot(offH, offV); +} + +void Neighborhood::activateHotspots() {	 +	InputHandler::activateHotspots(); + +	for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) { +		HotspotInfoTable::Entry entry = *it; + +		if (entry.hotspotRoom == GameState.getCurrentRoom() && entry.hotspotDirection == GameState.getCurrentDirection() +				&& (entry.hotspotActivation == _currentActivation || entry.hotspotActivation == kActivateHotSpotAlways)) { +			Hotspot *hotspot = g_allHotspots.findHotspotByID(entry.hotspot); +			if (hotspot) +				activateOneHotspot(entry, hotspot); +		} +	} +} + +void Neighborhood::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {	 +	HotSpotFlags flags = clickedSpot->getHotspotFlags(); + +	if ((flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag)) != 0) { +		ItemID itemID = kNoItemID; + +		for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) { +			if (it->hotspot == clickedSpot->getObjectID()) { +				itemID = it->hotspotItem; +				break; +			} +		} + +		if (itemID != kNoItemID) { +			Item *draggingItem = g_allItems.findItemByID(itemID); + +			if (draggingItem) { +				takeItemFromRoom(draggingItem); + +				if ((flags & kPickUpItemSpotFlag) != 0) +					_vm->dragItem(input, draggingItem, kDragInventoryPickup); +				else +					_vm->dragItem(input, draggingItem, kDragBiochipPickup); +			} +		} +	} else { +		// Check other flags here? +		if ((flags & kZoomSpotFlags) != 0) { +			zoomTo(clickedSpot); +		} else if ((flags & kPlayExtraSpotFlag) != 0) { +			HotspotInfoTable::Entry hotspotEntry; +			getHotspotEntry(clickedSpot->getObjectID(), hotspotEntry); +			startExtraSequence(hotspotEntry.hotspotExtra, kExtraCompletedFlag, kFilterNoInput); +		} else if ((flags & kOpenDoorSpotFlag) != 0) { +			openDoor(); +		} else { +			InputHandler::clickInHotspot(input, clickedSpot); +		} +	} +} + +void Neighborhood::cantMoveThatWay(CanMoveForwardReason reason) { +	switch (reason) { +	case kCantMoveDoorClosed: +	case kCantMoveDoorLocked: +		openDoor(); +		break; +	case kCantMoveBlocked: +		zoomUpOrBump(); +		break; +	default: +		bumpIntoWall(); +		break; +	} +} + +void Neighborhood::cantOpenDoor(CanOpenDoorReason) { +	bumpIntoWall(); +} + +void Neighborhood::turnTo(const DirectionConstant direction) { +	if (g_map) +		g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), direction); + +	// clone2727 says: Is this necessary? +	_vm->_gfx->setCurSurface(_navMovie.getSurface()); +	_pushIn.copyToCurrentPort(); +	_vm->_gfx->setCurSurface(_vm->_gfx->getWorkArea()); +	 +	// Added 2/10/97. Shouldn't this be here? Shouldn't we set the current activation to +	// always when turning to a new view? +	_currentActivation = kActivateHotSpotAlways; +	 +	_interruptionFilter = kFilterAllInput; + +	if (direction != GameState.getCurrentDirection()) { +		GameState.setCurrentDirection(direction); +		activateCurrentView(GameState.getCurrentRoom(), direction, kSpotOnTurnMask); +	} else { +		showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); +	} + +	if (GameState.getOpenDoorRoom() != kNoRoomID) { +		// Turning always closes a door. +		loadAmbientLoops(); +		closeDoorOffScreen(GameState.getOpenDoorRoom(), GameState.getOpenDoorDirection()); +		GameState.setOpenDoorLocation(kNoRoomID, kNoDirection); +	} + +	if (g_AIArea) +		g_AIArea->checkMiddleArea(); + +	checkContinuePoint(GameState.getCurrentRoom(), direction); + +	_vm->_cursor->hideUntilMoved(); +} + +void Neighborhood::spotCompleted() { +	_interruptionFilter = kFilterAllInput; +	showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); +} + +void Neighborhood::doorOpened() {	 +	_interruptionFilter = kFilterAllInput; + +	// 2/23/97 +	// Fixes funny bug with doors that are opened by dropping things on them... +	setCurrentActivation(kActivateHotSpotAlways); + +	GameState.setOpenDoorLocation(GameState.getCurrentRoom(), GameState.getCurrentDirection()); + +	SpotTable::Entry entry; +	findSpotEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), kSpotOnDoorOpenMask, entry); + +	if (entry.dstFlags & kSpotOnDoorOpenMask) { +		startSpotOnceOnly(entry.movieStart, entry.movieEnd); +	} else { +		findSpotEntry(GameState.getCurrentRoom(), GameState.getCurrentDirection(), kSpotOnDoorOpenMask | kSpotLoopsMask, entry); + +		if (entry.dstFlags & kSpotOnDoorOpenMask) +			startSpotLoop(entry.movieStart, entry.movieEnd); +	} + +	loadAmbientLoops(); + +	if (g_map) +		g_map->moveToMapLocation(GameState.getCurrentNeighborhood(), GameState.getNextRoom(), GameState.getNextDirection()); + +	if (g_AIArea) +		g_AIArea->checkMiddleArea(); +} + +void Neighborhood::moveForward() { +	ExitTable::Entry exitEntry; +	CanMoveForwardReason moveReason = canMoveForward(exitEntry); + +	if (moveReason == kCanMoveForward) +		startExitMovie(exitEntry); +	else +		cantMoveThatWay(moveReason); +} + +void Neighborhood::turn(const TurnDirection turnDirection) { +	DirectionConstant nextDir;	 +	CanTurnReason turnReason = canTurn(turnDirection, nextDir); + +	if (turnReason == kCanTurn) +		startTurnPush(turnDirection, getViewTime(GameState.getCurrentRoom(), nextDir), nextDir); +	else +		cantTurnThatWay(turnReason); +} + +void Neighborhood::turnLeft() { +	turn(kTurnLeft); +} + +void Neighborhood::turnRight() { +	turn(kTurnRight); +} + +void Neighborhood::turnUp() { +	turn(kTurnUp); +} + +void Neighborhood::turnDown() { +	turn(kTurnDown); +} + +void Neighborhood::openDoor() { +	DoorTable::Entry door;	 +	CanOpenDoorReason doorReason = canOpenDoor(door); + +	if (doorReason == kCanOpenDoor) +		startDoorOpenMovie(door.movieStart, door.movieEnd); +	else +		cantOpenDoor(doorReason); +} + +void Neighborhood::zoomTo(const Hotspot *hotspot) { +	ZoomTable::Entry zoomEntry; +	getZoomEntry(hotspot->getObjectID(), zoomEntry); +	if (!zoomEntry.isEmpty()) +		startZoomMovie(zoomEntry); +} + +void Neighborhood::updateViewFrame() { +	showViewFrame(getViewTime(GameState.getCurrentRoom(), GameState.getCurrentDirection())); +} + +void Neighborhood::startSpotLoop(TimeValue startTime, TimeValue stopTime, NotificationFlags flags) { +	_turnPush.hide(); +	startMovieSequence(startTime, stopTime, flags, true, kFilterAllInput); +} + +void Neighborhood::showViewFrame(TimeValue viewTime) {	 +	if ((int32)viewTime >= 0) { +		_turnPush.hide(); +		_navMovie.stop(); +		_navMovie.setFlags(0); +		_navMovie.setSegment(0, _navMovie.getDuration()); +		_navMovie.setTime(viewTime); + +		Common::Rect pushBounds; +		_turnPush.getBounds(pushBounds); + +		_navMovie.moveElementTo(pushBounds.left, pushBounds.top); +		_navMovie.show(); +		_navMovie.redrawMovieWorld(); +	} +} + +void Neighborhood::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) { +	ExtraTable::Entry entry; +	getExtraEntry(extraID, entry); + +	if (entry.movieStart != 0xffffffff) +		playExtraMovie(entry, flags, interruptionFilter); +} + +bool Neighborhood::startExtraSequenceSync(const ExtraID extraID, const InputBits interruptionFilter) { +	InputDevice.waitInput(interruptionFilter); +	return prepareExtraSync(extraID) && waitMovieFinish(&_navMovie, interruptionFilter); +} + +void Neighborhood::loopExtraSequence(const uint32 extraID, NotificationFlags flags) { +	ExtraTable::Entry entry; +	getExtraEntry(extraID, entry); +	 +	if (entry.movieStart != 0xffffffff) { +		_lastExtra = extraID; +		startSpotLoop(entry.movieStart, entry.movieEnd, flags); +	} +} + +bool Neighborhood::navMoviePlaying() { +	return _navMovie.isRunning(); +} + +void Neighborhood::playDeathExtra(ExtraID extra, DeathReason deathReason) { +	_extraDeathReason = deathReason; +	startExtraSequence(extra, kDeathExtraCompletedFlag, kFilterNoInput); +} + +void Neighborhood::die(const DeathReason deathReason) { +	loadLoopSound1(""); +	loadLoopSound2(""); +	_vm->die(deathReason); +} + +void Neighborhood::setSoundFXLevel(const uint16 fxLevel) { +	if (_navMovie.isSurfaceValid()) +		_navMovie.setVolume(fxLevel); +	if (_spotSounds.isSoundLoaded()) +		_spotSounds.setVolume(fxLevel); +	if (_currentInteraction) +		_currentInteraction->setSoundFXLevel(fxLevel); +} + +void Neighborhood::setAmbienceLevel(const uint16 ambientLevel) { +	if (_soundLoop1.isSoundLoaded()) +		_loop1Fader.setMasterVolume(_vm->getAmbienceLevel()); +	if (_soundLoop2.isSoundLoaded()) +		_loop2Fader.setMasterVolume(_vm->getAmbienceLevel()); +	if (_currentInteraction) +		_currentInteraction->setAmbienceLevel(ambientLevel); +} + +// Force the exit taken from (room, direction, alternate) to come to a stop. +void Neighborhood::forceStridingStop(const RoomID room, const DirectionConstant direction, const AlternateID alternate) { +	ExitTable::Entry entry = _exitTable.findEntry(room, direction, alternate); + +	if (entry.movieStart != 0xffffffff) { +		TimeValue strideStop = entry.exitEnd; +		TimeValue exitStop = entry.movieEnd; + +		if (strideStop != exitStop) { +			for (ExitTable::iterator it = _exitTable.begin(); it != _exitTable.end(); it++) { +				entry = *it; + +				if (entry.exitEnd == strideStop && entry.movieEnd <= exitStop) { +					entry.exitEnd = exitStop; +					*it = entry; +				} +			} +		} +	} +} + +// Restore the exit taken from (room, direction, alternate) to stride. +void Neighborhood::restoreStriding(const RoomID room, const DirectionConstant direction, const AlternateID alternate) { +	ExitTable::Entry entry = _exitTable.findEntry(room, direction, alternate); + +	if (entry.movieStart != 0xffffffff) { +		TimeValue strideStop = entry.exitEnd; +		TimeValue exitStop = entry.movieEnd; + +		if (strideStop != entry.originalEnd) { +			for (ExitTable::iterator it = _exitTable.begin(); it != _exitTable.end(); it++) { +				entry = *it; + +				if (entry.exitEnd == strideStop && entry.movieEnd <= exitStop) { +					entry.exitEnd = entry.originalEnd; +					*it = entry; +				} +			} +		} +	} +} + +HotspotInfoTable::Entry *Neighborhood::findHotspotEntry(const HotSpotID id) { +	for (HotspotInfoTable::iterator it = _hotspotInfoTable.begin(); it != _hotspotInfoTable.end(); it++) +		if (it->hotspot == id) +			return &(*it); + +	return 0; +} + +void Neighborhood::hideNav() { +	_isRunning = _navMovie.isRunning(); +	_navMovie.stop(); +	_navMovie.hide(); +	_turnPush.stopFader(); +	_turnPush.hide(); +} + +void Neighborhood::showNav() { +	_navMovie.show(); +	_turnPush.hide(); +	if (_isRunning) +		_navMovie.start(); +} + +void Neighborhood::startExitMovie(const ExitTable::Entry &exitEntry) { +	FaderMoveSpec compassMove; +	 +	if (g_compass) +		getExitCompassMove(exitEntry, compassMove); + +	GameState.setNextRoom(exitEntry.exitRoom); +	GameState.setNextDirection(exitEntry.exitDirection); + +	if (exitEntry.movieEnd == exitEntry.exitEnd) // Just a walk. +		startMovieSequence(exitEntry.movieStart, exitEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false); +	else // We're stridin'! +		startMovieSequence(exitEntry.movieStart, exitEntry.exitEnd, kStrideCompletedFlag, kFilterNoInput, false, exitEntry.movieEnd); + +	if (g_compass) +		g_compass->startFader(compassMove); +} + +void Neighborhood::startZoomMovie(const ZoomTable::Entry &zoomEntry) { +	FaderMoveSpec compassMove; +	 +	if (g_compass) +		getZoomCompassMove(zoomEntry, compassMove); + +	GameState.setNextRoom(zoomEntry.room); +	GameState.setNextDirection(zoomEntry.direction); + +	startMovieSequence(zoomEntry.movieStart, zoomEntry.movieEnd, kMoveForwardCompletedFlag, kFilterNoInput, false); + +	if (g_compass) +		g_compass->startFader(compassMove); +} + +void Neighborhood::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { +	startMovieSequence(startTime, stopTime, kDoorOpenCompletedFlag, kFilterNoInput, false); +} + +void Neighborhood::startTurnPush(const TurnDirection turnDirection, const TimeValue newView, const DirectionConstant nextDir) { +	if (g_AIArea) +		g_AIArea->lockAIOut(); + +	_vm->_cursor->hide(); + +	GameState.setNextDirection(nextDir); + +	_interruptionFilter = kFilterNoInput; +	_turnPush.stopFader(); + +	// Set up callback. +	_turnPushCallBack.setCallBackFlag(kTurnCompletedFlag); +	_turnPushCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + +	// Stop nav movie. +	_navMovie.stop(); +	_navMovie.setFlags(0); + +	// Set segment of nav movie to whole movie, so that subsequent initFromMovieFrame +	// will work. +	_navMovie.setSegment(0, _navMovie.getDuration()); + +	_pushIn.initFromMovieFrame(_navMovie.getMovie(), newView); + +	_navMovie.hide(); + +	switch (turnDirection) { +	case kTurnLeft: +		_turnPush.setSlideDirection(kSlideRightMask); +		break; +	case kTurnRight: +		_turnPush.setSlideDirection(kSlideLeftMask); +		break; +	case kTurnUp: +		_turnPush.setSlideDirection(kSlideDownMask); +		break; +	case kTurnDown: +		_turnPush.setSlideDirection(kSlideUpMask); +		break; +	} + +	_turnPush.show(); + +	FaderMoveSpec moveSpec; +	moveSpec.makeTwoKnotFaderSpec(60, 0, 0, 15, 1000); +	_turnPush.startFader(moveSpec); + +	if (g_compass) { +		_turnPush.pauseFader(); + +		int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); +		int32 stopAngle = getStaticCompassAngle(GameState.getCurrentRoom(), nextDir); + +		if (turnDirection == kTurnLeft) { +			if (startAngle < stopAngle) +				startAngle += 360; +		} else { +			if (stopAngle < startAngle) +				stopAngle += 360; +		} + +		FaderMoveSpec turnSpec; +		_turnPush.getCurrentFaderMove(turnSpec); + +		FaderMoveSpec compassMove; +		compassMove.makeTwoKnotFaderSpec(turnSpec.getFaderScale(), turnSpec.getNthKnotTime(0), startAngle, turnSpec.getNthKnotTime(1), stopAngle); +		g_compass->startFader(compassMove); +	} + +	_turnPushCallBack.cancelCallBack(); +	_turnPush.continueFader(); + +	do { +		_vm->checkCallBacks(); +		_vm->refreshDisplay(); +		_vm->_system->delayMillis(10); +	} while (_turnPush.isFading()); + +	_turnPush.stopFader(); +	_neighborhoodNotification.setNotificationFlags(kTurnCompletedFlag, kTurnCompletedFlag); +} + +void Neighborhood::playExtraMovie(const ExtraTable::Entry &extraEntry, const NotificationFlags flags, const InputBits interruptionInput) { +	FaderMoveSpec compassMove; + +	if (g_compass) +		getExtraCompassMove(extraEntry, compassMove); + +	_lastExtra = extraEntry.extra; +	_turnPush.hide(); +	startMovieSequence(extraEntry.movieStart, extraEntry.movieEnd, flags, false, interruptionInput); + +	if (g_compass) +		g_compass->startFader(compassMove); +} + +void Neighborhood::activateCurrentView(const RoomID room, const DirectionConstant direction, SpotFlags flag) { +	SpotTable::Entry entry; +	findSpotEntry(room, direction, flag, entry); + +	if (entry.dstFlags & flag) { +		startSpotOnceOnly(entry.movieStart, entry.movieEnd); +	} else { +		findSpotEntry(room, direction, flag | kSpotLoopsMask, entry); + +		if (entry.dstFlags & flag) +			startSpotLoop(entry.movieStart, entry.movieEnd); +		else +			showViewFrame(getViewTime(room, direction)); +	} +} + +void Neighborhood::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) { +	switch (_vm->getDragType()) { +	case kDragInventoryUse: +		if ((hotspot->getHotspotFlags() & kDropItemSpotFlag) != 0 && +				_vm->getDraggingItem()->getObjectID() == entry.hotspotItem) +			hotspot->setActive(); +		break; +	case kDragInventoryPickup: +	case kDragBiochipPickup: +		// Do nothing -- neighborhoods activate no hot spots in this case... +		break; +	default: +		if ((hotspot->getHotspotFlags() & kPickUpBiochipSpotFlag) != 0) { +			Item *item = g_allItems.findItemByID(entry.hotspotItem); +			if (item &&	item->getItemNeighborhood() == getObjectID()) +				hotspot->setActive(); +		} else { +			HotSpotFlags flags = hotspot->getHotspotFlags(); + +			if ((flags & kNeighborhoodSpotFlag) != 0) { +				if (flags & kOpenDoorSpotFlag) { +					if (!GameState.isCurrentDoorOpen()) +						hotspot->setActive(); +				} else if ((flags & (kZoomSpotFlags | kClickSpotFlag | kPlayExtraSpotFlag)) != 0) { +					hotspot->setActive(); +				} else if ((flags & kPickUpItemSpotFlag) != 0) { +					// Changed this 2/19/96 +					// Should only light up this hot spot if the item's taken flag is not +					// set. It's not based on neighborhood ID since that can be reset by the +					// destroying process. + +					if (!GameState.isTakenItemID(entry.hotspotItem)) +						hotspot->setActive(); +				} +			} +		} +		break; +	} +} + +void Neighborhood::startSpotOnceOnly(TimeValue startTime, TimeValue stopTime) { +	_turnPush.hide(); +	startMovieSequence(startTime, stopTime, kSpotCompletedFlag, kFilterNoInput, false); +} + +void Neighborhood::startMovieSequence(const TimeValue startTime, const TimeValue stopTime, NotificationFlags flags, bool loopSequence, +		const InputBits interruptionInput, const TimeValue strideStop) { +	if (!loopSequence && g_AIArea) +		g_AIArea->lockAIOut(); +	 +	_interruptionFilter = interruptionInput; + +	// Stop the movie before doing anything else +	_navMovie.stop(); + +	Common::Rect pushBounds; +	_turnPush.getBounds(pushBounds); + +	_navMovie.moveElementTo(pushBounds.left, pushBounds.top); +	_navMovie.show(); +	_navMovie.setFlags(0); +	_navMovie.setSegment(startTime, stopTime); +	_navMovie.setTime(startTime); + +	if (loopSequence) +		_navMovie.setFlags(kLoopTimeBase); +	else +		flags |= kNeighborhoodMovieCompletedFlag; + +	if (strideStop != 0xffffffff) +		// Subtract a little slop from the striding stop time to keep from "pumping" at the +		// end of a walk. +		// 40 is one frame (scale == 600, 15 fps). +		scheduleStridingCallBack(strideStop - kStridingSlop, flags); +	else +		scheduleNavCallBack(flags); + +	_navMovie.start(); +} + +void Neighborhood::throwAwayInterface() { +	_doorTable.clear(); +	_exitTable.clear(); +	_extraTable.clear(); +	_hotspotInfoTable.clear(); +	_spotTable.clear(); +	_turnTable.clear(); +	_viewTable.clear(); +	_zoomTable.clear(); + +	_navMovie.stopDisplaying(); +	_navMovie.releaseMovie(); +	_pushIn.deallocateSurface(); +	_turnPush.stopDisplaying(); +	_turnPush.setInAndOutElements(0, 0); +	_turnPush.disposeAllCallBacks(); + +	for (HotspotList::iterator it = _neighborhoodHotspots.begin(); it != _neighborhoodHotspots.end(); it++) +		g_allHotspots.remove(*it); + +	_neighborhoodHotspots.deleteHotspots(); +	_spotSounds.disposeSound(); +	_delayTimer.disposeAllCallBacks(); + +	if (g_AIArea) { +		g_AIArea->saveAIState(); +		g_AIArea->removeAllRules(); +	} + +	if (_currentInteraction) +		newInteraction(kNoInteractionID); + +	_croppedMovie.releaseMovie(); + +	loadLoopSound1(""); +	loadLoopSound2(""); + +	if (g_energyMonitor) { +		g_energyMonitor->stopEnergyDraining(); +		g_energyMonitor->saveCurrentEnergyValue(); +	} + +	delete g_interface; +} + +bool Neighborhood::prepareExtraSync(const ExtraID extraID) { +	ExtraTable::Entry extraEntry; +	FaderMoveSpec compassMove; +	 +	if (g_compass) { +		getExtraEntry(extraID, extraEntry); +		getExtraCompassMove(extraEntry, compassMove); +	} + +	ExtraTable::Entry entry; +	getExtraEntry(extraID, entry); +	bool result; + +	if (entry.movieStart != 0xffffffff) { +		_turnPush.hide(); + +		// Stop the movie before doing anything else +		_navMovie.stop(); + +		Common::Rect pushBounds; +		_turnPush.getBounds(pushBounds); +		_navMovie.moveElementTo(pushBounds.left, pushBounds.top); + +		_navMovie.show(); +		_navMovie.setFlags(0); +		_navMovie.setSegment(entry.movieStart, entry.movieEnd); +		_navMovie.setTime(entry.movieStart); +		_navMovie.start(); +		result = true; +	} else { +		result = false; +	} + +	if (result && g_compass) +		g_compass->startFader(compassMove); + +	return result; +} + +bool Neighborhood::waitMovieFinish(Movie *movie, const InputBits interruptionFilter) { +	Input input; +	bool result = true; +	bool saveAllowed = _vm->swapSaveAllowed(false); +	bool openAllowed = _vm->swapLoadAllowed(false); + +	while (movie->isRunning()) { +		InputDevice.getInput(input, interruptionFilter); + +		if (input.anyInput() || _vm->shouldQuit()) { +			result = false; +			break; +		} + +		_vm->checkCallBacks(); +		_vm->refreshDisplay(); +		_vm->_system->delayMillis(10); +	} + +	movie->stop(); +	_vm->swapSaveAllowed(saveAllowed); +	_vm->swapLoadAllowed(openAllowed); + +	return result; +} + +InputBits Neighborhood::getInputFilter() { +	return _interruptionFilter & InputHandler::getInputFilter(); +} + +void Neighborhood::getZoomCompassMove(const ZoomTable::Entry &zoomEntry, FaderMoveSpec &compassMove) {	 +	int32 startAngle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); +	int32 stopAngle = getStaticCompassAngle(zoomEntry.room, zoomEntry.direction); + +	if (startAngle > stopAngle) { +		if (stopAngle + 180 < startAngle) +			stopAngle += 360; +	} else { +		if (startAngle + 180 < stopAngle) +			startAngle += 360; +	} + +	compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), zoomEntry.movieStart, startAngle, zoomEntry.movieEnd, stopAngle); +} + +void Neighborhood::getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &compassMove) { +	compassMove.makeOneKnotFaderSpec(g_compass->getFaderValue()); +} + +void Neighborhood::setUpAIRules() { +	// Set up default rules here: +	// --  Energy warning rules. + +	if (g_AIArea) { +		g_AIArea->forceAIUnlocked(); + +		if (!_vm->isDemo() && (getObjectID() == kPrehistoricID || getObjectID() == kNoradAlphaID || +				getObjectID() == kNoradDeltaID || getObjectID() == kMarsID || getObjectID() == kWSCID)) { + +			AIEnergyMonitorCondition *condition50 = new AIEnergyMonitorCondition(kWorriedEnergy); +			AIPlayMessageAction *message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4A", false); +			AIRule *rule50 = new AIRule(condition50, message); + +			AIEnergyMonitorCondition *condition25 = new AIEnergyMonitorCondition(kNervousEnergy); +			AICompoundAction *compound = new AICompoundAction(); +			message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4B", false); +			compound->addAction(message); +			AIDeactivateRuleAction *deactivate = new AIDeactivateRuleAction(rule50); +			compound->addAction(deactivate); +			AIRule *rule25 = new AIRule(condition25, compound); + +			AIEnergyMonitorCondition *condition5 = new AIEnergyMonitorCondition(kPanicStrickenEnergy); +			compound = new AICompoundAction(); +			message = new AIPlayMessageAction("Images/AI/Globals/XGLOB4C", false); +			compound->addAction(message); +			deactivate = new AIDeactivateRuleAction(rule50); +			compound->addAction(deactivate); +			deactivate = new AIDeactivateRuleAction(rule25); +			compound->addAction(deactivate); +			AIRule *rule5 = new AIRule(condition5, compound); + +			g_AIArea->addAIRule(rule5); +			g_AIArea->addAIRule(rule25); +			g_AIArea->addAIRule(rule50); +		} +	} +} + +GameInteraction *Neighborhood::makeInteraction(const InteractionID interactionID) { +	if (interactionID == kNoInteractionID) +		return 0; + +	return new GameInteraction(interactionID, this); +} + +void Neighborhood::newInteraction(const InteractionID interactionID) { +	GameInteraction *interaction = makeInteraction(interactionID); +	_doneWithInteraction = false; + +	if (_currentInteraction) { +		_currentInteraction->stopInteraction(); +		delete _currentInteraction; +	} + +	_currentInteraction = interaction; + +	if (_currentInteraction) +		_currentInteraction->startInteraction(); + +	if (g_AIArea) +		g_AIArea->checkMiddleArea(); +} + +void Neighborhood::bumpIntoWall() { +	_vm->_gfx->shakeTheWorld(15, 30); +} + +void Neighborhood::zoomUpOrBump() { +	Hotspot *zoomSpot = 0; + +	for (HotspotList::iterator it = g_allHotspots.begin(); it != g_allHotspots.end(); it++) { +		Hotspot *hotspot = *it; + +		if ((hotspot->getHotspotFlags() & (kNeighborhoodSpotFlag | kZoomInSpotFlag)) == (kNeighborhoodSpotFlag | kZoomInSpotFlag)) { +			HotspotInfoTable::Entry *entry = findHotspotEntry(hotspot->getObjectID()); + +			if (entry && entry->hotspotRoom == GameState.getCurrentRoom() && entry->hotspotDirection == GameState.getCurrentDirection()) { +				if (zoomSpot) { +					zoomSpot = 0; +					break; +				} else { +					zoomSpot = hotspot; +				} +			} +		} +	} + +	if (zoomSpot) +		zoomTo(zoomSpot); +	else +		bumpIntoWall(); +} + +void Neighborhood::loadLoopSound1(const Common::String &soundName, uint16 volume, TimeValue fadeOut, TimeValue fadeIn, TimeScale fadeScale) { +	FaderMoveSpec faderMove; +	 +	if (!loop1Loaded(soundName)) { +		_loop1SoundString = soundName; + +		if (_soundLoop1.isSoundLoaded()) { +			faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop1Fader.getFaderValue(), fadeOut, 0); +			_loop1Fader.startFaderSync(faderMove); +		} + +		if (!_loop1SoundString.empty()) { +			_soundLoop1.initFromAIFFFile(_loop1SoundString); +			_soundLoop1.loopSound(); +			_loop1Fader.setMasterVolume(_vm->getAmbienceLevel()); +			_loop1Fader.setFaderValue(0); +			faderMove.makeTwoKnotFaderSpec(fadeScale, 0, 0, fadeIn, volume); +			_loop1Fader.startFaderSync(faderMove); +		} else { +			_soundLoop1.disposeSound(); +		} +	} else if (_loop1Fader.getFaderValue() != volume) { +		faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop1Fader.getFaderValue(), fadeIn, volume); +		_loop1Fader.startFaderSync(faderMove); +	} +} + +void Neighborhood::loadLoopSound2(const Common::String &soundName, uint16 volume, TimeValue fadeOut, TimeValue fadeIn, TimeScale fadeScale) { +	FaderMoveSpec faderMove; +	 +	if (!loop2Loaded(soundName)) { +		_loop2SoundString = soundName; + +		if (_soundLoop2.isSoundLoaded()) { +			faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop2Fader.getFaderValue(), fadeOut, 0); +			_loop2Fader.startFaderSync(faderMove); +		} + +		if (!_loop2SoundString.empty()) { +			_soundLoop2.initFromAIFFFile(_loop2SoundString); +			_soundLoop2.loopSound(); +			_loop2Fader.setMasterVolume(_vm->getAmbienceLevel()); +			_loop2Fader.setFaderValue(0); +			faderMove.makeTwoKnotFaderSpec(fadeScale, 0, 0, fadeIn, volume); +			_loop2Fader.startFaderSync(faderMove); +		} else { +			_soundLoop2.disposeSound(); +		} +	} else if (_loop2Fader.getFaderValue() != volume) { +		faderMove.makeTwoKnotFaderSpec(fadeScale, 0, _loop2Fader.getFaderValue(), fadeIn, volume); +		_loop2Fader.startFaderSync(faderMove); +	} +} + +void Neighborhood::takeItemFromRoom(Item *item) { +	item->setItemRoom(kNoNeighborhoodID, kNoRoomID, kNoDirection); +	// Also set the taken item flag. Do this before updating the view frame. +	GameState.setTakenItem(item, true); +	updateViewFrame(); +} + +void Neighborhood::dropItemIntoRoom(Item *item, Hotspot *) { +	item->setItemRoom(getObjectID(), GameState.getCurrentRoom(), GameState.getCurrentDirection()); +	// Also set the taken item flag. Do this before updating the view frame. +	GameState.setTakenItem(item, false); +	updateViewFrame(); +} + +void Neighborhood::makeContinuePoint() { +	_vm->makeContinuePoint(); +} + +void Neighborhood::startLoop1Fader(const FaderMoveSpec &faderMove) { +	_loop1Fader.startFader(faderMove); +} + +void Neighborhood::startLoop2Fader(const FaderMoveSpec &faderMove) { +	_loop2Fader.startFader(faderMove); +} + +// *** Revised 6/13/96 to use the last frame of the extra sequence. +//     Necessary for Cinepak buildup. +void Neighborhood::showExtraView(uint32 extraID) { +	ExtraTable::Entry entry; +	getExtraEntry(extraID, entry); + +	if (entry.movieEnd != 0xffffffff) +		showViewFrame(entry.movieEnd - 1); +} + +void Neighborhood::startExtraLongSequence(const uint32 firstExtra, const uint32 lastExtra, NotificationFlags flags, +		const InputBits interruptionFilter) { +	ExtraTable::Entry firstEntry, lastEntry; +	getExtraEntry(firstExtra, firstEntry); + +	if (firstEntry.movieStart != 0xffffffff) { +		getExtraEntry(lastExtra, lastEntry); +		_lastExtra = firstExtra; +		_turnPush.hide(); +		startMovieSequence(firstEntry.movieStart, lastEntry.movieEnd, flags, kFilterNoInput, interruptionFilter); +	} +} + +void Neighborhood::openCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) { +	if (_croppedMovie.isMovieValid()) +		closeCroppedMovie(); + +	_croppedMovie.initFromMovieFile(movieName); +	_croppedMovie.moveElementTo(left, top); +	_croppedMovie.startDisplaying(); +	_croppedMovie.show(); +} + +void Neighborhood::loopCroppedMovie(const Common::String &movieName, CoordType left, CoordType top) { +	openCroppedMovie(movieName, left, top); +	_croppedMovie.redrawMovieWorld(); +	_croppedMovie.setFlags(kLoopTimeBase); +	_croppedMovie.start(); +} + +void Neighborhood::closeCroppedMovie() { +	_croppedMovie.releaseMovie(); +} + +void Neighborhood::playCroppedMovieOnce(const Common::String &movieName, CoordType left, CoordType top, const InputBits interruptionFilter) {	 +	openCroppedMovie(movieName, left, top); +	_croppedMovie.redrawMovieWorld(); +	_croppedMovie.start(); + +	InputBits oldInterruptionFilter = _interruptionFilter; +	if (oldInterruptionFilter != kFilterNoInput) +		_interruptionFilter = kFilterNoInput; + +	bool saveAllowed = _vm->swapSaveAllowed(false); +	bool openAllowed = _vm->swapLoadAllowed(false); + +	Input input; +	while (_croppedMovie.isRunning() && !_vm->shouldQuit()) { +		_vm->processShell(); +		InputDevice.getInput(input, interruptionFilter); +		if (input.anyInput() || _vm->saveRequested() || _vm->loadRequested() || _vm->shouldQuit()) +			break; +		_vm->_system->delayMillis(10); +	} + +	if (oldInterruptionFilter != kFilterNoInput) +		_interruptionFilter = oldInterruptionFilter; + +	closeCroppedMovie(); +	_vm->swapSaveAllowed(saveAllowed); +	_vm->swapLoadAllowed(openAllowed); +} + +void Neighborhood::playMovieSegment(Movie *movie, TimeValue startTime, TimeValue stopTime) { +	TimeValue oldStart, oldStop; +	movie->getSegment(oldStart, oldStop); + +	if (stopTime == 0xffffffff) +		stopTime = movie->getDuration(); +	 +	movie->setSegment(startTime, stopTime); +	movie->setTime(startTime); +	movie->start(); + +	while (movie->isRunning()) { +		_vm->checkCallBacks(); +		_vm->refreshDisplay(); +		_vm->_system->delayMillis(10); +	} + +	movie->stop(); +	movie->setSegment(oldStart, oldStop); +} + +void Neighborhood::recallToTSASuccess() { +	if (GameState.allTimeZonesFinished()) +		_vm->jumpToNewEnvironment(kFullTSAID, kTSA37, kNorth); +	else +		_vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth); +} + +void Neighborhood::recallToTSAFailure() { +	_vm->jumpToNewEnvironment(kTinyTSAID, kTinyTSA37, kNorth); +} + +void Neighborhood::handleInput(const Input &input, const Hotspot *cursorSpot) { +	if (_vm->getGameMode() == kModeNavigation) { +		if (input.upButtonAnyDown()) +			upButton(input); +		else if (input.downButtonAnyDown()) +			downButton(input); +		else if (input.leftButtonAnyDown()) +			leftButton(input); +		else if (input.rightButtonAnyDown()) +			rightButton(input); +	} + +	InputHandler::handleInput(input, cursorSpot); +} + +void Neighborhood::setHotspotFlags(const HotSpotID id, const HotSpotFlags flags) { +	Hotspot *hotspot = g_allHotspots.findHotspotByID(id); +	hotspot->setMaskedHotspotFlags(flags, flags); +} + +void Neighborhood::setIsItemTaken(const ItemID id) { +	GameState.setTakenItemID(id, _vm->playerHasItemID(id)); +} + +void Neighborhood::upButton(const Input &) { +	moveForward(); +} + +void Neighborhood::leftButton(const Input &) { +	turnLeft(); +} + +void Neighborhood::rightButton(const Input &) { +	turnRight(); +} + +void Neighborhood::downButton(const Input &) { +	if (_inputHandler->wantsCursor()) { +		g_allHotspots.deactivateAllHotspots(); +		_inputHandler->activateHotspots(); + +		for (HotspotList::iterator it = g_allHotspots.begin(); it != g_allHotspots.end(); it++) { +			Hotspot *hotspot = *it; + +			if (hotspot->isSpotActive() && (hotspot->getHotspotFlags() & (kNeighborhoodSpotFlag | kZoomOutSpotFlag)) == (kNeighborhoodSpotFlag | kZoomOutSpotFlag)) { +				HotspotInfoTable::Entry *entry = findHotspotEntry(hotspot->getObjectID()); + +				if (entry && entry->hotspotRoom == GameState.getCurrentRoom() && entry->hotspotDirection == GameState.getCurrentDirection()) { +					Input scratch; +					_inputHandler->clickInHotspot(scratch, hotspot); +					return; +				} +			} +		} +	} +} + +void Neighborhood::initOnePicture(Picture *picture, const Common::String &pictureName, DisplayOrder order, CoordType left, CoordType top, bool show) { +	picture->initFromPICTFile(pictureName); +	picture->setDisplayOrder(order); +	picture->moveElementTo(left, top); +	picture->startDisplaying(); +	if (show) +		picture->show(); +} + +void Neighborhood::initOneMovie(Movie *movie, const Common::String &movieName, DisplayOrder order, CoordType left, CoordType top, bool show) { +	movie->initFromMovieFile(movieName); +	movie->setDisplayOrder(order); +	movie->moveElementTo(left, top); +	movie->startDisplaying(); + +	if (show) +		movie->show(); + +	movie->redrawMovieWorld(); +} + +void Neighborhood::reinstateMonocleInterface() { +	_vm->_gfx->disableErase(); + +	_vm->createInterface(); + +	if (g_AIArea) +		setNextHandler(g_AIArea); + +	init(); + +	moveNavTo(kNavAreaLeft, kNavAreaTop); + +	if (g_interface) +		g_interface->setDate(getDateResID()); + +	if (g_AIArea) +		g_AIArea->restoreAIState(); +} + +void Neighborhood::useIdleTime() { +	if (_doneWithInteraction) { +		newInteraction(kNoInteractionID); +		loadAmbientLoops(); +	} +} + +void timerFunction(FunctionPtr *, void *neighborhood) { +	((Neighborhood *)neighborhood)->timerExpired(((Neighborhood *)neighborhood)->getTimerEvent()); +} + +void Neighborhood::scheduleEvent(const TimeValue time, const TimeScale scale, const uint32 eventType) { +	_eventTimer.stopFuse(); +	_eventTimer.primeFuse(time, scale); +	_timerEvent = eventType; +	_eventTimer.setFunctionPtr(&timerFunction, this); +	_eventTimer.lightFuse(); +} + +void Neighborhood::cancelEvent() { +	_eventTimer.stopFuse(); +} + +void Neighborhood::pauseTimer() { +	_eventTimer.pauseFuse(); +} + +void Neighborhood::resumeTimer() { +	// NOTE: Yes, this function calls pauseFuse! +	// Looks like an original game bug, will need +	// to investigate how this affects gameplay. +	_eventTimer.pauseFuse(); +} + +bool Neighborhood::timerPaused() { +	return _eventTimer.isFusePaused(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/neighborhood.h b/engines/pegasus/neighborhood/neighborhood.h new file mode 100644 index 0000000000..8a38eb3389 --- /dev/null +++ b/engines/pegasus/neighborhood/neighborhood.h @@ -0,0 +1,408 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_H +#define PEGASUS_NEIGHBORHOOD_H + +#include "common/queue.h" +#include "common/str.h" + +#include "pegasus/fader.h" +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/sound.h" +#include "pegasus/timers.h" +#include "pegasus/transition.h" +#include "pegasus/util.h" +#include "pegasus/neighborhood/door.h" +#include "pegasus/neighborhood/exit.h" +#include "pegasus/neighborhood/extra.h" +#include "pegasus/neighborhood/hotspotinfo.h" +#include "pegasus/neighborhood/spot.h" +#include "pegasus/neighborhood/turn.h" +#include "pegasus/neighborhood/view.h" +#include "pegasus/neighborhood/zoom.h" + +namespace Pegasus { + +class PegasusEngine; + +// Pegasus Prime neighborhood id's +static const NeighborhoodID kCaldoriaID = 0; +static const NeighborhoodID kFullTSAID = 1; +static const NeighborhoodID kFinalTSAID = 2; +static const NeighborhoodID kTinyTSAID = 3; +static const NeighborhoodID kPrehistoricID = 4; +static const NeighborhoodID kMarsID = 5; +static const NeighborhoodID kWSCID = 6; +static const NeighborhoodID kNoradAlphaID = 7; +static const NeighborhoodID kNoradDeltaID = 8; +// The sub chase is not really a neighborhood, but we define a constant that is used +// to allow an easy transition out of Norad Alpha. +static const NeighborhoodID kNoradSubChaseID = 1000; + +static const TimeScale kDefaultLoopFadeScale = kThirtyTicksPerSecond; +static const TimeValue kDefaultLoopFadeOut = kHalfSecondPerThirtyTicks; +static const TimeValue kDefaultLoopFadeIn = kHalfSecondPerThirtyTicks; + +enum QueueRequestType { +	kNavExtraRequest, +	kSpotSoundRequest, +	kDelayRequest +}; + +// For delay requests, start is interpreted as the total delay and stop is interpreted +// as the scale the delay is in. +// For extra requests, start and stop are not used. +struct QueueRequest { +	QueueRequestType requestType; +	ExtraID extra; +	TimeValue start, stop; +	InputBits interruptionFilter; +	bool playing; +	NotificationFlags flags; +	Notification *notification; +}; + +bool operator==(const QueueRequest &arg1, const QueueRequest &arg2); +bool operator!=(const QueueRequest &arg1, const QueueRequest &arg2); + +class GameInteraction; +class Item; +class Neighborhood; + +class StriderCallBack : public TimeBaseCallBack { +public: +	StriderCallBack(Neighborhood *); +	virtual ~StriderCallBack() {} + +protected: +	virtual void callBack(); + +	Neighborhood *_neighborhood; +}; + +typedef Common::Queue<QueueRequest> NeighborhoodActionQueue; + +class Neighborhood : public IDObject, public NotificationReceiver, public InputHandler, public Idler { +friend class StriderCallBack; +friend void timerFunction(FunctionPtr *, void *); + +public: +	Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id); +	virtual ~Neighborhood(); + +	virtual void init(); +	virtual void start(); +	virtual void moveNavTo(const CoordType, const CoordType); +	virtual void checkContinuePoint(const RoomID, const DirectionConstant) = 0; +	void makeContinuePoint(); + +	virtual void activateHotspots(); +	virtual void clickInHotspot(const Input &, const Hotspot *); + +	virtual CanMoveForwardReason canMoveForward(ExitTable::Entry &entry); +	virtual CanTurnReason canTurn(TurnDirection turn, DirectionConstant &nextDir); +	virtual CanOpenDoorReason canOpenDoor(DoorTable::Entry &entry); + +	virtual void cantMoveThatWay(CanMoveForwardReason); +	virtual void cantTurnThatWay(CanTurnReason) {} +	virtual void cantOpenDoor(CanOpenDoorReason); +	virtual void arriveAt(const RoomID room, const DirectionConstant direction); +	virtual void turnTo(const DirectionConstant); +	virtual void spotCompleted(); +	virtual void doorOpened(); +	virtual void closeDoorOffScreen(const RoomID, const DirectionConstant) {} + +	virtual void moveForward(); +	virtual void turn(const TurnDirection); +	virtual void turnLeft(); +	virtual void turnRight(); +	virtual void turnUp(); +	virtual void turnDown(); +	virtual void openDoor(); +	virtual void zoomTo(const Hotspot *); + +	virtual void updateViewFrame(); +	 +	void requestExtraSequence(const ExtraID, const NotificationFlags, const InputBits interruptionFilter); +	void requestSpotSound(const TimeValue, const TimeValue, const InputBits interruptionFilter, const NotificationFlags); +	void playSpotSoundSync(const TimeValue in, const TimeValue out); +	void requestDelay(const TimeValue, const TimeScale, const InputBits interruptionFilter, const NotificationFlags); + +	Notification *getNeighborhoodNotification() { return &_neighborhoodNotification; } + +	virtual void getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry); +	virtual void startSpotLoop(TimeValue, TimeValue, NotificationFlags = 0); +	virtual bool actionQueueEmpty() { return _actionQueue.empty(); } +	virtual void showViewFrame(TimeValue); +	virtual void findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry); +	virtual void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits interruptionFilter); +	bool startExtraSequenceSync(const ExtraID, const InputBits); +	virtual void loopExtraSequence(const uint32, NotificationFlags = 0); +	int32 getLastExtra() const { return _lastExtra; } +	virtual void scheduleNavCallBack(NotificationFlags); +	 +	Movie *getNavMovie() { return &_navMovie; } +	bool navMoviePlaying(); +	 +	void setCurrentAlternate(const AlternateID alt) { _currentAlternate = alt; } +	AlternateID getCurrentAlternate() const { return _currentAlternate; } +	 +	void setCurrentActivation(const HotSpotActivationID a) { _currentActivation = a; } +	HotSpotActivationID getCurrentActivation() { return _currentActivation; } +	 +	virtual void playDeathExtra(ExtraID, DeathReason); +	virtual void die(const DeathReason); + +	virtual void setSoundFXLevel(const uint16); +	virtual void setAmbienceLevel(const uint16); +	 +	void forceStridingStop(const RoomID, const DirectionConstant, const AlternateID); +	void restoreStriding(const RoomID, const DirectionConstant, const AlternateID); +	 +	HotspotInfoTable::Entry *findHotspotEntry(const HotSpotID); +	 +	Push *getTurnPush() { return &_turnPush; } +	Picture *getTurnPushPicture() { return &_pushIn; } +	 +	void hideNav(); +	void showNav(); +	 +	virtual void loadAmbientLoops() {} +	 +	virtual void flushGameState() {} + +	virtual Common::String getBriefingMovie(); +	virtual Common::String getEnvScanMovie(); +	virtual uint getNumHints(); +	virtual Common::String getHintMovie(uint); +	virtual bool canSolve(); +	virtual void prepareForAIHint(const Common::String &) {} +	virtual void cleanUpAfterAIHint(const Common::String &) {} +	virtual void doSolve(); + +	virtual bool okayToJump(); + +	virtual AirQuality getAirQuality(const RoomID); +	virtual void checkAirMask() {} +	virtual void checkFlashlight() {} +	virtual void shieldOn() {} +	virtual void shieldOff() {} + +	virtual void loadLoopSound1(const Common::String &, const uint16 volume = 0x100, +			const TimeValue fadeOut = kDefaultLoopFadeOut, const TimeValue fadeIn = kDefaultLoopFadeIn, +			const TimeScale fadeScale = kDefaultLoopFadeScale); +	virtual void loadLoopSound2(const Common::String &, const uint16 volume = 0x100, +			const TimeValue fadeOut = kDefaultLoopFadeOut, const TimeValue fadeIn = kDefaultLoopFadeIn, +			const TimeScale fadeScale = kDefaultLoopFadeScale); +	bool loop1Loaded(const Common::String &soundName) { return _loop1SoundString == soundName; } +	bool loop2Loaded(const Common::String &soundName) { return _loop2SoundString == soundName; } +	void startLoop1Fader(const FaderMoveSpec &); +	void startLoop2Fader(const FaderMoveSpec &); + +	virtual void takeItemFromRoom(Item *); +	virtual void dropItemIntoRoom(Item *, Hotspot *); +	virtual Hotspot *getItemScreenSpot(Item *, DisplayElement *) { return 0; } + +	virtual GameInteraction *makeInteraction(const InteractionID); +	virtual void requestDeleteCurrentInteraction() { _doneWithInteraction = true; } + +	virtual uint16 getDateResID() const = 0; + +	virtual void showExtraView(uint32); +	virtual void startExtraLongSequence(const uint32, const uint32, NotificationFlags, const InputBits interruptionFilter); +	 +	void openCroppedMovie(const Common::String &, CoordType, CoordType); +	void loopCroppedMovie(const Common::String &, CoordType, CoordType); +	void closeCroppedMovie(); +	void playCroppedMovieOnce(const Common::String &, CoordType, CoordType, const InputBits interruptionFilter = kFilterNoInput); + +	void playMovieSegment(Movie *, TimeValue = 0, TimeValue = 0xffffffff); + +	virtual void recallToTSASuccess(); +	virtual void recallToTSAFailure(); +	 +	virtual void pickedUpItem(Item *) {} + +	virtual void handleInput(const Input &, const Hotspot *); +protected: +	PegasusEngine *_vm; +	Common::String _resName; +	 +	virtual Common::String getSoundSpotsName() = 0; +	virtual Common::String getNavMovieName() = 0; + +	// Notification function. +	virtual void receiveNotification(Notification *, const NotificationFlags); + +	// Map info functions. +	virtual void getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry); +	virtual TimeValue getViewTime(const RoomID room, const DirectionConstant direction); +	virtual void getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &doorEntry); +	virtual DirectionConstant getTurnEntry(const RoomID room, const DirectionConstant direction, const TurnDirection turn); +	virtual void getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry); +	virtual void getHotspotEntry(const HotSpotID id, HotspotInfoTable::Entry &hotspotEntry); + +	// Nav movie sequences. +	virtual void startExitMovie(const ExitTable::Entry &); +	virtual void keepStriding(ExitTable::Entry &); +	virtual void stopStriding(); +	virtual void checkStriding(); +	virtual bool stillMoveForward(); +	virtual void scheduleStridingCallBack(const TimeValue, NotificationFlags flags); +	virtual void startZoomMovie(const ZoomTable::Entry &); +	virtual void startDoorOpenMovie(const TimeValue, const TimeValue); +	virtual void startTurnPush(const TurnDirection, const TimeValue, const DirectionConstant); +	virtual void playExtraMovie(const ExtraTable::Entry &, const NotificationFlags, const InputBits interruptionFilter); +	 +	virtual void activateCurrentView(const RoomID, const DirectionConstant, SpotFlags); +	 +	virtual void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *); +	 +	virtual void startSpotOnceOnly(TimeValue, TimeValue); +	 +	virtual void startMovieSequence(const TimeValue, const TimeValue, NotificationFlags, +  			bool loopSequence, const InputBits interruptionFilter, const TimeValue strideStop = 0xffffffff); +	 +	virtual void createNeighborhoodSpots(); +	 +	void resetLastExtra() { _lastExtra = -1; } +	 +	virtual void throwAwayInterface(); + +	// Action queue stuff +	void popActionQueue(); +	void serviceActionQueue(); +	void requestAction(const QueueRequestType, const ExtraID, const TimeValue, const TimeValue, const InputBits, const NotificationFlags); + +	virtual bool prepareExtraSync(const ExtraID); +	virtual bool waitMovieFinish(Movie *, const InputBits); +	 +	virtual InputBits getInputFilter(); + +	// Misc. +	virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant dir); +	virtual void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); +	virtual void getZoomCompassMove(const ZoomTable::Entry &, FaderMoveSpec&); +	virtual void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec&); + +	virtual void setUpAIRules(); +	virtual void setHotspotFlags(const HotSpotID, const HotSpotFlags); +	virtual void setIsItemTaken(const ItemID); + +	virtual void upButton(const Input &); +	virtual void leftButton(const Input &); +	virtual void rightButton(const Input &); +	virtual void downButton(const Input &); + +	void initOnePicture(Picture *, const Common::String &, DisplayOrder, CoordType, CoordType, bool); +	void initOneMovie(Movie *, const Common::String &, DisplayOrder, CoordType, CoordType, bool); + +	void reinstateMonocleInterface(); + +	virtual void newInteraction(const InteractionID); +	virtual void useIdleTime(); +	virtual void bumpIntoWall(); +	virtual void zoomUpOrBump(); + +	void scheduleEvent(const TimeValue, const TimeScale, const uint32); +	void cancelEvent(); +	virtual void timerExpired(const uint32) {} +	bool isEventTimerRunning() { return _eventTimer.isFuseLit(); } +	uint32 getTimerEvent() { return _timerEvent; } + +	void pauseTimer(); +	void resumeTimer(); +	bool timerPaused(); + +	// Navigation Data +	DoorTable _doorTable; +	ExitTable _exitTable; +	ExtraTable _extraTable; +	HotspotInfoTable _hotspotInfoTable; +	SpotTable _spotTable; +	TurnTable _turnTable; +	ViewTable _viewTable; +	ZoomTable _zoomTable; +	AlternateID _currentAlternate; +	HotSpotActivationID _currentActivation; +	 +	int32 _lastExtra; +	DeathReason _extraDeathReason; + +	// Graphics +	Movie _navMovie; +	Picture _pushIn; +	Push _turnPush; + +	// Callbacks +	Notification _neighborhoodNotification; +	NotificationCallBack _navMovieCallBack; +	StriderCallBack _stridingCallBack; +	NotificationCallBack _turnPushCallBack; +	NotificationCallBack _spotSoundCallBack; +	NotificationCallBack _delayCallBack; + +	// Hotspots +	HotspotList _neighborhoodHotspots; + +	// Sounds +	SoundTimeBase _spotSounds; + +	// Action queue +	NeighborhoodActionQueue _actionQueue; +	TimeBase _delayTimer; + +	// Interruptibility... +	InputBits _interruptionFilter; + +	// Nav hiding (for info support...) +	bool _isRunning; + +	GameInteraction *_currentInteraction; +	bool _doneWithInteraction; +	Movie _croppedMovie; +	 +	Sound _soundLoop1; +	Common::String _loop1SoundString; +	SoundFader _loop1Fader; +	 +	Sound _soundLoop2; +	Common::String _loop2SoundString; +	SoundFader _loop2Fader; + +	// The event timer... +	FuseFunction _eventTimer; +	uint32 _timerEvent; +}; + +extern Neighborhood *g_neighborhood; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp new file mode 100644 index 0000000000..ff1f078b15 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.cpp @@ -0,0 +1,219 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/alpha/ecrmonitor.h" + +namespace Pegasus { + +static const NotificationFlags kECRSection1FinishedFlag = 1; +static const NotificationFlags kECRPanFinishedFlag = kECRSection1FinishedFlag << 1; +static const NotificationFlags kECRSection2FinishedFlag = kECRPanFinishedFlag << 1; +static const NotificationFlags kECRNotificationFlags = kECRSection1FinishedFlag | +													kECRPanFinishedFlag | +													kECRSection2FinishedFlag; + +static const TimeValue kSection1Start = 0; +static const TimeValue kSection1Stop = 25; +static const TimeValue kPanStart = 0; +static const TimeValue kPanStop = 20; +static const TimeValue kSection2Start = 26; +static const TimeValue kSection2Stop = 1000; + +// Seems to be a good value for a 20 second pan. +static const CoordType kPanPixelsPerFrame = 8; + +// Interesting times are in seconds. +static const TimeValue s_ECRInterestingTimes[] = { +	0, 1, 2, 10, 25, 26, 56, 64, 72, 80, 88, 94, 102, 108, 116, 999 +}; + +// Index into s_ECRInterestingTimes of interesting time before security pan. +static const int kBeforePanTime = 3; + +// Index into s_ECRInterestingTimes of interesting time after security pan. +static const int kAfterPanTime = 5; + +NoradAlphaECRMonitor::NoradAlphaECRMonitor(Neighborhood *nextHandler) : GameInteraction(kNoradECRMonitorInteractionID, nextHandler), +		_ecrSlideShowNotification(kNoradECRNotificationID, (PegasusEngine *)g_engine), _ecrMovie(kECRSlideShowMovieID), +		_ecrPan(kECRPanID) { +} + +void NoradAlphaECRMonitor::receiveNotification(Notification *, const NotificationFlags flags) { +	if (flags & kECRSection1FinishedFlag) +		ecrSection1Finished(); +	else if (flags & kECRPanFinishedFlag) +		ecrPanFinished(); +	else if (flags & kECRSection2FinishedFlag) +		ecrSection2Finished(); +} + +int NoradAlphaECRMonitor::findCurrentInterestingTime() {	 +	TimeValue time = _ecrMovie.getTime(); +	TimeScale scale = _ecrMovie.getScale(); + +	for (int i = ARRAYSIZE(s_ECRInterestingTimes) - 1; i >= 0; i--) +		if (time >= s_ECRInterestingTimes[i] * scale) +			return i; + +	return 0; +} + +void NoradAlphaECRMonitor::skipToNextInterestingTime() { +	if (_ecrMovie.isRunning()) { +		int interestingTime = findCurrentInterestingTime(); +		_ecrMovie.setTime(s_ECRInterestingTimes[interestingTime + 1] * _ecrMovie.getScale()); +		_ecrMovie.redrawMovieWorld(); +	} else if (_ecrPan.isRunning()) { +		_ecrPanCallBack.cancelCallBack(); +		ecrPanFinished(); +	} +} + +void NoradAlphaECRMonitor::skipToPreviousInterestingTime() { +	if (_ecrPan.isRunning()) { +		_ecrPan.stop(); +		_ecrPan.stopDisplaying(); +		_ecrPanCallBack.cancelCallBack(); + +		_ecrMovieCallBack.setCallBackFlag(kECRSection1FinishedFlag); +		_ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + +		TimeScale scale = _ecrMovie.getScale(); +		_ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1); +		_ecrMovie.setTime(s_ECRInterestingTimes[kBeforePanTime] * scale); +		_ecrMovie.start(); +	} else { +		int interestingTime = findCurrentInterestingTime(); + +		if (interestingTime == kAfterPanTime) { +			_ecrMovieCallBack.cancelCallBack(); +			TimeScale scale = _ecrMovie.getScale(); +			_ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1); +			_ecrMovie.setTime(kSection1Stop * scale); +			ecrSection1Finished(); +		} else if (interestingTime == 0) { +			_ecrMovie.setTime(kSection1Start * _ecrMovie.getScale()); +			_ecrMovie.redrawMovieWorld(); +		} else { +			_ecrMovie.setTime(s_ECRInterestingTimes[interestingTime - 1] * _ecrMovie.getScale()); +			_ecrMovie.redrawMovieWorld(); +		} +	} +} + +void NoradAlphaECRMonitor::handleInput(const Input &input, const Hotspot *cursorSpot) { +	if (isInteracting()) { +		if (input.rightButtonDown()) +			skipToNextInterestingTime(); +		else if (input.leftButtonDown()) +			skipToPreviousInterestingTime(); +		else +			InputHandler::handleInput(input, cursorSpot); +	} else { +		InputHandler::handleInput(input, cursorSpot); +	} +} + +void NoradAlphaECRMonitor::ecrSection1Finished() { +	_ecrMovie.stop(); +	_ecrPanCallBack.setNotification(&_ecrSlideShowNotification); +	_ecrPanCallBack.initCallBack(&_ecrPan, kCallBackAtExtremes); +	_ecrPanCallBack.setCallBackFlag(kECRPanFinishedFlag); +	_ecrSlideShowNotification.notifyMe(this, kECRNotificationFlags, kECRNotificationFlags); +	_ecrPanCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	_ecrPan.startDisplaying(); +	_ecrPan.show(); + +	TimeScale scale = _ecrPan.getScale(); +	_ecrPan.setSegment(kPanStart * scale, kPanStop * scale); +	_ecrPan.setTime(0); +	_ecrPan.start(); +} + +void NoradAlphaECRMonitor::ecrPanFinished() {	 +	_ecrPan.stop(); +	_ecrPan.stopDisplaying(); +	_ecrMovieCallBack.setCallBackFlag(kECRSection2FinishedFlag); +	_ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + +	TimeScale scale = _ecrMovie.getScale(); +	_ecrMovie.setSegment(kSection2Start * scale, kSection2Stop * scale); +	_ecrMovie.start(); +} + +void NoradAlphaECRMonitor::ecrSection2Finished() { +	_ecrMovie.stop(); +	_ecrMovie.stopDisplaying(); +} + +void NoradAlphaECRMonitor::openInteraction() { +	// Initialize the security pan. +	_ecrPan.initFromMovieFile("Images/Norad Alpha/Security Pan.pano"); +	_ecrPan.initMaskFromPICTFile("Images/Norad Alpha/Security Pan Mask"); +	_ecrPan.setBounds(Common::Rect(kECRPanLeft, kECRPanTop, kECRPanRight, kECRPanBottom)); +	_ecrPan.setDisplayOrder(kECRPanOrder); +	_ecrPan.setScale(15); // 15 fps. + +	// Begin the lame ECR slide show. +	// clone2727: I didn't say it :P +	_ecrMovie.initFromMovieFile("Images/Norad Alpha/ECR Monitor Movie"); + +	_ecrMovieCallBack.setNotification(&_ecrSlideShowNotification); +	_ecrMovieCallBack.initCallBack(&_ecrMovie, kCallBackAtExtremes); +	_ecrMovieCallBack.setCallBackFlag(kECRSection1FinishedFlag); + +	_ecrSlideShowNotification.notifyMe(this, kECRNotificationFlags, kECRNotificationFlags); +	_ecrMovieCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + +	_ecrMovie.moveElementTo(kECRSlideShowLeft, kECRSlideShowTop); +	_ecrMovie.setDisplayOrder(kECRMonitorOrder); +	_ecrMovie.startDisplaying(); +	_ecrMovie.show(); +	_ecrMovie.redrawMovieWorld(); + +	TimeScale scale = _ecrMovie.getScale();	 +	_ecrMovie.setSegment(kSection1Start * scale, kSection1Stop * scale + 1); + +	_ecrMovie.start(); +} + +void NoradAlphaECRMonitor::closeInteraction() { +	_ecrMovieCallBack.releaseCallBack(); +	_ecrMovie.stop(); +	_ecrMovie.stopDisplaying(); +	_ecrMovie.releaseMovie(); +	_ecrMovieCallBack.releaseCallBack(); + +	_ecrPanCallBack.releaseCallBack(); +	_ecrPan.stop(); +	_ecrPan.stopDisplaying(); +	_ecrPan.releasePanorama(); +	_ecrPanCallBack.releaseCallBack(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h new file mode 100644 index 0000000000..9e286ed337 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/ecrmonitor.h @@ -0,0 +1,65 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_ecrMONITOR_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_ecrMONITOR_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" +#include "pegasus/neighborhood/norad/alpha/panoramascroll.h" + +namespace Pegasus { + +class NoradAlphaECRMonitor : public GameInteraction, public NotificationReceiver { +public: +	NoradAlphaECRMonitor(Neighborhood *); +	virtual ~NoradAlphaECRMonitor() {} + +	virtual void handleInput(const Input &, const Hotspot *); + +protected: +	virtual void openInteraction(); +	virtual void closeInteraction(); + +	virtual void receiveNotification(Notification *, const NotificationFlags); + +	void ecrSection1Finished(); +	void ecrPanFinished(); +	void ecrSection2Finished(); + +	int findCurrentInterestingTime(); +	void skipToNextInterestingTime(); +	void skipToPreviousInterestingTime(); + +	Notification _ecrSlideShowNotification; +	Movie _ecrMovie; +	NotificationCallBack _ecrMovieCallBack; +	PanoramaScroll _ecrPan; +	NotificationCallBack _ecrPanCallBack; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp new file mode 100644 index 0000000000..61d77bf353 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.cpp @@ -0,0 +1,444 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/alpha/fillingstation.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +static const NotificationFlags kFSPowerUpFinishedFlag = 1; +static const NotificationFlags kFSSplashFinishedFlag = kFSPowerUpFinishedFlag << 1; +static const NotificationFlags kFSIntakeWarningFinishedFlag = kFSSplashFinishedFlag << 1; +static const NotificationFlags kFSIntakeHiliteFinishedFlag = kFSIntakeWarningFinishedFlag << 1; +static const NotificationFlags kFSDispenseHiliteFinishedFlag = kFSIntakeHiliteFinishedFlag << 1; +static const NotificationFlags kFSArHiliteFinishedFlag = kFSDispenseHiliteFinishedFlag << 1; +static const NotificationFlags kFSCO2HiliteFinishedFlag = kFSArHiliteFinishedFlag << 1; +static const NotificationFlags kFSHeHiliteFinishedFlag = kFSCO2HiliteFinishedFlag << 1; +static const NotificationFlags kFSOHiliteFinishedFlag = kFSHeHiliteFinishedFlag << 1; +static const NotificationFlags kFSNHiliteFinishedFlag = kFSOHiliteFinishedFlag << 1; + +static const NotificationFlags kFSNotificationFlags = kFSPowerUpFinishedFlag | +												kFSSplashFinishedFlag | +												kFSIntakeWarningFinishedFlag | +												kFSIntakeHiliteFinishedFlag | +												kFSDispenseHiliteFinishedFlag | +												kFSArHiliteFinishedFlag | +												kFSCO2HiliteFinishedFlag | +												kFSHeHiliteFinishedFlag | +												kFSOHiliteFinishedFlag | +												kFSNHiliteFinishedFlag; + +static const int16 kNoState = 0; +static const int16 kMainMenu = 1; +static const int16 kWaitingForAttach = 2; +static const int16 kDispenseMenu = 3; +static const int16 kWaitingForDispense = 4; + +// Dummy itemIDs +static const ItemID kCO2Item = 10000; +static const ItemID kHeItem = 10001; + +// Interactive points. +static const TimeValue kFSPowerUpStartStart = 0; +static const TimeValue kFSPowerUpStartStop = 600; +static const TimeValue kFSSplashStart = 600; +static const TimeValue kFSSplashStop = 7800; +static const TimeValue kFSSplashIntakeStart = 7800; +static const TimeValue kFSSplashIntakeStop = 18600; + +static const TimeValue kFSMainMenu = 18600; +static const TimeValue kFSIntakeHiliteStart = 19200; +static const TimeValue kFSIntakeHiliteStop = 19800; +static const TimeValue kFSDispenseHiliteStart = 19800; +static const TimeValue kFSDispenseHiliteStop = 20400; + +static const TimeValue kFSDispenseMenu = 20400; + +static const TimeValue kFSArHiliteStart = 21000; +static const TimeValue kFSArHiliteStop = 21600; +static const TimeValue kFSArAttach = 21600; +static const TimeValue kFSArFilledStart = 22200; +static const TimeValue kFSArFilledStop = 25200; +static const TimeValue kFSArIncompatibleStart = 25200; +static const TimeValue kFSArIncompatibleStop = 30000; + +static const TimeValue kFSCO2HiliteStart = 30000; +static const TimeValue kFSCO2HiliteStop = 30600; +static const TimeValue kFSCO2Attach = 30600; +static const TimeValue kFSCO2FilledStart = 31200; +static const TimeValue kFSCO2FilledStop = 34200; +static const TimeValue kFSCO2IncompatibleStart = 34200; +static const TimeValue kFSCO2IncompatibleStop = 39000; + +static const TimeValue kFSHeHiliteStart = 39000; +static const TimeValue kFSHeHiliteStop = 39600; +static const TimeValue kFSHeAttach = 39600; +static const TimeValue kFSHeFilledStart = 40200; +static const TimeValue kFSHeFilledStop = 43200; +static const TimeValue kFSHeIncompatibleStart = 43200; +static const TimeValue kFSHeIncompatibleStop = 48000; + +static const TimeValue kFSOHiliteStart = 48000; +static const TimeValue kFSOHiliteStop = 48600; +static const TimeValue kFSOAttach = 48600; +static const TimeValue kFSOFilledStart = 49200; +static const TimeValue kFSOFilledStop = 52200; +static const TimeValue kFSOIncompatibleStart = 52200; +static const TimeValue kFSOIncompatibleStop = 57000; + +static const TimeValue kFSNHiliteStart = 57000; +static const TimeValue kFSNHiliteStop = 57600; +static const TimeValue kFSNAttach = 57600; +static const TimeValue kFSNFilledStart = 58200; +static const TimeValue kFSNFilledStop = 61200; +static const TimeValue kFSNIncompatibleStart = 61200; +static const TimeValue kFSNIncompatibleStop = 66000; + +static const TimeValue kFSIntakeMenu = 66000; +static const TimeValue kFSIntakeInProgressStart = 66600; +static const TimeValue kFSIntakeInProgressStop = 69600; + +NoradAlphaFillingStation::NoradAlphaFillingStation(Neighborhood *owner) : GameInteraction(kNoradFillingStationInteractionID, owner), +		_rightSideMovie(kN01RightSideID), _rightSideNotification(kNoradFillingStationNotificationID, ((PegasusEngine *)g_engine)) { +	_state = kNoState; +} + +void NoradAlphaFillingStation::openInteraction() {	 +	_rightSideMovie.initFromMovieFile("Images/Norad Alpha/N01W Right Side"); +	_rightSideMovie.moveElementTo(kNoradAlpha01RightSideLeft, kNoradAlpha01RightSideTop); +	_rightSideMovie.setDisplayOrder(kN01RightSideOrder); +	_rightSideMovie.startDisplaying(); +	_rightSideCallBack.setNotification(&_rightSideNotification); +	_rightSideCallBack.initCallBack(&_rightSideMovie, kCallBackAtExtremes); +	_rightSideCallBack.setCallBackFlag(kFSPowerUpFinishedFlag); +	_rightSideNotification.notifyMe(this, kFSNotificationFlags, kFSNotificationFlags); +	_rightSideCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	_rightSideMovie.show(); +	_rightSideMovie.redrawMovieWorld(); +	_rightSideMovie.setSegment(kFSPowerUpStartStart, kFSPowerUpStartStop); +} + +void NoradAlphaFillingStation::initInteraction() { +	allowInput(false); + +	_rightSideMovie.setRate(2); +} + +void NoradAlphaFillingStation::closeInteraction() { +	_rightSideMovie.stop(); +	_rightSideMovie.stopDisplaying(); +	_rightSideMovie.releaseMovie(); +	_rightSideCallBack.releaseCallBack(); +	((NoradAlpha *)getOwner())->turnOffFillingStation(); +} + +void NoradAlphaFillingStation::setStaticState(TimeValue time, int16 state) { +	_rightSideMovie.stop(); +	_rightSideMovie.setSegment(0, _rightSideMovie.getDuration()); +	_rightSideMovie.setTime(time); +	_state = state; +	allowInput(true); +} + +void NoradAlphaFillingStation::setSegmentState(TimeValue start, TimeValue stop, NotificationFlags flag, int16 state) {	 +	_rightSideMovie.stop(); +	_rightSideMovie.setSegment(start, stop); +	_rightSideMovie.setTime(start); +	_rightSideCallBack.setCallBackFlag(flag); +	_rightSideCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	_state = state; +	allowInput(false); +	_rightSideMovie.setRate(2); +} + +void NoradAlphaFillingStation::powerUpFinished() { +	((NoradAlpha *)getOwner())->turnOnFillingStation(); +	setSegmentState(kFSSplashStart, kFSSplashStop, kFSSplashFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::splashFinished() { +	if (GameState.getNoradGassed()) +		setSegmentState(kFSSplashIntakeStart, kFSSplashIntakeStop, kFSIntakeWarningFinishedFlag, kNoState); +	else +		intakeWarningFinished(); +} + +void NoradAlphaFillingStation::intakeWarningFinished() { +	setStaticState(kFSMainMenu, kMainMenu); +} + +void NoradAlphaFillingStation::showIntakeInProgress(uint16 numSeconds) {	 +	if (numSeconds == 0) { +		setSegmentState(kFSIntakeInProgressStart, kFSIntakeInProgressStop, kFSIntakeWarningFinishedFlag, kNoState); +		Item *item = ((NoradAlpha *)getOwner())->getFillingItem(); + +		if (item->getObjectID() == kGasCanister) { +			GameState.setNoradGassed(true); +			((NoradAlpha *)getOwner())->loadAmbientLoops(); +			getOwner()->restoreStriding(kNorad03, kEast, kAltNoradAlphaNormal); +		} +	} else { +		setSegmentState(kFSIntakeInProgressStart, kFSIntakeInProgressStart + _rightSideMovie.getScale() * numSeconds, +				kFSIntakeWarningFinishedFlag, kNoState); +	} +} + +void NoradAlphaFillingStation::intakeHighlightFinished() {	 +	_rightSideMovie.stop(); + +	if (GameState.getNoradGassed()) { +		showIntakeInProgress(2); +	} else { +		Item *item = ((NoradAlpha *)getOwner())->getFillingItem(); +		if (item) +			showIntakeInProgress(0); +		else +			setStaticState(kFSIntakeMenu, kWaitingForAttach); +	} +} + +void NoradAlphaFillingStation::dispenseHighlightFinished() { +	setStaticState(kFSDispenseMenu, kDispenseMenu); +} + +void NoradAlphaFillingStation::dispenseGas() {	 +	Item *item = ((NoradAlpha *)getOwner())->getFillingItem(); + +	if (item) { +		if (item->getObjectID() != _dispenseItemID) +			switch (_dispenseItemID) { +			case kArgonCanister: +				setSegmentState(kFSArIncompatibleStart, kFSArIncompatibleStop, +						kFSIntakeWarningFinishedFlag, kNoState); +				break; +			case kCO2Item: +				setSegmentState(kFSCO2IncompatibleStart, kFSCO2IncompatibleStop, +						kFSIntakeWarningFinishedFlag, kNoState); +				break; +			case kHeItem: +				setSegmentState(kFSHeIncompatibleStart, kFSHeIncompatibleStop, +						kFSIntakeWarningFinishedFlag, kNoState); +				break; +			case kAirMask: +				setSegmentState(kFSOIncompatibleStart, kFSOIncompatibleStop, +						kFSIntakeWarningFinishedFlag, kNoState); +				break; +			case kNitrogenCanister: +				setSegmentState(kFSNIncompatibleStart, kFSNIncompatibleStop, +						kFSIntakeWarningFinishedFlag, kNoState); +				break; +			} +		else { +			if (_dispenseItemID == kArgonCanister) { +				setSegmentState(kFSArFilledStart, kFSArFilledStop, kFSIntakeWarningFinishedFlag, kNoState); +				item->setItemState(kArgonFull); +				GameState.setScoringFilledArgonCanister(true); +			} else if (_dispenseItemID == kAirMask) { +				setSegmentState(kFSOFilledStart, kFSOFilledStop, kFSIntakeWarningFinishedFlag, kNoState); +				((AirMask *)item)->refillAirMask(); +				GameState.setScoringFilledOxygenCanister(true); +			} else if (_dispenseItemID == kNitrogenCanister) { +				setSegmentState(kFSNFilledStart, kFSNFilledStop, kFSIntakeWarningFinishedFlag, kNoState); +				item->setItemState(kNitrogenFull); +			} +		} +	} else { +		switch (_dispenseItemID) { +		case kArgonCanister: +			setStaticState(kFSArAttach, kWaitingForDispense); +			break; +		case kCO2Item: +			setStaticState(kFSCO2Attach, kWaitingForDispense); +			break; +		case kHeItem: +			setStaticState(kFSHeAttach, kWaitingForDispense); +			break; +		case kAirMask: +			setStaticState(kFSOAttach, kWaitingForDispense); +			break; +		case kNitrogenCanister: +			setStaticState(kFSNAttach, kWaitingForDispense); +			break; +		} +	} +} + +void NoradAlphaFillingStation::ArHighlightFinished() { +	_dispenseItemID = kArgonCanister; +	dispenseGas(); +} + +void NoradAlphaFillingStation::CO2HighlightFinished() { +	_dispenseItemID = kCO2Item; +	dispenseGas(); +} + +void NoradAlphaFillingStation::HeHighlightFinished() { +	_dispenseItemID = kHeItem; +	dispenseGas(); +} + +void NoradAlphaFillingStation::OHighlightFinished() { +	_dispenseItemID = kAirMask; +	dispenseGas(); +} + +void NoradAlphaFillingStation::NHighlightFinished() { +	_dispenseItemID = kNitrogenCanister; +	dispenseGas(); +} + +void NoradAlphaFillingStation::receiveNotification(Notification *, const NotificationFlags flags) { +	switch (flags) { +	case kFSPowerUpFinishedFlag: +		powerUpFinished(); +		break; +	case kFSSplashFinishedFlag: +		splashFinished(); +		break; +	case kFSIntakeWarningFinishedFlag: +		intakeWarningFinished(); +		break; +	case kFSIntakeHiliteFinishedFlag: +		intakeHighlightFinished(); +		break; +	case kFSDispenseHiliteFinishedFlag: +		dispenseHighlightFinished(); +		break; +	case kFSArHiliteFinishedFlag: +		ArHighlightFinished(); +		break; +	case kFSCO2HiliteFinishedFlag: +		CO2HighlightFinished(); +		break; +	case kFSHeHiliteFinishedFlag: +		HeHighlightFinished(); +		break; +	case kFSOHiliteFinishedFlag: +		OHighlightFinished(); +		break; +	case kFSNHiliteFinishedFlag: +		NHighlightFinished(); +		break; +	} +} + +void NoradAlphaFillingStation::handleInput(const Input &input, const Hotspot *cursorSpot) { +	InputHandler::handleInput(input, cursorSpot); +} + +void NoradAlphaFillingStation::clickInIntake() { +	setSegmentState(kFSIntakeHiliteStart, kFSIntakeHiliteStop, kFSIntakeHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInDispense() { +	setSegmentState(kFSDispenseHiliteStart, kFSDispenseHiliteStop, kFSDispenseHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInAr() { +	setSegmentState(kFSArHiliteStart, kFSArHiliteStop, kFSArHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInCO2() { +	setSegmentState(kFSCO2HiliteStart, kFSCO2HiliteStop, kFSCO2HiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInHe() { +	setSegmentState(kFSHeHiliteStart, kFSHeHiliteStop, kFSHeHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInO() { +	setSegmentState(kFSOHiliteStart, kFSOHiliteStop, kFSOHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInN() { +	setSegmentState(kFSNHiliteStart, kFSNHiliteStop, kFSNHiliteFinishedFlag, kNoState); +} + +void NoradAlphaFillingStation::clickInHotspot(const Input &input, const Hotspot *spot) { +	GameInteraction::clickInHotspot(input, spot); + +	switch (spot->getObjectID()) { +	case kNorad01IntakeSpotID: +		clickInIntake(); +		break; +	case kNorad01DispenseSpotID: +		clickInDispense(); +		break; +	case kNorad01ArSpotID: +		clickInAr(); +		break; +	case kNorad01CO2SpotID: +		clickInCO2(); +		break; +	case kNorad01HeSpotID: +		clickInHe(); +		break; +	case kNorad01OSpotID: +		clickInO(); +		break; +	case kNorad01NSpotID: +		clickInN(); +		break; +	} +} + +void NoradAlphaFillingStation::activateHotspots() { +	GameInteraction::activateHotspots(); + +	switch (_state) { +	case kMainMenu: +		g_allHotspots.activateOneHotspot(kNorad01IntakeSpotID); +		g_allHotspots.activateOneHotspot(kNorad01DispenseSpotID); +		break; +	case kDispenseMenu: +		g_allHotspots.activateOneHotspot(kNorad01ArSpotID); +		g_allHotspots.activateOneHotspot(kNorad01CO2SpotID); +		g_allHotspots.activateOneHotspot(kNorad01HeSpotID); +		g_allHotspots.activateOneHotspot(kNorad01OSpotID); +		g_allHotspots.activateOneHotspot(kNorad01NSpotID); +		break; +	} +} + +void NoradAlphaFillingStation::newFillingItem(Item *item) { +	switch (_state) { +	case kWaitingForAttach: +		if (item) +			showIntakeInProgress(0); +		break; +	case kWaitingForDispense: +		dispenseGas(); +		break; +	default: +		break; +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/fillingstation.h b/engines/pegasus/neighborhood/norad/alpha/fillingstation.h new file mode 100644 index 0000000000..eb2088e373 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/fillingstation.h @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_FILLINGSTATION_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_FILLINGSTATION_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +class Item; + +class NoradAlphaFillingStation : public GameInteraction, public NotificationReceiver { +public: +	NoradAlphaFillingStation(Neighborhood *); +	virtual ~NoradAlphaFillingStation() {} + +	virtual void handleInput(const Input &, const Hotspot *); + +	virtual void clickInHotspot(const Input &, const Hotspot *); +	virtual void activateHotspots(); + +	void newFillingItem(Item *); + +protected: +	void receiveNotification(Notification *, const NotificationFlags); + +	virtual void openInteraction(); +	virtual void initInteraction(); +	virtual void closeInteraction(); + +	void powerUpFinished(); +	void splashFinished(); +	void intakeWarningFinished(); +	void intakeHighlightFinished(); +	void dispenseHighlightFinished(); +	void ArHighlightFinished(); +	void CO2HighlightFinished(); +	void HeHighlightFinished(); +	void OHighlightFinished(); +	void NHighlightFinished(); + +	void showIntakeInProgress(uint16); + +	void clickInIntake(); +	void clickInDispense(); +	void clickInAr(); +	void clickInCO2(); +	void clickInHe(); +	void clickInO(); +	void clickInN(); + +	void dispenseGas(); + +	void setStaticState(TimeValue, int16); +	void setSegmentState(TimeValue, TimeValue, NotificationFlags, int16); + +	Movie _rightSideMovie; +	Notification _rightSideNotification; +	NotificationCallBack _rightSideCallBack; +	int16 _state; +	ItemID _dispenseItemID; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp new file mode 100644 index 0000000000..8391a3ff10 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.cpp @@ -0,0 +1,763 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/alpha/ecrmonitor.h" +#include "pegasus/neighborhood/norad/alpha/fillingstation.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +const uint32 NoradAlpha::_noradAlphaClawExtras[22] = { +	kN22ClawFromAToB, +	kN22ClawALoop, +	kN22ClawAPinch, +	kN22ClawACounterclockwise, +	kN22ClawAClockwise, +	kN22ClawFromBToA, +	kN22ClawFromBToC, +	kN22ClawFromBToD, +	kN22ClawBLoop, +	kN22ClawBPinch, +	kN22ClawBCounterclockwise, +	kN22ClawBClockwise, +	kN22ClawFromCToB, +	kN22ClawCLoop, +	kN22ClawCPinch, +	kN22ClawCCounterclockwise, +	kN22ClawCClockwise, +	kN22ClawFromDToB, +	kN22ClawDLoop, +	kN22ClawDPinch, +	kN22ClawDCounterclockwise, +	kN22ClawDClockwise +}; + +NoradAlpha::NoradAlpha(InputHandler *nextHandler, PegasusEngine *owner) : Norad(nextHandler, owner, "Norad Alpha", kNoradAlphaID) { +	_elevatorUpRoomID = kNorad11South; +	_elevatorDownRoomID = kNorad12South; +	_elevatorUpSpotID = kNorad12ElevatorUpSpotID; +	_elevatorDownSpotID = kNorad11ElevatorDownSpotID; + +	_subRoomEntryRoom1 = kNorad10; +	_subRoomEntryDir1 = kEast; +	_subRoomEntryRoom2 = kNorad21; +	_subRoomEntryDir2 = kWest; +	_upperPressureDoorRoom = kNorad10East; +	_lowerPressureDoorRoom = kNorad21West; + +	_upperPressureDoorUpSpotID = kAlphaUpperPressureDoorUpSpotID; +	_upperPressureDoorDownSpotID = kAlphaUpperPressureDoorDownSpotID; +	_upperPressureDoorAbortSpotID = kNorad10EastOutSpotID; + +	_lowerPressureDoorUpSpotID = kAlphaLowerPressureDoorUpSpotID; +	_lowerPressureDoorDownSpotID = kAlphaLowerPressureDoorDownSpotID; +	_lowerPressureDoorAbortSpotID = kNorad21WestOutSpotID; + +	_pressureSoundIn = kPressureDoorIntro1In; +	_pressureSoundOut = kPressureDoorIntro1Out; +	_equalizeSoundIn = kPressureDoorIntro2In; +	_equalizeSoundOut = kPressureDoorIntro2Out; +	_accessDeniedIn = kAlphaAccessDeniedIn; +	_accessDeniedOut = kAlphaAccessDeniedOut; + +	_platformRoom = kNorad19West; +	_subControlRoom = kNorad22West; + +	_subPrepFailed = false; + +	setIsItemTaken(kGasCanister); +} + +void NoradAlpha::init() { +	Norad::init(); +	 +	Hotspot *hotspot = g_allHotspots.findHotspotByID(kN01GasCanisterSpotID); +	hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); +	HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasCanisterSpotID); +	hotspotEntry->hotspotItem = kGasCanister; +	 +	hotspot = g_allHotspots.findHotspotByID(kN01ArgonCanisterSpotID); +	hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); +	hotspotEntry = findHotspotEntry(kN01ArgonCanisterSpotID); +	hotspotEntry->hotspotItem = kArgonCanister; +	 +	hotspot = g_allHotspots.findHotspotByID(kN01NitrogenCanisterSpotID); +	hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); +	hotspotEntry = findHotspotEntry(kN01NitrogenCanisterSpotID); +	hotspotEntry->hotspotItem = kNitrogenCanister; +	 +	hotspot = g_allHotspots.findHotspotByID(kN01AirMaskSpotID); +	hotspot->setMaskedHotspotFlags(kPickUpItemSpotFlag, kPickUpItemSpotFlag); +	hotspotEntry = findHotspotEntry(kN01AirMaskSpotID); +	hotspotEntry->hotspotItem = kAirMask; +	 +	hotspot = g_allHotspots.findHotspotByID(kN01GasOutletSpotID); +	hotspot->setMaskedHotspotFlags(kDropItemSpotFlag, kDropItemSpotFlag); +} + +void NoradAlpha::start() {	 +	if (g_energyMonitor) { +		g_energyMonitor->stopEnergyDraining(); +		g_energyMonitor->restoreLastEnergyValue(); +		_vm->resetEnergyDeathReason(); +		g_energyMonitor->startEnergyDraining(); +	} + +	NeighborhoodID itemNeighborhood; +	RoomID itemRoom; +	DirectionConstant itemDirection; + +	Item *item = (Item *)g_allItems.findItemByID(kGasCanister); +	item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + +	if (itemNeighborhood == getObjectID()) { +		_fillingStationItem = item; +	} else { +		item = (Item *)g_allItems.findItemByID(kAirMask); +		item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + +		if (itemNeighborhood == getObjectID()) { +			_fillingStationItem = item; +		} else { +			item = (Item *)g_allItems.findItemByID(kNitrogenCanister); +			item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + +			if (itemNeighborhood == getObjectID()) { +				_fillingStationItem = item; +			} else { +				item = (Item *)g_allItems.findItemByID(kArgonCanister); +				item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); +				if (itemNeighborhood == getObjectID()) +					_fillingStationItem = item; +				else +					_fillingStationItem = 0; +			} +		} +	} + +	if (!GameState.getNoradGassed()) +		forceStridingStop(kNorad03, kEast, kAltNoradAlphaNormal); + +	GameState.setNoradArrivedFromSub(false); +	Norad::start(); +} + +void NoradAlpha::setUpAIRules() {	 +	Neighborhood::setUpAIRules(); + +	if (g_AIArea) { +		AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Norad/XN01WD1", false); +		AIHasItemCondition *hasGasCanisterCondition = new AIHasItemCondition(kGasCanister); +		AIRule *rule = new AIRule(hasGasCanisterCondition, messageAction); +		g_AIArea->addAIRule(rule); +	} +} + +bool NoradAlpha::okayToJump() { +	bool result = Neighborhood::okayToJump(); + +	if (!result) +		playSpotSoundSync(kAlphaCantTransportIn, kAlphaCantTransportOut); + +	return result; +} + +void NoradAlpha::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { +	if (entry.extra == kNorad19ExitToSub) { +		compassMove.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, entry.movieStart, 270 + kSubPlatformCompassAngle, +				entry.movieEnd, 90 + 20 + 360); +		compassMove.insertFaderKnot(entry.movieStart + 10 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle); +		compassMove.insertFaderKnot(entry.movieStart + 29 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle + 20); +		compassMove.insertFaderKnot(entry.movieStart + 52 * kNoradAlphaFrameDuration, 270 + kSubPlatformCompassAngle + 20); +		compassMove.insertFaderKnot(entry.movieStart + 84 * kNoradAlphaFrameDuration, 360 + 90); +		compassMove.insertFaderKnot(entry.movieStart + 198 * kNoradAlphaFrameDuration, 360 + 90); +		compassMove.insertFaderKnot(entry.movieStart + 270 * kNoradAlphaFrameDuration, 360 + 90 + 15); +		compassMove.insertFaderKnot(entry.movieStart + 280 * kNoradAlphaFrameDuration, 360 + 90 + 20); +	} else { +		Norad::getExtraCompassMove(entry, compassMove); +	} +} + +void NoradAlpha::playClawMonitorIntro() { +	playSpotSoundSync(kLoadClawIntroIn, kLoadClawIntroOut); +} + +GameInteraction *NoradAlpha::makeInteraction(const InteractionID interactionID) { +	switch (interactionID) { +	case kNoradECRMonitorInteractionID: +		return new NoradAlphaECRMonitor(this); +	case kNoradFillingStationInteractionID: +		return new NoradAlphaFillingStation(this); +	} + +	return Norad::makeInteraction(interactionID); +} + +void NoradAlpha::loadAmbientLoops() { +	// clone2727 would like to point out that the following comment does not quite +	// match the code logic below + +/* +	Logic: +	 +	loop sound 1: +		if gassed, +			play warning loop of some sort +		else +			play nothing +	loop sound 2: +		if gassed and not wearing air mask +			if in ECR +				play breathing water loop +			else +				play breathing +		else +			if in ECR +				play water loop +			if at N07 north +				play unmanned loop +*/ + +	if (!GameState.getNoradSeenTimeStream()) +		return; + +	RoomID room = GameState.getCurrentRoom(); +	if (GameState.getNoradGassed()) { +		if (room >= kNorad11 && room <= kNorad19West) +			loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3); +		else if (room >= kNorad21 && room <= kNorad22West) +			loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3); +		else +			loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume); +	} else { +		loadLoopSound1(""); +	} + +	if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) { +		if (room >= kNorad01 && room <= kNorad01West) { +			loadLoopSound2("Sounds/Norad/Breathing Water.22K.AIFF", kNoradSuckWindVolume); +		} else if (room == kNorad02) { +			if (GameState.isCurrentDoorOpen()) +				loadLoopSound2("Sounds/Norad/Breathing Water.22K.AIFF", kNoradSuckWindVolume); +			else +				loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0); +		} else { +			loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0); +		} +	} else { +		if (room >= kNorad01 && room <= kNorad01West) { +			loadLoopSound2("Sounds/Norad/WATER FLOWING.AIFF", 0x100 / 2); +		} else if (room == kNorad02) { +			if (GameState.isCurrentDoorOpen()) +				loadLoopSound2("Sounds/Norad/WATER FLOWING.AIFF", 0x100 / 2); +			else +				loadLoopSound2(""); +		} else { +			loadLoopSound2(""); +		} +	} +	 +} + +void NoradAlpha::checkContinuePoint(const RoomID room, const DirectionConstant direction) { +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kNorad02, kEast): +	case MakeRoomView(kNorad06, kEast): +	case MakeRoomView(kNorad11, kEast): +	case MakeRoomView(kNorad15, kEast): +	case MakeRoomView(kNorad19, kWest): +	case MakeRoomView(kNorad21, kSouth): +		makeContinuePoint(); +		break; +	} +} + +void NoradAlpha::arriveAt(const RoomID room, const DirectionConstant direction) { +	Norad::arriveAt(room, direction); + +	switch (GameState.getCurrentRoom()) { +	case kNorad01: +		arriveAtNorad01(); +		break; +	case kNorad01East: +		arriveAtNorad01East(); +		break; +	case kNorad01West: +		arriveAtNorad01West(); +		break; +	case kNorad04: +		arriveAtNorad04(); +		break; +	case kNorad07North: +		GameState.setScoringSawUnconsciousOperator(true); +		break; +	case kNorad11: +		GameState.setScoringWentThroughPressureDoor(true); +		break; +	case kNorad22: +		arriveAtNorad22(); +		break; +	} +} + +void NoradAlpha::arriveAtNorad01() { +	if (!GameState.getNoradSeenTimeStream() && GameState.getCurrentDirection() == kSouth) { +		GameState.setNoradN22MessagePlayed(false); +		requestExtraSequence(kNoradArriveFromTSA, kExtraCompletedFlag, kFilterNoInput); +		// You are no match for me, human. +		requestExtraSequence(kNorad01RobotTaunt, kExtraCompletedFlag, kFilterNoInput); +	} +} + +void NoradAlpha::arriveAtNorad01East() { +	GameState.setScoringSawSecurityMonitor(true); +	newInteraction(kNoradECRMonitorInteractionID); +} + +void NoradAlpha::arriveAtNorad01West() { +	newInteraction(kNoradFillingStationInteractionID); +} + +void NoradAlpha::arriveAtNorad04() { +	if (GameState.getCurrentDirection() == kEast && !GameState.getNoradGassed()) +		playDeathExtra(kNorad04EastDeath, kDeathWokeUpNorad); +} + +void NoradAlpha::arriveAtNorad22() { +	if (!GameState.getNoradN22MessagePlayed() && GameState.getCurrentDirection() == kSouth) { +		startExtraSequence(kNorad22SouthIntro, kExtraCompletedFlag, kFilterNoInput); +		GameState.setNoradN22MessagePlayed(true); +	} +} + +void NoradAlpha::bumpIntoWall() { +	requestSpotSound(kAlphaBumpIntoWallIn, kAlphaBumpIntoWallOut, kFilterNoInput, 0); +	Neighborhood::bumpIntoWall(); +} + +void NoradAlpha::receiveNotification(Notification *notification, const NotificationFlags flags) { +	if ((flags & kExtraCompletedFlag) != 0) { +		switch (_lastExtra) { +		case kNoradArriveFromTSA: +			GameState.setNoradSeenTimeStream(true); +			loadAmbientLoops(); +			break; +		case kNorad01RobotTaunt: +			g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN01SB", false, kWarningInterruption); +			_interruptionFilter = kFilterAllInput; +			makeContinuePoint(); +			break; +		} +	} + +	Norad::receiveNotification(notification, flags); + +	if ((flags & kExtraCompletedFlag) != 0) { +		switch (_lastExtra) { +		case kNorad22SouthIntro: +			loopExtraSequence(kNorad22SouthReply); +			playSpotSoundSync(kN22ReplyIn, kN22ReplyOut); +			startExtraSequence(kNorad22SouthFinish, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kNorad22SouthFinish: +			_interruptionFilter = kFilterAllInput; +			// Force ArriveAt to do its thing... +			GameState.setCurrentRoom(kNorad21); +			arriveAt(kNorad22, kSouth); +			break; +		} +	} + +	g_AIArea->checkMiddleArea(); +} + +void NoradAlpha::getZoomEntry(const HotSpotID spotID, ZoomTable::Entry &entry) { +	Norad::getZoomEntry(spotID, entry); + +	ExtraTable::Entry extra; + +	if (spotID == kNorad01GasSpotID) { +		if (_fillingStationItem) { +			if (_fillingStationItem->getObjectID() == kGasCanister) { +				getExtraEntry(kNorad01ZoomInWithGasCanister, extra); +				entry.movieStart = extra.movieStart; +				entry.movieEnd = extra.movieEnd; +			} else { +				entry.clear(); +			} +		} +	} else if (spotID == kNorad01GasOutSpotID) { +		if (_fillingStationItem) { +			if (_fillingStationItem->getObjectID() == kGasCanister) { +				getExtraEntry(kNorad01ZoomOutWithGasCanister, extra); +				entry.movieStart = extra.movieStart; +				entry.movieEnd = extra.movieEnd; +			} else { +				entry.clear(); +			} +		} +	} +} + +TimeValue NoradAlpha::getViewTime(const RoomID room, const DirectionConstant direction) { +	ExtraTable::Entry entry; +	 +	if (room == kNorad01 && direction == kSouth && !GameState.getNoradSeenTimeStream()) { +		getExtraEntry(kNoradArriveFromTSA, entry); +		return entry.movieStart; +	} + +	if (room == kNorad01 && direction == kWest) { +		if (!_fillingStationItem) { +			return Norad::getViewTime(room, direction); +		} else { +			getExtraEntry(kN01WGasCanister, entry); +			return entry.movieStart; +		} +	} else if (room == kNorad01West && direction == kWest) { +		uint32 extraID = 0xffffffff; +		if (_fillingStationItem) { +			switch (_fillingStationItem->getObjectID()) { +			case kArgonCanister: +				if (GameState.getNoradFillingStationOn()) +					extraID = kN01WZArgonCanisterLit; +				else +					extraID = kN01WZArgonCanisterDim; +				break; +			case kGasCanister: +				if (GameState.getNoradFillingStationOn()) +					extraID = kN01WZGasCanisterLit; +				else +					extraID = kN01WZGasCanisterDim; +				break; +			case kAirMask: +				if (GameState.getNoradFillingStationOn()) +					extraID = kN01WZAirMaskLit; +				else +					extraID = kN01WZAirMaskDim; +				break; +			case kNitrogenCanister: +				if (GameState.getNoradFillingStationOn()) +					extraID = kN01WZNitrogenCanisterLit; +				else +					extraID = kN01WZNitrogenCanisterDim; +				break; +			default: +				// Should never happen. +				break; +			} +		} else if (GameState.getNoradFillingStationOn()) { +			extraID = kN01WZEmptyLit; +		} + +		if (extraID == 0xffffffff) { +			return Norad::getViewTime(room, direction); +		} else { +			getExtraEntry(extraID, entry); +			return entry.movieStart; +		} +	} + +	return Norad::getViewTime(room, direction); +} + +void NoradAlpha::turnOnFillingStation() { +	if (GameState.getCurrentRoom() == kNorad01West && !GameState.getNoradFillingStationOn()) { +		GameState.setNoradFillingStationOn(true); +		updateViewFrame(); +	} +} + +void NoradAlpha::turnOffFillingStation() { +	if (GameState.getCurrentRoom() == kNorad01West && GameState.getNoradFillingStationOn()) { +		GameState.setNoradFillingStationOn(false); +		updateViewFrame(); +	} +} + +void NoradAlpha::activateHotspots() { +	Norad::activateHotspots(); + +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kNorad01West, kWest): +		if (_vm->getDragType() == kDragInventoryUse) { +			if (!_fillingStationItem) { +				ItemID itemID = _vm->getDraggingItem()->getObjectID(); +				if (itemID == kArgonCanister || itemID == kGasCanister || itemID == kAirMask || +						itemID == kNitrogenCanister) +					g_allHotspots.activateOneHotspot(kN01GasOutletSpotID); +			} +		} else { +			HotSpotID spotID; + +			if (_fillingStationItem) { +				switch (_fillingStationItem->getObjectID()) { +				case kArgonCanister: +					spotID = kN01ArgonCanisterSpotID; +					g_allHotspots.deactivateOneHotspot(kNorad01GasOutSpotID); +					break; +				case kGasCanister: +					spotID = kN01GasCanisterSpotID; +					break; +				case kAirMask: +					spotID = kN01AirMaskSpotID; +					g_allHotspots.deactivateOneHotspot(kNorad01GasOutSpotID); +					break; +				case kNitrogenCanister: +					spotID = kN01NitrogenCanisterSpotID; +					g_allHotspots.deactivateOneHotspot(kNorad01GasOutSpotID); +					break; +				default: +					// Should never happen. +					spotID = kNoHotSpotID; +					break; +				} +				g_allHotspots.activateOneHotspot(spotID); +			} +		} +		break; +	case MakeRoomView(kNorad10, kEast): +		if (GameState.isCurrentDoorOpen()) +			g_allHotspots.deactivateOneHotspot(kNorad10DoorSpotID); +		break; +	case MakeRoomView(kNorad21, kWest): +		if (GameState.isCurrentDoorOpen()) +			g_allHotspots.deactivateOneHotspot(kNorad21WestSpotID); +		break; +	} +} + +void NoradAlpha::clickInHotspot(const Input &input, const Hotspot *cursorSpot) {	 +	Norad::clickInHotspot(input, cursorSpot); + +	if (_vm->getDragType() == kDragInventoryUse) { +		if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad01West, kWest)) { +			Item *item = _vm->getDraggingItem(); +			if (item->getObjectID() == kAirMask || item->getObjectID() == kArgonCanister || +					item->getObjectID() == kNitrogenCanister || item->getObjectID() == kGasCanister) { +				HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kN01GasOutletSpotID); +				hotspotEntry->hotspotItem = item->getObjectID(); +			} +		} +	} +} + +void NoradAlpha::takeItemFromRoom(Item *item) { +	if (GameState.getCurrentRoom() == kNorad01West) { +		if (_fillingStationItem == item) { +			_fillingStationItem = 0; +			GameState.setNoradGassed(false); +			loadAmbientLoops(); +			((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(0); +			forceStridingStop(kNorad03, kEast, kAltNoradAlphaNormal); +		} +	} + +	Norad::takeItemFromRoom(item); +} + +void NoradAlpha::dropItemIntoRoom(Item *item, Hotspot *droppedSpot) { +	if (GameState.getCurrentRoom() == kNorad01West) { +		if (!_fillingStationItem) { +			_fillingStationItem = item; +			((NoradAlphaFillingStation *)_currentInteraction)->newFillingItem(item); +		} +	} + +	Norad::dropItemIntoRoom(item, droppedSpot); +} + +void NoradAlpha::getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID, +		HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, +		HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &clawPosition, const uint32 *&clawExtraIDs) { +	outSpotID = kNorad22MonitorOutSpotID; +	prepSpotID = kNorad22LaunchPrepSpotID; +	clawControlSpotID = kNorad22ClawControlSpotID; +	pinchClawSpotID = kNorad22ClawPinchSpotID; +	moveClawDownSpotID = kNorad22ClawDownSpotID; +	moveClawRightSpotID = kNorad22ClawRightSpotID; +	moveClawLeftSpotID = kNorad22ClawLeftSpotID; +	moveClawUpSpotID = kNorad22ClawUpSpotID; +	clawCCWSpotID = kNorad22ClawCCWSpotID; +	clawCWSpotID = kNorad22ClawCWSpotID; +	clawPosition = kClawAtD; +	clawExtraIDs = _noradAlphaClawExtras; +} + +Hotspot *NoradAlpha::getItemScreenSpot(Item *item, DisplayElement *element) { +	switch (item->getObjectID()) { +	case kGasCanister: +		return g_allHotspots.findHotspotByID(kN01GasCanisterSpotID); +	case kAirMask: +		return g_allHotspots.findHotspotByID(kN01AirMaskSpotID); +	case kArgonCanister: +		return g_allHotspots.findHotspotByID(kN01ArgonCanisterSpotID); +	case kNitrogenCanister: +		return g_allHotspots.findHotspotByID(kN01NitrogenCanisterSpotID); +	} + +	return Norad::getItemScreenSpot(item, element); +} + +Common::String NoradAlpha::getEnvScanMovie() { +	Common::String movieName = Neighborhood::getEnvScanMovie(); + +	if (movieName.empty()) { +		RoomID room = GameState.getCurrentRoom(); +		if (room >= kNorad01 && room <= kNorad01West) +			return "Images/AI/Norad/XNE1"; +		else if ((room >= kNorad02 && room <= kNorad19West)) +			return "Images/AI/Norad/XNE2"; + +		return "Images/AI/Norad/XNE3"; +	} + +	return movieName; +} + +uint NoradAlpha::getNumHints() { +	uint numHints = Neighborhood::getNumHints(); + +	if (numHints == 0) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kNorad01, kNorth): +		case MakeRoomView(kNorad01, kSouth): +		case MakeRoomView(kNorad01, kEast): +		case MakeRoomView(kNorad01, kWest): +		case MakeRoomView(kNorad01East, kEast): +		case MakeRoomView(kNorad01West, kWest): +			if (GameState.getNoradGassed()) { +				if (g_airMask->isAirFilterOn()) +					numHints = 0; +				else +					numHints = 3; +			} else { +				numHints = 2; +			} +			break; +		case MakeRoomView(kNorad19West, kWest): +			if (getSubPrepFailed() && GameState.getNoradSubPrepState() != kSubPrepped) +				numHints = 1; +			break; +		case MakeRoomView(kNorad22, kWest): +			numHints = 1; +			break; +		} +	} + +	return numHints; +} + +Common::String NoradAlpha::getHintMovie(uint hintNum) { +	Common::String movieName = Neighborhood::getHintMovie(hintNum); + +	if (movieName.empty()) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kNorad01, kNorth): +		case MakeRoomView(kNorad01, kSouth): +		case MakeRoomView(kNorad01, kEast): +		case MakeRoomView(kNorad01, kWest): +		case MakeRoomView(kNorad01East, kEast): +		case MakeRoomView(kNorad01West, kWest): +			switch (hintNum) { +			case 1: +				if (GameState.getNoradGassed()) +					return "Images/AI/Norad/XN01SW"; + +				return "Images/AI/Norad/XN01WD2"; +			case 2: +				if (GameState.getNoradGassed()) { +					if (_vm->playerHasItemID(kAirMask)) +						// Mask must not be on if we get here... +						return "Images/AI/Globals/XGLOB1A"; + +					return "Images/AI/Globals/XGLOB3D"; +				} + +				return "Images/AI/Globals/XGLOB5C"; +			case 3: +				return "Images/AI/Norad/XN01SH"; +			} +			break; +		case MakeRoomView(kNorad19West, kWest): +			return "Images/AI/Norad/XN19NH"; +		case MakeRoomView(kNorad22, kWest): +			return "Images/AI/Globals/XGLOB1C"; +		} +	} + +	return movieName; +} + +void NoradAlpha::closeDoorOffScreen(const RoomID room, const DirectionConstant) { +	switch (room) { +	case kNorad12: +	case kNorad13: +	case kNorad18: +	case kNorad19: +		playSpotSoundSync(kAlphaElevatorDoorCloseIn, kAlphaElevatorDoorCloseOut); +		break; +	default: +		playSpotSoundSync(kAlphaRegDoorCloseIn, kAlphaRegDoorCloseOut); +		break; +	} +} + +void NoradAlpha::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) { +	if (room == kNorad01 && direction == kSouth) +		spotEntry.clear(); +	else +		Norad::findSpotEntry(room, direction, flags, spotEntry); +} + +bool NoradAlpha::canSolve() { +	return Norad::canSolve() || getHintMovie(1) == "Images/AI/Norad/XN01SW"; +} + +void NoradAlpha::doSolve() { +	Norad::doSolve(); + +	if (getHintMovie(1) == "Images/AI/Norad/XN01SW") { +		_vm->addItemToInventory(g_airMask); +		g_airMask->putMaskOn(); +	} +} + +Common::String NoradAlpha::getNavMovieName() { +	return "Images/Norad Alpha/Norad Alpha.movie"; +} + +Common::String NoradAlpha::getSoundSpotsName() { +	return "Sounds/Norad/Norad Alpha Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/noradalpha.h b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h new file mode 100644 index 0000000000..b88a6eb802 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/noradalpha.h @@ -0,0 +1,115 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_NORADALPHA_H + +#include "pegasus/neighborhood/norad/norad.h" + +namespace Pegasus { + +class Item; + +class NoradAlpha : public Norad { +public: +	NoradAlpha(InputHandler *, PegasusEngine *); +	virtual ~NoradAlpha() {} + +	virtual void init(); +	void start(); + +	virtual bool okayToJump(); + +	void playClawMonitorIntro(); + +	void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + +	void turnOnFillingStation(); +	void turnOffFillingStation(); +	Item *getFillingItem() { return _fillingStationItem; } +	bool gasCanisterIntake(); + +	virtual void takeItemFromRoom(Item *); +	virtual void dropItemIntoRoom(Item *, Hotspot *); + +	virtual GameInteraction *makeInteraction(const InteractionID); + +	virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, +			HotSpotID &pinchClawSpotID, HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, +			HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, HotSpotID &clawCCWSpotID, +			HotSpotID &clawCWSpotID, uint32 &, const uint32 *&); + +	void loadAmbientLoops(); + +	Common::String getEnvScanMovie(); +	uint getNumHints(); +	Common::String getHintMovie(uint); +	void setUpAIRules(); + +	void setSubPrepFailed(bool value) { _subPrepFailed = value; } +	bool getSubPrepFailed() { return _subPrepFailed; } + +	void closeDoorOffScreen(const RoomID, const DirectionConstant); +	void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); +	void clickInHotspot(const Input &, const Hotspot *); + +	void checkContinuePoint(const RoomID, const DirectionConstant); + +	bool canSolve(); +	void doSolve(); + +protected: +	static const uint32 _noradAlphaClawExtras[22]; + +	virtual void arriveAtNorad01(); +	virtual void arriveAtNorad01East(); +	virtual void arriveAtNorad01West(); +	virtual void arriveAtNorad04(); +	virtual void arriveAtNorad22(); +	 +	virtual void arriveAt(const RoomID, const DirectionConstant); +	 +	virtual void getZoomEntry(const HotSpotID, ZoomTable::Entry &); +	virtual TimeValue getViewTime(const RoomID, const DirectionConstant); +	 +	virtual void receiveNotification(Notification *, const NotificationFlags); + +	virtual void activateHotspots(); + +	Hotspot *getItemScreenSpot(Item *, DisplayElement *); + +	void bumpIntoWall(); + +	Item *_fillingStationItem; + +	bool _subPrepFailed; + +	Common::String getSoundSpotsName(); +	Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/panorama.cpp b/engines/pegasus/neighborhood/norad/alpha/panorama.cpp new file mode 100644 index 0000000000..ecd428239b --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panorama.cpp @@ -0,0 +1,239 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/macresman.h" +#include "common/stream.h" +#include "pegasus/neighborhood/norad/alpha/panorama.h" + +namespace Pegasus { + +Panorama::Panorama() : _panoramaMovie(kNoDisplayElement) { +	blankFields(); +} + +Panorama::~Panorama() { +	releasePanorama(); +} + +void Panorama::blankFields() { +	_viewBounds = Common::Rect(); +	_drawBounds = Common::Rect(); +	_mask = 0; +	_panoramaWidth = 0; +	_panoramaHeight = 0; +	_stripWidth = 0; +	_stripLeft = -1; +	_stripRight = -1; +} + +void Panorama::releasePanorama() { +	if (_panoramaMovie.isMovieValid()) { +		_panoramaMovie.releaseMovie(); +		_panoramaWorld.deallocateSurface(); +		blankFields(); +	} +} + +static const uint32 kPanoramaResType = MKTAG('P', 'a', 'n', 'I'); // Panorama Information. +static const uint16 kPanoramaResID = 128; + +void Panorama::initFromMovieFile(const Common::String &fileName) { +	// First, we need the resource fork for other reasons -- PanI resource +	Common::MacResManager *resFork = new Common::MacResManager(); +	if (!resFork->open(fileName) || !resFork->hasResFork()) +		error("Could not open the resource fork of '%s'", fileName.c_str()); + +	Common::SeekableReadStream *resource = resFork->getResource(kPanoramaResType, kPanoramaResID); +	if (!resource) +		error("No panorama information in the resource fork of '%s'", fileName.c_str()); + +	_panoramaWidth = resource->readUint16BE(); +	_panoramaHeight = resource->readUint16BE(); +	_stripWidth = resource->readUint16BE(); + +	delete resource; +	delete resFork; + +	// Now we open the movie like normal +	_panoramaMovie.initFromMovieFile(fileName); +} + +void Panorama::setMask(Surface *mask) { +	_mask = mask; +} + +// If the panorama is not open, do nothing and return. +// Otherwise, set up the view bounds. +void Panorama::setViewBounds(const Common::Rect &newView) {	 +	if (!isPanoramaOpen()) +		return; + +	if (newView.isEmpty()) +		return; + +	Common::Rect r = newView; + +	if (r.width() > _panoramaWidth) { +		r.left = 0; +		r.right = _panoramaWidth; +	} else { +		if (r.right > _panoramaWidth) +			r.translate(_panoramaWidth - r.right, 0); + +		if (r.left < 0) +			r.translate(-r.left, 0); +	} + +	if (r.height() > _panoramaHeight) { +		r.top = 0; +		r.bottom = _panoramaHeight; +	} else { +		if (r.bottom > _panoramaHeight) +			r.translate(0, _panoramaHeight - r.bottom); + +		if (r.top < 0) +			r.translate(0, -r.top); +	} + +	if (_viewBounds != r) { +		CoordType stripLeft = 0; + +		if (r.width() != _viewBounds.width() || !_panoramaWorld.isSurfaceValid()) { +			_panoramaWorld.deallocateSurface(); +			makeNewSurface(r); +		} else { +			CoordType stripRight; +			calcStripRange(r, stripLeft, stripRight); +			loadStrips(stripLeft, stripRight); +		} + +		_viewBounds = r; +		_drawBounds = r; +		_drawBounds.translate(-stripLeft * _stripWidth, 0); +	} +} + +void Panorama::getViewBounds(Common::Rect &r) const { +	r = _viewBounds; +} + +void Panorama::getPanoramaBounds(Common::Rect &r) const { +	r = Common::Rect(0, 0, _panoramaWidth, _panoramaHeight); +} + +void Panorama::drawPanorama(const Common::Rect &destRect) { +	if (_panoramaWorld.isSurfaceValid()) { +		if (_mask) +			_panoramaWorld.copyToCurrentPortMasked(_drawBounds, destRect, _mask); +		else +			_panoramaWorld.copyToCurrentPortTransparent(_drawBounds, destRect); +	} +} + +// Make a new Surface big enough to show r, which is assumed to be a valid view bounds. +// Assumptions: +//      r is a valid view bounds. +//      _panoramaWorld is not allocated. +//      _panoramaHeight, _stripWidth is correct. +//      _panoramaMovie is allocated. +void Panorama::makeNewSurface(const Common::Rect& view) { +	CoordType stripLeft, stripRight;	 +	calcStripRange(view, stripLeft, stripRight); + +	Common::Rect r(0, 0, (stripRight - stripLeft + 1) * _stripWidth, _panoramaHeight); +	_panoramaWorld.allocateSurface(r); +	_panoramaMovie.shareSurface(&_panoramaWorld); +	loadStrips(stripLeft, stripRight); +} + +// Assumes view is not empty. +void Panorama::calcStripRange(const Common::Rect &view, CoordType &stripLeft, CoordType &stripRight) { +	stripLeft = view.left / _stripWidth; +	stripRight = (view.left - view.left % _stripWidth + _stripWidth - 1 + view.width()) / _stripWidth; +} + +// Load in all needed strips to put range (stripLeft, stripRight) into the +// panorama's Surface. Try to optimize by saving any pixels already in the Surface. +// Assumptions: +//      Surface is allocated and is big enough for maximum range of +//          stripLeft and stripRight +void Panorama::loadStrips(CoordType stripLeft, CoordType stripRight) {	 +	if (_stripLeft == -1) { +		// Surface has just been allocated. +		// Load in all strips. +		for (CoordType i = stripLeft; i <= stripRight; i++) +			loadOneStrip(i, stripLeft); + +		_stripLeft = stripLeft; +		_stripRight = stripRight; +	} else if (stripLeft != _stripLeft) { +		CoordType overlapLeft = MAX(stripLeft, _stripLeft); +		CoordType overlapRight = MIN(stripRight, _stripRight); + +		if (overlapLeft <= overlapRight) { +			Common::Rect r1((overlapLeft - _stripLeft) * _stripWidth, 0, +					(overlapRight - _stripLeft + 1) * _stripWidth, _panoramaHeight); + +			if (stripLeft < _stripLeft) { +				Common::Rect bounds; +				_panoramaWorld.getSurfaceBounds(bounds); +				_panoramaWorld.getSurface()->move(bounds.right - r1.right, 0, _panoramaHeight); + +				for (CoordType i = stripLeft; i < _stripLeft; i++) +					loadOneStrip(i, stripLeft); +			} else { +				_panoramaWorld.getSurface()->move(-r1.left, 0, _panoramaHeight); + +				for (CoordType i = _stripRight + 1; i <= stripRight; i++) +					loadOneStrip(i, stripLeft); +			} +		} else { +			// No overlap. +			// Load everything. +			for (CoordType i = stripLeft; i <= stripRight; i++) +				loadOneStrip(i, stripLeft); +		} + +		_stripLeft = stripLeft; +		_stripRight = stripRight; +	} else if (stripRight > _stripRight) { +		// Need to add one or more strips. +		for (CoordType i = _stripRight + 1; i <= stripRight; i++) +			loadOneStrip(i, _stripLeft); + +		_stripRight = stripRight; +	} else if (stripRight < _stripRight) { +		// Need to chop off one strip. +		_stripRight = stripRight; +	} +} + +void Panorama::loadOneStrip(CoordType stripToLoad, CoordType leftStrip) { +	_panoramaMovie.moveMovieBoxTo((stripToLoad - leftStrip) * _stripWidth, 0); +	_panoramaMovie.setTime(stripToLoad, 1); +	_panoramaMovie.redrawMovieWorld(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/panorama.h b/engines/pegasus/neighborhood/norad/alpha/panorama.h new file mode 100644 index 0000000000..3ca2c1e305 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panorama.h @@ -0,0 +1,98 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMA_H + +#include "pegasus/movie.h" + +namespace Pegasus { + +/* + +	Panorama implements a wide image using a specially constructed movie file. +	The movie holds the image as a series of vertical strips, say 16 or 32 pixels wide. +	 +	The panorama bounds defines the entire panorama. The view bounds represents the +	area on the panorama that is kept in memory. +	 +	The panorama bounds is also stored in the movie file; it cannot be changed. The +	view bounds must always be a subset of the panorama bounds. +	 +	In actuality, the area kept in memory is at least as wide as the view bounds (but +	may be wider to coincide with the width of the movies slices), and is as tall as +	the panorama bounds. The view bounds is used by the drawPanorama function to draw +	a piece of the panorama to the current screen. +	 +	The panorama movie is built at a time scale of 1, with each strip lasting for one +	second, so that strip number corresponds exactly with the time value at which the +	strip is stored. +	 +	TO USE: +	 +	Call one initFromMovieFile to open the movie. Then set up a view rect by +	calling setViewBounds. Once these two functions have been called, drawPanorama +	will draw the panorama. + +*/ + +class Panorama { +public: +	Panorama(); +	virtual ~Panorama(); + +	void initFromMovieFile(const Common::String &); +	void releasePanorama(); +	bool isPanoramaOpen() { return _panoramaMovie.isMovieValid(); } + +	void setViewBounds(const Common::Rect &); +	void getViewBounds(Common::Rect &) const; + +	void setMask(Surface *); + +	void getPanoramaBounds(Common::Rect &) const; + +	void drawPanorama(const Common::Rect &); + +protected: +	void blankFields(); +	void makeNewSurface(const Common::Rect &); +	void calcStripRange(const Common::Rect &, CoordType &, CoordType &); +	void loadStrips(CoordType, CoordType); +	void loadOneStrip(CoordType, CoordType); +	 +	Movie _panoramaMovie; +	Surface _panoramaWorld, *_mask; +	Common::Rect _viewBounds; +	Common::Rect _drawBounds; +	CoordType _panoramaWidth, _panoramaHeight; +	CoordType _stripWidth; // Pixels per strip. +	CoordType _numStrips; +	CoordType _stripLeft, _stripRight; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp new file mode 100644 index 0000000000..fcb49c52ee --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.cpp @@ -0,0 +1,91 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/neighborhood/norad/alpha/panoramascroll.h" + +namespace Pegasus { + +PanoramaScroll::PanoramaScroll(const DisplayElementID id) : IdlerAnimation(id) { +	_boundsWidth = 0; +	_totalWidth = 0; +} + +void PanoramaScroll::initFromMovieFile(const Common::String &fileName) {	 +	_panorama.initFromMovieFile(fileName); + +	Common::Rect r; +	_panorama.getPanoramaBounds(r); +	_totalWidth = r.width(); +} + +void PanoramaScroll::initMaskFromPICTFile(const Common::String &fileName) {	 +	if (!_panorama.isPanoramaOpen()) +		return; + +	_mask.getImageFromPICTFile(fileName); +	_panorama.setMask(&_mask); +} + +void PanoramaScroll::releasePanorama() { +	if (_panorama.isPanoramaOpen()) +		_panorama.releasePanorama(); + +	_mask.deallocateSurface(); +} + +void PanoramaScroll::setBounds(const Common::Rect &r) { +	Animation::setBounds(r); + +	_boundsWidth = r.width(); + +	Common::Rect r2; +	_panorama.getViewBounds(r2); +	r2.right = r2.left + _boundsWidth; +	r2.bottom = r2.top + r.height(); +	_panorama.setViewBounds(r2); +} + +void PanoramaScroll::draw(const Common::Rect &) { +	_panorama.drawPanorama(_bounds); +} + +void PanoramaScroll::timeChanged(const TimeValue newTime) {	 +	CoordType leftPixel = (_totalWidth - _boundsWidth) * newTime / getDuration(); + +	Common::Rect r; +	_panorama.getViewBounds(r); +	if (leftPixel != r.left) { +		_panorama.getViewBounds(r); +		r.moveTo(leftPixel, 0); +		_panorama.setViewBounds(r); +		triggerRedraw(); +	} +} + +void PanoramaScroll::getPanoramaBounds(Common::Rect &r) const { +	_panorama.getPanoramaBounds(r); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h new file mode 100644 index 0000000000..6a3e1515e2 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/alpha/panoramascroll.h @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMASCROLL_H +#define PEGASUS_NEIGHBORHOOD_NORAD_ALPHA_PANORAMASCROLL_H + +#include "pegasus/neighborhood/norad/alpha/panorama.h" + +namespace Pegasus { + +class PanoramaScroll : public IdlerAnimation { +public: +	PanoramaScroll(const DisplayElementID); +	virtual ~PanoramaScroll() {} + +	void initFromMovieFile(const Common::String &); +	void initMaskFromPICTFile(const Common::String &); + +	void releasePanorama(); + +	bool isPanoramaOpen() { return _panorama.isPanoramaOpen(); } +	void getPanoramaBounds(Common::Rect &) const; + +	void setBounds(const Common::Rect&); + +	void draw(const Common::Rect &); + +protected: +	void timeChanged(const TimeValue); + +	Panorama _panorama; +	Surface _mask; +	CoordType _totalWidth, _boundsWidth; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/constants.h b/engines/pegasus/neighborhood/norad/constants.h new file mode 100644 index 0000000000..37c1769309 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/constants.h @@ -0,0 +1,755 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_CONSTANTS_H +#define PEGASUS_NEIGHBORHOOD_NORAD_CONSTANTS_H + +#include "pegasus/constants.h" + +namespace Pegasus { + +// Norad Alpha spot constants + +static const TimeValue kAlphaBumpIntoWallIn = 0; +static const TimeValue kAlphaBumpIntoWallOut = 303; + +static const TimeValue kAlphaAccessDeniedIn = 303; +static const TimeValue kAlphaAccessDeniedOut = 3045; + +static const TimeValue kAlphaRegDoorCloseIn = 3045; +static const TimeValue kAlphaRegDoorCloseOut = 4476; + +static const TimeValue kAlphaElevatorDoorCloseIn = 4476; +static const TimeValue kAlphaElevatorDoorCloseOut = 5071; + +static const TimeValue kAlphaCantTransportIn = 5071; +static const TimeValue kAlphaCantTransportOut = 9348; + +static const TimeValue kAlphaPressureDoorIntro1In = 9348; +static const TimeValue kAlphaPressureDoorIntro1Out = 11061; + +static const TimeValue kAlphaPressureDoorIntro2In = 11061; +static const TimeValue kAlphaPressureDoorIntro2Out = 14098; + +static const TimeValue kN22ReplyIn = 14098; +static const TimeValue kN22ReplyOut = 18442; + +static const TimeValue kAlphaLoadClawIntroIn = 18442; +static const TimeValue kAlphaLoadClawIntroOut = 20698; + +// Norad Delta spot constants + +static const TimeValue kDeltaBumpIntoWallIn = 0; +static const TimeValue kDeltaBumpIntoWallOut = 303; + +static const TimeValue kDeltaAccessDeniedIn = 303; +static const TimeValue kDeltaAccessDeniedOut = 3045; + +static const TimeValue kDeltaRegDoorCloseIn = 3045; +static const TimeValue kDeltaRegDoorCloseOut = 4476; + +static const TimeValue kDeltaElevatorDoorCloseIn = 4476; +static const TimeValue kDeltaElevatorDoorCloseOut = 5071; + +static const TimeValue kPressureDoorIntro1In = 5071; +static const TimeValue kPressureDoorIntro1Out = 6784; + +static const TimeValue kPressureDoorIntro2In = 6784; +static const TimeValue kPressureDoorIntro2Out = 9821; + +static const TimeValue kLoadClawIntroIn = 9821; +static const TimeValue kLoadClawIntroOut = 12077; + +static const TimeValue kHoldForRetinalIn = 12077; +static const TimeValue kHoldForRetinalOut = 14104; + +static const TimeValue kRetinalScanFailedIn = 14104; +static const TimeValue kRetinalScanFailedOut = 17538; + +static const TimeValue kAddisAbabaIn = 17538; +static const TimeValue kAddisAbabaOut = 19263; + +static const TimeValue kBangkokIn = 19263; +static const TimeValue kBangkokOut = 20201; + +static const TimeValue kBonnIn = 20201; +static const TimeValue kBonnOut = 20915; + +static const TimeValue kDublinIn = 20915; +static const TimeValue kDublinOut = 21660; + +static const TimeValue kHonoluluIn = 21660; +static const TimeValue kHonoluluOut = 22498; + +static const TimeValue kMadridIn = 22498; +static const TimeValue kMadridOut = 23474; + +static const TimeValue kReykjavikIn = 23474; +static const TimeValue kReykjavikOut = 24488; + +static const TimeValue kSanAntonioIn = 24488; +static const TimeValue kSanAntonioOut = 25561; + +static const TimeValue kSeoulIn = 25561; +static const TimeValue kSeoulOut = 26461; + +static const TimeValue kSvortalskIn = 26461; +static const TimeValue kSvortalskOut = 27582; + +static const TimeValue kSiloBeepIn = 27582; +static const TimeValue kSiloBeepOut = 27721; + +static const TimeValue kAllSilosDeactivatedIn = 27721; +static const TimeValue kAllSilosDeactivatedOut = 28928; + +static const TimeValue kGlobalLaunchOverrideIn = 28928; +static const TimeValue kGlobalLaunchOverrideOut = 30736; + +static const TimeValue kLaunchSiloSelectedIn = 30736; +static const TimeValue kLaunchSiloSelectedOut = 31660; + +static const TimeValue kLaunchToProceedIn = 31660; +static const TimeValue kLaunchToProceedOut = 32536; + +static const TimeValue kMaximumDeactivationIn = 32536; +static const TimeValue kMaximumDeactivationOut = 34337; + +static const TimeValue kMissileLaunchedIn = 34337; +static const TimeValue kMissileLaunchedOut = 35082; + +static const TimeValue kNewLaunchSiloIn = 35082; +static const TimeValue kNewLaunchSiloOut = 36320; + +static const TimeValue kStrikeAuthorizedIn = 36320; +static const TimeValue kStrikeAuthorizedOut = 37393; + +static const TimeValue kPrimaryTargetIn = 37393; +static const TimeValue kPrimaryTargetOut = 38628; + +static const TimeValue kSiloDeactivatedIn = 38628; +static const TimeValue kSiloDeactivatedOut = 39566; + +static const TimeValue kStrikeCodeRejectedIn = 39566; +static const TimeValue kStrikeCodeRejectedOut = 41056; + +static const TimeValue kToDeactivateIn = 41056; +static const TimeValue kToDeactivateOut = 46494; + +static const TimeValue kTwoMinutesIn = 46494; +static const TimeValue kTwoMinutesOut = 47166; + +static const TimeValue kOneMinuteIn = 47166; +static const TimeValue kOneMinuteOut = 47856; + +static const TimeValue kFiftySecondsIn = 47856; +static const TimeValue kFiftySecondsOut = 48691; + +static const TimeValue kFortySecondsIn = 48691; +static const TimeValue kFortySecondsOut = 49500; + +static const TimeValue kThirtySecondsIn = 49500; +static const TimeValue kThirtySecondsOut = 50362; + +static const TimeValue kTwentySecondsIn = 50362; +static const TimeValue kTwentySecondsOut = 51245; + +static const TimeValue kTenSecondsIn = 51245; +static const TimeValue kTenSecondsOut = 52069; + +static const TimeValue kGiveUpHumanIn = 52069; +static const TimeValue kGiveUpHumanOut = 55023; + +static const TimeValue kIJustBrokeIn = 55023; +static const TimeValue kIJustBrokeOut = 59191; + +static const TimeValue kTheOnlyGoodHumanIn = 59191; +static const TimeValue kTheOnlyGoodHumanOut = 62379; + +static const TimeValue kYouAreRunningIn = 62379; +static const TimeValue kYouAreRunningOut = 64201; + +static const TimeValue kYouCannotPossiblyIn = 64201; +static const TimeValue kYouCannotPossiblyOut = 65740; + +static const TimeValue kYouWillFailIn = 65740; +static const TimeValue kYouWillFailOut = 67217; + +static const CanOpenDoorReason kCantOpenBadPressure = kCantOpenLastReason + 1; + +static const NotificationFlags kAirTimerExpiredFlag = kLastNeighborhoodNotificationFlag << 1; + +static const uint16 kNoradWarningVolume = 0x100 / 3; +static const uint16 kNoradSuckWindVolume = 0x100 / 2; + +static const int16 kElevatorCompassAngle = -40; +static const int16 kSubPlatformCompassAngle = 45; +static const int16 kSubControlCompassAngle = -10; + +// Norad interactions. + +static const InteractionID kNoradGlobeGameInteractionID = 0; +static const InteractionID kNoradECRMonitorInteractionID = 1; +static const InteractionID kNoradFillingStationInteractionID = 2; +static const InteractionID kNoradElevatorInteractionID = 3; +static const InteractionID kNoradPressureDoorInteractionID = 4; +static const InteractionID kNoradSubControlRoomInteractionID = 5; +static const InteractionID kNoradSubPlatformInteractionID = 6; + +///////////////////////////////////////////// +// +// Norad Alpha + +static const CoordType kECRSlideShowLeft = kNavAreaLeft + 78; +static const CoordType kECRSlideShowTop = kNavAreaTop + 1; + +static const CoordType kECRPanLeft = kNavAreaLeft + 78 + 5; +static const CoordType kECRPanTop = kNavAreaTop + 1 + 4; +static const CoordType kECRPanRight = kECRPanLeft + 213; +static const CoordType kECRPanBottom = kECRPanTop + 241; + +static const CoordType kNoradAlphaElevatorControlsLeft = kNavAreaLeft + 332; +static const CoordType kNoradAlphaElevatorControlsTop = kNavAreaTop + 127; + +static const CoordType kNoradAlpha01LeftSideLeft = kNavAreaLeft + 0; +static const CoordType kNoradAlpha01LeftSideTop = kNavAreaTop + 0; + +static const CoordType kNoradAlpha01RightSideLeft = kNavAreaLeft + 240; +static const CoordType kNoradAlpha01RightSideTop = kNavAreaTop + 12; + +static const CoordType kNoradUpperLevelsLeft = kNavAreaLeft + 98; +static const CoordType kNoradUpperLevelsTop = kNavAreaTop + 31; + +static const CoordType kNoradUpperTypeLeft = kNoradUpperLevelsLeft + 114; +static const CoordType kNoradUpperTypeTop = kNoradUpperLevelsTop + 8; + +static const CoordType kNoradUpperUpLeft = kNavAreaLeft + 361; +static const CoordType kNoradUpperUpTop = kNavAreaTop + 32; + +static const CoordType kNoradUpperDownLeft = kNavAreaLeft + 367; +static const CoordType kNoradUpperDownTop = kNavAreaTop + 66; + +static const CoordType kNoradLowerLevelsLeft = kNavAreaLeft + 74; +static const CoordType kNoradLowerLevelsTop = kNavAreaTop + 157; + +static const CoordType kNoradLowerTypeLeft = kNoradLowerLevelsLeft + 144; +static const CoordType kNoradLowerTypeTop = kNoradLowerLevelsTop + 9; + +static const CoordType kNoradLowerUpLeft = kNavAreaLeft + 380; +static const CoordType kNoradLowerUpTop = kNavAreaTop + 164; + +static const CoordType kNoradLowerDownLeft = kNavAreaLeft + 388; +static const CoordType kNoradLowerDownTop = kNavAreaTop + 212; + +static const CoordType kNoradPlatformLeft = kNavAreaLeft + 36; +static const CoordType kNoradPlatformTop = kNavAreaTop + 87; + +static const CoordType kNoradSubControlLeft = kNavAreaLeft + 0; +static const CoordType kNoradSubControlTop = kNavAreaTop + 84; + +static const CoordType kNoradSubControlPinchLeft = kNoradSubControlLeft + 106; +static const CoordType kNoradSubControlPinchTop = kNoradSubControlTop + 86; + +static const CoordType kNoradSubControlDownLeft = kNoradSubControlLeft + 66; +static const CoordType kNoradSubControlDownTop = kNoradSubControlTop + 106; + +static const CoordType kNoradSubControlRightLeft = kNoradSubControlLeft + 83; +static const CoordType kNoradSubControlRightTop = kNoradSubControlTop + 90; + +static const CoordType kNoradSubControlLeftLeft = kNoradSubControlLeft + 56; +static const CoordType kNoradSubControlLeftTop = kNoradSubControlTop + 91; + +static const CoordType kNoradSubControlUpLeft = kNoradSubControlLeft + 66; +static const CoordType kNoradSubControlUpTop = kNoradSubControlTop + 81; + +static const CoordType kNoradSubControlCCWLeft = kNoradSubControlLeft + 29; +static const CoordType kNoradSubControlCCWTop = kNoradSubControlTop + 88; + +static const CoordType kNoradSubControlCWLeft = kNoradSubControlLeft + 0; +static const CoordType kNoradSubControlCWTop = kNoradSubControlTop + 89; + +static const CoordType kNoradClawMonitorLeft = kNavAreaLeft + 288; +static const CoordType kNoradClawMonitorTop = kNavAreaTop + 97; + +static const CoordType kNoradGreenBallAtALeft = kNoradClawMonitorLeft + 179; +static const CoordType kNoradGreenBallAtATop = kNoradClawMonitorTop + 82; + +static const CoordType kNoradGreenBallAtBLeft = kNoradClawMonitorLeft + 130; +static const CoordType kNoradGreenBallAtBTop = kNoradClawMonitorTop + 73; + +static const CoordType kNoradGreenBallAtCLeft = kNoradClawMonitorLeft + 110; +static const CoordType kNoradGreenBallAtCTop = kNoradClawMonitorTop + 26; + +static const CoordType kNoradGreenBallAtDLeft = kNoradClawMonitorLeft + 21; +static const CoordType kNoradGreenBallAtDTop = kNoradClawMonitorTop + 49; + +///////////////////////////////////////////// +// +// Norad Delta + +static const CoordType kGlobeMonitorLeft = kNavAreaLeft + 360; +static const CoordType kGlobeMonitorTop = kNavAreaTop + 144; + +static const CoordType kGlobeLeft = kNavAreaLeft + 172; +static const CoordType kGlobeTop = kNavAreaTop; + +static const CoordType kGlobeCircleLeftLeft = kNavAreaLeft + 186; +static const CoordType kGlobeCircleLeftTop = kNavAreaTop + 41; + +static const CoordType kGlobeCircleRightLeft = kNavAreaLeft + 321; +static const CoordType kGlobeCircleRightTop = kNavAreaTop + 41; + +static const CoordType kGlobeCircleUpLeft = kNavAreaLeft + 220; +static const CoordType kGlobeCircleUpTop = kNavAreaTop + 7; + +static const CoordType kGlobeCircleDownLeft = kNavAreaLeft + 220; +static const CoordType kGlobeCircleDownTop = kNavAreaTop + 142; + +static const CoordType kGlobeUpperLeftHiliteLeft = kNavAreaLeft + 207; +static const CoordType kGlobeUpperLeftHiliteTop = kNavAreaTop + 28; + +static const CoordType kGlobeUpperRightHiliteLeft = kNavAreaLeft + 307; +static const CoordType kGlobeUpperRightHiliteTop = kNavAreaTop + 28; + +static const CoordType kGlobeLowerLeftHiliteLeft = kNavAreaLeft + 207; +static const CoordType kGlobeLowerLeftHiliteTop = kNavAreaTop + 128; + +static const CoordType kGlobeLowerRightHiliteLeft = kNavAreaLeft + 307; +static const CoordType kGlobeLowerRightHiliteTop = kNavAreaTop + 128; + +static const CoordType kGlobeLeftMotionHiliteLeft = kNavAreaLeft + 182; +static const CoordType kGlobeLeftMotionHiliteTop = kNavAreaTop + 60; + +static const CoordType kGlobeRightMotionHiliteLeft = kNavAreaLeft + 331; +static const CoordType kGlobeRightMotionHiliteTop = kNavAreaTop + 60; + +static const CoordType kGlobeUpMotionHiliteLeft = kNavAreaLeft + 239; +static const CoordType kGlobeUpMotionHiliteTop = kNavAreaTop + 3; + +static const CoordType kGlobeDownMotionHiliteLeft = kNavAreaLeft + 239; +static const CoordType kGlobeDownMotionHiliteTop = kNavAreaTop + 152; + +static const CoordType kGlobeUpperNamesLeft = kNavAreaLeft + 368; +static const CoordType kGlobeUpperNamesTop = kNavAreaTop + 188; + +static const CoordType kGlobeLowerNamesLeft = kNavAreaLeft + 368; +static const CoordType kGlobeLowerNamesTop = kNavAreaTop + 212; + +static const CoordType kGlobeCountdownLeft = kNavAreaLeft + 478; +static const CoordType kGlobeCountdownTop = kNavAreaTop + 164; + +// Norad Alpha display IDs. + +static const DisplayElementID kECRSlideShowMovieID = kNeighborhoodDisplayID; +static const DisplayElementID kECRPanID = kECRSlideShowMovieID + 1; +static const DisplayElementID kNoradAlphaDeathMovieID = kECRPanID + 1; +static const DisplayElementID kNoradElevatorControlsID = kNoradAlphaDeathMovieID + 1; +static const DisplayElementID kN01LeftSideID = kNoradElevatorControlsID + 1; +static const DisplayElementID kN01RightSideID = kN01LeftSideID + 1; +static const DisplayElementID kPressureDoorLevelsID = kN01RightSideID + 1; +static const DisplayElementID kPressureDoorTypeID = kPressureDoorLevelsID + 1; +static const DisplayElementID kPressureDoorUpButtonID = kPressureDoorTypeID + 1; +static const DisplayElementID kPressureDoorDownButtonID = kPressureDoorUpButtonID + 1; +static const DisplayElementID kPlatformMonitorID = kPressureDoorDownButtonID + 1; +static const DisplayElementID kSubControlMonitorID = kPlatformMonitorID + 1; +static const DisplayElementID kClawMonitorID = kSubControlMonitorID + 1; +static const DisplayElementID kSubControlPinchID = kClawMonitorID + 1; +static const DisplayElementID kSubControlDownID = kSubControlPinchID + 1; +static const DisplayElementID kSubControlRightID = kSubControlDownID + 1; +static const DisplayElementID kSubControlLeftID = kSubControlRightID + 1; +static const DisplayElementID kSubControlUpID = kSubControlLeftID + 1; +static const DisplayElementID kSubControlCCWID = kSubControlUpID + 1; +static const DisplayElementID kSubControlCWID = kSubControlCCWID + 1; +static const DisplayElementID kClawMonitorGreenBallID = kSubControlCWID + 1; + +// Norad Delta display IDs. + +static const DisplayElementID kGlobeMonitorID = kNeighborhoodDisplayID; +static const DisplayElementID kGlobeMovieID = kGlobeMonitorID + 14; +static const DisplayElementID kGlobeCircleLeftID = kGlobeMovieID + 1; +static const DisplayElementID kGlobeCircleRightID = kGlobeCircleLeftID + 1; +static const DisplayElementID kGlobeCircleUpID = kGlobeCircleRightID + 1; +static const DisplayElementID kGlobeCircleDownID = kGlobeCircleUpID + 1; +static const DisplayElementID kMotionHiliteLeftID = kGlobeCircleDownID + 1; +static const DisplayElementID kMotionHiliteRightID = kMotionHiliteLeftID + 1; +static const DisplayElementID kMotionHiliteUpID = kMotionHiliteRightID + 1; +static const DisplayElementID kMotionHiliteDownID = kMotionHiliteUpID + 1; +static const DisplayElementID kTargetHiliteUpperLeftID = kMotionHiliteDownID + 1; +static const DisplayElementID kTargetHiliteUpperRightID = kTargetHiliteUpperLeftID + 1; +static const DisplayElementID kTargetHiliteLowerLeftID = kTargetHiliteUpperRightID + 1; +static const DisplayElementID kTargetHiliteLowerRightID = kTargetHiliteLowerLeftID + 1; +static const DisplayElementID kGlobeUpperNamesID = kTargetHiliteLowerRightID + 1; +static const DisplayElementID kGlobeLowerNamesID = kGlobeUpperNamesID + 1; +static const DisplayElementID kGlobeCountdownID = kGlobeLowerNamesID + 1; + +// Norad Alpha: + +static const DisplayOrder kECRMonitorOrder = kMonitorLayer; +static const DisplayOrder kECRPanOrder = kECRMonitorOrder + 1; + +static const DisplayOrder kN01LeftSideOrder = kMonitorLayer; +static const DisplayOrder kN01RightSideOrder = kN01LeftSideOrder + 1; + +static const DisplayOrder kElevatorControlsOrder = kMonitorLayer; + +static const DisplayOrder kPressureLevelsOrder = kMonitorLayer; +static const DisplayOrder kPressureTypeOrder = kPressureLevelsOrder + 1; +static const DisplayOrder kPressureUpOrder = kPressureTypeOrder + 1; +static const DisplayOrder kPressureDownOrder = kPressureUpOrder + 1; + +static const DisplayOrder kPlatformOrder = kMonitorLayer; + +static const DisplayOrder kSubControlOrder = kMonitorLayer; +static const DisplayOrder kClawMonitorOrder = kSubControlOrder + 1; +static const DisplayOrder kSubControlPinchOrder = kClawMonitorOrder + 1; +static const DisplayOrder kSubControlDownOrder = kSubControlPinchOrder + 1; +static const DisplayOrder kSubControlRightOrder = kSubControlDownOrder + 1; +static const DisplayOrder kSubControlLeftOrder = kSubControlRightOrder + 1; +static const DisplayOrder kSubControlUpOrder = kSubControlLeftOrder + 1; +static const DisplayOrder kSubControlCCWOrder = kSubControlUpOrder + 1; +static const DisplayOrder kSubControlCWOrder = kSubControlCCWOrder + 1; +static const DisplayOrder kClawMonitorGreenBallOrder = kSubControlCWOrder + 1; + +// Norad Delta: + +static const DisplayOrder kGlobeMonitorLayer = kMonitorLayer; +static const DisplayOrder kGlobeMovieLayer = kGlobeMonitorLayer + 1; +static const DisplayOrder kGlobeCircleLayer = kGlobeMovieLayer + 1; +static const DisplayOrder kGlobeHilitesLayer = kGlobeCircleLayer + 1; +static const DisplayOrder kGlobeUpperNamesLayer = kGlobeHilitesLayer + 1; +static const DisplayOrder kGlobeLowerNamesLayer = kGlobeUpperNamesLayer + 1; +static const DisplayOrder kGlobeCountdownLayer = kGlobeLowerNamesLayer + 1; + +// Norad Alpha Tables + +static const TimeScale kNoradAlphaMovieScale = 600; +static const TimeScale kNoradAlphaFramesPerSecond = 15; +static const TimeScale kNoradAlphaFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltNoradAlphaNormal = 0; + +// Room IDs. + +static const RoomID kNorad01 = 0; +static const RoomID kNorad01East = 1; +static const RoomID kNorad01West = 2; +static const RoomID kNorad02 = 3; +static const RoomID kNorad03 = 4; +static const RoomID kNorad04 = 5; +static const RoomID kNorad05 = 6; +static const RoomID kNorad06 = 7; +static const RoomID kNorad07 = 8; +static const RoomID kNorad07North = 9; +static const RoomID kNorad08 = 10; +static const RoomID kNorad09 = 11; +static const RoomID kNorad10 = 12; +static const RoomID kNorad10East = 13; +static const RoomID kNorad11 = 14; +static const RoomID kNorad11South = 15; +static const RoomID kNorad12 = 16; +static const RoomID kNorad12South = 17; +static const RoomID kNorad13 = 18; +static const RoomID kNorad14 = 19; +static const RoomID kNorad15 = 20; +static const RoomID kNorad16 = 21; +static const RoomID kNorad17 = 22; +static const RoomID kNorad18 = 23; +static const RoomID kNorad19 = 24; +static const RoomID kNorad19West = 25; +static const RoomID kNorad21 = 26; +static const RoomID kNorad21West = 27; +static const RoomID kNorad22 = 28; +static const RoomID kNorad22West = 29; + +// Hot Spot Activation IDs. + + +// Hot Spot IDs. + +static const HotSpotID kNorad01ECRSpotID = 5000; +static const HotSpotID kNorad01GasSpotID = 5001; +static const HotSpotID kNorad01ECROutSpotID = 5002; +static const HotSpotID kNorad01GasOutSpotID = 5003; +static const HotSpotID kNorad01MonitorSpotID = 5004; +static const HotSpotID kNorad01IntakeSpotID = 5005; +static const HotSpotID kNorad01DispenseSpotID = 5006; +static const HotSpotID kNorad01ArSpotID = 5007; +static const HotSpotID kNorad01CO2SpotID = 5008; +static const HotSpotID kNorad01HeSpotID = 5009; +static const HotSpotID kNorad01OSpotID = 5010; +static const HotSpotID kNorad01NSpotID = 5011; +static const HotSpotID kN01GasCanisterSpotID = 5012; +static const HotSpotID kN01ArgonCanisterSpotID = 5013; +static const HotSpotID kN01AirMaskSpotID = 5014; +static const HotSpotID kN01NitrogenCanisterSpotID = 5015; +static const HotSpotID kN01GasOutletSpotID = 5016; +static const HotSpotID kNorad07DoorSpotID = 5017; +static const HotSpotID kNorad07DoorOutSpotID = 5018; +static const HotSpotID kNorad10DoorSpotID = 5019; +static const HotSpotID kNorad10EastOutSpotID = 5020; +static const HotSpotID kAlphaUpperPressureDoorUpSpotID = 5021; +static const HotSpotID kAlphaUpperPressureDoorDownSpotID = 5022; +static const HotSpotID kNorad11ElevatorSpotID = 5023; +static const HotSpotID kNorad11ElevatorOutSpotID = 5024; +static const HotSpotID kNorad11ElevatorDownSpotID = 5025; +static const HotSpotID kNorad12ElevatorSpotID = 5026; +static const HotSpotID kNorad12ElevatorOutSpotID = 5027; +static const HotSpotID kNorad12ElevatorUpSpotID = 5028; +static const HotSpotID kNorad19MonitorSpotID = 5029; +static const HotSpotID kNorad19MonitorOutSpotID = 5030; +static const HotSpotID kNorad19ActivateMonitorSpotID = 5031; +static const HotSpotID kNorad21WestSpotID = 5032; +static const HotSpotID kNorad21WestOutSpotID = 5033; +static const HotSpotID kAlphaLowerPressureDoorUpSpotID = 5034; +static const HotSpotID kAlphaLowerPressureDoorDownSpotID = 5035; +static const HotSpotID kNorad22MonitorSpotID = 5036; +static const HotSpotID kNorad22MonitorOutSpotID = 5037; +static const HotSpotID kNorad22LaunchPrepSpotID = 5038; +static const HotSpotID kNorad22ClawControlSpotID = 5039; +static const HotSpotID kNorad22ClawPinchSpotID = 5040; +static const HotSpotID kNorad22ClawDownSpotID = 5041; +static const HotSpotID kNorad22ClawRightSpotID = 5042; +static const HotSpotID kNorad22ClawLeftSpotID = 5043; +static const HotSpotID kNorad22ClawUpSpotID = 5044; +static const HotSpotID kNorad22ClawCCWSpotID = 5045; +static const HotSpotID kNorad22ClawCWSpotID = 5046; + +// Extra sequence IDs. + +static const ExtraID kNoradArriveFromTSA = 0; +static const ExtraID kNorad01RobotTaunt = 1; +static const ExtraID kNorad01ZoomInWithGasCanister = 2; +static const ExtraID kN01WGasCanister = 3; +static const ExtraID kNorad01ZoomOutWithGasCanister = 4; +static const ExtraID kN01WZEmptyLit = 5; +static const ExtraID kN01WZGasCanisterDim = 6; +static const ExtraID kN01WZGasCanisterLit = 7; +static const ExtraID kN01WZArgonCanisterDim = 8; +static const ExtraID kN01WZArgonCanisterLit = 9; +static const ExtraID kN01WZAirMaskDim = 10; +static const ExtraID kN01WZAirMaskLit = 11; +static const ExtraID kN01WZNitrogenCanisterDim = 12; +static const ExtraID kN01WZNitrogenCanisterLit = 13; +static const ExtraID kNorad04EastDeath = 14; +static const ExtraID kNorad19PrepSub = 15; +static const ExtraID kNorad19ExitToSub = 16; +static const ExtraID kNorad22SouthIntro = 17; +static const ExtraID kNorad22SouthReply = 18; +static const ExtraID kNorad22SouthFinish = 19; +static const ExtraID kN22ClawFromAToB = 20; +static const ExtraID kN22ClawALoop = 21; +static const ExtraID kN22ClawAPinch = 22; +static const ExtraID kN22ClawACounterclockwise = 23; +static const ExtraID kN22ClawAClockwise = 24; +static const ExtraID kN22ClawFromBToA = 25; +static const ExtraID kN22ClawFromBToC = 26; +static const ExtraID kN22ClawFromBToD = 27; +static const ExtraID kN22ClawBLoop = 28; +static const ExtraID kN22ClawBPinch = 29; +static const ExtraID kN22ClawBCounterclockwise = 30; +static const ExtraID kN22ClawBClockwise = 31; +static const ExtraID kN22ClawFromCToB = 32; +static const ExtraID kN22ClawCLoop = 33; +static const ExtraID kN22ClawCPinch = 34; +static const ExtraID kN22ClawCCounterclockwise = 35; +static const ExtraID kN22ClawCClockwise = 36; +static const ExtraID kN22ClawFromDToB = 37; +static const ExtraID kN22ClawDLoop = 38; +static const ExtraID kN22ClawDPinch = 39; +static const ExtraID kN22ClawDCounterclockwise = 40; +static const ExtraID kN22ClawDClockwise = 41; + + +// Norad Delta Extra sequence IDs. + +static const ExtraID kArriveFromSubChase = 0; +static const ExtraID kN59ZoomWithRobot = 1; +static const ExtraID kN59RobotApproaches = 2; +static const ExtraID kN59RobotPunchLoop = 3; +static const ExtraID kN59PlayerWins1 = 4; +static const ExtraID kN59PlayerWins2 = 5; +static const ExtraID kN59RobotWins = 6; +static const ExtraID kN59RobotHeadOpens = 7; +static const ExtraID kN59Biochips111 = 8; +static const ExtraID kN59Biochips011 = 9; +static const ExtraID kN59Biochips101 = 10; +static const ExtraID kN59Biochips001 = 11; +static const ExtraID kN59Biochips110 = 12; +static const ExtraID kN59Biochips010 = 13; +static const ExtraID kN59Biochips100 = 14; +static const ExtraID kN59Biochips000 = 15; +static const ExtraID kN59RobotDisappears = 16; +static const ExtraID kN60ClawFromAToB = 17; +static const ExtraID kN60ClawALoop = 18; +static const ExtraID kN60ClawAPinch = 19; +static const ExtraID kN60ClawACounterclockwise = 20; +static const ExtraID kN60ClawAClockwise = 21; +static const ExtraID kN60ClawFromBToA = 22; +static const ExtraID kN60ClawFromBToC = 23; +static const ExtraID kN60ClawFromBToD = 24; +static const ExtraID kN60ClawBLoop = 25; +static const ExtraID kN60ClawBPinch = 26; +static const ExtraID kN60ClawBCounterclockwise = 27; +static const ExtraID kN60ClawBClockwise = 28; +static const ExtraID kN60ClawFromCToB = 29; +static const ExtraID kN60ClawCLoop = 30; +static const ExtraID kN60ClawCPinch = 31; +static const ExtraID kN60ClawCCounterclockwise = 32; +static const ExtraID kN60ClawCClockwise = 33; +static const ExtraID kN60ClawFromDToB = 34; +static const ExtraID kN60ClawDLoop = 35; +static const ExtraID kN60ClawDPinch = 36; +static const ExtraID kN60ClawDCounterclockwise = 37; +static const ExtraID kN60ClawDClockwise = 38; +static const ExtraID kN60RobotApproaches = 39; +static const ExtraID kN60FirstMistake = 40; +static const ExtraID kN60ArmActivated = 41; +static const ExtraID kN60SecondMistake = 42; +static const ExtraID kN60ArmToPositionB = 43; +static const ExtraID kN60ThirdMistake = 44; +static const ExtraID kN60ArmGrabsRobot = 45; +static const ExtraID kN60FourthMistake = 46; +static const ExtraID kN60ArmCarriesRobotToPositionA = 47; +static const ExtraID kN60PlayerFollowsRobotToDoor = 48; +static const ExtraID kN60RobotHeadOpens = 49; +static const ExtraID kN60Biochips111 = 50; +static const ExtraID kN60Biochips011 = 51; +static const ExtraID kN60Biochips101 = 52; +static const ExtraID kN60Biochips001 = 53; +static const ExtraID kN60Biochips110 = 54; +static const ExtraID kN60Biochips010 = 55; +static const ExtraID kN60Biochips100 = 56; +static const ExtraID kN60Biochips000 = 57; +static const ExtraID kN60RobotDisappears = 58; +static const ExtraID kNoradDeltaRetinalScanBad = 59; +static const ExtraID kNoradDeltaRetinalScanGood = 60; +static const ExtraID kN79BrightView = 61; + +// Norad Delta Tables + +static const TimeScale kNoradDeltaMovieScale = 600; +static const TimeScale kNoradDeltaFramesPerSecond = 15; +static const TimeScale kNoradDeltaFrameDuration = 40; + +// Alternate IDs. + +static const AlternateID kAltNoradDeltaNormal = 0; + +// Room IDs. + +static const RoomID kNorad41 = 0; +static const RoomID kNorad42 = 1; +static const RoomID kNorad43 = 2; +static const RoomID kNorad44 = 3; +static const RoomID kNorad45 = 4; +static const RoomID kNorad46 = 5; +static const RoomID kNorad47 = 6; +static const RoomID kNorad48 = 7; +static const RoomID kNorad48South = 8; +static const RoomID kNorad49 = 9; +static const RoomID kNorad49South = 10; +static const RoomID kNorad50 = 11; +static const RoomID kNorad50East = 12; +static const RoomID kNorad51 = 13; +static const RoomID kNorad52 = 14; +static const RoomID kNorad53 = 15; +static const RoomID kNorad54 = 16; +static const RoomID kNorad54North = 17; +static const RoomID kNorad55 = 18; +static const RoomID kNorad56 = 19; +static const RoomID kNorad57 = 20; +static const RoomID kNorad58 = 21; +static const RoomID kNorad59 = 22; +static const RoomID kNorad59West = 23; +static const RoomID kNorad60 = 24; +static const RoomID kNorad60West = 25; +static const RoomID kNorad61 = 26; +static const RoomID kNorad62 = 27; +static const RoomID kNorad63 = 28; +static const RoomID kNorad64 = 29; +static const RoomID kNorad65 = 30; +static const RoomID kNorad66 = 31; +static const RoomID kNorad67 = 32; +static const RoomID kNorad68 = 33; +static const RoomID kNorad68West = 34; +static const RoomID kNorad69 = 35; +static const RoomID kNorad78 = 36; +static const RoomID kNorad79 = 37; +static const RoomID kNorad79West = 38; + +// Hot Spot Activation IDs. + + +// Hot Spot IDs. + +static const HotSpotID kNorad48ElevatorSpotID = 5000; +static const HotSpotID kNorad48ElevatorOutSpotID = 5001; +static const HotSpotID kNorad48ElevatorUpSpotID = 5002; +static const HotSpotID kNorad49ElevatorSpotID = 5003; +static const HotSpotID kNorad49ElevatorOutSpotID = 5004; +static const HotSpotID kNorad49ElevatorDownSpotID = 5005; +static const HotSpotID kNorad50DoorSpotID = 5006; +static const HotSpotID kNorad50DoorOutSpotID = 5007; +static const HotSpotID kDeltaUpperPressureDoorUpSpotID = 5008; +static const HotSpotID kDeltaUpperPressureDoorDownSpotID = 5009; +static const HotSpotID kNorad54DoorSpotID = 5010; +static const HotSpotID kNorad54DoorOutSpotID = 5011; +static const HotSpotID kNorad59WestSpotID = 5012; +static const HotSpotID kNorad59WestOutSpotID = 5013; +static const HotSpotID kDeltaLowerPressureDoorUpSpotID = 5014; +static const HotSpotID kDeltaLowerPressureDoorDownSpotID = 5015; +static const HotSpotID kDelta59RobotHeadSpotID = 5016; +static const HotSpotID kDelta59RobotShieldBiochipSpotID = 5017; +static const HotSpotID kDelta59RobotOpMemBiochipSpotID = 5018; +static const HotSpotID kDelta59RobotRetinalBiochipSpotID = 5019; +static const HotSpotID kNorad60MonitorSpotID = 5020; +static const HotSpotID kNorad60MonitorOutSpotID = 5021; +static const HotSpotID kNorad60LaunchPrepSpotID = 5022; +static const HotSpotID kNorad60ClawControlSpotID = 5023; +static const HotSpotID kNorad60ClawPinchSpotID = 5024; +static const HotSpotID kNorad60ClawDownSpotID = 5025; +static const HotSpotID kNorad60ClawRightSpotID = 5026; +static const HotSpotID kNorad60ClawLeftSpotID = 5027; +static const HotSpotID kNorad60ClawUpSpotID = 5028; +static const HotSpotID kNorad60ClawCCWSpotID = 5029; +static const HotSpotID kNorad60ClawCWSpotID = 5030; +static const HotSpotID kDelta60RobotHeadSpotID = 5031; +static const HotSpotID kDelta60RobotShieldBiochipSpotID = 5032; +static const HotSpotID kDelta60RobotOpMemBiochipSpotID = 5033; +static const HotSpotID kDelta60RobotRetinalBiochipSpotID = 5034; +static const HotSpotID kNorad68WestSpotID = 5035; +static const HotSpotID kNorad68WestOutSpotID = 5036; +static const HotSpotID kNorad79WestSpotID = 5037; +static const HotSpotID kNorad79WestOutSpotID = 5038; +static const HotSpotID kNorad79SpinLeftSpotID = 5039; +static const HotSpotID kNorad79SpinRightSpotID = 5040; +static const HotSpotID kNorad79SpinUpSpotID = 5041; +static const HotSpotID kNorad79SpinDownSpotID = 5042; +static const HotSpotID kNorad79SiloAreaSpotID = 5043; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.cpp b/engines/pegasus/neighborhood/norad/delta/globegame.cpp new file mode 100644 index 0000000000..7d4c1c5f8f --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/globegame.cpp @@ -0,0 +1,1062 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/cursor.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/delta/globegame.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +static const TimeValue kDurationPerFrame = 600 / 15; +static const TimeValue kDurationPerRow = kNumLongSlices * kDurationPerFrame; +static const short kVerticalDuration = 16; + +GlobeTracker::GlobeTracker(Movie *globeMovie, Picture *leftHighlight, Picture *rightHighlight, +		Picture *upHighlight, Picture *downHighlight) { +	_globeMovie = globeMovie; +	_leftHighlight = leftHighlight; +	_rightHighlight = rightHighlight; +	_upHighlight = upHighlight; +	_downHighlight = downHighlight; +} + +void GlobeTracker::setTrackParameters(const Hotspot *trackSpot, GlobeTrackDirection direction) { +	_trackSpot = trackSpot; +	_trackDirection = direction; + +	TimeValue time, newTime, start; + +	switch (_trackDirection) { +	case kTrackLeft: +		time = _globeMovie->getTime(); + +		if (((time / kDurationPerRow) & 1) == 0) { +			start = (time / kDurationPerRow + 1) * kDurationPerRow; +			newTime = start + kDurationPerRow - time % kDurationPerRow; +		} else { +			start = (time / kDurationPerRow) * kDurationPerRow; +			newTime = time; +		} + +		_globeMovie->setSegment(start, start + kDurationPerRow); + +		if (newTime != time) { +			_globeMovie->setTime(newTime); +			_globeMovie->redrawMovieWorld(); +		} + +		_globeMovie->setFlags(kLoopTimeBase); +		break; +	case kTrackRight: +		time = _globeMovie->getTime(); + +		if (((time / kDurationPerRow) & 1) == 0) { +			start = (time / kDurationPerRow) * kDurationPerRow; +			newTime = time; +		} else { +			start = (time / kDurationPerRow - 1) * kDurationPerRow; +			newTime = start + kDurationPerRow - time % kDurationPerRow; +		} + +		_globeMovie->setSegment(start, start + kDurationPerRow); + +		if (newTime != time) { +			_globeMovie->setTime(newTime); +			_globeMovie->redrawMovieWorld(); +		} + +		_globeMovie->setFlags(kLoopTimeBase); +		break; +	case kTrackUp: +	case kTrackDown: +		_globeMovie->setSegment(0, _globeMovie->getDuration()); +		_globeMovie->setFlags(0); +		break; +	} +} + +void GlobeTracker::activateHotspots() { +	Tracker::activateHotspots(); + +	if (_trackSpot) +		g_allHotspots.activateOneHotspot(_trackSpot->getObjectID()); +} + +bool GlobeTracker::stopTrackingInput(const Input &input) { +	return !JMPPPInput::isPressingInput(input); +} + +void GlobeTracker::continueTracking(const Input &input) { +	Common::Point where; +	input.getInputLocation(where); + +	if (g_allHotspots.findHotspot(where) == _trackSpot) +		trackGlobeMovie(); +	else +		stopGlobeMovie(); +} + +void GlobeTracker::startTracking(const Input &input) { +	Tracker::startTracking(input); +	trackGlobeMovie(); +} + +void GlobeTracker::stopTracking(const Input &input) { +	Tracker::stopTracking(input); +	stopGlobeMovie(); +} + +void GlobeTracker::trackGlobeMovie() { +	TimeValue time; + +	switch (_trackDirection) { +	case kTrackLeft: +		if (!_globeMovie->isRunning()) +			_globeMovie->start(); + +		_leftHighlight->show(); +		break; +	case kTrackRight: +		if (!_globeMovie->isRunning()) +			_globeMovie->start(); + +		_rightHighlight->show(); +		break; +	case kTrackUp: +		time = _globeMovie->getTime(); + +		if (_trackTime == 0) { +			_trackTime = tickCount(); +		} else if ((int)time - (int)kDurationPerRow * 2 >= 0 && (int)tickCount() >= _trackTime + kVerticalDuration) { +			_trackTime = tickCount(); +			_globeMovie->setTime(time - kDurationPerRow * 2); +			_globeMovie->redrawMovieWorld(); +		} + +		_upHighlight->show(); +		break; +	case kTrackDown: +		time = _globeMovie->getTime(); + +		if (_trackTime == 0) { +			_trackTime = tickCount(); +		} else if (time + kDurationPerRow * 2 < _globeMovie->getDuration() && (int)tickCount() >= _trackTime + kVerticalDuration) { +			_trackTime = tickCount(); +			_globeMovie->setTime(time + kDurationPerRow * 2); +			_globeMovie->redrawMovieWorld(); +		} + +		_downHighlight->show(); +		break; +	} +} + +void GlobeTracker::stopGlobeMovie() { +	switch (_trackDirection) { +	case kTrackLeft: +		_leftHighlight->hide(); +		_globeMovie->stop(); +		break; +	case kTrackRight: +		_rightHighlight->hide(); +		_globeMovie->stop(); +		break; +	case kTrackUp: +		_upHighlight->hide(); +		_trackTime = tickCount() - kVerticalDuration; +		break; +	case kTrackDown: +		_downHighlight->hide(); +		_trackTime = tickCount() - kVerticalDuration; +		break; +	} +} + +// Globe game PICTs: +static const ResIDType kGlobeCircleLeftPICTID = 300; +static const ResIDType kGlobeCircleRightPICTID = 301; +static const ResIDType kGlobeCircleUpPICTID = 302; +static const ResIDType kGlobeCircleDownPICTID = 303; +static const ResIDType kTargetUpperLeftPICTID = 304; +static const ResIDType kTargetUpperRightPICTID = 305; +static const ResIDType kTargetLowerLeftPICTID = 306; +static const ResIDType kTargetLowerRightPICTID = 307; +static const ResIDType kMotionHiliteLeftPICTID = 308; +static const ResIDType kMotionHiliteRightPICTID = 309; +static const ResIDType kMotionHiliteUpPICTID = 310; +static const ResIDType kMotionHiliteDownPICTID = 311; + +static const ResIDType kGlobeCountdownDigitsID = 350; + +static const int kGlobeCountdownWidth = 28; +static const int kGlobeCountdownHeight = 12; +static const int kGlobeCountdownOffset1 = 12; +static const int kGlobeCountdownOffset2 = 20; + +GlobeCountdown::GlobeCountdown(const DisplayElementID id) : IdlerAnimation(id) {	 +	_digits.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCountdownDigitsID); + +	Common::Rect r; +	_digits.getSurfaceBounds(r); +	_digitOffset = r.width() / 10; +	setScale(1); +	sizeElement(kGlobeCountdownWidth, kGlobeCountdownHeight); +} + +void GlobeCountdown::setDisplayOrder(const DisplayOrder order) { +	IdlerAnimation::setDisplayOrder(order); +} + +void GlobeCountdown::show() { +	IdlerAnimation::show(); +} + +void GlobeCountdown::hide() { +	IdlerAnimation::hide(); +} + +void GlobeCountdown::moveElementTo(const CoordType x, const CoordType y) { +	IdlerAnimation::moveElementTo(x, y); +} + +void GlobeCountdown::setCountdownTime(const int numSeconds) { +	stop(); +	setSegment(0, numSeconds); +	setTime(numSeconds); +} + +void GlobeCountdown::startCountdown() { +	setRate(-1); +} + +void GlobeCountdown::stopCountdown() { +	stop(); +} + +void GlobeCountdown::draw(const Common::Rect &) { +	Common::Rect r1; +	_digits.getSurfaceBounds(r1); +	r1.right = r1.left + _digitOffset; +	Common::Rect r2 = r1; +	TimeValue time = getTime(); + +	Common::Rect bounds; +	getBounds(bounds); + +	if (time > 60 * 9 + 59) { +		r2.moveTo(bounds.left, bounds.top); +		r1.moveTo(9 * _digitOffset, 0); +		_digits.copyToCurrentPort(r1, r2); +		 +		r2.moveTo(bounds.left + kGlobeCountdownOffset1, bounds.top); +		r1.moveTo(5 * _digitOffset, 0); +		_digits.copyToCurrentPort(r1, r2); +		 +		r2.moveTo(bounds.left + kGlobeCountdownOffset2, bounds.top); +		r1.moveTo(9 * _digitOffset, 0); +		_digits.copyToCurrentPort(r1, r2); +	} else { +		r2.moveTo(bounds.left, bounds.top); +		r1.moveTo((time / 60) * _digitOffset, 0); +		_digits.copyToCurrentPort(r1, r2); +		 +		time %= 60; +		r2.moveTo(bounds.left + kGlobeCountdownOffset1, bounds.top); +		r1.moveTo((time / 10) * _digitOffset, 0); +		_digits.copyToCurrentPort(r1, r2); +		 +		r2.moveTo(bounds.left + kGlobeCountdownOffset2, bounds.top); +		r1.moveTo((time % 10) * _digitOffset, 0); +		_digits.copyToCurrentPort(r1, r2); +	} +} + +const int16 GlobeGame::_siloCoords[kNumAllSilos][2] = { +	{ 60, -151 }, // Anchorage, Alaska +	{ 6, 39 },    // Addis Ababa, Ethiopia +	{ -22, 44 },  // Antaro, Madagascar +	{ 30, -83 },  // Atlanta, Georgia +	{ -41, 173 }, // Auckland, New Zealand +	{ 39, -78 },  // Baltimore, Maryland +	{ 11, 101 },  // Bangkok, Thailand +	{ 2, -75 },   // Bogota, Colombia +	{ 46, 4 },    // Bonn, Germany +	{ 51, -7 },   // Dublin, Ireland +	{ 28, -1 },   // El Menia, Algeria +	{ 67, -111 }, // Ellesmere, Canada +	{ 43, -107 }, // Glasgow, Montana +	{ 61, -48 },  // Godthab, Greenland +	{ 19, -157 }, // Honolulu, Hawaii +	{ 6, 5 },     // Ibadan, Nigeria +	{ -29, 26 },  // Johannesburg, South Africa +	{ 46, 92 },   // Kobdo, Mongolia +	{ -15, -63 }, // La Paz, Bolivia +	{ -35, -61 }, // La Plata, Argentina +	{ -9, -76 },  // Lima, Peru +	{ 38, -4 },   // Madrid, Spain +	{ -8, -51 },  // Manaus, Brazil +	{ 13, 120 },  // Manila, Phillipines +	{ -35, 143 }, // Melbourne, Australia +	{ 60, -161 }, // Nome, Alaska +	{ -7, 142 },  // Papua, New Guinea +	{ -32, 117 }, // Perth, West Australia +	{ 34, -114 }, // Phoenix, Arizona +	{ 18, -71 },  // Port-Au-Prince, Haiti +	{ 42, -121 }, // Portland, Oregon +	{ 61, -20 },  // Reykjavik, Iceland +	{ -22, -46 }, // Rio de Janeiro +	{ 27, -101 }, // San Antonio, Texas +	{ 34, 126 },  // Seoul, Korea +	{ 37, -87 },  // Saint Louis, Missouri +	{ 60, 30 },   // Saint Petersberg, Russia +	{ 56, 12 },   // Stockholm, Sweden +	{ 51, 105 },  // Svortalsk, Siberia +	{ 36, -96 }   // Tulsa, Oklahoma +}; + +const int16 GlobeGame::_targetSilo[kNumTargetSilos] = { +	14, 9, 1, 33, 6, 8, 34, 31, 38, 21 +}; + +const short GlobeGame::_timeLimit[kNumTargetSilos] = { +	120, 110, 100, 90, 80, 70, 60, 50, 40, 30 +}; + +const TimeValue GlobeGame::_siloName[kNumTargetSilos][2] = { +	{ kHonoluluIn, kHonoluluOut }, +	{ kDublinIn, kDublinOut }, +	{ kAddisAbabaIn, kAddisAbabaOut }, +	{ kSanAntonioIn, kSanAntonioOut }, +	{ kBangkokIn, kBangkokOut }, +	{ kBonnIn, kBonnOut }, +	{ kSeoulIn, kSeoulOut }, +	{ kReykjavikIn, kReykjavikOut }, +	{ kSvortalskIn, kSvortalskOut }, +	{ kMadridIn, kMadridOut } +}; + +// From globe room models + +static const GlobeGame::Point3D kCameraLocation = { 0.53f, 4.4f, -0.86f }; +static const GlobeGame::Point3D kGlobeCenter = { -31.5f, 8.0f, 0.0f }; +static const float kGlobeRadius = 8.25f; +static const int16 kDegreesPerLongSlice = 360 / kNumLongSlices; +static const int16 kDegreesPerLatSlice = 25; +static const int16 kLongOrigin = -95; + +// Other constants. + +static const float kTanFieldOfView = 0.7082373180482f; +static const float kPicturePlaneDistance = 10.0f; // Completely arbitrary. +static const int16 kLatError = 2; +static const int16 kLongError = 2; +static const TimeValue kGlobeMovieStartTime = 2 * 2 * kNumLongSlices * 600 / 15; + +static const TimeValue kTimePerGlobeFrame = 40; + +static const NotificationFlags kGlobeSplash1Finished = 1; +static const NotificationFlags kGlobeTimerExpired = kGlobeSplash1Finished << 1; +static const NotificationFlags kMaxDeactivatedFinished = kGlobeTimerExpired << 1; + +static const NotificationFlags kGlobeNotificationFlags = kGlobeSplash1Finished | +													kGlobeTimerExpired | +													kMaxDeactivatedFinished; + +static const int16 kSplash1End = 4; +static const int16 kSplash2End = 5; +static const int16 kSplash3Start = 8; +static const int16 kSplash3Stop = 9; +static const int16 kSplash4Start = 9; +static const int16 kSplash4Stop = 10; +static const int16 kNewLaunchSiloTime = 10; +static const int16 kSiloDeactivatedTime = 11; +static const int16 kMissileLaunchedTime = 12; +static const int16 kMaxDeactivatedStart = 13; +static const int16 kMaxDeactivatedStop = 23; + +static const int16 kGamePlaying = 1; +static const int16 kGameOver = 2; + +enum { +	kGameIntro, +	kPlayingRobotIntro, +	kPlayingStrikeAuthorized, +	kPlayingPrimaryTarget, +	kPlayingNewSilo1, +	kPlayingNewSilo2, +	kPlayingNewSilo3, +	kPlayingTime, +	kPlayingInstructions, +	kWaitingForPlayer, +	kSiloDeactivated, +	kRobotTaunting, +	kDelayingPlayer, +	kPlayerWon1, +	kPlayerWon2, +	kPlayerLost1 +}; + +// TODO: Use ScummVM equivalent +static const float kPI = 3.1415926535f; + +float degreesToRadians(float angle) { +	return (angle * kPI) / 180; +} + +float radiansToDegrees(float angle) { +	return (angle * 180) / kPI; +} + +GlobeGame::GlobeGame(Neighborhood *handler) : GameInteraction(kNoradGlobeGameInteractionID, handler), +		_monitorMovie(kGlobeMonitorID), _globeMovie(kGlobeMovieID), _upperNamesMovie(kGlobeUpperNamesID), +		_lowerNamesMovie(kGlobeLowerNamesID), _globeNotification(kNoradGlobeNotificationID, (PegasusEngine *)g_engine), +		_globeCircleLeft(kGlobeCircleLeftID), _globeCircleRight(kGlobeCircleRightID), +		_globeCircleUp(kGlobeCircleUpID), _globeCircleDown(kGlobeCircleDownID), +		_motionHighlightLeft(kMotionHiliteLeftID), _motionHighlightRight(kMotionHiliteRightID), +		_motionHighlightUp(kMotionHiliteUpID), _motionHighlightDown(kMotionHiliteDownID), +		_targetHighlightUpperLeft(kTargetHiliteUpperLeftID), _targetHighlightUpperRight(kTargetHiliteUpperRightID), +		_targetHighlightLowerLeft(kTargetHiliteLowerLeftID), _targetHighlightLowerRight(kTargetHiliteLowerRightID), +		_globeTracker(&_globeMovie, &_motionHighlightLeft, &_motionHighlightRight, &_motionHighlightUp, +				&_motionHighlightDown), _countdown(kGlobeCountdownID) { +	_neighborhoodNotification = handler->getNeighborhoodNotification(); +} + +void GlobeGame::openInteraction() { +	_monitorMovie.initFromMovieFile("Images/Norad Delta/N79 Left Monitor"); +	_monitorMovie.moveElementTo(kGlobeMonitorLeft, kGlobeMonitorTop); +	_monitorMovie.setDisplayOrder(kGlobeMonitorLayer); +	_monitorMovie.startDisplaying(); +	_monitorMovie.setSegment(0, kSplash1End * _monitorMovie.getScale()); +	_monitorMovie.show(); +	 +	_monitorCallBack.setNotification(&_globeNotification); +	_monitorCallBack.initCallBack(&_monitorMovie, kCallBackAtExtremes); +	_monitorCallBack.setCallBackFlag(kGlobeSplash1Finished); +	_monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + +	_upperNamesMovie.initFromMovieFile("Images/Norad Delta/Upper Names"); +	_upperNamesMovie.moveElementTo(kGlobeUpperNamesLeft, kGlobeUpperNamesTop); +	_upperNamesMovie.setDisplayOrder(kGlobeUpperNamesLayer); +	_upperNamesMovie.startDisplaying(); + +	_lowerNamesMovie.initFromMovieFile("Images/Norad Delta/Lower Names"); +	_lowerNamesMovie.moveElementTo(kGlobeLowerNamesLeft, kGlobeLowerNamesTop); +	_lowerNamesMovie.setDisplayOrder(kGlobeLowerNamesLayer); +	_lowerNamesMovie.startDisplaying(); + +	_globeMovie.initFromMovieFile("Images/Norad Delta/Spinning Globe"); +	_globeMovie.moveElementTo(kGlobeLeft, kGlobeTop); +	_globeMovie.setDisplayOrder(kGlobeMovieLayer); +	_globeMovie.startDisplaying(); +	_globeMovie.setTime(kGlobeMovieStartTime); +	_globeMovie.redrawMovieWorld(); + +	_globeCircleLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleLeftPICTID, true); +	_globeCircleLeft.moveElementTo(kGlobeCircleLeftLeft, kGlobeCircleLeftTop); +	_globeCircleLeft.setDisplayOrder(kGlobeCircleLayer); +	_globeCircleLeft.startDisplaying(); + +	_globeCircleRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleRightPICTID, true); +	_globeCircleRight.moveElementTo(kGlobeCircleRightLeft, kGlobeCircleRightTop); +	_globeCircleRight.setDisplayOrder(kGlobeCircleLayer); +	_globeCircleRight.startDisplaying(); + +	_globeCircleUp.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleUpPICTID, true); +	_globeCircleUp.moveElementTo(kGlobeCircleUpLeft, kGlobeCircleUpTop); +	_globeCircleUp.setDisplayOrder(kGlobeCircleLayer); +	_globeCircleUp.startDisplaying(); + +	_globeCircleDown.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kGlobeCircleDownPICTID, true); +	_globeCircleDown.moveElementTo(kGlobeCircleDownLeft, kGlobeCircleDownTop); +	_globeCircleDown.setDisplayOrder(kGlobeCircleLayer); +	_globeCircleDown.startDisplaying(); + +	_motionHighlightLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteLeftPICTID, true); +	_motionHighlightLeft.moveElementTo(kGlobeLeftMotionHiliteLeft, kGlobeLeftMotionHiliteTop); +	_motionHighlightLeft.setDisplayOrder(kGlobeHilitesLayer); +	_motionHighlightLeft.startDisplaying(); + +	_motionHighlightRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteRightPICTID, true); +	_motionHighlightRight.moveElementTo(kGlobeRightMotionHiliteLeft, kGlobeRightMotionHiliteTop); +	_motionHighlightRight.setDisplayOrder(kGlobeCircleLayer); +	_motionHighlightRight.startDisplaying(); + +	_motionHighlightUp.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteUpPICTID, true); +	_motionHighlightUp.moveElementTo(kGlobeUpMotionHiliteLeft, kGlobeUpMotionHiliteTop); +	_motionHighlightUp.setDisplayOrder(kGlobeHilitesLayer); +	_motionHighlightUp.startDisplaying(); + +	_motionHighlightDown.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kMotionHiliteDownPICTID, true); +	_motionHighlightDown.moveElementTo(kGlobeDownMotionHiliteLeft, kGlobeDownMotionHiliteTop); +	_motionHighlightDown.setDisplayOrder(kGlobeHilitesLayer); +	_motionHighlightDown.startDisplaying(); + +	_targetHighlightUpperLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetUpperLeftPICTID, true); +	_targetHighlightUpperLeft.moveElementTo(kGlobeUpperLeftHiliteLeft, kGlobeUpperLeftHiliteTop); +	_targetHighlightUpperLeft.setDisplayOrder(kGlobeHilitesLayer); +	_targetHighlightUpperLeft.startDisplaying(); + +	_targetHighlightUpperRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetUpperRightPICTID, true); +	_targetHighlightUpperRight.moveElementTo(kGlobeUpperRightHiliteLeft, kGlobeUpperRightHiliteTop); +	_targetHighlightUpperRight.setDisplayOrder(kGlobeHilitesLayer); +	_targetHighlightUpperRight.startDisplaying(); + +	_targetHighlightLowerLeft.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetLowerLeftPICTID, true); +	_targetHighlightLowerLeft.moveElementTo(kGlobeLowerLeftHiliteLeft, kGlobeLowerLeftHiliteTop); +	_targetHighlightLowerLeft.setDisplayOrder(kGlobeHilitesLayer); +	_targetHighlightLowerLeft.startDisplaying(); + +	_targetHighlightLowerRight.initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kTargetLowerRightPICTID, true); +	_targetHighlightLowerRight.moveElementTo(kGlobeLowerRightHiliteLeft, kGlobeLowerRightHiliteTop); +	_targetHighlightLowerRight.setDisplayOrder(kGlobeHilitesLayer); +	_targetHighlightLowerRight.startDisplaying(); + +	_countdown.setDisplayOrder(kGlobeCountdownLayer); +	_countdown.moveElementTo(kGlobeCountdownLeft, kGlobeCountdownTop); +	_countdown.startDisplaying(); +	_countdown.setCountdownTime(_timeLimit[0]); + +	_countdownCallBack.setNotification(&_globeNotification); +	_countdownCallBack.initCallBack(&_countdown, kCallBackAtExtremes); +	_countdownCallBack.setCallBackFlag(kGlobeTimerExpired); +	_countdownCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); + +	_globeNotification.notifyMe(this, kGlobeNotificationFlags, kGlobeNotificationFlags); + +	_gameState = kGameIntro; +	_currentSiloIndex = 0; +	_playedInstructions = false; + +	_neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag, kDelayCompletedFlag | kSpotSoundCompletedFlag); +} + +void GlobeGame::initInteraction() { +	_monitorMovie.start(); +	_monitorMovie.redrawMovieWorld(); +} + +void GlobeGame::closeInteraction() { +	_monitorMovie.stop(); +	_monitorMovie.stopDisplaying(); +	_monitorMovie.releaseMovie(); +	_monitorCallBack.releaseCallBack(); + +	_globeMovie.stop(); +	_globeMovie.stopDisplaying(); +	_globeMovie.releaseMovie(); +	_globeNotification.cancelNotification(this); + +	_upperNamesMovie.stop(); +	_upperNamesMovie.stopDisplaying(); +	_upperNamesMovie.releaseMovie(); + +	_lowerNamesMovie.stop(); +	_lowerNamesMovie.stopDisplaying(); +	_lowerNamesMovie.releaseMovie(); + +	_countdown.hide(); +	_countdown.stopDisplaying(); +	_countdownCallBack.releaseCallBack(); + +	_globeCircleLeft.stopDisplaying(); +	_globeCircleLeft.deallocateSurface(); +	_globeCircleRight.stopDisplaying(); +	_globeCircleRight.deallocateSurface(); +	_globeCircleUp.stopDisplaying(); +	_globeCircleUp.deallocateSurface(); +	_globeCircleDown.stopDisplaying(); +	_globeCircleDown.deallocateSurface(); + +	_motionHighlightLeft.stopDisplaying(); +	_motionHighlightLeft.deallocateSurface(); +	_motionHighlightRight.stopDisplaying(); +	_motionHighlightRight.deallocateSurface(); +	_motionHighlightUp.stopDisplaying(); +	_motionHighlightUp.deallocateSurface(); +	_motionHighlightDown.stopDisplaying(); +	_motionHighlightDown.deallocateSurface(); + +	_targetHighlightUpperLeft.stopDisplaying(); +	_targetHighlightUpperLeft.deallocateSurface(); +	_targetHighlightUpperRight.stopDisplaying(); +	_targetHighlightUpperRight.deallocateSurface(); +	_targetHighlightLowerLeft.stopDisplaying(); +	_targetHighlightLowerLeft.deallocateSurface(); +	_targetHighlightLowerRight.stopDisplaying(); +	_targetHighlightLowerRight.deallocateSurface(); + +	_neighborhoodNotification->cancelNotification(this); +} + +void GlobeGame::receiveNotification(Notification *notification, const NotificationFlags flags) { +	TimeScale scale = _monitorMovie.getScale(); + +	if (notification == _neighborhoodNotification) { +		switch (_gameState) { +		case kPlayingRobotIntro: +			_monitorMovie.stop(); +			_monitorMovie.setSegment(0, _monitorMovie.getDuration()); +			_monitorMovie.setTime(kSplash2End * scale - 1); +			_monitorMovie.setFlags(0); + +			_owner->requestDelay(1, 2, kFilterNoInput, 0); +			_owner->requestSpotSound(kStrikeAuthorizedIn, kStrikeAuthorizedOut, +					kFilterNoInput, kSpotSoundCompletedFlag); +			_gameState = kPlayingStrikeAuthorized; +			break; +		case kPlayingStrikeAuthorized: +			_monitorMovie.setSegment(kSplash3Start * scale, kSplash3Stop * scale); +			_monitorMovie.setTime(kSplash3Start * scale); +			_monitorMovie.redrawMovieWorld(); + +			_owner->requestDelay(1, 3, kFilterNoInput, 0); +			_owner->requestSpotSound(kPrimaryTargetIn, kPrimaryTargetOut, kFilterNoInput, 0); +			_owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); +			_monitorMovie.start(); +			_gameState = kPlayingPrimaryTarget; +			break; +		case kPlayingPrimaryTarget: +			_monitorMovie.stop(); +			_monitorMovie.setSegment(0, _monitorMovie.getDuration()); +			_monitorMovie.setTime(kNewLaunchSiloTime * scale); +			_owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput, +					kSpotSoundCompletedFlag); +			_gameState = kPlayingNewSilo1; +			break; +		case kPlayingNewSilo1: +			_monitorMovie.stop(); +			_monitorMovie.setSegment(0, _monitorMovie.getDuration()); +			_owner->requestDelay(1, 3, kFilterNoInput, kDelayCompletedFlag); +			_gameState = kPlayingNewSilo2; +			break; +		case kPlayingNewSilo2: +			_upperNamesMovie.show(); +			_upperNamesMovie.setTime(_currentSiloIndex * _upperNamesMovie.getScale()); +			_upperNamesMovie.redrawMovieWorld(); +			_monitorMovie.setTime(kSplash4Stop * scale - 1); +			_monitorMovie.redrawMovieWorld(); +			_owner->requestSpotSound(_siloName[_currentSiloIndex][0], _siloName[_currentSiloIndex][1], kFilterNoInput, 0); +			_owner->requestDelay(1, 3, kFilterNoInput, 0); +			_owner->requestSpotSound(kLaunchToProceedIn, kLaunchToProceedOut, kFilterNoInput, 0); +			_owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); +			_gameState = kPlayingNewSilo3; +			break; +		case kPlayingNewSilo3: +			_countdown.stopCountdown(); +			_countdown.setCountdownTime(_timeLimit[_currentSiloIndex]); +			_countdown.show(); +			_gameState = kPlayingTime; + +			if (_timeLimit[_currentSiloIndex] >= 120) +				_owner->requestSpotSound(kTwoMinutesIn, kTwoMinutesOut, kFilterNoInput, 0); +			else if (_timeLimit[_currentSiloIndex] >= 60) +				_owner->requestSpotSound(kOneMinuteIn, kOneMinuteOut, kFilterNoInput, 0); + +			switch (_timeLimit[_currentSiloIndex] % 60) { +			case 0: +				_owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); +				break; +			case 10: +				_owner->requestDelay(1, 5, kFilterNoInput, 0); +				_owner->requestSpotSound(kTenSecondsIn, kTenSecondsOut, kFilterNoInput, +						kSpotSoundCompletedFlag); +				break; +			case 20: +				_owner->requestDelay(1, 5, kFilterNoInput, 0); +				_owner->requestSpotSound(kTwentySecondsIn, kTwentySecondsOut, +						kFilterNoInput, kSpotSoundCompletedFlag); +				break; +			case 30: +				_owner->requestDelay(1, 5, kFilterNoInput, 0); +				_owner->requestSpotSound(kThirtySecondsIn, kThirtySecondsOut, +						kFilterNoInput, kSpotSoundCompletedFlag); +				break; +			case 40: +				_owner->requestDelay(1, 5, kFilterNoInput, 0); +				_owner->requestSpotSound(kFortySecondsIn, kFortySecondsOut, +						kFilterNoInput, kSpotSoundCompletedFlag); +				break; +			case 50: +				_owner->requestDelay(1, 5, kFilterNoInput, 0); +				_owner->requestSpotSound(kFiftySecondsIn, kFiftySecondsOut, +						kFilterNoInput, kSpotSoundCompletedFlag); +				break; +			} +		case kPlayingTime: +			_gameState = kPlayingInstructions; +			_globeMovie.show(); +			_globeCircleLeft.show(); +			_globeCircleRight.show(); +			_globeCircleUp.show(); +			_globeCircleDown.show(); + +			if (_playedInstructions) { +				receiveNotification(notification, flags); +			} else { +				_owner->requestSpotSound(kToDeactivateIn, kToDeactivateOut, kFilterNoInput, +						kSpotSoundCompletedFlag); +				_playedInstructions = true; +			} +			break; +		case kPlayingInstructions: +			_gameState = kWaitingForPlayer; +			_countdown.startCountdown(); +			break; +		case kSiloDeactivated: +			_gameState = kRobotTaunting; + +			switch (_currentSiloIndex) { +			case 3: +				_owner->requestSpotSound(kYouCannotPossiblyIn, kYouCannotPossiblyOut, +						kFilterNoInput, kSpotSoundCompletedFlag); +				break; +			case 5: +				_owner->requestSpotSound(kYouWillFailIn, kYouWillFailOut, kFilterNoInput, +						kSpotSoundCompletedFlag); +				break; +			case 7: +				_owner->requestSpotSound(kGiveUpHumanIn, kGiveUpHumanOut, kFilterNoInput, +						kSpotSoundCompletedFlag); +				break; +			case 9: +				_owner->requestSpotSound(kYouAreRunningIn, kYouAreRunningOut, +						kFilterNoInput, kSpotSoundCompletedFlag); +				break; +			default: +				_owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, +						kFilterNoInput, kSpotSoundCompletedFlag); +				_monitorMovie.setTime(kNewLaunchSiloTime * scale); +				_monitorMovie.redrawMovieWorld(); +				_gameState = kPlayingNewSilo1; +				break; +			} +			break; +		case kRobotTaunting: +			_owner->requestDelay(1, 1, kFilterNoInput, 0); +			_owner->requestSpotSound(kNewLaunchSiloIn, kNewLaunchSiloOut, kFilterNoInput, kSpotSoundCompletedFlag); +			_monitorMovie.setTime(kNewLaunchSiloTime * scale); +			_monitorMovie.redrawMovieWorld(); +			_gameState = kPlayingNewSilo1; +			break; +		case kDelayingPlayer: +			_gameState = kWaitingForPlayer; +			break; +		case kPlayerLost1: +			_owner->recallToTSAFailure(); +			break; +		case kPlayerWon2: +			((NoradDelta *)_owner)->finishedGlobeGame(); +			_owner->requestDeleteCurrentInteraction(); +			break; +		default: +			break; +		} +	} else if (notification == &_globeNotification) { +		ExtraTable::Entry entry; + +		switch (flags) { +		case kGlobeSplash1Finished: +			_owner->getExtraEntry(kN79BrightView, entry); +			_monitorMovie.stop(); +			_monitorMovie.setSegment(kSplash1End * scale, kSplash2End * scale); +			_monitorMovie.setFlags(kLoopTimeBase); +			_monitorMovie.start(); +			_owner->showViewFrame(entry.movieStart); +			_owner->requestSpotSound(kIJustBrokeIn, kIJustBrokeOut, kFilterNoInput, 0); +			_owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag); +			_gameState = kPlayingRobotIntro; +			break; +		case kGlobeTimerExpired: +			// Missile launched, player loses. +			_owner->requestSpotSound(kMissileLaunchedIn, kMissileLaunchedOut, kFilterNoInput, kSpotSoundCompletedFlag); +			_gameState = kPlayerLost1; +			break; +		case kMaxDeactivatedFinished: +			_monitorMovie.stop(); +			_monitorMovie.setSegment(0, _monitorMovie.getDuration()); +			_owner->requestDelay(1, 2, kFilterNoInput, 0); +			_owner->requestSpotSound(kTheOnlyGoodHumanIn, kTheOnlyGoodHumanOut, kFilterNoInput, 0); +			_owner->requestDelay(1, 2, kFilterNoInput, kDelayCompletedFlag); +			_gameState = kPlayerWon2; +			break; +		default: +			break; +		} +	} +} + +// Prevent the player from getting up until the game is over. + +void GlobeGame::handleInput(const Input &input, const Hotspot *cursorSpot) { +	Common::Point where; +	input.getInputLocation(where); +	Hotspot *spot = g_allHotspots.findHotspot(where); + +	if (((PegasusEngine *)g_engine)->_cursor->isVisible() && spot != 0 && +			spot->getObjectID() == kNorad79SiloAreaSpotID && findClickedSilo(input) != -1) { +		_targetHighlightUpperLeft.show(); +		_targetHighlightUpperRight.show(); +		_targetHighlightLowerLeft.show(); +		_targetHighlightLowerRight.show(); +	} else { +		_targetHighlightUpperLeft.hide(); +		_targetHighlightUpperRight.hide(); +		_targetHighlightLowerLeft.hide(); +		_targetHighlightLowerRight.hide(); +	} + +	// Interrupt certain inputs to prevent player from switching modes. +	InputHandler::handleInput(input, cursorSpot); +} + +int16 GlobeGame::findClickedSilo(const Input &input) { +	Common::Point screenPoint; +	input.getInputLocation(screenPoint); +	screenPoint.x -= kNavAreaLeft; +	screenPoint.y -= kNavAreaTop; + +	Line3D ray; +	screenPointTo3DPoint(screenPoint.x, screenPoint.y, ray.pt2); +	ray.pt1 = kCameraLocation; + +	Point3D globePoint; +	if (lineHitsGlobe(ray, globePoint)) { +		int16 latOrigin, longOrigin, latitude, longitude; +		globeMovieFrameToOrigin(_globeMovie.getTime() / kTimePerGlobeFrame, latOrigin, longOrigin); +		globePointToLatLong(globePoint, latOrigin, longOrigin, latitude, longitude); + +		for (int16 i = 0; i < kNumAllSilos; i++) +			if (_siloCoords[i][0] >= latitude - kLatError && _siloCoords[i][0] <= latitude + kLatError && +					_siloCoords[i][1] >= longitude - kLongError && _siloCoords[i][1] <= longitude + kLongError) +				return i; +	} + +	return -1; +} + +void GlobeGame::spinGlobe(const Input &input, const Hotspot *spot, GlobeTrackDirection trackDirection) { +	_globeTracker.setTrackParameters(spot, trackDirection); +	_globeTracker.startTracking(input); +} + +void GlobeGame::clickGlobe(const Input &input) {	 +	int16 newSilo = findClickedSilo(input); + +	if (newSilo != -1) { +		_targetHighlightUpperLeft.hide(); +		_targetHighlightUpperRight.hide(); +		_targetHighlightLowerLeft.hide(); +		_targetHighlightLowerRight.hide(); +		_lowerNamesMovie.show(); +		_lowerNamesMovie.setTime(newSilo * _lowerNamesMovie.getScale()); +		_lowerNamesMovie.redrawMovieWorld(); +		_owner->requestSpotSound(kSiloBeepIn, kSiloBeepOut, kFilterNoInput, 0); + +		if (newSilo == _targetSilo[_currentSiloIndex]) { +			_currentSiloIndex++; +			_countdown.stopCountdown(); +			_owner->requestSpotSound(kSiloDeactivatedIn, kSiloDeactivatedOut, kFilterNoInput, 0); + +			if (_currentSiloIndex == kNumTargetSilos) { +				// Player won. +				_owner->requestDelay(1, 2, kFilterNoInput, 0); +				_upperNamesMovie.hide(); +				_lowerNamesMovie.hide(); +				_countdown.hide(); +				_monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(), +						kMaxDeactivatedStop * _monitorMovie.getScale()); +				_monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale()); +				_monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished); +				_monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +				_monitorMovie.start(); +				_owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut, +						kFilterNoInput, kSpotSoundCompletedFlag); +				_gameState = kPlayerWon1; +			} else { +				_owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag); +				_upperNamesMovie.hide(); +				_lowerNamesMovie.hide(); +				_countdown.hide(); +				_monitorMovie.setTime(kSiloDeactivatedTime * _monitorMovie.getScale()); +				_monitorMovie.redrawMovieWorld(); +				_gameState = kSiloDeactivated; +			} +		} else { +			_owner->requestDelay(5, 1, kFilterNoInput, kDelayCompletedFlag); +			_gameState = kDelayingPlayer; +			// Play "incorrect" sound? +		} +	} +} + +void GlobeGame::clickInHotspot(const Input &input, const Hotspot *spot) { +	switch (spot->getObjectID()) { +	case kNorad79SpinLeftSpotID: +		spinGlobe(input, spot, kTrackLeft); +		break; +	case kNorad79SpinRightSpotID: +		spinGlobe(input, spot, kTrackRight); +		break; +	case kNorad79SpinUpSpotID: +		spinGlobe(input, spot, kTrackUp); +		break; +	case kNorad79SpinDownSpotID: +		spinGlobe(input, spot, kTrackDown); +		break; +	case kNorad79SiloAreaSpotID: +		clickGlobe(input); +		break; +	default: +		GameInteraction::clickInHotspot(input, spot); +		break; +	} +} + +void GlobeGame::activateHotspots() { +	GameInteraction::activateHotspots(); + +	switch (_gameState) { +	case kWaitingForPlayer: +		g_allHotspots.deactivateOneHotspot(kNorad79WestOutSpotID); +		g_allHotspots.activateOneHotspot(kNorad79SpinLeftSpotID); +		g_allHotspots.activateOneHotspot(kNorad79SpinRightSpotID); +		g_allHotspots.activateOneHotspot(kNorad79SpinUpSpotID); +		g_allHotspots.activateOneHotspot(kNorad79SpinDownSpotID); +		g_allHotspots.activateOneHotspot(kNorad79SiloAreaSpotID); +		break; +	default: +		g_allHotspots.deactivateOneHotspot(kNorad79WestOutSpotID); +		break; +	} +} + +void GlobeGame::globeMovieFrameToOrigin(int16 frameNum, int16 &latOrigin, int16 &longOrigin) { +	latOrigin = kDegreesPerLatSlice * 2 - (frameNum / (kNumLongSlices * 2)) * kDegreesPerLatSlice; +	frameNum %= kNumLongSlices * 2; + +	if (frameNum >= kNumLongSlices) +		longOrigin = kLongOrigin + (kNumLongSlices * 2 - 1 - frameNum) * kDegreesPerLongSlice; +	else +		longOrigin = kLongOrigin + frameNum * kDegreesPerLongSlice; + +	if (longOrigin > 180) +		longOrigin -= 360; +} + +void GlobeGame::globePointToLatLong(const GlobeGame::Point3D &pt, int16 latOrigin, int16 longOrigin, +		int16 &latitude, int16 &longitude) {	 +	Point3D scratch = pt; +	 +	// Translate globe center to origin. +	scratch.x -= kGlobeCenter.x; +	scratch.y -= kGlobeCenter.y; +	scratch.z -= kGlobeCenter.z; + +	// Rotate around z axis latOrigin degrees to bring equator parallel with XZ plane +	float theta = degreesToRadians(latOrigin); +	float s = sin(theta); +	float c = cos(theta); +	float x = scratch.x * c - scratch.y * s; +	float y = scratch.y * c + scratch.x * s; +	scratch.x = x; +	scratch.y = y; +	 +	// Calculate latitude +	latitude = (int16)radiansToDegrees(asin(scratch.y / kGlobeRadius)); + +	// Rotate around y axis longOrigin degrees to bring longitude 0 to positive X axis +	theta = degreesToRadians(longOrigin); +	s = sin(theta); +	c = cos(theta); +	x = scratch.x * c - scratch.z * s; +	float z = scratch.z * c + scratch.x * s; +	scratch.x = x; +	scratch.z = z; +	 +	// Calculate longitude +	longitude = (int16)radiansToDegrees(acos(scratch.x / sqrt(scratch.x * scratch.x + scratch.z * scratch.z))); + +	if (scratch.z < 0) +		longitude = -longitude; +} + +// h, v in [0, 511][0, 255] +// Looking down negative x axis. +void GlobeGame::screenPointTo3DPoint(int16 h, int16 v, GlobeGame::Point3D &pt) { +	pt.x = kCameraLocation.x - kPicturePlaneDistance; +	pt.y = kCameraLocation.y + (128 - v) * kPicturePlaneDistance * kTanFieldOfView / 256; +	pt.z = kCameraLocation.z + (h - 256) * kPicturePlaneDistance * kTanFieldOfView / 256; +} + +// Fundamentals of Three-Dimensional Graphics, by Alan Watt +// pp. 163-164 +bool GlobeGame::lineHitsGlobe(const GlobeGame::Line3D &line, GlobeGame::Point3D &pt) {	 +	float i = line.pt2.x - line.pt1.x; +	float j = line.pt2.y - line.pt1.y; +	float k = line.pt2.z - line.pt1.z; +	float a = i * i + j * j + k * k; +	float b = 2 * i * (line.pt1.x - kGlobeCenter.x) + 2 * j * (line.pt1.y - kGlobeCenter.y) + +			2 * k * (line.pt1.z - kGlobeCenter.z); +	float c = kGlobeCenter.x * kGlobeCenter.x + kGlobeCenter.y * kGlobeCenter.y + +			kGlobeCenter.z * kGlobeCenter.z + line.pt1.x * line.pt1.x + line.pt1.y * line.pt1.y + +			line.pt1.z * line.pt1.z + -2 * (kGlobeCenter.x * line.pt1.x + kGlobeCenter.y * line.pt1.y + +			kGlobeCenter.z * line.pt1.z) - kGlobeRadius * kGlobeRadius; + +	// Solve quadratic equation of a, b, c. +	float t = b * b - 4 * a * c; + +	if (t >= 0.0f) { +		// Return smaller root, which corresponds to closest intersection point. +		t = (-b - sqrt(t)) / (2 * a); +		pt.x = i * t + line.pt1.x; +		pt.y = j * t + line.pt1.y; +		pt.z = k * t + line.pt1.z; +		return true; +	} + +	return false; +} + +bool GlobeGame::canSolve() { +	return	_gameState != kPlayerWon1 && _gameState != kPlayerWon2 && _gameState != kPlayerLost1; +} + +void GlobeGame::doSolve() { +	_owner->requestDelay(1, 2, kFilterNoInput, 0); +	_upperNamesMovie.hide(); +	_lowerNamesMovie.hide(); +	_countdown.hide(); +	_monitorMovie.setSegment(kMaxDeactivatedStart * _monitorMovie.getScale(), kMaxDeactivatedStop * _monitorMovie.getScale()); +	_monitorMovie.setTime(kMaxDeactivatedStart * _monitorMovie.getScale()); +	_monitorCallBack.setCallBackFlag(kMaxDeactivatedFinished); +	_monitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	_monitorMovie.start(); +	_owner->requestSpotSound(kMaximumDeactivationIn, kMaximumDeactivationOut, kFilterNoInput, kSpotSoundCompletedFlag); +	_gameState = kPlayerWon1; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/delta/globegame.h b/engines/pegasus/neighborhood/norad/delta/globegame.h new file mode 100644 index 0000000000..9c31a931fc --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/globegame.h @@ -0,0 +1,169 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_DELTA_GLOBEGAME_H +#define PEGASUS_NEIGHBORHOOD_NORAD_DELTA_GLOBEGAME_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +enum GlobeTrackDirection { +	kTrackLeft, +	kTrackRight, +	kTrackUp, +	kTrackDown +}; + +// This class assumes that the globe movie is built at 15 frames per second with a +// time scale of 600, yielding 40 time unit per frame. + +class GlobeTracker : public Tracker { +public: +	GlobeTracker(Movie *, Picture *, Picture *, Picture *, Picture *); +	virtual ~GlobeTracker() {} + +	void setTrackParameters(const Hotspot *, GlobeTrackDirection); +	void continueTracking(const Input &); +	void startTracking(const Input &); +	void stopTracking(const Input &); +	void activateHotspots(); +	bool stopTrackingInput(const Input &); + +protected: +	void trackGlobeMovie(); +	void stopGlobeMovie(); + +	Movie *_globeMovie; +	Picture *_leftHighlight; +	Picture *_rightHighlight; +	Picture *_upHighlight; +	Picture *_downHighlight; +	const Hotspot *_trackSpot; +	int _trackTime; +	GlobeTrackDirection _trackDirection; +}; + +class GlobeCountdown : public IdlerAnimation { +public: +	GlobeCountdown(const DisplayElementID); +	virtual ~GlobeCountdown() {} + +	void setCountdownTime(const int); +	void startCountdown(); +	void stopCountdown(); + +	void setDisplayOrder(const DisplayOrder); +	void show(); +	void hide(); +	void moveElementTo(const CoordType, const CoordType); + +	void draw(const Common::Rect &); + +protected: +	Surface _digits; +	int16 _digitOffset; +}; + +static const int16 kNumAllSilos = 40; +static const int16 kNumTargetSilos = 10; +static const int16 kNumLongSlices = 72; + +class GlobeGame : public GameInteraction, public NotificationReceiver { +public: +	GlobeGame(Neighborhood *); +	virtual ~GlobeGame() {} + +	void handleInput(const Input &, const Hotspot *); +	void clickInHotspot(const Input &, const Hotspot *); +	void activateHotspots(); +	 +	bool canSolve(); +	void doSolve(); + +	struct Point3D { +		float x, y, z; +	}; + +	struct Line3D { +		Point3D	pt1, pt2; +	}; + +protected: +	// Latitude (-90 - 90) and longitude (-180 - 180) +	static const int16 _siloCoords[kNumAllSilos][2]; + +	static const int16 _targetSilo[kNumTargetSilos]; +	static const int16 _timeLimit[kNumTargetSilos]; +	static const TimeValue _siloName[kNumTargetSilos][2]; + +	void openInteraction(); +	void initInteraction(); +	void closeInteraction(); + +	void receiveNotification(Notification *, const NotificationFlags); + +	void spinGlobe(const Input &, const Hotspot *, GlobeTrackDirection); +	void clickGlobe(const Input &); + +	int16 findClickedSilo(const Input &); + +	void globeMovieFrameToOrigin(int16, int16 &, int16 &); +	void globePointToLatLong(const Point3D &, int16, int16, int16 &, int16 &); +	void screenPointTo3DPoint(int16, int16, Point3D &); +	bool lineHitsGlobe(const Line3D &, Point3D &); + +	Movie _monitorMovie; +	Movie _globeMovie; +	Movie _upperNamesMovie; +	Movie _lowerNamesMovie; +	Notification _globeNotification; +	NotificationCallBack _monitorCallBack; +	GlobeTracker _globeTracker; +	Picture _globeCircleLeft; +	Picture _globeCircleRight; +	Picture _globeCircleUp; +	Picture _globeCircleDown; +	Picture _motionHighlightLeft; +	Picture _motionHighlightRight; +	Picture _motionHighlightUp; +	Picture _motionHighlightDown; +	Picture _targetHighlightUpperLeft; +	Picture _targetHighlightUpperRight; +	Picture _targetHighlightLowerLeft; +	Picture _targetHighlightLowerRight; +	GlobeCountdown _countdown; +	NotificationCallBack _countdownCallBack; +	int16 _gameState; +	int16 _currentSiloIndex; +	Notification *_neighborhoodNotification; +	bool _playedInstructions; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp new file mode 100644 index 0000000000..2b208aab71 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.cpp @@ -0,0 +1,869 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/retscanchip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/delta/globegame.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +const uint32 NoradDelta::_noradDeltaClawExtras[22] = { +	kN60ClawFromAToB, +	kN60ClawALoop, +	kN60ClawAPinch, +	kN60ClawACounterclockwise, +	kN60ClawAClockwise, +	kN60ClawFromBToA, +	kN60ClawFromBToC, +	kN60ClawFromBToD, +	kN60ClawBLoop, +	kN60ClawBPinch, +	kN60ClawBCounterclockwise, +	kN60ClawBClockwise, +	kN60ClawFromCToB, +	kN60ClawCLoop, +	kN60ClawCPinch, +	kN60ClawCCounterclockwise, +	kN60ClawCClockwise, +	kN60ClawFromDToB, +	kN60ClawDLoop, +	kN60ClawDPinch, +	kN60ClawDCounterclockwise, +	kN60ClawDClockwise +}; +	 +NoradDelta::NoradDelta(InputHandler *nextHandler, PegasusEngine *owner) : Norad(nextHandler, owner, "Norad Delta", kNoradDeltaID) {	 +	_elevatorUpRoomID = kNorad49South; +	_elevatorDownRoomID = kNorad48South; +	_elevatorUpSpotID = kNorad48ElevatorUpSpotID; +	_elevatorDownSpotID = kNorad49ElevatorDownSpotID; + +	// Pressure door stuff. + +	_subRoomEntryRoom1 = kNorad50; +	_subRoomEntryDir1 = kEast; +	_subRoomEntryRoom2 = kNorad59; +	_subRoomEntryDir2 = kWest; +	_upperPressureDoorRoom = kNorad50East; +	_lowerPressureDoorRoom = kNorad59West; + +	_upperPressureDoorUpSpotID = kDeltaUpperPressureDoorUpSpotID; +	_upperPressureDoorDownSpotID = kDeltaUpperPressureDoorDownSpotID; +	_upperPressureDoorAbortSpotID = kNorad50DoorOutSpotID; + +	_lowerPressureDoorUpSpotID = kDeltaLowerPressureDoorUpSpotID; +	_lowerPressureDoorDownSpotID = kDeltaLowerPressureDoorDownSpotID; +	_lowerPressureDoorAbortSpotID = kNorad59WestOutSpotID; + +	_pressureSoundIn = kPressureDoorIntro1In; +	_pressureSoundOut = kPressureDoorIntro1Out; +	_equalizeSoundIn = kPressureDoorIntro2In; +	_equalizeSoundOut = kPressureDoorIntro2Out; +	_accessDeniedIn = kDeltaAccessDeniedIn; +	_accessDeniedOut = kDeltaAccessDeniedOut; + +	GameState.setNoradSubPrepState(kSubDamaged); + +	_subControlRoom = kNorad60West; +} + +void NoradDelta::init() {	 +	Norad::init(); + +	// Little fix for the retinal scan zoom in spot... +	Hotspot *hotspot = g_allHotspots.findHotspotByID(kNorad68WestSpotID); +	hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag); +	 +	hotspot = g_allHotspots.findHotspotByID(kNorad79WestSpotID); +	hotspot->setMaskedHotspotFlags(kZoomInSpotFlag, kZoomInSpotFlag | kZoomOutSpotFlag); +	 +	hotspot = g_allHotspots.findHotspotByID(kDelta59RobotShieldBiochipSpotID); +	hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); +	HotspotInfoTable::Entry *hotspotEntry = findHotspotEntry(kDelta59RobotShieldBiochipSpotID); +	hotspotEntry->hotspotItem = kShieldBiochip; +	 +	hotspot = g_allHotspots.findHotspotByID(kDelta59RobotOpMemBiochipSpotID); +	hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); +	hotspotEntry = findHotspotEntry(kDelta59RobotOpMemBiochipSpotID); +	hotspotEntry->hotspotItem = kOpticalBiochip; +	 +	hotspot = g_allHotspots.findHotspotByID(kDelta59RobotRetinalBiochipSpotID); +	hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); +	hotspotEntry = findHotspotEntry(kDelta59RobotRetinalBiochipSpotID); +	hotspotEntry->hotspotItem = kRetinalScanBiochip; +	 +	hotspot = g_allHotspots.findHotspotByID(kDelta60RobotShieldBiochipSpotID); +	hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); +	hotspotEntry = findHotspotEntry(kDelta60RobotShieldBiochipSpotID); +	hotspotEntry->hotspotItem = kShieldBiochip; +	 +	hotspot = g_allHotspots.findHotspotByID(kDelta60RobotOpMemBiochipSpotID); +	hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); +	hotspotEntry = findHotspotEntry(kDelta60RobotOpMemBiochipSpotID); +	hotspotEntry->hotspotItem = kOpticalBiochip; +	 +	hotspot = g_allHotspots.findHotspotByID(kDelta60RobotRetinalBiochipSpotID); +	hotspot->setMaskedHotspotFlags(kPickUpBiochipSpotFlag, kPickUpBiochipSpotFlag); +	hotspotEntry = findHotspotEntry(kDelta60RobotRetinalBiochipSpotID); +	hotspotEntry->hotspotItem = kRetinalScanBiochip; +} + +void NoradDelta::start() { +	if (g_energyMonitor) { +		g_energyMonitor->stopEnergyDraining(); +		g_energyMonitor->restoreLastEnergyValue(); +		_vm->resetEnergyDeathReason(); +		g_energyMonitor->startEnergyDraining(); +	} + +	Norad::start(); +} + +void NoradDelta::setUpAIRules() {	 +	Neighborhood::setUpAIRules(); + +	if (g_AIArea) { +		AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Norad/XN07NE", false); +		AILocationCondition *locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kNorad68, kWest)); +		AIRule *rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); +	} +} + +void NoradDelta::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { +	switch (entry.extra) { +	case kArriveFromSubChase: +		compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 20, entry.movieEnd, 90); +		compassMove.insertFaderKnot(entry.movieStart + 25 * kNoradDeltaFrameDuration, 20); +		compassMove.insertFaderKnot(entry.movieStart + 94 * kNoradDeltaFrameDuration, 45); +		compassMove.insertFaderKnot(entry.movieStart + 101 * kNoradDeltaFrameDuration, 45); +		compassMove.insertFaderKnot(entry.movieStart + 146 * kNoradDeltaFrameDuration, 90 + 15); +		compassMove.insertFaderKnot(entry.movieStart + 189 * kNoradDeltaFrameDuration, 90 + 15); +		compassMove.insertFaderKnot(entry.movieStart + 204 * kNoradDeltaFrameDuration, 90 + 30); +		compassMove.insertFaderKnot(entry.movieStart + 214 * kNoradDeltaFrameDuration, 90 + 20); +		compassMove.insertFaderKnot(entry.movieStart + 222 * kNoradDeltaFrameDuration, 90 + 20); +		compassMove.insertFaderKnot(entry.movieStart + 228 * kNoradDeltaFrameDuration, 90 + 10); +		compassMove.insertFaderKnot(entry.movieStart + 245 * kNoradDeltaFrameDuration, 90 + 85); +		compassMove.insertFaderKnot(entry.movieStart + 262 * kNoradDeltaFrameDuration, 90 + 70); +		compassMove.insertFaderKnot(entry.movieStart + 273 * kNoradDeltaFrameDuration, 90 + 80); +		compassMove.insertFaderKnot(entry.movieStart + 287 * kNoradDeltaFrameDuration, 90); +		break; +	case kN60PlayerFollowsRobotToDoor: +		compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 270 + kSubControlCompassAngle, +				entry.movieEnd, 270 - 15); +		compassMove.insertFaderKnot(entry.movieStart + 280, 270 + kSubControlCompassAngle); +		compassMove.insertFaderKnot(entry.movieStart + 920, 360); +		compassMove.insertFaderKnot(entry.movieStart + 1840, 360); +		compassMove.insertFaderKnot(entry.movieStart + 2520, 270); +		compassMove.insertFaderKnot(entry.movieStart + 3760, 270); +		compassMove.insertFaderKnot(entry.movieStart + 4640, 270 + kSubControlCompassAngle); +		break; +	case kN59PlayerWins2: +		compassMove.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, entry.movieStart, 270, entry.movieEnd, 280); +		compassMove.insertFaderKnot(entry.movieEnd - 1000, 270); +	default: +		Norad::getExtraCompassMove(entry, compassMove); +		break; +	} +} + +GameInteraction *NoradDelta::makeInteraction(const InteractionID interactionID) { +	if (interactionID == kNoradGlobeGameInteractionID) +		return new GlobeGame(this); + +	return Norad::makeInteraction(interactionID); +} + +void NoradDelta::playClawMonitorIntro() { +	playSpotSoundSync(kLoadClawIntroIn, kLoadClawIntroOut); +} + +void NoradDelta::getExitEntry(const RoomID room, const DirectionConstant direction, ExitTable::Entry &entry) { +	Norad::getExitEntry(room, direction, entry); + +	if (room == kNorad61 && direction == kSouth) +		entry.movieStart += kNoradDeltaFrameDuration; +} + +void NoradDelta::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) { +	Norad::getZoomEntry(id, zoomEntry); + +	if (id == kNorad59WestSpotID && GameState.getNoradPlayedGlobeGame()) { +		ExtraTable::Entry extraEntry; +		getExtraEntry(kN59ZoomWithRobot, extraEntry); +		zoomEntry.movieStart = extraEntry.movieStart; +		zoomEntry.movieEnd = extraEntry.movieEnd; +	} +} + +void NoradDelta::loadAmbientLoops() {	 +/* +	Logic: +	 +	loop sound 1: +		if room == kNorad79West +			if player globe game +				play kNoradGlobeLoop2SoundNum +			else +				play kNoradRedAlertLoopSoundNum +		else if room >= kNorad78 && room <= kNorad79 +			play kNoradGlobeLoop2SoundNum +		else if gassed, +			if room >= kNorad41 && room <= kNorad49South +				play kNoradNewSubLoopSoundNum, kNoradWarningVolume +			else if room >= kNorad59 && room <= kNorad60West +				play kNoradSubControlLoopSoundNum, kNoradWarningVolume +			else +				play kNoradWarningLoopSoundNum, kNoradWarningVolume +		else +			play nothing +	loop sound 2: +		if gassed and not wearing air mask +			if room == kNorad54North +				play breathing unmanned loop +			else +				play breathing +		else +			if room == kNorad54North +				play unmanned loop +			else +				play nothing +*/ +	 +	if (GameState.getNoradArrivedFromSub()) { +		RoomID room = GameState.getCurrentRoom(); + +		if (room == kNorad79West) { +			if (_privateFlags.getFlag(kNoradPrivateFinishedGlobeGameFlag)) +				loadLoopSound1("Sounds/Norad/GlobAmb2.22K.AIFF"); +			else +				loadLoopSound1("Sounds/Norad/RedAlert.22K.AIFF"); +		} else if (room >= kNorad78 && room <= kNorad79) { +			// clone2727 says: This looks like it should be loadLoopSound1... +			loadLoopSound2("Sounds/Norad/RedAlert.22K.AIFF"); +		} else if (GameState.getNoradGassed()) { +			if (room >= kNorad41 && room <= kNorad49South) +				loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", kNoradWarningVolume * 3); +			else if (room >= kNorad59 && room <= kNorad60West) +				loadLoopSound1("Sounds/Norad/SUB CONTRL LOOP.22K.AIFF", kNoradWarningVolume * 3); +			else +				loadLoopSound1("Sounds/Norad/WARNING LOOP.22K.AIFF", kNoradWarningVolume); +		} else { +			loadLoopSound1(""); +		} + +		if (GameState.getNoradGassed() && !g_airMask->isAirFilterOn()) { +			if (room == kNorad54North) +				loadLoopSound2("Sounds/Norad/Breathing Typing.22K.AIFF", 0x100 / 2); +			else +				loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", kNoradSuckWindVolume, 0, 0); +		} else { +			if (room == kNorad54North) +				loadLoopSound2("Sounds/Norad/N54NAS.22K.AIFF", 0x100 / 2); +			else +				loadLoopSound2(""); +		} +	} else { +		// Start them off at zero... +		if (GameState.getNoradGassed()) +			loadLoopSound1("Sounds/Norad/NEW SUB AMB.22K.AIFF", 0, 0, 0); +		if (!g_airMask->isAirFilterOn()) +			loadLoopSound2("Sounds/Norad/SUCKING WIND.22K.AIFF", 0, 0, 0); +	} +} + +void NoradDelta::checkContinuePoint(const RoomID room, const DirectionConstant direction) { +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kNorad41, kEast): +	case MakeRoomView(kNorad49, kEast): +	case MakeRoomView(kNorad49, kWest): +	case MakeRoomView(kNorad61, kSouth): +	case MakeRoomView(kNorad68, kEast): +	case MakeRoomView(kNorad79, kWest): +		makeContinuePoint(); +		break; +	} +} + +void NoradDelta::arriveAt(const RoomID room, const DirectionConstant direction) { +	if (room != kNorad68) +		GameState.setNoradRetScanGood(false); + +	Norad::arriveAt(room, direction); + +	FaderMoveSpec loop1Spec, loop2Spec; +	ExtraTable::Entry entry; + +	switch (room) { +	case kNorad41: +		if (direction == kEast && !GameState.getNoradArrivedFromSub()) { +			GameState.setNoradPlayedGlobeGame(false); + +			GameState.setNoradBeatRobotWithClaw(false); +			GameState.setNoradBeatRobotWithDoor(false); +			GameState.setNoradRetScanGood(false); + +			GameState.setScoringExitedSub(true); + +			getExtraEntry(kArriveFromSubChase, entry); + +			loop1Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd - +					entry.movieStart, kNoradWarningVolume); +			loop1Spec.insertFaderKnot(7320, 0); +			loop1Spec.insertFaderKnot(7880, kNoradWarningVolume); + +			loop2Spec.makeTwoKnotFaderSpec(kNoradDeltaMovieScale, 0, 0, entry.movieEnd - +					entry.movieStart, kNoradSuckWindVolume); +			loop1Spec.insertFaderKnot(7320, 0); +			loop1Spec.insertFaderKnot(7880, kNoradSuckWindVolume); + +			startExtraSequence(kArriveFromSubChase, kExtraCompletedFlag, kFilterNoInput); + +			startLoop1Fader(loop1Spec); +			startLoop2Fader(loop2Spec); +		} +		break; +	case kNorad54North: +		GameState.setScoringSawRobotAt54North(true); +		break; +	case kNorad68: +		if (GameState.getNoradRetScanGood()) +			openDoor(); +		break; +	case kNorad68West: +		arriveAtNorad68West(); +		break; +	case kNorad79West: +		arriveAtNorad79West(); +		break; +	default: +		break; +	} +} + +void NoradDelta::doorOpened() { +	Norad::doorOpened(); +	GameState.setNoradRetScanGood(false); +} + +void NoradDelta::arriveAtNorad68West() { +	playSpotSoundSync(kHoldForRetinalIn, kHoldForRetinalOut); + +	BiochipItem *retScan = _vm->getCurrentBiochip(); + +	if (retScan != 0 && retScan->getObjectID() == kRetinalScanBiochip) { +		((RetScanChip *)retScan)->searchForLaser(); +		succeedRetinalScan(); +	} else { +		failRetinalScan(); +	} +} + +void NoradDelta::arriveAtNorad79West() {	 +	if (!GameState.getNoradPlayedGlobeGame()) +		newInteraction(kNoradGlobeGameInteractionID); +} + +void NoradDelta::bumpIntoWall() { +	requestSpotSound(kDeltaBumpIntoWallIn, kDeltaBumpIntoWallOut, kFilterNoInput, 0); +	Neighborhood::bumpIntoWall(); +} + +void NoradDelta::failRetinalScan() { +	startExtraSequence(kNoradDeltaRetinalScanBad, kExtraCompletedFlag, kFilterNoInput); +} + +void NoradDelta::succeedRetinalScan() { +	startExtraSequence(kNoradDeltaRetinalScanGood, kExtraCompletedFlag, kFilterNoInput); +	GameState.setNoradRetScanGood(true); +	GameState.setScoringUsedRetinalChip(true); +} + +void NoradDelta::getDoorEntry(const RoomID room, const DirectionConstant direction, DoorTable::Entry &entry) { +	Norad::getDoorEntry(room, direction, entry); + +	if (room == kNorad68 && direction == kWest && !GameState.getNoradRetScanGood()) +		entry.flags = kDoorPresentMask | kDoorLockedMask; +} + +void NoradDelta::finishedGlobeGame() { +	GameState.setNoradPlayedGlobeGame(true); +	_privateFlags.setFlag(kNoradPrivateFinishedGlobeGameFlag, true); +	GameState.setScoringFinishedGlobeGame(true); +	loadAmbientLoops(); +	g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN60WD1", false, kWarningInterruption); +} + +bool NoradDelta::playingAgainstRobot() { +	return GameState.getNoradPlayedGlobeGame(); +} + +void NoradDelta::getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID, +		HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, +		HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &clawPosition, const uint32 *&clawExtraIDs) { +	outSpotID = kNorad60MonitorOutSpotID; +	prepSpotID = kNorad60LaunchPrepSpotID; +	clawControlSpotID = kNorad60ClawControlSpotID; +	pinchClawSpotID = kNorad60ClawPinchSpotID; +	moveClawDownSpotID = kNorad60ClawDownSpotID; +	moveClawRightSpotID = kNorad60ClawRightSpotID; +	moveClawLeftSpotID = kNorad60ClawLeftSpotID; +	moveClawUpSpotID = kNorad60ClawUpSpotID; +	clawCCWSpotID = kNorad60ClawCCWSpotID; +	clawCWSpotID = kNorad60ClawCWSpotID; +	clawPosition = kClawAtC; +	clawExtraIDs = _noradDeltaClawExtras; +} + +void NoradDelta::playerBeatRobotWithDoor() { +	GameState.setNoradBeatRobotWithDoor(true); +	updateViewFrame(); +	GameState.setScoringStoppedNoradRobot(true); +	g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption); +} + +void NoradDelta::playerBeatRobotWithClaw() { +	GameState.setNoradBeatRobotWithClaw(true); +	updateViewFrame(); +	GameState.setScoringStoppedNoradRobot(true); +	GameState.setScoringNoradGandhi(true); +	g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/Norad/XN59WD", false, kWarningInterruption); +} + +TimeValue NoradDelta::getViewTime(const RoomID room, const DirectionConstant direction) { +	ExtraTable::Entry entry; +	 +	if (room == kNorad41 && direction == kSouth && !GameState.getNoradArrivedFromSub()) { +		getExtraEntry(kArriveFromSubChase, entry); +		return entry.movieStart; +	} + +	if (GameState.getNoradBeatRobotWithDoor()) { +		if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { +			uint32 extraID = kN59Biochips111; +			if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) +				extraID += 1; +			if (_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) +				extraID += 2; +			if (_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) +				extraID += 4; +			getExtraEntry(extraID, entry); +			return entry.movieStart; +		} + +		getExtraEntry(kN59RobotHeadOpens, entry); +		return entry.movieStart; +	} else if (GameState.getNoradBeatRobotWithClaw()) { +		if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { +			uint32 extraID = kN60Biochips111; +			if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) +				extraID += 1; +			if (_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) +				extraID += 2; +			if (_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) +				extraID += 4; +			getExtraEntry(extraID, entry); +			return entry.movieStart; +		} + +		getExtraEntry(kN60RobotHeadOpens, entry); +		return entry.movieStart; +	} + +	return Norad::getViewTime(room, direction); +} + +void NoradDelta::openDoor() {	 +	if (GameState.getCurrentRoom() == kNorad59 && GameState.getCurrentDirection() == kWest && GameState.getNoradPlayedGlobeGame()) { +		Input scratch; +		InputHandler::_inputHandler->clickInHotspot(scratch, g_allHotspots.findHotspotByID(kNorad59WestSpotID)); +	} else { +		Norad::openDoor(); +	} +} + +void NoradDelta::activateHotspots() { +	Norad::activateHotspots(); + +	if (GameState.getCurrentRoom() == kNorad59West && GameState.getCurrentDirection() == kWest && GameState.getNoradBeatRobotWithDoor()) { +		g_allHotspots.deactivateOneHotspot(kNorad59WestOutSpotID); + +		if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { +			if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) +				g_allHotspots.activateOneHotspot(kDelta59RobotShieldBiochipSpotID); +			else +				g_allHotspots.deactivateOneHotspot(kDelta59RobotShieldBiochipSpotID); + +			if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) +				g_allHotspots.activateOneHotspot(kDelta59RobotOpMemBiochipSpotID); +			else +				g_allHotspots.deactivateOneHotspot(kDelta59RobotOpMemBiochipSpotID); + +			if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) +				g_allHotspots.activateOneHotspot(kDelta59RobotRetinalBiochipSpotID); +			else +				g_allHotspots.deactivateOneHotspot(kDelta59RobotRetinalBiochipSpotID); +		} else +			g_allHotspots.activateOneHotspot(kDelta59RobotHeadSpotID); +	} else if (GameState.getCurrentRoom() == kNorad60West && GameState.getCurrentDirection() == kWest && +			GameState.getNoradBeatRobotWithClaw()) { +		g_allHotspots.deactivateOneHotspot(kNorad60MonitorOutSpotID); + +		if (_privateFlags.getFlag(kNoradPrivateRobotHeadOpenFlag)) { +			if (!_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag)) +				g_allHotspots.activateOneHotspot(kDelta60RobotShieldBiochipSpotID); +			else +				g_allHotspots.deactivateOneHotspot(kDelta60RobotShieldBiochipSpotID); + +			if (!_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) +				g_allHotspots.activateOneHotspot(kDelta60RobotOpMemBiochipSpotID); +			else +				g_allHotspots.deactivateOneHotspot(kDelta60RobotOpMemBiochipSpotID); + +			if (!_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag)) +				g_allHotspots.activateOneHotspot(kDelta60RobotRetinalBiochipSpotID); +			else +				g_allHotspots.deactivateOneHotspot(kDelta60RobotRetinalBiochipSpotID); +		} else { +			g_allHotspots.activateOneHotspot(kDelta60RobotHeadSpotID); +		} +	} else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad50, kEast)) { +		if (GameState.isCurrentDoorOpen()) +			g_allHotspots.deactivateOneHotspot(kNorad50DoorSpotID); +	} else if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad59, kWest)) { +		if (GameState.isCurrentDoorOpen()) +			g_allHotspots.deactivateOneHotspot(kNorad59WestSpotID); +	} +} + +void NoradDelta::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { +	switch (clickedSpot->getObjectID()) { +	case kDelta59RobotHeadSpotID: +		startExtraSequence(kN59RobotHeadOpens, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kDelta60RobotHeadSpotID: +		startExtraSequence(kN60RobotHeadOpens, kExtraCompletedFlag, kFilterNoInput); +		break; +	default: +		Norad::clickInHotspot(input, clickedSpot); +		break; +	} +} + +void NoradDelta::receiveNotification(Notification *notification, const NotificationFlags flags) {	 +	Norad::receiveNotification(notification, flags); + +	if ((flags & kExtraCompletedFlag) != 0) { +		RetScanChip *retScan; +		Input dummy; + +		switch (_lastExtra) { +		case kArriveFromSubChase: +			GameState.setNoradArrivedFromSub(true); +			GameState.setCurrentRoom(kNoRoomID); +			GameState.setCurrentDirection(kNoDirection); +			arriveAt(kNorad41, kEast); +			break; +		case kN59RobotHeadOpens: +		case kN60RobotHeadOpens: +			_privateFlags.setFlag(kNoradPrivateRobotHeadOpenFlag, true); +			break; +		case kNoradDeltaRetinalScanBad: +			retScan = (RetScanChip *)_vm->getCurrentBiochip(); +			retScan->setItemState(kNormalItem); +			playSpotSoundSync(kRetinalScanFailedIn, kRetinalScanFailedOut); +			downButton(dummy); +			break; +		case kNoradDeltaRetinalScanGood: +			retScan = (RetScanChip *)_vm->getCurrentBiochip(); +			retScan->setItemState(kNormalItem); +			downButton(dummy); +			break; +		case kN59RobotDisappears: +		case kN60RobotDisappears: +			recallToTSASuccess(); +			break; +		} + +		_interruptionFilter = kFilterAllInput; +	} + +	g_AIArea->checkMiddleArea(); +} + +void NoradDelta::pickedUpItem(Item *item) { +	switch (item->getObjectID()) { +	case kShieldBiochip: +		if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) && +					_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) && +					_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) { +			GameState.setNoradFinished(true); + +			if (GameState.getCurrentRoom() == kNorad59West) +				startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kRetinalScanBiochip: +		if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) && +				_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) && +				_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) { +			GameState.setNoradFinished(true); + +			if (GameState.getCurrentRoom() == kNorad59West) +				startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kOpticalBiochip: +		g_opticalChip->addPoseidon(); +		GameState.setScoringGotNoradOpMemChip(); + +		if (_privateFlags.getFlag(kNoradPrivateGotShieldChipFlag) && +				_privateFlags.getFlag(kNoradPrivateGotRetScanChipFlag) && +				_privateFlags.getFlag(kNoradPrivateGotOpticalChipFlag)) { +			GameState.setNoradFinished(true); + +			if (GameState.getCurrentRoom() == kNorad59West) +				startExtraSequence(kN59RobotDisappears, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kN60RobotDisappears, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	} + +	Norad::pickedUpItem(item); +} + +void NoradDelta::takeItemFromRoom(Item *item) { +	switch (item->getObjectID()) { +	case kShieldBiochip: +		_privateFlags.setFlag(kNoradPrivateGotShieldChipFlag, true); +		break; +	case kRetinalScanBiochip: +		_privateFlags.setFlag(kNoradPrivateGotRetScanChipFlag, true); +		break; +	case kOpticalBiochip: +		_privateFlags.setFlag(kNoradPrivateGotOpticalChipFlag, true); +		break; +	} + +	Norad::takeItemFromRoom(item); +} + +void NoradDelta::dropItemIntoRoom(Item *item, Hotspot *hotspot) { +	switch (item->getObjectID()) { +	case kShieldBiochip: +		_privateFlags.setFlag(kNoradPrivateGotShieldChipFlag, false); +		break; +	case kOpticalBiochip: +		_privateFlags.setFlag(kNoradPrivateGotOpticalChipFlag, false); +		break; +	case kRetinalScanBiochip: +		_privateFlags.setFlag(kNoradPrivateGotRetScanChipFlag, false); +		break; +	} + +	Norad::dropItemIntoRoom(item, hotspot); +} + +Hotspot *NoradDelta::getItemScreenSpot(Item *item, DisplayElement *element) { +	HotSpotID id = kNoHotSpotID; +	 +	switch (item->getObjectID()) { +	case kShieldBiochip: +		if (GameState.getNoradBeatRobotWithDoor()) +			id = kDelta59RobotShieldBiochipSpotID; +		else +			id = kDelta60RobotShieldBiochipSpotID; +		break; +	case kOpticalBiochip: +		if (GameState.getNoradBeatRobotWithDoor()) +			id = kDelta59RobotOpMemBiochipSpotID; +		else +			id = kDelta60RobotOpMemBiochipSpotID; +		break; +	case kRetinalScanBiochip: +		if (GameState.getNoradBeatRobotWithDoor()) +			id = kDelta59RobotRetinalBiochipSpotID; +		else +			id = kDelta60RobotRetinalBiochipSpotID; +		break; +	} + +	if (id != kNoHotSpotID) +		return g_allHotspots.findHotspotByID(id); + +	return Norad::getItemScreenSpot(item, element); +} + +Common::String NoradDelta::getEnvScanMovie() { +	return "Images/AI/Norad/XNE2"; +} + +uint NoradDelta::getNumHints() {	 +	uint numHints = Neighborhood::getNumHints(); + +	if (numHints == 0) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kNorad60, kWest): +			if (GameState.getNoradPlayedGlobeGame()) +				numHints = 2; +			else +				numHints = 1; +			break; +		case MakeRoomView(kNorad59, kNorth): +		case MakeRoomView(kNorad59, kSouth): +		case MakeRoomView(kNorad59, kEast): +		case MakeRoomView(kNorad59, kWest): +		case MakeRoomView(kNorad60, kNorth): +		case MakeRoomView(kNorad60, kSouth): +		case MakeRoomView(kNorad60, kEast): +			if (GameState.getNoradPlayedGlobeGame()) +				numHints = 2; +			break; +		case MakeRoomView(kNorad68, kWest): +			if (_vm->playerHasItemID(kRetinalScanBiochip)) { +				BiochipItem *retScan = _vm->getCurrentBiochip(); +				if (retScan == 0 || retScan->getObjectID() != kRetinalScanBiochip) +					numHints = 2; +			} else if (!GameState.isCurrentDoorOpen()) { +				numHints = 2; +			} +			break; +		} +	} + +	return numHints; +} + +Common::String NoradDelta::getHintMovie(uint hintNum) { +	Common::String movieName = Neighborhood::getHintMovie(hintNum); + +	if (movieName.empty()) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kNorad60, kWest): +			if (GameState.getNoradPlayedGlobeGame()) { +				if (hintNum == 1) +					return "Images/AI/Norad/XN60WD2"; + +				return "Images/AI/Norad/XN60WD3"; +			} + +			return "Images/AI/Globals/XGLOB1C"; +		case MakeRoomView(kNorad59, kNorth): +		case MakeRoomView(kNorad59, kSouth): +		case MakeRoomView(kNorad59, kEast): +		case MakeRoomView(kNorad59, kWest): +		case MakeRoomView(kNorad60, kNorth): +		case MakeRoomView(kNorad60, kSouth): +		case MakeRoomView(kNorad60, kEast): +			if (hintNum == 1) +				return "Images/AI/Norad/XN60WD2"; + +			return "Images/AI/Norad/XN60WD3"; +		case MakeRoomView(kNorad68, kWest): +			if (_vm->playerHasItemID(kRetinalScanBiochip)) { +				if (hintNum == 1) +					return "Images/AI/Globals/XGLOB1A"; + +				return "Images/AI/Globals/XGLOB1C"; +			} + +			if (hintNum == 1) +				return "Images/AI/Globals/XGLOB1B"; + +			return "Images/AI/Globals/XGLOB3B"; +		} +	} + +	return movieName; +} + +void NoradDelta::closeDoorOffScreen(const RoomID room, const DirectionConstant) { +	switch (room) { +	case kNorad47: +	case kNorad48: +	case kNorad41: +	case kNorad42: +		playSpotSoundSync(kDeltaElevatorDoorCloseIn, kDeltaElevatorDoorCloseOut); +		break; +	default: +		playSpotSoundSync(kDeltaRegDoorCloseIn, kDeltaRegDoorCloseOut); +		break; +	} +} + +bool NoradDelta::canSolve() { +	if (Norad::canSolve()) +		return true; + +	if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) { +		BiochipItem *biochip = _vm->getCurrentBiochip(); +		if (biochip != 0 && biochip->getObjectID() != kRetinalScanBiochip) +			return true; +	} + +	return false; +} + +void NoradDelta::doSolve() { +	Norad::doSolve(); + +	if (GameState.getCurrentRoomAndView() == MakeRoomView(kNorad68, kWest)) { +		if (!_vm->playerHasItemID(kRetinalScanBiochip)) +			_vm->addItemToBiochips((BiochipItem *)g_allItems.findItemByID(kRetinalScanBiochip)); + +		BiochipItem *biochip = _vm->getCurrentBiochip(); +		if (biochip != 0 && biochip->getObjectID() != kRetinalScanBiochip && g_interface) +			g_interface->setCurrentBiochipID(kRetinalScanBiochip); + +		Hotspot *spot = g_allHotspots.findHotspotByID(kNorad68WestSpotID); +		Input scratch; +		InputHandler::_inputHandler->clickInHotspot(scratch, spot); +	} +} + +Common::String NoradDelta::getSoundSpotsName() { +	return "Sounds/Norad/Norad Delta Spots"; +} + +Common::String NoradDelta::getNavMovieName() { +	return "Images/Norad Delta/Norad Delta.movie"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/delta/noraddelta.h b/engines/pegasus/neighborhood/norad/delta/noraddelta.h new file mode 100644 index 0000000000..11065f2c9d --- /dev/null +++ b/engines/pegasus/neighborhood/norad/delta/noraddelta.h @@ -0,0 +1,117 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_DELTA_NORADDELTA_H +#define PEGASUS_NEIGHBORHOOD_NORAD_DELTA_NORADDELTA_H + +#include "pegasus/neighborhood/norad/norad.h" + +namespace Pegasus { + +class NoradDelta : public Norad { +public: +	NoradDelta(InputHandler *, PegasusEngine *); +	virtual ~NoradDelta() {} + +	void init(); + +	void start(); + +	void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); + +	void finishedGlobeGame(); + +	virtual GameInteraction *makeInteraction(const InteractionID); + +	void playClawMonitorIntro(); + +	virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, HotSpotID &clawControlSpotID, +			HotSpotID &pinchClawSpotID, HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, +			HotSpotID &moveClawLeftSpotID, HotSpotID &moveClawUpSpotID, HotSpotID &clawCCWSpotID, +			HotSpotID &clawCWSpotID, uint32 &, const uint32 *&); + +	void playerBeatRobotWithClaw(); +	void playerBeatRobotWithDoor(); + +	void loadAmbientLoops(); + +	void setUpAIRules(); +	Common::String getEnvScanMovie(); +	uint getNumHints(); +	Common::String getHintMovie(uint); +	void closeDoorOffScreen(const RoomID, const DirectionConstant); + +	void checkContinuePoint(const RoomID, const DirectionConstant); + +	bool canSolve(); +	void doSolve(); + +	void doorOpened(); + +protected: +	enum { +		kNoradPrivateArrivedFromSubFlag, +		kNoradPrivateFinishedGlobeGameFlag, +		kNoradPrivateRobotHeadOpenFlag, +		kNoradPrivateGotShieldChipFlag, +		kNoradPrivateGotOpticalChipFlag, +		kNoradPrivateGotRetScanChipFlag, +		kNumNoradPrivateFlags +	}; + +	static const uint32 _noradDeltaClawExtras[22]; + +	void getExitEntry(const RoomID, const DirectionConstant, ExitTable::Entry &); +	void getZoomEntry(const HotSpotID, ZoomTable::Entry &); +	virtual void arriveAt(const RoomID, const DirectionConstant); +	void arriveAtNorad68West(); +	void arriveAtNorad79West(); +	TimeValue getViewTime(const RoomID, const DirectionConstant); +	void openDoor(); +	void activateHotspots(); +	void clickInHotspot(const Input &, const Hotspot *); +	void receiveNotification(Notification *, const NotificationFlags); +	void pickedUpItem(Item *item); +	void takeItemFromRoom(Item *item); +	void dropItemIntoRoom(Item *item, Hotspot *); +	Hotspot *getItemScreenSpot(Item *, DisplayElement *); + +	virtual bool playingAgainstRobot(); + +	void failRetinalScan(); +	void succeedRetinalScan(); +	void getDoorEntry(const RoomID, const DirectionConstant, DoorTable::Entry &); + +	void bumpIntoWall(); + +	FlagsArray<byte, kNumNoradPrivateFlags> _privateFlags; + +	Common::String getSoundSpotsName(); +	Common::String getNavMovieName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/norad.cpp b/engines/pegasus/neighborhood/norad/norad.cpp new file mode 100644 index 0000000000..9ee8205ec1 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/norad.cpp @@ -0,0 +1,285 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/noradelevator.h" +#include "pegasus/neighborhood/norad/pressuredoor.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/subplatform.h" + +namespace Pegasus { + +const NotificationFlags kDoneWithPressureDoorNotification = 1; + +const NotificationFlags kNoradNotificationFlags = kDoneWithPressureDoorNotification; + +// This class handles everything that Norad Alpha and Delta have in common, such as +// oxygen mask usage, the elevator and the pressure doors. + +Norad::Norad(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, NeighborhoodID id) : +		Neighborhood(nextHandler, vm, resName, id), _noradNotification(kNoradNotificationID, vm) { +	_elevatorUpSpotID = kNoHotSpotID; +	_elevatorDownSpotID = kNoHotSpotID; +	_elevatorUpRoomID = kNoHotSpotID; +	_elevatorDownRoomID = kNoHotSpotID; + +	_subRoomEntryRoom1 = kNoRoomID; +	_subRoomEntryDir1 = kNoDirection; +	_subRoomEntryRoom2 = kNoRoomID; +	_subRoomEntryDir2 = kNoDirection; +	_upperPressureDoorRoom = kNoRoomID; +	_lowerPressureDoorRoom = kNoRoomID; + +	_upperPressureDoorUpSpotID = kNoHotSpotID; +	_upperPressureDoorDownSpotID = kNoHotSpotID; +	_upperPressureDoorAbortSpotID = kNoHotSpotID; + +	_lowerPressureDoorUpSpotID = kNoHotSpotID; +	_lowerPressureDoorDownSpotID = kNoHotSpotID; +	_lowerPressureDoorAbortSpotID = kNoHotSpotID; + +	_pressureSoundIn = 0xffffffff; +	_pressureSoundOut = 0xffffffff; +	_equalizeSoundIn = 0xffffffff; +	_equalizeSoundOut = 0xffffffff; +	_accessDeniedIn = 0xffffffff; +	_accessDeniedOut = 0xffffffff; + +	_platformRoom = kNoRoomID; +	_subControlRoom = kNoRoomID; + +	_doneWithPressureDoor = false; + +	_noradNotification.notifyMe(this, kNoradNotificationFlags, kNoradNotificationFlags); +} + +GameInteraction *Norad::makeInteraction(const InteractionID interactionID) { +	PressureDoor *pressureDoor; +	SubControlRoom *subControl; + +	switch (interactionID) { +	case kNoradElevatorInteractionID: +		return new NoradElevator(this, _elevatorUpRoomID, _elevatorDownRoomID, _elevatorUpSpotID, _elevatorDownSpotID); +	case kNoradPressureDoorInteractionID: +		if (GameState.getCurrentRoom() == _upperPressureDoorRoom) +			pressureDoor = new PressureDoor(this, true, _upperPressureDoorUpSpotID, _upperPressureDoorDownSpotID, +					_upperPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut); +		else +			pressureDoor = new PressureDoor(this, false, _lowerPressureDoorUpSpotID, _lowerPressureDoorDownSpotID, +					_lowerPressureDoorAbortSpotID, _pressureSoundIn, _pressureSoundOut, _equalizeSoundIn, _equalizeSoundOut); + +		if (GameState.getCurrentRoom() == kNorad59West && playingAgainstRobot()) +			pressureDoor->playAgainstRobot(); + +		return pressureDoor; +	case kNoradSubControlRoomInteractionID: +		subControl = new SubControlRoom(this); + +		if (GameState.getCurrentRoom() == kNorad60West && playingAgainstRobot()) +			subControl->playAgainstRobot(); + +		return subControl; +	case kNoradSubPlatformInteractionID: +		return new SubPlatform(this); +	default: +		return 0; +	} +} + +void Norad::flushGameState() { +	g_energyMonitor->saveCurrentEnergyValue(); +} + +void Norad::start() { +	setUpAirMask(); +	Neighborhood::start(); +} + +void Norad::activateHotspots() {	 +	Neighborhood::activateHotspots(); + +	RoomID room = GameState.getCurrentRoom(); +	if (room == _elevatorUpRoomID) +		_neighborhoodHotspots.activateOneHotspot(_elevatorDownSpotID); +	else if (room == _elevatorDownRoomID) +		_neighborhoodHotspots.activateOneHotspot(_elevatorUpSpotID); +} + +void Norad::arriveAt(const RoomID room, const DirectionConstant direction) { +	Neighborhood::arriveAt(room, direction); + +	if (GameState.getCurrentRoom() == _elevatorUpRoomID || GameState.getCurrentRoom() == _elevatorDownRoomID) +		arriveAtNoradElevator(); +	else if (GameState.getCurrentRoom() == _upperPressureDoorRoom) +		arriveAtUpperPressureDoorRoom(); +	else if (GameState.getCurrentRoom() == _lowerPressureDoorRoom) +		arriveAtLowerPressureDoorRoom(); +	else if (GameState.getCurrentRoom() == _platformRoom) +		arriveAtSubPlatformRoom(); +	else if (GameState.getCurrentRoom() == _subControlRoom) +		arriveAtSubControlRoom(); + +	if (_doneWithPressureDoor) { +		_doneWithPressureDoor = false; +		openDoor(); +	} +} + +void Norad::arriveAtNoradElevator() { +	if (_currentInteraction) +		_currentInteraction->startOverInteraction(); +	else +		newInteraction(kNoradElevatorInteractionID); +} + +void Norad::arriveAtUpperPressureDoorRoom() { +	newInteraction(kNoradPressureDoorInteractionID); +} + +void Norad::arriveAtLowerPressureDoorRoom() { +	newInteraction(kNoradPressureDoorInteractionID); +} + +void Norad::arriveAtSubPlatformRoom() { +	newInteraction(kNoradSubPlatformInteractionID); +} + +void Norad::arriveAtSubControlRoom() {  +	newInteraction(kNoradSubControlRoomInteractionID); +} + +int16 Norad::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { +	int16 result = Neighborhood::getStaticCompassAngle(room, dir); + +	if (room == _elevatorUpRoomID || room == _elevatorDownRoomID) +		result += kElevatorCompassAngle; +	else if (room == _platformRoom) +		result += kSubPlatformCompassAngle; +	else if (room == _subControlRoom) +		result += kSubControlCompassAngle; + +	return result; +} + +CanOpenDoorReason Norad::canOpenDoor(DoorTable::Entry &entry) { +	if (((GameState.getCurrentRoom() == _subRoomEntryRoom1 && GameState.getCurrentDirection() == _subRoomEntryDir1) || +			(GameState.getCurrentRoom() == _subRoomEntryRoom2 && GameState.getCurrentDirection() == _subRoomEntryDir2)) && +			GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure) +		return kCantOpenBadPressure; + +	return Neighborhood::canOpenDoor(entry); +} + +void Norad::cantOpenDoor(CanOpenDoorReason reason) { +	if (reason == kCantOpenBadPressure) +		playSpotSoundSync(_pressureSoundIn, _pressureSoundOut); +	else +		playSpotSoundSync(_accessDeniedIn, _accessDeniedOut); +} + +void Norad::startExitMovie(const ExitTable::Entry &exitEntry) { +	if (GameState.getCurrentRoom() == _elevatorUpRoomID) { +		if (exitEntry.exitRoom != _elevatorDownRoomID) +			newInteraction(kNoInteractionID); +	} else if (GameState.getCurrentRoom() == _elevatorDownRoomID) { +		if (exitEntry.exitRoom != _elevatorUpRoomID) +			newInteraction(kNoInteractionID); +	} else { +		newInteraction(kNoInteractionID); +	} + +	Neighborhood::startExitMovie(exitEntry); +} + +void Norad::startZoomMovie(const ZoomTable::Entry &zoomEntry) { +	newInteraction(kNoInteractionID); +	Neighborhood::startZoomMovie(zoomEntry); +} + +void Norad::upButton(const Input &input) { +	if (GameState.getCurrentRoom() != _elevatorUpRoomID && GameState.getCurrentRoom() != _elevatorDownRoomID) +		Neighborhood::upButton(input); +} + +void Norad::setUpAirMask() {	 +	_airMaskCallBack.setNotification(&_neighborhoodNotification); +	_airMaskCallBack.initCallBack(&_airMaskTimer, kCallBackAtExtremes); +	_airMaskCallBack.setCallBackFlag(kAirTimerExpiredFlag); +	_neighborhoodNotification.notifyMe(this, kAirTimerExpiredFlag, kAirTimerExpiredFlag); +	_airMaskCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	_airMaskTimer.setScale(1); +	_airMaskTimer.setSegment(0, kNoradAirMaskTimeLimit); +	checkAirMask(); +} + +void Norad::checkAirMask() { +	if (g_airMask && g_airMask->isAirFilterOn()) { +		_airMaskTimer.stop(); +	} else if (GameState.getNoradGassed() && !_airMaskTimer.isRunning()) { +		_airMaskTimer.setTime(0); +		_airMaskTimer.start(); +	} + +	loadAmbientLoops(); +} + +void Norad::receiveNotification(Notification *notification, const NotificationFlags flags) {	 +	if (notification == &_neighborhoodNotification && (flags & kAirTimerExpiredFlag) != 0) +		((PegasusEngine *)g_engine)->die(kDeathGassedInNorad); + +	Neighborhood::receiveNotification(notification, flags); + +	if (notification == &_noradNotification) { +		// Must be kDoneWithPressureDoorNotification... +		Input scratch; +		_doneWithPressureDoor = true; +		downButton(scratch); +	} +} + +uint16 Norad::getDateResID() const { +	return kDate2112ID; +} + +Common::String Norad::getBriefingMovie() { +	return "Images/AI/Norad/XNO"; +} + +void Norad::pickedUpItem(Item *item) { +	Neighborhood::pickedUpItem(item); +	g_AIArea->checkMiddleArea(); +} + +void Norad::doneWithPressureDoor() { +	_noradNotification.setNotificationFlags(kDoneWithPressureDoorNotification, kDoneWithPressureDoorNotification); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/norad.h b/engines/pegasus/neighborhood/norad/norad.h new file mode 100644 index 0000000000..4723410b4c --- /dev/null +++ b/engines/pegasus/neighborhood/norad/norad.h @@ -0,0 +1,121 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H +#define PEGASUS_NEIGHBORHOOD_NORAD_NORAD_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +// This is the code common to both Norad Alpha and Norad Delta + +class Norad : public Neighborhood { +public: +	Norad(InputHandler *, PegasusEngine *owner, const Common::String &resName, const NeighborhoodID); +	virtual ~Norad() {} + +	void flushGameState(); + +	virtual void start(); + +	virtual void getClawInfo(HotSpotID &outSpotID, HotSpotID &prepSpotID, +			HotSpotID &clawControlSpotID, HotSpotID &pinchClawSpotID, +			HotSpotID &moveClawDownSpotID, HotSpotID &moveClawRightSpotID, +			HotSpotID &moveClawLeftSpotID,HotSpotID &moveClawUpSpotID, +			HotSpotID &clawCCWSpotID, HotSpotID &clawCWSpotID, uint32 &, const uint32 *&) = 0; +	void checkAirMask(); + +	virtual uint16 getDateResID() const; + +	virtual GameInteraction *makeInteraction(const InteractionID); + +	Common::String getBriefingMovie(); + +	void pickedUpItem(Item *); + +	virtual void playClawMonitorIntro() {} + +	void doneWithPressureDoor(); + +protected: +	CanOpenDoorReason canOpenDoor(DoorTable::Entry &); +	void cantOpenDoor(CanOpenDoorReason); +	int16 getStaticCompassAngle(const RoomID, const DirectionConstant); +	virtual void startExitMovie(const ExitTable::Entry &); +	void startZoomMovie(const ZoomTable::Entry &); +	virtual void upButton(const Input &); +	virtual void activateHotspots(); + +	virtual void arriveAt(const RoomID, const DirectionConstant); +	virtual void arriveAtNoradElevator(); +	virtual void arriveAtUpperPressureDoorRoom(); +	virtual void arriveAtLowerPressureDoorRoom(); +	virtual void arriveAtSubPlatformRoom(); +	virtual void arriveAtSubControlRoom(); +	void setUpAirMask(); +	virtual void receiveNotification(Notification *, const NotificationFlags); +	virtual bool playingAgainstRobot() { return false; } + +	Notification _noradNotification; +	bool _doneWithPressureDoor; + +	RoomID _elevatorUpRoomID; +	RoomID _elevatorDownRoomID; +	HotSpotID _elevatorUpSpotID; +	HotSpotID _elevatorDownSpotID; + +	TimeBase _airMaskTimer; +	NotificationCallBack _airMaskCallBack; + +	RoomID _subRoomEntryRoom1; +	DirectionConstant _subRoomEntryDir1; +	RoomID _subRoomEntryRoom2; +	DirectionConstant _subRoomEntryDir2; +	RoomID _upperPressureDoorRoom; +	RoomID _lowerPressureDoorRoom; + +	HotSpotID _upperPressureDoorUpSpotID; +	HotSpotID _upperPressureDoorDownSpotID; +	HotSpotID _upperPressureDoorAbortSpotID; +	 +	HotSpotID _lowerPressureDoorUpSpotID; +	HotSpotID _lowerPressureDoorDownSpotID; +	HotSpotID _lowerPressureDoorAbortSpotID; +	 +	TimeValue _pressureSoundIn; +	TimeValue _pressureSoundOut; +	TimeValue _equalizeSoundIn; +	TimeValue _equalizeSoundOut; +	TimeValue _accessDeniedIn; +	TimeValue _accessDeniedOut; +	 +	RoomID	 _platformRoom; +	RoomID _subControlRoom; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/noradelevator.cpp b/engines/pegasus/neighborhood/norad/noradelevator.cpp new file mode 100644 index 0000000000..4279e236ae --- /dev/null +++ b/engines/pegasus/neighborhood/norad/noradelevator.cpp @@ -0,0 +1,130 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/noradelevator.h" + +namespace Pegasus { + +// Norad elevator PICTs: +static const ResIDType kElevatorLabelID = 200; +static const ResIDType kElevatorButtonsID = 201; +static const ResIDType kElevatorDownOnID = 202; +static const ResIDType kElevatorUpOnID = 203; + +NoradElevator::NoradElevator(Neighborhood *handler, const RoomID upRoom, const RoomID downRoom, +		const HotSpotID upHotspot, const HotSpotID downHotspot) : GameInteraction(kNoradElevatorInteractionID, handler), +		_elevatorControls(kNoradElevatorControlsID), _elevatorNotification(kNoradElevatorNotificationID, ((PegasusEngine *)g_engine)) { +	_timerExpired = false; +	_upRoom = upRoom; +	_downRoom = downRoom; +	_upHotspot = upHotspot; +	_downHotspot = downHotspot; +} + +void NoradElevator::openInteraction() {	 +	SpriteFrame *frame = new SpriteFrame(); +	frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorLabelID, true); +	_elevatorControls.addFrame(frame, 0, 0); +	 +	frame = new SpriteFrame(); +	frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorButtonsID, true); +	_elevatorControls.addFrame(frame, 0, 0); +	 +	frame = new SpriteFrame(); +	frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorDownOnID, true); +	_elevatorControls.addFrame(frame, 0, 0); +	 +	frame = new SpriteFrame(); +	frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kElevatorUpOnID, true); +	_elevatorControls.addFrame(frame, 0, 0); +	 +	_elevatorControls.setCurrentFrameIndex(0); +	_elevatorControls.setDisplayOrder(kElevatorControlsOrder); + +	Common::Rect r; +	frame->getSurfaceBounds(r); +	r.moveTo(kNoradAlphaElevatorControlsLeft, kNoradAlphaElevatorControlsTop); + +	_elevatorControls.setBounds(r); +	_elevatorControls.startDisplaying(); +	_elevatorControls.show(); +} + +void NoradElevator::initInteraction() {	 +	_elevatorTimer.setScale(2); +	_elevatorTimer.setSegment(0, 1); +	_elevatorCallBack.initCallBack(&_elevatorTimer, kCallBackAtExtremes); +	_elevatorCallBack.setCallBackFlag(1); +	_elevatorCallBack.setNotification(&_elevatorNotification); +	_elevatorNotification.notifyMe(this, 1, 1); +	_elevatorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	_elevatorTimer.start(); +} + +void NoradElevator::closeInteraction() { +	_elevatorControls.stopDisplaying(); +	_elevatorControls.discardFrames(); +	_elevatorCallBack.releaseCallBack(); +} + +void NoradElevator::resetInteraction() { +	_elevatorControls.setCurrentFrameIndex(1); +} + +void NoradElevator::activateHotspots() { +	GameInteraction::activateHotspots(); + +	if (_timerExpired) { +		if (GameState.getCurrentRoom() == _upRoom) +			g_allHotspots.activateOneHotspot(_downHotspot); +		else if (GameState.getCurrentRoom() == _downRoom) +			g_allHotspots.activateOneHotspot(_upHotspot); +	} +} + +void NoradElevator::clickInHotspot(const Input &input, const Hotspot *spot) { +	HotSpotID id = spot->getObjectID(); + +	if (id == _upHotspot || id == _downHotspot) { +		g_neighborhood->moveForward(); +		if (id == _downHotspot) +			_elevatorControls.setCurrentFrameIndex(2); +		else +			_elevatorControls.setCurrentFrameIndex(3); +	} else { +		GameInteraction::clickInHotspot(input, spot); +	} +} + +void NoradElevator::receiveNotification(Notification *, const NotificationFlags) { +	_elevatorControls.setCurrentFrameIndex(1); +	_timerExpired = true; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/noradelevator.h b/engines/pegasus/neighborhood/norad/noradelevator.h new file mode 100644 index 0000000000..a34c77b2e0 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/noradelevator.h @@ -0,0 +1,67 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H +#define PEGASUS_NEIGHBORHOOD_NORAD_NORADELEVATOR_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" +#include "pegasus/surface.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class Neighborhood; + +class NoradElevator : public GameInteraction, private NotificationReceiver { +public: +	NoradElevator(Neighborhood *, const RoomID, const RoomID, const HotSpotID, const HotSpotID); +	virtual ~NoradElevator() {} + +protected: +	virtual void openInteraction(); +	virtual void initInteraction(); +	virtual void closeInteraction(); +	virtual void resetInteraction(); +	 +	virtual void activateHotspots(); +	virtual void clickInHotspot(const Input &, const Hotspot *); +	 +	virtual void receiveNotification(Notification *, const NotificationFlags); +	 +	RoomID _upRoom; +	RoomID _downRoom; +	HotSpotID _upHotspot; +	HotSpotID _downHotspot; +	Sprite _elevatorControls; +	TimeBase _elevatorTimer; +	NotificationCallBack _elevatorCallBack; +	Notification _elevatorNotification; +	bool _timerExpired; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.cpp b/engines/pegasus/neighborhood/norad/pressuredoor.cpp new file mode 100644 index 0000000000..520d568b5d --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuredoor.cpp @@ -0,0 +1,554 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/pressuredoor.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +static const TimeValue kLevelsSplashStart = 0; +static const TimeValue kLevelsSplashStop = 1; +static const TimeValue kPressureBase = 1; + +static const TimeValue kDoorSealedTime = 0; +static const TimeValue kEqualizeTime = 1; +static const TimeValue kMaxPressureLoopStart = 2; +static const TimeValue kMaxPressureLoopStop = 3; +static const TimeValue kOpeningDoorLoopStart = 3; +static const TimeValue kOpeningDoorLoopStop = 4; +static const TimeValue kIncreasingPressureTime = 4; +static const TimeValue kDecreasingPressureTime = 5; +static const TimeValue kCautionLoopStart = 6; +static const TimeValue kCautionLoopStop = 7; + +static const NotificationFlags kSplashFinished = 1; +static const NotificationFlags kPressureDroppingFlag = kSplashFinished << 1; + +static const NotificationFlags kPressureNotificationFlags = kSplashFinished | +														kPressureDroppingFlag; + +static const NotificationFlags kDoorJumpsUpFlag = 1; +static const NotificationFlags kDoorJumpsBackFlag = kDoorJumpsUpFlag << 1; +static const NotificationFlags kDoorCrushedFlag = kDoorJumpsBackFlag << 1; + +static const NotificationFlags kUtilityNotificationFlags = kDoorJumpsUpFlag | +														kDoorJumpsBackFlag | +														kDoorCrushedFlag; + +enum { +	kPlayingRobotApproaching, +	kRobotPunching, +	kRobotComingThrough, +	kRobotDying, +	kRobotDead +}; + +const short kMaxPunches = 5; + +enum { +	kPlayingSplash, +	kPlayingPressureMessage, +	kPlayingEqualizeMessage, +	kWaitingForPlayer, +	kPlayingDoneMessage, +	kGameOver +}; + +// Pressure values range from 0 to 11. +static const short kMinPressure = 0; +static const short kMaxPressure = 11; + +static const TimeScale kNavTimeScale = 600; +static const TimeValue kNavFrameRate = 15; +static const TimeValue kNavTimePerFrame = kNavTimeScale / kNavFrameRate; + +static const TimeValue kApproachPunchInTime = 122 * kNavTimePerFrame; +static const TimeValue kLoopPunchInTime = 38 * kNavTimePerFrame; +static const TimeValue kPunchThroughTime = 38 * kNavTimePerFrame; + +// Pressure door PICTs: +static const ResIDType kUpperPressureUpOffPICTID = 400; +static const ResIDType kUpperPressureUpOnPICTID = 401; +static const ResIDType kUpperPressureDownOffPICTID = 402; +static const ResIDType kUpperPressureDownOnPICTID = 403; + +static const ResIDType kLowerPressureUpOffPICTID = 404; +static const ResIDType kLowerPressureUpOnPICTID = 405; +static const ResIDType kLowerPressureDownOffPICTID = 406; +static const ResIDType kLowerPressureDownOnPICTID = 407; + +PressureDoor::PressureDoor(Neighborhood *handler, bool isUpperDoor, const HotSpotID upSpotID, const HotSpotID downSpotID, +		const HotSpotID outSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut, TimeValue equalizeSoundIn, +		TimeValue equalizeSoundOut) : GameInteraction(kNoradPressureDoorInteractionID, handler), +		_levelsMovie(kPressureDoorLevelsID), _typeMovie(kPressureDoorTypeID), _upButton(kPressureDoorUpButtonID), +		_downButton(kPressureDoorDownButtonID), _pressureNotification(kNoradPressureNotificationID, ((PegasusEngine *)g_engine)), +		_doorTracker(this), _utilityNotification(kNoradUtilityNotificationID, ((PegasusEngine *)g_engine)) { +	_neighborhoodNotification = handler->getNeighborhoodNotification(); +	_upHotspotID = upSpotID; +	_downHotspotID = downSpotID; +	_outHotspotID = outSpotID; +	_pressureSoundIn = pressureSoundIn; +	_pressureSoundOut = pressureSoundOut; +	_equalizeSoundIn = equalizeSoundIn; +	_equalizeSoundOut = equalizeSoundOut; +	_playingAgainstRobot = false; +	_isUpperDoor = isUpperDoor; +} + +void PressureDoor::openInteraction() { +	if (_isUpperDoor) { +		_levelsMovie.initFromMovieFile("Images/Norad Alpha/Upper Levels Movie"); +		_levelsMovie.moveElementTo(kNoradUpperLevelsLeft, kNoradUpperLevelsTop); +	} else { +		_levelsMovie.initFromMovieFile("Images/Norad Alpha/Lower Levels Movie"); +		_levelsMovie.moveElementTo(kNoradLowerLevelsLeft, kNoradLowerLevelsTop); +	} + +	_levelsScale = _levelsMovie.getScale(); +	_levelsMovie.setDisplayOrder(kPressureLevelsOrder); +	_levelsMovie.startDisplaying(); +	_levelsMovie.setSegment(kLevelsSplashStart * _levelsScale, kLevelsSplashStop * _levelsScale); +	_levelsMovie.setTime(kLevelsSplashStart * _levelsScale); +	_levelsMovie.redrawMovieWorld(); +	_levelsMovie.show(); +	 +	_pressureCallBack.setNotification(&_pressureNotification); +	_pressureCallBack.initCallBack(&_levelsMovie, kCallBackAtExtremes); +	_pressureCallBack.setCallBackFlag(kSplashFinished); +	_pressureCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + +	_pressureNotification.notifyMe(this, kPressureNotificationFlags, kPressureNotificationFlags); + +	if (_isUpperDoor) { +		_typeMovie.initFromMovieFile("Images/Norad Alpha/Upper Type Movie"); +		_typeMovie.moveElementTo(kNoradUpperTypeLeft, kNoradUpperTypeTop); +	} else { +		_typeMovie.initFromMovieFile("Images/Norad Alpha/Lower Type Movie"); +		_typeMovie.moveElementTo(kNoradLowerTypeLeft, kNoradLowerTypeTop); +	} + +	_typeScale = _typeMovie.getScale(); +	_typeMovie.setDisplayOrder(kPressureTypeOrder); +	_typeMovie.startDisplaying(); +	_typeMovie.setTime(kDoorSealedTime * _typeScale); +	_typeMovie.redrawMovieWorld(); + +	SpriteFrame *frame = new SpriteFrame(); +	if (_isUpperDoor) +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOffPICTID); +	else +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOffPICTID); +	_upButton.addFrame(frame, 0, 0); + +	frame = new SpriteFrame(); +	if (_isUpperDoor) +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOnPICTID); +	else +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOnPICTID); +	_upButton.addFrame(frame, 0, 0); + +	_upButton.setCurrentFrameIndex(0); +	_upButton.setDisplayOrder(kPressureUpOrder); + +	Common::Rect r; +	frame->getSurfaceBounds(r); +	if (_isUpperDoor) +		r.moveTo(kNoradUpperUpLeft, kNoradUpperUpTop); +	else +		r.moveTo(kNoradLowerUpLeft, kNoradLowerUpTop); + +	_upButton.setBounds(r); +	_upButton.startDisplaying(); +	_upButton.show(); + +	frame = new SpriteFrame(); +	if (_isUpperDoor) +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOffPICTID); +	else +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOffPICTID); +	_downButton.addFrame(frame, 0, 0); + +	frame = new SpriteFrame(); +	if (_isUpperDoor) +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOnPICTID); +	else +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOnPICTID); +	_downButton.addFrame(frame, 0, 0); + +	_downButton.setCurrentFrameIndex(0); +	_downButton.setDisplayOrder(kPressureDownOrder); + +	frame->getSurfaceBounds(r); +	if (_isUpperDoor) +		r.moveTo(kNoradUpperDownLeft, kNoradUpperDownTop); +	else +		r.moveTo(kNoradLowerDownLeft, kNoradLowerDownTop); + +	_downButton.setBounds(r); +	_downButton.startDisplaying(); +	_downButton.show(); + +	_utilityCallBack.setNotification(&_utilityNotification); +	_utilityCallBack.initCallBack(&_utilityTimer, kCallBackAtTime); +	_utilityNotification.notifyMe(this, kUtilityNotificationFlags, kUtilityNotificationFlags); +	_utilityTimer.setMasterTimeBase(getOwner()->getNavMovie()); + +	if (_playingAgainstRobot) +		_neighborhoodNotification->notifyMe(this, kExtraCompletedFlag | kDelayCompletedFlag | +				kSpotSoundCompletedFlag, kExtraCompletedFlag | kDelayCompletedFlag | kSpotSoundCompletedFlag); +	else +		_neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag, +				kDelayCompletedFlag | kSpotSoundCompletedFlag); +	 +	_gameState = kPlayingSplash; +} + +void PressureDoor::initInteraction() {	 +	_levelsMovie.start(); + +	if (_playingAgainstRobot) { +		ExtraTable::Entry entry; +		_owner->getExtraEntry(kN59RobotApproaches, entry); +		_utilityTimer.setSegment(entry.movieStart, entry.movieEnd); +		_utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); +		_punchInTime = kApproachPunchInTime + entry.movieStart; +		_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); +		_utilityTimer.setTime(entry.movieStart); +		_owner->startExtraSequence(kN59RobotApproaches, kExtraCompletedFlag, kFilterAllInput); +		_utilityTimer.start(); +		_robotState = kPlayingRobotApproaching; +	} + +	_levelsMovie.redrawMovieWorld(); +} + +void PressureDoor::closeInteraction() {	 +	_pressureNotification.cancelNotification(this); +	_pressureCallBack.releaseCallBack(); +	_utilityNotification.cancelNotification(this); +	_utilityCallBack.releaseCallBack(); +	_neighborhoodNotification->cancelNotification(this); +} + +void PressureDoor::playAgainstRobot() { +	_playingAgainstRobot = true; +} + +void PressureDoor::receiveNotification(Notification *notification, const NotificationFlags flags) { +	Neighborhood *owner = getOwner(); + +	if (notification == _neighborhoodNotification) { +		if (_playingAgainstRobot && (flags & kExtraCompletedFlag) != 0) { +			ExtraTable::Entry entry; + +			switch (_robotState) { +			case kPlayingRobotApproaching: +				_utilityTimer.stop(); +				if (GameState.getNoradSubRoomPressure() == kMaxPressure) { +					owner->getExtraEntry(kN59PlayerWins1, entry); +					_utilityTimer.setSegment(entry.movieStart, entry.movieEnd); +					_utilityTimer.setTime(entry.movieStart); +					_utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); +					_punchInTime = kLoopPunchInTime + entry.movieStart; +					_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); +					owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput); +					_utilityTimer.start(); +					_robotState = kRobotDying; +				} else { +					owner->getExtraEntry(kN59RobotPunchLoop, entry); +					_utilityTimer.setSegment(entry.movieStart, entry.movieEnd); +					_utilityTimer.setTime(entry.movieStart); +					_utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); +					_punchInTime = kLoopPunchInTime + entry.movieStart; +					_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); +					owner->startSpotLoop(entry.movieStart, entry.movieEnd, kExtraCompletedFlag); +					_utilityTimer.start(); +					_robotState = kRobotPunching; +					_punchCount = 1; +				} +				break; +			case kRobotPunching: +				if (GameState.getNoradSubRoomPressure() == kMaxPressure) { +					owner->startExtraSequence(kN59PlayerWins1, kExtraCompletedFlag, kFilterNoInput); +					_robotState = kRobotDying; +				} else if (++_punchCount >= kMaxPunches) { +					_robotState = kRobotComingThrough; +					owner->getExtraEntry(kN59RobotWins, entry); +					_utilityTimer.stop(); +					_utilityTimer.setSegment(entry.movieStart, entry.movieEnd); +					_utilityTimer.setTime(entry.movieStart); +					_utilityCallBack.cancelCallBack(); +					_utilityCallBack.setCallBackFlag(kDoorCrushedFlag); +					_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, kPunchThroughTime + entry.movieStart, kNavTimeScale); +					owner->startExtraSequence(kN59RobotWins, kExtraCompletedFlag, kFilterNoInput); +					_utilityTimer.start(); +				} else { +					_utilityCallBack.setCallBackFlag(kDoorJumpsUpFlag); +					_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime, kNavTimeScale); +					owner->scheduleNavCallBack(kExtraCompletedFlag); +				} +				break; +			case kRobotComingThrough: +				g_system->delayMillis(2 * 1000); +				((PegasusEngine *)g_engine)->die(kDeathRobotThroughNoradDoor); +				break; +			case kRobotDying: +				_robotState = kRobotDead; +				_levelsMovie.stop(); +				_levelsMovie.setSegment((kNormalSubRoomPressure + kPressureBase) * _levelsScale, +						(GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); +				_pressureCallBack.setCallBackFlag(kPressureDroppingFlag); +				_pressureCallBack.scheduleCallBack(kTriggerAtStart, 0, 0); +				_typeMovie.stop(); +				_typeMovie.setSegment(0, _typeMovie.getDuration()); +				_typeMovie.setTime(kDecreasingPressureTime * _typeScale); +				_typeMovie.redrawMovieWorld(); +				_typeMovie.show(); +				_downButton.show(); +				_downButton.setCurrentFrameIndex(1); +				_gameState = kGameOver; +				allowInput(false); +				_levelsMovie.setRate(Common::Rational(0x5555, 0x10000) - 1); // Should match door tracker. +				break; +			case kRobotDead: +				allowInput(true); +				((NoradDelta *)owner)->playerBeatRobotWithDoor(); +				owner->requestDeleteCurrentInteraction(); +				break; +			} +		} + +		if ((flags & (kDelayCompletedFlag | kSpotSoundCompletedFlag)) != 0) { +			switch (_gameState) { +			case kPlayingPressureMessage: +				_typeMovie.setTime(kEqualizeTime * _typeScale); +				_typeMovie.redrawMovieWorld(); +				owner->requestDelay(1, 5, kFilterNoInput, 0); +				owner->requestSpotSound(_equalizeSoundIn, _equalizeSoundOut, kFilterNoInput, 0); +				owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); +				_gameState = kPlayingEqualizeMessage; +				break; +			case kPlayingEqualizeMessage: +				_gameState = kWaitingForPlayer; +				stopChangingPressure(); +				break; +			case kPlayingDoneMessage: +				_gameState = kWaitingForPlayer; +				_typeMovie.stop(); +				_typeMovie.setFlags(0); +				_typeMovie.hide(); +				if (!_playingAgainstRobot) +					((Norad *)_owner)->doneWithPressureDoor(); +				break; +			} +		} +	} else if (notification == &_pressureNotification) { +		switch (flags) { +		case kSplashFinished: +			_levelsMovie.stop(); +			_levelsMovie.setSegment(0, _levelsMovie.getDuration()); +			_levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); +			_levelsMovie.redrawMovieWorld(); + +			if (GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure) { +				_typeMovie.show(); +				owner->requestDelay(1, 5, kFilterNoInput, 0); +				owner->requestSpotSound(_pressureSoundIn, _pressureSoundOut, kFilterNoInput, 0); +				owner->requestDelay(1, 5, kFilterNoInput, kDelayCompletedFlag); +				_gameState = kPlayingPressureMessage; +			} else { +				_gameState = kWaitingForPlayer; +			} +			break; +		case kPressureDroppingFlag: +			_levelsMovie.stop(); +			_levelsMovie.hide(); +			_typeMovie.stop(); +			_typeMovie.hide(); +			_upButton.hide(); +			_downButton.hide(); +			owner->startExtraSequence(kN59PlayerWins2, kExtraCompletedFlag, kFilterNoInput); +			break; +		} +	} else if (notification == &_utilityNotification) { +		switch (flags) { +		case kDoorJumpsUpFlag: +			_utilityCallBack.setCallBackFlag(kDoorJumpsBackFlag); +			_utilityCallBack.scheduleCallBack(kTriggerTimeFwd, _punchInTime + kNavTimePerFrame, kNavTimeScale); +			_levelsMovie.hide(); +			_typePunched = _typeMovie.isVisible(); +			if (_typePunched == true) +				_typeMovie.hide(); +			_upButton.hide(); +			_downButton.hide(); +			break; +		case kDoorJumpsBackFlag: +			_levelsMovie.show(); +			_upButton.show(); +			_downButton.show(); +			if (_typePunched) +				_typeMovie.show(); +			break; +		case kDoorCrushedFlag: +			_levelsMovie.hide(); +			_typeMovie.hide(); +			_upButton.hide(); +			_downButton.hide(); +			break; +		} +	} +} + +void PressureDoor::activateHotspots() { +	GameInteraction::activateHotspots(); + +	switch (_gameState) { +	case kWaitingForPlayer: +		g_allHotspots.activateOneHotspot(_upHotspotID); +		g_allHotspots.activateOneHotspot(_downHotspotID); +		if (!_playingAgainstRobot) +			g_allHotspots.activateOneHotspot(_outHotspotID); +		break; +	default: +		break; +	} +} + +void PressureDoor::clickInHotspot(const Input &input, const Hotspot *spot) { +	HotSpotID id = spot->getObjectID(); + +	if (id == _upHotspotID || id == _downHotspotID) { +		if (id == _upHotspotID) +			_doorTracker.setTrackParameters(spot, &_upButton); +		else +			_doorTracker.setTrackParameters(spot, &_downButton); + +		_doorTracker.startTracking(input); +	} else { +		GameInteraction::clickInHotspot(input, spot); +	} +} + +void PressureDoor::incrementPressure(const HotSpotID id) { +	_typeMovie.stop(); +	_typeMovie.setSegment(0, _typeMovie.getDuration()); +	_typeMovie.setFlags(0); + +	if (id == _upHotspotID) { +		if (GameState.getNoradSubRoomPressure() < kMaxPressure) { +			GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() + 1); +			_levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); +			_levelsMovie.redrawMovieWorld(); +			_typeMovie.setTime(kIncreasingPressureTime * _typeScale); +			_typeMovie.redrawMovieWorld(); +			_typeMovie.show(); +			g_AIArea->checkMiddleArea(); +		} else { +			_typeMovie.hide(); +		} +	} else if (id == _downHotspotID) { +		if (GameState.getNoradSubRoomPressure() > kMinPressure) { +			GameState.setNoradSubRoomPressure(GameState.getNoradSubRoomPressure() - 1); +			_levelsMovie.setTime((GameState.getNoradSubRoomPressure() + kPressureBase) * _levelsScale); +			_levelsMovie.redrawMovieWorld(); +			_typeMovie.setTime(kDecreasingPressureTime * _typeScale); +			_typeMovie.redrawMovieWorld(); +			_typeMovie.show(); +			g_AIArea->checkMiddleArea(); +		} else { +			_typeMovie.hide(); +		} +	} +} + +void PressureDoor::stopChangingPressure() { +	Neighborhood *owner; + +	switch (GameState.getNoradSubRoomPressure()) { +	case 11: +		_typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale); +		_typeMovie.setFlags(kLoopTimeBase); +		_typeMovie.show(); +		_typeMovie.start(); +		break; +	case 10: +		_typeMovie.setSegment(kCautionLoopStart * _typeScale, kCautionLoopStop * _typeScale); +		_typeMovie.setFlags(kLoopTimeBase); +		_typeMovie.show(); +		_typeMovie.start(); +		break; +	case kNormalSubRoomPressure: +		owner = getOwner(); +		_typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale); +		_typeMovie.setFlags(kLoopTimeBase); +		_typeMovie.show(); +		_gameState = kPlayingDoneMessage; +		owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag); +		_typeMovie.start(); +		break; +	default: +		_typeMovie.hide(); +		break; +	} +} + +bool PressureDoor::canSolve() { +	if (_playingAgainstRobot) +		return GameState.getNoradSubRoomPressure() < 11; + +	return GameState.getNoradSubRoomPressure() != kNormalSubRoomPressure; +} + +void PressureDoor::doSolve() {	 +	if (_playingAgainstRobot) { +		GameState.setNoradSubRoomPressure(11); +		_levelsMovie.setTime((11 + kPressureBase) * _levelsScale); +		_levelsMovie.redrawMovieWorld(); +		_typeMovie.setSegment(kMaxPressureLoopStart * _typeScale, kMaxPressureLoopStop * _typeScale); +		_typeMovie.setFlags(kLoopTimeBase); +		_typeMovie.show(); +		_typeMovie.start(); +		g_AIArea->checkMiddleArea(); +	} else { +		GameState.setNoradSubRoomPressure(kNormalSubRoomPressure); +		_levelsMovie.setTime((kNormalSubRoomPressure + kPressureBase) * _levelsScale); +		_levelsMovie.redrawMovieWorld(); +		_typeMovie.setSegment(kOpeningDoorLoopStart * _typeScale, kOpeningDoorLoopStop * _typeScale); +		_typeMovie.setFlags(kLoopTimeBase); +		_typeMovie.show(); +		Neighborhood *owner = getOwner(); +		owner->requestDelay(2, 1, kFilterNoInput, kDelayCompletedFlag); +		_gameState = kPlayingDoneMessage; +		_typeMovie.start(); +		g_AIArea->checkMiddleArea(); +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/pressuredoor.h b/engines/pegasus/neighborhood/norad/pressuredoor.h new file mode 100644 index 0000000000..7ef1518c9f --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuredoor.h @@ -0,0 +1,93 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H +#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSUREDOOR_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/neighborhood/norad/pressuretracker.h" + +namespace Pegasus { + +static const short kNormalSubRoomPressure = 2; + +class PressureDoor : public GameInteraction, public NotificationReceiver { +public: +	PressureDoor(Neighborhood *, bool isUpperDoor, const HotSpotID, const HotSpotID, +			const HotSpotID, TimeValue pressureSoundIn, TimeValue pressureSoundOut, +			TimeValue equalizeSoundIn, TimeValue equalizeSoundOut); +	virtual ~PressureDoor() {} + +	void incrementPressure(const HotSpotID); +	void stopChangingPressure(); + +	void playAgainstRobot(); + +	bool canSolve(); +	void doSolve(); + +protected: +	virtual void openInteraction(); +	virtual void initInteraction(); +	virtual void closeInteraction(); + +	virtual void activateHotspots(); +	virtual void clickInHotspot(const Input &, const Hotspot *); + +	virtual void receiveNotification(Notification *, const NotificationFlags); + +	Movie _levelsMovie; +	TimeScale _levelsScale; +	Movie _typeMovie; +	TimeScale _typeScale; +	Sprite _upButton; +	Sprite _downButton; +	Notification _pressureNotification; +	NotificationCallBack _pressureCallBack; +	Notification *_neighborhoodNotification; +	int _gameState; +	HotSpotID _upHotspotID; +	HotSpotID _downHotspotID; +	HotSpotID _outHotspotID; +	PressureTracker _doorTracker; +	TimeValue _pressureSoundIn; +	TimeValue _pressureSoundOut; +	TimeValue _equalizeSoundIn; +	TimeValue _equalizeSoundOut; +	bool _isUpperDoor; +	 +	bool _playingAgainstRobot, _typePunched; +	int _robotState, _punchCount; +	TimeBase _utilityTimer; +	Notification _utilityNotification; +	NotificationCallBack _utilityCallBack; +	TimeValue _punchInTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/pressuretracker.cpp b/engines/pegasus/neighborhood/norad/pressuretracker.cpp new file mode 100644 index 0000000000..60521a4a98 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuretracker.cpp @@ -0,0 +1,86 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/hotspot.h" +#include "pegasus/neighborhood/norad/pressuredoor.h" +#include "pegasus/neighborhood/norad/pressuretracker.h" + +namespace Pegasus { + +PressureTracker::PressureTracker(PressureDoor *pressureDoor) { +	_pressureDoor = pressureDoor; +	_trackSpot = 0; +	_trackTime = 0; +} + +void PressureTracker::setTrackParameters(const Hotspot *trackSpot, Sprite *trackButton) { +	_trackSpot = trackSpot; +	_trackButton = trackButton; +	_trackTime = 0; +} + +void PressureTracker::activateHotspots() { +	Tracker::activateHotspots(); + +	if (_trackSpot) +		g_allHotspots.activateOneHotspot(_trackSpot->getObjectID()); +} + +// For click-hold dragging. +bool PressureTracker::stopTrackingInput(const Input &input) { +	return !JMPPPInput::isPressingInput(input); +} + +void PressureTracker::continueTracking(const Input &input) { +	Common::Point where; +	input.getInputLocation(where); + +	if (g_allHotspots.findHotspot(where) == _trackSpot) { +		trackPressure(); +		_trackButton->setCurrentFrameIndex(1); +	} else { +		_trackButton->setCurrentFrameIndex(0); +	} +} + +void PressureTracker::startTracking(const Input &input) { +	Tracker::startTracking(input); +	trackPressure(); +} + +void PressureTracker::stopTracking(const Input &input) { +	_trackButton->setCurrentFrameIndex(0); +	_pressureDoor->stopChangingPressure(); +	Tracker::stopTracking(input); +} + +void PressureTracker::trackPressure() { +	if (g_system->getMillis() - _trackTime > kPressureDoorTrackInterval * 1000 / 60) { +		_pressureDoor->incrementPressure(_trackSpot->getObjectID()); +		_trackTime = g_system->getMillis(); +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/pressuretracker.h b/engines/pegasus/neighborhood/norad/pressuretracker.h new file mode 100644 index 0000000000..7d572593d0 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/pressuretracker.h @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H +#define PEGASUS_NEIGHBORHOOD_NORAD_PRESSURETRACKER_H + +#include "pegasus/input.h" + +namespace Pegasus { + +// This class assumes that the globe movie is built at 15 frames per second with a +// time scale of 600, yielding 40 time unit per frame. + +enum PressureTrackDirection { +	kTrackPressureUp, +	kTrackPressureDown +}; + +static const int kPressureDoorTrackInterval = 45; + +class PressureDoor; +class Sprite; + +class PressureTracker : public Tracker { +public: +	PressureTracker(PressureDoor *); +	virtual ~PressureTracker() {} +	 +	void setTrackParameters(const Hotspot *, Sprite *); +	void continueTracking(const Input &); +	void startTracking(const Input &); +	void stopTracking(const Input &); +	void activateHotspots(); +	bool stopTrackingInput(const Input &); + +protected: +	void trackPressure(); + +	PressureDoor *_pressureDoor; +	const Hotspot *_trackSpot; +	Sprite *_trackButton; +	long _trackTime; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.cpp b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp new file mode 100644 index 0000000000..2b15ad4b7d --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subcontrolroom.cpp @@ -0,0 +1,1178 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/subcontrolroom.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" + +namespace Pegasus { + +// Right Monitor times + +static const TimeValue kAlphaClawSplashStart = 0; +static const TimeValue kAlphaClawSplashStop = 4000; + +static const TimeValue kDeltaClawSplashStart = 4000; +static const TimeValue kDeltaClawSplashStop = 8000; + +static const TimeValue kClawAtATime = 8000; +static const TimeValue kClawAtAPinchedTime = 8600; +static const TimeValue kClawAtATurnedTime = 9200; +static const TimeValue kClawAtAWithRobotPinchedTime = 9800; + +static const TimeValue kClawAtBTime = 10400; +static const TimeValue kClawAtBPinchedTime = 11000; +static const TimeValue kClawAtBTurnedTime = 11600; +static const TimeValue kClawAtBWithRobotTime = 12200; +static const TimeValue kClawAtBWithRobotPinchedTime = 12800; + +static const TimeValue kClawAtCTime = 13400; +static const TimeValue kClawAtCPinchedTime = 14000; +static const TimeValue kClawAtCTurnedTime = 14600; + +static const TimeValue kClawAtDTime = 15200; +static const TimeValue kClawAtDPinchedTime = 15800; +static const TimeValue kClawAtDTurnedTime = 16400; + +static const TimeValue kAToBStart = 17000; +static const TimeValue kAToBStop = 18680; +static const TimeValue kAPinchStart = 18680; +static const TimeValue kAPinchStop = 20200; +static const TimeValue kACCWStart = 20200; +static const TimeValue kACCWStop = 21600; +static const TimeValue kACWStart = 21600; +static const TimeValue kACWStop = 23000; + +static const TimeValue kBToAStart = 23000; +static const TimeValue kBToAStop = 24680; +static const TimeValue kBToCStart = 24680; +static const TimeValue kBToCStop = 26520; +static const TimeValue kBToDStart = 26520; +static const TimeValue kBToDStop = 28320; +static const TimeValue kBPinchStart = 28320; +static const TimeValue kBPinchStop = 29680; +static const TimeValue kBCCWStart = 29680; +static const TimeValue kBCCWStop = 31200; +static const TimeValue kBCWStart = 31200; +static const TimeValue kBCWStop = 32720; + +static const TimeValue kCToBStart = 32720; +static const TimeValue kCToBStop = 34560; +static const TimeValue kCPinchStart = 34560; +static const TimeValue kCPinchStop = 36400; +static const TimeValue kCCCWStart = 36400; +static const TimeValue kCCCWStop = 37840; +static const TimeValue kCCWStart = 37840; +static const TimeValue kCCWStop = 39280; + +static const TimeValue kDToBStart = 39280; +static const TimeValue kDToBStop = 41080; +static const TimeValue kDPinchStart = 41080; +static const TimeValue kDPinchStop = 42600; +static const TimeValue kDCCWStart = 42600; +static const TimeValue kDCCWStop = 44000; +static const TimeValue kDCWStart = 44000; +static const TimeValue kDCWStop = 45400; + +static const TimeValue kRobotApproachStart = 45400; +static const TimeValue kRobotApproachStop = 56800; + +static const TimeValue kCToBWithRobotStart = 56800; +static const TimeValue kCToBWithRobotStop = 58600; + +static const TimeValue kBPinchWithRobotStart = 58600; +static const TimeValue kBPinchWithRobotStop = 60400; +static const TimeValue kBToAWithRobotStart = 60400; +static const TimeValue kBToAWithRobotStop = 62240; + +// As usual, times here are in seconds. + +// Left monitor times. + +static const TimeValue kAlphaSplashStart = 0; +static const TimeValue kAlphaSplashStop = 2; + +static const TimeValue kMainMenuTime = 2; +static const TimeValue kLaunchPrepRolloverTime = 3; +static const TimeValue kLaunchPrepHighlightStart = 4; +static const TimeValue kLaunchPrepHighlightStop = 5; +static const TimeValue kClawControlRolloverTime = 5; +static const TimeValue kClawControlHighlightStart = 6; +static const TimeValue kClawControlHighlightStop = 7; + +static const TimeValue kAlphaLaunchPrepStart = 7; +static const TimeValue kAlphaLaunchPrepStop = 17; + +static const TimeValue kClawMenuStart = 17; +static const TimeValue kClawMenuStop = 18; + +static const TimeValue kClawMenuTime = 18; + +static const TimeValue kDeltaSplashStart = 19; +static const TimeValue kDeltaSplashStop = 21; + +static const TimeValue kDeltaLaunchPrepStart = 21; +static const TimeValue kDeltaLaunchPrepStop = 30; + +// Right monitor times. + +static const NotificationFlags kAlphaSplashFinished = 1; +static const NotificationFlags kAlphaPrepFinished = kAlphaSplashFinished << 1; +static const NotificationFlags kPrepHighlightFinished = kAlphaPrepFinished << 1; +static const NotificationFlags kClawHighlightFinished = kPrepHighlightFinished << 1; +static const NotificationFlags kClawMenuFinished = kClawHighlightFinished << 1; +static const NotificationFlags kDeltaSplashFinished = kClawMenuFinished << 1; +static const NotificationFlags kDeltaPrepFinished = kDeltaSplashFinished << 1; + +static const NotificationFlags kSubControlNotificationFlags = kAlphaSplashFinished | +														kAlphaPrepFinished | +														kPrepHighlightFinished | +														kClawHighlightFinished | +														kClawMenuFinished | +														kDeltaSplashFinished | +														kDeltaPrepFinished; + +static const NotificationFlags kOneSecondOfMoveFinished = 1; + +static const NotificationFlags kGreenBallNotificationFlags = kOneSecondOfMoveFinished; + +enum { +	kButtonDimFrame, +	kButtonActiveFrame, +	kButtonHighlightedFrame +}; + +enum { +	kAlphaSplash, +	kAlphaMainMenu, +	kDeltaSplash, +	kDeltaMainMenu, +	kClawMenu, +	kPlayingHighlight, +	kPuttingClawAway +}; + +// The owning neighborhood must provide an array of longs which hold the various +// extra IDs for moving the claw around. In addition, the owner must tell the sub +// control room interaction what position the claw starts out in (which is also the +// position the claw must be in before leaving). + +// Standard array indices: +enum { +	kClawFromAToBIndex, +	kClawALoopIndex, +	kClawAPinchIndex, +	kClawACounterclockwiseIndex, +	kClawAClockwiseIndex, +	kClawFromBToAIndex, +	kClawFromBToCIndex, +	kClawFromBToDIndex, +	kClawBLoopIndex, +	kClawBPinchIndex, +	kClawBCounterclockwiseIndex, +	kClawBClockwiseIndex, +	kClawFromCToBIndex, +	kClawCLoopIndex, +	kClawCPinchIndex, +	kClawCCounterclockwiseIndex, +	kClawCClockwiseIndex, +	kClawFromDToBIndex, +	kClawDLoopIndex, +	kClawDPinchIndex, +	kClawDCounterclockwiseIndex, +	kClawDClockwiseIndex +}; + +// Action indices for s_clawStateTable: +// Can also be used as indices into _buttons (except for kNoActionIndex and kLoopActionIndex). +enum { +	kNoActionIndex = -1, +	kPinchActionIndex = 0, +	kMoveDownActionIndex, +	kMoveRightActionIndex, +	kMoveLeftActionIndex, +	kMoveUpActionIndex, +	kCCWActionIndex, +	kCWActionIndex, +	kLoopActionIndex +}; + +/* +	_currentAction and _nextAction: +	 +	At any time, _currentAction contains an action index (defined above). The current +	action index is what the claw is doing right now. If the player presses a button +	before the current action completes, _nextAction saves the new action and input +	is disabled. This has the effect of implementing a queue of commands for the claw +	that can save at most one extra command. +	 +	The general strategy for using _currentAction and _nextAction are: +	--	If the player presses a claw button and _currentAction is kNoActionIndex, +		do the command immediately and set _currentAction accordingly. +	--	If the player presses a claw button and _currentAction is not kNoActionIndex, +		save the appropriate action index in _nextAction. +	--	When a command animation completes, set _nextAction to kNoActionIndex, then +		check if _nextAction has a command waiting in it. If so, play the appriate +		animation, copy _nextAction into _currentAction and set _nextAction to +		kNoActionIndex. +	--	If the player tries to get up, disable input (including all claw buttons) until +		the player rises. Then, if the claw is in its original position, play the +		animation of the player rising. +	--	If the claw needs to be put back, play the first move required to put the +		claw back by setting _currentAction and playing the appropriate animation. +		Leave _nextAction alone. When the animation, completes, check to see if the +		claw is in its original position or not. If so, complete the player rising +		sequence by playing the rising animation. If not, repeat this whole step. +	 +	Using this general strategy allows the use of a single function, +	DispatchClawAction, which can both cause the claw to perform a command and saving +	the next command in _nextAction. +*/ + +// Array indexed by [claw position] [action] +// array yields an index into the neighborhood's extra id table for claw animation or -1. +static const int s_clawStateTable[4][8] = { +	{ +		kClawAPinchIndex, +		kNoActionIndex, +		kNoActionIndex, +		kClawFromAToBIndex, +		kNoActionIndex, +		kClawACounterclockwiseIndex, +		kClawAClockwiseIndex, +		kClawALoopIndex +	}, +	{ +		kClawBPinchIndex, +		kNoActionIndex, +		kClawFromBToAIndex, +		kClawFromBToDIndex, +		kClawFromBToCIndex, +		kClawBCounterclockwiseIndex, +		kClawBClockwiseIndex, +		kClawBLoopIndex +	}, +	{ +		kClawCPinchIndex, +		kClawFromCToBIndex, +		kNoActionIndex, +		kNoActionIndex, +		kNoActionIndex, +		kClawCCounterclockwiseIndex, +		kClawCClockwiseIndex, +		kClawCLoopIndex +	}, +	{ +		kClawDPinchIndex, +		kNoActionIndex, +		kClawFromDToBIndex, +		kNoActionIndex, +		kNoActionIndex, +		kClawDCounterclockwiseIndex, +		kClawDClockwiseIndex, +		kClawDLoopIndex +	} +}; + +// Move directions for s_clawMovieTable: +enum { +	kMoveClawDown, +	kMoveClawRight, +	kMoveClawLeft, +	kMoveClawUp +}; + +static const int kClawNowhere = -1; + +// Array indexed by [claw position] [move direction] +// array yields new claw position or -1. +static const int s_clawMovieTable[4][4] = { +	{ +		kClawNowhere, +		kClawNowhere, +		kClawAtB, +		kClawNowhere +	}, +	{ +		kClawNowhere, +		kClawAtA, +		kClawAtD, +		kClawAtC +	}, +	{ +		kClawAtB, +		kClawNowhere, +		kClawNowhere, +		kClawNowhere +	}, +	{ +		kClawNowhere, +		kClawAtB, +		kClawNowhere, +		kClawNowhere +	} +}; + +// Indexed by claw action index, claw position, plus 0 for start, 1 for stop. +// (Never indexed with kLoopActionIndex.) +static const TimeValue s_clawMonitorTable[7][4][2] = { +	{ +		{ kAPinchStart, kAPinchStop }, +		{ kBPinchStart, kBPinchStop }, +		{ kCPinchStart, kCPinchStop }, +		{ kDPinchStart, kDPinchStop } +	}, +	{ +		{ 0xffffffff, 0xffffffff }, +		{ 0xffffffff, 0xffffffff }, +		{ kCToBStart, kCToBStop }, +		{ 0xffffffff, 0xffffffff } +	}, +	{ +		{ 0xffffffff, 0xffffffff }, +		{ kBToAStart, kBToAStop }, +		{ 0xffffffff, 0xffffffff }, +		{ kDToBStart, kDToBStop } +	}, +	{ +		{ kAToBStart, kAToBStop }, +		{ kBToDStart, kBToDStop }, +		{ 0xffffffff, 0xffffffff }, +		{ 0xffffffff, 0xffffffff } +	}, +	{ +		{ 0xffffffff, 0xffffffff }, +		{ kBToCStart, kBToCStop }, +		{ 0xffffffff, 0xffffffff }, +		{ 0xffffffff, 0xffffffff } +	}, +	{ +		{ kACCWStart, kACCWStop }, +		{ kBCCWStart, kBCCWStop }, +		{ kCCCWStart, kCCCWStop }, +		{ kDCCWStart, kDCCWStop } +	}, +	{ +		{ kACWStart, kACWStop }, +		{ kBCWStart, kBCWStop }, +		{ kCCWStart, kCCWStop }, +		{ kDCWStart, kDCWStop } +	} +}; + +// Frame indices for the green ball sprite. +enum { +	kGreenBallAtA, +	kGreenBallAtAWithClaw, +	kGreenBallAtAWithClawAndRobot, +	kGreenBallAtB, +	kGreenBallAtBWithClaw, +	kGreenBallAtBWithClawAndRobot, +	kGreenBallAtCArmAtA, +	kGreenBallAtCArmAtB, +	kGreenBallAtCArmAtD, +	kGreenBallAtCWithClaw, +	kGreenBallAtD, +	kGreenBallAtDWithClaw, +	kNumClawGreenBalls +}; + +// State constants for _robotState. +enum { +	kNoRobot, +	kRobotApproaching, +	kPunchingOnce, +	kPunchingTwice, +	kPunchingThrice, +	kCarriedToDoor, +	kPlayerWon, +	kRobotWon +}; + +// Sub Control Room button PICTs: +static const ResIDType kSubControlButtonBaseID = 500; +static const ResIDType kClawMonitorGreenBallBaseID = 600; + +// Constructor +SubControlRoom::SubControlRoom(Neighborhood *handler) : GameInteraction(kNoradSubControlRoomInteractionID, handler), +		_subControlMovie(kSubControlMonitorID), _subControlNotification(kSubControlNotificationID, (PegasusEngine *)g_engine), +		_clawMonitorMovie(kClawMonitorID), _pinchButton(kSubControlPinchID), _downButton(kSubControlDownID), +		_rightButton(kSubControlRightID), _leftButton(kSubControlLeftID), _upButton(kSubControlUpID), +		_ccwButton(kSubControlCCWID), _cwButton(kSubControlCWID), _greenBall(kClawMonitorGreenBallID), +		_greenBallNotification(kNoradGreenBallNotificationID, (PegasusEngine *)g_engine) { +	_neighborhoodNotification = handler->getNeighborhoodNotification(); +	_playingAgainstRobot = false; +	_robotState = kNoRobot; +} + +void SubControlRoom::playAgainstRobot() { +	_playingAgainstRobot = true; +} + +void SubControlRoom::openInteraction() {	 +	_currentAction = kNoActionIndex; +	_nextAction = kNoActionIndex; + +	Norad *owner = (Norad *)getOwner(); +	owner->getClawInfo(_outSpotID, _prepSpotID, _clawControlSpotID, _clawButtonSpotIDs[0], +			_clawButtonSpotIDs[1], _clawButtonSpotIDs[2], _clawButtonSpotIDs[3], +			_clawButtonSpotIDs[4], _clawButtonSpotIDs[5], _clawButtonSpotIDs[6], +			_clawStartPosition, _clawExtraIDs); + +	_clawPosition = _clawStartPosition; +	_clawNextPosition = _clawPosition; +	_subControlMovie.initFromMovieFile("Images/Norad Alpha/N22 Left Monitor Movie"); +	_subControlMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); +	_subControlMovie.moveElementTo(kNoradSubControlLeft, kNoradSubControlTop); +	_subControlScale = _subControlMovie.getScale(); +	_subControlMovie.setDisplayOrder(kSubControlOrder); +	_subControlMovie.startDisplaying(); +	_subControlCallBack.setNotification(&_subControlNotification); +	_subControlCallBack.initCallBack(&_subControlMovie, kCallBackAtExtremes); + +	_clawMonitorMovie.initFromMovieFile("Images/Norad Alpha/N22:N60 Right Monitor"); +	_clawMonitorMovie.moveElementTo(kNoradClawMonitorLeft, kNoradClawMonitorTop); +	_clawMonitorMovie.setDisplayOrder(kClawMonitorOrder); +	_clawMonitorMovie.startDisplaying(); +	_clawMonitorCallBack.setNotification(&_subControlNotification); +	_clawMonitorCallBack.initCallBack(&_clawMonitorMovie, kCallBackAtExtremes); + +	_subControlNotification.notifyMe(this, kSubControlNotificationFlags, kSubControlNotificationFlags); + +	_neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); + +	_buttons[0] = &_pinchButton; +	_buttons[1] = &_downButton; +	_buttons[2] = &_rightButton; +	_buttons[3] = &_leftButton; +	_buttons[4] = &_upButton; +	_buttons[5] = &_ccwButton; +	_buttons[6] = &_cwButton; + +	_pinchButton.setDisplayOrder(kSubControlPinchOrder); +	_downButton.setDisplayOrder(kSubControlDownOrder); +	_rightButton.setDisplayOrder(kSubControlRightOrder); +	_leftButton.setDisplayOrder(kSubControlLeftOrder); +	_upButton.setDisplayOrder(kSubControlUpOrder); +	_ccwButton.setDisplayOrder(kSubControlCCWOrder); +	_cwButton.setDisplayOrder(kSubControlCWOrder); + +	for (int i = 0; i < kNumClawButtons; i++) { +		SpriteFrame *frame = new SpriteFrame(); +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3, true); +		_buttons[i]->addFrame(frame, 0, 0); + +		frame = new SpriteFrame(); +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3 + 1, true); +		_buttons[i]->addFrame(frame, 0, 0); + +		frame = new SpriteFrame(); +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kSubControlButtonBaseID + i * 3 + 2, true); +		_buttons[i]->addFrame(frame, 0, 0); + +		_buttons[i]->setCurrentFrameIndex(0); +		_buttons[i]->startDisplaying(); +	} + +	_pinchButton.moveElementTo(kNoradSubControlPinchLeft, kNoradSubControlPinchTop); +	_downButton.moveElementTo(kNoradSubControlDownLeft, kNoradSubControlDownTop); +	_rightButton.moveElementTo(kNoradSubControlRightLeft, kNoradSubControlRightTop); +	_leftButton.moveElementTo(kNoradSubControlLeftLeft, kNoradSubControlLeftTop); +	_upButton.moveElementTo(kNoradSubControlUpLeft, kNoradSubControlUpTop); +	_ccwButton.moveElementTo(kNoradSubControlCCWLeft, kNoradSubControlCCWTop); +	_cwButton.moveElementTo(kNoradSubControlCWLeft, kNoradSubControlCWTop); + +	_greenBall.setDisplayOrder(kClawMonitorGreenBallOrder); + +	for (int i = 0; i < kNumClawGreenBalls; i++) { +		SpriteFrame *frame = new SpriteFrame(); +		frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kClawMonitorGreenBallBaseID + i); +		_greenBall.addFrame(frame, 0, 0); +	} + +	_greenBall.setCurrentFrameIndex(0); +	_greenBall.startDisplaying(); + +	_greenBallTimer.setScale(owner->getNavMovie()->getScale()); +	_greenBallCallBack.setNotification(&_greenBallNotification); +	_greenBallCallBack.initCallBack(&_greenBallTimer, kCallBackAtExtremes); +	_greenBallCallBack.setCallBackFlag(kOneSecondOfMoveFinished); +	_greenBallNotification.notifyMe(this, kGreenBallNotificationFlags, kGreenBallNotificationFlags); + +	_subControlMovie.show(); +	_clawMonitorMovie.show(); +}									 + +void SubControlRoom::initInteraction() { +	if (GameState.getNoradSubPrepState() == kSubDamaged) { +		playControlMonitorSection(kDeltaSplashStart * _subControlScale, kDeltaSplashStop * _subControlScale, +				0, kDeltaSplash, false); +		playClawMonitorSection(kDeltaClawSplashStart, kDeltaClawSplashStop, kDeltaSplashFinished, _gameState, false); +	} else { +		playControlMonitorSection(kAlphaSplashStart * _subControlScale, kAlphaSplashStop * _subControlScale, +				0, kAlphaSplash, false); +		playClawMonitorSection(kAlphaClawSplashStart, kAlphaClawSplashStop, kAlphaSplashFinished, _gameState, false); +	} + +	_subControlMovie.redrawMovieWorld(); +	_clawMonitorMovie.redrawMovieWorld(); + +	GameState.setScoringPlayedWithClaw(true); +}									 + +void SubControlRoom::closeInteraction() { +	_subControlNotification.cancelNotification(this); +	_subControlCallBack.releaseCallBack(); +	_greenBallNotification.cancelNotification(this); +	_greenBallCallBack.releaseCallBack(); +	_neighborhoodNotification->cancelNotification(this); +} + +void SubControlRoom::setSoundFXLevel(const uint16 fxLevel) { +	_subControlMovie.setVolume(fxLevel); +} + +void SubControlRoom::receiveNotification(Notification *notification, const NotificationFlags flags) {	 +	Norad *owner = (Norad *)getOwner(); + +	if (notification == &_subControlNotification) { +		switch (flags) { +		case kAlphaSplashFinished: +			setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true); +			break; +		case kPrepHighlightFinished: +			if (GameState.getNoradSubPrepState() == kSubDamaged) +				playControlMonitorSection(kDeltaLaunchPrepStart * _subControlScale, +						kDeltaLaunchPrepStop * _subControlScale, kDeltaPrepFinished, _gameState, false); +			else +				playControlMonitorSection(kAlphaLaunchPrepStart * _subControlScale, +						kAlphaLaunchPrepStop * _subControlScale, kAlphaPrepFinished, _gameState, false); +			break; +		case kAlphaPrepFinished: +			GameState.setNoradSubPrepState(kSubPrepped); +			GameState.setScoringPreppedSub(true); +			setControlMonitorToTime(kMainMenuTime * _subControlScale, kAlphaMainMenu, true); +			break; +		case kClawHighlightFinished: +			playControlMonitorSection(kClawMenuStart * _subControlScale, kClawMenuStop * _subControlScale, +					kClawMenuFinished, _gameState, false); +			break; +		case kClawMenuFinished: +			owner->playClawMonitorIntro(); +			showButtons(); +			setControlMonitorToTime(kClawMenuTime * _subControlScale, kClawMenu, true); + +			if (!_playingAgainstRobot) { +				updateClawMonitor(); +				owner->loopExtraSequence(_clawExtraIDs[s_clawStateTable[_clawPosition][kLoopActionIndex]]); +			} +			break; +		case kDeltaSplashFinished: +			setControlMonitorToTime(kMainMenuTime * _subControlScale, kDeltaMainMenu, true); + +			if (_playingAgainstRobot) { +				_robotState = kRobotApproaching; +				playClawMonitorSection(kRobotApproachStart, kRobotApproachStop, 0, _gameState, true); +				owner->startExtraSequence(kN60RobotApproaches, kExtraCompletedFlag, kFilterAllInput); +			} +			break; +		case kDeltaPrepFinished: +			setControlMonitorToTime(kMainMenuTime * _subControlScale, kDeltaMainMenu, true); +			break; +		} +	} else if (notification == &_greenBallNotification) { +		if (_robotState == kRobotWon) { +			// We are using the green ball notification to hide stuff when the robot comes through +			// the glass. +			hideEverything(); +		} else { +			// We are now midway through a move, time to update the claw's position and the green +			// ball. +			_clawPosition = _clawNextPosition; +			updateClawMonitor(); +			updateGreenBall(); +		} +	} else if (notification == _neighborhoodNotification) { +		_currentAction = kNoActionIndex; +		if (_playingAgainstRobot) { +			switch (_robotState) { +			case kRobotApproaching: +				if (_gameState == kClawMenu) { +					_robotState = kPunchingOnce; +					dispatchClawAction(kNoActionIndex); +				} else { +					robotKillsPlayer(kN60FirstMistake, owner); +				} +				break; +			case kPunchingOnce: +				if (_nextAction == kMoveDownActionIndex) { +					_robotState = kPunchingTwice; +					performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner); +				} else { +					robotKillsPlayer(kN60SecondMistake, owner); +				} +				break; +			case kPunchingTwice: +				if (_nextAction == kPinchActionIndex) { +					_robotState = kPunchingThrice; +					performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner); +				} else { +					robotKillsPlayer(kN60ThirdMistake, owner); +				} +				break; +			case kPunchingThrice: +				if (_nextAction == kMoveRightActionIndex) { +					_robotState = kCarriedToDoor; +					performActionImmediately(_nextAction, _clawExtraIDs[s_clawStateTable[_clawPosition][_nextAction]], owner); +				} else { +					robotKillsPlayer(kN60FourthMistake, owner); +				} +				break; +			case kCarriedToDoor: +				hideEverything(); +				_robotState = kPlayerWon; +				owner->startExtraSequence(kN60PlayerFollowsRobotToDoor, kExtraCompletedFlag, kFilterAllInput); +				break; +			case kPlayerWon: +				((NoradDelta *)owner)->playerBeatRobotWithClaw(); +				owner->requestDeleteCurrentInteraction(); +				break; +			case kRobotWon: +				g_system->delayMillis(2 * 1000); // 120 ticks +				((PegasusEngine *)g_engine)->die(kDeathRobotSubControlRoom); +				break; +			} +		} else { +			if (_gameState == kPuttingClawAway && _nextAction == kNoActionIndex) { +				if (_clawPosition == _clawStartPosition) { +					Input scratch; +					GameInteraction::clickInHotspot(scratch, g_allHotspots.findHotspotByID(_outSpotID)); +				} else { +					switch (_clawPosition) { +					case kClawAtA: +						dispatchClawAction(kMoveLeftActionIndex); +						break; +					case kClawAtB: +						if (_clawStartPosition == kClawAtD)             // Norad Alpha +							dispatchClawAction(kMoveLeftActionIndex); +						else if (_clawStartPosition == kClawAtC)        // Norad Delta +							dispatchClawAction(kMoveUpActionIndex); +						break; +					case kClawAtC: +						dispatchClawAction(kMoveDownActionIndex); +						break; +					case kClawAtD: +						dispatchClawAction(kMoveRightActionIndex); +						break; +					} +				} +			} else { +				dispatchClawAction(_nextAction); +			} +		} +	} +} + +void SubControlRoom::hideEverything() { +	hideButtons(); +	_subControlMovie.hide(); +	_clawMonitorMovie.hide(); +	_greenBall.hide(); +} + +void SubControlRoom::robotKillsPlayer(const uint32 extraID, Neighborhood *owner) {	 +	_robotState = kRobotWon; +	owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterAllInput); +	_greenBallTimer.stop(); +	_greenBallTimer.setSegment(0, 32 * _greenBallTimer.getScale() / 15); +	_greenBallTimer.setTime(0); +	_greenBallCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	_greenBallTimer.start(); +} + +void SubControlRoom::activateHotspots() {	 +	if (_robotState == kRobotWon || _robotState == kPlayerWon) +		return; + +	GameInteraction::activateHotspots(); + +	switch (_gameState) { +	case kAlphaMainMenu: +	case kDeltaMainMenu: +		g_allHotspots.activateOneHotspot(_prepSpotID); +		g_allHotspots.activateOneHotspot(_clawControlSpotID); +		break; +	case kClawMenu: +		// This could be called during a move, so use _clawNextPosition. +		if (_playingAgainstRobot) { +			g_allHotspots.deactivateOneHotspot(_outSpotID); +			if (_robotState != kRobotApproaching && _nextAction == kNoActionIndex) +				for (int i = 0; i < kNumClawButtons; i++) +					if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex) +						g_allHotspots.activateOneHotspot(_clawButtonSpotIDs[i]); +		} else if (_nextAction == kNoActionIndex) { +			for (int i = 0; i < kNumClawButtons; i++) +				if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex) +					g_allHotspots.activateOneHotspot(_clawButtonSpotIDs[i]); +		} +		break; +	default: +		break; +	} +} + +void SubControlRoom::showButtons() {	 +	if (_playingAgainstRobot && _robotState == kRobotApproaching) { +		for (int i = 0; i < kNumClawButtons; i++) { +			_buttons[i]->show(); +			_buttons[i]->setCurrentFrameIndex(kButtonDimFrame); +		} +	} else if (_nextAction != kNoActionIndex) { +		for (int i = 0; i < kNumClawButtons; i++) { +			_buttons[i]->show(); +			if (i == _currentAction || i == _nextAction) +				_buttons[i]->setCurrentFrameIndex(kButtonHighlightedFrame); +			else +				_buttons[i]->setCurrentFrameIndex(kButtonDimFrame); +		} +	} else { +		for (int i = 0; i < kNumClawButtons; i++) { +			_buttons[i]->show(); +			if (i == _currentAction) +				_buttons[i]->setCurrentFrameIndex(kButtonHighlightedFrame); +			else if (s_clawStateTable[_clawNextPosition][i] != kNoActionIndex && +					_gameState != kPuttingClawAway) // this could be called during a move, so check _clawNextPosition +				_buttons[i]->setCurrentFrameIndex(kButtonActiveFrame); +			else +				_buttons[i]->setCurrentFrameIndex(kButtonDimFrame); +		} +	} +} + +void SubControlRoom::hideButtons() {	 +	for (int i = 0; i < kNumClawButtons; i++) +		_buttons[i]->hide(); +} + +int SubControlRoom::findActionIndex(HotSpotID id) {	 +	for (int i = 0; i < kNumClawButtons; i++) +		if (id == _clawButtonSpotIDs[i]) +			return i; + +	return kNoActionIndex; +} + +void SubControlRoom::clickInHotspot(const Input &input, const Hotspot *spot) { +	HotSpotID clickedID = spot->getObjectID(); +	int actionIndex = findActionIndex(clickedID); + +	if (actionIndex != kNoActionIndex) { +		dispatchClawAction(actionIndex); +	} else if (clickedID == _prepSpotID) { +		playControlMonitorSection(kLaunchPrepHighlightStart * _subControlScale, +				kLaunchPrepHighlightStop * _subControlScale, +				kPrepHighlightFinished, kPlayingHighlight, false); +	} else if (clickedID == _clawControlSpotID) { +		playControlMonitorSection(kClawControlHighlightStart * _subControlScale, +				kClawControlHighlightStop * _subControlScale, +				kClawHighlightFinished, kPlayingHighlight, false); +	} else if (clickedID == _outSpotID) { +		_gameState = kPuttingClawAway; + +		if (_currentAction == kNoActionIndex) { +			if (_clawPosition == _clawStartPosition) { +				GameInteraction::clickInHotspot(input, spot); +			} else { +				switch (_clawPosition) { +				case kClawAtA: +					dispatchClawAction(kMoveLeftActionIndex); +					break; +				case kClawAtB: +					if (_clawStartPosition == kClawAtD)             // Norad Alpha +						dispatchClawAction(kMoveLeftActionIndex); +					else if (_clawStartPosition == kClawAtC)        // Norad Delta +						dispatchClawAction(kMoveUpActionIndex); +					break; +				case kClawAtC: +					dispatchClawAction(kMoveDownActionIndex); +					break; +				case kClawAtD: +					dispatchClawAction(kMoveRightActionIndex); +					break; +				} +			} +		} +	} else { +		GameInteraction::clickInHotspot(input, spot); +	} +} + +void SubControlRoom::dispatchClawAction(const int newAction) { +	Neighborhood *owner = getOwner(); + +	if (newAction == kNoActionIndex) { +		_currentAction = kNoActionIndex; +		_nextAction = kNoActionIndex; +		showButtons(); +		updateGreenBall(); + +		if (_playingAgainstRobot) +			owner->startExtraSequence(kN60ArmActivated, kExtraCompletedFlag, kFilterAllInput); +		else +			owner->loopExtraSequence(_clawExtraIDs[s_clawStateTable[_clawPosition][kLoopActionIndex]]); +	} else { +		if (_currentAction == kNoActionIndex) { +			if (_playingAgainstRobot) { +				_nextAction = newAction; +				showButtons(); +				updateGreenBall(); +			} else { +				performActionImmediately(newAction, _clawExtraIDs[s_clawStateTable[_clawPosition][newAction]], owner); +			} +		} else if (_nextAction == kNoActionIndex) { +			_nextAction = newAction; +			showButtons(); +			updateGreenBall(); +		} +	} +} + +void SubControlRoom::performActionImmediately(const int action, const uint32 extraID, Neighborhood *owner) { +	_currentAction = action; +	_nextAction = kNoActionIndex; +	ExtraTable::Entry entry; + +	switch (action) { +	case kMoveDownActionIndex: +	case kMoveRightActionIndex: +	case kMoveLeftActionIndex: +	case kMoveUpActionIndex: +		// Set up green ball callback. +		owner->getExtraEntry(extraID, entry); +		_greenBallTimer.stop(); +		_greenBallTimer.setSegment(entry.movieStart, entry.movieStart + owner->getNavMovie()->getScale()); +		_greenBallTimer.setTime(entry.movieStart); +		_greenBallCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +		// Start move. +		_greenBallTimer.start(); +		break; +	} + +	if (_playingAgainstRobot) { +		switch (_robotState) { +		case kPunchingTwice: +			owner->startExtraSequence(kN60ArmToPositionB, kExtraCompletedFlag, kFilterAllInput); +			break; +		case kPunchingThrice: +			owner->startExtraSequence(kN60ArmGrabsRobot, kExtraCompletedFlag, kFilterAllInput); +			break; +		case kCarriedToDoor: +			owner->startExtraSequence(kN60ArmCarriesRobotToPositionA, kExtraCompletedFlag, kFilterAllInput); +			break; +		} +	} else { +		owner->startExtraSequence(extraID, kExtraCompletedFlag, kFilterAllInput); +	} + +	switch (action) { +	case kMoveDownActionIndex: +		_clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawDown]; +		break; +	case kMoveRightActionIndex: +		_clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawRight]; +		break; +	case kMoveLeftActionIndex: +		_clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawLeft]; +		break; +	case kMoveUpActionIndex: +		_clawNextPosition = s_clawMovieTable[_clawPosition][kMoveClawUp]; +		break; +	case kLoopActionIndex: +		// Do nothing. +		break; +	default: +		playClawMonitorSection(s_clawMonitorTable[action][_clawPosition][0], +				s_clawMonitorTable[action][_clawPosition][1], 0, _gameState, true); +		break; +	} + +	showButtons(); +	updateGreenBall(); +} + +void SubControlRoom::setControlMonitorToTime(const TimeValue newTime, const int newState, const bool shouldAllowInput) { +	_subControlMovie.stop(); +	_subControlMovie.setSegment(0, _subControlMovie.getDuration()); +	_subControlMovie.setTime(newTime); +	_subControlMovie.redrawMovieWorld(); +	_gameState = newState; +	allowInput(shouldAllowInput); +} + +void SubControlRoom::playControlMonitorSection(const TimeValue in, const TimeValue out, const NotificationFlags flags, +		const int newState, const bool shouldAllowInput) {	 +	_subControlMovie.stop(); +	_subControlMovie.setSegment(in, out); +	_subControlMovie.setTime(in); + +	if (flags != 0) { +		_subControlCallBack.setCallBackFlag(flags); +		_subControlCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	} + +	_gameState = newState; +	allowInput(shouldAllowInput); +	_subControlMovie.start(); +} + +void SubControlRoom::updateClawMonitor() { +	switch (_clawPosition) { +	case kClawAtA: +		setClawMonitorToTime(kClawAtATime); +		break; +	case kClawAtB: +		setClawMonitorToTime(kClawAtBTime); +		break; +	case kClawAtC: +		setClawMonitorToTime(kClawAtCTime); +		break; +	case kClawAtD: +		setClawMonitorToTime(kClawAtDTime); +		break; +	} +} + +void SubControlRoom::setClawMonitorToTime(const TimeValue newTime) { +	_clawMonitorMovie.stop(); +	_clawMonitorMovie.setSegment(0, _clawMonitorMovie.getDuration()); +	_clawMonitorMovie.setTime(newTime); +	_clawMonitorMovie.redrawMovieWorld(); +} + +void SubControlRoom::playClawMonitorSection(const TimeValue in, const TimeValue out, const NotificationFlags flags, +		const int newState, const bool shouldAllowInput) {	 +	_clawMonitorMovie.stop(); +	_clawMonitorMovie.setSegment(in, out); +	_clawMonitorMovie.setTime(in); + +	if (flags != 0) { +		_clawMonitorCallBack.setCallBackFlag(flags); +		_clawMonitorCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +	} + +	_gameState = newState; +	allowInput(shouldAllowInput); +	_clawMonitorMovie.start(); +} + +void SubControlRoom::updateGreenBall() { +	switch (_currentAction) { +	case kMoveDownActionIndex: +		switch (_nextAction) { +		case kMoveRightActionIndex: +			moveGreenBallToA(); +			break; +		case kMoveLeftActionIndex: +			moveGreenBallToD(); +			break; +		case kMoveUpActionIndex: +			moveGreenBallToC(); +			break; +		default: +			moveGreenBallToB(); +			break; +		} +		break; +	case kMoveRightActionIndex: +		if (_clawNextPosition == kClawAtA) { +			switch (_nextAction) { +			case kMoveLeftActionIndex: +				moveGreenBallToB(); +				break; +			default: +				moveGreenBallToA(); +				break; +			} +		} else { +			switch (_nextAction) { +			case kMoveRightActionIndex: +				moveGreenBallToA(); +				break; +			case kMoveLeftActionIndex: +				moveGreenBallToD(); +				break; +			case kMoveUpActionIndex: +				moveGreenBallToC(); +				break; +			default: +				moveGreenBallToB(); +				break; +			} +		} +		break; +	case kMoveLeftActionIndex: +		if (_clawNextPosition == kClawAtB) { +			switch (_nextAction) { +			case kMoveRightActionIndex: +				moveGreenBallToA(); +				break; +			case kMoveLeftActionIndex: +				moveGreenBallToD(); +				break; +			case kMoveUpActionIndex: +				moveGreenBallToC(); +				break; +			default: +				moveGreenBallToB(); +				break; +			} +		} else { +			switch (_nextAction) { +			case kMoveRightActionIndex: +				moveGreenBallToB(); +				break; +			default: +				moveGreenBallToD(); +				break; +			} +		} +		break; +	case kMoveUpActionIndex: +		switch (_nextAction) { +		case kMoveDownActionIndex: +			moveGreenBallToB(); +			break; +		default: +			moveGreenBallToC(); +			break; +		} +		break; +	default: +		switch (_nextAction) { +		case kMoveDownActionIndex: +			moveGreenBallToB(); +			break; +		case kMoveRightActionIndex: +			if (_clawPosition == kClawAtB) +				moveGreenBallToA(); +			else +				moveGreenBallToB(); +			break; +		case kMoveLeftActionIndex: +			if (_clawPosition == kClawAtB) +				moveGreenBallToD(); +			else +				moveGreenBallToB(); +			break; +		case kMoveUpActionIndex: +			moveGreenBallToC(); +			break; +		default: +			_greenBall.hide(); +			break; +		} +		break; +	} +} + +void SubControlRoom::moveGreenBallToA() { +	if (_clawPosition == kClawAtA) { +		if (_playingAgainstRobot) +			_greenBall.setCurrentFrameIndex(kGreenBallAtAWithClawAndRobot); +		else +			_greenBall.setCurrentFrameIndex(kGreenBallAtAWithClaw); +	} else { +		_greenBall.setCurrentFrameIndex(kGreenBallAtA); +	} + +	_greenBall.moveElementTo(kNoradGreenBallAtALeft, kNoradGreenBallAtATop); +	_greenBall.show(); +} + +void SubControlRoom::moveGreenBallToB() { +	if (_clawPosition == kClawAtB) { +		if (_playingAgainstRobot) +			_greenBall.setCurrentFrameIndex(kGreenBallAtBWithClawAndRobot); +		else +			_greenBall.setCurrentFrameIndex(kGreenBallAtBWithClaw); +	} else { +		_greenBall.setCurrentFrameIndex(kGreenBallAtB); +	} + +	_greenBall.moveElementTo(kNoradGreenBallAtBLeft, kNoradGreenBallAtBTop); +	_greenBall.show(); +} + +void SubControlRoom::moveGreenBallToC() { +	switch (_clawPosition) { +	case kClawAtA: +		_greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtA); +		break; +	case kClawAtB: +		_greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtB); +		break; +	case kClawAtC: +		_greenBall.setCurrentFrameIndex(kGreenBallAtCWithClaw); +		break; +	case kClawAtD: +		_greenBall.setCurrentFrameIndex(kGreenBallAtCArmAtD); +		break; +	} + +	_greenBall.moveElementTo(kNoradGreenBallAtCLeft, kNoradGreenBallAtCTop); +	_greenBall.show(); +} + +void SubControlRoom::moveGreenBallToD() { +	if (_clawPosition == kClawAtD) +		_greenBall.setCurrentFrameIndex(kGreenBallAtDWithClaw); +	else +		_greenBall.setCurrentFrameIndex(kGreenBallAtD); + +	_greenBall.moveElementTo(kNoradGreenBallAtDLeft, kNoradGreenBallAtDTop); +	_greenBall.show(); +} + +bool SubControlRoom::canSolve() { +	return _playingAgainstRobot && _robotState < kCarriedToDoor; +} + +void SubControlRoom::doSolve() { +	_robotState = kCarriedToDoor; +	hideEverything(); +	getOwner()->startExtraSequence(kN60ArmGrabsRobot, kExtraCompletedFlag, kFilterAllInput); +} + +InputBits SubControlRoom::getInputFilter() { +	if (_playingAgainstRobot) +		return GameInteraction::getInputFilter() & ~kFilterDownButtonAny; + +	return GameInteraction::getInputFilter(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/subcontrolroom.h b/engines/pegasus/neighborhood/norad/subcontrolroom.h new file mode 100644 index 0000000000..3ee729b302 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subcontrolroom.h @@ -0,0 +1,133 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H +#define PEGASUS_NEIGHBORHOOD_NORAD_SUBCONTROLROOM_H + +#include "pegasus/interaction.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +static const uint32 kClawAtA = 0; +static const uint32 kClawAtB = 1; +static const uint32 kClawAtC = 2; +static const uint32 kClawAtD = 3; + +static const int kNumClawButtons = 7; + +class Norad; + +class SubControlRoom : public GameInteraction, public NotificationReceiver { +public: +	SubControlRoom(Neighborhood *); +	virtual ~SubControlRoom() {} + +	void playAgainstRobot(); + +	virtual void setSoundFXLevel(const uint16); + +	bool canSolve(); +	void doSolve(); + +protected:	 +	virtual void openInteraction(); +	virtual void initInteraction(); +	virtual void closeInteraction(); + +	virtual void activateHotspots(); +	virtual void clickInHotspot(const Input &, const Hotspot *); + +	virtual void receiveNotification(Notification *, const NotificationFlags); + +	void robotKillsPlayer(const uint32, Neighborhood *); +	InputBits getInputFilter(); + +	int findActionIndex(HotSpotID); +	void dispatchClawAction(const int); +	void performActionImmediately(const int, const uint32, Neighborhood *); + +	void hideEverything(); +	void showButtons(); +	void hideButtons(); + +	void updateGreenBall(); +	void moveGreenBallToA(); +	void moveGreenBallToB(); +	void moveGreenBallToC(); +	void moveGreenBallToD(); + +	void setControlMonitorToTime(const TimeValue, const int, const bool); +	void playControlMonitorSection(const TimeValue, const TimeValue, const NotificationFlags, +			const int, const bool); +	 +	void updateClawMonitor(); +	void setClawMonitorToTime(const TimeValue); +	void playClawMonitorSection(const TimeValue, const TimeValue, const NotificationFlags, +			const int, const bool); +	 +	Movie _subControlMovie; +	TimeScale _subControlScale; +	Notification _subControlNotification; +	NotificationCallBack _subControlCallBack; +	Movie _clawMonitorMovie; +	NotificationCallBack _clawMonitorCallBack; +	int _gameState; +	uint32 _clawStartPosition; +	uint32 _clawPosition; +	uint32 _clawNextPosition; +	const uint32 *_clawExtraIDs; + +	int _currentAction; +	int _nextAction; + +	Sprite *_buttons[kNumClawButtons]; +	Sprite _pinchButton; +	Sprite _downButton; +	Sprite _rightButton; +	Sprite _leftButton; +	Sprite _upButton; +	Sprite _ccwButton; +	Sprite _cwButton; + +	Sprite _greenBall; +	TimeBase _greenBallTimer; +	Notification _greenBallNotification; +	NotificationCallBack _greenBallCallBack; + +	HotSpotID _outSpotID; +	HotSpotID _prepSpotID; +	HotSpotID _clawControlSpotID; +	HotSpotID _clawButtonSpotIDs[kNumClawButtons]; + +	Notification *_neighborhoodNotification; + +	bool _playingAgainstRobot; +	int _robotState; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/norad/subplatform.cpp b/engines/pegasus/neighborhood/norad/subplatform.cpp new file mode 100644 index 0000000000..bfe93ea22c --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subplatform.cpp @@ -0,0 +1,205 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/norad.h" +#include "pegasus/neighborhood/norad/subplatform.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" + +namespace Pegasus { + +// As usual, times here are in seconds. + +static const TimeValue kNormalSplashStart = 0; +static const TimeValue kNormalSplashStop = 5; + +static const TimeValue kPrepSubStart = 5; +static const TimeValue kPrepSubStop = 15; + +static const TimeValue kPrepIncompleteStart = 15; +static const TimeValue kPrepIncompleteStop = 19; + +static const TimeValue kDamagedStart = 19; +static const TimeValue kDamagedStop = 28; + +static const NotificationFlags kNormalSplashFinished = 1; +static const NotificationFlags kPrepSubFinished = kNormalSplashFinished << 1; +static const NotificationFlags kPrepIncompleteFinished = kPrepSubFinished << 1; +static const NotificationFlags kDamagedFinished = kPrepIncompleteFinished << 1; + +static const NotificationFlags kPlatformNotificationFlags = kNormalSplashFinished | +														kPrepSubFinished | +														kPrepIncompleteFinished | +														kDamagedFinished; + +static const uint16 kSubPreppedBit = (1 << 0); +static const uint16 kWaitingForPlayerBit = (1 << 1); + +SubPlatform::SubPlatform(Neighborhood *handler) : GameInteraction(kNoradSubPlatformInteractionID, handler), +		_platformMovie(kPlatformMonitorID), _platformNotification(kNoradSubPlatformNotificationID, (PegasusEngine *)g_engine) { +	_neighborhoodNotification = handler->getNeighborhoodNotification(); +} + +void SubPlatform::openInteraction() { +	_stateBits = 0; + +	// TODO: These next two lines seem unused? +	if (GameState.getNoradSubPrepState() == kSubPrepped) +		_stateBits |= kSubPreppedBit; + +	_stateBits |= kWaitingForPlayerBit; +	_platformMovie.initFromMovieFile("Images/Norad Alpha/Platform Monitor Movie"); +	_platformMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); +	_platformMovie.moveElementTo(kNoradPlatformLeft, kNoradPlatformTop); +	_platformScale = _platformMovie.getScale(); +	_platformMovie.setDisplayOrder(kPlatformOrder); +	_platformMovie.startDisplaying(); +	_platformCallBack.setNotification(&_platformNotification); +	_platformCallBack.initCallBack(&_platformMovie, kCallBackAtExtremes); +	 +	_platformNotification.notifyMe(this, kPlatformNotificationFlags, kPlatformNotificationFlags); +} + +void SubPlatform::initInteraction() { +	_neighborhoodNotification->notifyMe(this, kExtraCompletedFlag, kExtraCompletedFlag); +} + +void SubPlatform::closeInteraction() { +	_platformNotification.cancelNotification(this); +	_platformCallBack.releaseCallBack(); +	_neighborhoodNotification->cancelNotification(this); +} + +void SubPlatform::setSoundFXLevel(const uint16 fxLevel) { +	_platformMovie.setVolume(fxLevel); +} + +void SubPlatform::receiveNotification(Notification *notification, const NotificationFlags flags) { +	FaderMoveSpec loop1Spec, loop2Spec; +	ExtraTable::Entry entry; + +	Norad *owner = (Norad *)getOwner(); + +	if (notification == &_platformNotification) { +		switch (flags) { +		case kNormalSplashFinished: +			_platformMovie.stop(); +			switch (GameState.getNoradSubPrepState()) { +			case kSubNotPrepped: +				_platformMovie.setSegment(kPrepIncompleteStart * _platformScale, kPrepIncompleteStop * _platformScale); +				_platformMovie.setTime(kPrepIncompleteStart * _platformScale); +				_platformCallBack.setCallBackFlag(kPrepIncompleteFinished); +				_platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +				_platformMovie.start(); +				break; +			case kSubPrepped: +				_platformMovie.setSegment(kPrepSubStart * _platformScale, kPrepSubStop * _platformScale); +				_platformMovie.setTime(kPrepSubStart * _platformScale); +				_platformCallBack.setCallBackFlag(kPrepSubFinished); +				_platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +				owner->startExtraSequence(kNorad19PrepSub, 0, kFilterNoInput); +				_platformMovie.start(); +				break; +			case kSubDamaged: +				// Shouldn't happen. +				break; +			} +			break; +		case kPrepSubFinished: +			_platformMovie.stop(); +			_platformMovie.stopDisplaying(); + +			owner->getExtraEntry(kNorad19ExitToSub, entry); +				 +			loop1Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradWarningVolume, +					entry.movieEnd - entry.movieStart, 0); +			loop1Spec.insertFaderKnot(4560, kNoradWarningVolume); +			loop1Spec.insertFaderKnot(5080, 0); +				 +			loop2Spec.makeTwoKnotFaderSpec(kNoradAlphaMovieScale, 0, kNoradSuckWindVolume, +					entry.movieEnd - entry.movieStart, 0); +			loop1Spec.insertFaderKnot(4560, kNoradSuckWindVolume); +			loop1Spec.insertFaderKnot(5080, 0); + +			owner->startExtraSequence(kNorad19ExitToSub, kExtraCompletedFlag, kFilterNoInput); + +			owner->startLoop1Fader(loop1Spec); +			owner->startLoop2Fader(loop2Spec); +			break; +		case kPrepIncompleteFinished: +			((NoradAlpha *)owner)->setSubPrepFailed(true); +			g_AIArea->checkMiddleArea(); +			// Fall through... +		case kDamagedFinished: +			_platformMovie.stop(); +			_platformMovie.hide(); +			_stateBits |= kWaitingForPlayerBit; +			allowInput(true); +			break; +		} +	} else if (notification == _neighborhoodNotification) { +		allowInput(true); +		((PegasusEngine *)g_engine)->jumpToNewEnvironment(kNoradSubChaseID, kNoRoomID, kNoDirection); +		GameState.setScoringEnteredSub(true); +	} +} + +void SubPlatform::activateHotspots() { +	if (_stateBits & kWaitingForPlayerBit) +		g_allHotspots.activateOneHotspot(kNorad19ActivateMonitorSpotID); + +	GameInteraction::activateHotspots(); +} + +void SubPlatform::clickInHotspot(const Input &input, const Hotspot *spot) {	 +	if (spot->getObjectID() == kNorad19ActivateMonitorSpotID) { +		if (GameState.getNoradSubPrepState() == kSubDamaged) { +			_platformMovie.setSegment(kDamagedStart * _platformScale, kDamagedStop * _platformScale); +			_platformMovie.setTime(kDamagedStart * _platformScale); +			_platformCallBack.setCallBackFlag(kDamagedFinished); +		} else { +			_platformMovie.setSegment(kNormalSplashStart * _platformScale, kNormalSplashStop * _platformScale); +			_platformMovie.setTime(kNormalSplashStart * _platformScale); +			_platformCallBack.setCallBackFlag(kNormalSplashFinished); +		} + +		_platformCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); + +		_platformMovie.show(); +		_platformMovie.start(); +		_platformMovie.redrawMovieWorld(); + +		_stateBits &= ~kWaitingForPlayerBit; +		 +		allowInput(false); +	} else { +		GameInteraction::clickInHotspot(input, spot); +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/norad/subplatform.h b/engines/pegasus/neighborhood/norad/subplatform.h new file mode 100644 index 0000000000..a83487db23 --- /dev/null +++ b/engines/pegasus/neighborhood/norad/subplatform.h @@ -0,0 +1,63 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H +#define PEGASUS_NEIGHBORHOOD_NORAD_SUBPLATFORM_H + +#include "pegasus/interaction.h" +#include "pegasus/movie.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +class SubPlatform : public GameInteraction, public NotificationReceiver { +public: +	SubPlatform(Neighborhood *); +	virtual ~SubPlatform() {} + +	virtual void setSoundFXLevel(const uint16); + +protected: +	virtual void openInteraction(); +	virtual void initInteraction(); +	virtual void closeInteraction(); + +	virtual void activateHotspots(); +	virtual void clickInHotspot(const Input &, const Hotspot *); +	 +	virtual void receiveNotification(Notification *, const NotificationFlags); +	 +	Movie _platformMovie; +	TimeScale _platformScale; +	Notification _platformNotification; +	NotificationCallBack _platformCallBack; +	Notification *_neighborhoodNotification; +	uint16 _stateBits; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp new file mode 100644 index 0000000000..b67d31ad95 --- /dev/null +++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.cpp @@ -0,0 +1,689 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/compass.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_action.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/ai/ai_condition.h" +#include "pegasus/ai/ai_rule.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" + +namespace Pegasus { + +static const int16 s_prehistoricCompass[kPrehistoric25 + 1][4] = { +	{ 0, 170, 90, 270 },   // kPrehistoric01 +	{ 0, 180, 90, 270 },   // kPrehistoric02 +	{ 10, 180, 90, 270 },  // kPrehistoric03 +	{ 10, 190, 90, 270 },  // kPrehistoric04 +	{ 10, 195, 90, 270 },  // kPrehistoric05 +	{ 20, 210, 90, 270 },  // kPrehistoric06 +	{ 20, 200, 130, 276 }, // kPrehistoric07 +	{ 20, 176, 110, 260 }, // kPrehistoric08 +	{ 20, 200, 100, 270 }, // kPrehistoric09 +	{ 14, 186, 100, 280 }, // kPrehistoric10 +	{ 26, 206, 116, 296 }, // kPrehistoric11 +	{ 60, 226, 140, 320 }, // kPrehistoric12 +	{ 0, 180, 80, 270 },   // kPrehistoric13 +	{ 14, 200, 106, 286 }, // kPrehistoric14 +	{ -10, 174, 80, 260 }, // kPrehistoric15 +	{ 54, 236, 140, 210 }, // kPrehistoric16 +	{ -24, 160, 70, 250 }, // kPrehistoric17 +	{ 26, 206, 140, 296 }, // kPrehistoric18 +	{ -16, 160, 70, 250 }, // kPrehistoric19 +	{ -16, 160, 70, 250 }, // kPrehistoric20 +	{ -10, 160, 90, 250 }, // kPrehistoric21 +	{ -20, 160, 70, 244 }, // kPrehistoric22 +	{ -20, 160, 70, 244 }, // kPrehistoric22North +	{ 60, 234, 150, 330 }, // kPrehistoric23 +	{ 50, 230, 140, 320 }, // kPrehistoric24 +	{ 60, 240, 140, 330 }  // kPrehistoric25 +}; + +static const TimeValue kPrehistoricFlashlightClickIn = 0; +static const TimeValue kPrehistoricFlashlightClickOut = 138; + +static const TimeValue kPrehistoricBumpIntoWallIn = 138; +static const TimeValue kPrehistoricBumpIntoWallOut = 291; + +static const TimeValue kBridgeRetractIn = 291; +static const TimeValue kBridgeRetractOut = 1499; + +static const TimeValue kPrehistoricWarningTimeLimit = kTenMinutes; + +Prehistoric::Prehistoric(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Prehistoric", kPrehistoricID) { +	setIsItemTaken(kHistoricalLog); +} + +uint16 Prehistoric::getDateResID() const { +	return kDatePrehistoricID; +} + +void Prehistoric::init() { +	Neighborhood::init(); +	 +	// Forces a stop so the flashlight can turn off... +	forceStridingStop(kPrehistoric12, kSouth, kNoAlternateID); +} + +void Prehistoric::start() { +	if (g_energyMonitor) { +		g_energyMonitor->stopEnergyDraining(); +		g_energyMonitor->restoreLastEnergyValue(); +		_vm->resetEnergyDeathReason(); +		g_energyMonitor->startEnergyDraining(); +	} + +	Neighborhood::start(); +} + +class FinishPrehistoricAction : public AIPlayMessageAction { +public: +	FinishPrehistoricAction() : AIPlayMessageAction("Images/AI/Prehistoric/XP25W", false) {} +	~FinishPrehistoricAction() {} +	 +	void performAIAction(AIRule *); + +}; + +void FinishPrehistoricAction::performAIAction(AIRule *rule) { +	AIPlayMessageAction::performAIAction(rule); +	((PegasusEngine *)g_engine)->die(kPlayerWonGame); +} + +void Prehistoric::setUpAIRules() { +	Neighborhood::setUpAIRules(); + +	if (g_AIArea) { +		if (_vm->isDemo()) { +			FinishPrehistoricAction *doneAction = new FinishPrehistoricAction(); +			AIHasItemCondition *hasLogCondition = new AIHasItemCondition(kHistoricalLog); +			AIRule *rule = new AIRule(hasLogCondition, doneAction); +			g_AIArea->addAIRule(rule); +		} else { +			AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP1NB", false); +			AILocationCondition *locCondition = new AILocationCondition(1); +			locCondition->addLocation(MakeRoomView(kPrehistoric16, kNorth)); +			AIRule *rule = new AIRule(locCondition, messageAction); +			g_AIArea->addAIRule(rule); +		 +			messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); +			locCondition = new AILocationCondition(1); +			locCondition->addLocation(MakeRoomView(kPrehistoric01, kSouth)); +			rule = new AIRule(locCondition, messageAction); +			g_AIArea->addAIRule(rule); +		 +			messageAction = new  AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); +			locCondition = new AILocationCondition(1); +			locCondition->addLocation(MakeRoomView(kPrehistoric08, kEast)); +			rule = new AIRule(locCondition, messageAction); +			g_AIArea->addAIRule(rule); +		 +			messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP2SB", false); +			locCondition = new AILocationCondition(1); +			locCondition->addLocation(MakeRoomView(kPrehistoric25, kWest)); +			rule = new AIRule(locCondition, messageAction); +			g_AIArea->addAIRule(rule); +		 +			messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP16NB", false); +			locCondition = new AILocationCondition(1); +			locCondition->addLocation(MakeRoomView(kPrehistoric23, kNorth)); +			rule = new AIRule(locCondition, messageAction); +			g_AIArea->addAIRule(rule); +		 +			messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP18NB", false); +			AITimerCondition *timerCondition = new AITimerCondition(kPrehistoricWarningTimeLimit, 1, true); +			rule = new AIRule(timerCondition, messageAction); +			g_AIArea->addAIRule(rule); +		 +			messageAction = new AIPlayMessageAction("Images/AI/Prehistoric/XP25W", false); +			AIHasItemCondition *hasLogCondition = new AIHasItemCondition(kHistoricalLog); +			rule = new AIRule(hasLogCondition, messageAction); +			g_AIArea->addAIRule(rule); +		} +	} +} + +TimeValue Prehistoric::getViewTime(const RoomID room, const DirectionConstant direction) { +	ExtraTable::Entry extra; +	uint32 extraID = 0xffffffff; + +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kPrehistoric02, kSouth): +		if (!GameState.getPrehistoricSeenTimeStream()) { +			getExtraEntry(kPreArrivalFromTSA, extra); +			return extra.movieStart; +		} +		break; +	case MakeRoomView(kPrehistoric25, kEast): +		if (_privateFlags.getFlag(kPrehistoricPrivateVaultOpenFlag)) { +			if (_vm->itemInLocation(kHistoricalLog, kPrehistoricID, kPrehistoric25, kEast)) +				extraID = kPre25EastViewWithLog; +			else +				extraID = kPre25EastViewNoLog; +		} +		break; +	} + +	if (extraID == 0xffffffff) +		return Neighborhood::getViewTime(room, direction); +	 +	getExtraEntry(extraID, extra); +	return extra.movieEnd - 1; +} + + +void Prehistoric::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { +	Neighborhood::findSpotEntry(room, direction, flags, entry); + +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kPrehistoric01, kSouth): +	case MakeRoomView(kPrehistoric25, kSouth): +		entry.clear(); +		break; +	case MakeRoomView(kPrehistoric01, kEast): +		if (GameState.getPrehistoricSeenFlyer1()) +			entry.clear(); +		else +			GameState.setPrehistoricSeenFlyer1(true); +		break; +	case MakeRoomView(kPrehistoric08, kEast): +		if (GameState.getPrehistoricSeenFlyer2()) +			entry.clear(); +		else +			GameState.setPrehistoricSeenFlyer2(true); +		break; +	} +} + +int16 Prehistoric::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { +	if (room == kPrehistoricDeath) +		return g_compass->getFaderValue(); + +	return s_prehistoricCompass[room][dir]; +} + +void Prehistoric::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { +	uint32 angle; +	Neighborhood::getExitCompassMove(exitEntry, compassMove); + +	switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { +	case MakeRoomView(kPrehistoric01, kNorth): +		compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 2, -10); +		break; +	case MakeRoomView(kPrehistoric06, kEast): +		compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 4, 95); +		compassMove.insertFaderKnot(exitEntry.movieStart + (exitEntry.movieEnd - exitEntry.movieStart) / 4 * 1, 100); +		break; +	case MakeRoomView(kPrehistoric18, kEast): +		if (getCurrentAlternate() == kAltPrehistoricBridgeSet) { +			compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 11, 145); +			compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 26, 145); +			compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 39, 148); +			compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 114, 140); +		} else { +			compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 10, 140); +			compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 16, 145); +			compassMove.insertFaderKnot(exitEntry.movieEnd, 145); +		} +		break; +	case MakeRoomView(kPrehistoric23, kWest): +		angle = compassMove.getNthKnotValue(0); +		compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 17, angle); +		compassMove.insertFaderKnot(exitEntry.movieStart + kPrehistoricFrameDuration * 32, angle - 90); +		compassMove.insertFaderKnot(exitEntry.movieEnd, angle - 90); +		break; +	} +} + +void Prehistoric::turnTo(const DirectionConstant newDirection) { +	setCurrentAlternate(kAltPrehistoricNormal); +	_privateFlags.setFlag(kPrehistoricPrivateVaultOpenFlag, false); +	Neighborhood::turnTo(newDirection); + +	Item *keyCard; + +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kPrehistoric18, kEast): +		zoomToVault(); +		break; +	case MakeRoomView(kPrehistoric18, kNorth): +	case MakeRoomView(kPrehistoric18, kSouth): +		if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) { +			playSpotSoundSync(kBridgeRetractIn, kBridgeRetractOut); +			_privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, false); +			loadAmbientLoops(); +		} +		// fall through +	case MakeRoomView(kPrehistoric25, kEast): +		setCurrentActivation(kActivationVaultClosed); +		break; +	case MakeRoomView(kPrehistoric16, kNorth): +	case MakeRoomView(kPrehistoric21, kWest): +		keyCard = g_allItems.findItemByID(kKeyCard); +		if (keyCard->getItemState() == kFlashlightOff) { +			keyCard->setItemState(kFlashlightOn); +			playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); +		} +		break; +	case MakeRoomView(kPrehistoric16, kEast): +	case MakeRoomView(kPrehistoric16, kWest): +	case MakeRoomView(kPrehistoric21, kNorth): +	case MakeRoomView(kPrehistoric21, kSouth): +		keyCard = g_allItems.findItemByID(kKeyCard); +		if (keyCard->getItemState() == kFlashlightOn) { +			keyCard->setItemState(kFlashlightOff); +			playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); +		} +		break; +	} +} + +void Prehistoric::zoomToVault() { +	if (!GameState.getPrehistoricSeenBridgeZoom()) +		startExtraSequence(kPre18EastZoom, kExtraCompletedFlag, kFilterNoInput); +} + +void Prehistoric::checkContinuePoint(const RoomID room, const DirectionConstant direction) { +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kPrehistoric08, kEast): +	case MakeRoomView(kPrehistoric18, kSouth): +	case MakeRoomView(kPrehistoric16, kNorth): +	case MakeRoomView(kPrehistoric21, kNorth): +	case MakeRoomView(kPrehistoric25, kNorth): +		makeContinuePoint(); +		break; +	} +} + +void Prehistoric::arriveAt(const RoomID room, const DirectionConstant direction) { +	Item *keyCard; +	 +	if (MakeRoomView(room, direction) == MakeRoomView(kPrehistoric25, kEast) && +			_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) { +		_navMovie.stop(); +		playSpotSoundSync(kBridgeRetractIn, kBridgeRetractOut); +		_privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, false); +	} + +	Neighborhood::arriveAt(room, direction); + +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kPrehistoricDeath, kNorth): +	case MakeRoomView(kPrehistoricDeath, kSouth): +	case MakeRoomView(kPrehistoricDeath, kEast): +	case MakeRoomView(kPrehistoricDeath, kWest): +		if (GameState.getLastRoom() == kPrehistoric23) +			die(kDeathEatenByDinosaur); +		else +			die(kDeathFallOffCliff); +		break; +	case MakeRoomView(kPrehistoric02, kSouth): +		if (!GameState.getPrehistoricSeenTimeStream()) { +			GameState.setPrehistoricTriedToExtendBridge(false); +			GameState.setPrehistoricSeenFlyer1(false); +			GameState.setPrehistoricSeenFlyer2(false); +			GameState.setPrehistoricSeenBridgeZoom(false); +			GameState.setPrehistoricBreakerThrown(false); +			startExtraSequence(kPreArrivalFromTSA, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case MakeRoomView(kPrehistoric18, kEast): +		zoomToVault(); +		break; +	case MakeRoomView(kPrehistoric16, kNorth): +		keyCard = g_allItems.findItemByID(kKeyCard); + +		if (keyCard->getItemState() == kFlashlightOff) { +			keyCard->setItemState(kFlashlightOn); +			playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); +		} + +		if (g_AIArea) +			g_AIArea->checkRules(); +		break; +	case MakeRoomView(kPrehistoric01, kSouth): +	case MakeRoomView(kPrehistoric23, kNorth): +		if (g_AIArea) +			g_AIArea->checkRules(); +		break; +	case MakeRoomView(kPrehistoric08, kSouth): +	case MakeRoomView(kPrehistoric10, kSouth): +	case MakeRoomView(kPrehistoric12, kSouth): +	case MakeRoomView(kPrehistoric13, kNorth): +	case MakeRoomView(kPrehistoric14, kSouth): +	case MakeRoomView(kPrehistoric15, kNorth): +	case MakeRoomView(kPrehistoric16, kSouth): +	case MakeRoomView(kPrehistoric17, kNorth): +	case MakeRoomView(kPrehistoric18, kSouth): +	case MakeRoomView(kPrehistoric19, kNorth): +	case MakeRoomView(kPrehistoric20, kNorth): +	case MakeRoomView(kPrehistoric21, kEast): +		keyCard = g_allItems.findItemByID(kKeyCard); + +		if (keyCard->getItemState() == kFlashlightOn) { +			keyCard->setItemState(kFlashlightOff); +			playSpotSoundSync(kPrehistoricFlashlightClickIn, kPrehistoricFlashlightClickOut); +		} +		break; +	case MakeRoomView(kPrehistoric25, kEast): +		setCurrentActivation(kActivationVaultClosed); +		break; +	} +} + +void Prehistoric::loadAmbientLoops() { +	RoomID room = GameState.getCurrentRoom(); +	 +	switch (room) { +	case kPrehistoric02: +		// 1/4 volume. +		if (GameState.getPrehistoricSeenTimeStream()) +			loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64); +		break; +	case kPrehistoric01: +	case kPrehistoric03: +	case kPrehistoric04: +	case kPrehistoric05: +	case kPrehistoric06: +	case kPrehistoric07: +	case kPrehistoric09: +	case kPrehistoric11: +	case kPrehistoric13: +	case kPrehistoric15: +	case kPrehistoric17: +	case kPrehistoric19: +	case kPrehistoric20: +		// 1/4 volume. +		loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 64); +		break; +	case kPrehistoric08: +	case kPrehistoric10: +	case kPrehistoric12: +	case kPrehistoric14: +	case kPrehistoric16: +	case kPrehistoric18: +	case kPrehistoric21: +		// 3/16 volume. +		loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 48); +		break; +	case kPrehistoric25: +		// 1/8 volume. +		loadLoopSound1("Sounds/Prehistoric/P02SAL00.22k.AIFF", 32); +		break; +	case kPrehistoric22: +	case kPrehistoric22North: +	case kPrehistoric23: +	case kPrehistoric24: +	case kPrehistoricDeath: +		// 0 volume. +		loadLoopSound1(""); +		break; +	} + +	switch (room) { +	case kPrehistoric02: +	case kPrehistoric03: +	case kPrehistoric04: +	case kPrehistoric05: +	case kPrehistoric06: +	case kPrehistoric07: +	case kPrehistoric08: +	case kPrehistoric09: +	case kPrehistoric10: +	case kPrehistoric11: +	case kPrehistoric12: +	case kPrehistoric13: +	case kPrehistoric14: +	case kPrehistoric15: +	case kPrehistoric16: +	case kPrehistoric17: +	case kPrehistoric19: +	case kPrehistoric20: +	case kPrehistoric21: +	case kPrehistoricDeath: +		loadLoopSound2(""); +		break; +	case kPrehistoric01: +	case kPrehistoric25: +		loadLoopSound2("Sounds/Prehistoric/VolcLoop.22K.AIFF", 64); +		break; +	case kPrehistoric18: +		if (_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) +			loadLoopSound2("Sounds/Prehistoric/P18EAL00.22k.AIFF", 0x100, 0, 0); +		else +			loadLoopSound2(""); +		break; +	case kPrehistoric23: +	case kPrehistoric24: +	case kPrehistoric22: +	case kPrehistoric22North: +		loadLoopSound2("Sounds/Prehistoric/P24NAL00.22k.AIFF", 64); +		break; +	} +} + +void Prehistoric::activateHotspots() { +	Neighborhood::activateHotspots(); + +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kPrehistoric18, kEast): +		if (!_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) +			g_allHotspots.activateOneHotspot(kPre18EastSpotID); +		break; +	case MakeRoomView(kPrehistoric22North, kNorth): +		g_allHotspots.activateOneHotspot(kPre22NorthBreakerSpotID); +		break; +	} +} + +void Prehistoric::clickInHotspot(const Input &input, const Hotspot *spot) { +	switch (spot->getObjectID()) { +	case kPre18EastSpotID: +		if (GameState.getPrehistoricBreakerThrown()) +			startExtraSequence(kPre18EastBridgeOn, kExtraCompletedFlag, kFilterNoInput); +		else +			startExtraSequence(kPre18EastBridgeOut, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kPre22NorthBreakerSpotID: +		startExtraSequence(kPre22ThrowBreaker, kExtraCompletedFlag, kFilterNoInput); +		break; +	default: +		Neighborhood::clickInHotspot(input, spot); +		break; +	} +} + +void Prehistoric::receiveNotification(Notification *notification, const NotificationFlags flags) { +	Neighborhood::receiveNotification(notification, flags); + +	if ((flags & kExtraCompletedFlag) != 0) { +		_interruptionFilter = kFilterAllInput; + +		switch (_lastExtra) { +		case kPreArrivalFromTSA: +			GameState.setPrehistoricSeenTimeStream(true); +			loadAmbientLoops(); +			makeContinuePoint(); +			break; +		case kPre18EastZoom: +			startExtraSequence(kPre18EastZoomOut, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kPre18EastZoomOut: +			GameState.setPrehistoricSeenBridgeZoom(true); +			break; +		case kPre18EastBridgeOn: +			_privateFlags.setFlag(kPrehistoricPrivateExtendedBridgeFlag, true); +			setCurrentAlternate(kAltPrehistoricBridgeSet); +			GameState.setPrehistoricTriedToExtendBridge(false); +			loadAmbientLoops(); +			GameState.setScoringExtendedBridge(true); +			break; +		case kPre18EastBridgeOut: +			GameState.setPrehistoricTriedToExtendBridge(true); +			if (g_AIArea) +				g_AIArea->checkMiddleArea(); +			break; +		case kPre22ThrowBreaker: +			GameState.setPrehistoricBreakerThrown(true); +			GameState.setScoringThrewBreaker(true); +			break; +		case kPre25EastUnlockingVaultNoLog: +		case kPre25EastUnlockingVaultWithLog: +			_vm->addItemToInventory((InventoryItem *)g_allItems.findItemByID(kJourneymanKey)); +			break; +		} +	} + +	g_AIArea->checkMiddleArea(); +} + +Common::String Prehistoric::getBriefingMovie() { +	Common::String movieName = Neighborhood::getBriefingMovie(); + +	if (movieName.empty()) +		movieName = "Images/AI/Prehistoric/XPE"; + +	return movieName; +} + +Common::String Prehistoric::getEnvScanMovie() { +	Common::String movieName = Neighborhood::getEnvScanMovie(); + +	if (movieName.empty()) { +		if (!_vm->isDemo()) { +			switch (GameState.getCurrentRoom()) { +			case kPrehistoric16: +			case kPrehistoric23: +			case kPrehistoric24: +				return "Images/AI/Prehistoric/XP7WB"; +			} +		} + +		return "Images/AI/Prehistoric/XP17NB"; +	} + +	return movieName; +} + +uint Prehistoric::getNumHints() { +	uint numHints = Neighborhood::getNumHints(); +	 +	if (numHints == 0) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kPrehistoric18, kEast): +			if (!GameState.getPrehistoricBreakerThrown() && GameState.getPrehistoricTriedToExtendBridge() && +					!_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag)) +				numHints = 1; +			break; +		case MakeRoomView(kPrehistoric25, kEast): +			if (!_privateFlags.getFlag(kPrehistoricPrivateVaultOpenFlag)) +				numHints = 1; +			break; +		} +	} + +	return numHints; +} + +Common::String Prehistoric::getHintMovie(uint hintNum) { +	Common::String movieName = Neighborhood::getHintMovie(hintNum); + +	if (movieName.empty()) { +		switch (GameState.getCurrentRoomAndView()) { +		case MakeRoomView(kPrehistoric18, kEast): +			return "Images/AI/Prehistoric/XP18WD"; +		case MakeRoomView(kPrehistoric25, kEast): +			return "Images/AI/Globals/XGLOB1A"; +		} +	} + +	return movieName; +} + +bool Prehistoric::canSolve() { +	return	GameState.getCurrentRoomAndView() == MakeRoomView(kPrehistoric18, kEast) && +			!GameState.getPrehistoricBreakerThrown() && +			GameState.getPrehistoricTriedToExtendBridge() && +			!_privateFlags.getFlag(kPrehistoricPrivateExtendedBridgeFlag); +} + +void Prehistoric::doSolve() { +	GameState.setPrehistoricBreakerThrown(true); +	startExtraSequence(kPre18EastBridgeOn, kExtraCompletedFlag, kFilterNoInput); +} + +Hotspot *Prehistoric::getItemScreenSpot(Item *item, DisplayElement *element) { +	if (item->getObjectID() == kHistoricalLog) +		return g_allHotspots.findHotspotByID(kPrehistoricHistoricalLogSpotID); + +	return Neighborhood::getItemScreenSpot(item, element); +} + +void Prehistoric::pickedUpItem(Item *item) { +	switch (item->getObjectID()) { +	case kHistoricalLog: +		GameState.setScoringGotHistoricalLog(true); +		break; +	} + +	Neighborhood::pickedUpItem(item); +} + +void Prehistoric::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { +	switch (item->getObjectID()) { +	case kJourneymanKey: +		Neighborhood::dropItemIntoRoom(item, dropSpot); + +		if (GameState.isTakenItemID(kHistoricalLog)) +			startExtraLongSequence(kPre25EastUnlockingVaultNoLog, kPre25EastVaultOpenNoLog, kExtraCompletedFlag, kFilterNoInput); +		else +			startExtraLongSequence(kPre25EastUnlockingVaultWithLog, kPre25EastVaultOpenWithLog, kExtraCompletedFlag, kFilterNoInput); + +		_privateFlags.setFlag(kPrehistoricPrivateVaultOpenFlag, true); +		setCurrentActivation(kActivationVaultOpen); +		break; +	default: +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		break; +	} +} + +void Prehistoric::bumpIntoWall() { +	requestSpotSound(kPrehistoricBumpIntoWallIn, kPrehistoricBumpIntoWallOut, kFilterAllInput, 0); +	Neighborhood::bumpIntoWall(); +} + +Common::String Prehistoric::getNavMovieName() { +	return "Images/Prehistoric/Prehistoric.movie"; +} + +Common::String Prehistoric::getSoundSpotsName() { +	return "Sounds/Prehistoric/Prehistoric Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/prehistoric/prehistoric.h b/engines/pegasus/neighborhood/prehistoric/prehistoric.h new file mode 100644 index 0000000000..2750fc0ee8 --- /dev/null +++ b/engines/pegasus/neighborhood/prehistoric/prehistoric.h @@ -0,0 +1,158 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_PREHISTORIC_H +#define PEGASUS_NEIGHBORHOOD_PREHISTORIC_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +static const TimeScale kPrehistoricMovieScale = 600; +static const TimeScale kPrehistoricFramesPerSecond = 15; +static const TimeScale kPrehistoricFrameDuration = 40; + +//	Alternate IDs. + +static const AlternateID kAltPrehistoricNormal = 0; +static const AlternateID kAltPrehistoricBridgeSet = 1; + +//	Room IDs. + +static const RoomID kPrehistoric01 = 0; +static const RoomID kPrehistoric02 = 1; +static const RoomID kPrehistoric03 = 2; +static const RoomID kPrehistoric04 = 3; +static const RoomID kPrehistoric05 = 4; +static const RoomID kPrehistoric06 = 5; +static const RoomID kPrehistoric07 = 6; +static const RoomID kPrehistoric08 = 7; +static const RoomID kPrehistoric09 = 8; +static const RoomID kPrehistoric10 = 9; +static const RoomID kPrehistoric11 = 10; +static const RoomID kPrehistoric12 = 11; +static const RoomID kPrehistoric13 = 12; +static const RoomID kPrehistoric14 = 13; +static const RoomID kPrehistoric15 = 14; +static const RoomID kPrehistoric16 = 15; +static const RoomID kPrehistoric17 = 16; +static const RoomID kPrehistoric18 = 17; +static const RoomID kPrehistoric19 = 18; +static const RoomID kPrehistoric20 = 19; +static const RoomID kPrehistoric21 = 20; +static const RoomID kPrehistoric22 = 21; +static const RoomID kPrehistoric22North = 22; +static const RoomID kPrehistoric23 = 23; +static const RoomID kPrehistoric24 = 24; +static const RoomID kPrehistoric25 = 25; +static const RoomID kPrehistoricDeath = 26; + +//	Hot Spot Activation IDs. + +static const HotSpotActivationID kActivationVaultClosed = 1; +static const HotSpotActivationID kActivationVaultOpen = 2; + +//	Hot Spot IDs. + +static const HotSpotID kPre18EastSpotID = 5000; +static const HotSpotID kPre22NorthSpotID = 5001; +static const HotSpotID kPre22NorthOutSpotID = 5002; +static const HotSpotID kPre22NorthBreakerSpotID = 5003; +static const HotSpotID kPrehistoricKeyDropSpotID = 5004; +static const HotSpotID kPrehistoricHistoricalLogSpotID = 5005; + +//	Extra sequence IDs. + +static const ExtraID kPreArrivalFromTSA = 0; +static const ExtraID kPre18EastBridgeOut = 1; +static const ExtraID kPre18EastBridgeOn = 2; +static const ExtraID kPre18EastZoom = 3; +static const ExtraID kPre18EastZoomOut = 4; +static const ExtraID kPre22ThrowBreaker = 5; +static const ExtraID kPre25EastUnlockingVaultWithLog = 6; +static const ExtraID kPre25EastVaultOpenWithLog = 7; +static const ExtraID kPre25EastViewWithLog = 8; +static const ExtraID kPre25EastUnlockingVaultNoLog = 9; +static const ExtraID kPre25EastVaultOpenNoLog = 10; +static const ExtraID kPre25EastViewNoLog = 11; + +class PegasusEngine; + +class Prehistoric : public Neighborhood { +public: +	Prehistoric(InputHandler *, PegasusEngine *); +	virtual ~Prehistoric() {} +	 +	virtual uint16 getDateResID() const; +	virtual void init(); +	 +	virtual void arriveAt(const RoomID, const DirectionConstant); +	virtual void activateHotspots(); +	virtual void clickInHotspot(const Input &, const Hotspot *); +	Common::String getBriefingMovie(); +	Common::String getEnvScanMovie(); +	uint getNumHints(); +	Common::String getHintMovie(uint); + +	Hotspot *getItemScreenSpot(Item *, DisplayElement *); +	void dropItemIntoRoom(Item *, Hotspot *); +	void pickedUpItem(Item *); + +	void start(); + +	void bumpIntoWall(); + +	void checkContinuePoint(const RoomID, const DirectionConstant); + +	bool canSolve(); +	void doSolve(); + +protected: +	enum { +		kPrehistoricPrivateVaultOpenFlag, +		kPrehistoricPrivateExtendedBridgeFlag, +		kNumPrehistoricPrivateFlags +	}; + +	void setUpAIRules(); +	int16 getStaticCompassAngle(const RoomID, const DirectionConstant); +	void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); +	virtual void receiveNotification(Notification *, const NotificationFlags); +	void turnTo(const DirectionConstant); +	void zoomToVault(); +	TimeValue getViewTime(const RoomID, const DirectionConstant); +	void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); + +	void loadAmbientLoops(); +	 +	FlagsArray<byte, kNumPrehistoricPrivateFlags> _privateFlags; + +	Common::String getNavMovieName(); +	Common::String getSoundSpotsName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/spot.cpp b/engines/pegasus/neighborhood/spot.cpp new file mode 100644 index 0000000000..f285bf9bc2 --- /dev/null +++ b/engines/pegasus/neighborhood/spot.cpp @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/spot.h" + +namespace Pegasus { + +void SpotTable::loadFromStream(Common::SeekableReadStream *stream) { +	uint32 count = stream->readUint32BE(); +	_entries.resize(count); + +	for (uint32 i = 0; i < count; i++) { +		_entries[i].room = stream->readUint16BE(); +		_entries[i].direction = stream->readByte(); +		_entries[i].srcFlags = stream->readByte(); +		_entries[i].altCode = stream->readByte(); +		stream->readByte(); // alignment +		_entries[i].movieStart = stream->readUint32BE(); +		_entries[i].movieEnd = stream->readUint32BE(); +		_entries[i].dstFlags = stream->readByte(); +		stream->readByte(); // alignment +		debug(0, "Spot[%d]: %d %d %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, +				_entries[i].srcFlags, _entries[i].altCode, _entries[i].movieStart, +				_entries[i].movieEnd, _entries[i].dstFlags); +	} +} + +void SpotTable::clear() { +	_entries.clear(); +} + +// Two SpotTable::Entries are equal if +//      In addition to having their rooms, directions and alt codes identical... +//      They are both either loops or once only animations AND +//      They overlap in at least one of the on arrival, on turn and on door open bits. +SpotTable::Entry SpotTable::findEntry(RoomID room, DirectionConstant direction, SpotFlags srcFlags, AlternateID altCode) { +	for (uint32 i = 0; i < _entries.size(); i++) +		if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode && (_entries[i].srcFlags & kSpotLoopsMask) == (srcFlags & kSpotLoopsMask) && ((_entries[i].srcFlags & srcFlags) & kSpotTriggers) != 0) +			return _entries[i]; + +	return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/spot.h b/engines/pegasus/neighborhood/spot.h new file mode 100644 index 0000000000..a985420b7c --- /dev/null +++ b/engines/pegasus/neighborhood/spot.h @@ -0,0 +1,97 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_SPOT_H +#define PEGASUS_NEIGHBORHOOD_SPOT_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { +	class SeekableReadStream; +} + +namespace Pegasus { + +typedef byte SpotFlags; + +enum { +	kSpotLoopsBit, // Loop or once only? +	kSpotOnArrivalBit, +	kSpotOnTurnBit, +	kSpotOnDoorOpenBit +}; + +static const SpotFlags kNoSpotFlags = 0; +static const SpotFlags kSpotLoopsMask = 1 << kSpotLoopsBit; +static const SpotFlags kSpotOnArrivalMask = 1 << kSpotOnArrivalBit; +static const SpotFlags kSpotOnTurnMask = 1 << kSpotOnTurnBit; +static const SpotFlags kSpotOnDoorOpenMask = 1 << kSpotOnDoorOpenBit; + +static const SpotFlags kSpotTriggers = kSpotOnArrivalMask | kSpotOnTurnMask | kSpotOnDoorOpenMask; + +class SpotTable { +public: +	SpotTable() {} +	~SpotTable() {} + +	static uint32 getResTag() { return MKTAG('S', 'p', 'o', 't'); } + +	void loadFromStream(Common::SeekableReadStream *stream); +	void clear(); + +	struct Entry { +		Entry() { clear(); } +		bool isEmpty() { return movieStart == 0xffffffff; } +		void clear() { +			room = kNoRoomID; +			direction = kNoDirection; +			srcFlags = kNoSpotFlags; +			altCode = kNoAlternateID; +			movieStart = 0xffffffff; +			movieEnd = 0xffffffff; +			dstFlags = kNoSpotFlags; +		} + +		RoomID room; +		DirectionConstant direction; +		SpotFlags srcFlags; +		AlternateID altCode; +		TimeValue movieStart; +		TimeValue movieEnd; +		SpotFlags dstFlags; +	}; + +	Entry findEntry(RoomID room, DirectionConstant direction, SpotFlags srcFlags, AlternateID altCode); + +private: +	Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.cpp b/engines/pegasus/neighborhood/tsa/fulltsa.cpp new file mode 100644 index 0000000000..0ed3355a10 --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/fulltsa.cpp @@ -0,0 +1,3017 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +// TSA PICTs: + +static const ResIDType kTBPCloseBoxPICTID = 800; +static const ResIDType kTBPRewindPICTID = 801; +static const ResIDType kUnresolvedPICTID = 802; +static const ResIDType kResolvedPICTID = 803; +static const ResIDType kJumpMenuPICTID = 804; +static const ResIDType kJumpMenuHilitedPICTID = 805; +static const ResIDType kExitPICTID = 806; +static const ResIDType kExitHilitedPICTID = 807; +static const ResIDType kLeftRipPICTID = 808; +static const ResIDType kComparisonCloseBoxPICTID = 809; +static const ResIDType kComparisonLeftRewindPICTID = 810; +static const ResIDType kComparisonRightRewindPICTID = 811; +static const ResIDType kComparisonHiliteNoradPICTID = 812; +static const ResIDType kComparisonHiliteMarsPICTID = 813; +static const ResIDType kComparisonHiliteCaldoriaPICTID = 814; +static const ResIDType kComparisonHiliteWSCPICTID = 815; +static const ResIDType kComparisonChancesNoradPICTID = 816; +static const ResIDType kComparisonChancesMarsPICTID = 817; +static const ResIDType kComparisonChancesCaldoriaPICTID = 818; +static const ResIDType kComparisonChancesWSCPICTID = 819; +static const ResIDType kRedirectionCCRolloverPICTID = 820; +static const ResIDType kRedirectionRRRolloverPICTID = 821; +static const ResIDType kRedirectionFDRolloverPICTID = 822; +static const ResIDType kRedirectionCCDoorPICTID = 823; +static const ResIDType kRedirectionRRDoorPICTID = 824; +static const ResIDType kRedirectionFDDoorPICTID = 825; +static const ResIDType kRedirectionSecuredPICTID = 826; +static const ResIDType kRedirectionNewTargetPICTID = 827; +static const ResIDType kRedirectionClosePICTID = 828; + +static const int16 kCompassShift = 15; + +static const TimeScale kFullTSAMovieScale = 600; +static const TimeScale kFullTSAFramesPerSecond = 15; +static const TimeScale kFullTSAFrameDuration = 40; + +// Alternate IDs. +static const AlternateID kAltTSANormal = 0; +static const AlternateID kAltTSARobotsAtReadyRoom = 1; +static const AlternateID kAltTSARobotsAtFrontDoor = 2; +static const AlternateID kAltTSARedAlert = 3; + +// Room IDs. +static const RoomID kTSA01 = 1; +static const RoomID kTSA02 = 2; +static const RoomID kTSA03 = 3; +static const RoomID kTSA04 = 4; +static const RoomID kTSA05 = 5; +static const RoomID kTSA0A = 6; +static const RoomID kTSA06 = 7; +static const RoomID kTSA07 = 8; +static const RoomID kTSA08 = 9; +static const RoomID kTSA09 = 10; +static const RoomID kTSA10 = 11; +static const RoomID kTSA11 = 12; +static const RoomID kTSA12 = 13; +static const RoomID kTSA13 = 14; +static const RoomID kTSA14 = 15; +static const RoomID kTSA15 = 16; +static const RoomID kTSA16 = 17; +static const RoomID kTSA17 = 18; +static const RoomID kTSA18 = 19; +static const RoomID kTSA19 = 20; +static const RoomID kTSA0B = 21; +static const RoomID kTSA21Cyan = 22; +static const RoomID kTSA22Cyan = 23; +static const RoomID kTSA23Cyan = 24; +static const RoomID kTSA24Cyan = 25; +static const RoomID kTSA25Cyan = 26; +static const RoomID kTSA21Red = 27; +static const RoomID kTSA23Red = 29; +static const RoomID kTSA24Red = 30; +static const RoomID kTSA25Red = 31; +static const RoomID kTSA26 = 32; +static const RoomID kTSA27 = 33; +static const RoomID kTSA28 = 34; +static const RoomID kTSA29 = 35; +static const RoomID kTSA30 = 36; +static const RoomID kTSA31 = 37; +static const RoomID kTSA32 = 38; +static const RoomID kTSA33 = 39; +static const RoomID kTSA34 = 40; +static const RoomID kTSA35 = 41; +static const RoomID kTSADeathRoom = 43; + +// Hot Spot Activation IDs. +static const HotSpotActivationID kActivateTSAReadyForCard = 1; +static const HotSpotActivationID kActivateTSAReadyToTransport = 2; +static const HotSpotActivationID kActivateTSARobotsAwake = 3; +static const HotSpotActivationID kActivateTSA0BZoomedOut = 4; +static const HotSpotActivationID kActivateTSA0BZoomedIn = 5; +static const HotSpotActivationID kActivateTSA0BComparisonVideo = 6; +static const HotSpotActivationID kActivationLogReaderOpen = 7; +static const HotSpotActivationID kActivateTSA0BTBPVideo = 8; +static const HotSpotActivationID kActivationDoesntHaveKey = 9; +static const HotSpotActivationID kActivationKeyVaultOpen = 10; +static const HotSpotActivationID kActivationDoesntHaveChips = 11; +static const HotSpotActivationID kActivationChipVaultOpen = 12; +static const HotSpotActivationID kActivationJumpToPrehistoric = 13; +static const HotSpotActivationID kActivationJumpToNorad = 14; +static const HotSpotActivationID kActivationJumpToMars = 15; +static const HotSpotActivationID kActivationJumpToWSC = 16; +static const HotSpotActivationID kActivationReadyToExit = 17; +static const HotSpotActivationID kActivationReadyForJumpMenu = 18; +static const HotSpotActivationID kActivationMainJumpMenu = 19; + +// Hot Spot IDs. +static const HotSpotID kTSAGTCardDropSpotID = 5000; +static const HotSpotID kTSAGTTokyoSpotID = 5001; +static const HotSpotID kTSAGTCaldoriaSpotID = 5002; +static const HotSpotID kTSAGTBeachSpotID = 5003; +static const HotSpotID kTSAGTOtherSpotID = 5004; +static const HotSpotID kTSA02DoorSpotID = 5005; +static const HotSpotID kTSA03EastJimenezSpotID = 5006; +static const HotSpotID kTSA03WestCrenshawSpotID = 5007; +static const HotSpotID kTSA04EastMatsumotoSpotID = 5008; +static const HotSpotID kTSA04WestCastilleSpotID = 5009; +static const HotSpotID kTSA05EastSinclairSpotID = 5010; +static const HotSpotID kTSA05WestWhiteSpotID = 5011; +static const HotSpotID kTSA0AEastSpotID = 5012; +static const HotSpotID kTSA0AWastSpotID = 5013; +static const HotSpotID kTSA0BEastMonitorSpotID = 5014; +static const HotSpotID kTSA0BEastMonitorOutSpotID = 5015; +static const HotSpotID kTSA0BEastCompareNoradSpotID = 5016; +static const HotSpotID kTSA0BEastCompareMarsSpotID = 5017; +static const HotSpotID kTSA0BEastCompareCaldoriaSpotID = 5018; +static const HotSpotID kTSA0BEastCompareWSCSpotID = 5019; +static const HotSpotID kTSA0BEastLeftRewindSpotID = 5020; +static const HotSpotID kTSA0BEastLeftPlaySpotID = 5021; +static const HotSpotID kTSA0BEastRightRewindSpotID = 5022; +static const HotSpotID kTSA0BEastRightPlaySpotID = 5023; +static const HotSpotID kTSA0BEastCloseVideoSpotID = 5024; +static const HotSpotID kTSA0BNorthMonitorSpotID = 5025; +static const HotSpotID kTSA0BNorthMonitorOutSpotID = 5026; +static const HotSpotID kTSA0BNorthHistLogSpotID = 5027; +static const HotSpotID kTSA0BNorthRobotsToCommandCenterSpotID = 5028; +static const HotSpotID kTSA0BNorthRobotsToReadyRoomSpotID = 5029; +static const HotSpotID kTSA0BNorthRobotsToFrontDoorSpotID = 5030; +static const HotSpotID kTSA0BWestMonitorSpotID = 5031; +static const HotSpotID kTSA0BWestMonitorOutSpotID = 5032; +static const HotSpotID kTSA0BWestTheorySpotID = 5033; +static const HotSpotID kTSA0BWestBackgroundSpotID = 5034; +static const HotSpotID kTSA0BWestProcedureSpotID = 5035; +static const HotSpotID kTSA0BWestCloseVideoSpotID = 5036; +static const HotSpotID kTSA0BWestPlayVideoSpotID = 5037; +static const HotSpotID kTSA0BWestRewindVideoSpotID = 5038; +static const HotSpotID kTSA22EastMonitorSpotID = 5039; +static const HotSpotID kTSA22EastKeySpotID = 5040; +static const HotSpotID kTSA23WestMonitorSpotID = 5041; +static const HotSpotID kTSA23WestChipsSpotID = 5042; +static const HotSpotID kTSA34NorthDoorSpotID = 5043; +static const HotSpotID kTSA37NorthJumpToPrehistoricSpotID = 5044; +static const HotSpotID kTSA37NorthJumpToNoradSpotID = 5045; +static const HotSpotID kTSA37NorthCancelNoradSpotID = 5046; +static const HotSpotID kTSA37NorthJumpToMarsSpotID = 5047; +static const HotSpotID kTSA37NorthCancelMarsSpotID = 5048; +static const HotSpotID kTSA37NorthJumpToWSCSpotID = 5049; +static const HotSpotID kTSA37NorthCancelWSCSpotID = 5050; +static const HotSpotID kTSA37NorthExitSpotID = 5051; +static const HotSpotID kTSA37NorthJumpMenuSpotID = 5052; +static const HotSpotID kTSA37NorthNoradMenuSpotID = 5053; +static const HotSpotID kTSA37NorthMarsMenuSpotID = 5054; +static const HotSpotID kTSA37NorthWSCMenuSpotID = 5055; + +// Extra sequence IDs. +static const ExtraID kTSATransporterArrowLoop = 0; +static const ExtraID kTSAArriveFromCaldoria = 1; +static const ExtraID kTSAGTOtherChoice = 2; +static const ExtraID kTSAGTCardSwipe = 3; +static const ExtraID kTSAGTSelectCaldoria = 4; +static const ExtraID kTSAGTGoToCaldoria = 5; +static const ExtraID kTSAGTSelectBeach = 6; +static const ExtraID kTSAGTGoToBeach = 7; +static const ExtraID kTSAGTArriveAtBeach = 8; +static const ExtraID kTSAGTSelectTokyo = 9; +static const ExtraID kTSAGTGoToTokyo = 10; +static const ExtraID kTSAGTArriveAtTokyo = 11; +static const ExtraID kTSA02NorthZoomIn = 12; +static const ExtraID kTSA02NorthTenSecondDoor = 13; +static const ExtraID kTSA02NorthZoomOut = 14; +static const ExtraID kTSA02NorthDoorWithAgent3 = 15; +static const ExtraID kTSA03JimenezZoomIn = 16; +static const ExtraID kTSA03JimenezSpeech = 17; +static const ExtraID kTSA03JimenezZoomOut = 18; +static const ExtraID kTSA03CrenshawZoomIn = 19; +static const ExtraID kTSA03CrenshawSpeech = 20; +static const ExtraID kTSA03CrenshawZoomOut = 21; +static const ExtraID kTSA03SouthRobotDeath = 22; +static const ExtraID kTSA04NorthRobotGreeting = 23; +static const ExtraID kTSA04MatsumotoZoomIn = 24; +static const ExtraID kTSA04MatsumotoSpeech = 25; +static const ExtraID kTSA04MatsumotoZoomOut = 26; +static const ExtraID kTSA04CastilleZoomIn = 27; +static const ExtraID kTSA04CastilleSpeech = 28; +static const ExtraID kTSA04CastilleZoomOut = 29; +static const ExtraID kTSA05SinclairZoomIn = 30; +static const ExtraID kTSA05SinclairSpeech = 31; +static const ExtraID kTSA05SinclairZoomOut = 32; +static const ExtraID kTSA05WhiteZoomIn = 33; +static const ExtraID kTSA05WhiteSpeech = 34; +static const ExtraID kTSA05WhiteZoomOut = 35; +static const ExtraID kTSA0AEastRobot = 36; +static const ExtraID kTSA0AWestRobot = 37; +static const ExtraID kTSA16NorthRobotDeath = 38; +static const ExtraID kTSA0BEastZoomIn = 39; +static const ExtraID kTSA0BEastZoomedView = 40; +static const ExtraID kTSA0BEastZoomOut = 41; +static const ExtraID kTSA0BEastTurnLeft = 42; +static const ExtraID kTSA0BComparisonStartup = 43; +static const ExtraID kTSA0BComparisonView0000 = 44; +static const ExtraID kTSA0BComparisonView0002 = 45; +static const ExtraID kTSA0BComparisonView0020 = 46; +static const ExtraID kTSA0BComparisonView0022 = 47; +static const ExtraID kTSA0BComparisonView0200 = 48; +static const ExtraID kTSA0BComparisonView0202 = 49; +static const ExtraID kTSA0BComparisonView0220 = 50; +static const ExtraID kTSA0BComparisonView0222 = 51; +static const ExtraID kTSA0BComparisonView2000 = 52; +static const ExtraID kTSA0BComparisonView2002 = 53; +static const ExtraID kTSA0BComparisonView2020 = 54; +static const ExtraID kTSA0BComparisonView2022 = 55; +static const ExtraID kTSA0BComparisonView2200 = 56; +static const ExtraID kTSA0BComparisonView2202 = 57; +static const ExtraID kTSA0BComparisonView2220 = 58; +static const ExtraID kTSA0BComparisonView2222 = 59; +static const ExtraID kTSA0BNoradComparisonView = 60; +static const ExtraID kTSA0BNoradUnaltered = 61; +static const ExtraID kTSA0BNoradAltered = 62; +static const ExtraID kTSA0BMarsComparisonView = 63; +static const ExtraID kTSA0BMarsUnaltered = 64; +static const ExtraID kTSA0BMarsAltered = 65; +static const ExtraID kTSA0BWSCComparisonView = 66; +static const ExtraID kTSA0BWSCUnaltered = 67; +static const ExtraID kTSA0BWSCAltered = 68; +static const ExtraID kTSA0BCaldoriaComparisonView = 69; +static const ExtraID kTSA0BCaldoriaUnaltered = 70; +static const ExtraID kTSA0BCaldoriaAltered = 71; +static const ExtraID kTSA0BNorthZoomIn = 72; +static const ExtraID kTSA0BNorthZoomedView = 73; +static const ExtraID kTSA0BNorthZoomOut = 74; +static const ExtraID kTSA0BNorthTurnLeft = 75; +static const ExtraID kTSA0BNorthTurnRight = 76; +static const ExtraID kTSA0BNorthHistLogOpen = 77; +static const ExtraID kTSA0BNorthHistLogClose = 78; +static const ExtraID kTSA0BNorthHistLogCloseWithLog = 79; +static const ExtraID kTSA0BNorthCantChangeHistory = 80; +static const ExtraID kTSA0BNorthYoureBusted = 81; +static const ExtraID kTSA0BNorthFinallyHappened = 82; +static const ExtraID kTSA0BShowRip1 = 83; +static const ExtraID kTSA0BNorthRipView1 = 84; +static const ExtraID kTSA0BShowRip2 = 85; +static const ExtraID kTSA0BShowGuardRobots = 86; +static const ExtraID kTSA0BAIInterruption = 87; +static const ExtraID kTSA0BRobotsToCommandCenter = 88; +static const ExtraID kTSA0BNorthRobotsAtCCView = 89; +static const ExtraID kTSA0BNorthRobotsAtRRView = 90; +static const ExtraID kTSA0BNorthRobotsAtFDView = 91; +static const ExtraID kTSA0BRobotsFromCommandCenterToReadyRoom = 92; +static const ExtraID kTSA0BRobotsFromReadyRoomToCommandCenter = 93; +static const ExtraID kTSA0BRobotsFromCommandCenterToFrontDoor = 94; +static const ExtraID kTSA0BRobotsFromFrontDoorToCommandCenter = 95; +static const ExtraID kTSA0BRobotsFromFrontDoorToReadyRoom = 96; +static const ExtraID kTSA0BRobotsFromReadyRoomToFrontDoor = 97; +static const ExtraID kTSA0BWestZoomIn = 98; +static const ExtraID kTSA0BWestZoomedView = 99; +static const ExtraID kTSA0BWestZoomOut = 100; +static const ExtraID kTSA0BWestTurnRight = 101; +static const ExtraID kTSA0BTBPTheoryHighlight = 102; +static const ExtraID kTSA0BTBPBackgroundHighlight = 103; +static const ExtraID kTSA0BTBPProcedureHighlight = 104; +static const ExtraID kTSA0BTBPTheory = 105; +static const ExtraID kTSA0BTBPBackground = 106; +static const ExtraID kTSA0BTBPProcedure = 107; +static const ExtraID kTSA0BRipAlarmScreen = 108; +static const ExtraID kTSA22RedEastZoomInSequence = 109; +static const ExtraID kTSA22RedEastVaultViewWithKey = 110; +static const ExtraID kTSA22RedEastVaultViewNoKey = 111; +static const ExtraID kTSA23RedWestVaultZoomInSequence = 112; +static const ExtraID kTSA23RedWestVaultViewWithChips = 113; +static const ExtraID kTSA23RedWestVaultViewNoChips = 114; +static const ExtraID kTSA25NorthDeniedNoKey = 115; +static const ExtraID kTSA25NorthDeniedNoChip = 116; +static const ExtraID kTSA25NorthPutOnSuit = 117; +static const ExtraID kTSA25NorthAlreadyHaveSuit = 118; +static const ExtraID kTSA25NorthDescending1 = 119; +static const ExtraID kTSA25NorthDescending2 = 120; +static const ExtraID kTSA37HorseToAI1 = 121; +static const ExtraID kTSA37PegasusAI1 = 122; +static const ExtraID kTSA37AI1ToCommissioner1 = 123; +static const ExtraID kTSA37Commissioner1 = 124; +static const ExtraID kTSA37Commissioner1ToZoom = 125; +static const ExtraID kTSA37ZoomToPrehistoric = 126; +static const ExtraID kTSA37PrehistoricToAI2 = 127; +static const ExtraID kTSA37PegasusAI2 = 128; +static const ExtraID kTSA37AI2ToPrehistoric = 129; +static const ExtraID kTSA37PrehistoricToDepart = 130; +static const ExtraID kTSA37PegasusDepart = 131; +static const ExtraID kTSA37TimeJumpToPegasus = 132; +static const ExtraID kTSA37RecallToDownload = 133; +static const ExtraID kTSA37DownloadToColonel1 = 134; +static const ExtraID kTSA37Colonel1 = 135; +static const ExtraID kTSA37Colonel1ToReviewRequired = 136; +static const ExtraID kTSA37ReviewRequiredToExit = 137; +static const ExtraID kTSA37ExitHilited = 138; +static const ExtraID kTSA37ExitToHorse = 139; +static const ExtraID kTSA37HorseToColonel2 = 140; +static const ExtraID kTSA37Colonel2 = 141; +static const ExtraID kTSA37PegasusAI3 = 142; +static const ExtraID kTSA37AI3ToHorse = 143; +static const ExtraID kTSA37HorseToZoom = 144; +static const ExtraID kTSA37ZoomToMainMenu = 145; +static const ExtraID kTSA37MainMenuToAI4 = 146; +static const ExtraID kTSA37PegasusAI4 = 147; +static const ExtraID kTSA37AI4ToMainMenu = 148; +static const ExtraID kTSA37JumpMenu000 = 149; +static const ExtraID kTSA37JumpMenu001 = 150; +static const ExtraID kTSA37JumpMenu010 = 151; +static const ExtraID kTSA37JumpMenu011 = 152; +static const ExtraID kTSA37JumpMenu100 = 153; +static const ExtraID kTSA37JumpMenu101 = 154; +static const ExtraID kTSA37JumpMenu110 = 155; +static const ExtraID kTSA37JumpMenu111 = 156; +static const ExtraID kTSA37JumpToWSCMenu = 157; +static const ExtraID kTSA37CancelWSC = 158; +static const ExtraID kTSA37JumpToWSC = 159; +static const ExtraID kTSA37WSCToAI5 = 160; +static const ExtraID kTSA37PegasusAI5 = 161; +static const ExtraID kTSA37AI5ToWSC = 162; +static const ExtraID kTSA37WSCToDepart = 163; +static const ExtraID kTSA37JumpToMarsMenu = 164; +static const ExtraID kTSA37CancelMars = 165; +static const ExtraID kTSA37JumpToMars = 166; +static const ExtraID kTSA37MarsToAI6 = 167; +static const ExtraID kTSA37PegasusAI6 = 168; +static const ExtraID kTSA37AI6ToMars = 169; +static const ExtraID kTSA37MarsToDepart = 170; +static const ExtraID kTSA37JumpToNoradMenu = 171; +static const ExtraID kTSA37CancelNorad = 172; +static const ExtraID kTSA37JumpToNorad = 173; +static const ExtraID kTSA37NoradToAI7 = 174; +static const ExtraID kTSA37PegasusAI7 = 175; +static const ExtraID kTSA37AI7ToNorad = 176; +static const ExtraID kTSA37NoradToDepart = 177; +static const ExtraID kTSA37EnvironmentalScan = 178; +static const ExtraID kTSA37DownloadToMainMenu = 179; +static const ExtraID kTSA37DownloadToOpMemReview = 180; +static const ExtraID kTSA37OpMemReviewToMainMenu = 181; +static const ExtraID kTSA37OpMemReviewToAllClear = 182; +static const ExtraID kTSA37AllClearToCongratulations = 183; +static const ExtraID kTSA37Congratulations = 184; +static const ExtraID kTSA37CongratulationsToExit = 185; + +const DisplayOrder kRipTimerOrder = kMonitorLayer; + + +const CoordType kUnresolvedLeft = kNavAreaLeft + 14; +const CoordType kUnresolvedTop = kNavAreaTop + 236; + +const CoordType kResolvedLeft = kNavAreaLeft + 36; +const CoordType kResolvedTop = kNavAreaTop + 236; + +const CoordType kJumpMenuLeft = kNavAreaLeft + 360; +const CoordType kJumpMenuTop = kNavAreaTop + 202; + +const CoordType kJumpMenuHilitedLeft = kNavAreaLeft + 354; +const CoordType kJumpMenuHilitedTop = kNavAreaTop + 196; + +const CoordType kExitLeft = kNavAreaLeft + 360; +const CoordType kExitTop = kNavAreaTop + 216; + +const CoordType kExitHilitedLeft = kNavAreaLeft + 354; +const CoordType kExitHilitedTop = kNavAreaTop + 210; + +const CoordType kRipTimerLeft = kNavAreaLeft + 95; +const CoordType kRipTimerTop = kNavAreaTop + 87; + +const CoordType kTBPCloseLeft = kNavAreaLeft + 30; +const CoordType kTBPCloseTop = kNavAreaTop + 16; + +const CoordType kTBPRewindLeft = kNavAreaLeft + 86; +const CoordType kTBPRewindTop = kNavAreaTop + 218; + +const CoordType kComparisonCloseLeft = kNavAreaLeft + 50; +const CoordType kComparisonCloseTop = kNavAreaTop + 14; + +const CoordType kComparisonLeftRewindLeft = kNavAreaLeft + 96; +const CoordType kComparisonLeftRewindTop = kNavAreaTop + 190; + +const CoordType kComparisonRightRewindLeft = kNavAreaLeft + 282; +const CoordType kComparisonRightRewindTop = kNavAreaTop + 190; + +const CoordType kComparisonHiliteSpriteLeft = kNavAreaLeft + 45; +const CoordType kComparisonHiliteSpriteTop = kNavAreaTop + 65; + +const CoordType kComparisonHiliteNoradLeft = kNavAreaLeft + 45; +const CoordType kComparisonHiliteNoradTop = kNavAreaTop + 65; + +const CoordType kComparisonHiliteMarsLeft = kNavAreaLeft + 45 + 4; +const CoordType kComparisonHiliteMarsTop = kNavAreaTop + 65 + 23; + +const CoordType kComparisonHiliteCaldoriaLeft = kNavAreaLeft + 45 + 7; +const CoordType kComparisonHiliteCaldoriaTop = kNavAreaTop + 65 + 46; + +const CoordType kComparisonHiliteWSCLeft = kNavAreaLeft + 45 + 11; +const CoordType kComparisonHiliteWSCTop = kNavAreaTop + 65 + 68; + +const CoordType kComparisonChancesSpriteLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesSpriteTop = kNavAreaTop + 162; + +const CoordType kComparisonChancesNoradLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesNoradTop = kNavAreaTop + 162; + +const CoordType kComparisonChancesMarsLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesMarsTop = kNavAreaTop + 162; + +const CoordType kComparisonChancesCaldoriaLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesCaldoriaTop = kNavAreaTop + 162 + 1; + +const CoordType kComparisonChancesWSCLeft = kNavAreaLeft + 148; +const CoordType kComparisonChancesWSCTop = kNavAreaTop + 162; + +const CoordType kRedirectionSprite1Left = kNavAreaLeft + 58; +const CoordType kRedirectionSprite1Top = kNavAreaTop + 16; + +const CoordType kRedirectionSprite2Left = kNavAreaLeft + 36; +const CoordType kRedirectionSprite2Top = kNavAreaTop + 166; + +const CoordType kRedirectionCCRolloverLeft = kNavAreaLeft + 58; +const CoordType kRedirectionCCRolloverTop = kNavAreaTop + 16; + +const CoordType kRedirectionRRRolloverLeft = kNavAreaLeft + 430; +const CoordType kRedirectionRRRolloverTop = kNavAreaTop + 30; + +const CoordType kRedirectionFDRolloverLeft = kNavAreaLeft + 278; +const CoordType kRedirectionFDRolloverTop = kNavAreaTop + 160; + +const CoordType kRedirectionCCDoorLeft = kNavAreaLeft + 174; +const CoordType kRedirectionCCDoorTop = kNavAreaTop + 36; + +const CoordType kRedirectionRRDoorLeft = kNavAreaLeft + 418; +const CoordType kRedirectionRRDoorTop = kNavAreaTop + 32; + +const CoordType kRedirectionFDDoorLeft = kNavAreaLeft + 298; +const CoordType kRedirectionFDDoorTop = kNavAreaTop + 240; + +const CoordType kRedirectionSecuredLeft = kNavAreaLeft + 36; +const CoordType kRedirectionSecuredTop = kNavAreaTop + 166; + +const CoordType kRedirectionNewTargetLeft = kNavAreaLeft + 36; +const CoordType kRedirectionNewTargetTop = kNavAreaTop + 166; + +const CoordType kRedirectionCloseLeft = kNavAreaLeft + 56; +const CoordType kRedirectionCloseTop = kNavAreaTop + 220; + +static const TimeValue kTSABumpIntoWallIn = 0; +static const TimeValue kTSABumpIntoWallOut = 148; + +static const TimeValue kTSAGTDoorCloseIn = 148; +static const TimeValue kTSAGTDoorCloseOut = 1570; + +static const TimeValue kTSANoOtherDestinationIn = 1570; +static const TimeValue kTSANoOtherDestinationOut = 3601; + +static const TimeValue kTSAEntryDoorCloseIn = 3601; +static const TimeValue kTSAEntryDoorCloseOut = 4200; + +static const TimeValue kTSAInsideDoorCloseIn = 4200; +static const TimeValue kTSAInsideDoorCloseOut = 4800; + +static const TimeValue kTSAVaultCloseIn = 4800; +static const TimeValue kTSAVaultCloseOut = 5388; + +static const TimeValue kTSAPegasusDoorCloseIn = 5388; +static const TimeValue kTSAPegasusDoorCloseOut = 6457; + +static const bool kPegasusUnresolved = false; +static const bool kPegasusResolved = true; +static const bool kPegasusCantExit = false; +static const bool kPegasusCanExit = true; + +// Monitor modes +enum { +	kMonitorNeutral = 0, +	kMonitorTheory = 1, +	kMonitorProcedure = 2, +	kMonitorBackground = 3, +	kMonitorNoradComparison = 4, +	kMonitorMarsComparison = 5, +	kMonitorCaldoriaComparison = 6, +	kMonitorWSCComparison = 7, +	 +	kRawModeMask = 0x0F, +	kPlayingTBPMask = 0x10, +	kPlayingLeftComparisonMask = 0x20, +	kPlayingRightComparisonMask = 0x40, +	 +	kPlayingAnyMask = kPlayingTBPMask | +						kPlayingLeftComparisonMask | +						kPlayingRightComparisonMask, +	 +	kMonitorPlayingTheory = kMonitorTheory | kPlayingTBPMask, +	kMonitorPlayingProcedure = kMonitorProcedure | kPlayingTBPMask, +	kMonitorPlayingBackground = kMonitorBackground | kPlayingTBPMask, +	 +	kMonitorPlayingLeftNoradComparison = kMonitorNoradComparison | +											kPlayingLeftComparisonMask, +	kMonitorPlayingRightNoradComparison = kMonitorNoradComparison | +											kPlayingRightComparisonMask, +	kMonitorPlayingLeftMarsComparison = kMonitorMarsComparison | +										kPlayingLeftComparisonMask, +	kMonitorPlayingRightMarsComparison = kMonitorMarsComparison | +											kPlayingRightComparisonMask, +	kMonitorPlayingLeftCaldoriaComparison = kMonitorCaldoriaComparison | +											kPlayingLeftComparisonMask, +	kMonitorPlayingRightCaldoriaComparison = kMonitorCaldoriaComparison | +												kPlayingRightComparisonMask, +	kMonitorPlayingLeftWSCComparison = kMonitorWSCComparison | +										kPlayingLeftComparisonMask, +	kMonitorPlayingRightWSCComparison = kMonitorWSCComparison | +										kPlayingRightComparisonMask +}; + +static const ExtraID s_historicalLogViews[16] = { +	kTSA0BComparisonView0000, +	kTSA0BComparisonView0002, +	kTSA0BComparisonView0020, +	kTSA0BComparisonView0022, +	kTSA0BComparisonView0200, +	kTSA0BComparisonView0202, +	kTSA0BComparisonView0220, +	kTSA0BComparisonView0222, +	kTSA0BComparisonView2000, +	kTSA0BComparisonView2002, +	kTSA0BComparisonView2020, +	kTSA0BComparisonView2022, +	kTSA0BComparisonView2200, +	kTSA0BComparisonView2202, +	kTSA0BComparisonView2220, +	kTSA0BComparisonView2222 +}; + +static const int kRedirectionCCRolloverSprite = 0; +static const int kRedirectionRRRolloverSprite = 1; +static const int kRedirectionFDRolloverSprite = 2; +static const int kRedirectionCCDoorSprite = 3; +static const int kRedirectionRRDoorSprite = 4; +static const int kRedirectionFDDoorSprite = 5; +static const int kRedirectionCloseSprite = 6; +static const int kRedirectionSecuredSprite = 0; +static const int kRedirectionNewTargetSprite = 1; + +void RipTimer::initImage() { +	_middle = -1; +	 +	_timerImage.getImageFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLeftRipPICTID); + +	Common::Rect r; +	_timerImage.getSurfaceBounds(r); +	setBounds(r); +} + +void RipTimer::releaseImage() { +	_timerImage.deallocateSurface(); +} + +void RipTimer::draw(const Common::Rect &updateRect) { +	Common::Rect bounds; +	getBounds(bounds); + +	Common::Rect r1 = bounds; +	r1.right = _middle; +	r1 = updateRect.findIntersectingRect(r1); + +	if (!r1.isEmpty()) { +		Common::Rect r2 = r1; +		r2.moveTo(r1.left - _bounds.left, r1.top - bounds.top); +		_timerImage.copyToCurrentPort(r2, r1); +	} +} + +void RipTimer::timeChanged(const TimeValue newTime) { +	Common::Rect bounds; +	getBounds(bounds); + +	CoordType newMiddle = bounds.left + bounds.width() * newTime / getDuration(); + +	if (newMiddle != _middle) { +		_middle = newMiddle; +		triggerRedraw(); +	} + +	if (newTime == getStop()) +		((PegasusEngine *)g_engine)->die(kDeathUncreatedInTSA); +} + +FullTSA::FullTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Full TSA", kFullTSAID), +		_ripTimer(kNoDisplayElement), _sprite1(kNoDisplayElement), _sprite2(kNoDisplayElement), _sprite3(kNoDisplayElement) { +	setIsItemTaken(kJourneymanKey); +	setIsItemTaken(kPegasusBiochip); +	setIsItemTaken(kMapBiochip); +} + +void FullTSA::init() {	 +	Neighborhood::init(); +	_ripTimer.setDisplayOrder(kRipTimerOrder); +	_ripTimer.startDisplaying(); + +	if (!GameState.getTSASeenRobotGreeting()) +		forceStridingStop(kTSA03, kNorth, kNoAlternateID); + +	_sprite1.setDisplayOrder(kMonitorLayer); +	_sprite1.startDisplaying(); +	_sprite2.setDisplayOrder(kMonitorLayer); +	_sprite2.startDisplaying(); +	_sprite3.setDisplayOrder(kMonitorLayer); +	_sprite3.startDisplaying(); +	 +	// Fix a mistake in the world builder tables. +	HotspotInfoTable::Entry *entry = findHotspotEntry(kTSA23WestChipsSpotID); +	entry->hotspotItem = kPegasusBiochip; +} + +void uncreatedInTSAFunction(FunctionPtr *, void *tsa) { +	((FullTSA *)tsa)->die(kDeathUncreatedInTSA); +} + +void FullTSA::start() { +	g_energyMonitor->stopEnergyDraining(); + +	if (!GameState.getScoringEnterTSA()) { +		_utilityFuse.primeFuse(GameState.getTSAFuseTimeLimit()); +		_utilityFuse.setFunctionPtr(&uncreatedInTSAFunction, (void *)this); +		_utilityFuse.lightFuse(); +	} else if (GameState.getTSAState() == kTSAPlayerDetectedRip || GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) { +		_ripTimer.initImage(); +		_ripTimer.moveElementTo(kRipTimerLeft, kRipTimerTop); +		_ripTimer.setSegment(0, kRipTimeLimit, kRipTimeScale); +		_ripTimer.setTime(GameState.getRipTimerTime()); +		_ripTimer.start(); +	} + +	Neighborhood::start(); +} + +void FullTSA::flushGameState() { +	GameState.setRipTimerTime(_ripTimer.getTime()); +	GameState.setTSAFuseTimeLimit(_utilityFuse.getTimeRemaining()); +} + +Common::String FullTSA::getBriefingMovie() {	 +	Common::String movieName = Neighborhood::getBriefingMovie(); + +	if (movieName.empty()) { +		RoomID room = GameState.getCurrentRoom(); + +		switch (GameState.getTSAState()) { +		case kTSAPlayerNotArrived: +		case kTSAPlayerForcedReview: +			if (room >= kTSA16 && room <= kTSA0B) +				return "Images/AI/TSA/XT01A"; + +			return "Images/AI/TSA/XT01"; +		case kTSAPlayerDetectedRip: +		case kTSAPlayerNeedsHistoricalLog: +			return "Images/AI/TSA/XT02"; +		case kTSAPlayerGotHistoricalLog: +		case kTSAPlayerInstalledHistoricalLog: +			return "Images/AI/TSA/XT03"; +		default: +			switch (getCurrentActivation()) { +			case kActivationJumpToPrehistoric: +				g_AIChip->showBriefingClicked(); +				startExtraSequenceSync(kTSA37PegasusAI2, kHintInterruption); +				startExtraSequenceSync(kTSA37AI2ToPrehistoric, kFilterNoInput); +				g_AIChip->clearClicked(); +				break; +			case kActivationJumpToNorad: +				g_AIChip->showBriefingClicked(); +				startExtraSequenceSync(kTSA37PegasusAI7, kHintInterruption); +				startExtraSequenceSync(kTSA37AI7ToNorad, kFilterNoInput); +				g_AIChip->clearClicked(); +				break; +			case kActivationJumpToMars: +				g_AIChip->showBriefingClicked(); +				startExtraSequenceSync(kTSA37PegasusAI6, kHintInterruption); +				startExtraSequenceSync(kTSA37AI6ToMars, kFilterNoInput); +				g_AIChip->clearClicked(); +				break; +			case kActivationJumpToWSC: +				g_AIChip->showBriefingClicked(); +				startExtraSequenceSync(kTSA37PegasusAI5, kHintInterruption); +				startExtraSequenceSync(kTSA37AI5ToWSC, kFilterNoInput); +				g_AIChip->clearClicked(); +				break; +			default: +				if (GameState.allTimeZonesFinished()) +					return "Images/AI/TSA/XT05"; + +				return "Images/AI/TSA/XT04"; +			} +			break; +		} +	} + +	return movieName; +} + +Common::String FullTSA::getEnvScanMovie() { +	Common::String movieName = Neighborhood::getEnvScanMovie(); + +	if (movieName.empty()) { +		switch (GameState.getTSAState()) { +		case kTSAPlayerNotArrived: +		case kTSAPlayerForcedReview: +		case kTSAPlayerDetectedRip: +		case kTSAPlayerNeedsHistoricalLog: +			return "Images/AI/TSA/XTE1"; +		default: +			if (GameState.getCurrentRoom() == kTSA37) { +				g_AIChip->showEnvScanClicked(); +				startExtraSequenceSync(kTSA37EnvironmentalScan, kHintInterruption); +	 +				switch (getCurrentActivation()) { +				case kActivationJumpToPrehistoric: +					startExtraSequenceSync(kTSA37AI2ToPrehistoric, kFilterNoInput); +					break; +				case kActivationJumpToNorad: +					startExtraSequenceSync(kTSA37AI7ToNorad, kFilterNoInput); +					showExtraView(kTSA37JumpToNoradMenu); +					break; +				case kActivationJumpToMars: +					startExtraSequenceSync(kTSA37AI6ToMars, kFilterNoInput); +					showExtraView(kTSA37JumpToMarsMenu); +					break; +				case kActivationJumpToWSC: +					startExtraSequenceSync(kTSA37AI5ToWSC, kFilterNoInput); +					showExtraView(kTSA37JumpToWSCMenu); +					break; +				default: +					startExtraSequenceSync(kTSA37AI4ToMainMenu, kFilterNoInput); +					break; +				} + +				g_AIChip->clearClicked(); +			} else if (GameState.allTimeZonesFinished()) { +				return "Images/AI/TSA/XTE1"; +			} else { +				return "Images/AI/TSA/XTE2"; +			} +			break; +		} +	} + +	return movieName; +} + +uint FullTSA::getNumHints() { +	uint numHints = Neighborhood::getNumHints(); + +	if (numHints == 0) { +		switch (GameState.getTSAState()) { +		case kRobotsAtCommandCenter: +		case kRobotsAtFrontDoor: +		case kRobotsAtReadyRoom: +			if (GameState.getCurrentRoom() == kTSA0B && GameState.getTSA0BZoomedIn()) +				numHints = 3; +			break; +		} +	} + +	return numHints; +} + +Common::String FullTSA::getHintMovie(uint hintNum) { +	Common::String movieName = Neighborhood::getHintMovie(hintNum); + +	if (movieName.empty()) +		movieName = Common::String::format("Images/AI/TSA/XT20NH%d", hintNum); +		 +	return movieName; +} + +void FullTSA::loadAmbientLoops() { +	RoomID room = GameState.getCurrentRoom(); + +	switch (GameState.getTSAState()) { +	case kTSAPlayerDetectedRip: +	case kTSAPlayerNeedsHistoricalLog: +		if ((room >= kTSA16 && room <= kTSA0B) || (room >= kTSA21Cyan && room <= kTSA24Cyan) || (room >= kTSA21Red && room <= kTSA24Red)) +			loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 4, 0, 0); +		else if (room == kTSA25Cyan || room == kTSA25Red) +			loadLoopSound1("Sounds/TSA/TSA CLAXON.22K.AIFF", 0x100 / 6, 0, 0); +		else +			loadLoopSound1("Sounds/TSA/TSA EchoClaxon.22K.AIFF", 0x100 / 4, 0, 0); +		break; +	default: +		if (room >= kTSA00 && room <= kTSA02) +			loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); +		else if (room >= kTSA03 && room <= kTSA15) +			loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); +		else if (room >= kTSA16 && room <= kTSA0B) +			loadLoopSound1("Sounds/TSA/T14SAEO1.22K.AIFF"); +		else if (room >= kTSA21Cyan && room <= kTSA25Red) +			loadLoopSound1("Sounds/TSA/T15SAE01.22K.AIFF"); +		else if (room >= kTSA26 && room <= kTSA37) +			loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); +		break; +	} +} + +short FullTSA::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { +	int16 result = Neighborhood::getStaticCompassAngle(room, dir); + +	switch (room) { +	case kTSA08: +		result += kCompassShift; +		break; +	case kTSA09: +		result -= kCompassShift; +		break; +	case kTSA10: +		result += kCompassShift * 2; +		break; +	case kTSA11: +	case kTSA22Cyan: +	case kTSA22Red: +		result -= kCompassShift * 2; +		break; +	case kTSA12: +		result += kCompassShift * 3; +		break; +	case kTSA13: +		result -= kCompassShift * 3; +		break; +	case kTSA14: +	case kTSA16: +	case kTSA17: +	case kTSA18: +	case kTSA19: +		result += kCompassShift * 4; +		break; +	case kTSA0B: +		result += kCompassShift * 4; + +		if (dir == kWest) +			result += 30; +		else if (dir == kEast) +			result -= 30; +		break; +	case kTSA33: +		result += kCompassShift * 4; +		break; +	case kTSA15: +	case kTSA21Cyan: +	case kTSA24Cyan: +	case kTSA25Cyan: +	case kTSA21Red: +	case kTSA24Red: +	case kTSA25Red: +	case kTSA26: +	case kTSA27: +	case kTSA28: +	case kTSA29: +	case kTSA30: +		result -= kCompassShift * 4; +		break; +	case kTSA23Cyan: +	case kTSA23Red: +		result -= kCompassShift * 6; +		break; +	case kTSA32: +		result -= kCompassShift * 8; +		break; +	case kTSA34: +		result -= kCompassShift * 12; +		break; +	case kTSA35: +		result += kCompassShift * 8; +		break; +	case kTSA37: +		result -= kCompassShift * 2; +		break; +	} + +	return result; +} + +void FullTSA::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { +	Neighborhood::getExitCompassMove(exitEntry, compassMove); + +	switch (MakeRoomView(exitEntry.room, exitEntry.direction)) { +	case MakeRoomView(kTSA01, kSouth): +		compassMove.insertFaderKnot(exitEntry.movieStart, -180); +		compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 3, -180); +		compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 33, +				getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); +		break; +	case MakeRoomView(kTSA11, kEast): +		if (getCurrentAlternate() == kAltTSARobotsAtReadyRoom) { +			compassMove.makeTwoKnotFaderSpec(kFullTSAMovieScale, exitEntry.movieStart, +					getStaticCompassAngle(kTSA11, kEast), exitEntry.movieEnd, +					getStaticCompassAngle(kTSA13, kEast)); +			compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 13, compassMove.getNthKnotValue(1)); +		} +		break; +	case MakeRoomView(kTSA34, kNorth): +		compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 48, +				getStaticCompassAngle(exitEntry.room, exitEntry.direction)); +		compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 68, +				getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); +		break; +	case MakeRoomView(kTSA37, kNorth): +		compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 38, +				getStaticCompassAngle(exitEntry.room, exitEntry.direction)); +		compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 64, +				getStaticCompassAngle(exitEntry.room, exitEntry.direction) + kCompassShift * 3 / 2); +		compassMove.insertFaderKnot(exitEntry.movieStart + kFullTSAFrameDuration * 105, +				getStaticCompassAngle(exitEntry.exitRoom, exitEntry.exitDirection)); +		break; +	} +} + +void FullTSA::getExtraCompassMove(const ExtraTable::Entry &extraEntry, FaderMoveSpec &compassMove) { +	int16 angle; +	 +	switch (extraEntry.extra) { +	case kTSA0BEastTurnLeft: +	case kTSA0BNorthTurnLeft: +		angle =getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, +				extraEntry.movieEnd, angle - 60); +		break; +	case kTSA0BNorthTurnRight: +	case kTSA0BWestTurnRight: +		angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, +				extraEntry.movieEnd, angle + 60); +		break; +	case kTSA22RedEastZoomInSequence: +		angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, +				extraEntry.movieEnd, angle); +		compassMove.insertFaderKnot(extraEntry.movieStart + 1200, angle - kCompassShift * 2); +		compassMove.insertFaderKnot(extraEntry.movieStart + 8160, angle - kCompassShift * 2); +		compassMove.insertFaderKnot(extraEntry.movieStart + 9840, angle); +		break; +	case kTSA23RedWestVaultZoomInSequence: +		angle = getStaticCompassAngle(GameState.getCurrentRoom(), GameState.getCurrentDirection()); +		compassMove.makeTwoKnotFaderSpec(_navMovie.getScale(), extraEntry.movieStart, angle, +				extraEntry.movieEnd, angle); +		compassMove.insertFaderKnot(extraEntry.movieStart + 1200, angle - kCompassShift * 2); +		compassMove.insertFaderKnot(extraEntry.movieStart + 10100, angle - kCompassShift * 2); +		compassMove.insertFaderKnot(extraEntry.movieStart + 11880, angle); +		break; +	default: +		Neighborhood::getExtraCompassMove(extraEntry, compassMove); +		break; +	} +} + +uint16 FullTSA::getDateResID() const { +	return kDate2318ID; +} + +TimeValue FullTSA::getViewTime(const RoomID room, const DirectionConstant direction) { +	ExtraID extraID = 0xffffffff; +	 +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kTSA0B, kEast): +		if (GameState.getTSA0BZoomedIn()) +			switch (GameState.getTSAState()) { +			case kTSAPlayerInstalledHistoricalLog: +			case kTSABossSawHistoricalLog: +			case kRobotsAtCommandCenter: +			case kRobotsAtFrontDoor: +			case kRobotsAtReadyRoom: +				extraID = s_historicalLogViews[getHistoricalLogIndex()]; +				break; +			default: +				extraID = kTSA0BEastZoomedView; +				break; +			} +		break; +	case MakeRoomView(kTSA0B, kNorth): +		if (GameState.getTSA0BZoomedIn()) +			switch (GameState.getTSAState()) { +			case kTSAPlayerNeedsHistoricalLog: +				extraID = kTSA0BNorthRipView1; +				break; +			default: +				extraID = kTSA0BNorthZoomedView; +				break; +			} +		break; +	case MakeRoomView(kTSA0B, kWest): +		if (GameState.getTSA0BZoomedIn()) +			extraID = kTSA0BWestZoomedView; +		break; +	case MakeRoomView(kTSA22Red, kEast): +		if (_privateFlags.getFlag(kTSAPrivateKeyVaultOpenFlag)) { +			if (_vm->itemInLocation(kJourneymanKey, kFullTSAID, kTSA22Red, kEast)) +				extraID = kTSA22RedEastVaultViewWithKey; +			else +				extraID = kTSA22RedEastVaultViewNoKey; +		} +		break; +	case MakeRoomView(kTSA23Red, kWest): +		if (_privateFlags.getFlag(kTSAPrivateChipVaultOpenFlag)) { +			if (_vm->itemInLocation(kPegasusBiochip, kFullTSAID, kTSA23Red, kWest)) +				extraID = kTSA23RedWestVaultViewWithChips; +			else +				extraID = kTSA23RedWestVaultViewNoChips; +		} +		break; +	case MakeRoomView(kTSA37, kNorth): +		switch (GameState.getTSAState()) { +		case kTSAPlayerGotHistoricalLog: +			extraID = kTSA37ReviewRequiredToExit; +			break; +		case kPlayerFinishedWithTSA: +			extraID = kTSA37CongratulationsToExit; +			break; +		default: +			extraID = kTSA37AI3ToHorse; +			break; +		} +		break; +	} + +	if (extraID != 0xffffffff) { +		ExtraTable::Entry entry; +		getExtraEntry(extraID, entry); +		return entry.movieEnd - 1; +	} + +	return Neighborhood::getViewTime(room, direction); +} + +void FullTSA::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &entry) { +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kTSA0B, kNorth): +	case MakeRoomView(kTSA0B, kEast): +	case MakeRoomView(kTSA0B, kWest): +		if (!GameState.getTSA0BZoomedIn()) +			Neighborhood::findSpotEntry(room, direction, flags, entry); +		break; +	default: +		Neighborhood::findSpotEntry(room, direction, flags, entry); +		break; +	} +} + +void FullTSA::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) { +	Neighborhood::getExtraEntry(id, extraEntry); + +	if (id == kTSA0BShowGuardRobots) +		extraEntry.movieStart += kFullTSAFrameDuration * 3; +} + +void FullTSA::pickedUpItem(Item *item) { +	BiochipItem *biochip; +	 +	switch (item->getObjectID()) { +	case kJourneymanKey: +		GameState.setScoringGotJourneymanKey(true); +		break; +	case kPegasusBiochip: +		biochip = (BiochipItem *)g_allItems.findItemByID(kMapBiochip); +		_vm->addItemToBiochips(biochip); +		GameState.setScoringGotPegasusBiochip(true); +		break; +	} +} + +void FullTSA::playExtraMovie(const ExtraTable::Entry &extraEntry, const NotificationFlags flags, const InputBits interruptionInput) { +	switch (extraEntry.extra) { +	case kTSA0BNorthZoomIn: +		if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) { +			_privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); +			requestExtraSequence(kTSA0BNorthHistLogClose, 0, kFilterNoInput); +			requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); +		} else { +			Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); +		} +		break; +	case kTSA0BNorthZoomOut: +		if (_ripTimer.isVisible()) +			_ripTimer.hide(); + +		shutDownRobotMonitor(); +		Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); +		break; +	case kTSA0BEastZoomOut: +		shutDownComparisonMonitor(); +		Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); +		break; +	default: +		Neighborhood::playExtraMovie(extraEntry, flags, interruptionInput); +		break; +	} +} + +void FullTSA::startDoorOpenMovie(const TimeValue startTime, const TimeValue stopTime) { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kTSA00, kNorth): +		if (GameState.getLastNeighborhood() != kFullTSAID) { +			startExtraSequence(kTSAArriveFromCaldoria, kDoorOpenCompletedFlag, kFilterNoInput); +			return; +		} +		break; +	case MakeRoomView(kTSA02, kNorth): +		if (!GameState.getTSAIDedAtDoor()) { +			GameState.setTSAIDedAtDoor(true); +			requestExtraSequence(kTSA02NorthZoomIn, 0, kFilterNoInput); +			requestExtraSequence(kTSA02NorthTenSecondDoor, 0, kFilterNoInput); + +			if (GameState.getTSASeenAgent3AtDoor()) { +				requestExtraSequence(kTSA02NorthZoomOut, kExtraCompletedFlag, kFilterNoInput); +			} else { +				GameState.setTSASeenAgent3AtDoor(true); +				requestExtraSequence(kTSA02NorthZoomOut, 0, kFilterNoInput); +				requestExtraSequence(kTSA02NorthDoorWithAgent3, kDoorOpenCompletedFlag, kFilterNoInput); +			} +			return; +		} +		break; +	case MakeRoomView(kTSA03, kSouth): +		if (GameState.getTSAState() == kRobotsAtFrontDoor) { +			playDeathExtra(kTSA03SouthRobotDeath, kDeathShotByTSARobots); +			return; +		} +		break; +	case MakeRoomView(kTSA16, kNorth): +		if (GameState.getTSAState() == kRobotsAtCommandCenter) { +			playDeathExtra(kTSA16NorthRobotDeath, kDeathShotByTSARobots); +			return; +		} +		break; +	} + +	Neighborhood::startDoorOpenMovie(startTime, stopTime); +} + +InputBits FullTSA::getInputFilter() { +	InputBits result = Neighborhood::getInputFilter(); + +	switch (GameState.getCurrentRoom()) { +	case kTSA0B: +		if (GameState.getT0BMonitorMode() != kMonitorNeutral) +			// Only allow a click. +			result &= JMPPPInput::getClickInputFilter(); +		break; +	case kTSA37: +		// Can't move forward in Pegasus. Only press the exit button. +		result &= ~(kFilterUpButton | kFilterUpAuto); +		break; +	} + +	return result; +} + +void FullTSA::turnLeft() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kTSA15, kNorth): +		if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) +			setCurrentAlternate(kAltTSANormal); +		break; +	case MakeRoomView(kTSA0B, kNorth): +		if (_ripTimer.isVisible()) +			_ripTimer.hide(); +		releaseSprites(); +		break; +	case MakeRoomView(kTSA0B, kEast): +		shutDownComparisonMonitor(); +		break; +	} + +	Neighborhood::turnLeft(); +} + +void FullTSA::turnRight() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kTSA15, kSouth): +		if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog) +			setCurrentAlternate(kAltTSANormal); +		break; +	case MakeRoomView(kTSA0B, kNorth): +		if (_ripTimer.isVisible()) +			_ripTimer.hide(); +		releaseSprites(); +		break; +	case MakeRoomView(kTSA0B, kEast): +		shutDownComparisonMonitor(); +		break; +	} + +	Neighborhood::turnRight(); +} + +void FullTSA::openDoor() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kTSA15, kSouth): +		if (GameState.getTSAState() == kTSAPlayerNeedsHistoricalLog || GameState.getTSAState() == kRobotsAtFrontDoor) +			setCurrentAlternate(kAltTSARedAlert); +		break; +	} + +	Neighborhood::openDoor(); +} + +CanMoveForwardReason FullTSA::canMoveForward(ExitTable::Entry &entry) { +	if (GameState.getCurrentRoomAndView() == MakeRoomView(kTSA25Red, kNorth)) +		return kCantMoveBlocked; + +	return Neighborhood::canMoveForward(entry); +} + +CanOpenDoorReason FullTSA::canOpenDoor(DoorTable::Entry &entry) { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kTSA02, kNorth): +		if (!GameState.getTSAFrontDoorUnlockedOutside()) +			return kCantOpenLocked; +		break; +	case MakeRoomView(kTSA03, kSouth): +		if (!GameState.getTSAFrontDoorUnlockedInside()) +			return kCantOpenLocked; +		break; +	case MakeRoomView(kTSA16, kNorth): +		if (GameState.getTSACommandCenterLocked()) +			return kCantOpenLocked; +		break; +	} + +	return Neighborhood::canOpenDoor(entry); +} + +void FullTSA::bumpIntoWall() { +	requestSpotSound(kTSABumpIntoWallIn, kTSABumpIntoWallOut, kFilterAllInput, 0); +	Neighborhood::bumpIntoWall(); +} + +void FullTSA::downButton(const Input &input) { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kTSA0B, kEast): +		if (GameState.getTSA0BZoomedIn()) +			startExtraSequence(kTSA0BEastZoomOut, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kTSA0B, kNorth): +		if (GameState.getTSA0BZoomedIn()) +			startExtraSequence(kTSA0BNorthZoomOut, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kTSA0B, kWest): +		if (GameState.getTSA0BZoomedIn() && GameState.getT0BMonitorMode() == kMonitorNeutral) +			startExtraSequence(kTSA0BWestZoomOut, kExtraCompletedFlag, kFilterNoInput); +		break; +	default: +		Neighborhood::downButton(input); +	} +} + +void FullTSA::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *spot) { +	switch (spot->getObjectID()) { +	case kTSA0BEastLeftRewindSpotID: +	case kTSA0BEastLeftPlaySpotID: +		if (_privateFlags.getFlag(kTSAPrivatePlayingRightComparisonFlag)) +			spot->setInactive(); +		else +			Neighborhood::activateOneHotspot(entry, spot); +		break; +	case kTSA0BEastRightRewindSpotID: +	case kTSA0BEastRightPlaySpotID: +		if (_privateFlags.getFlag(kTSAPrivatePlayingLeftComparisonFlag)) +			spot->setInactive(); +		else +			Neighborhood::activateOneHotspot(entry, spot); +		break; +	default: +		Neighborhood::activateOneHotspot(entry, spot); +		break; +	} +} + +void FullTSA::activateHotspots() { +	Neighborhood::activateHotspots(); + +	switch (MakeRoomView(GameState.getCurrentRoom(), GameState.getCurrentDirection())) { +	case MakeRoomView(kTSA02, kNorth): +		if (!GameState.getTSAFrontDoorUnlockedOutside()) +			g_allHotspots.activateOneHotspot(kTSA02DoorSpotID); +		break; +	case MakeRoomView(kTSA0B, kEast): +		if (GameState.getTSA0BZoomedIn()) +			switch (GameState.getTSAState()) { +			case kTSAPlayerInstalledHistoricalLog: +			case kTSABossSawHistoricalLog: +			case kRobotsAtCommandCenter: +			case kRobotsAtFrontDoor: +			case kRobotsAtReadyRoom: +				if (getCurrentActivation() != kActivateTSA0BComparisonVideo) { +					g_allHotspots.activateOneHotspot(kTSA0BEastCompareNoradSpotID); +					g_allHotspots.activateOneHotspot(kTSA0BEastCompareMarsSpotID); +					g_allHotspots.activateOneHotspot(kTSA0BEastCompareCaldoriaSpotID); +					g_allHotspots.activateOneHotspot(kTSA0BEastCompareWSCSpotID); +				} +				break; +			} +		break; +	case MakeRoomView(kTSA0B, kNorth): +		if (GameState.getTSA0BZoomedIn()) +			switch (GameState.getTSAState()) { +			case kRobotsAtCommandCenter: +			case kRobotsAtFrontDoor: +			case kRobotsAtReadyRoom: +				g_allHotspots.activateOneHotspot(kTSA0BNorthRobotsToCommandCenterSpotID); +				g_allHotspots.activateOneHotspot(kTSA0BNorthRobotsToReadyRoomSpotID); +				g_allHotspots.activateOneHotspot(kTSA0BNorthRobotsToFrontDoorSpotID); +				break; +			} +		break; +	} +} + +void FullTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) {	 +	switch (clickedSpot->getObjectID()) { +	case kTSAGTOtherSpotID: +		showExtraView(kTSAGTOtherChoice); +		playSpotSoundSync(kTSANoOtherDestinationIn, kTSANoOtherDestinationOut); +		showExtraView(kTSAGTCardSwipe); +		break; +	case kTSA02DoorSpotID: +		GameState.setTSAFrontDoorUnlockedOutside(true); +		Neighborhood::clickInHotspot(input, clickedSpot); +		break; +	case kTSA03EastJimenezSpotID: +		startExtraLongSequence(kTSA03JimenezZoomIn, kTSA03JimenezZoomOut, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA03WestCrenshawSpotID: +		startExtraLongSequence(kTSA03CrenshawZoomIn, kTSA03CrenshawZoomOut, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA04EastMatsumotoSpotID: +		startExtraLongSequence(kTSA04MatsumotoZoomIn, kTSA04MatsumotoZoomOut, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA04WestCastilleSpotID: +		startExtraLongSequence(kTSA04CastilleZoomIn, kTSA04CastilleZoomOut, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA05EastSinclairSpotID: +		startExtraLongSequence(kTSA05SinclairZoomIn, kTSA05SinclairZoomOut, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA05WestWhiteSpotID: +		startExtraLongSequence(kTSA05WhiteZoomIn, kTSA05WhiteZoomOut, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA0BEastCompareNoradSpotID: +		initializeComparisonMonitor(kMonitorNoradComparison, kTSA0BNoradComparisonView); +		break; +	case kTSA0BEastCompareMarsSpotID: +		initializeComparisonMonitor(kMonitorMarsComparison, kTSA0BMarsComparisonView); +		break; +	case kTSA0BEastCompareCaldoriaSpotID: +		initializeComparisonMonitor(kMonitorCaldoriaComparison, kTSA0BCaldoriaComparisonView); +		break; +	case kTSA0BEastCompareWSCSpotID: +		initializeComparisonMonitor(kMonitorWSCComparison, kTSA0BWSCComparisonView); +		break; +	case kTSA0BEastCloseVideoSpotID: +		_navMovie.stop(); +		_sprite3.show(); +		_vm->delayShell(1, 2); +		_sprite3.hide(); +		initializeComparisonMonitor(kMonitorNeutral, 0); +		break; +	case kTSA0BEastLeftPlaySpotID: +		playLeftComparison(); +		break; +	case kTSA0BEastRightPlaySpotID: +		playRightComparison(); +		break; +		 +	// Command center +	case kTSA0BWestTheorySpotID: +		initializeTBPMonitor(kMonitorTheory, kTSA0BTBPTheoryHighlight); +		break; +	case kTSA0BWestBackgroundSpotID: +		initializeTBPMonitor(kMonitorBackground, kTSA0BTBPBackgroundHighlight); +		break; +	case kTSA0BWestProcedureSpotID: +		initializeTBPMonitor(kMonitorProcedure, kTSA0BTBPProcedureHighlight); +		break; +	case kTSA0BWestCloseVideoSpotID: +		_navMovie.stop(); +		_sprite2.show(); +		_vm->delayShell(1, 2); +		_sprite2.hide(); +		initializeTBPMonitor(kMonitorNeutral, 0); +		break; +	case kTSA0BWestPlayVideoSpotID: +		playTBPMonitor(); +		break; +	case kTSA0BEastLeftRewindSpotID: +	case kTSA0BEastRightRewindSpotID: +	case kTSA0BWestRewindVideoSpotID: +		if ((GameState.getT0BMonitorMode() & kPlayingAnyMask) != 0) { +			bool playing = _navMovie.isRunning(); +			if (playing) +				_navMovie.stop(); + +			if (clickedSpot->getObjectID() == kTSA0BEastRightRewindSpotID) +				_sprite2.show(); +			else +				_sprite1.show(); + +			_vm->delayShell(1, 2); + +			if (clickedSpot->getObjectID() == kTSA0BEastRightRewindSpotID) +				_sprite2.hide(); +			else +				_sprite1.hide(); + +			_navMovie.setTime(GameState.getT0BMonitorStart()); + +			if (playing) { +				_navMovie.start(); +			} else { +				_privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag, false); +				_privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag, false); +			} +		} +		break; +	case kTSA22EastMonitorSpotID: +		requestExtraSequence(kTSA22RedEastZoomInSequence, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA23WestMonitorSpotID: +		requestExtraSequence(kTSA23RedWestVaultZoomInSequence, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA0BNorthRobotsToCommandCenterSpotID: +		_sprite1.setCurrentFrameIndex(kRedirectionCCDoorSprite); +		_sprite1.show(); +		_vm->delayShell(1, 2); +		_sprite1.hide(); + +		switch (GameState.getTSAState()) { +		case kRobotsAtCommandCenter: +			// Nothing +			break; +		case kRobotsAtFrontDoor: +			GameState.setTSAState(kRobotsAtCommandCenter); +			_sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); +			startExtraSequence(kTSA0BRobotsFromFrontDoorToCommandCenter, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kRobotsAtReadyRoom: +			GameState.setTSAState(kRobotsAtCommandCenter); +			_sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); +			startExtraSequence(kTSA0BRobotsFromReadyRoomToCommandCenter, kExtraCompletedFlag, kFilterNoInput); +			break; +		} +		break; +	case kTSA0BNorthRobotsToReadyRoomSpotID: +		_sprite1.setCurrentFrameIndex(kRedirectionRRDoorSprite); +		_sprite1.show(); +		_vm->delayShell(1, 2); +		_sprite1.hide(); + +		switch (GameState.getTSAState()) { +		case kRobotsAtCommandCenter: +			GameState.setTSAState(kRobotsAtReadyRoom); +			_sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); +			startExtraSequence(kTSA0BRobotsFromCommandCenterToReadyRoom, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kRobotsAtFrontDoor: +			GameState.setTSAState(kRobotsAtReadyRoom); +			_sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); +			startExtraSequence(kTSA0BRobotsFromFrontDoorToReadyRoom, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kRobotsAtReadyRoom: +			// Nothing +			break; +		} +		break; +	case kTSA0BNorthRobotsToFrontDoorSpotID: +		_sprite1.setCurrentFrameIndex(kRedirectionFDDoorSprite); +		_sprite1.show(); +		_vm->delayShell(1, 2); +		_sprite1.hide(); + +		switch (GameState.getTSAState()) { +		case kRobotsAtCommandCenter: +			GameState.setTSAState(kRobotsAtFrontDoor); +			_sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); +			startExtraSequence(kTSA0BRobotsFromCommandCenterToFrontDoor, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kRobotsAtFrontDoor: +			// Nothing +			break; +		case kRobotsAtReadyRoom: +			GameState.setTSAState(kRobotsAtFrontDoor); +			_sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); +			startExtraSequence(kTSA0BRobotsFromReadyRoomToFrontDoor, kExtraCompletedFlag, kFilterNoInput); +			break; +		} +		break; + +	// Pegasus +	case kTSA37NorthJumpToPrehistoricSpotID: +		startExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA37NorthExitSpotID: +		_sprite2.setCurrentFrameIndex(1); +		_vm->delayShell(1, 2); +		releaseSprites(); +		moveForward(); +		break; +	case kTSA37NorthJumpMenuSpotID: +		_sprite2.setCurrentFrameIndex(1); +		_vm->delayShell(1, 2); +		releaseSprites(); +		break;			 +	case kTSA37NorthJumpToNoradSpotID: +		GameState.setTSAState(kPlayerOnWayToNorad); +		requestExtraSequence(kTSA37JumpToNorad, 0, kFilterNoInput); + +		if (!GameState.getBeenToNorad()) { +			requestExtraSequence(kTSA37NoradToAI7, 0, kFilterNoInput); +			requestExtraSequence(kTSA37PegasusAI7, 0, kFilterNoInput); +			requestExtraSequence(kTSA37AI7ToNorad, 0, kFilterNoInput); +			GameState.setBeenToNorad(true); +		} + +		requestExtraSequence(kTSA37NoradToDepart, 0, kFilterNoInput); +		requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA37NorthJumpToMarsSpotID: +		GameState.setTSAState(kPlayerOnWayToMars); +		requestExtraSequence(kTSA37JumpToMars, 0, kFilterNoInput); + +		if (!GameState.getBeenToMars()) { +			requestExtraSequence(kTSA37MarsToAI6, 0, kFilterNoInput); +			requestExtraSequence(kTSA37PegasusAI6, 0, kFilterNoInput); +			requestExtraSequence(kTSA37AI6ToMars, 0, kFilterNoInput); +			GameState.setBeenToMars(true); +		} + +		requestExtraSequence(kTSA37MarsToDepart, 0, kFilterNoInput); +		requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTSA37NorthJumpToWSCSpotID: +		GameState.setTSAState(kPlayerOnWayToWSC); +		requestExtraSequence(kTSA37JumpToWSC, 0, kFilterNoInput); + +		if (!GameState.getBeenToWSC()) { +			requestExtraSequence(kTSA37WSCToAI5, 0, kFilterNoInput); +			requestExtraSequence(kTSA37PegasusAI5, 0, kFilterNoInput); +			requestExtraSequence(kTSA37AI5ToWSC, 0, kFilterNoInput); +			GameState.setBeenToWSC(true); +		} + +		requestExtraSequence(kTSA37WSCToDepart, 0, kFilterNoInput); +		requestExtraSequence(kTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); +		break; +	default: +		Neighborhood::clickInHotspot(input, clickedSpot); +		break; +	} +} + +void FullTSA::showMainJumpMenu() { +	ExtraID jumpMenuView = kTSA37JumpMenu000; + +	if (GameState.getNoradFinished()) +		jumpMenuView += 4; +	if (GameState.getMarsFinished()) +		jumpMenuView += 2; +	if (GameState.getWSCFinished()) +		jumpMenuView += 1; + +	showExtraView(jumpMenuView); +	setCurrentActivation(kActivationMainJumpMenu); +} + +void FullTSA::playTBPMonitor() { +	InputDevice.waitInput(kFilterAllButtons); +	 +	if ((GameState.getT0BMonitorMode() & kPlayingTBPMask) == 0) { +		ExtraID extra; + +		switch (GameState.getT0BMonitorMode() & kRawModeMask) { +		case kMonitorTheory: +			GameState.setTSASeenTheory(true); +			extra = kTSA0BTBPTheory; +			GameState.setScoringSawTheory(true); +			break; +		case kMonitorBackground: +			GameState.setTSASeenBackground(true); +			extra = kTSA0BTBPBackground; +			GameState.setScoringSawBackground(true); +			break; +		case kMonitorProcedure: +			GameState.setTSASeenProcedure(true); +			extra = kTSA0BTBPProcedure; +			GameState.setScoringSawProcedure(true); +			break; +		} + +		GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingTBPMask); + +		ExtraTable::Entry entry; +		getExtraEntry(extra, entry); +		_lastExtra = extra; + +		GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); +		startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, kExtraCompletedFlag, false, kFilterAllInput); +	} else if (_navMovie.isRunning()) { +		_navMovie.stop(); +	} else { +		_navMovie.start(); +	} +} + +void FullTSA::initializeTBPMonitor(const int newMode, const ExtraID highlightExtra) { +	GameState.setT0BMonitorMode(newMode); + +	if (newMode != kMonitorNeutral) { +		showExtraView(highlightExtra); +		_vm->delayShell(1, 2); +		setCurrentActivation(kActivateTSA0BTBPVideo); +		_sprite1.addPICTResourceFrame(kTBPRewindPICTID, false, 0, 0); +		_sprite1.moveElementTo(kTBPRewindLeft, kTBPRewindTop); +		_sprite1.setCurrentFrameIndex(0); +		_sprite2.addPICTResourceFrame(kTBPCloseBoxPICTID, false, 0, 0); +		_sprite2.moveElementTo(kTBPCloseLeft, kTBPCloseTop); +		_sprite2.setCurrentFrameIndex(0); +		playTBPMonitor(); +	} else { +		if (GameState.getTSAState() == kTSAPlayerForcedReview && GameState.getTSASeenTheory() && +				GameState.getTSASeenBackground() && GameState.getTSASeenProcedure()) { +			setOffRipAlarm(); +		} else { +			setCurrentActivation(kActivateTSA0BZoomedIn); +			updateViewFrame(); +		} + +		releaseSprites(); +	} + +	_interruptionFilter = kFilterAllInput; +} + +void FullTSA::startUpComparisonMonitor() { +	releaseSprites(); + +	_sprite1.addPICTResourceFrame(kComparisonHiliteNoradPICTID, false, +			kComparisonHiliteNoradLeft - kComparisonHiliteSpriteLeft, +			kComparisonHiliteNoradTop - kComparisonHiliteSpriteTop); +	_sprite1.addPICTResourceFrame(kComparisonHiliteMarsPICTID, false, +			kComparisonHiliteMarsLeft - kComparisonHiliteSpriteLeft, +			 kComparisonHiliteMarsTop - kComparisonHiliteSpriteTop); +	_sprite1.addPICTResourceFrame(kComparisonHiliteCaldoriaPICTID, false, +			kComparisonHiliteCaldoriaLeft - kComparisonHiliteSpriteLeft, +			kComparisonHiliteCaldoriaTop - kComparisonHiliteSpriteTop); +	_sprite1.addPICTResourceFrame(kComparisonHiliteWSCPICTID, false, +			kComparisonHiliteWSCLeft - kComparisonHiliteSpriteLeft, +			kComparisonHiliteWSCTop - kComparisonHiliteSpriteTop); + +	_sprite1.setCurrentFrameIndex(0); +	_sprite1.moveElementTo(kComparisonHiliteSpriteLeft, kComparisonHiliteSpriteTop); +	 +	_sprite2.addPICTResourceFrame(kComparisonChancesNoradPICTID, false, +			kComparisonChancesNoradLeft - kComparisonChancesSpriteLeft, +			kComparisonChancesNoradTop - kComparisonChancesSpriteTop); +	_sprite2.addPICTResourceFrame(kComparisonChancesMarsPICTID, false, +			kComparisonChancesMarsLeft - kComparisonChancesSpriteLeft, +			kComparisonChancesMarsTop - kComparisonChancesSpriteTop); +	_sprite2.addPICTResourceFrame(kComparisonChancesCaldoriaPICTID, false, +			kComparisonChancesCaldoriaLeft - kComparisonChancesSpriteLeft, +			kComparisonChancesCaldoriaTop - kComparisonChancesSpriteTop); +	_sprite2.addPICTResourceFrame(kComparisonChancesWSCPICTID, false, +			kComparisonChancesWSCLeft - kComparisonChancesSpriteLeft, +			kComparisonChancesWSCTop - kComparisonChancesSpriteTop); + +	_sprite2.setCurrentFrameIndex(0); +	_sprite2.moveElementTo(kComparisonChancesSpriteLeft, kComparisonChancesSpriteTop); +	updateViewFrame(); +} + +void FullTSA::shutDownComparisonMonitor() { +	releaseSprites(); +} + +void FullTSA::initializeComparisonMonitor(const int newMode, const ExtraID comparisonView) { +	GameState.setT0BMonitorMode(newMode); +	_privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag, false); +	_privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag, false); + +	if (newMode != kMonitorNeutral) { +		shutDownComparisonMonitor(); +		setCurrentActivation(kActivateTSA0BComparisonVideo); +		_sprite1.addPICTResourceFrame(kComparisonLeftRewindPICTID, false, 0, 0); +		_sprite1.moveElementTo(kComparisonLeftRewindLeft, kComparisonLeftRewindTop); +		_sprite1.setCurrentFrameIndex(0); +		_sprite2.addPICTResourceFrame(kComparisonRightRewindPICTID, false, 0, 0); +		_sprite2.moveElementTo(kComparisonRightRewindLeft, kComparisonRightRewindTop); +		_sprite2.setCurrentFrameIndex(0); +		_sprite3.addPICTResourceFrame(kComparisonCloseBoxPICTID, false, 0, 0); +		_sprite3.moveElementTo(kComparisonCloseLeft, kComparisonCloseTop); +		_sprite3.setCurrentFrameIndex(0); +		showExtraView(comparisonView); +	} else { +		if (GameState.getTSAState() == kTSAPlayerInstalledHistoricalLog && +				GameState.getTSASeenNoradNormal() && +				GameState.getTSASeenNoradAltered() && +				GameState.getTSASeenMarsNormal() && +				GameState.getTSASeenMarsAltered() && +				GameState.getTSASeenCaldoriaNormal() && +				GameState.getTSASeenCaldoriaAltered() && +				GameState.getTSASeenWSCNormal() && +				GameState.getTSASeenWSCAltered()) { +			GameState.setTSAState(kTSABossSawHistoricalLog); +			requestExtraSequence(kTSA0BEastZoomOut, kExtraCompletedFlag, kFilterNoInput); +			requestExtraSequence(kTSA0BEastTurnLeft, kExtraCompletedFlag, kFilterNoInput); +			requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); +		} else { +			setCurrentActivation(kActivateTSA0BZoomedIn); +			releaseSprites(); +			startUpComparisonMonitor(); +		} +	} + +	_interruptionFilter = kFilterAllInput; +} + +void FullTSA::playLeftComparison() { +	InputDevice.waitInput(kFilterAllButtons); + +	if ((GameState.getT0BMonitorMode() & kPlayingLeftComparisonMask) == 0) { +		ExtraID extra; + +		switch (GameState.getT0BMonitorMode() & kRawModeMask) { +		case kMonitorNoradComparison: +			GameState.setTSASeenNoradAltered(true); +			extra = kTSA0BNoradAltered; +			GameState.setScoringSawNoradAltered(true); +			break; +		case kMonitorMarsComparison: +			GameState.setTSASeenMarsAltered(true); +			extra = kTSA0BMarsAltered; +			GameState.setScoringSawMarsAltered(true); +			break; +		case kMonitorCaldoriaComparison: +			GameState.setTSASeenCaldoriaAltered(true); +			extra = kTSA0BCaldoriaAltered; +			GameState.setScoringSawCaldoriaAltered(true); +			break; +		case kMonitorWSCComparison: +			GameState.setTSASeenWSCAltered(true); +			extra = kTSA0BWSCAltered; +			GameState.setScoringSawWSCAltered(true); +			break; +		} + +		GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingLeftComparisonMask); + +		ExtraTable::Entry entry; +		getExtraEntry(extra, entry); +		_lastExtra = extra; + +		// skip first five frames of movie +		// (this is a dissolve that doesn't belong...) +		GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); +		_privateFlags.setFlag(kTSAPrivatePlayingLeftComparisonFlag); + +		// Allow clicking... +		startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, +				kExtraCompletedFlag, false, JMPPPInput::getClickInputFilter()); +	} else if (_navMovie.isRunning()) { +		_navMovie.stop(); +	} else { +		_navMovie.start(); +	} +} + +void FullTSA::playRightComparison() {	 +	InputDevice.waitInput(kFilterAllButtons); +	 +	if ((GameState.getT0BMonitorMode() & kPlayingRightComparisonMask) == 0) { +		ExtraID extra; + +		switch (GameState.getT0BMonitorMode() & kRawModeMask) { +		case kMonitorNoradComparison: +			GameState.setTSASeenNoradNormal(true); +			extra = kTSA0BNoradUnaltered; +			GameState.setScoringSawNoradNormal(true); +			break; +		case kMonitorMarsComparison: +			GameState.setTSASeenMarsNormal(true); +			extra = kTSA0BMarsUnaltered; +			GameState.setScoringSawMarsNormal(true); +			break; +		case kMonitorCaldoriaComparison: +			GameState.setTSASeenCaldoriaNormal(true); +			extra = kTSA0BCaldoriaUnaltered; +			GameState.setScoringSawCaldoriaNormal(true); +			break; +		case kMonitorWSCComparison: +			GameState.setTSASeenWSCNormal(true); +			extra = kTSA0BWSCUnaltered; +			GameState.setScoringSawWSCNormal(true); +			break; +		} + +		GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() | kPlayingRightComparisonMask); + +		ExtraTable::Entry entry; +		getExtraEntry(extra, entry); +		_lastExtra = extra; + +		// skip first five frames of movie +		// (this is a dissolve that doesn't belong...) +		GameState.setT0BMonitorStart(entry.movieStart + kFullTSAFrameDuration * 5); +		_privateFlags.setFlag(kTSAPrivatePlayingRightComparisonFlag); + +		// Allow clicking... +		startMovieSequence(GameState.getT0BMonitorStart(), entry.movieEnd, +				kExtraCompletedFlag, false, JMPPPInput::getClickInputFilter()); +	} else if (_navMovie.isRunning()) { +		_navMovie.stop(); +	} else { +		_navMovie.start(); +	} +} + +// When this function is called, the player is zoomed up on the center monitor, and the +// TSA state is kTSABossSawHistoricalLog. +void FullTSA::startRobotGame() { +	requestExtraSequence(kTSA0BNorthCantChangeHistory, 0, kFilterNoInput); +	requestExtraSequence(kTSA0BAIInterruption, 0, kFilterNoInput); +	requestExtraSequence(kTSA0BShowGuardRobots, 0, kFilterNoInput); +	requestExtraSequence(kTSA0BRobotsToCommandCenter, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::startUpRobotMonitor() { +	releaseSprites(); + +	_sprite1.addPICTResourceFrame(kRedirectionCCRolloverPICTID, true, +			kRedirectionCCRolloverLeft - kRedirectionSprite1Left, +			kRedirectionCCRolloverTop - kRedirectionSprite1Top); +	_sprite1.addPICTResourceFrame(kRedirectionRRRolloverPICTID, true, +			kRedirectionRRRolloverLeft - kRedirectionSprite1Left, +			kRedirectionRRRolloverTop - kRedirectionSprite1Top); +	_sprite1.addPICTResourceFrame(kRedirectionFDRolloverPICTID, false, +			kRedirectionFDRolloverLeft - kRedirectionSprite1Left, +			kRedirectionFDRolloverTop - kRedirectionSprite1Top); +	_sprite1.addPICTResourceFrame(kRedirectionCCDoorPICTID, true, +			kRedirectionCCDoorLeft - kRedirectionSprite1Left, +			kRedirectionCCDoorTop - kRedirectionSprite1Top); +	_sprite1.addPICTResourceFrame(kRedirectionRRDoorPICTID, true, +			kRedirectionRRDoorLeft - kRedirectionSprite1Left, +			kRedirectionRRDoorTop - kRedirectionSprite1Top); +	_sprite1.addPICTResourceFrame(kRedirectionFDDoorPICTID, false, +			kRedirectionFDDoorLeft - kRedirectionSprite1Left, +			kRedirectionFDDoorTop - kRedirectionSprite1Top); +	_sprite1.addPICTResourceFrame(kRedirectionClosePICTID, false, +			kRedirectionCloseLeft - kRedirectionSprite1Left, +			kRedirectionCloseTop - kRedirectionSprite1Top); +	_sprite1.moveElementTo(kRedirectionSprite1Left, kRedirectionSprite1Top); + +	_sprite2.addPICTResourceFrame(kRedirectionSecuredPICTID, false, +			kRedirectionSecuredLeft - kRedirectionSprite2Left, +			kRedirectionSecuredTop - kRedirectionSprite2Top); +	_sprite2.addPICTResourceFrame(kRedirectionNewTargetPICTID, false, +			kRedirectionNewTargetLeft - kRedirectionSprite2Left, +			kRedirectionNewTargetTop - kRedirectionSprite2Top); +	_sprite2.moveElementTo(kRedirectionSprite2Left, kRedirectionSprite2Top); + +	switch (GameState.getTSAState()) { +	case kRobotsAtCommandCenter: +		showExtraView(kTSA0BNorthRobotsAtCCView); +		break; +	case kRobotsAtFrontDoor: +		showExtraView(kTSA0BNorthRobotsAtFDView); +		break; +	case kRobotsAtReadyRoom: +		showExtraView(kTSA0BNorthRobotsAtRRView); +		break; +	} +} + +void FullTSA::shutDownRobotMonitor() { +	releaseSprites(); +} + +// Assume this is called only when zoomed in at T0B west +void FullTSA::setOffRipAlarm() { +	GameState.setTSAState(kTSAPlayerDetectedRip); +	_ripTimer.initImage(); +	_ripTimer.moveElementTo(kRipTimerLeft, kRipTimerTop); +	_ripTimer.setSegment(0, kRipTimeLimit, kRipTimeScale); +	_ripTimer.start(); +	loadAmbientLoops(); +	startExtraSequenceSync(kTSA0BRipAlarmScreen, kFilterNoInput); +	_vm->delayShell(2, 1); // Two seconds.. +	requestExtraSequence(kTSA0BWestZoomOut, kExtraCompletedFlag, kFilterNoInput); +	requestExtraSequence(kTSA0BWestTurnRight, 0, kFilterNoInput); +	requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); +	requestExtraSequence(kTSA0BNorthFinallyHappened, 0, kFilterNoInput); +	requestExtraSequence(kTSA0BShowRip1, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::checkContinuePoint(const RoomID room, const DirectionConstant direction) { +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kTSA04, kNorth): +	case MakeRoomView(kTSA14, kEast): +	case MakeRoomView(kTSA15, kWest): +	case MakeRoomView(kTSA16, kNorth): +	case MakeRoomView(kTSA16, kSouth): +	case MakeRoomView(kTSA21Cyan, kSouth): +	case MakeRoomView(kTSA21Red, kSouth): +	case MakeRoomView(kTSA26, kNorth): +		makeContinuePoint(); +		break; +	} +} + +void FullTSA::arriveAt(const RoomID room, const DirectionConstant direction) { +	checkRobotLocations(room, direction); +	Neighborhood::arriveAt(room, direction); + +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kTSADeathRoom, kNorth): +	case MakeRoomView(kTSADeathRoom, kSouth): +	case MakeRoomView(kTSADeathRoom, kEast): +	case MakeRoomView(kTSADeathRoom, kWest): +		die(kDeathShotByTSARobots); +		break; +	case MakeRoomView(kTSA00, kNorth): +		if (GameState.getLastNeighborhood() != kFullTSAID) { +			makeContinuePoint(); +			openDoor(); +		} else { +			setCurrentActivation(kActivateTSAReadyForCard); +			loopExtraSequence(kTSATransporterArrowLoop, 0); +		} +		break; +	case MakeRoomView(kTSA03, kNorth): +	case MakeRoomView(kTSA05, kNorth): +	case MakeRoomView(kTSA0A, kNorth): +	case MakeRoomView(kTSA06, kNorth): +	case MakeRoomView(kTSA07, kNorth): +		if (_utilityFuse.isFuseLit()) +			_utilityFuse.stopFuse(); +		GameState.setScoringEnterTSA(true); +		break; +	case MakeRoomView(kTSA04, kNorth): +		if (_utilityFuse.isFuseLit()) +			_utilityFuse.stopFuse(); +		if (!GameState.getTSASeenRobotGreeting()) +			startExtraSequence(kTSA04NorthRobotGreeting, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kTSA03, kSouth): +		GameState.setTSAFrontDoorUnlockedInside(GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished()); +		break; +	case MakeRoomView(kTSA0A, kEast): +	case MakeRoomView(kTSA0A, kWest): +		if (GameState.getTSAState() == kTSAPlayerNotArrived) +			setCurrentActivation(kActivateTSARobotsAwake); +		break; +	case MakeRoomView(kTSA0B, kNorth): +		if (GameState.getTSA0BZoomedIn()) { +			setCurrentActivation(kActivateTSA0BZoomedIn); + +			switch (GameState.getTSAState()) { +			case kTSAPlayerNeedsHistoricalLog: +				_ripTimer.show(); +				break; +			case kRobotsAtCommandCenter: +			case kRobotsAtFrontDoor: +			case kRobotsAtReadyRoom: +				startUpRobotMonitor(); +				break; +			} +		} else { +			setCurrentActivation(kActivateTSA0BZoomedOut); + +			switch (GameState.getTSAState()) { +			case kTSAPlayerNotArrived: +				requestExtraSequence(kTSA0BNorthZoomIn, kExtraCompletedFlag, kFilterNoInput); +				requestExtraSequence(kTSA0BNorthYoureBusted, 0, kFilterNoInput); +				requestExtraSequence(kTSA0BNorthZoomOut, kExtraCompletedFlag, kFilterNoInput); +				requestExtraSequence(kTSA0BNorthTurnLeft, 0, kFilterNoInput); +				requestExtraSequence(kTSA0BWestZoomIn, kExtraCompletedFlag, kFilterNoInput); +				break; +			case kTSAPlayerGotHistoricalLog: +				startExtraSequence(kTSA0BNorthHistLogOpen, kExtraCompletedFlag, kFilterNoInput); +				break; +			} +		} +		break; +	case MakeRoomView(kTSA0B, kSouth): +		GameState.setTSA0BZoomedIn(false); +		setCurrentActivation(kActivateTSA0BZoomedOut); +		break; +	case MakeRoomView(kTSA0B, kWest): +		if (GameState.getTSA0BZoomedIn()) { +			setCurrentActivation(kActivateTSA0BZoomedIn); +			initializeTBPMonitor(kMonitorNeutral, 0); +		} else { +			setCurrentActivation(kActivateTSA0BZoomedOut); +		} +		break; +	case MakeRoomView(kTSA0B, kEast): +		if (GameState.getTSA0BZoomedIn()) { +			setCurrentActivation(kActivateTSA0BZoomedIn); + +			switch (GameState.getTSAState()) { +			case kTSAPlayerInstalledHistoricalLog: +			case kTSABossSawHistoricalLog: +			case kRobotsAtCommandCenter: +			case kRobotsAtFrontDoor: +			case kRobotsAtReadyRoom: +				initializeComparisonMonitor(kMonitorNeutral, 0); +				break; +			} +		} else { +			setCurrentActivation(kActivateTSA0BZoomedOut); +		} +		break; +	case MakeRoomView(kTSA21Red, kSouth): +		if (GameState.getTSAState() == kRobotsAtFrontDoor) +			GameState.setScoringWentToReadyRoom2(true); +		break; +	case MakeRoomView(kTSA22Red, kEast): +		if (!_vm->playerHasItemID(kJourneymanKey)) +			setCurrentActivation(kActivationDoesntHaveKey); +		break; +	case MakeRoomView(kTSA23Red, kWest): +		if (!_vm->playerHasItemID(kPegasusBiochip)) +			setCurrentActivation(kActivationDoesntHaveChips); +		break; +	case MakeRoomView(kTSA25Red, kNorth): +		arriveAtTSA25Red(); +		break; +	case MakeRoomView(kTSA34, kSouth): +		if (GameState.getLastRoom() == kTSA37) +			closeDoorOffScreen(kTSA37, kNorth); +		break; +	case MakeRoomView(kTSA37, kNorth): +		arriveAtTSA37(); +		break; +	} +} + +void FullTSA::checkRobotLocations(const RoomID room, const DirectionConstant dir) { +	switch (room) { +	case kTSA03: +	case kTSA04: +	case kTSA05: +	case kTSA06: +	case kTSA0A: +	case kTSA07: +	case kTSA08: +	case kTSA09: +	case kTSA10: +	case kTSA11: +	case kTSA12: +	case kTSA13: +	case kTSA14: +	case kTSA15: +		switch (GameState.getTSAState()) { +		case kRobotsAtFrontDoor: +			setCurrentAlternate(kAltTSARobotsAtFrontDoor); +			break; +		case kRobotsAtReadyRoom: +			setCurrentAlternate(kAltTSARobotsAtReadyRoom); +			break; +		} +		break; +	case kTSA16: +		if (dir == kNorth) { +			switch (GameState.getTSAState()) { +			case kRobotsAtCommandCenter: +				if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) { +					g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption); +					_privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true); +				} +				break; +			case kRobotsAtFrontDoor: +				setCurrentAlternate(kAltTSARobotsAtFrontDoor); +				break; +			case kRobotsAtReadyRoom: +				setCurrentAlternate(kAltTSARobotsAtReadyRoom); +				break; +			} +		} +		break; +	} +} + +void FullTSA::arriveAtTSA25Red() { +	if (!_vm->playerHasItemID(kJourneymanKey)) +		startExtraSequence(kTSA25NorthDeniedNoKey, kExtraCompletedFlag, kFilterNoInput); +	else if (!_vm->playerHasItemID(kPegasusBiochip)) +		startExtraSequence(kTSA25NorthDeniedNoChip, kExtraCompletedFlag, kFilterNoInput); +	else if (GameState.getTSABiosuitOn()) +		startExtraSequence(kTSA25NorthAlreadyHaveSuit, kExtraCompletedFlag, kFilterNoInput); +	else +		startExtraSequence(kTSA25NorthPutOnSuit, kExtraCompletedFlag, kFilterNoInput); +} + +void FullTSA::arriveAtTSA37() { +	_ripTimer.stop(); +	_ripTimer.releaseImage(); + +	switch (GameState.getTSAState()) { +	case kTSAPlayerNeedsHistoricalLog: +		startExtraLongSequence(kTSA37HorseToAI1, kTSA37AI2ToPrehistoric, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kPlayerOnWayToPrehistoric: +		setCurrentActivation(kActivationJumpToPrehistoric); +		showExtraView(kTSA37AI2ToPrehistoric); +		break; +	case kTSAPlayerGotHistoricalLog: +		initializePegasusButtons(false); +		break; +	case kPlayerWentToPrehistoric: +	case kPlayerOnWayToNorad: +	case kPlayerOnWayToMars: +	case kPlayerOnWayToWSC: +		startExtraSequence(kTSA37TimeJumpToPegasus, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kRobotsAtFrontDoor: +		startExtraLongSequence(kTSA37HorseToColonel2, kTSA37AI4ToMainMenu, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kPlayerLockedInPegasus: +		showMainJumpMenu(); +		break; +	case kPlayerFinishedWithTSA: +		initializePegasusButtons(true); +		break; +	} +} + +void FullTSA::turnTo(const DirectionConstant newDirection) { +	Neighborhood::turnTo(newDirection); + +	switch (MakeRoomView(GameState.getCurrentRoom(), newDirection)) { +	case MakeRoomView(kTSA03, kSouth): +		if (GameState.getTSAState() == kRobotsAtFrontDoor || GameState.allTimeZonesFinished()) +			GameState.setTSAFrontDoorUnlockedInside(true); +		else +			GameState.setTSAFrontDoorUnlockedInside(false); +		break; +	case MakeRoomView(kTSA0A, kEast): +	case MakeRoomView(kTSA0A, kWest): +		setCurrentActivation(kActivateTSARobotsAwake); +		break; +	case MakeRoomView(kTSA0B, kEast): +		if (GameState.getTSA0BZoomedIn()) +			setCurrentActivation(kActivateTSA0BZoomedIn); +		else +			setCurrentActivation(kActivateTSA0BZoomedOut); + +		GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + +		if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) +			_privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + +		switch (GameState.getTSAState()) { +		case kTSAPlayerInstalledHistoricalLog: +		case kTSABossSawHistoricalLog: +		case kRobotsAtCommandCenter: +		case kRobotsAtFrontDoor: +		case kRobotsAtReadyRoom: +			if (GameState.getTSA0BZoomedIn()) +				startUpComparisonMonitor(); +			break; +		} +		break; +	case MakeRoomView(kTSA0B, kNorth): +		if (GameState.getTSA0BZoomedIn()) +			setCurrentActivation(kActivateTSA0BZoomedIn); +		else +			setCurrentActivation(kActivateTSA0BZoomedOut); + +		GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + +		switch (GameState.getTSAState()) { +		case kTSAPlayerNeedsHistoricalLog: +			if (GameState.getTSA0BZoomedIn()) +				_ripTimer.show(); +			break; +		case kTSAPlayerGotHistoricalLog: +			if (!GameState.getTSA0BZoomedIn()) +				startExtraSequence(kTSA0BNorthHistLogOpen, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kTSAPlayerInstalledHistoricalLog: +			if (GameState.getTSA0BZoomedIn()) { +				if ((GameState.getTSASeenNoradNormal() || GameState.getTSASeenNoradAltered()) && +						(GameState.getTSASeenMarsNormal() || GameState.getTSASeenMarsAltered()) && +						(GameState.getTSASeenCaldoriaNormal() || GameState.getTSASeenCaldoriaAltered()) && +						(GameState.getTSASeenWSCNormal() || GameState.getTSASeenWSCAltered())) { +					GameState.setTSAState(kTSABossSawHistoricalLog); +					startRobotGame(); +				} +			} +			break; +		case kRobotsAtCommandCenter: +		case kRobotsAtFrontDoor: +		case kRobotsAtReadyRoom: +			if (GameState.getTSA0BZoomedIn()) +				startExtraSequence(kTSA0BShowGuardRobots, kExtraCompletedFlag, kFilterNoInput); +			break; +		} +		break; +	case MakeRoomView(kTSA0B, kWest): +		if (GameState.getTSA0BZoomedIn()) +			setCurrentActivation(kActivateTSA0BZoomedIn); +		else +			setCurrentActivation(kActivateTSA0BZoomedOut); + +		GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + +		if (_privateFlags.getFlag(kTSAPrivateLogReaderOpenFlag)) +			_privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); + +		if (GameState.getTSA0BZoomedIn()) +			initializeTBPMonitor(kMonitorNeutral, 0); +		break; +	case MakeRoomView(kTSA0B, kSouth): +		GameState.setTSA0BZoomedIn(false); +		setCurrentActivation(kActivateTSA0BZoomedOut); +		break; +	case MakeRoomView(kTSA16, kNorth): +		switch (GameState.getTSAState()) { +		case kRobotsAtCommandCenter: +			if (!_privateFlags.getFlag(kTSAPrivateSeenRobotWarningFlag)) { +				g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/TSA/XT11WB", false, kWarningInterruption); +				_privateFlags.setFlag(kTSAPrivateSeenRobotWarningFlag, true); +			} +			break; +		case kRobotsAtFrontDoor: +			setCurrentAlternate(kAltTSARobotsAtFrontDoor); +			break; +		case kRobotsAtReadyRoom: +			setCurrentAlternate(kAltTSARobotsAtReadyRoom); +			break; +		} +		break; +	case MakeRoomView(kTSA22Red, kEast): +		if (!_vm->playerHasItemID(kJourneymanKey)) +			setCurrentActivation(kActivationDoesntHaveKey); +		break; +	case MakeRoomView(kTSA22Red, kNorth): +	case MakeRoomView(kTSA22Red, kSouth): +		if (_privateFlags.getFlag(kTSAPrivateKeyVaultOpenFlag)) { +			playSpotSoundSync(kTSAVaultCloseIn, kTSAVaultCloseOut); +			_privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, false); +		} + +		setCurrentActivation(kActivateHotSpotAlways); +		break; +	case MakeRoomView(kTSA23Red, kWest): +		if (!_vm->playerHasItemID(kPegasusBiochip)) +			setCurrentActivation(kActivationDoesntHaveChips); +		break; +	case MakeRoomView(kTSA23Red, kNorth): +	case MakeRoomView(kTSA23Red, kSouth): +		if (_privateFlags.getFlag(kTSAPrivateChipVaultOpenFlag)) { +			playSpotSoundSync(kTSAVaultCloseIn, kTSAVaultCloseOut); +			_privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, false); +		} + +		setCurrentActivation(kActivateHotSpotAlways); +		break; +	} + +	// Make sure the TBP monitor is forced neutral. +	GameState.setT0BMonitorMode(kMonitorNeutral); +} + +void FullTSA::closeDoorOffScreen(const RoomID room, const DirectionConstant) { +	switch (room) { +	case kTSA00: +	case kTSA01: +		if (GameState.getCurrentRoom() == kTSA01 || GameState.getCurrentRoom() == kTSA02) +			playSpotSoundSync(kTSAGTDoorCloseIn, kTSAGTDoorCloseOut); +		break; +	case kTSA02: +	case kTSA03: +		playSpotSoundSync(kTSAEntryDoorCloseIn, kTSAEntryDoorCloseOut); +		break; +	case kTSA14: +	case kTSA15: +	case kTSA16: +	case kTSA21Cyan: +	case kTSA21Red: +		playSpotSoundSync(kTSAInsideDoorCloseIn, kTSAInsideDoorCloseOut); +		break; +	case kTSA34: +	case kTSA37: +		playSpotSoundSync(kTSAPegasusDoorCloseIn, kTSAPegasusDoorCloseOut); +		break; +	} +} + +void FullTSA::receiveNotification(Notification *notification, const NotificationFlags flags) {	 +	ExtraID lastExtra = _lastExtra; + +	if ((flags & kExtraCompletedFlag) != 0) { +		switch (lastExtra) { +		case kTSA0BEastTurnLeft: +			// Need to check this here because turnTo will call _navMovie.stop, +			// so it has to happen before Neighborhood::receiveNotification, +			// which may end up starting another sequence... +			turnTo(kNorth); +			break; +		} +	} + +	Neighborhood::receiveNotification(notification, flags); + +	InventoryItem *item; + +	if ((flags & kExtraCompletedFlag) != 0) { +		// Only allow input if we're not in the middle of series of queue requests. +		if (actionQueueEmpty()) +			_interruptionFilter = kFilterAllInput; + +		switch (lastExtra) { +		case kTSAGTCardSwipe: +			item = (InventoryItem *)g_allItems.findItemByID(kKeyCard); +			_vm->addItemToInventory(item); +			setCurrentActivation(kActivateTSAReadyToTransport); +			break; +		case kTSAGTGoToCaldoria: +			_vm->jumpToNewEnvironment(kCaldoriaID, kCaldoria44, kEast); + +			if (GameState.allTimeZonesFinished()) +				GameState.setScoringWentAfterSinclair(true); +			break; +		case kTSAGTGoToTokyo: +		case kTSAGTGoToBeach: +			if (GameState.allTimeZonesFinished()) +				die(kDeathSinclairShotDelegate); +			else +				die(kDeathUncreatedInTSA); +			break; +		case kTSA02NorthZoomOut: +			openDoor(); +			break; + +		// Hall of suspects. +		case kTSA04NorthRobotGreeting: +			GameState.setTSASeenRobotGreeting(true); +			restoreStriding(kTSA03, kNorth, kNoAlternateID); +			break; +		case kTSA03JimenezZoomIn: +			GameState.setScoringSawBust1(true); +			break; +		case kTSA03CrenshawZoomIn: +			GameState.setScoringSawBust2(true); +			break; +		case kTSA04MatsumotoZoomIn: +			GameState.setScoringSawBust3(true); +			break; +		case kTSA04CastilleZoomIn: +			GameState.setScoringSawBust4(true); +			break; +		case kTSA05SinclairZoomIn: +			GameState.setScoringSawBust5(true); +			break; +		case kTSA05WhiteZoomIn: +			GameState.setScoringSawBust6(true); +			break; +			 +		// Command center +		// Historical comparison... +		case kTSA0BEastZoomIn: +			GameState.setTSA0BZoomedIn(true); +			setCurrentActivation(kActivateTSA0BZoomedIn); +			GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + +			switch (GameState.getTSAState()) { +			case kTSAPlayerInstalledHistoricalLog: +			case kTSABossSawHistoricalLog: +			case kRobotsAtCommandCenter: +			case kRobotsAtFrontDoor: +			case kRobotsAtReadyRoom: +				startUpComparisonMonitor(); +				break; +			} +			break; +		case kTSA0BEastZoomOut: +			GameState.setTSA0BZoomedIn(false); +			setCurrentActivation(kActivateTSA0BZoomedOut); + +			switch (GameState.getTSAState()) { +			case kTSABossSawHistoricalLog: +				// Prevent current view from activating. +				break; +			default: +				activateCurrentView(GameState.getCurrentRoom(), GameState.getCurrentDirection(), +						kSpotOnTurnMask); +				break; +			} +			break; +		case kTSA0BComparisonStartup: +			if ((flags & kActionRequestCompletedFlag) != 0) { +				_privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, false); +				GameState.setTSAState(kTSAPlayerInstalledHistoricalLog); +				turnTo(kEast); +			} + +			startUpComparisonMonitor(); +			break; +		case kTSA0BNoradAltered: +		case kTSA0BMarsAltered: +		case kTSA0BCaldoriaAltered: +		case kTSA0BWSCAltered: +		case kTSA0BNoradUnaltered: +		case kTSA0BMarsUnaltered: +		case kTSA0BCaldoriaUnaltered: +		case kTSA0BWSCUnaltered: +			initializeComparisonMonitor(kMonitorNeutral, 0); +			break; +			 +		// Center monitor. +		case kTSA0BNorthZoomIn: +			GameState.setTSA0BZoomedIn(true); +			setCurrentActivation(kActivateTSA0BZoomedIn); +			GameState.setT0BMonitorMode(GameState.getT0BMonitorMode() & ~kPlayingAnyMask); + +			switch (GameState.getTSAState()) { +			case kTSAPlayerNeedsHistoricalLog: +				startExtraSequence(kTSA0BShowRip1, kExtraCompletedFlag, kFilterNoInput); +				break; +			case kTSABossSawHistoricalLog: +			case kTSAPlayerInstalledHistoricalLog: +				if ((GameState.getTSASeenNoradNormal() || GameState.getTSASeenNoradAltered()) && +						(GameState.getTSASeenMarsNormal() || GameState.getTSASeenMarsAltered()) && +						(GameState.getTSASeenCaldoriaNormal() || GameState.getTSASeenCaldoriaAltered()) && +						(GameState.getTSASeenWSCNormal() || GameState.getTSASeenWSCAltered())) { +					GameState.setTSAState(kTSABossSawHistoricalLog); +					startRobotGame(); +				} +				break; +			case kRobotsAtCommandCenter: +			case kRobotsAtFrontDoor: +			case kRobotsAtReadyRoom: +				startExtraSequence(kTSA0BShowGuardRobots, kExtraCompletedFlag, kFilterNoInput); +				break; +			} +			break; +		case kTSA0BNorthZoomOut: +			GameState.setTSA0BZoomedIn(false); +			setCurrentActivation(kActivateTSA0BZoomedOut); +			break; +		case kTSA0BShowRip1: +			GameState.setTSAState(kTSAPlayerNeedsHistoricalLog); +			GameState.setTSACommandCenterLocked(false); + +			if ((flags & kActionRequestCompletedFlag) != 0) +				turnTo(kNorth); + +			_ripTimer.show(); +			break; +		case kTSA0BNorthHistLogOpen: +			setCurrentActivation(kActivationLogReaderOpen); +			_privateFlags.setFlag(kTSAPrivateLogReaderOpenFlag, true); +			break; +		case kTSA0BRobotsToCommandCenter: +			GameState.setTSAState(kRobotsAtCommandCenter); +			// Fall through +		case kTSA0BShowGuardRobots: +			startUpRobotMonitor(); +			// Fall through +		case kTSA0BRobotsFromCommandCenterToReadyRoom: +		case kTSA0BRobotsFromReadyRoomToCommandCenter: +		case kTSA0BRobotsFromCommandCenterToFrontDoor: +		case kTSA0BRobotsFromFrontDoorToCommandCenter: +		case kTSA0BRobotsFromFrontDoorToReadyRoom: +		case kTSA0BRobotsFromReadyRoomToFrontDoor: +			_sprite2.setCurrentFrameIndex(kRedirectionSecuredSprite); +			_sprite2.show(); +			break; +			 +		// TBP monitor. +		case kTSA0BWestZoomIn: +			GameState.setTSA0BZoomedIn(true); +			setCurrentActivation(kActivateTSA0BZoomedIn); + +			if (GameState.getTSAState() == kTSAPlayerNotArrived) { +				turnTo(kWest); +				GameState.setTSACommandCenterLocked(true); +				GameState.setTSAState(kTSAPlayerForcedReview); +			} + +			initializeTBPMonitor(kMonitorNeutral, 0); +			break; +		case kTSA0BWestZoomOut: +			GameState.setTSA0BZoomedIn(false); +			setCurrentActivation(kActivateTSA0BZoomedOut); +			GameState.setT0BMonitorMode(kMonitorNeutral); + +			switch (GameState.getTSAState()) { +			case kTSAPlayerDetectedRip: +				// Keep the current view from activating. +				break; +			default: +				activateCurrentView(GameState.getCurrentRoom(), GameState.getCurrentDirection(), +						kSpotOnTurnMask); +				break; +			} +			break; +		case kTSA0BTBPTheory: +		case kTSA0BTBPBackground: +		case kTSA0BTBPProcedure: +			initializeTBPMonitor(kMonitorNeutral, 0); +			break; +			 +		// Ready room +		case kTSA22RedEastZoomInSequence: +			_privateFlags.setFlag(kTSAPrivateKeyVaultOpenFlag, true); +			setCurrentActivation(kActivationKeyVaultOpen); +			break; +		case kTSA23RedWestVaultZoomInSequence: +			_privateFlags.setFlag(kTSAPrivateChipVaultOpenFlag, true); +			setCurrentActivation(kActivationChipVaultOpen); +			break; +		case kTSA25NorthPutOnSuit: +			GameState.setTSABiosuitOn(true); +			GameState.setScoringGotBiosuit(true); +			// Fall through... +		case kTSA25NorthAlreadyHaveSuit: +			requestExtraSequence(kTSA25NorthDescending1, 0, kFilterNoInput); +			requestExtraSequence(kTSA25NorthDescending2, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kTSA25NorthDescending2: +			arriveAt(kTSA26, kNorth); +			break; + +		// Pegasus. +		case kTSA37HorseToAI1: +		case kTSA37AI2ToPrehistoric: +			setCurrentActivation(kActivationJumpToPrehistoric); +			GameState.setTSAState(kPlayerOnWayToPrehistoric); +			break; +		case kTSA37PegasusDepart: +			_vm->setLastEnergyValue(kFullEnergy); + +			switch (GameState.getTSAState()) { +			case kPlayerOnWayToPrehistoric: +				_vm->jumpToNewEnvironment(kPrehistoricID, kPrehistoric02, kSouth); +				GameState.setPrehistoricSeenTimeStream(false); +				GameState.setPrehistoricSeenFlyer1(false); +				GameState.setPrehistoricSeenFlyer2(false); +				GameState.setPrehistoricSeenBridgeZoom(false); +				GameState.setPrehistoricBreakerThrown(false); +				GameState.setScoringGoToPrehistoric(true); +				GameState.setTSAState(kPlayerWentToPrehistoric); +				break; +			case kPlayerOnWayToNorad: +				_vm->jumpToNewEnvironment(kNoradAlphaID, kNorad01, kSouth); +				GameState.setNoradSeenTimeStream(false); +				GameState.setNoradGassed(true); +				GameState.setNoradFillingStationOn(false); +				GameState.setNoradN22MessagePlayed(false); +				GameState.setNoradPlayedGlobeGame(false); +				GameState.setNoradBeatRobotWithClaw(false); +				GameState.setNoradBeatRobotWithDoor(false); +				GameState.setNoradRetScanGood(false); +				GameState.setNoradWaitingForLaser(false); +				GameState.setNoradSubRoomPressure(9); +				GameState.setNoradSubPrepState(kSubNotPrepped); +				break; +			case kPlayerOnWayToMars: +				_vm->jumpToNewEnvironment(kMarsID, kMars0A, kNorth); +				GameState.setMarsSeenTimeStream(false); +				GameState.setMarsHeardUpperPodMessage(false); +				GameState.setMarsRobotThrownPlayer(false); +				GameState.setMarsHeardCheckInMessage(false); +				GameState.setMarsPodAtUpperPlatform(false); +				GameState.setMarsSeenThermalScan(false); +				GameState.setMarsArrivedBelow(false); +				GameState.setMarsSeenRobotAtReactor(false); +				GameState.setMarsAvoidedReactorRobot(false); +				GameState.setMarsLockFrozen(false); +				GameState.setMarsLockBroken(false); +				GameState.setMarsSecurityDown(false); +				GameState.setMarsAirlockOpen(false); +				GameState.setMarsReadyForShuttleTransport(false); +				GameState.setMarsFinishedCanyonChase(false); +				GameState.setMarsThreadedMaze(false); +				break; +			case kPlayerOnWayToWSC: +				_vm->jumpToNewEnvironment(kWSCID, kWSC01, kWest); +				GameState.setWSCSeenTimeStream(false); +				GameState.setWSCPoisoned(false); +				GameState.setWSCAnsweredAboutDart(false); +				GameState.setWSCRemovedDart(false); +				GameState.setWSCAnalyzerOn(false); +				GameState.setWSCDartInAnalyzer(false); +				GameState.setWSCAnalyzedDart(false); +				GameState.setWSCPickedUpAntidote(false); +				GameState.setWSCSawMorph(false); +				GameState.setWSCDesignedAntidote(false); +				GameState.setWSCOfficeMessagesOpen(false); +				GameState.setWSCSeenNerd(false); +				GameState.setWSCHeardPage1(false); +				GameState.setWSCHeardPage2(false); +				GameState.setWSCHeardCheckIn(false); +				GameState.setWSCDidPlasmaDodge(false); +				GameState.setWSCSeenSinclairLecture(false); +				GameState.setWSCBeenAtWSC93(false); +				GameState.setWSCCatwalkDark(false); +				GameState.setWSCRobotDead(false); +				GameState.setWSCRobotGone(false); +				break; +			}; +			break; +		case kTSA37TimeJumpToPegasus: +			if (g_energyMonitor) +				g_energyMonitor->stopEnergyDraining(); + +			switch (GameState.getTSAState()) { +			case kPlayerWentToPrehistoric: +				arriveFromPrehistoric(); +				break; +			case kPlayerOnWayToNorad: +				arriveFromNorad(); +				break; +			case kPlayerOnWayToMars: +				arriveFromMars(); +				break; +			case kPlayerOnWayToWSC: +				arriveFromWSC(); +				break; +			default: +				break; +			} +			break; +		case kTSA37DownloadToOpMemReview: +			switch (GameState.getTSAState()) { +			case kPlayerOnWayToNorad: +				g_opticalChip->playOpMemMovie(kPoseidonSpotID); +				break; +			case kPlayerOnWayToMars: +				g_opticalChip->playOpMemMovie(kAriesSpotID); +				break; +			case kPlayerOnWayToWSC: +				g_opticalChip->playOpMemMovie(kMercurySpotID); +				break; +			} + +			if (GameState.allTimeZonesFinished()) { +				requestExtraSequence(kTSA37OpMemReviewToAllClear, 0, kFilterNoInput); +				requestExtraSequence(kTSA37AllClearToCongratulations, 0, kFilterNoInput); +				requestExtraSequence(kTSA37Congratulations, 0, kFilterNoInput); +				requestExtraSequence(kTSA37CongratulationsToExit, kExtraCompletedFlag, kFilterNoInput); +			} else { +				requestExtraSequence(kTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput); +			} +			break; +		case kTSA37RecallToDownload: +		case kTSA37ReviewRequiredToExit: +			GameState.setTSAState(kTSAPlayerGotHistoricalLog); +			initializePegasusButtons(kPegasusUnresolved); +			break; +		case kTSA37ZoomToMainMenu: +		case kTSA37HorseToColonel2: +		case kTSA37DownloadToMainMenu: +		case kTSA37OpMemReviewToMainMenu: +		case kTSA37AI4ToMainMenu: +			GameState.setTSAState(kPlayerLockedInPegasus); +			showMainJumpMenu(); +			makeContinuePoint(); +			break; +		case kTSA37JumpToNoradMenu: +			setCurrentActivation(kActivationJumpToNorad); +			break; +		case kTSA37JumpToMarsMenu: +			setCurrentActivation(kActivationJumpToMars); +			break; +		case kTSA37JumpToWSCMenu: +			setCurrentActivation(kActivationJumpToWSC); +			break; +		case kTSA37CancelNorad: +		case kTSA37CancelMars: +		case kTSA37CancelWSC: +			showMainJumpMenu(); +			break; +		case kTSA37CongratulationsToExit: +			GameState.setTSAState(kPlayerFinishedWithTSA); +			initializePegasusButtons(true); +			break; +		} +	} + +	g_AIArea->checkMiddleArea(); +} + +void FullTSA::arriveFromPrehistoric() { +	if (_vm->playerHasItemID(kHistoricalLog)) { +		GameState.setScoringFinishedPrehistoric(); +		requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); +		requestExtraSequence(kTSA37DownloadToColonel1, 0, kFilterNoInput); +		requestExtraSequence(kTSA37Colonel1, 0, kFilterNoInput); +		requestExtraSequence(kTSA37Colonel1ToReviewRequired, 0, kFilterNoInput); +		requestExtraSequence(kTSA37ReviewRequiredToExit, kExtraCompletedFlag, kFilterNoInput); +	} else { +		// Make sure rip timer is going... +		startExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); +	} +} + +void FullTSA::arriveFromNorad() { +	requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + +	if (GameState.getNoradFinished() && !GameState.getScoringFinishedNorad()) { +		GameState.setScoringFinishedNorad(); +		requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); +	} else { +		requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); +	} +} + +void FullTSA::arriveFromMars() { +	requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + +	if (GameState.getMarsFinished() && !GameState.getScoringFinishedMars()) { +		GameState.setScoringFinishedMars(); +		requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); +	} else { +		requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); +	} +} + +void FullTSA::arriveFromWSC() { +	requestExtraSequence(kTSA37RecallToDownload, 0, kFilterNoInput); + +	if (GameState.getWSCFinished() && !GameState.getScoringFinishedWSC()) { +		GameState.setScoringFinishedWSC(); +		requestExtraSequence(kTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); +	} else { +		requestExtraSequence(kTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); +	} +} + +void FullTSA::initializePegasusButtons(bool resolved) { +	if (resolved) { +		_sprite1.addPICTResourceFrame(kResolvedPICTID, false, 0, 0); +		_sprite1.moveElementTo(kResolvedLeft, kResolvedTop); +	} else { +		_sprite1.addPICTResourceFrame(kUnresolvedPICTID, false, 0, 0); +		_sprite1.moveElementTo(kUnresolvedLeft, kUnresolvedTop); +	} + +	_sprite1.setCurrentFrameIndex(0); +	_sprite1.show(); + +	_sprite2.addPICTResourceFrame(kExitPICTID, false, kExitLeft - kExitHilitedLeft, kExitTop - kExitHilitedTop); +	_sprite2.addPICTResourceFrame(kExitHilitedPICTID, false, 0, 0); +	_sprite2.moveElementTo(kExitHilitedLeft, kExitHilitedTop); +	setCurrentActivation(kActivationReadyToExit); +	_sprite2.setCurrentFrameIndex(0); +	_sprite2.show(); +} + +Hotspot *FullTSA::getItemScreenSpot(Item *item, DisplayElement *element) { +	switch (item->getObjectID()) { +	case kJourneymanKey: +		return g_allHotspots.findHotspotByID(kTSA22EastKeySpotID); +		break; +	case kPegasusBiochip: +		return g_allHotspots.findHotspotByID(kTSA23WestChipsSpotID); +		break; +	} + +	return Neighborhood::getItemScreenSpot(item, element); +} + +void FullTSA::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { +	Neighborhood::dropItemIntoRoom(item, dropSpot); + +	switch (item->getObjectID()) { +	case kKeyCard: +		if (dropSpot->getObjectID() == kTSAGTCardDropSpotID) +			startExtraSequence(kTSAGTCardSwipe, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kHistoricalLog: +		if (dropSpot->getObjectID() == kTSA0BNorthHistLogSpotID) { +			requestExtraSequence(kTSA0BNorthHistLogCloseWithLog, 0, kFilterNoInput); +			requestExtraSequence(kTSA0BNorthTurnRight, 0, kFilterNoInput); +			requestExtraSequence(kTSA0BEastZoomIn, kExtraCompletedFlag, kFilterNoInput); +			requestExtraSequence(kTSA0BComparisonStartup, kExtraCompletedFlag, kFilterNoInput); +			GameState.setScoringPutLogInReader(true); +		} +		break; +	} +} + +uint FullTSA::getHistoricalLogIndex() { +	uint index; +	 +	if (GameState.getTSASeenNoradNormal() && GameState.getTSASeenNoradAltered()) +		index = 8; +	else +		index = 0; + +	if (GameState.getTSASeenMarsNormal() && GameState.getTSASeenMarsAltered()) +		index += 4; + +	if (GameState.getTSASeenCaldoriaNormal() && GameState.getTSASeenCaldoriaAltered()) +		index += 2; + +	if (GameState.getTSASeenWSCNormal() && GameState.getTSASeenWSCAltered()) +		index += 1; + +	return index; +} + +void FullTSA::handleInput(const Input &input, const Hotspot *cursorSpot) { +	switch (MakeRoomView(GameState.getCurrentRoom(), GameState.getCurrentDirection())) { +	case MakeRoomView(kTSA0B, kEast): +		if (GameState.getTSA0BZoomedIn() && !_navMovie.isRunning() && GameState.getT0BMonitorMode() == kMonitorNeutral) { +			switch (GameState.getTSAState()) { +			case kTSAPlayerInstalledHistoricalLog: +			case kTSABossSawHistoricalLog: +			case kRobotsAtCommandCenter: +			case kRobotsAtFrontDoor: +			case kRobotsAtReadyRoom: +				if (cursorSpot) { +					switch (cursorSpot->getObjectID()) { +					case kTSA0BEastCompareNoradSpotID: +						_sprite1.setCurrentFrameIndex(0); +						_sprite2.setCurrentFrameIndex(0); +						_sprite1.show(); +						_sprite2.show(); +						break; +					case kTSA0BEastCompareMarsSpotID: +						_sprite1.setCurrentFrameIndex(1); +						_sprite2.setCurrentFrameIndex(1); +						_sprite1.show(); +						_sprite2.show(); +						break; +					case kTSA0BEastCompareCaldoriaSpotID: +						_sprite1.setCurrentFrameIndex(2); +						_sprite2.setCurrentFrameIndex(2); +						_sprite1.show(); +						_sprite2.show(); +						break; +					case kTSA0BEastCompareWSCSpotID: +						_sprite1.setCurrentFrameIndex(3); +						_sprite2.setCurrentFrameIndex(3); +						_sprite1.show(); +						_sprite2.show(); +						break; +					default: +						_sprite1.hide(); +						_sprite2.hide(); +						break; +					} +				} else { +					_sprite1.hide(); +					_sprite2.hide(); +				} +				break; +			} +		} +		break; +	case MakeRoomView(kTSA0B, kNorth): +		if (GameState.getTSA0BZoomedIn() && !_navMovie.isRunning()) { +			switch (GameState.getTSAState()) { +			case kRobotsAtCommandCenter: +			case kRobotsAtFrontDoor: +			case kRobotsAtReadyRoom: +				if (cursorSpot) { +					switch (cursorSpot->getObjectID()) { +					case kTSA0BNorthRobotsToCommandCenterSpotID: +						_sprite1.setCurrentFrameIndex(kRedirectionCCRolloverSprite); +						_sprite1.show(); +						break; +					case kTSA0BNorthRobotsToReadyRoomSpotID: +						_sprite1.setCurrentFrameIndex(kRedirectionRRRolloverSprite); +						_sprite1.show(); +						break; +					case kTSA0BNorthRobotsToFrontDoorSpotID: +						_sprite1.setCurrentFrameIndex(kRedirectionFDRolloverSprite); +						_sprite1.show(); +						break; +					default: +						_sprite1.hide(); +						break; +					} +				} else { +					_sprite1.hide(); +				} +				break; +			} +		} +		break; +	} + +	Neighborhood::handleInput(input, cursorSpot); +} + +void FullTSA::releaseSprites() { +	_sprite1.hide(); +	_sprite2.hide(); +	_sprite3.hide(); +	_sprite1.discardFrames(); +	_sprite2.discardFrames(); +	_sprite3.discardFrames(); +} + +bool FullTSA::canSolve() { +	return GameState.getCurrentRoomAndView() == MakeRoomView(kTSA0B, kNorth) && +		   GameState.getTSA0BZoomedIn() && +		   (GameState.getTSAState() == kRobotsAtCommandCenter || +		   GameState.getTSAState() == kRobotsAtFrontDoor || +		   GameState.getTSAState() == kRobotsAtReadyRoom); +} + +void FullTSA::doSolve() { +	// REROUTING ROBOTS + +	_sprite1.setCurrentFrameIndex(kRedirectionFDDoorSprite); +	_sprite1.show(); +	_vm->delayShell(1, 2); +	_sprite1.hide(); + +	switch (GameState.getTSAState()) { +	case kRobotsAtCommandCenter: +		GameState.setTSAState(kRobotsAtFrontDoor); +		_sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); +		startExtraSequence(kTSA0BRobotsFromCommandCenterToFrontDoor, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kRobotsAtFrontDoor: +		// Nothing +		break; +	case kRobotsAtReadyRoom: +		GameState.setTSAState(kRobotsAtFrontDoor); +		_sprite2.setCurrentFrameIndex(kRedirectionNewTargetSprite); +		startExtraSequence(kTSA0BRobotsFromReadyRoomToFrontDoor, kExtraCompletedFlag, kFilterNoInput); +		break; +	} +} + +void FullTSA::updateCursor(const Common::Point where, const Hotspot *cursorSpot) { +	if (cursorSpot) { +		switch (cursorSpot->getObjectID()) { +		case kTSA0BEastMonitorSpotID: +		case kTSA0BNorthMonitorSpotID: +		case kTSA0BWestMonitorSpotID: +		case kTSA22EastMonitorSpotID: +		case kTSA23WestMonitorSpotID: +			_vm->_cursor->setCurrentFrameIndex(1); +			return; +		case kTSA0BEastMonitorOutSpotID: +		case kTSA0BNorthMonitorOutSpotID: +		case kTSA0BWestMonitorOutSpotID: +			_vm->_cursor->setCurrentFrameIndex(2); +			return; +		} +	} + +	Neighborhood::updateCursor(where, cursorSpot); +} + +Common::String FullTSA::getNavMovieName() { +	return "Images/TSA/Full TSA.movie"; +} + +Common::String FullTSA::getSoundSpotsName() { +	return "Sounds/TSA/TSA Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/tsa/fulltsa.h b/engines/pegasus/neighborhood/tsa/fulltsa.h new file mode 100644 index 0000000000..4260a83a78 --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/fulltsa.h @@ -0,0 +1,159 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_TSA_FULLTSA_H +#define PEGASUS_NEIGHBORHOOD_TSA_FULLTSA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +class RipTimer : public IdlerAnimation { +public: +	RipTimer(const DisplayElementID id) : IdlerAnimation(id) {} +	virtual ~RipTimer() {} + +	void initImage(); +	void releaseImage(); + +	void draw(const Common::Rect &); + +protected: +	void timeChanged(const TimeValue); +	 +	CoordType _middle; +	Surface _timerImage; +}; + +//	Room IDs. + +static const RoomID kTSA00 = 0; +static const RoomID kTSA22Red = 28; +static const RoomID kTSA37 = 42; + +class FullTSA : public Neighborhood { +friend void uncreatedInTSAFunction(FunctionPtr *, void *tsa); + +public: +	FullTSA(InputHandler *, PegasusEngine *); +	virtual ~FullTSA() {} + +	virtual void init(); + +	void start(); + +	virtual uint16 getDateResID() const; + +	void flushGameState(); + +	void checkContinuePoint(const RoomID, const DirectionConstant); + +	bool canSolve(); +	void doSolve(); + +	void updateCursor(const Common::Point, const Hotspot *); + +protected: +	enum { +		kTSAPrivateLogReaderOpenFlag, +		kTSAPrivateKeyVaultOpenFlag, +		kTSAPrivateChipVaultOpenFlag, +		kTSAPrivatePlayingLeftComparisonFlag, +		kTSAPrivatePlayingRightComparisonFlag, +		kTSAPrivateSeenRobotWarningFlag, +		kNumTSAPrivateFlags +	}; + +	Common::String getBriefingMovie(); +	Common::String getEnvScanMovie(); +	uint getNumHints(); +	Common::String getHintMovie(uint); +	void loadAmbientLoops(); +	virtual void clickInHotspot(const Input &, const Hotspot *); + +	virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant); +	void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *spot); +	virtual void activateHotspots(); +	void getExitCompassMove(const ExitTable::Entry &, FaderMoveSpec &); +	void dropItemIntoRoom(Item *, Hotspot *); +	void downButton(const Input &); +	void startDoorOpenMovie(const TimeValue, const TimeValue); +	TimeValue getViewTime(const RoomID, const DirectionConstant); +	void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); +	void turnTo(const DirectionConstant); +	CanMoveForwardReason canMoveForward(ExitTable::Entry &); +	CanOpenDoorReason canOpenDoor(DoorTable::Entry &); +	void bumpIntoWall(); +	void initializeTBPMonitor(const int, const ExtraID); +	void playTBPMonitor(); +	void getExtraCompassMove(const ExtraTable::Entry &, FaderMoveSpec &); +	Hotspot *getItemScreenSpot(Item *, DisplayElement *); +	void openDoor(); +	void turnRight(); +	void turnLeft(); +	void closeDoorOffScreen(const RoomID, const DirectionConstant); +	void playExtraMovie(const ExtraTable::Entry &, const NotificationFlags, const InputBits interruptionInput); +	void handleInput(const Input &, const Hotspot *); +	void arriveAtTSA25Red(); +	void startUpComparisonMonitor(); +	void shutDownComparisonMonitor(); +	void initializeComparisonMonitor(const int, const ExtraID); +	void playLeftComparison(); +	void playRightComparison(); +	void startRobotGame(); +	void setOffRipAlarm(); +	uint getHistoricalLogIndex(); +	void startUpRobotMonitor(); +	void shutDownRobotMonitor(); +	void pickedUpItem(Item *item); +	void arriveFromPrehistoric(); + +	void arriveFromNorad(); +	void arriveFromMars(); +	void arriveFromWSC(); + +	InputBits getInputFilter(); +	void arriveAt(const RoomID, const DirectionConstant); +	void initializePegasusButtons(bool); +	void releaseSprites(); +	void showMainJumpMenu(); +	void arriveAtTSA37(); +	void receiveNotification(Notification *, const NotificationFlags); +	void checkRobotLocations(const RoomID, const DirectionConstant); +	void getExtraEntry(const uint32, ExtraTable::Entry &); + +	Sprite _sprite1, _sprite2, _sprite3; +	FuseFunction _utilityFuse; +	RipTimer _ripTimer; + +	FlagsArray<byte, kNumTSAPrivateFlags> _privateFlags; + +	Common::String getNavMovieName(); +	Common::String getSoundSpotsName(); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.cpp b/engines/pegasus/neighborhood/tsa/tinytsa.cpp new file mode 100644 index 0000000000..2fa6c5377a --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/tinytsa.cpp @@ -0,0 +1,453 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/neighborhood/mars/constants.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +static const int16 kCompassShift = 30; + +static const TimeScale kTinyTSAMovieScale = 600; +static const TimeScale kTinyTSAFramesPerSecond = 15; +static const TimeScale kTinyTSAFrameDuration = 40; + +// Alternate IDs. +static const AlternateID kAltTinyTSANormal = 0; + +// Hot Spot Activation IDs. +static const HotSpotActivationID kActivationTinyTSAJumpToNorad = 1; +static const HotSpotActivationID kActivationTinyTSAJumpToMars = 2; +static const HotSpotActivationID kActivationTinyTSAJumpToWSC = 3; +static const HotSpotActivationID kActivationTinyTSAReadyForJumpMenu = 4; +static const HotSpotActivationID kActivationTinyTSAMainJumpMenu = 5; + +// Hot Spot IDs. +static const HotSpotID kTinyTSA37NorthJumpToNoradSpotID = 5000; +static const HotSpotID kTinyTSA37NorthCancelNoradSpotID = 5001; +static const HotSpotID kTinyTSA37NorthJumpToMarsSpotID = 5002; +static const HotSpotID kTinyTSA37NorthCancelMarsSpotID = 5003; +static const HotSpotID kTinyTSA37NorthJumpToWSCSpotID = 5004; +static const HotSpotID kTinyTSA37NorthCancelWSCSpotID = 5005; +static const HotSpotID kTinyTSA37NorthJumpMenuSpotID = 5006; +static const HotSpotID kTinyTSA37NorthNoradMenuSpotID = 5007; +static const HotSpotID kTinyTSA37NorthMarsMenuSpotID = 5008; +static const HotSpotID kTinyTSA37NorthWSCMenuSpotID = 5009; + +// Extra sequence IDs. +static const ExtraID kTinyTSA37PegasusDepart = 0; +static const ExtraID kTinyTSA37TimeJumpToPegasus = 1; +static const ExtraID kTinyTSA37RecallToDownload = 2; +static const ExtraID kTinyTSA37ExitHilited = 3; +static const ExtraID kTinyTSA37ExitToHorse = 4; +static const ExtraID kTinyTSA37JumpMenu000 = 5; +static const ExtraID kTinyTSA37JumpMenu001 = 6; +static const ExtraID kTinyTSA37JumpMenu010 = 7; +static const ExtraID kTinyTSA37JumpMenu011 = 8; +static const ExtraID kTinyTSA37JumpMenu100 = 9; +static const ExtraID kTinyTSA37JumpMenu101 = 10; +static const ExtraID kTinyTSA37JumpMenu110 = 11; +static const ExtraID kTinyTSA37JumpMenu111 = 12; +static const ExtraID kTinyTSA37JumpToWSCMenu = 13; +static const ExtraID kTinyTSA37CancelWSC = 14; +static const ExtraID kTinyTSA37JumpToWSC = 15; +static const ExtraID kTinyTSA37WSCToAI5 = 16; +static const ExtraID kTinyTSA37PegasusAI5 = 17; +static const ExtraID kTinyTSA37AI5ToWSC = 18; +static const ExtraID kTinyTSA37WSCToDepart = 19; +static const ExtraID kTinyTSA37JumpToMarsMenu = 20; +static const ExtraID kTinyTSA37CancelMars = 21; +static const ExtraID kTinyTSA37JumpToMars = 22; +static const ExtraID kTinyTSA37MarsToAI6 = 23; +static const ExtraID kTinyTSA37PegasusAI6 = 24; +static const ExtraID kTinyTSA37AI6ToMars = 25; +static const ExtraID kTinyTSA37MarsToDepart = 26; +static const ExtraID kTinyTSA37JumpToNoradMenu = 27; +static const ExtraID kTinyTSA37CancelNorad = 28; +static const ExtraID kTinyTSA37JumpToNorad = 29; +static const ExtraID kTinyTSA37NoradToAI7 = 30; +static const ExtraID kTinyTSA37PegasusAI7 = 31; +static const ExtraID kTinyTSA37AI7ToNorad = 32; +static const ExtraID kTinyTSA37NoradToDepart = 33; +static const ExtraID kTinyTSA37EnvironmentalScan = 34; +static const ExtraID kTinyTSA37DownloadToMainMenu = 35; +static const ExtraID kTinyTSA37DownloadToOpMemReview = 36; +static const ExtraID kTinyTSA37OpMemReviewToMainMenu = 37; + +TinyTSA::TinyTSA(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "Tiny TSA", kTinyTSAID) { +} + +void TinyTSA::start() { +	g_energyMonitor->stopEnergyDraining(); +	Neighborhood::start(); +} + +Common::String TinyTSA::getBriefingMovie() { +	Common::String movieName = Neighborhood::getBriefingMovie(); + +	if (movieName.empty()) { +		switch (getCurrentActivation()) { +		case kActivationTinyTSAJumpToNorad: +			g_AIChip->showBriefingClicked(); +			startExtraSequenceSync(kTinyTSA37PegasusAI7, kHintInterruption); +			startExtraSequenceSync(kTinyTSA37AI7ToNorad, kFilterNoInput); +			g_AIChip->clearClicked(); +			movieName = ""; +			break; +		case kActivationTinyTSAJumpToMars: +			g_AIChip->showBriefingClicked(); +			startExtraSequenceSync(kTinyTSA37PegasusAI6, kHintInterruption); +			startExtraSequenceSync(kTinyTSA37AI6ToMars, kFilterNoInput); +			g_AIChip->clearClicked(); +			movieName = ""; +			break; +		case kActivationTinyTSAJumpToWSC: +			g_AIChip->showBriefingClicked(); +			startExtraSequenceSync(kTinyTSA37PegasusAI5, kHintInterruption); +			startExtraSequenceSync(kTinyTSA37AI5ToWSC, kFilterNoInput); +			g_AIChip->clearClicked(); +			movieName = ""; +			break; +		default: +			movieName = "Images/AI/TSA/XT04"; +			break; +		} +	} + +	return movieName; +} + +Common::String TinyTSA::getEnvScanMovie() { +	Common::String movieName = Neighborhood::getEnvScanMovie(); + +	if (movieName.empty()) { +		g_AIChip->showEnvScanClicked(); +		startExtraSequenceSync(kTinyTSA37EnvironmentalScan, kHintInterruption); + +		switch (getCurrentActivation()) { +		case kActivationTinyTSAJumpToNorad: +			startExtraSequenceSync(kTinyTSA37AI7ToNorad, kFilterNoInput); +			showExtraView(kTinyTSA37JumpToNoradMenu); +			break; +		case kActivationTinyTSAJumpToMars: +			startExtraSequenceSync(kTinyTSA37AI6ToMars, kFilterNoInput); +			showExtraView(kTinyTSA37JumpToMarsMenu); +			break; +		case kActivationTinyTSAJumpToWSC: +			startExtraSequenceSync(kTinyTSA37AI5ToWSC, kFilterNoInput); +			showExtraView(kTinyTSA37JumpToWSCMenu); +			break; +		default: +			showMainJumpMenu(); +			break; +		} + +		g_AIChip->clearClicked(); +	} + +	return movieName; +} + +void TinyTSA::loadAmbientLoops() { +	loadLoopSound1("Sounds/TSA/T01NAE.NEW.22K.AIFF"); +} + +int16 TinyTSA::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { +	return Neighborhood::getStaticCompassAngle(room, dir) - kCompassShift; +} + +uint16 TinyTSA::getDateResID() const { +	return kDate2318ID; +} + +InputBits TinyTSA::getInputFilter() { +	// Can't move forward... +	return Neighborhood::getInputFilter() & ~(kFilterUpButton | kFilterUpAuto); +} + +void TinyTSA::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { +	if (clickedSpot) { +		switch (clickedSpot->getObjectID()) { +		case kTinyTSA37NorthJumpMenuSpotID: +			// This hotspot isn't accessable from Tiny TSA +			warning("jump menu spot"); +			return; +		case kTinyTSA37NorthJumpToNoradSpotID: +			GameState.setTSAState(kPlayerOnWayToNorad); +			requestExtraSequence(kTinyTSA37JumpToNorad, 0, kFilterNoInput); +			if (!GameState.getBeenToNorad()) { +				requestExtraSequence(kTinyTSA37NoradToAI7, 0, kFilterNoInput); +				requestExtraSequence(kTinyTSA37PegasusAI7, 0, kFilterNoInput); +				requestExtraSequence(kTinyTSA37AI7ToNorad, 0, kFilterNoInput); +				GameState.setBeenToNorad(true); +			} + +			requestExtraSequence(kTinyTSA37NoradToDepart, 0, kFilterNoInput); +			requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); +			return; +		case kTinyTSA37NorthJumpToMarsSpotID: +			GameState.setTSAState(kPlayerOnWayToMars); +			requestExtraSequence(kTinyTSA37JumpToMars, 0, kFilterNoInput); +			if (!GameState.getBeenToMars()) { +				requestExtraSequence(kTinyTSA37MarsToAI6, 0, kFilterNoInput); +				requestExtraSequence(kTinyTSA37PegasusAI6, 0, kFilterNoInput); +				requestExtraSequence(kTinyTSA37AI6ToMars, 0, kFilterNoInput); +				GameState.setBeenToMars(true); +			} + +			requestExtraSequence(kTinyTSA37MarsToDepart, 0, kFilterNoInput); +			requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); +			return; +		case kTinyTSA37NorthJumpToWSCSpotID: +			GameState.setTSAState(kPlayerOnWayToWSC); +			requestExtraSequence(kTinyTSA37JumpToWSC, 0, kFilterNoInput); +			if (!GameState.getBeenToWSC()) { +				requestExtraSequence(kTinyTSA37WSCToAI5, 0, kFilterNoInput); +				requestExtraSequence(kTinyTSA37PegasusAI5, 0, kFilterNoInput); +				requestExtraSequence(kTinyTSA37AI5ToWSC, 0, kFilterNoInput); +				GameState.setBeenToWSC(true); +			} + +			requestExtraSequence(kTinyTSA37WSCToDepart, 0, kFilterNoInput); +			requestExtraSequence(kTinyTSA37PegasusDepart, kExtraCompletedFlag, kFilterNoInput); +			return; +		} +	} + +	Neighborhood::clickInHotspot(input, clickedSpot); +} + +void TinyTSA::showMainJumpMenu() { +	ExtraID jumpMenuView = kTinyTSA37JumpMenu000; + +	if (GameState.getNoradFinished()) +		jumpMenuView += 4; +	if (GameState.getMarsFinished()) +		jumpMenuView += 2; +	if (GameState.getWSCFinished()) +		jumpMenuView += 1; + +	showExtraView(jumpMenuView); +	setCurrentActivation(kActivationTinyTSAMainJumpMenu); +} + +void TinyTSA::checkContinuePoint(const RoomID, const DirectionConstant) { +	makeContinuePoint(); +} + +void TinyTSA::arriveAt(const RoomID room, const DirectionConstant direction) { +	Neighborhood::arriveAt(room, direction); + +	switch (GameState.getTSAState()) { +	case kPlayerOnWayToNorad: +	case kPlayerOnWayToMars: +	case kPlayerOnWayToWSC: +		startExtraSequence(kTinyTSA37TimeJumpToPegasus, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kPlayerLockedInPegasus: +		showMainJumpMenu(); +		break; +	} +} + +void TinyTSA::receiveNotification(Notification *notification, const NotificationFlags flags) { +	ExtraID lastExtra = _lastExtra; +	 +	Neighborhood::receiveNotification(notification, flags); + +	if ((flags & kExtraCompletedFlag) != 0) { +		// Only allow input if we're not in the middle of series of queue requests. +		if (actionQueueEmpty()) +			_interruptionFilter = kFilterAllInput; + +		switch (lastExtra) { +		case kTinyTSA37PegasusDepart: +			_vm->setLastEnergyValue(kFullEnergy); + +			switch (GameState.getTSAState()) { +			case kPlayerOnWayToNorad: +				_vm->jumpToNewEnvironment(kNoradAlphaID, kNorad01, kSouth); +				GameState.setNoradSeenTimeStream(false); +				GameState.setNoradGassed(true); +				GameState.setNoradFillingStationOn(false); +				GameState.setNoradN22MessagePlayed(false); +				GameState.setNoradPlayedGlobeGame(false); +				GameState.setNoradBeatRobotWithClaw(false); +				GameState.setNoradBeatRobotWithDoor(false); +				GameState.setNoradRetScanGood(false); +				GameState.setNoradWaitingForLaser(false); +				GameState.setNoradSubRoomPressure(9); +				GameState.setNoradSubPrepState(kSubNotPrepped); +				break; +			case kPlayerOnWayToMars: +				_vm->jumpToNewEnvironment(kMarsID, kMars0A, kNorth); +				GameState.setMarsSeenTimeStream(false); +				GameState.setMarsHeardUpperPodMessage(false); +				GameState.setMarsRobotThrownPlayer(false); +				GameState.setMarsHeardCheckInMessage(false); +				GameState.setMarsPodAtUpperPlatform(false); +				GameState.setMarsSeenThermalScan(false); +				GameState.setMarsArrivedBelow(false); +				GameState.setMarsSeenRobotAtReactor(false); +				GameState.setMarsAvoidedReactorRobot(false); +				GameState.setMarsLockFrozen(false); +				GameState.setMarsLockBroken(false); +				GameState.setMarsSecurityDown(false); +				GameState.setMarsAirlockOpen(false); +				GameState.setMarsReadyForShuttleTransport(false); +				GameState.setMarsFinishedCanyonChase(false); +				GameState.setMarsThreadedMaze(false); +				break; +			case kPlayerOnWayToWSC: +				_vm->jumpToNewEnvironment(kWSCID, kWSC01, kWest); +				GameState.setWSCSeenTimeStream(false); +				GameState.setWSCPoisoned(false); +				GameState.setWSCAnsweredAboutDart(false); +				GameState.setWSCDartInAnalyzer(false); +				GameState.setWSCRemovedDart(false); +				GameState.setWSCAnalyzerOn(false); +				GameState.setWSCAnalyzedDart(false); +				GameState.setWSCPickedUpAntidote(false); +				GameState.setWSCSawMorph(false); +				GameState.setWSCDesignedAntidote(false); +				GameState.setWSCOfficeMessagesOpen(false); +				GameState.setWSCSeenNerd(false); +				GameState.setWSCHeardPage1(false); +				GameState.setWSCHeardPage2(false); +				GameState.setWSCHeardCheckIn(false); +				GameState.setWSCDidPlasmaDodge(false); +				GameState.setWSCSeenSinclairLecture(false); +				GameState.setWSCBeenAtWSC93(false); +				GameState.setWSCCatwalkDark(false); +				GameState.setWSCRobotDead(false); +				GameState.setWSCRobotGone(false); +				break; +			}; +			break; +		case kTinyTSA37TimeJumpToPegasus: +			if (g_energyMonitor) +				g_energyMonitor->stopEnergyDraining(); + +			switch (GameState.getTSAState()) { +			case kPlayerOnWayToNorad: +				arriveFromNorad(); +				break; +			case kPlayerOnWayToMars: +				arriveFromMars(); +				break; +			case kPlayerOnWayToWSC: +				arriveFromWSC(); +				break; +			default: +				break; +			} +			break; +		case kTinyTSA37DownloadToOpMemReview: +			switch (GameState.getTSAState()) { +			case kPlayerOnWayToNorad: +				g_opticalChip->playOpMemMovie(kPoseidonSpotID); +				break; +			case kPlayerOnWayToMars: +				g_opticalChip->playOpMemMovie(kAriesSpotID); +				break; +			case kPlayerOnWayToWSC: +				g_opticalChip->playOpMemMovie(kMercurySpotID); +				break; +			} + +			requestExtraSequence(kTinyTSA37OpMemReviewToMainMenu, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kTinyTSA37DownloadToMainMenu: +		case kTinyTSA37OpMemReviewToMainMenu: +			GameState.setTSAState(kPlayerLockedInPegasus); +			showMainJumpMenu(); +			makeContinuePoint(); +			break; +		case kTinyTSA37JumpToNoradMenu: +			setCurrentActivation(kActivationTinyTSAJumpToNorad); +			break; +		case kTinyTSA37JumpToMarsMenu: +			setCurrentActivation(kActivationTinyTSAJumpToMars); +			break; +		case kTinyTSA37JumpToWSCMenu: +			setCurrentActivation(kActivationTinyTSAJumpToWSC); +			break; +		case kTinyTSA37CancelNorad: +		case kTinyTSA37CancelMars: +		case kTinyTSA37CancelWSC: +			showMainJumpMenu(); +			break; +		} +	} + +	g_AIArea->checkMiddleArea(); +} + +void TinyTSA::arriveFromNorad() { +	requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + +	if (GameState.getNoradFinished() && !GameState.getScoringFinishedNorad()) { +		GameState.setScoringFinishedNorad(); +		requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); +	} else { +		requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); +	} +} + +void TinyTSA::arriveFromMars() { +	requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + +	if (GameState.getMarsFinished() && !GameState.getScoringFinishedMars()) { +		GameState.setScoringFinishedMars(); +		requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); +	} else { +		requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); +	} +} + +void TinyTSA::arriveFromWSC() { +	requestExtraSequence(kTinyTSA37RecallToDownload, 0, kFilterNoInput); + +	if (GameState.getWSCFinished() && !GameState.getScoringFinishedWSC()) { +		GameState.setScoringFinishedWSC(); +		requestExtraSequence(kTinyTSA37DownloadToOpMemReview, kExtraCompletedFlag, kFilterNoInput); +	} else { +		requestExtraSequence(kTinyTSA37DownloadToMainMenu, kExtraCompletedFlag, kFilterNoInput); +	} +} + +Common::String TinyTSA::getNavMovieName() { +	return "Images/TSA/Tiny TSA.movie"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/tsa/tinytsa.h b/engines/pegasus/neighborhood/tsa/tinytsa.h new file mode 100644 index 0000000000..db74206d4f --- /dev/null +++ b/engines/pegasus/neighborhood/tsa/tinytsa.h @@ -0,0 +1,71 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_TSA_TINYTSA_H +#define PEGASUS_NEIGHBORHOOD_TSA_TINYTSA_H + +#include "pegasus/neighborhood/neighborhood.h" + +namespace Pegasus { + +//	Room IDs. + +static const RoomID kTinyTSA37 = 0; + +class TinyTSA : public Neighborhood { +public: +	TinyTSA(InputHandler *, PegasusEngine *); +	virtual ~TinyTSA() {} +	 +	virtual uint16 getDateResID() const; +	 +	void start(); +	 +	void checkContinuePoint(const RoomID, const DirectionConstant); + +protected: +	Common::String getBriefingMovie(); +	Common::String getEnvScanMovie(); +	void loadAmbientLoops(); +	virtual void clickInHotspot(const Input &, const Hotspot *); +	 +	virtual int16 getStaticCompassAngle(const RoomID, const DirectionConstant); +	 +	void arriveFromNorad(); +	void arriveFromMars(); +	void arriveFromWSC(); +	 +	InputBits getInputFilter(); +	void arriveAt(const RoomID, const DirectionConstant); +	void showMainJumpMenu(); +	void receiveNotification(Notification *, const NotificationFlags); + +	Common::String getNavMovieName(); +	Common::String getSoundSpotsName() { return ""; } +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/turn.cpp b/engines/pegasus/neighborhood/turn.cpp new file mode 100644 index 0000000000..1157796f55 --- /dev/null +++ b/engines/pegasus/neighborhood/turn.cpp @@ -0,0 +1,63 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/turn.h" + +namespace Pegasus { + +void TurnTable::loadFromStream(Common::SeekableReadStream *stream) { +	uint32 count = stream->readUint32BE(); +	_entries.resize(count); + +	for (uint32 i = 0; i < count; i++) { +		_entries[i].room = stream->readUint16BE(); +		_entries[i].direction = stream->readByte(); +		_entries[i].turnDirection = stream->readByte(); +		_entries[i].altCode = stream->readByte(); +		stream->readByte(); // alignment +		_entries[i].endDirection = stream->readByte(); +		stream->readByte(); // alignment +		debug(0, "Turn[%d]: %d %d %d %d %d", i, _entries[i].room, _entries[i].direction, +				_entries[i].turnDirection, _entries[i].altCode, _entries[i].endDirection); +	} +} + +void TurnTable::clear() { +	_entries.clear(); +} + +TurnTable::Entry TurnTable::findEntry(RoomID room, DirectionConstant direction, TurnDirection turnDirection, AlternateID altCode) { +	for (uint32 i = 0; i < _entries.size(); i++) +		if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].turnDirection == turnDirection && _entries[i].altCode == altCode) +			return _entries[i]; + +	return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/turn.h b/engines/pegasus/neighborhood/turn.h new file mode 100644 index 0000000000..329b03eddb --- /dev/null +++ b/engines/pegasus/neighborhood/turn.h @@ -0,0 +1,69 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_TURN_H +#define PEGASUS_NEIGHBORHOOD_TURN_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { +	class SeekableReadStream; +} + +namespace Pegasus { + +class TurnTable { +public: +	TurnTable() {} +	~TurnTable() {} + +	static uint32 getResTag() { return MKTAG('T', 'u', 'r', 'n'); } + +	void loadFromStream(Common::SeekableReadStream *stream); +	void clear(); + +	struct Entry { +		Entry() { endDirection = kNoDirection; } +		bool isEmpty() { return endDirection == kNoDirection; } + +		RoomID room; +		DirectionConstant direction; +		TurnDirection turnDirection; +		AlternateID altCode; +		DirectionConstant endDirection; +	}; + +	Entry findEntry(RoomID room, DirectionConstant direction, TurnDirection turnDirection, AlternateID altCode); + +private: +	Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/view.cpp b/engines/pegasus/neighborhood/view.cpp new file mode 100644 index 0000000000..4e46f5374e --- /dev/null +++ b/engines/pegasus/neighborhood/view.cpp @@ -0,0 +1,60 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/view.h" + +namespace Pegasus { + +void ViewTable::loadFromStream(Common::SeekableReadStream *stream) { +	uint32 count = stream->readUint32BE(); +	_entries.resize(count); + +	for (uint32 i = 0; i < count; i++) { +		_entries[i].room = stream->readUint16BE(); +		_entries[i].direction = stream->readByte(); +		_entries[i].altCode = stream->readByte(); +		_entries[i].time = stream->readUint32BE(); +		debug(0, "View[%d]: %d %d %d %d", i, _entries[i].room, _entries[i].direction, +				_entries[i].altCode, _entries[i].time); +	} +} + +void ViewTable::clear() { +	_entries.clear(); +} + +ViewTable::Entry ViewTable::findEntry(RoomID room, DirectionConstant direction, AlternateID altCode) { +	for (uint32 i = 0; i < _entries.size(); i++) +		if (_entries[i].room == room && _entries[i].direction == direction && _entries[i].altCode == altCode) +			return _entries[i]; + +	return Entry(); +} + +} // End of namespace pegasus diff --git a/engines/pegasus/neighborhood/view.h b/engines/pegasus/neighborhood/view.h new file mode 100644 index 0000000000..3397508b61 --- /dev/null +++ b/engines/pegasus/neighborhood/view.h @@ -0,0 +1,68 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_VIEW_H +#define PEGASUS_NEIGHBORHOOD_VIEW_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { +	class SeekableReadStream; +} + +namespace Pegasus { + +class ViewTable { +public: +	ViewTable() {} +	~ViewTable() {} + +	static uint32 getResTag() { return MKTAG('V', 'i', 'e', 'w'); } + +	void loadFromStream(Common::SeekableReadStream *stream); +	void clear(); + +	struct Entry { +		Entry() { time = 0xffffffff; } +		bool isEmpty() { return time == 0xffffffff; } + +		RoomID	room; +		DirectionConstant direction; +		AlternateID altCode; +		TimeValue time; +	}; + +	Entry findEntry(RoomID room, DirectionConstant direction, AlternateID altCode); + +private: +	Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/wsc/moleculebin.cpp b/engines/pegasus/neighborhood/wsc/moleculebin.cpp new file mode 100644 index 0000000000..210c0ad313 --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/moleculebin.cpp @@ -0,0 +1,127 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/graphics.h" +#include "pegasus/neighborhood/wsc/moleculebin.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +static const CoordType kMoleculeBinWidth = 138; +static const CoordType kMoleculeBinHeight = 128; + +static const CoordType kMoleculeWidth = 66; +static const CoordType kMoleculeHeight = 40; + +static const CoordType kMoleculeBinLeft = kNavAreaLeft + 286; +static const CoordType kMoleculeBinTop = kNavAreaLeft + 96; + +//	Layouts: + +MoleculeBin::MoleculeBin() : DisplayElement(kNoDisplayElement) { +	_highlightColor = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 102); +	_selectedMolecule = -1; +} + +void MoleculeBin::initMoleculeBin() { +	if (!isDisplaying()) { +		for (int i = 0; i < 6; i++) +			_binLayout[i] = i; + +		resetBin(); +		_binImages.getImageFromPICTFile("Images/World Science Center/Molecules"); +		setDisplayOrder(kWSCMoleculeBinOrder); +		setBounds(kMoleculeBinLeft, kMoleculeBinTop, kMoleculeBinLeft + kMoleculeBinWidth, +				kMoleculeBinTop + kMoleculeBinHeight); +		startDisplaying(); +		show(); +	} +} + +void MoleculeBin::cleanUpMoleculeBin() { +	if (isDisplaying()) { +		stopDisplaying(); +		_binImages.deallocateSurface(); +	} +} + +void MoleculeBin::setBinLayout(const uint32 *layout) { +	for (int i = 0; i < 6; i++) +		_binLayout[i] = layout[i]; +} + +void MoleculeBin::highlightMolecule(const uint32 whichMolecule) { +	if (!_moleculeFlags.getFlag(whichMolecule)) { +		_moleculeFlags.setFlag(whichMolecule, true); +		triggerRedraw(); +	} +} + +bool MoleculeBin::isMoleculeHighlighted(uint32 whichMolecule) { +	return _moleculeFlags.getFlag(whichMolecule); +} + +void MoleculeBin::selectMolecule(const int whichMolecule) { +	if (_selectedMolecule != whichMolecule) { +		_selectedMolecule = whichMolecule; +		triggerRedraw(); +	} +} + +void MoleculeBin::resetBin() { +	_moleculeFlags.clearAllFlags(); +	_selectedMolecule = -1; +	triggerRedraw(); +} + +void MoleculeBin::draw(const Common::Rect &) { +	Common::Rect r1(0, 0, kMoleculeWidth, kMoleculeHeight); +	Common::Rect r2 = r1; + +	for (int i = 0; i < 6; i++) { +		r1.moveTo(i * (kMoleculeWidth * 2), 0); + +		if (_moleculeFlags.getFlag(_binLayout[i])) +			r1.translate(kMoleculeWidth, 0); + +		r2.moveTo((_binLayout[i] & 1) * (kMoleculeWidth + 2) + _bounds.left + 2, +				(_binLayout[i] >> 1) * (kMoleculeHeight + 2) + _bounds.top + 2); + +		_binImages.copyToCurrentPort(r1, r2); +	} + +	if (_selectedMolecule >= 0) { +		r2.moveTo((_selectedMolecule & 1) * (kMoleculeWidth + 2) + _bounds.left + 2, +				(_selectedMolecule >> 1) * (kMoleculeHeight + 2) + _bounds.top + 2); + +		Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getWorkArea(); + +		screen->frameRect(r2, _highlightColor); +		r2.grow(1); +		screen->frameRect(r2, _highlightColor); +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/wsc/moleculebin.h b/engines/pegasus/neighborhood/wsc/moleculebin.h new file mode 100644 index 0000000000..3de4b5ed2a --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/moleculebin.h @@ -0,0 +1,72 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_WSC_MOLECULEBIN_H +#define PEGASUS_NEIGHBORHOOD_WSC_MOLECULEBIN_H + +#include "pegasus/elements.h" +#include "pegasus/surface.h" +#include "pegasus/util.h" + +namespace Pegasus { + +enum { +	kMolecule1, +	kMolecule2, +	kMolecule3, +	kMolecule4, +	kMolecule5, +	kMolecule6 +}; + +class MoleculeBin : public DisplayElement { +public: +	MoleculeBin(); +	virtual ~MoleculeBin() {} + +	void initMoleculeBin(); +	void cleanUpMoleculeBin(); + +	void setBinLayout(const uint32 *); + +	void highlightMolecule(const uint32 whichMolecule); +	void selectMolecule(const int whichMolecule); +	void resetBin(); + +	bool isMoleculeHighlighted(uint32); + +protected: +	void draw(const Common::Rect &); + +	Surface _binImages; +	FlagsArray<byte, kMolecule6 + 1> _moleculeFlags; +	int _selectedMolecule; +	uint32 _binLayout[6]; +	uint32 _highlightColor; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/wsc/wsc.cpp b/engines/pegasus/neighborhood/wsc/wsc.cpp new file mode 100644 index 0000000000..1f00999aa7 --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/wsc.cpp @@ -0,0 +1,2540 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +static const CanMoveForwardReason kCantMoveWatchingDiagnosis = kCantMoveLastReason + 1; + +static const CanTurnReason kCantTurnWatchingDiagnosis = kCantTurnLastReason + 1; +static const CanTurnReason kCantTurnWatchingAnalysis = kCantTurnWatchingDiagnosis + 1; +static const CanTurnReason kCantTurnInMoleculeGame = kCantTurnWatchingAnalysis + 1; + +static const TimeScale kMoleculesMovieScale = 600; +static const TimeValue kMoleculeLoopTime = 4 * kMoleculesMovieScale; +static const TimeValue kMoleculeFailTime = 2 * kMoleculesMovieScale; + +enum { +	kMoleculeLoop0Time = 0, +	kMoleculeFail0Time = kMoleculeLoop0Time + kMoleculeLoopTime, +	kMoleculeLoop1Time = kMoleculeFail0Time + kMoleculeFailTime, +	kMoleculeFail1Time = kMoleculeLoop1Time + kMoleculeLoopTime, +	kMoleculeLoop2Time = kMoleculeFail1Time + kMoleculeFailTime, +	kMoleculeFail2Time = kMoleculeLoop2Time + kMoleculeLoopTime, +	kMoleculeLoop3Time = kMoleculeFail2Time + kMoleculeFailTime, +	kMoleculeFail3Time = kMoleculeLoop3Time + kMoleculeLoopTime, +	kMoleculeLoop4Time = kMoleculeFail3Time + kMoleculeFailTime, +	kMoleculeFail4Time = kMoleculeLoop4Time + kMoleculeLoopTime, +	kMoleculeLoop5Time = kMoleculeFail4Time + kMoleculeFailTime, +	kMoleculeFail5Time = kMoleculeLoop5Time + kMoleculeLoopTime, +	kMoleculeLoop6Time = kMoleculeFail5Time + kMoleculeFailTime +}; + +static const TimeValue s_moleculeLoopTimes[] = { +	kMoleculeLoop0Time, +	kMoleculeLoop1Time, +	kMoleculeLoop2Time, +	kMoleculeLoop3Time, +	kMoleculeLoop4Time, +	kMoleculeLoop5Time, +	kMoleculeLoop6Time +}; + +static const TimeValue s_moleculeFailTimes[] = { +	kMoleculeFail0Time, +	kMoleculeFail1Time, +	kMoleculeFail2Time, +	kMoleculeFail3Time, +	kMoleculeFail4Time, +	kMoleculeFail5Time +}; + +static const int16 kAuditoriumAngleOffset = 5; + +static const int kPlasmaEnergyWithShield = kMaxJMPEnergy * 10 / 100; +static const int kPlasmaEnergyNoShield = kMaxJMPEnergy * 20 / 100; + +static const int kTimerEventPlasmaHit = 0; +static const int kTimerEventPlayerGawkingAtRobot = 1; +static const int kTimerEventPlayerGawkingAtRobot2 = 2; + +static const TimeValue kWSCMolecule1In = 0; +static const TimeValue kWSCMolecule1Out = 937; + +static const TimeValue kWSCMolecule2In = 937; +static const TimeValue kWSCMolecule2Out = 1864; + +static const TimeValue kWSCMolecule3In = 1864; +static const TimeValue kWSCMolecule3Out = 2790; + +static const TimeValue kWSCClick1In = 2790; +static const TimeValue kWSCClick1Out = 2890; + +static const TimeValue kWSCClick2In = 2890; +static const TimeValue kWSCClick2Out = 3059; + +static const TimeValue kWSCClick3In = 3059; +static const TimeValue kWSCClick3Out = 3156; + +static const TimeValue kWSCFlashlightClickIn = 3156; +static const TimeValue kWSCFlashlightClickOut = 3211; + +static const TimeValue kWSCBumpIntoWallIn = 3211; +static const TimeValue kWSCBumpIntoWallOut = 3514; + +static const TimeValue kWSCCantTransportIn = 3514; +static const TimeValue kWSCCantTransportOut = 7791; + +static const TimeValue kHernandezNotHomeIn = 7791; +static const TimeValue kHernandezNotHomeOut = 10199; + +static const TimeValue kWashingtonNotHomeIn = 10199; +static const TimeValue kWashingtonNotHomeOut = 12649; + +static const TimeValue kSullivanNotHomeIn = 12649; +static const TimeValue kSullivanNotHomeOut = 15031; + +static const TimeValue kNakamuraNotHomeIn = 15031; +static const TimeValue kNakamuraNotHomeOut = 17545; + +static const TimeValue kGrailisNotHomeIn = 17545; +static const TimeValue kGrailisNotHomeOut = 19937; + +static const TimeValue kTheriaultNotHomeIn = 19937; +static const TimeValue kTheriaultNotHomeOut = 22395; + +static const TimeValue kGlennerNotHomeIn = 22395; +static const TimeValue kGlennerNotHomeOut = 24770; + +static const TimeValue kSinclairNotHomeIn = 24770; +static const TimeValue kSinclairNotHomeOut = 27328; + +static const TimeValue kWSCLabClosedIn = 27328; +static const TimeValue kWSCLabClosedOut = 28904; + +static const TimeValue kSlidingDoorCloseIn = 28904; +static const TimeValue kSlidingDoorCloseOut = 29295; + +static const TimeValue kSlimyDoorCloseIn = 29295; +static const TimeValue kSlimyDoorCloseOut = 29788; + +static const TimeValue kPaging1In = 29788; +static const TimeValue kPaging1Out = 32501; + +static const TimeValue kPaging2In = 32501; +static const TimeValue kPaging2Out = 34892; + +static const TimeValue kCheckInIn = 34892; +static const TimeValue kCheckInOut = 37789; + +static const TimeValue kDrinkAntidoteIn = 37789; +static const TimeValue kDrinkAntidoteOut = 39725; + +static const TimeScale kWSCMovieScale = 600; +static const TimeScale kWSCFramesPerSecond = 15; +static const TimeScale kWSCFrameDuration = 40; + +// Alternate IDs. +static const AlternateID kAltWSCNormal = 0; +static const AlternateID kAltWSCTookMachineGun = 1; +static const AlternateID kAltWSCW0ZDoorOpen = 2; +static const AlternateID kAltWSCPeopleAtW19North = 3; + +// Room IDs. +static const RoomID kWSC02 = 1; +static const RoomID kWSC03 = 4; +static const RoomID kWSC04 = 5; +static const RoomID kWSC06 = 6; +static const RoomID kWSC07 = 7; +static const RoomID kWSC08 = 8; +static const RoomID kWSC09 = 9; +static const RoomID kWSC10 = 10; +static const RoomID kWSC11 = 11; +static const RoomID kWSC13 = 12; +static const RoomID kWSC14 = 13; +static const RoomID kWSC15 = 14; +static const RoomID kWSC16 = 15; +static const RoomID kWSC17 = 16; +static const RoomID kWSC18 = 17; +static const RoomID kWSC19 = 18; +static const RoomID kWSC20 = 19; +static const RoomID kWSC21 = 20; +static const RoomID kWSC22 = 21; +static const RoomID kWSC23 = 22; +static const RoomID kWSC24 = 23; +static const RoomID kWSC25 = 24; +static const RoomID kWSC26 = 25; +static const RoomID kWSC27 = 26; +static const RoomID kWSC28 = 27; +static const RoomID kWSC29 = 28; +static const RoomID kWSC31 = 29; +static const RoomID kWSC32 = 30; +static const RoomID kWSC33 = 31; +static const RoomID kWSC34 = 32; +static const RoomID kWSC35 = 33; +static const RoomID kWSC36 = 34; +static const RoomID kWSC37 = 35; +static const RoomID kWSC38 = 36; +static const RoomID kWSC39 = 37; +static const RoomID kWSC40 = 38; +static const RoomID kWSC41 = 39; +static const RoomID kWSC42 = 40; +static const RoomID kWSC43 = 41; +static const RoomID kWSC44 = 42; +static const RoomID kWSC45 = 43; +static const RoomID kWSC46 = 44; +static const RoomID kWSC47 = 45; +static const RoomID kWSC48 = 46; +static const RoomID kWSC49 = 47; +static const RoomID kWSC50 = 48; +static const RoomID kWSC52 = 49; +static const RoomID kWSC53 = 50; +static const RoomID kWSC54 = 51; +static const RoomID kWSC55 = 52; +static const RoomID kWSC56 = 53; +static const RoomID kWSC57 = 54; +static const RoomID kWSC58 = 55; +static const RoomID kWSC60 = 56; +static const RoomID kWSC60East = 57; +static const RoomID kWSC60North = 58; +static const RoomID kWSC61 = 59; +static const RoomID kWSC61South = 60; +static const RoomID kWSC61West = 61; +static const RoomID kWSC63 = 63; +static const RoomID kWSC64 = 64; +static const RoomID kWSC65 = 65; +static const RoomID kWSC65Screen = 66; +static const RoomID kWSC66 = 67; +static const RoomID kWSC67 = 68; +static const RoomID kWSC68 = 69; +static const RoomID kWSC69 = 70; +static const RoomID kWSC70 = 71; +static const RoomID kWSC71 = 72; +static const RoomID kWSC72 = 73; +static const RoomID kWSC73 = 74; +static const RoomID kWSC74 = 75; +static const RoomID kWSC75 = 76; +static const RoomID kWSC0Z = 77; +static const RoomID kWSC76 = 78; +static const RoomID kWSC77 = 79; +static const RoomID kWSC78 = 80; +static const RoomID kWSC79 = 81; +static const RoomID kWSC80 = 82; +static const RoomID kWSC81 = 83; +static const RoomID kWSC82 = 84; +static const RoomID kWSC83 = 85; +static const RoomID kWSC84 = 86; +static const RoomID kWSC85 = 87; +static const RoomID kWSC86 = 88; +static const RoomID kWSC87 = 89; +static const RoomID kWSC88 = 90; +static const RoomID kWSC89 = 91; +static const RoomID kWSC90 = 92; +static const RoomID kWSC91 = 93; +static const RoomID kWSC92 = 94; +static const RoomID kWSC93 = 95; +static const RoomID kWSC94 = 96; +static const RoomID kWSC95 = 97; +static const RoomID kWSC96 = 98; +static const RoomID kWSC97 = 99; +static const RoomID kWSC98 = 100; +static const RoomID kWSCDeathRoom = 101; + +// Hot Spot Activation IDs. +static const HotSpotActivationID kActivationZoomedInToAnalyzer = 1; +static const HotSpotActivationID kActivationShotByRobot = 2; +static const HotSpotActivationID kActivationWarnedAboutPoison = 3; +static const HotSpotActivationID kActivationMorphScreenOff = 4; +static const HotSpotActivationID kActivationReadyForMorph = 5; +static const HotSpotActivationID kActivationMorphLooping = 6; +static const HotSpotActivationID kActivationMorphInterrupted = 7; +static const HotSpotActivationID kActivationW03NorthOff = 8; +static const HotSpotActivationID kActivationW03NorthReadyForInstructions = 9; +static const HotSpotActivationID kActivationW03NorthSawInstructions = 10; +static const HotSpotActivationID kActivationW03NorthInGame = 11; +static const HotSpotActivationID kActivationReadyForSynthesis = 12; +static const HotSpotActivationID kActivationSynthesizerLooping = 13; +static const HotSpotActivationID kActivationReadyForMap = 14; +static const HotSpotActivationID kActivationSinclairOfficeLocked = 15; +static const HotSpotActivationID kActivationW58SouthDoorLocked = 16; +static const HotSpotActivationID kActivationW61SouthOff = 17; +static const HotSpotActivationID kActivationW61SouthOn = 18; +static const HotSpotActivationID kActivationW61MessagesOff = 19; +static const HotSpotActivationID kActivationW61MessagesOn = 20; +static const HotSpotActivationID kActivationWSCRobotHeadOpen = 21; +static const HotSpotActivationID kActivationRobotTurning = 22; +static const HotSpotActivationID kActivationRobotDead = 23; +static const HotSpotActivationID kActivationRobotGone = 24; + +// Hot Spot IDs. +static const HotSpotID kWSCDropDartSpotID = 5000; +static const HotSpotID kWSCTurnOnAnalyzerSpotID = 5001; +static const HotSpotID kWSCAnalyzerScreenSpotID = 5002; +static const HotSpotID kWSCSpinRobotSpotID = 5003; +static const HotSpotID kWSC01YesSpotID = 5004; +static const HotSpotID kWSC01NoSpotID = 5005; +static const HotSpotID kWSC01AcknowledgeWarningSpotID = 5006; +static const HotSpotID kWSC02SouthMorphSpotID = 5007; +static const HotSpotID kWSC02SouthMessagesSpotID = 5008; +static const HotSpotID kWSC02SouthMorphOutSpotID = 5009; +static const HotSpotID kWSC02ActivateMorphScreenSpotID = 5010; +static const HotSpotID kWSC02SouthStartMorphSpotID = 5011; +static const HotSpotID kWSC02SouthInterruptMorphSpotID = 5012; +static const HotSpotID kWSC02SouthMorphFinishedSpotID = 5013; +static const HotSpotID kWSC02SouthTakeArgonSpotID = 5014; +static const HotSpotID kWSC02SouthMessagesOutSpotID = 5015; +static const HotSpotID kWSC02SouthTakeNitrogenSpotID = 5016; +static const HotSpotID kWSC02SouthPlayMessagesSpotID = 5017; +static const HotSpotID kWSC03NorthActivateScreenSpotID = 5018; +static const HotSpotID kWSC03NorthBuildMoleculeSpotID = 5019; +static const HotSpotID kWSC03NorthProceedSpotID = 5020; +static const HotSpotID kWSC03NorthMolecule1SpotID = 5021; +static const HotSpotID kWSC03NorthMolecule2SpotID = 5022; +static const HotSpotID kWSC03NorthMolecule3SpotID = 5023; +static const HotSpotID kWSC03NorthMolecule4SpotID = 5024; +static const HotSpotID kWSC03NorthMolecule5SpotID = 5025; +static const HotSpotID kWSC03NorthMolecule6SpotID = 5026; +static const HotSpotID kWSC03SouthActivateSynthesizerSpotID = 5027; +static const HotSpotID kWSC03SouthPickUpAntidoteSpotID = 5028; +static const HotSpotID kWSC07SouthMapSpotID = 5029; +static const HotSpotID kW42EastUnlockDoorSpotID = 5030; +static const HotSpotID kW56NorthMapSpotID = 5031; +static const HotSpotID kW58SouthPryDoorSpotID = 5032; +static const HotSpotID kWSC60EastSpotID = 5033; +static const HotSpotID kWSC60NorthSpotID = 5034; +static const HotSpotID kWSC60EastOutSpotID = 5035; +static const HotSpotID kWSC60NorthOutSpotID = 5036; +static const HotSpotID kWSC61EastSpotID = 5037; +static const HotSpotID kWSC61SouthSpotID = 5038; +static const HotSpotID kW61SouthMachineGunSpotID = 5039; +static const HotSpotID kW61SouthDropMachineGunSpotID = 5040; +static const HotSpotID kWSC61WestSpotID = 5041; +static const HotSpotID kWSC61SouthOutSpotID = 5042; +static const HotSpotID kW61SouthActivateSpotID = 5043; +static const HotSpotID kW61SmartAlloysSpotID = 5044; +static const HotSpotID kW61MorphingSpotID = 5045; +static const HotSpotID kW61TimeBendingSpotID = 5046; +static const HotSpotID kWSC61WestOutSpotID = 5047; +static const HotSpotID kW61TurnOnMessagesSpotID = 5048; +static const HotSpotID kW61WhiteMessageSpotID = 5049; +static const HotSpotID kW61WalchekMessageSpotID = 5050; +static const HotSpotID kWSC65SouthScreenSpotID = 5051; +static const HotSpotID kWSC65SouthScreenOutSpotID = 5052; +static const HotSpotID kW98RetinalChipSpotID = 5053; +static const HotSpotID kW98MapChipSpotID = 5054; +static const HotSpotID kW98OpticalChipSpotID = 5055; +static const HotSpotID kW98DropArgonSpotID = 5056; +static const HotSpotID kW98GrabCableSpotID = 5057; +static const HotSpotID kW98OpenRobotSpotID = 5058; +static const HotSpotID kW98StunGunSpotID = 5059; + +// Extra sequence IDs. +static const ExtraID kWSCArrivalFromTSA = 0; +static const ExtraID kWSCShotByRobot = 1; +static const ExtraID kWSCDartScan1 = 2; +static const ExtraID kWSCDartScan2 = 3; +static const ExtraID kWSCDartScanNo = 4; +static const ExtraID kWSCDartScan3 = 5; +static const ExtraID kWSCAnalyzerPowerUp = 6; +static const ExtraID kWSCAnalyzerPowerUpWithDart = 7; +static const ExtraID kWSCDropDartIntoAnalyzer = 8; +static const ExtraID kWSCAnalyzeDart = 9; +static const ExtraID kWSCZoomOutFromAnalyzer = 10; +static const ExtraID kWSCSpinRobot = 11; +static const ExtraID kWSC02MorphZoomNoArgon = 12; +static const ExtraID kWSC02MessagesZoomNoNitrogen = 13; +static const ExtraID kWSC02ZoomOutNoArgon = 14; +static const ExtraID kWSC02TurnOnMorphScreen = 15; +static const ExtraID kWSC02DropToMorphExperiment = 16; +static const ExtraID kWSC02MorphLoop = 17; +static const ExtraID kWSC02MorphInterruption = 18; +static const ExtraID kWSC02MorphFinished = 19; +static const ExtraID kWSC02TurnOffMorphScreen = 20; +static const ExtraID kWSC02SouthViewNoArgon = 21; +static const ExtraID kMessagesMovedToOffice = 22; +static const ExtraID kMessagesOff = 23; +static const ExtraID kMessagesZoomOutNoNitrogen = 24; +static const ExtraID kMessagesMovedToOfficeNoNitrogen = 25; +static const ExtraID kMessagesOffNoNitrogen = 26; +static const ExtraID kMessagesViewNoNitrogen = 27; +static const ExtraID kMessagesViewMachineOnNoNitrogen = 28; +static const ExtraID kW03NorthActivate = 29; +static const ExtraID kW03NorthGetData = 30; +static const ExtraID kW03NorthInstructions = 31; +static const ExtraID kW03NorthPrepMolecule1 = 32; +static const ExtraID kW03NorthPrepMolecule2 = 33; +static const ExtraID kW03NorthPrepMolecule3 = 34; +static const ExtraID kW03NorthFinishSynthesis = 35; +static const ExtraID kW03SouthCreateAntidote = 36; +static const ExtraID kW03SouthAntidoteLoop = 37; +static const ExtraID kW03SouthDeactivate = 38; +static const ExtraID kW03SouthViewNoAntidote = 39; +static const ExtraID kWSC07SouthMap = 40; +static const ExtraID kW17WestPeopleCrossing = 41; +static const ExtraID kW17WestPeopleCrossingView = 42; +static const ExtraID kW21SouthPeopleCrossing = 43; +static const ExtraID kW24SouthPeopleCrossing = 44; +static const ExtraID kW34EastPeopleCrossing = 45; +static const ExtraID kW36WestPeopleCrossing = 46; +static const ExtraID kW38NorthPeopleCrossing = 47; +static const ExtraID kW46SouthPeopleCrossing = 48; +static const ExtraID kW49NorthPeopleCrossing = 49; +static const ExtraID kW49NorthPeopleCrossingView = 50; +static const ExtraID kWSC56SouthMap = 51; +static const ExtraID kNerdAtTheDoor1 = 52; +static const ExtraID kNerdAtTheDoor2 = 53; +static const ExtraID kW61SouthZoomInNoGun = 54; +static const ExtraID kW61Brochure = 55; +static const ExtraID kW61SouthScreenOnWithGun = 56; +static const ExtraID kW61SouthScreenOffWithGun = 57; +static const ExtraID kW61SouthSmartAlloysWithGun = 58; +static const ExtraID kW61SouthMorphingWithGun = 59; +static const ExtraID kW61SouthTimeBendingWithGun = 60; +static const ExtraID kW61SouthZoomOutNoGun = 61; +static const ExtraID kW61SouthScreenOnNoGun = 62; +static const ExtraID kW61SouthScreenOffNoGun = 63; +static const ExtraID kW61SouthSmartAlloysNoGun = 64; +static const ExtraID kW61SouthMorphingNoGun = 65; +static const ExtraID kW61SouthTimeBendingNoGun = 66; +static const ExtraID kW61MessagesOn = 67; +static const ExtraID kW61MessagesOff = 68; +static const ExtraID kW61WhiteMessage = 69; +static const ExtraID kW61WalchekMessage = 70; +static const ExtraID kW61WalchekEasterEgg1 = 71; +static const ExtraID kW62SouthPlasmaRobotAppears = 72; +static const ExtraID kW62ZoomToRobot = 73; +static const ExtraID kW62ZoomOutFromRobot = 74; +static const ExtraID kW62PlasmaDodgeSurvive = 75; +static const ExtraID kW62PlasmaDodgeDie = 76; +static const ExtraID kW65SouthSinclairLecture = 77; +static const ExtraID kW73WestPeopleCrossing = 78; +static const ExtraID kW73WestPeopleCrossingView = 79; +static const ExtraID kW0ZSpottedByWomen = 80; +static const ExtraID kW95RobotShoots = 81; +static const ExtraID kW98MorphsToRobot = 82; +static const ExtraID kW98RobotShoots = 83; +static const ExtraID kW98RobotShocked = 84; +static const ExtraID kW98RobotGassed = 85; +static const ExtraID kW98RobotHeadOpensDark = 86; +static const ExtraID kW98RobotHead000Dark = 87; +static const ExtraID kW98RobotHead001Dark = 88; +static const ExtraID kW98RobotHead010Dark = 89; +static const ExtraID kW98RobotHead011Dark = 90; +static const ExtraID kW98RobotHead100Dark = 91; +static const ExtraID kW98RobotHead101Dark = 92; +static const ExtraID kW98RobotHead110Dark = 93; +static const ExtraID kW98RobotHead111Dark = 94; +static const ExtraID kW98RobotHeadClosesDark = 95; +static const ExtraID kW98WestViewWithGunDark = 96; +static const ExtraID kW98WestViewNoGunDark = 97; +static const ExtraID kW98RobotHeadOpensLight = 98; +static const ExtraID kW98RobotHead000Light = 99; +static const ExtraID kW98RobotHead001Light = 100; +static const ExtraID kW98RobotHead010Light = 101; +static const ExtraID kW98RobotHead011Light = 102; +static const ExtraID kW98RobotHead100Light = 103; +static const ExtraID kW98RobotHead101Light = 104; +static const ExtraID kW98RobotHead110Light = 105; +static const ExtraID kW98RobotHead111Light = 106; +static const ExtraID kW98RobotHeadClosesLight = 107; +static const ExtraID kW98WestViewWithGunLight = 108; +static const ExtraID kW98WestViewNoGunLight = 109; + +static const CoordType kMoleculesMovieLeft = kNavAreaLeft + 112; +static const CoordType kMoleculesMovieTop = kNavAreaTop + 40; + +WSC::WSC(InputHandler *nextHandler, PegasusEngine *owner) : Neighborhood(nextHandler, owner, "WSC", kWSCID), +		_moleculesMovie(kNoDisplayElement) { +	setIsItemTaken(kArgonCanister); +	setIsItemTaken(kSinclairKey); +	setIsItemTaken(kNitrogenCanister); +	setIsItemTaken(kPoisonDart); +	setIsItemTaken(kAntidote); +	setIsItemTaken(kMachineGun); +	setIsItemTaken(kStunGun); +	 +	GameState.setTakenItemID(kArgonPickup, GameState.isTakenItemID(kArgonCanister) && +			GameState.isTakenItemID(kSinclairKey)); +} + +uint16 WSC::getDateResID() const { +	return kDate2310ID; +} + +void WSC::init() { +	Neighborhood::init(); +	 +	_cachedZoomSpot = 0; +	_argonSprite = 0; +	 +	// HACK: Fix the drag item for picking up the Sinclair Key Card +	HotspotInfoTable::Entry *entry = findHotspotEntry(kWSC02SouthTakeArgonSpotID); +	entry->hotspotItem = kArgonPickup; +} + +void WSC::flushGameState() { +	g_energyMonitor->saveCurrentEnergyValue(); +} + +void WSC::start() { +	if (g_energyMonitor) { +		g_energyMonitor->stopEnergyDraining(); +		g_energyMonitor->restoreLastEnergyValue(); +		_vm->resetEnergyDeathReason(); +		g_energyMonitor->startEnergyDraining(); +	} +	 +	if (!GameState.getWSCDidPlasmaDodge()) +		forceStridingStop(kWSC58, kSouth, kAltWSCNormal); +	 +	Neighborhood::start(); +} + +class PryDoorMessage : public AIPlayMessageAction { +public: +	PryDoorMessage() : AIPlayMessageAction("Images/AI/WSC/XW59SD3", false) {} + +protected: +	virtual void performAIAction(AIRule *); +}; + +void PryDoorMessage::performAIAction(AIRule *rule) { +	if (((PegasusEngine *)g_engine)->playerHasItemID(kShieldBiochip) +			&& ((PegasusEngine *)g_engine)->getCurrentBiochip()->getObjectID() != kShieldBiochip) +		AIPlayMessageAction::performAIAction(rule); +} + +void WSC::setUpAIRules() { +	Neighborhood::setUpAIRules(); + +	if (g_AIArea) { +		AIPlayMessageAction *messageAction = new AIPlayMessageAction("Images/AI/WSC/XW1WB1", false); +		AILastExtraCondition *extraCondition = new AILastExtraCondition(kWSCDartScan1); +		AIRule *rule = new AIRule(extraCondition, messageAction); +		g_AIArea->addAIRule(rule); + +		messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); +		AILocationCondition *locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kWSC06, kNorth)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); +		 +		messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kWSC10, kWest)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); +		 +		messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kWSC28, kWest)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); +		 +		messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kWSC49, kWest)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); +		 +		messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kWSC65, kSouth)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); +		 +		messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kWSC73, kSouth)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); +		 +		messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB5A", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kWSC79, kWest)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); +		 +		messageAction = new AIPlayMessageAction("Images/AI/WSC/XW59SD1", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kWSC58, kSouth)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); +		 +		PryDoorMessage *pryDoorMessage = new PryDoorMessage(); +		AIDoorOpenedCondition *doorCondition = new AIDoorOpenedCondition(MakeRoomView(kWSC58, kSouth)); +		rule = new AIRule(doorCondition, pryDoorMessage); +		g_AIArea->addAIRule(rule); +		 +		messageAction = new AIPlayMessageAction("Images/AI/WSC/XW61E", false); +		AIHasItemCondition *itemCondition = new AIHasItemCondition(kMachineGun); +		rule = new AIRule(itemCondition, messageAction); +		g_AIArea->addAIRule(rule); +		 +		messageAction = new AIPlayMessageAction("Images/AI/Globals/XGLOB1E", false); +		locCondition = new AILocationCondition(1); +		locCondition->addLocation(MakeRoomView(kWSC95, kWest)); +		rule = new AIRule(locCondition, messageAction); +		g_AIArea->addAIRule(rule); +	} +} + +Common::String WSC::getBriefingMovie() { +	return "Images/AI/WSC/XWO"; +} + +Common::String WSC::getEnvScanMovie() { +	RoomID room = GameState.getCurrentRoom(); +	 +	if (room >= kWSC01 && room <= kWSC04) +		return "Images/AI/WSC/XWE1"; +	else if (room >= kWSC06 && room <= kWSC58) +		return "Images/AI/WSC/XWE2"; +	else if (room >= kWSC60 && room <= kWSC61West) +		return "Images/AI/WSC/XWE3"; +	else if (room >= kWSC64 && room <= kWSC98) +		return "Images/AI/WSC/XWE4"; + +	return "Images/AI/WSC/XWE5"; +} + +uint WSC::getNumHints() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kWSC10, kWest): +	case MakeRoomView(kWSC28, kWest): +	case MakeRoomView(kWSC49, kWest): +	case MakeRoomView(kWSC65, kSouth): +	case MakeRoomView(kWSC75, kSouth): +	case MakeRoomView(kWSC79, kWest): +		return 2; +	case MakeRoomView(kWSC02, kSouth): +		if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && +				!_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && +				!GameState.getWSCDesignedAntidote()) +			return 3; +		else if (!GameState.getScoringGotNitrogenCanister() || +				!GameState.getScoringGotSinclairKey()) +			return 1; +		break; +	case MakeRoomView(kWSC03, kNorth): +		if (inSynthesizerGame() || (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && +				!_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && +				!GameState.getWSCDesignedAntidote())) +			return 3; +		break; +	case MakeRoomView(kWSC01, kNorth): +	case MakeRoomView(kWSC01, kSouth): +	case MakeRoomView(kWSC01, kEast): +	case MakeRoomView(kWSC01, kWest): +	case MakeRoomView(kWSC02, kNorth): +	case MakeRoomView(kWSC02, kEast): +	case MakeRoomView(kWSC02, kWest): +	case MakeRoomView(kWSC02Morph, kNorth): +	case MakeRoomView(kWSC02Morph, kEast): +	case MakeRoomView(kWSC02Morph, kWest): +	case MakeRoomView(kWSC02Messages, kNorth): +	case MakeRoomView(kWSC02Messages, kEast): +	case MakeRoomView(kWSC02Messages, kWest): +	case MakeRoomView(kWSC03, kSouth): +	case MakeRoomView(kWSC03, kEast): +	case MakeRoomView(kWSC03, kWest): +	case MakeRoomView(kWSC04, kNorth): +	case MakeRoomView(kWSC04, kSouth): +	case MakeRoomView(kWSC04, kEast): +	case MakeRoomView(kWSC04, kWest): +		if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && +				!_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && +				!GameState.getWSCDesignedAntidote()) +			return 3; +		break; +	case MakeRoomView(kWSC02Messages, kSouth): +		if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && +				!_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && +				!GameState.getWSCDesignedAntidote()) +			return 3; +		else if (!GameState.getScoringGotNitrogenCanister()) +			return 1; +		break; +	case MakeRoomView(kWSC02Morph, kSouth): +		if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && +				!_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && +				!GameState.getWSCDesignedAntidote()) +			return 3; +		else if (!GameState.getScoringGotSinclairKey()) +			return 1; +		break; +	case MakeRoomView(kWSC42, kEast): +		if (!GameState.isCurrentDoorOpen()) +			return 1; +		break; +	case MakeRoomView(kWSC58, kSouth): +		if (GameState.isCurrentDoorOpen()) { +			if (GameState.getWSCDidPlasmaDodge()) +				return 0; +			else +				return 1; +		} else if (_vm->playerHasItemID(kCrowbar)) +			return 2; + +		return 3; +	case MakeRoomView(kWSC61, kEast): +		if (!GameState.getScoringSawBrochure()) +			return 1; +		break; +	case MakeRoomView(kWSC61, kSouth): +		if (!GameState.getScoringSawSinclairEntry1() || +				!GameState.getScoringSawSinclairEntry2() || +				!GameState.getScoringSawSinclairEntry3()) +			return 1; +		break; +	case MakeRoomView(kWSC98, kWest): +		if (getCurrentActivation() == kActivationRobotTurning) +			return 1; +		break; +	} + +	return 0; +} + +Common::String WSC::getHintMovie(uint hintNum) { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kWSC10, kWest): +	case MakeRoomView(kWSC28, kWest): +	case MakeRoomView(kWSC49, kWest): +	case MakeRoomView(kWSC65, kSouth): +	case MakeRoomView(kWSC75, kSouth): +	case MakeRoomView(kWSC79, kWest): +		if (hintNum == 1) +			return "Images/AI/Globals/XGLOB5B"; + +		return "Images/AI/Globals/XGLOB5C"; +	case MakeRoomView(kWSC02, kSouth): +		if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && +				!_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && +				!GameState.getWSCDesignedAntidote()) +			return Common::String::format("Images/AI/WSC/XWPH%d", hintNum); + +		return "Images/AI/Globals/XGLOB1C"; +	case MakeRoomView(kWSC61, kEast): +	case MakeRoomView(kWSC61, kSouth): +		return "Images/AI/Globals/XGLOB1C"; +	case MakeRoomView(kWSC42, kEast): +		if (_vm->playerHasItemID(kSinclairKey)) +			return "Images/AI/Globals/XGLOB1A"; + +		return "Images/AI/Globals/XGLOB2C"; +	case MakeRoomView(kWSC58, kSouth): +		switch (hintNum) { +		case 1: +			if (GameState.isCurrentDoorOpen()) { +				// Only get here if we haven't done the plasma dodge game... +				if (_vm->playerHasItemID(kShieldBiochip)) +					return "Images/AI/Globals/XGLOB1A"; +				else +					return "Images/AI/Globals/XGLOB3F"; +			} else if (_vm->playerHasItemID(kCrowbar)) { +				return "Images/AI/Globals/XGLOB1A"; +			} + +			return "Images/AI/Globals/XGLOB1B"; +		case 2: +			// Only get here if the door is still locked... +			if (_vm->playerHasItemID(kCrowbar)) +				return "Images/AI/WSC/XW59SD2"; + +			return "Images/AI/Globals/XGLOB2D"; +		case 3: +			// Only get here if the door is still locked and we don't have the +			// crowbar... +			return "Images/AI/WSC/XW59SD2"; +		} +		break; +	case MakeRoomView(kWSC03, kNorth): +		if (inSynthesizerGame()) +			return Common::String::format("Images/AI/WSC/XW03NH%d", hintNum); + +		return Common::String::format("Images/AI/WSC/XWPH%d", hintNum); +	case MakeRoomView(kWSC01, kNorth): +	case MakeRoomView(kWSC01, kSouth): +	case MakeRoomView(kWSC01, kEast): +	case MakeRoomView(kWSC01, kWest): +	case MakeRoomView(kWSC02, kNorth): +	case MakeRoomView(kWSC02, kEast): +	case MakeRoomView(kWSC02, kWest): +	case MakeRoomView(kWSC02Morph, kNorth): +	case MakeRoomView(kWSC02Morph, kEast): +	case MakeRoomView(kWSC02Morph, kWest): +	case MakeRoomView(kWSC02Messages, kNorth): +	case MakeRoomView(kWSC02Messages, kEast): +	case MakeRoomView(kWSC02Messages, kWest): +	case MakeRoomView(kWSC03, kSouth): +	case MakeRoomView(kWSC03, kEast): +	case MakeRoomView(kWSC03, kWest): +	case MakeRoomView(kWSC04, kNorth): +	case MakeRoomView(kWSC04, kSouth): +	case MakeRoomView(kWSC04, kEast): +	case MakeRoomView(kWSC04, kWest): +		// analyzer hint +		return Common::String::format("Images/AI/WSC/XWPH%d", hintNum); +	case MakeRoomView(kWSC02Messages, kSouth): +	case MakeRoomView(kWSC02Morph, kSouth): +		if (_vm->getEnergyDeathReason() == kDeathDidntStopPoison && +				!_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag) && +				!GameState.getWSCDesignedAntidote()) +			// analyzer hint +			return Common::String::format("Images/AI/WSC/XWPH%d", hintNum); + +		return "Images/AI/Globals/XGLOB1C"; +	case MakeRoomView(kWSC98, kWest): +		return "Images/AI/WSC/XW98WH2"; +	} + +	return ""; +} + +void WSC::prepareForAIHint(const Common::String &movieName) { +	if (movieName == "Images/AI/WSC/XW98WH2" && isEventTimerRunning()) +		pauseTimer(); +} + +void WSC::cleanUpAfterAIHint(const Common::String &movieName) { +	if (movieName == "Images/AI/WSC/XW98WH2" && isEventTimerRunning()) +		resumeTimer(); +} + +bool WSC::okayToJump() {	 +	if (GameState.getWSCPoisoned()) { +		die(kDeathDidntStopPoison); +		return false; +	} + +	bool result = Neighborhood::okayToJump(); +	if (!result) +		playSpotSoundSync(kWSCCantTransportIn, kWSCCantTransportOut); + +	return result; +} + +TimeValue WSC::getViewTime(const RoomID room, const DirectionConstant direction) { +	ExtraID viewExtra = 0xffffffff; +	ExtraTable::Entry extra; + +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kWSC01, kWest): +		if (!GameState.getWSCSeenTimeStream()) { +			getExtraEntry(kWSCArrivalFromTSA, extra); +			return extra.movieStart; +		} else if (GameState.getWSCPoisoned() && !GameState.getWSCAnsweredAboutDart()) { +			viewExtra = kWSCDartScan1; +		} +		break; +	case MakeRoomView(kWSC02Morph, kSouth): +		if (GameState.isTakenItemID(kArgonPickup) || GameState.isTakenItemID(kArgonCanister)) +			viewExtra = kWSC02SouthViewNoArgon; +		break; +	case MakeRoomView(kWSC02Messages, kSouth): +		if (GameState.isTakenItemID(kNitrogenCanister)) { +			if (_privateFlags.getFlag(kWSCPrivateLabMessagesOpenFlag)) +				viewExtra = kMessagesViewMachineOnNoNitrogen; +			else +				viewExtra = kMessagesViewNoNitrogen; +		} +		break; +	case MakeRoomView(kWSC03, kSouth): +		if (_privateFlags.getFlag(kWSCDraggingAntidoteFlag)) +			viewExtra = kW03SouthViewNoAntidote; +		break; +	case MakeRoomView(kWSC17, kWest): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt17WestFlag)) +			viewExtra = kW17WestPeopleCrossingView; +		break; +	case MakeRoomView(kWSC49, kNorth): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt49NorthFlag)) +			viewExtra = kW49NorthPeopleCrossingView; +		break; +	case MakeRoomView(kWSC73, kWest): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt73WestFlag)) +			viewExtra = kW73WestPeopleCrossingView; +		break; +	case MakeRoomView(kWSC98, kWest): +		if (GameState.getWSCRobotDead()) { +			if (GameState.getWSCRobotGone()) { +				if (GameState.isTakenItemID(kStunGun)) { +					if (GameState.getWSCCatwalkDark()) +						viewExtra = kW98WestViewNoGunDark; +					else +						viewExtra = kW98WestViewNoGunLight; +				} else { +					if (GameState.getWSCCatwalkDark()) +						viewExtra = kW98WestViewWithGunDark; +					else +						viewExtra = kW98WestViewWithGunLight; +				} +			} else if (_privateFlags.getFlag(kWSCPrivateRobotHeadOpenFlag)) { +				if (GameState.getWSCCatwalkDark()) +					viewExtra = kW98RobotHead111Dark; +				else +					viewExtra = kW98RobotHead111Light; + +				if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag)) +					viewExtra -= 1; +				if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag)) +					viewExtra -= 2; +				if (_privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) +					viewExtra -= 4; +			} else if (GameState.getWSCRobotDead()) { +				// Should only happen on loading a saved game, so it can take its time. +				if (GameState.getWSCCatwalkDark()) +					viewExtra = kW98RobotShocked; +				else +					viewExtra = kW98RobotGassed; +			} +		} +		break; +	} + +	if (viewExtra != 0xffffffff) { +		getExtraEntry(viewExtra, extra); +		return extra.movieEnd - 1; +	} + +	return Neighborhood::getViewTime(room, direction); +} + +void WSC::findSpotEntry(const RoomID room, const DirectionConstant direction, SpotFlags flags, SpotTable::Entry &spotEntry) { +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kWSC58, kSouth): +	case MakeRoomView(kWSC79, kWest): +		if ((flags & kSpotOnTurnMask) != 0) { +			spotEntry.clear(); +			return; +		} +		break; +	} + +	Neighborhood::findSpotEntry(room, direction, flags, spotEntry); +} + +void WSC::getZoomEntry(const HotSpotID id, ZoomTable::Entry &zoomEntry) { +	Neighborhood::getZoomEntry(id, zoomEntry); + +	ExtraTable::Entry extra; +	ExtraID zoomExtra = 0xffffffff; + +	switch (id) { +	case kWSC02SouthMessagesSpotID: +		if (GameState.isTakenItemID(kNitrogenCanister)) +			zoomExtra = kWSC02MessagesZoomNoNitrogen; +		break; +	case kWSC02SouthMessagesOutSpotID: +		if (GameState.isTakenItemID(kNitrogenCanister)) +			zoomExtra = kMessagesZoomOutNoNitrogen; +		break; +	case kWSC02SouthMorphSpotID: +		if (GameState.isTakenItemID(kArgonCanister)) +			zoomExtra = kWSC02MorphZoomNoArgon; +		break; +	case kWSC02SouthMorphOutSpotID: +		if (GameState.isTakenItemID(kArgonCanister)) +			zoomExtra = kWSC02ZoomOutNoArgon; +		break; +	case kWSC61SouthSpotID: +		if (GameState.isTakenItemID(kMachineGun)) +			zoomExtra = kW61SouthZoomInNoGun; +		break; +	case kWSC61SouthOutSpotID: +		if (GameState.isTakenItemID(kMachineGun)) +			zoomExtra = kW61SouthZoomOutNoGun; +		break; +	} + +	if (zoomExtra != 0xffffffff) { +		getExtraEntry(zoomExtra, extra); +		zoomEntry.movieStart = extra.movieStart; +		zoomEntry.movieEnd = extra.movieEnd; +	} +} + +void WSC::getExtraEntry(const uint32 id, ExtraTable::Entry &extraEntry) { +	switch (id) { +	case kWSCZoomOutFromAnalyzer: +		Neighborhood::getExtraEntry(kWSCZoomOutFromAnalyzer, extraEntry); +		extraEntry.movieEnd = extraEntry.movieStart + 14 * kWSCFrameDuration; +		break; +	case kW61WalchekMessage: +		if (GameState.getEasterEgg()) +			Neighborhood::getExtraEntry(kW61WalchekEasterEgg1, extraEntry); +		else +			Neighborhood::getExtraEntry(id, extraEntry); +		break; +	case kW61SouthScreenOnWithGun: +		if (GameState.isTakenItemID(kMachineGun)) +			Neighborhood::getExtraEntry(id, extraEntry); +		else +			Neighborhood::getExtraEntry(kW61SouthScreenOnNoGun, extraEntry); +		break; +	case kW61SouthSmartAlloysWithGun: +		if (GameState.isTakenItemID(kMachineGun)) +			Neighborhood::getExtraEntry(id, extraEntry); +		else +			Neighborhood::getExtraEntry(kW61SouthSmartAlloysNoGun, extraEntry); +		break; +	case kW61SouthMorphingWithGun: +		if (GameState.isTakenItemID(kMachineGun)) +			Neighborhood::getExtraEntry(id, extraEntry); +		else +			Neighborhood::getExtraEntry(kW61SouthMorphingNoGun, extraEntry); +		break; +	case kW61SouthTimeBendingWithGun: +		if (GameState.isTakenItemID(kMachineGun)) +			Neighborhood::getExtraEntry(id, extraEntry); +		else +			Neighborhood::getExtraEntry(kW61SouthTimeBendingNoGun, extraEntry); +		break; +	case kW98RobotHeadOpensLight: +		if (GameState.getWSCCatwalkDark()) +			Neighborhood::getExtraEntry(kW98RobotHeadOpensDark, extraEntry); +		else +			Neighborhood::getExtraEntry(id, extraEntry); +		break; +	default: +		Neighborhood::getExtraEntry(id, extraEntry); +		break; +	} +} + +CanMoveForwardReason WSC::canMoveForward(ExitTable::Entry &entry) { +	if (GameState.getCurrentRoomAndView() == MakeRoomView(kWSC01, kWest) && +			getCurrentActivation() != kActivateHotSpotAlways) +		return kCantMoveWatchingDiagnosis; + +	return Neighborhood::canMoveForward(entry); +} + +// Also add cases here for compound analyzer... +CanTurnReason WSC::canTurn(TurnDirection turnDirection, DirectionConstant &nextDir) { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kWSC01, kWest): +		if (getCurrentActivation() != kActivateHotSpotAlways) +			return kCantTurnWatchingDiagnosis; +		break; +	case MakeRoomView(kWSC01, kEast): +		if (getCurrentActivation() != kActivateHotSpotAlways) +			return kCantTurnWatchingAnalysis; +		break; +	case MakeRoomView(kWSC03, kNorth): +		if (_privateFlags.getFlag(kWSCPrivateInMoleculeGameFlag)) +			return kCantTurnInMoleculeGame; +		break; +	} + +	return Neighborhood::canTurn(turnDirection, nextDir); +} + +CanOpenDoorReason WSC::canOpenDoor(DoorTable::Entry &entry) { +	switch (GameState.getCurrentRoom()) { +	case kWSC42: +		if (!_privateFlags.getFlag(kWSCPrivateSinclairOfficeOpenFlag)) +			return kCantOpenLocked; +		break; +	case kWSC58: +		if (!_privateFlags.getFlag(kWSCPrivate58SouthOpenFlag)) +			return kCantOpenLocked; +		break; +	} + +	return Neighborhood::canOpenDoor(entry); +} + +void WSC::bumpIntoWall() { +	requestSpotSound(kWSCBumpIntoWallIn, kWSCBumpIntoWallOut, kFilterAllInput, 0); +	Neighborhood::bumpIntoWall(); +} + +void WSC::closeDoorOffScreen(const RoomID room, const DirectionConstant) { +	Item *keyCard; + +	switch (room) { +	case kWSC58: +	case kWSC62: +	case kWSC63: +	case kWSC64: +	case kWSC85: +	case kWSC86: +	case kWSC88: +	case kWSC89: +		playSpotSoundSync(kSlidingDoorCloseIn, kSlidingDoorCloseOut); +		break; +	case kWSC81: +	case kWSC82: +	case kWSC92: +	case kWSC93: +		keyCard = g_allItems.findItemByID(kKeyCard); +		if (keyCard->getItemState() == kFlashlightOn && (GameState.getCurrentRoom() == kWSC81 || +				GameState.getCurrentRoom() == kWSC93)) { +			keyCard->setItemState(kFlashlightOff); +			playSpotSoundSync(kWSCFlashlightClickIn, kWSCFlashlightClickOut); +		} else if (keyCard->getItemState() == kFlashlightOff && (GameState.getCurrentRoom() == kWSC82 || +				GameState.getCurrentRoom() == kWSC92)) { +			keyCard->setItemState(kFlashlightOn); +			playSpotSoundSync(kWSCFlashlightClickIn, kWSCFlashlightClickOut); +		} + +		playSpotSoundSync(kSlimyDoorCloseIn, kSlimyDoorCloseOut); +		break; +	default: +		playSpotSoundSync(kSlimyDoorCloseIn, kSlimyDoorCloseOut); +		break; +	} +} + +void WSC::cantMoveThatWay(CanMoveForwardReason reason) { +	if (reason != kCantMoveWatchingDiagnosis) +		Neighborhood::cantMoveThatWay(reason); +} + +void WSC::cantOpenDoor(CanOpenDoorReason reason) { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kWSC22, kWest): +		playSpotSoundSync(kNakamuraNotHomeIn, kNakamuraNotHomeOut); +		break; +	case MakeRoomView(kWSC23, kEast): +		playSpotSoundSync(kHernandezNotHomeIn, kHernandezNotHomeOut); +		break; +	case MakeRoomView(kWSC26, kWest): +		playSpotSoundSync(kGrailisNotHomeIn, kGrailisNotHomeOut); +		break; +	case MakeRoomView(kWSC27, kEast): +		playSpotSoundSync(kWashingtonNotHomeIn, kWashingtonNotHomeOut); +		break; +	case MakeRoomView(kWSC32, kWest): +		playSpotSoundSync(kTheriaultNotHomeIn, kTheriaultNotHomeOut); +		break; +	case MakeRoomView(kWSC33, kEast): +		playSpotSoundSync(kSullivanNotHomeIn, kSullivanNotHomeOut); +		break; +	case MakeRoomView(kWSC41, kWest): +		playSpotSoundSync(kGlennerNotHomeIn, kGlennerNotHomeOut); +		break; +	case MakeRoomView(kWSC42, kEast): +		playSpotSoundSync(kSinclairNotHomeIn, kSinclairNotHomeOut); +		break; +	case MakeRoomView(kWSC15, kWest): +	case MakeRoomView(kWSC25, kWest): +	case MakeRoomView(kWSC33, kWest): +	case MakeRoomView(kWSC41, kEast): +	case MakeRoomView(kWSC46, kWest): +		playSpotSoundSync(kWSCLabClosedIn, kWSCLabClosedOut); +		break; +	default: +		Neighborhood::cantOpenDoor(reason); +		break; +	} +} + +void WSC::doorOpened() { +	Neighborhood::doorOpened(); + +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kWSC42, kEast): +		_vm->addItemToInventory((InventoryItem *)g_allItems.findItemByID(kSinclairKey)); +		break; +	case MakeRoomView(kWSC58, kSouth): +		GameState.setScoringUsedCrowBarInWSC(); +		_vm->addItemToInventory((InventoryItem *)g_allItems.findItemByID(kCrowbar)); +		break; +	case MakeRoomView(kWSC06, kNorth): +	case MakeRoomView(kWSC79, kWest): +		die(kDeathArrestedInWSC); +		break; +	case MakeRoomView(kWSC60, kWest): +		if (_vm->itemInInventory(kMachineGun)) +			startExtraSequence(kNerdAtTheDoor2, kExtraCompletedFlag, kFilterNoInput); +		else if (!GameState.getWSCSeenNerd()) +			startExtraSequence(kNerdAtTheDoor1, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kWSC95, kWest): +		GameState.setScoringOpenedCatwalk(); +		scheduleEvent(kGawkAtRobotTime, 1, kTimerEventPlayerGawkingAtRobot); +		break; +	} +} + +void WSC::turnLeft() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kWSC17, kNorth): +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt17WestFlag) && _vm->getRandomNumber(2) == 0) +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, true); +		break; +	case MakeRoomView(kWSC49, kEast): +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt49NorthFlag) && _vm->getRandomNumber(2) == 0) +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, true); +		break; +	case MakeRoomView(kWSC73, kNorth): +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt73WestFlag) && _vm->getRandomNumber(2) == 0) +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, true); +		break; +	case MakeRoomView(kWSC73, kWest): +		if (!GameState.getWSCBeenAtWSC93()) +			setCurrentAlternate(kAltWSCW0ZDoorOpen); +		break; +	case MakeRoomView(kWSC95, kWest): +		cancelEvent(); +		break; +	} + +	Neighborhood::turnLeft(); +} + +void WSC::turnRight() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kWSC17, kSouth): +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt17WestFlag) && _vm->getRandomNumber(2) == 0) +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, true); +		break; +	case MakeRoomView(kWSC49, kWest): +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt49NorthFlag) && _vm->getRandomNumber(2) == 0) +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, true); +		break; +	case MakeRoomView(kWSC73, kSouth): +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt73WestFlag) && _vm->getRandomNumber(2) == 0) +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, true); +		break; +	case MakeRoomView(kWSC73, kEast): +		if (!GameState.getWSCBeenAtWSC93()) +			setCurrentAlternate(kAltWSCW0ZDoorOpen); +		break; +	case MakeRoomView(kWSC95, kWest): +		cancelEvent(); +		break; +	} + +	Neighborhood::turnRight(); +} + +void WSC::moveForward() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kWSC19, kNorth): +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt19NorthFlag)) +			setCurrentAlternate(kAltWSCPeopleAtW19North); +		break; +	case MakeRoomView(kWSC95, kWest): +		cancelEvent(); +		break; +	} + +	Neighborhood::moveForward(); +} + +void WSC::zoomTo(const Hotspot *hotspot) { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kWSC02Messages, kSouth): +		if (_privateFlags.getFlag(kWSCPrivateLabMessagesOpenFlag)) { +			_cachedZoomSpot = hotspot; +			if (GameState.isTakenItemID(kNitrogenCanister)) +				startExtraSequence(kMessagesOffNoNitrogen, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kMessagesOff, kExtraCompletedFlag, kFilterNoInput); +			return; +		} +		break; +	case MakeRoomView(kWSC61West, kWest): +		if (GameState.getWSCOfficeMessagesOpen()) { +			_cachedZoomSpot = hotspot; +			startExtraSequence(kW61MessagesOff, kExtraCompletedFlag, kFilterNoInput); +			return; +		} +		break; +	case MakeRoomView(kWSC61South, kSouth): +		if (_privateFlags.getFlag(kWSCPrivateOfficeLogOpenFlag)) { +			_cachedZoomSpot = hotspot; +			if (GameState.isTakenItemID(kMachineGun)) +				startExtraSequence(kW61SouthScreenOffNoGun, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kW61SouthScreenOffWithGun, kExtraCompletedFlag, kFilterNoInput); +			return; +		} +		break; +	} + +	Neighborhood::zoomTo(hotspot); +} + +void WSC::startExtraSequence(const ExtraID extraID, const NotificationFlags flags, const InputBits interruptionFilter) { +	if (extraID == kW61Brochure) +		loadLoopSound1(""); + +	Neighborhood::startExtraSequence(extraID, flags, interruptionFilter); +} + +int16 WSC::getStaticCompassAngle(const RoomID room, const DirectionConstant dir) { +	int16 angle = Neighborhood::getStaticCompassAngle(room, dir); + +	switch (room) { +	case kWSC02Messages: +		angle -= 50; +		break; +	case kWSC02Morph: +		angle += 5; +		break; +	case kWSC60East: +		angle -= 10; +		break; +	case kWSC66: +		angle -= kAuditoriumAngleOffset; +		break; +	case kWSC67: +		angle += kAuditoriumAngleOffset; +		break; +	case kWSC68: +		angle -= kAuditoriumAngleOffset * 2; +		break; +	case kWSC69: +		angle += kAuditoriumAngleOffset * 2; +		break; +	case kWSC70: +		angle -= kAuditoriumAngleOffset * 3; +		break; +	case kWSC71: +		angle += kAuditoriumAngleOffset * 3; +		break; +	case kWSC72: +		if (dir == kEast || dir == kWest) +			angle -= kAuditoriumAngleOffset * 4; +		break; +	case kWSC73: +		if (dir == kEast || dir == kWest) +			angle += kAuditoriumAngleOffset * 4; +		break; +	} + +	return angle; +} + +void WSC::getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove) { +	Neighborhood::getExitCompassMove(exitEntry, compassMove); + +	if (exitEntry.room == kWSC65 && exitEntry.direction == kSouth) { +		compassMove.insertFaderKnot(exitEntry.movieStart + 100 * kWSCFrameDuration, 180); +		compassMove.insertFaderKnot(exitEntry.movieStart + 108 * kWSCFrameDuration, 150); +		compassMove.insertFaderKnot(exitEntry.movieEnd, 150); +	} +} + +void WSC::getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove) { +	switch (entry.extra) { +	case kW61Brochure: +		compassMove.insertFaderKnot(entry.movieStart + 15 * kWSCFrameDuration, 85); +		compassMove.insertFaderKnot(entry.movieEnd - 15 * kWSCFrameDuration, 85); +		compassMove.insertFaderKnot(entry.movieEnd, 90); +		break; +	default: +		Neighborhood::getExtraCompassMove(entry, compassMove); +		break; +	} +} + +void WSC::loadAmbientLoops() { +	RoomID room = GameState.getCurrentRoom(); + +	if (room >= kWSC01 && room <= kWSC04) { +		if (GameState.getWSCSeenTimeStream()) +			loadLoopSound1("Sounds/World Science Center/WLabLoop.22K.AIFF", 0x100 / 2); +	} else if ((room >= kWSC06 && room <= kWSC58) || (room >= kWSC62 && room <= kWSC63)) +		loadLoopSound1("Sounds/World Science Center/Organic Walls.22K.AIFF", 0x100 / 2); +	else if (room >= kWSC82 && room <= kWSC92) +		loadLoopSound1("Sounds/World Science Center/Creature Feature.22K.AIFF"); +	else if ((room >= kWSC60 && room <= kWSC61West) || (room >= kWSC64 && room <= kWSC81) || +			(room >= kWSC93 && room <= kWSC97)) +		loadLoopSound1("Sounds/World Science Center/The Other Side.22K.AIFF", 0x100 / 12); +	else if (room == kWSC98) +		loadLoopSound1("Sounds/World Science Center/WCatLoop.22K.AIFF"); +} + +void WSC::checkContinuePoint(const RoomID room, const DirectionConstant direction) { +	switch (MakeRoomView(room, direction)) { +	case MakeRoomView(kWSC07, kNorth): +	case MakeRoomView(kWSC11, kSouth): +	case MakeRoomView(kWSC13, kSouth): +	case MakeRoomView(kWSC13, kWest): +	case MakeRoomView(kWSC16, kWest): +	case MakeRoomView(kWSC17, kEast): +	case MakeRoomView(kWSC19, kWest): +	case MakeRoomView(kWSC28, kNorth): +	case MakeRoomView(kWSC28, kSouth): +	case MakeRoomView(kWSC28, kEast): +	case MakeRoomView(kWSC28, kWest): +	case MakeRoomView(kWSC29, kNorth): +	case MakeRoomView(kWSC29, kSouth): +	case MakeRoomView(kWSC29, kEast): +	case MakeRoomView(kWSC29, kWest): +	case MakeRoomView(kWSC40, kEast): +	case MakeRoomView(kWSC42, kEast): +	case MakeRoomView(kWSC49, kWest): +	case MakeRoomView(kWSC49, kNorth): +	case MakeRoomView(kWSC50, kNorth): +	case MakeRoomView(kWSC55, kEast): +	case MakeRoomView(kWSC65, kSouth): +	case MakeRoomView(kWSC65, kEast): +	case MakeRoomView(kWSC65, kWest): +	case MakeRoomView(kWSC72, kEast): +	case MakeRoomView(kWSC72, kSouth): +	case MakeRoomView(kWSC73, kWest): +	case MakeRoomView(kWSC73, kSouth): +	case MakeRoomView(kWSC79, kWest): +	case MakeRoomView(kWSC81, kEast): +	case MakeRoomView(kWSC93, kNorth): +	case MakeRoomView(kWSC95, kWest): +		makeContinuePoint(); +		break; +	case MakeRoomView(kWSC58, kSouth): +		if (!GameState.getWSCDidPlasmaDodge()) +			makeContinuePoint(); +		break; +	case MakeRoomView(kWSC60, kWest): +		if (_vm->playerHasItemID(kMachineGun)) +			makeContinuePoint(); +		break; +	} +} + +void WSC::arriveAt(const RoomID room, const DirectionConstant dir) {	 +	switch (MakeRoomView(room, dir)) { +	case MakeRoomView(kWSC60, kNorth): +	case MakeRoomView(kWSC60, kSouth): +	case MakeRoomView(kWSC60, kEast): +	case MakeRoomView(kWSC60, kWest): +	case MakeRoomView(kWSC60East, kNorth): +	case MakeRoomView(kWSC60East, kSouth): +	case MakeRoomView(kWSC60East, kEast): +	case MakeRoomView(kWSC60East, kWest): +	case MakeRoomView(kWSC60North, kNorth): +	case MakeRoomView(kWSC60North, kSouth): +	case MakeRoomView(kWSC60North, kEast): +	case MakeRoomView(kWSC60North, kWest): +	case MakeRoomView(kWSC61, kNorth): +	case MakeRoomView(kWSC61, kSouth): +	case MakeRoomView(kWSC61, kEast): +	case MakeRoomView(kWSC61, kWest): +	case MakeRoomView(kWSC61South, kNorth): +	case MakeRoomView(kWSC61South, kSouth): +	case MakeRoomView(kWSC61South, kEast): +	case MakeRoomView(kWSC61South, kWest): +	case MakeRoomView(kWSC61West, kNorth): +	case MakeRoomView(kWSC61West, kSouth): +	case MakeRoomView(kWSC61West, kEast): +	case MakeRoomView(kWSC61West, kWest): +		if (GameState.isTakenItemID(kMachineGun)) +			setCurrentAlternate(kAltWSCTookMachineGun); +		else +			setCurrentAlternate(kAltWSCNormal); +		break; +	case MakeRoomView(kWSC73, kSouth): +	case MakeRoomView(kWSC75, kNorth): +	case MakeRoomView(kWSC75, kSouth): +	case MakeRoomView(kWSC75, kEast): +	case MakeRoomView(kWSC75, kWest): +		if (!GameState.getWSCBeenAtWSC93()) +			setCurrentAlternate(kAltWSCW0ZDoorOpen); +		break; +	} + +	Neighborhood::arriveAt(room, dir); + +	switch (MakeRoomView(room, dir)) { +	case MakeRoomView(kWSC01, kWest): +		if (!GameState.getWSCSeenTimeStream()) { +			requestExtraSequence(kWSCArrivalFromTSA, kExtraCompletedFlag, kFilterNoInput); +			requestExtraSequence(kWSCShotByRobot, 0, kFilterNoInput); +			requestExtraSequence(kWSCDartScan1, kExtraCompletedFlag, kFilterNoInput); +		} else if (GameState.getWSCPoisoned() && !GameState.getWSCAnsweredAboutDart()) { +			setCurrentActivation(kActivationShotByRobot); +		} +		break; +	case MakeRoomView(kWSC01, kEast): +		if (GameState.getWSCDartInAnalyzer()) +			requestExtraSequence(kWSCDropDartIntoAnalyzer, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kWSC02Morph, kSouth): +		setCurrentActivation(kActivationMorphScreenOff); +		break; +	case MakeRoomView(kWSC03, kNorth): +		setCurrentActivation(kActivationW03NorthOff); +		break; +	case MakeRoomView(kWSC03, kSouth): +		if (GameState.getWSCDesignedAntidote() && !GameState.getWSCPickedUpAntidote()) +			setCurrentActivation(kActivationReadyForSynthesis); +		break; +	case MakeRoomView(kWSC16, kNorth): +		if (getCurrentAlternate() == kAltWSCPeopleAtW19North) { +			setCurrentAlternate(kAltWSCNormal); +			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt19NorthFlag, true); +		} +		break; +	case MakeRoomView(kWSC07, kSouth): +	case MakeRoomView(kWSC56, kNorth): +		setCurrentActivation(kActivationReadyForMap); +		break; +	case MakeRoomView(kWSC42, kWest): +		setCurrentAlternate(kAltWSCNormal); +		break; +	case MakeRoomView(kWSC42, kEast): +		_privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, false); +		setCurrentActivation(kActivationSinclairOfficeLocked); +		break; +	case MakeRoomView(kWSC58, kSouth): +		setCurrentActivation(kActivationW58SouthDoorLocked); +		_privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, false); +		break; +	case MakeRoomView(kWSC60, kEast): +		GameState.setScoringEnteredSinclairOffice(); +		break; +	case MakeRoomView(kWSC61West, kWest): +		setCurrentActivation(kActivationW61MessagesOff); +		break; +	case MakeRoomView(kWSC61South, kSouth): +		setCurrentActivation(kActivationW61SouthOff); +		break; +	case MakeRoomView(kWSC62, kSouth): +		if (!GameState.getWSCDidPlasmaDodge()) { +			g_AIArea->lockAIOut(); +			loadLoopSound1("Sounds/World Science Center/Plasma Rock.22K.AIFF"); +			requestExtraSequence(kW62SouthPlasmaRobotAppears, 0, kFilterNoInput); +			requestExtraSequence(kW62ZoomToRobot, 0, kFilterNoInput); +			requestExtraSequence(kW62ZoomOutFromRobot, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case MakeRoomView(kWSC65Screen, kSouth): +		if (!GameState.getWSCSeenSinclairLecture()) { +			GameState.setWSCSeenSinclairLecture(true); +			startExtraSequence(kW65SouthSinclairLecture, kExtraCompletedFlag, kFilterAllInput); +		}	 +		break; +	case MakeRoomView(kWSC66, kWest): +	case MakeRoomView(kWSC67, kEast): +		if (!GameState.getWSCHeardPage2()) { +			playSpotSoundSync(kPaging2In, kPaging2Out); +			GameState.setWSCHeardPage2(true); +		} +	case MakeRoomView(kWSC10, kNorth): +	case MakeRoomView(kWSC26, kSouth): +	case MakeRoomView(kWSC72, kWest): +	case MakeRoomView(kWSC83, kWest): +		if (!GameState.getWSCHeardCheckIn()) { +			playSpotSoundSync(kCheckInIn, kCheckInOut); +			GameState.setWSCHeardCheckIn(true); +		} +		break; +	case MakeRoomView(kWSC0Z, kSouth): +		if (getCurrentAlternate() == kAltWSCW0ZDoorOpen) +			turnLeft(); +		break; +	case MakeRoomView(kWSC93, kEast): +		GameState.setWSCBeenAtWSC93(true); +		break; +	case MakeRoomView(kWSC98, kWest): +		if (!GameState.getWSCRobotDead()) { +			scheduleEvent(kGawkAtRobotTime2, 1, kTimerEventPlayerGawkingAtRobot2); +			setCurrentActivation(kActivationRobotTurning); +			if (g_AIArea) +				g_AIArea->checkMiddleArea(); +		} else if (!GameState.getWSCRobotGone()) { +			setCurrentActivation(kActivationRobotDead); +		} else { +			if (GameState.getWSCCatwalkDark()) { +				//	Change the gun hot spot... +				g_allHotspots.setHotspotRect(kW98StunGunSpotID, Common::Rect(181 + kNavAreaLeft, +						99 + kNavAreaTop,372 + kNavAreaLeft, 149 + kNavAreaTop)); +			} +			setCurrentActivation(kActivationRobotGone); +		} +		break; +	case MakeRoomView(kWSCDeathRoom, kNorth): +	case MakeRoomView(kWSCDeathRoom, kSouth): +	case MakeRoomView(kWSCDeathRoom, kEast): +	case MakeRoomView(kWSCDeathRoom, kWest): +		die(kDeathArrestedInWSC); +		break; +	} + +	checkPeopleCrossing(); +	setUpPoison(); +} + +void WSC::turnTo(const DirectionConstant direction) { +	Neighborhood::turnTo(direction); + +	switch (MakeRoomView(GameState.getCurrentRoom(), direction)) { +	case MakeRoomView(kWSC01, kNorth): +	case MakeRoomView(kWSC01, kSouth): +		GameState.setWSCAnalyzerOn(false); +		break; +	case MakeRoomView(kWSC03, kNorth): +		setCurrentActivation(kActivationW03NorthOff); +		break; +	case MakeRoomView(kWSC03, kSouth): +		if (GameState.getWSCDesignedAntidote() && !GameState.getWSCPickedUpAntidote()) +			setCurrentActivation(kActivationReadyForSynthesis); +		break; +	case MakeRoomView(kWSC07, kSouth): +	case MakeRoomView(kWSC56, kNorth): +		setCurrentActivation(kActivationReadyForMap); +		break; +	case MakeRoomView(kWSC18, kSouth): +	case MakeRoomView(kWSC57, kEast): +	case MakeRoomView(kWSC75, kEast): +	case MakeRoomView(kWSC90, kSouth): +		if (!GameState.getWSCHeardCheckIn()) { +			playSpotSoundSync(kCheckInIn, kCheckInOut); +			GameState.setWSCHeardCheckIn(true); +		} +		break; +	case MakeRoomView(kWSC56, kSouth): +		if (!GameState.getWSCHeardPage1()) { +			playSpotSoundSync(kPaging1In, kPaging1Out); +			GameState.setWSCHeardPage1(true); +		} +		// clone2727 says: This falls through?!??! WTF? +	case MakeRoomView(kWSC42, kEast): +		_privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, false); +		setCurrentActivation(kActivationSinclairOfficeLocked); +		break; +	case MakeRoomView(kWSC58, kSouth): +		setCurrentActivation(kActivationW58SouthDoorLocked); +		_privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, false); +		break; +	case MakeRoomView(kWSC73, kWest): +		setCurrentAlternate(kAltWSCNormal); +		break; +	case MakeRoomView(kWSC0Z, kEast): +		if (getCurrentAlternate() == kAltWSCW0ZDoorOpen) +			startExtraSequence(kW0ZSpottedByWomen, kExtraCompletedFlag, kFilterNoInput); +		break; +	} + +	checkPeopleCrossing(); +} + +void WSC::receiveNotification(Notification *notification, const NotificationFlags flags) { +	int32 currentEnergy; +	Item *item; +	 +	if (flags & kExtraCompletedFlag) { +		_interruptionFilter = kFilterAllInput; + +		switch (_lastExtra) { +		case kWSCArrivalFromTSA: +			GameState.setWSCSeenTimeStream(true); +			loadAmbientLoops(); +			break; +		case kWSCDartScan1: +			setCurrentActivation(kActivationShotByRobot); +			GameState.setWSCPoisoned(true); +			setUpPoison(); +			makeContinuePoint(); +			break; +		case kWSCDartScan2: +			_vm->addItemToInventory((InventoryItem *)g_allItems.findItemByID(kPoisonDart)); +			GameState.setScoringRemovedDart(); +			GameState.setWSCRemovedDart(true); +			setUpPoison(); +			g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XW1WB2", false, kHintInterruption); +			// Fall through... +		case kWSCDartScanNo: +			GameState.setWSCAnsweredAboutDart(true); +			startExtraSequence(kWSCDartScan3, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kWSCDartScan3: +			setCurrentActivation(kActivateHotSpotAlways); +			break; +		case kWSCAnalyzerPowerUp: +		case kWSCAnalyzerPowerUpWithDart: +			GameState.setWSCAnalyzerOn(true); +			break; +		case kWSCDropDartIntoAnalyzer: +			setCurrentActivation(kActivationZoomedInToAnalyzer); +			break; +		case kWSCAnalyzeDart: +			GameState.setWSCAnalyzedDart(true); +			GameState.setScoringAnalyzedDart(); +			break; +		case kWSCZoomOutFromAnalyzer: +			setCurrentActivation(kActivateHotSpotAlways); +			GameState.setWSCAnalyzerOn(false); +			GameState.setWSCDartInAnalyzer(false); +			updateViewFrame(); +			break; +		case kMessagesMovedToOffice: +		case kMessagesMovedToOfficeNoNitrogen: +			_privateFlags.setFlag(kWSCPrivateLabMessagesOpenFlag, true); +			GameState.setScoringPlayedWithMessages(); +			break; +		case kMessagesOff: +		case kMessagesOffNoNitrogen: +			_privateFlags.setFlag(kWSCPrivateLabMessagesOpenFlag, false); +			if (_cachedZoomSpot) { +				zoomTo(_cachedZoomSpot); +				_cachedZoomSpot = 0; +			} +			break; +		case kWSC02TurnOnMorphScreen: +			setCurrentActivation(kActivationReadyForMorph); +			break; +		case kWSC02DropToMorphExperiment: +			loopExtraSequence(kWSC02MorphLoop, kExtraCompletedFlag); +			setCurrentActivation(kActivationMorphLooping); +			break; +		case kWSC02MorphLoop: +			if (_privateFlags.getFlag(kWSCPrivateInterruptedMorphFlag)) +				startExtraSequence(kWSC02MorphInterruption, kExtraCompletedFlag, kFilterNoInput); +			else +				scheduleNavCallBack(kExtraCompletedFlag); +			break; +		case kWSC02MorphInterruption: +			setCurrentActivation(kActivationMorphInterrupted); +			GameState.setScoringSawMorphExperiment(); +			break; +		case kWSC02TurnOffMorphScreen: +			setCurrentActivation(kActivationMorphScreenOff); +			GameState.setWSCSawMorph(true); +			break; +		case kW03NorthActivate: +			if (GameState.getWSCAnalyzedDart() && !GameState.getWSCDesignedAntidote()) +				startExtraSequence(kW03NorthGetData, kExtraCompletedFlag, kFilterNoInput); +			else +				setCurrentActivation(kActivateHotSpotAlways); +			break; +		case kW03NorthGetData: +			setCurrentActivation(kActivationW03NorthReadyForInstructions); +			break; +		case kW03NorthInstructions: +			setCurrentActivation(kActivationW03NorthSawInstructions); +			break; +		case kW03NorthPrepMolecule1: +			setUpMoleculeGame(); +			break; +		case kW03NorthPrepMolecule2: +		case kW03NorthPrepMolecule3: +			nextMoleculeGameLevel(); +			break; +		case kW03NorthFinishSynthesis: +			setCurrentActivation(kActivateHotSpotAlways); +			_privateFlags.setFlag(kWSCPrivateInMoleculeGameFlag, false); +			GameState.setWSCDesignedAntidote(true); +			GameState.setScoringBuiltAntidote(); +			break; +		case kW03SouthCreateAntidote: +			setCurrentActivation(kActivationSynthesizerLooping); +			loopExtraSequence(kW03SouthAntidoteLoop); +			break; +		case kW03SouthDeactivate: +			setCurrentActivation(kActivateHotSpotAlways); +			break; +		case kWSC07SouthMap: +		case kWSC56SouthMap: +			setCurrentActivation(kActivateHotSpotAlways); +			GameState.setScoringSawWSCDirectory(); +			break; +		case kNerdAtTheDoor1: +			GameState.setWSCSeenNerd(true); +			break; +		case kNerdAtTheDoor2: +			die(kDeathArrestedInWSC); +			break; +		case kW61Brochure: +			GameState.setScoringSawBrochure(); +			loadAmbientLoops(); +			break; +		case kW61SouthSmartAlloysWithGun: +		case kW61SouthSmartAlloysNoGun: +			GameState.setScoringSawSinclairEntry1(); +			break; +		case kW61SouthMorphingWithGun: +		case kW61SouthMorphingNoGun: +			GameState.setScoringSawSinclairEntry2(); +			break; +		case kW61SouthTimeBendingWithGun: +		case kW61SouthTimeBendingNoGun: +			GameState.setScoringSawSinclairEntry3(); +			break; +		case kW61MessagesOn: +			GameState.setWSCOfficeMessagesOpen(true); +			setCurrentActivation(kActivationW61MessagesOn); +			break; +		case kW61MessagesOff: +			GameState.setWSCOfficeMessagesOpen(false); +			setCurrentActivation(kActivationW61MessagesOff); +			if (_cachedZoomSpot) { +				zoomTo(_cachedZoomSpot); +				_cachedZoomSpot = 0; +			} +			break; +		case kW61SouthScreenOnWithGun: +		case kW61SouthScreenOnNoGun: +			_privateFlags.setFlag(kWSCPrivateOfficeLogOpenFlag, true); +			setCurrentActivation(kActivationW61SouthOn); +			break; +		case kW61SouthScreenOffWithGun: +		case kW61SouthScreenOffNoGun: +			_privateFlags.setFlag(kWSCPrivateOfficeLogOpenFlag, false); +			setCurrentActivation(kActivationW61SouthOff); +			if (_cachedZoomSpot) { +				zoomTo(_cachedZoomSpot); +				_cachedZoomSpot = 0; +			} +			break; +		case kW62ZoomOutFromRobot: +			// Handle action queue before starting new movie sequences. +			Neighborhood::receiveNotification(notification, flags); +			_energyDrainRate = g_energyMonitor->getEnergyDrainRate(); +			g_energyMonitor->setEnergyDrainRate(0); +			currentEnergy = g_energyMonitor->getCurrentEnergy(); +			_vm->setEnergyDeathReason(kDeathHitByPlasma); + +			if (GameState.getShieldOn()) +				currentEnergy -= kPlasmaEnergyWithShield; +			else +				currentEnergy -= kPlasmaEnergyNoShield; + +			if (currentEnergy <= 0) +				startExtraSequence(kW62PlasmaDodgeDie, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kW62PlasmaDodgeSurvive, kExtraCompletedFlag, kFilterNoInput); + +			scheduleEvent(kPlasmaImpactTime, kOneTickPerSecond, kTimerEventPlasmaHit); +			break; +		case kW62PlasmaDodgeDie: +			g_energyMonitor->setEnergyValue(0); +			break; +		case kW62PlasmaDodgeSurvive: +			if (GameState.getShieldOn()) { +				g_shield->setItemState(kShieldNormal); +				g_energyMonitor->drainEnergy(kPlasmaEnergyWithShield); +			} else { +				g_energyMonitor->drainEnergy(kPlasmaEnergyNoShield); +			} + +			g_energyMonitor->setEnergyDrainRate(_energyDrainRate); +			g_AIArea->unlockAI(); +			GameState.setScoringFinishedPlasmaDodge(); +			GameState.setWSCDidPlasmaDodge(true); +			restoreStriding(kWSC58, kSouth, kAltWSCNormal); +			loadAmbientLoops(); +			break; +		case kW0ZSpottedByWomen: +			die(kDeathArrestedInWSC); +			break; +		case kW17WestPeopleCrossing: +			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt17WestFlag, true); +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt17WestFlag, false); +			break; +		case kW21SouthPeopleCrossing: +			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt21SouthFlag, true); +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, true); +			break; +		case kW24SouthPeopleCrossing: +			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt24SouthFlag, true); +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, true); +			break; +		case kW34EastPeopleCrossing: +			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt34EastFlag, true); +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, true); +			break; +		case kW36WestPeopleCrossing: +			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt36WestFlag, true); +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, true); +			break; +		case kW38NorthPeopleCrossing: +			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt38NorthFlag, true); +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, true); +			break; +		case kW46SouthPeopleCrossing: +			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt46SouthFlag, true); +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, true); +			break; +		case kW49NorthPeopleCrossing: +			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt49NorthFlag, true); +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt49NorthFlag, false); +			break; +		case kW73WestPeopleCrossing: +			_privateFlags.setFlag(kWSCPrivateSeenPeopleAt73WestFlag, true); +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt73WestFlag, false); +			break; +		case kW95RobotShoots: +		case kW98RobotShoots: +			die(kDeathShotOnCatwalk); +			break; +		case kW98MorphsToRobot: +			if (_argonSprite) { +				delete _argonSprite; _argonSprite = 0; +				startExtraSequence(kW98RobotGassed, kExtraCompletedFlag, kFilterNoInput); +			} else if (_privateFlags.getFlag(kWSCPrivateClickedCatwalkCableFlag)) { +				startExtraSequence(kW98RobotShocked, kExtraCompletedFlag, kFilterNoInput); +			} else { +				startExtraSequence(kW98RobotShoots, kExtraCompletedFlag, kFilterNoInput); +			} +			break; +		case kW98RobotShocked: +			GameState.setWSCCatwalkDark(true); +			// Change the gun hot spot... +			g_allHotspots.setHotspotRect(kW98StunGunSpotID, Common::Rect(181 + kNavAreaLeft, 99 + kNavAreaTop, +					372 + kNavAreaLeft, 149 + kNavAreaTop)); +			setCurrentActivation(kActivationRobotDead); +			GameState.setWSCRobotDead(true); + +			// Video is not present +			//g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption); +			break; +		case kW98RobotGassed: +			item = (Item *)g_allItems.findItemByID(kArgonCanister); +			_vm->addItemToInventory((InventoryItem *)item); +			setCurrentActivation(kActivationRobotDead); +			GameState.setWSCRobotDead(true); + +			// Video is not present +			//g_AIArea->playAIMovie(kRightAreaSignature, "Images/AI/WSC/XN59WD", false, kWarningInterruption); +			break; +		case kW98RobotHeadOpensLight: +		case kW98RobotHeadOpensDark: +			setCurrentActivation(kActivationWSCRobotHeadOpen); +			_privateFlags.setFlag(kWSCPrivateRobotHeadOpenFlag, true); +			break; +		case kW98RobotHeadClosesDark: +		case kW98RobotHeadClosesLight: +			setCurrentActivation(kActivationRobotGone); +			_privateFlags.setFlag(kWSCPrivateRobotHeadOpenFlag, false); +			GameState.setWSCRobotGone(true); +			break; +		} +	} + +	Neighborhood::receiveNotification(notification, flags); +	g_AIArea->checkMiddleArea(); +} + +void WSC::timerExpired(const uint32 event) { +	switch (event) { +	case kTimerEventPlasmaHit: +		if (GameState.getShieldOn()) +			g_shield->setItemState(kShieldPlasma); +		break; +	case kTimerEventPlayerGawkingAtRobot: +		startExtraSequence(kW95RobotShoots, kExtraCompletedFlag, kFilterNoInput); +		break; +	case kTimerEventPlayerGawkingAtRobot2: +		startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput); +		break; +	} +} + +void WSC::setUpMoleculeGame() { +	_privateFlags.setFlag(kWSCPrivateInMoleculeGameFlag, true); +	setCurrentActivation(kActivationW03NorthInGame); +	initOneMovie(&_moleculesMovie, "Images/World Science Center/Molecules.movie", +			kWSCMoleculesMovieOrder, kMoleculesMovieLeft, kMoleculesMovieTop, true); +	_moleculesMovie.redrawMovieWorld(); +	_moleculeBin.initMoleculeBin(); +	_moleculeGameLevel = 0; +	nextMoleculeGameLevel(); +} + +void WSC::nextMoleculeGameLevel() { +	_moleculeGameLevel++; + +	for (byte i = 0; i < 6; ++i) +		_levelArray[i] = i; + +	_vm->shuffleArray((int32 *)_levelArray, 6); +	_moleculeBin.setBinLayout(_levelArray); +	startMoleculeGameLevel(); +} + +void WSC::startMoleculeGameLevel() { +	_moleculeBin.resetBin(); +	_numCorrect = 0; +	_moleculesMovie.stop(); +	_moleculesMovie.setFlags(0); +	_moleculesMovie.setSegment(s_moleculeLoopTimes[0], s_moleculeLoopTimes[0] + kMoleculeLoopTime); +	_moleculesMovie.setTime(s_moleculeLoopTimes[0]); +	_moleculesMovie.setFlags(kLoopTimeBase); +	_moleculesMovie.show(); + +	switch (_moleculeGameLevel) { +	case 1: +		playSpotSoundSync(kWSCMolecule1In, kWSCMolecule1Out); +		break; +	case 2: +		playSpotSoundSync(kWSCMolecule2In, kWSCMolecule2Out); +		break; +	case 3: +		playSpotSoundSync(kWSCMolecule3In, kWSCMolecule3Out); +		break; +	} + +	_moleculesMovie.start(); +} + +void WSC::moleculeGameClick(const HotSpotID id) { +	uint32 molecule = id - kWSC03NorthMolecule1SpotID; + +	_moleculeBin.highlightMolecule(molecule); +	_moleculeBin.selectMolecule(molecule); + +	if (molecule == _levelArray[_numCorrect]) { +		playSpotSoundSync(kWSCClick2In, kWSCClick2Out); +		_numCorrect++; +		_moleculesMovie.stop(); +		_moleculesMovie.setFlags(0); + +		TimeValue time = _moleculesMovie.getTime(); +		_moleculesMovie.setSegment(s_moleculeLoopTimes[_numCorrect], s_moleculeLoopTimes[_numCorrect] + kMoleculeLoopTime); +		_moleculesMovie.setTime(s_moleculeLoopTimes[_numCorrect] + time - s_moleculeLoopTimes[_numCorrect - 1]); + +		if (_numCorrect == 6) { +			_moleculesMovie.start(); + +			while (_moleculesMovie.isRunning()) { +				_vm->checkCallBacks(); +				_vm->refreshDisplay(); +				_vm->_system->delayMillis(10); +			} + +			_moleculesMovie.stop(); +			_moleculesMovie.hide(); + +			switch (_moleculeGameLevel) { +			case 1: +				startExtraSequence(kW03NorthPrepMolecule2, kExtraCompletedFlag, kFilterNoInput); +				break; +			case 2: +				startExtraSequence(kW03NorthPrepMolecule3, kExtraCompletedFlag, kFilterNoInput); +				break; +			case 3: +				_moleculesMovie.releaseMovie(); +				_moleculeBin.cleanUpMoleculeBin(); +				requestExtraSequence(kW03NorthFinishSynthesis, kExtraCompletedFlag, kFilterNoInput); +				break; +			} +		} else { +			_moleculesMovie.setFlags(kLoopTimeBase); +			_moleculesMovie.start(); +		} +	} else { +		// FAIL +		playSpotSoundSync(kWSCClick3In, kWSCClick3Out); + +		_moleculesMovie.stop(); +		_moleculesMovie.setFlags(0); +		_moleculesMovie.start(); + +		while (_moleculesMovie.isRunning()) { +			_vm->checkCallBacks(); +			_vm->refreshDisplay(); +			_vm->_system->delayMillis(10); +		} + +		_moleculesMovie.stop(); +		_moleculesMovie.setFlags(0); +		_moleculesMovie.setSegment(s_moleculeFailTimes[_numCorrect], s_moleculeFailTimes[_numCorrect] + kMoleculeFailTime); +		_moleculesMovie.setTime(s_moleculeFailTimes[_numCorrect]); +		_moleculesMovie.start(); + + +		while (_moleculesMovie.isRunning()) { +			_vm->checkCallBacks(); +			_vm->refreshDisplay(); +			_vm->_system->delayMillis(10); +		} + +		_moleculesMovie.stop(); +		startMoleculeGameLevel(); +	} +} + +void WSC::activateOneHotspot(HotspotInfoTable::Entry &entry, Hotspot *hotspot) { +	Neighborhood::activateOneHotspot(entry, hotspot); + +	Item *argonCanister; + +	switch (hotspot->getObjectID()) { +	case kWSCTurnOnAnalyzerSpotID: +		if (GameState.getWSCAnalyzerOn()) +			hotspot->setInactive(); +		break; +	case kWSC02SouthTakeArgonSpotID: +		if (!GameState.getWSCSawMorph() || GameState.isTakenItemID(kArgonCanister)) +			hotspot->setInactive(); +		break; +	case kWSC02ActivateMorphScreenSpotID: +		if (GameState.getWSCSawMorph()) +			hotspot->setInactive(); +		break; +	case kWSC03NorthMolecule1SpotID: +	case kWSC03NorthMolecule2SpotID: +	case kWSC03NorthMolecule3SpotID: +	case kWSC03NorthMolecule4SpotID: +	case kWSC03NorthMolecule5SpotID: +	case kWSC03NorthMolecule6SpotID: +		if (_moleculeBin.isMoleculeHighlighted(hotspot->getObjectID() - kWSC03NorthMolecule1SpotID)) +			hotspot->setInactive(); +		break; +	case kWSC03SouthPickUpAntidoteSpotID: +		if (getCurrentActivation() == kActivationSynthesizerLooping) +			hotspot->setActive(); +		break; +	case kW98DropArgonSpotID: +		argonCanister = g_allItems.findItemByID(kArgonCanister); +		if (argonCanister->getItemState() != kArgonFull) +			hotspot->setInactive(); +		break; +	} +} + +void WSC::activateHotspots() { +	Neighborhood::activateHotspots(); + +	if (GameState.getCurrentRoomAndView() == MakeRoomView(kWSC98, kWest) && _privateFlags.getFlag(kWSCPrivateRobotHeadOpenFlag)) { +		if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag)) +			g_allHotspots.deactivateOneHotspot(kW98RetinalChipSpotID); +		else +			g_allHotspots.activateOneHotspot(kW98RetinalChipSpotID); + +		if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag)) +			g_allHotspots.deactivateOneHotspot(kW98MapChipSpotID); +		else +			g_allHotspots.activateOneHotspot(kW98MapChipSpotID); + +		if (_privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) +			g_allHotspots.deactivateOneHotspot(kW98OpticalChipSpotID); +		else +			g_allHotspots.activateOneHotspot(kW98OpticalChipSpotID); +	} +} + +void WSC::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { +	if (JMPPPInput::isEasterEggModifierInput(input)) +		GameState.setEasterEgg(true); + +	if (clickedSpot) { +		switch (clickedSpot->getObjectID()) { +		case kWSCAnalyzerScreenSpotID: +			requestExtraSequence(kWSCAnalyzeDart, kExtraCompletedFlag, kFilterNoInput); +			requestExtraSequence(kWSCZoomOutFromAnalyzer, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kWSC02SouthPlayMessagesSpotID: +			if (GameState.isTakenItemID(kNitrogenCanister)) { +				if (_lastExtra == (uint32)kMessagesMovedToOfficeNoNitrogen) +					startExtraSequence(kMessagesOffNoNitrogen, kExtraCompletedFlag, kFilterNoInput); +				else +					startExtraSequence(kMessagesMovedToOfficeNoNitrogen, kExtraCompletedFlag, kFilterNoInput); +			} else { +				if (_lastExtra == (uint32)kMessagesMovedToOffice) +					startExtraSequence(kMessagesOff, kExtraCompletedFlag, kFilterNoInput); +				else +					startExtraSequence(kMessagesMovedToOffice, kExtraCompletedFlag, kFilterNoInput); +			} +			break; +		case kWSC02SouthInterruptMorphSpotID: +			_privateFlags.setFlag(kWSCPrivateInterruptedMorphFlag, true); +			break; +		case kWSC02SouthMorphFinishedSpotID: +			requestExtraSequence(kWSC02MorphFinished, 0, kFilterNoInput); +			requestExtraSequence(kWSC02TurnOffMorphScreen, kExtraCompletedFlag, kFilterNoInput); +			break; +		case kWSC03NorthMolecule1SpotID: +		case kWSC03NorthMolecule2SpotID: +		case kWSC03NorthMolecule3SpotID: +		case kWSC03NorthMolecule4SpotID: +		case kWSC03NorthMolecule5SpotID: +		case kWSC03NorthMolecule6SpotID: +			moleculeGameClick(clickedSpot->getObjectID()); +			break; +		case kW98GrabCableSpotID: +			if (isEventTimerRunning()) { +				cancelEvent(); +				startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput); +			} + +			_privateFlags.setFlag(kWSCPrivateClickedCatwalkCableFlag, true); +			break; +		default: +			Neighborhood::clickInHotspot(input, clickedSpot); +			break; +		} +	} else { +		Neighborhood::clickInHotspot(input, clickedSpot); +	} +	 +	GameState.setEasterEgg(false); +} + +void WSC::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { +	CoordType h, v; + +	switch (item->getObjectID()) { +	case kPoisonDart: +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		GameState.setWSCDartInAnalyzer(true); +		if (dropSpot && dropSpot->getObjectID() == kWSCDropDartSpotID) { +			if (!GameState.getWSCAnalyzerOn()) +				requestExtraSequence(kWSCAnalyzerPowerUpWithDart, kExtraCompletedFlag, kFilterNoInput); + +			requestExtraSequence(kWSCDropDartIntoAnalyzer, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kAntidote: +		_privateFlags.setFlag(kWSCDraggingAntidoteFlag, false); +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		loopExtraSequence(kW03SouthAntidoteLoop); +		break; +	case kSinclairKey: +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		_privateFlags.setFlag(kWSCPrivateSinclairOfficeOpenFlag, true); +		openDoor(); +		break; +	case kCrowbar: +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		_privateFlags.setFlag(kWSCPrivate58SouthOpenFlag, true); +		openDoor(); +		break; +	case kMachineGun: +		setCurrentAlternate(kAltWSCNormal); +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		break; +	case kArgonCanister: +		item->setItemState(kArgonEmpty); +		_argonSprite = item->getDragSprite(0); +		_argonSprite->setCurrentFrameIndex(1); +		_argonSprite->setDisplayOrder(kDragSpriteOrder); +		dropSpot->getCenter(h, v); +		_argonSprite->centerElementAt(h, v); +		_argonSprite->startDisplaying(); +		_argonSprite->show(); + +		if (isEventTimerRunning()) { +			cancelEvent(); +			startExtraSequence(kW98MorphsToRobot, kExtraCompletedFlag, kFilterAllInput); +		} +		break; +	case kRetinalScanBiochip: +		_privateFlags.setFlag(kWSCPrivateGotRetScanChipFlag, false); +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		break; +	case kMapBiochip: +		_privateFlags.setFlag(kWSCPrivateGotMapChipFlag, false); +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		break; +	case kOpticalBiochip: +		_privateFlags.setFlag(kWSCPrivateGotOpticalChipFlag, false); +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		break; +	default: +		Neighborhood::dropItemIntoRoom(item, dropSpot); +		break; +	} +} + +void WSC::takeItemFromRoom(Item *item) { +	switch (item->getObjectID()) { +	case kAntidote: +		_privateFlags.setFlag(kWSCDraggingAntidoteFlag, true); +		Neighborhood::takeItemFromRoom(item); +		break; +	case kMachineGun: +		setCurrentAlternate(kAltWSCTookMachineGun); +		Neighborhood::takeItemFromRoom(item); +		break; +	case kRetinalScanBiochip: +		_privateFlags.setFlag(kWSCPrivateGotRetScanChipFlag, true); +		Neighborhood::takeItemFromRoom(item); +		break; +	case kMapBiochip: +		_privateFlags.setFlag(kWSCPrivateGotMapChipFlag, true); +		Neighborhood::takeItemFromRoom(item); +		break; +	case kOpticalBiochip: +		_privateFlags.setFlag(kWSCPrivateGotOpticalChipFlag, true); +		Neighborhood::takeItemFromRoom(item); +		break; +	default: +		Neighborhood::takeItemFromRoom(item); +		break; +	} +} + +Hotspot *WSC::getItemScreenSpot(Item *item, DisplayElement *element) { +	HotSpotID destSpotID; +	 +	switch (item->getObjectID()) { +	case kNitrogenCanister: +		destSpotID = kWSC02SouthTakeNitrogenSpotID; +		break; +	case kArgonPickup: +		destSpotID = kWSC02SouthTakeArgonSpotID; +		break; +	case kAntidote: +		destSpotID = kWSC03SouthPickUpAntidoteSpotID; +		break; +	case kMachineGun: +		destSpotID = kW61SouthMachineGunSpotID; +		break; +	case kRetinalScanBiochip: +		destSpotID = kW98RetinalChipSpotID; +		break; +	case kMapBiochip: +		destSpotID = kW98MapChipSpotID; +		break; +	case kOpticalBiochip: +		destSpotID = kW98OpticalChipSpotID; +		break; +	default: +		destSpotID = kNoHotSpotID; +		break; +	} + +	if (destSpotID == kNoHotSpotID) +		return Neighborhood::getItemScreenSpot(item, element); + +	return g_allHotspots.findHotspotByID(destSpotID); +} + +void WSC::pickedUpItem(Item *item) {	 +	switch (item->getObjectID()) { +	case kAntidote: +		if (!GameState.getWSCPickedUpAntidote()) { +			GameState.setWSCPoisoned(false); +			GameState.setWSCRemovedDart(false); +			GameState.setWSCPickedUpAntidote(true); +			_privateFlags.setFlag(kWSCDraggingAntidoteFlag, false); +			playSpotSoundSync(kDrinkAntidoteIn, kDrinkAntidoteOut); +			setUpPoison(); +			startExtraSequence(kW03SouthDeactivate, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kArgonPickup: +		_vm->removeItemFromInventory((InventoryItem *)item); +		item = (Item *)g_allItems.findItemByID(kArgonCanister); +		_vm->addItemToInventory((InventoryItem *)item); +		item = (Item *)g_allItems.findItemByID(kSinclairKey); +		_vm->addItemToInventory((InventoryItem *)item); +		g_allHotspots.setHotspotRect(kWSC02SouthMorphOutSpotID, +				Common::Rect(kNavAreaLeft, kNavAreaTop, 512 + kNavAreaLeft, 256 + kNavAreaTop)); +		break; +	case kArgonCanister: +		GameState.setScoringGotArgonCanister(); +		break; +	case kSinclairKey: +		GameState.setScoringGotSinclairKey(); +		break; +	case kNitrogenCanister: +		GameState.setScoringGotNitrogenCanister(); +		break; +	case kRetinalScanBiochip: +		if (_privateFlags.getFlag(kWSCPrivateGotMapChipFlag) && _privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) { +			if (GameState.getWSCCatwalkDark()) +				startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kMapBiochip: +		if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag) && _privateFlags.getFlag(kWSCPrivateGotOpticalChipFlag)) { +			if (GameState.getWSCCatwalkDark()) +				startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kOpticalBiochip: +		g_opticalChip->addMercury(); +		GameState.setScoringGotWSCOpMemChip(); +		if (_privateFlags.getFlag(kWSCPrivateGotRetScanChipFlag) && _privateFlags.getFlag(kWSCPrivateGotMapChipFlag)) { +			if (GameState.getWSCCatwalkDark()) +				startExtraSequence(kW98RobotHeadClosesDark, kExtraCompletedFlag, kFilterNoInput); +			else +				startExtraSequence(kW98RobotHeadClosesLight, kExtraCompletedFlag, kFilterNoInput); +		} +		break; +	case kStunGun: +		GameState.setWSCFinished(true); + +		if (!GameState.getWSCCatwalkDark()) +			GameState.setScoringWSCGandhi(); + +		recallToTSASuccess(); +		break; +	} +} + +void WSC::checkPeopleCrossing() { +	switch (GameState.getCurrentRoomAndView()) { +	case MakeRoomView(kWSC17, kWest): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt17WestFlag)) +			startExtraSequence(kW17WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kWSC21, kSouth): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt21SouthFlag)) +			startExtraSequence(kW21SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kWSC24, kSouth): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt24SouthFlag)) +			startExtraSequence(kW24SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kWSC34, kEast): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt34EastFlag)) +			startExtraSequence(kW34EastPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kWSC36, kWest): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt36WestFlag)) +			startExtraSequence(kW36WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kWSC38, kNorth): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt38NorthFlag)) +			startExtraSequence(kW38NorthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kWSC46, kSouth): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt46SouthFlag)) +			startExtraSequence(kW46SouthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kWSC49, kNorth): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt49NorthFlag)) +			startExtraSequence(kW49NorthPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); +		break; +	case MakeRoomView(kWSC73, kWest): +		if (_privateFlags.getFlag(kWSCPrivateNeedPeopleAt73WestFlag)) +			startExtraSequence(kW73WestPeopleCrossing, kExtraCompletedFlag, kFilterNoInput); +		break; +	default: +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt21SouthFlag) && _vm->getRandomNumber(2) == 0) { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, true); +			forceStridingStop(kWSC18, kSouth, kAltWSCNormal); +		} else { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt21SouthFlag, false); +			restoreStriding(kWSC18, kSouth, kAltWSCNormal); +		} + +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt19NorthFlag) && _vm->getRandomNumber(2) == 0) { +			forceStridingStop(kWSC22, kNorth, kAltWSCNormal); +		} else { +			restoreStriding(kWSC22, kNorth, kAltWSCNormal); +		} + +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt24SouthFlag) && _vm->getRandomNumber(2) == 0) { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, true); +			forceStridingStop(kWSC22, kSouth, kAltWSCNormal); +		} else { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt24SouthFlag, false); +			restoreStriding(kWSC22, kSouth, kAltWSCNormal); +		} + +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt34EastFlag) && _vm->getRandomNumber(2) == 0) { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, true); +			forceStridingStop(kWSC28, kEast, kAltWSCNormal); +		} else { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt34EastFlag, false); +			restoreStriding(kWSC28, kEast, kAltWSCNormal); +		} + +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt36WestFlag) && _vm->getRandomNumber(2) == 0) { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, true); +			forceStridingStop(kWSC40, kWest, kAltWSCNormal); +		} else { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt36WestFlag, false); +			restoreStriding(kWSC40, kWest, kAltWSCNormal); +		} + +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt38NorthFlag) && _vm->getRandomNumber(2) == 0) { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, true); +			forceStridingStop(kWSC42, kNorth, kAltWSCNormal); +		} else { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt38NorthFlag, false); +			restoreStriding(kWSC42, kNorth, kAltWSCNormal); +		} + +		if (!_privateFlags.getFlag(kWSCPrivateSeenPeopleAt46SouthFlag) && _vm->getRandomNumber(2) == 0) { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, true); +			forceStridingStop(kWSC44, kSouth, kAltWSCNormal); +		} else { +			_privateFlags.setFlag(kWSCPrivateNeedPeopleAt46SouthFlag, false); +			restoreStriding(kWSC44, kSouth, kAltWSCNormal); +		} +		break; +	} +} + +void WSC::setUpPoison() { +	if (GameState.getWSCPoisoned()) { +		if (GameState.getWSCRemovedDart()) { +			if (g_energyMonitor->getEnergyDrainRate() != kWSCPoisonEnergyDrainNoDart) { +				g_energyMonitor->setEnergyDrainRate(kWSCPoisonEnergyDrainNoDart); +				_vm->setEnergyDeathReason(kDeathDidntStopPoison); +			} +		} else { +			if (g_energyMonitor->getEnergyDrainRate() != kWSCPoisonEnergyDrainWithDart) { +				g_energyMonitor->setEnergyDrainRate(kWSCPoisonEnergyDrainWithDart); +				_vm->setEnergyDeathReason(kDeathDidntStopPoison); +			} +		} +	} else if (g_energyMonitor->getEnergyDrainRate() != kEnergyDrainNormal) { +		g_energyMonitor->setEnergyDrainRate(kEnergyDrainNormal); +		_vm->resetEnergyDeathReason(); +	} +} + +bool WSC::inSynthesizerGame() { +	return _moleculesMovie.isMovieValid(); +} + +bool WSC::canSolve() { +	return (inSynthesizerGame() || (GameState.getCurrentRoom() == kWSC98 && !GameState.getWSCRobotDead())); +} + +void WSC::doSolve() { +	if (inSynthesizerGame()) { +		_moleculesMovie.releaseMovie(); +		_moleculeBin.cleanUpMoleculeBin(); +		requestExtraSequence(kW03NorthFinishSynthesis, kExtraCompletedFlag, kFilterNoInput); +	} else if (GameState.getCurrentRoom() == kWSC98 && !GameState.getWSCRobotDead()) { +		cancelEvent(); +		startExtraSequence(kW98RobotShocked, kExtraCompletedFlag, kFilterNoInput); +	} +} + +Common::String WSC::getNavMovieName() { +	return "Images/World Science Center/WSC.movie"; +} + +Common::String WSC::getSoundSpotsName() { +	return "Sounds/World Science Center/WSC Spots"; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/wsc/wsc.h b/engines/pegasus/neighborhood/wsc/wsc.h new file mode 100644 index 0000000000..e2e931ecdc --- /dev/null +++ b/engines/pegasus/neighborhood/wsc/wsc.h @@ -0,0 +1,166 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_WSC_WSC_H +#define PEGASUS_NEIGHBORHOOD_WSC_WSC_H + +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/wsc/moleculebin.h" + +namespace Pegasus { + +static const DisplayOrder kWSCMoleculeBinOrder = kMonitorLayer; +static const DisplayOrder kWSCMoleculesMovieOrder = kWSCMoleculeBinOrder + 1; + +static const RoomID kWSC01 = 0; +static const RoomID kWSC02Morph = 2; +static const RoomID kWSC02Messages = 3; +static const RoomID kWSC62 = 62; + +class WSC : public Neighborhood { +public: +	WSC(InputHandler *, PegasusEngine *); +	virtual ~WSC() {} + +	void flushGameState(); + +	virtual uint16 getDateResID() const; + +	bool okayToJump(); + +	void checkContinuePoint(const RoomID, const DirectionConstant); +	 +	bool inSynthesizerGame(); +	 +	bool canSolve(); +	void doSolve(); +	 +	virtual void prepareForAIHint(const Common::String &); +	virtual void cleanUpAfterAIHint(const Common::String &); + +	void init(); +	void start(); + +protected: +	enum { +		kWSCDraggingAntidoteFlag, +		 +		kWSCPrivateLabMessagesOpenFlag, +		kWSCPrivateInterruptedMorphFlag, +		kWSCPrivateInMoleculeGameFlag, +		kWSCPrivateSinclairOfficeOpenFlag, +		kWSCPrivateOfficeLogOpenFlag, +		kWSCPrivate58SouthOpenFlag, +		kWSCPrivateClickedCatwalkCableFlag, +		kWSCPrivateRobotHeadOpenFlag, +		 +		kWSCPrivateSeenPeopleAt17WestFlag, +		kWSCPrivateSeenPeopleAt19NorthFlag, +		kWSCPrivateSeenPeopleAt21SouthFlag, +		kWSCPrivateSeenPeopleAt24SouthFlag, +		kWSCPrivateSeenPeopleAt34EastFlag, +		kWSCPrivateSeenPeopleAt36WestFlag, +		kWSCPrivateSeenPeopleAt38NorthFlag, +		kWSCPrivateSeenPeopleAt46SouthFlag, +		kWSCPrivateSeenPeopleAt49NorthFlag, +		kWSCPrivateSeenPeopleAt73WestFlag, +		 +		kWSCPrivateNeedPeopleAt17WestFlag, +		kWSCPrivateNeedPeopleAt21SouthFlag, +		kWSCPrivateNeedPeopleAt24SouthFlag, +		kWSCPrivateNeedPeopleAt34EastFlag, +		kWSCPrivateNeedPeopleAt36WestFlag, +		kWSCPrivateNeedPeopleAt38NorthFlag, +		kWSCPrivateNeedPeopleAt46SouthFlag, +		kWSCPrivateNeedPeopleAt49NorthFlag, +		kWSCPrivateNeedPeopleAt73WestFlag, +		 +		kWSCPrivateGotRetScanChipFlag, +		kWSCPrivateGotMapChipFlag, +		kWSCPrivateGotOpticalChipFlag, +		 +		kNumWSCPrivateFlags +	}; + +	void arriveAt(const RoomID, const DirectionConstant); +	void turnTo(const DirectionConstant); +	void receiveNotification(Notification *, const NotificationFlags); +	void dropItemIntoRoom(Item *, Hotspot *); +	void clickInHotspot(const Input &, const Hotspot *); +	TimeValue getViewTime(const RoomID, const DirectionConstant); +	void getZoomEntry(const HotSpotID, ZoomTable::Entry &); +	CanMoveForwardReason canMoveForward(ExitTable::Entry &entry); +	void cantMoveThatWay(CanMoveForwardReason reason); +	CanTurnReason canTurn(TurnDirection turn, DirectionConstant &nextDir); +	void zoomTo(const Hotspot *hotspot); +	void activateOneHotspot(HotspotInfoTable::Entry &, Hotspot *); +	void setUpMoleculeGame(); +	void nextMoleculeGameLevel(); +	void startMoleculeGameLevel(); +	void moleculeGameClick(const HotSpotID); +	void loadAmbientLoops(); +	CanOpenDoorReason canOpenDoor(DoorTable::Entry &); +	void cantOpenDoor(CanOpenDoorReason); +	void pickedUpItem(Item *); +	void doorOpened(); +	void startExtraSequence(const ExtraID, const NotificationFlags, const InputBits); +	void getExtraEntry(const uint32, ExtraTable::Entry &); +	void takeItemFromRoom(Item *item); +	void checkPeopleCrossing(); +	void turnLeft(); +	void turnRight(); +	void moveForward(); +	Hotspot *getItemScreenSpot(Item *, DisplayElement *); +	int16 getStaticCompassAngle(const RoomID, const DirectionConstant); +	void getExitCompassMove(const ExitTable::Entry &exitEntry, FaderMoveSpec &compassMove); +	void getExtraCompassMove(const ExtraTable::Entry &entry, FaderMoveSpec &compassMove); +	void bumpIntoWall(); +	void activateHotspots(); +	void setUpAIRules(); +	Common::String getBriefingMovie(); +	Common::String getEnvScanMovie(); +	uint getNumHints(); +	Common::String getHintMovie(uint); +	void closeDoorOffScreen(const RoomID, const DirectionConstant); +	void setUpPoison(); +	void findSpotEntry(const RoomID, const DirectionConstant, SpotFlags, SpotTable::Entry &); +	void timerExpired(const uint32); + +	Common::String getSoundSpotsName(); +	Common::String getNavMovieName(); + +	FlagsArray<byte, kNumWSCPrivateFlags> _privateFlags; +	const Hotspot *_cachedZoomSpot; +	MoleculeBin _moleculeBin; +	int32 _moleculeGameLevel, _numCorrect; +	Movie _moleculesMovie; +	uint32 _levelArray[6]; +	Common::Rational _energyDrainRate; +	Sprite *_argonSprite; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/neighborhood/zoom.cpp b/engines/pegasus/neighborhood/zoom.cpp new file mode 100644 index 0000000000..478ec6e493 --- /dev/null +++ b/engines/pegasus/neighborhood/zoom.cpp @@ -0,0 +1,74 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/debug.h" +#include "common/stream.h" +#include "common/textconsole.h" + +#include "pegasus/neighborhood/zoom.h" + +namespace Pegasus { + +void ZoomTable::loadFromStream(Common::SeekableReadStream *stream) { +	uint32 count = stream->readUint32BE(); +	_entries.resize(count); + +	for (uint32 i = 0; i < count; i++) { +		_entries[i].hotspot = stream->readUint16BE(); +		_entries[i].movieStart = stream->readUint32BE(); +		_entries[i].movieEnd = stream->readUint32BE(); +		_entries[i].room = stream->readUint16BE(); +		_entries[i].direction = stream->readByte(); +		debug(0, "Zoom[%d]: %d %d %d %d %d", i, _entries[i].hotspot, _entries[i].movieStart, +				_entries[i].movieEnd, _entries[i].room, _entries[i].direction); +		stream->readByte(); // alignment +	} +} + +void ZoomTable::clear() { +	_entries.clear(); +} + +ZoomTable::Entry::Entry() { +	clear(); +} + +void ZoomTable::Entry::clear() { +	hotspot = kNoHotSpotID; +	movieStart = 0xffffffff; +	movieEnd = 0xffffffff; +	room = kNoRoomID; +	direction = kNoDirection; +} + +ZoomTable::Entry ZoomTable::findEntry(HotSpotID hotspot) { +	for (uint32 i = 0; i < _entries.size(); i++) +		if (_entries[i].hotspot == hotspot) +			return _entries[i]; + +	return Entry(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/zoom.h b/engines/pegasus/neighborhood/zoom.h new file mode 100644 index 0000000000..8bcf8974f8 --- /dev/null +++ b/engines/pegasus/neighborhood/zoom.h @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NEIGHBORHOOD_ZOOM_H +#define PEGASUS_NEIGHBORHOOD_ZOOM_H + +#include "common/array.h" +#include "common/endian.h" + +#include "pegasus/constants.h" + +namespace Common { +	class SeekableReadStream; +} + +namespace Pegasus { + +class ZoomTable { +public: +	ZoomTable() {} +	~ZoomTable() {} + +	static uint32 getResTag() { return MKTAG('Z', 'o', 'o', 'm'); } + +	void loadFromStream(Common::SeekableReadStream *stream); +	void clear(); + +	struct Entry { +		Entry(); +		void clear(); +		bool isEmpty() { return movieStart == 0xffffffff; } + +		HotSpotID hotspot; +		TimeValue movieStart; +		TimeValue movieEnd; +		RoomID	room; +		DirectionConstant direction; +	}; + +	Entry findEntry(HotSpotID hotspot); + +private: +	Common::Array<Entry> _entries; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/notification.cpp b/engines/pegasus/notification.cpp new file mode 100644 index 0000000000..4179afa70d --- /dev/null +++ b/engines/pegasus/notification.cpp @@ -0,0 +1,149 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/constants.h" +#include "pegasus/notification.h" + +namespace Pegasus { + +typedef ReceiverList::iterator ReceiverIterator; + +Notification::Notification(const NotificationID id, NotificationManager *owner) : IDObject(id) { +	_owner = owner; +	_currentFlags = kNoNotificationFlags; +	if (_owner) +		_owner->addNotification(this); +} + +Notification::~Notification() { +	for (uint i = 0; i < _receivers.size(); i++) +		_receivers[i].receiver->newNotification(NULL); + +	if (_owner) +		_owner->removeNotification(this); +} + +// Selectively set or clear notificiation bits. +// Wherever mask is 0, leave existing bits untouched. +// Wherever mask is 1, set bit equivalent to flags. +void Notification::notifyMe(NotificationReceiver *receiver, NotificationFlags flags, NotificationFlags mask) { +	for (uint i = 0; i < _receivers.size(); i++) { +		if (_receivers[i].receiver == receiver) { +			_receivers[i].mask = (_receivers[i].mask & ~mask) | (flags & mask); +			receiver->newNotification(this); +			return; +		} +	} + +	ReceiverEntry newEntry; +	newEntry.receiver = receiver; +	newEntry.mask = flags; +	_receivers.push_back(newEntry); + +	receiver->newNotification(this); +} + +void Notification::cancelNotification(NotificationReceiver *receiver) { +	for (uint i = 0; i < _receivers.size(); i++) { +		if (_receivers[i].receiver == receiver) { +			_receivers.remove_at(i); +			i--; +		} +	} +} + +void Notification::setNotificationFlags(NotificationFlags flags, NotificationFlags mask) { +	_currentFlags = (_currentFlags & ~mask) | flags; +} + +void Notification::checkReceivers() {	 +	NotificationFlags currentFlags = _currentFlags; +	_currentFlags = kNoNotificationFlags; + +	for (uint i = 0; i < _receivers.size(); i++) +		if (_receivers[i].mask & currentFlags) +			_receivers[i].receiver->receiveNotification(this, currentFlags); +} + +// Receiver entries are equal if their receivers are equal. + +int operator==(const ReceiverEntry &entry1, const ReceiverEntry &entry2) { +	return	entry1.receiver == entry2.receiver; +} + +int operator!=(const ReceiverEntry &entry1, const ReceiverEntry &entry2) { +	return	entry1.receiver != entry2.receiver; +} + +NotificationReceiver::NotificationReceiver() { +	_notification = NULL; +} + +NotificationReceiver::~NotificationReceiver() { +	if (_notification) +		_notification->cancelNotification(this); +} + +void NotificationReceiver::receiveNotification(Notification *, const NotificationFlags) { +} + +void NotificationReceiver::newNotification(Notification *notification) { +	_notification = notification; +} + +typedef NotificationList::iterator NotificationIterator; + +NotificationManager::NotificationManager() { +} + +NotificationManager::~NotificationManager() { +	detachNotifications(); +} + +void NotificationManager::addNotification(Notification *notification) { +	_notifications.push_back(notification); +} + +void NotificationManager::removeNotification(Notification *notification) { +	for (NotificationIterator it = _notifications.begin(); it != _notifications.end();) { +		if ((*it) == notification) +			it = _notifications.erase(it); +		else +			it++; +	} +} + +void NotificationManager::detachNotifications() { +	for (NotificationIterator it = _notifications.begin(); it != _notifications.end(); it++) +		(*it)->_owner = 0; +} + +void NotificationManager::checkNotifications() { +	for (NotificationIterator it = _notifications.begin(); it != _notifications.end(); it++) +		if ((*it)->_currentFlags != kNoNotificationFlags) +			(*it)->checkReceivers(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/notification.h b/engines/pegasus/notification.h new file mode 100644 index 0000000000..e795a4b375 --- /dev/null +++ b/engines/pegasus/notification.h @@ -0,0 +1,123 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_NOTIFICATION_H +#define PEGASUS_NOTIFICATION_H + +#include "common/array.h" +#include "common/list.h" + +#include "pegasus/types.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class NotificationManager; +class NotificationReceiver; + +struct ReceiverEntry { +	NotificationReceiver *receiver; +	NotificationFlags mask; +}; + +int operator==(const ReceiverEntry &entry1, const ReceiverEntry &entry2); +int operator!=(const ReceiverEntry &entry1, const ReceiverEntry &entry2); + +typedef Common::Array<ReceiverEntry> ReceiverList; + +/* +	A notification can have 32 flags associated with it, which can be user-defined. +*/ + +class Notification : public IDObject { +friend class NotificationManager; + +public: +	Notification(const NotificationID id, NotificationManager *owner); +	virtual ~Notification(); + +	// notifyMe will have this receiver notified when any of the specified notification +	// flags are set. +	// If there is already a notification set for this receiver, notifyMe does a bitwise +	// OR with the receiver's current notification flags. + +	// Can selectively set or clear notification bits by using the flags and mask argument. + +	void notifyMe(NotificationReceiver*, NotificationFlags flags, NotificationFlags mask); +	void cancelNotification(NotificationReceiver *receiver); +	 +	void setNotificationFlags(NotificationFlags flags,	NotificationFlags mask); +	NotificationFlags getNotificationFlags() { return _currentFlags; } +	 +	void clearNotificationFlags() { setNotificationFlags(0, ~(NotificationFlags)0); } + +protected: +	void checkReceivers(); +	 +	NotificationManager *_owner; +	ReceiverList _receivers; +	NotificationFlags _currentFlags; +}; + +class NotificationReceiver { +friend class Notification; + +public: +	NotificationReceiver(); +	virtual ~NotificationReceiver(); +	 +protected: +	// receiveNotification is called automatically whenever a notification that this +	// receiver depends on has its flags set +	 +	virtual void receiveNotification(Notification *, const NotificationFlags); +	virtual void newNotification(Notification *notification); + +private: +	Notification *_notification; +}; + +typedef Common::List<Notification *> NotificationList; + +class NotificationManager : public NotificationReceiver { +friend class Notification; + +public: +	NotificationManager(); +	virtual ~NotificationManager(); +	 +	void checkNotifications(); + +protected: +	void addNotification(Notification *notification); +	void removeNotification(Notification *notification); +	void detachNotifications(); +	 +	NotificationList _notifications; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp new file mode 100644 index 0000000000..d2d1973a66 --- /dev/null +++ b/engines/pegasus/pegasus.cpp @@ -0,0 +1,2293 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/config-manager.h" +#include "common/error.h" +#include "common/events.h" +#include "common/fs.h" +#include "common/file.h" +#include "common/memstream.h" +#include "common/savefile.h" +#include "common/textconsole.h" +#include "common/translation.h" +#include "common/random.h" +#include "base/plugins.h" +#include "base/version.h" +#include "gui/saveload.h" +#include "video/qt_decoder.h" + +#include "pegasus/console.h" +#include "pegasus/cursor.h" +#include "pegasus/energymonitor.h" +#include "pegasus/gamestate.h" +#include "pegasus/interface.h" +#include "pegasus/menu.h" +#include "pegasus/movie.h" +#include "pegasus/pegasus.h" +#include "pegasus/timers.h" +#include "pegasus/ai/ai_area.h" +#include "pegasus/items/itemlist.h" +#include "pegasus/items/biochips/aichip.h" +#include "pegasus/items/biochips/biochipitem.h" +#include "pegasus/items/biochips/mapchip.h" +#include "pegasus/items/biochips/opticalchip.h" +#include "pegasus/items/biochips/pegasuschip.h" +#include "pegasus/items/biochips/retscanchip.h" +#include "pegasus/items/biochips/shieldchip.h" +#include "pegasus/items/inventory/airmask.h" +#include "pegasus/items/inventory/gascanister.h" +#include "pegasus/items/inventory/inventoryitem.h" +#include "pegasus/items/inventory/keycard.h" +#include "pegasus/neighborhood/neighborhood.h" +#include "pegasus/neighborhood/caldoria/caldoria.h" +#include "pegasus/neighborhood/mars/mars.h" +#include "pegasus/neighborhood/norad/constants.h" +#include "pegasus/neighborhood/norad/alpha/noradalpha.h" +#include "pegasus/neighborhood/norad/delta/noraddelta.h" +#include "pegasus/neighborhood/prehistoric/prehistoric.h" +#include "pegasus/neighborhood/tsa/fulltsa.h" +#include "pegasus/neighborhood/tsa/tinytsa.h" +#include "pegasus/neighborhood/wsc/wsc.h" + +namespace Pegasus { + +PegasusEngine::PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc) : Engine(syst), InputHandler(0), _gameDescription(gamedesc), +		_shellNotification(kJMPDCShellNotificationID, this), _returnHotspot(kInfoReturnSpotID), _itemDragger(this), _bigInfoMovie(kNoDisplayElement), +		_smallInfoMovie(kNoDisplayElement) { +	_continuePoint = 0; +	_saveAllowed = _loadAllowed = true; +	_saveRequested = _loadRequested = false; +	_gameMenu = 0; +	_deathReason = kDeathStranded; +	_neighborhood = 0; +	_FXLevel = 0x80; +	_ambientLevel = 0x80; +	_gameMode = kNoMode; +	_switchModesSync = false; +	_draggingItem = 0; +	_dragType = kDragNoDrag; +	_idlerHead = 0; +	_currentCD = 1; +	_introTimer = 0; +	_aiSaveStream = 0; +} + +PegasusEngine::~PegasusEngine() { +	delete _resFork; +	delete _console; +	delete _cursor; +	delete _continuePoint; +	delete _gameMenu; +	delete _neighborhood; +	delete _rnd; +	delete _introTimer; +	delete _aiSaveStream; + +	// NOTE: This must be deleted last! +	delete _gfx; +} + +void introTimerExpiredFunction(FunctionPtr *, void *) { +	((PegasusEngine *)g_engine)->introTimerExpired(); +} + +Common::Error PegasusEngine::run() { +	_console = new PegasusConsole(this); +	_gfx = new GraphicsManager(this); +	_resFork = new Common::MacResManager(); +	_cursor = new Cursor(); +	_rnd = new Common::RandomSource("Pegasus"); +	 +	if (!_resFork->open("JMP PP Resources") || !_resFork->hasResFork()) +		error("Could not load JMP PP Resources"); + +	// Initialize items +	createItems(); + +	// Initialize cursors +	_cursor->addCursorFrames(0x80); // Main +	_cursor->addCursorFrames(900);  // Mars Shuttle + +	// Initialize the item dragger bounds +	_itemDragger.setHighlightBounds(); + +	if (!isDemo() && !detectOpeningClosingDirectory()) { +		Common::String message = "Missing intro directory. "; + +		// Give Mac OS X a more specific message because we can +#ifdef MACOSX +		message += "Make sure \"Opening/Closing\" is present."; +#else +		message += "Be sure to rename \"Opening/Closing\" to \"Opening_Closing\"."; +#endif + +		GUIErrorMessage(message); +		warning("%s", message.c_str()); +		return Common::kNoGameDataFoundError; +	} + +	// Set up input +	InputHandler::setInputHandler(this); +	allowInput(true); + +	// Set up inventories +	_items.setWeightLimit(9); +	_items.setOwnerID(kPlayerID); +	_biochips.setWeightLimit(8); +	_biochips.setOwnerID(kPlayerID); + +	_returnHotspot.setArea(Common::Rect(kNavAreaLeft, kNavAreaTop, 512 + kNavAreaLeft, 256 + kNavAreaTop)); +	_returnHotspot.setHotspotFlags(kInfoReturnSpotFlag); +	g_allHotspots.push_back(&_returnHotspot); + +	_screenDimmer.setBounds(Common::Rect(0, 0, 640, 480)); +	_screenDimmer.setDisplayOrder(kScreenDimmerOrder); + +	// Load from the launcher/cli if requested (and don't show the intro in those cases) +	bool doIntro = true; +	if (ConfMan.hasKey("save_slot")) { +		uint32 gameToLoad = ConfMan.getInt("save_slot"); +		doIntro = (loadGameState(gameToLoad).getCode() != Common::kNoError); +	} + +	_shellNotification.notifyMe(this, kJMPShellNotificationFlags, kJMPShellNotificationFlags); + +	if (doIntro) +		// Start up the first notification +		_shellNotification.setNotificationFlags(kGameStartingFlag, kGameStartingFlag); + +	if (!isDemo()) { +		_introTimer = new FuseFunction(); +		_introTimer->setFunctionPtr(&introTimerExpiredFunction, 0); +	} + +	while (!shouldQuit()) { +		processShell(); +		_system->delayMillis(10); // Ease off the CPU +	} + +	return Common::kNoError; +} + +bool PegasusEngine::detectOpeningClosingDirectory() { +	// We need to detect what our Opening/Closing directory is listed as +	// On the original disc, it was 'Opening/Closing' but only HFS(+) supports the slash +	// Mac OS X will display this as 'Opening:Closing' and we can use that directly +	// On other systems, users will need to rename to "Opening_Closing" + +	Common::FSNode gameDataDir(ConfMan.get("path")); +	gameDataDir = gameDataDir.getChild("Images"); + +	if (!gameDataDir.exists()) +		return false; + +	Common::FSList fsList; +	if (!gameDataDir.getChildren(fsList, Common::FSNode::kListDirectoriesOnly, true)) +		return false; + +	for (uint i = 0; i < fsList.size() && _introDirectory.empty(); i++) { +		Common::String name = fsList[i].getName(); + +		if (name.equalsIgnoreCase("Opening:Closing")) +			_introDirectory = name; +		else if (name.equalsIgnoreCase("Opening_Closing")) +			_introDirectory = name; +	} + +	if (_introDirectory.empty()) +		return false; + +	debug(0, "Detected intro location as '%s'", _introDirectory.c_str()); +	_introDirectory = Common::String("Images/") + _introDirectory; +	return true; +} + +void PegasusEngine::createItems() { +	Common::SeekableReadStream *res = _resFork->getResource(MKTAG('N', 'I', 't', 'm'), 0x80); + +	uint16 entryCount = res->readUint16BE(); + +	for (uint16 i = 0; i < entryCount; i++) { +		ItemID itemID = res->readUint16BE(); +		NeighborhoodID neighborhoodID = res->readUint16BE(); +		RoomID roomID = res->readUint16BE(); +		DirectionConstant direction = res->readByte(); +		res->readByte(); // alignment + +		createItem(itemID, neighborhoodID, roomID, direction); +	} + +	delete res; +} + +void PegasusEngine::createItem(ItemID itemID, NeighborhoodID neighborhoodID, RoomID roomID, DirectionConstant direction) { +	switch (itemID) { +	case kInterfaceBiochip: +		// Unused in game, but still in the data and we need to create +		// it because it's saved/loaded from save files. +		new BiochipItem(itemID, neighborhoodID, roomID, direction); +		break; +	case kAIBiochip: +		new AIChip(itemID, neighborhoodID, roomID, direction); +		break; +	case kPegasusBiochip: +		new PegasusChip(itemID, neighborhoodID, roomID, direction); +		break; +	case kOpticalBiochip: +		new OpticalChip(itemID, neighborhoodID, roomID, direction); +		break; +	case kMapBiochip: +		new MapChip(itemID, neighborhoodID, roomID, direction); +		break; +	case kRetinalScanBiochip: +		new RetScanChip(itemID, neighborhoodID, roomID, direction); +		break; +	case kShieldBiochip: +		new ShieldChip(itemID, neighborhoodID, roomID, direction); +		break;		 +	case kAirMask: +		new AirMask(itemID, neighborhoodID, roomID, direction); +		break; +	case kKeyCard: +		new KeyCard(itemID, neighborhoodID, roomID, direction); +		break; +	case kGasCanister: +		new GasCanister(itemID, neighborhoodID, roomID, direction); +		break; +	default: +		// Everything else is a normal inventory item +		new InventoryItem(itemID, neighborhoodID, roomID, direction); +		break; +	} +} + +void PegasusEngine::runIntro() { +	stopIntroTimer(); + +	bool skipped = false; + +	Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder(); +	if (video->loadFile(_introDirectory + "/BandaiLogo.movie")) { +		while (!shouldQuit() && !video->endOfVideo() && !skipped) { +			if (video->needsUpdate()) { +				const Graphics::Surface *frame = video->decodeNextFrame(); + +				if (frame) { +					_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 0, 0, frame->w, frame->h); +					_system->updateScreen(); +				} +			} + +			Input input; +			InputDevice.getInput(input, kFilterAllInput); +			if (input.anyInput()) +				skipped = true; + +			_system->delayMillis(10); +		} +	} + +	delete video; + +	if (shouldQuit() || skipped) +		return; + +	video = new Video::QuickTimeDecoder(); + +	if (!video->loadFile(_introDirectory + "/Big Movie.movie")) +		error("Could not load intro movie"); + +	video->seekToTime(Audio::Timestamp(0, 10 * 600, 600)); + +	playMovieScaled(video, 0, 0); + +	delete video; +} + +Common::Error PegasusEngine::showLoadDialog() { +	GUI::SaveLoadChooser slc(_("Load game:"), _("Load")); +	slc.setSaveMode(false); + +	Common::String gameId = ConfMan.get("gameid"); + +	const EnginePlugin *plugin = 0; +	EngineMan.findGame(gameId, &plugin); + +	int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); + +	Common::Error result; + +	if (slot >= 0) { +		if (loadGameState(slot).getCode() == Common::kNoError) +			result = Common::kNoError; +		else +			result = Common::kUnknownError; +	} else { +		result = Common::kUserCanceled; +	} + +	slc.close(); + +	return result; +} + +Common::Error PegasusEngine::showSaveDialog() { +	GUI::SaveLoadChooser slc(_("Save game:"), _("Save")); +	slc.setSaveMode(true); + +	Common::String gameId = ConfMan.get("gameid"); + +	const EnginePlugin *plugin = 0; +	EngineMan.findGame(gameId, &plugin); + +	int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName()); + +	Common::Error result; + +	if (slot >= 0) { +		if (saveGameState(slot, slc.getResultString()).getCode() == Common::kNoError) +			result = Common::kNoError; +		else +			result = Common::kUnknownError; +	} else { +		result = Common::kUserCanceled; +	} + +	slc.close(); + +	return result; +} + +GUI::Debugger *PegasusEngine::getDebugger() { +	return _console; +} + +void PegasusEngine::addIdler(Idler *idler) { +	idler->_nextIdler = _idlerHead; +	if (_idlerHead) +		_idlerHead->_prevIdler = idler; +	idler->_prevIdler = 0; +	_idlerHead = idler; +} + +void PegasusEngine::removeIdler(Idler *idler) { +	if (idler->_prevIdler) +		idler->_prevIdler->_nextIdler = idler->_nextIdler; +	if (idler->_nextIdler) +		idler->_nextIdler->_prevIdler = idler->_prevIdler; +	if (idler == _idlerHead) +		_idlerHead = idler->_nextIdler; +	idler->_nextIdler = 0; +	idler->_prevIdler = 0; +} + +void PegasusEngine::giveIdleTime() { +	for (Idler *idler = _idlerHead; idler != 0; idler = idler->_nextIdler) +		idler->useIdleTime(); +} + +void PegasusEngine::addTimeBase(TimeBase *timeBase) { +	_timeBases.push_back(timeBase); +} + +void PegasusEngine::removeTimeBase(TimeBase *timeBase) { +	_timeBases.remove(timeBase); +} + +bool PegasusEngine::loadFromStream(Common::ReadStream *stream) { +	// Dispose currently running stuff +	useMenu(0); +	useNeighborhood(0); +	removeAllItemsFromInventory(); +	removeAllItemsFromBiochips(); +	_currentItemID = kNoItemID; +	_currentBiochipID = kNoItemID; + +	if (!g_interface) +		createInterface(); + +	// Signature +	uint32 creator = stream->readUint32BE(); +	if (creator != kPegasusPrimeCreator) { +		warning("Bad save creator '%s'", tag2str(creator)); +		return false; +	} + +	uint32 gameType = stream->readUint32BE(); +	int saveType; + +	switch (gameType) { +	case kPegasusPrimeDisk1GameType: +	case kPegasusPrimeDisk2GameType: +	case kPegasusPrimeDisk3GameType: +	case kPegasusPrimeDisk4GameType: +		_currentCD = gameType - kPegasusPrimeDisk1GameType + 1; +		saveType = kNormalSave; +		break; +	case kPegasusPrimeContinueType: +		saveType = kContinueSave; +		break; +	default: +		// There are five other possible game types on the Pippin +		// version, but hopefully we don't see any of those here +		warning("Unhandled pegasus game type '%s'", tag2str(gameType)); +		return false; +	} + +	uint32 version = stream->readUint32BE(); +	if (version != kPegasusPrimeVersion) { +		warning("Where did you get this save? It's a beta (v%04x)!", version & 0x7fff); +		return false; +	} + +	// Game State +	GameState.readGameState(stream); + +	// Energy +	setLastEnergyValue(stream->readUint32BE()); + +	// Death reason +	setEnergyDeathReason(stream->readByte()); + +	// Items +	g_allItems.readFromStream(stream); + +	// Inventory +	byte itemCount = stream->readByte(); + +	if (itemCount > 0) { +		for (byte i = 0; i < itemCount; i++) { +			InventoryItem *inv = (InventoryItem *)g_allItems.findItemByID((ItemID)stream->readUint16BE()); +			addItemToInventory(inv); +		} + +		g_interface->setCurrentInventoryItemID((ItemID)stream->readUint16BE()); +	} + +	// Biochips +	byte biochipCount = stream->readByte(); + +	if (biochipCount > 0) { +		for (byte i = 0; i < biochipCount; i++) { +			BiochipItem *biochip = (BiochipItem *)g_allItems.findItemByID((ItemID)stream->readUint16BE()); +			addItemToBiochips(biochip); +		} + +		g_interface->setCurrentBiochipID((ItemID)stream->readUint16BE()); +	} + + +	// TODO: Disc check + +	// Jump to environment +	jumpToNewEnvironment(GameState.getCurrentNeighborhood(), GameState.getCurrentRoom(), GameState.getCurrentDirection()); +	_shellNotification.setNotificationFlags(0, kNeedNewJumpFlag); +	performJump(GameState.getCurrentNeighborhood()); + +	// AI rules +	if (g_AIArea) +		g_AIArea->readAIRules(stream); + +	startNeighborhood(); + +	// Make a new continue point if this isn't already one +	if (saveType == kNormalSave) +		makeContinuePoint(); + +	return true; +} + +bool PegasusEngine::writeToStream(Common::WriteStream *stream, int saveType) { +	if (g_neighborhood) +		g_neighborhood->flushGameState(); + +	// Signature +	stream->writeUint32BE(kPegasusPrimeCreator); + +	if (saveType == kNormalSave) +		stream->writeUint32BE(kPegasusPrimeDisk1GameType + _currentCD - 1); +	else // Continue +		stream->writeUint32BE(kPegasusPrimeContinueType); + +	stream->writeUint32BE(kPegasusPrimeVersion); + +	// Game State +	GameState.writeGameState(stream); + +	// Energy +	stream->writeUint32BE(getSavedEnergyValue()); + +	// Death reason +	stream->writeByte(getEnergyDeathReason()); + +	// Items +	g_allItems.writeToStream(stream); + +	// Inventory +	byte itemCount = _items.getNumItems(); +	stream->writeByte(itemCount); + +	if (itemCount > 0) { +		for (uint32 i = 0; i < itemCount; i++) +			stream->writeUint16BE(_items.getItemIDAt(i)); + +		stream->writeUint16BE(g_interface->getCurrentInventoryItem()->getObjectID()); +	} + +	// Biochips +	byte biochipCount = _biochips.getNumItems(); +	stream->writeByte(biochipCount); + +	if (biochipCount > 0) { +		for (uint32 i = 0; i < biochipCount; i++) +			stream->writeUint16BE(_biochips.getItemIDAt(i)); + +		stream->writeUint16BE(g_interface->getCurrentBiochip()->getObjectID()); +	} + +	// AI rules +	if (g_AIArea) +		g_AIArea->writeAIRules(stream); + +	return true; +} + +void PegasusEngine::makeContinuePoint() { +	// WORKAROUND: Do not attempt to make a continue point if the interface is not set +	// up. The original did *not* do this and attempting to restore the game using the pause +	// menu during the canyon/space chase sequence would segfault the game and crash the +	// system. Nice! +	if (!g_interface) +		return; + +	delete _continuePoint; + +	Common::MemoryWriteStreamDynamic newPoint(DisposeAfterUse::NO); +	writeToStream(&newPoint, kContinueSave); +	_continuePoint = new Common::MemoryReadStream(newPoint.getData(), newPoint.size(), DisposeAfterUse::YES); +} + +void PegasusEngine::loadFromContinuePoint() { +	// Failure to load a continue point is fatal + +	if (!_continuePoint) +		error("Attempting to load from non-existant continue point"); + +	if (!loadFromStream(_continuePoint)) +		error("Failed loading continue point"); +} + +Common::Error PegasusEngine::loadGameState(int slot) { +	Common::StringArray filenames = _saveFileMan->listSavefiles("pegasus-*.sav"); +	Common::InSaveFile *loadFile = _saveFileMan->openForLoading(filenames[slot]); +	if (!loadFile) +		return Common::kUnknownError; + +	bool valid = loadFromStream(loadFile); +	delete loadFile; + +	return valid ? Common::kNoError : Common::kUnknownError; +} + +Common::Error PegasusEngine::saveGameState(int slot, const Common::String &desc) { +	Common::String output = Common::String::format("pegasus-%s.sav", desc.c_str()); +	Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(output); +	if (!saveFile) +		return Common::kUnknownError; + +	bool valid = writeToStream(saveFile, kNormalSave); +	delete saveFile; + +	return valid ? Common::kNoError : Common::kUnknownError; +} + +void PegasusEngine::receiveNotification(Notification *notification, const NotificationFlags flags) { +	if (&_shellNotification == notification) { +		switch (flags) { +		case kGameStartingFlag: { +			useMenu(new MainMenu()); + +			if (!isDemo()) { +				runIntro(); +				resetIntroTimer(); +			} else { +				showTempScreen("Images/Demo/NGsplashScrn.pict"); +			} + +			if (shouldQuit()) +				return; + +			_gfx->invalRect(Common::Rect(0, 0, 640, 480)); +			_gfx->updateDisplay(); +			((MainMenu *)_gameMenu)->startMainMenuLoop(); +			break; +		} +		case kPlayerDiedFlag: +			doDeath(); +			break; +		case kNeedNewJumpFlag: +			performJump(GameState.getNextNeighborhood()); +			startNeighborhood(); +			break; +		default: +			break; +		} +	} +} + +void PegasusEngine::checkCallBacks() { +	for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) +		(*it)->checkCallBacks(); +} + +void PegasusEngine::resetIntroTimer() { +	if (!isDemo() && _gameMenu && _gameMenu->getObjectID() == kMainMenuID) { +		_introTimer->stopFuse(); +		_introTimer->primeFuse(kIntroTimeOut); +		_introTimer->lightFuse(); +	} +} + +void PegasusEngine::introTimerExpired() { +	if (_gameMenu && _gameMenu->getObjectID() == kMainMenuID) { +		((MainMenu *)_gameMenu)->stopMainMenuLoop(); + +		bool skipped = false; + +		Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder(); +		if (!video->loadFile(_introDirectory + "/LilMovie.movie")) +			error("Failed to load little movie"); + +		bool saveAllowed = swapSaveAllowed(false); +		bool openAllowed = swapLoadAllowed(false); + +		skipped = playMovieScaled(video, 0, 0); + +		delete video; + +		if (shouldQuit()) +			return; + +		if (!skipped) { +			runIntro(); + +			if (shouldQuit()) +				return; +		} + +		resetIntroTimer(); +		_gfx->invalRect(Common::Rect(0, 0, 640, 480)); + +		swapSaveAllowed(saveAllowed); +		swapLoadAllowed(openAllowed); + +		_gfx->updateDisplay(); +		((MainMenu *)_gameMenu)->startMainMenuLoop(); +	} +} + +void PegasusEngine::stopIntroTimer() { +	if (_introTimer) +		_introTimer->stopFuse(); +} + +void PegasusEngine::delayShell(TimeValue time, TimeScale scale) { +	if (time == 0 || scale == 0) +		return; + +	uint32 startTime = g_system->getMillis(); +	uint32 timeInMillis = time * 1000 / scale; + +	while (g_system->getMillis() < startTime + timeInMillis) { +		checkCallBacks(); +		_gfx->updateDisplay(); +	} +} + +void PegasusEngine::useMenu(GameMenu *newMenu) { +	if (_gameMenu) { +		_gameMenu->restorePreviousHandler(); +		delete _gameMenu; +	} + +	_gameMenu = newMenu; + +	if (_gameMenu) +		_gameMenu->becomeCurrentHandler(); +} + +bool PegasusEngine::checkGameMenu() { +	GameMenuCommand command = kMenuCmdNoCommand; + +	if (_gameMenu) { +		command = _gameMenu->getLastCommand(); +		if (command != kMenuCmdNoCommand) { +			_gameMenu->clearLastCommand(); +			doGameMenuCommand(command); +		} +	} + +	return command != kMenuCmdNoCommand; +} + +void PegasusEngine::doGameMenuCommand(const GameMenuCommand command) { +	Common::Error result; + +	switch (command) { +	case kMenuCmdStartAdventure: +		stopIntroTimer(); +		GameState.setWalkthroughMode(false); +		startNewGame(); +		break; +	case kMenuCmdCredits: +		if (isDemo()) { +			showTempScreen("Images/Demo/DemoCredits.pict"); +			_gfx->doFadeOutSync(); +			_gfx->updateDisplay(); +			_gfx->doFadeInSync(); +		} else { +			stopIntroTimer(); +			_gfx->doFadeOutSync(); +			useMenu(new CreditsMenu()); +			_gfx->updateDisplay(); +			_gfx->doFadeInSync(); +		} +		break; +	case kMenuCmdQuit: +	case kMenuCmdDeathQuitDemo: +		if (isDemo()) +			showTempScreen("Images/Demo/NGquitScrn.pict"); +		_system->quit(); +		break; +	case kMenuCmdOverview: +		stopIntroTimer(); +		doInterfaceOverview(); +		resetIntroTimer(); +		break; +	case kMenuCmdStartWalkthrough: +		stopIntroTimer(); +		GameState.setWalkthroughMode(true); +		startNewGame(); +		break; +	case kMenuCmdRestore: +		stopIntroTimer(); +		// fall through +	case kMenuCmdDeathRestore: +		result = showLoadDialog(); +		if (command == kMenuCmdRestore && result.getCode() != Common::kNoError) +			resetIntroTimer(); +		break; +	case kMenuCmdCreditsMainMenu: +		_gfx->doFadeOutSync(); +		useMenu(new MainMenu()); +		_gfx->updateDisplay(); +		((MainMenu *)_gameMenu)->startMainMenuLoop(); +		_gfx->doFadeInSync(); +		resetIntroTimer(); +		break; +	case kMenuCmdDeathContinue: +		if (((DeathMenu *)_gameMenu)->playerWon()) { +			if (isDemo()) { +				showTempScreen("Images/Demo/DemoCredits.pict"); +				_gfx->doFadeOutSync(); +				_gfx->updateDisplay(); +				_gfx->doFadeInSync(); +			} else { +				_gfx->doFadeOutSync(); +				useMenu(0); +				_gfx->clearScreen(); +				_gfx->updateDisplay(); + +				Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder(); +				if (!video->loadFile(_introDirectory + "/Closing.movie")) +					error("Could not load closing movie"); + +				uint16 x = (640 - video->getWidth() * 2) / 2; +				uint16 y = (480 - video->getHeight() * 2) / 2; + +				playMovieScaled(video, x, y); + +				delete video; + +				if (shouldQuit()) +					return; + +				useMenu(new MainMenu()); +				_gfx->updateDisplay(); +				((MainMenu *)_gameMenu)->startMainMenuLoop(); +				_gfx->doFadeInSync(); +				resetIntroTimer(); +			} +		} else { +			loadFromContinuePoint(); +		} +		break; +	case kMenuCmdDeathMainMenuDemo: +	case kMenuCmdDeathMainMenu: +		_gfx->doFadeOutSync(); +		useMenu(new MainMenu()); +		_gfx->updateDisplay(); +		((MainMenu *)_gameMenu)->startMainMenuLoop(); +		_gfx->doFadeInSync(); +		resetIntroTimer(); +		break; +	case kMenuCmdPauseSave: +		if (showSaveDialog().getCode() != Common::kUserCanceled) +			pauseMenu(false); +		break; +	case kMenuCmdPauseContinue: +		pauseMenu(false); +		break; +	case kMenuCmdPauseRestore: +		makeContinuePoint(); +		result = showLoadDialog(); + +		if (result.getCode() == Common::kNoError) { +			// Successfully loaded, unpause the game +			pauseMenu(false); +		} else if (result.getCode() != Common::kUserCanceled) { +			// Try to get us back to a sane state +			loadFromContinuePoint(); +		} +		break; +	case kMenuCmdPauseQuit: +		_gfx->doFadeOutSync(); +		throwAwayEverything(); +		pauseMenu(false); +		useMenu(new MainMenu()); +		_gfx->updateDisplay(); +		((MainMenu *)_gameMenu)->startMainMenuLoop(); +		_gfx->doFadeInSync(); +		resetIntroTimer(); +		break; +	case kMenuCmdNoCommand: +		break; +	default: +		error("Unknown menu command %d", command); +	} +} + +void PegasusEngine::handleInput(const Input &input, const Hotspot *cursorSpot) { +	if (!checkGameMenu()) +		shellGameInput(input, cursorSpot); + +	// Handle the console here +	if (input.isConsoleRequested()) { +		_console->attach(); +		_console->onFrame(); +	} + +	// Handle save requests here +	if (_saveRequested && _saveAllowed) { +		_saveRequested = false; + +		// Can only save during a game and not in the demo +		if (g_neighborhood && !isDemo()) { +			pauseEngine(true); +			showSaveDialog(); +			pauseEngine(false); +		} +	} + +	// Handle load requests here +	if (_loadRequested && _loadAllowed) { +		_loadRequested = false; + +		// WORKAROUND: Do not entertain load requests when the pause menu is up +		// The original did and the game entered a bad state after loading. +		// It's theoretically possible to make it so it does work while the +		// pause menu is up, but the pause state of the engine is just too weird. +		// Just use the pause menu's restore button since it's there for that +		// for you to load anyway. +		if (!isDemo() && !(_gameMenu && _gameMenu->getObjectID() == kPauseMenuID)) { +			pauseEngine(true); + +			if (g_neighborhood) { +				makeContinuePoint(); + +				Common::Error result = showLoadDialog(); +				if (result.getCode() != Common::kNoError && result.getCode() != Common::kUserCanceled) +					loadFromContinuePoint(); +			} else { +				if (_introTimer) +					_introTimer->stopFuse(); + +				Common::Error result = showLoadDialog(); +				if (result.getCode() != Common::kNoError) { +					if (!_gameMenu) { +						useMenu(new MainMenu()); +						((MainMenu *)_gameMenu)->startMainMenuLoop(); +					} + +					resetIntroTimer(); +				} +			} + +			pauseEngine(false); +		} +	} +} + +void PegasusEngine::doInterfaceOverview() { +	static const short kNumOverviewSpots = 11; +	static const Common::Rect overviewSpots[kNumOverviewSpots] = { +		Common::Rect(354, 318, 354 + 204, 318 + 12), +		Common::Rect(211, 34, 211 + 114, 34 + 28), +		Common::Rect(502, 344, 502 + 138, 344 + 120), +		Common::Rect(132, 40, 132 + 79, 40 + 22), +		Common::Rect(325, 40, 332 + 115, 40 + 22), +		Common::Rect(70, 318, 70 + 284, 318 + 12), +		Common::Rect(76, 334, 76 + 96, 334 + 96), +		Common::Rect(64, 64, 64 + 512, 64 + 256), +		Common::Rect(364, 334, 364 + 96, 334 + 96), +		Common::Rect(172, 334, 172 + 192, 334 + 96), +		Common::Rect(542, 36, 542 + 58, 36 + 20) +	}; + +	_gfx->doFadeOutSync(); +	useMenu(0); + +	Picture leftBackground(kNoDisplayElement); +	leftBackground.initFromPICTFile("Images/Interface/OVLeft.mac"); +	leftBackground.setDisplayOrder(0); +	leftBackground.moveElementTo(kBackground1Left, kBackground1Top); +	leftBackground.startDisplaying(); +	leftBackground.show(); + +	Picture topBackground(kNoDisplayElement); +	topBackground.initFromPICTFile("Images/Interface/OVTop.mac"); +	topBackground.setDisplayOrder(0); +	topBackground.moveElementTo(kBackground2Left, kBackground2Top); +	topBackground.startDisplaying(); +	topBackground.show(); + +	Picture rightBackground(kNoDisplayElement); +	rightBackground.initFromPICTFile("Images/Interface/OVRight.mac"); +	rightBackground.setDisplayOrder(0); +	rightBackground.moveElementTo(kBackground3Left, kBackground3Top); +	rightBackground.startDisplaying(); +	rightBackground.show(); + +	Picture bottomBackground(kNoDisplayElement); +	bottomBackground.initFromPICTFile("Images/Interface/OVBottom.mac"); +	bottomBackground.setDisplayOrder(0); +	bottomBackground.moveElementTo(kBackground4Left, kBackground4Top); +	bottomBackground.startDisplaying(); +	bottomBackground.show(); + +	Picture controllerHighlight(kNoDisplayElement); +	controllerHighlight.initFromPICTFile("Images/Interface/OVcontrollerHilite.mac"); +	controllerHighlight.setDisplayOrder(0); +	controllerHighlight.moveElementTo(kOverviewControllerLeft, kOverviewControllerTop); +	controllerHighlight.startDisplaying(); + +	Movie overviewText(kNoDisplayElement); +	overviewText.initFromMovieFile("Images/Interface/Overview Mac.movie"); +	overviewText.setDisplayOrder(0); +	overviewText.moveElementTo(kNavAreaLeft, kNavAreaTop); +	overviewText.startDisplaying(); +	overviewText.show(); +	overviewText.redrawMovieWorld(); + +	DropHighlight highlight(kNoDisplayElement); +	highlight.setDisplayOrder(1); +	highlight.startDisplaying(); +	highlight.setHighlightThickness(4); +	highlight.setHighlightColor(g_system->getScreenFormat().RGBToColor(239, 239, 0)); +	highlight.setHighlightCornerDiameter(8); + +	Input input; +	InputDevice.getInput(input, kFilterAllInput); + +	Common::Point cursorLoc; +	input.getInputLocation(cursorLoc); + +	uint16 i; +	for (i = 0; i < kNumOverviewSpots; ++i) +		if (overviewSpots[i].contains(cursorLoc)) +			break; + +	TimeValue time; +	if (i == kNumOverviewSpots) +		time = 5; +	else if (i > 4) +		time = i + 1; +	else +		time = i; + +	if (time == 2) { +		highlight.hide(); +		controllerHighlight.show(); +	} else if (i != kNumOverviewSpots) { +		controllerHighlight.hide(); +		Common::Rect r = overviewSpots[i]; +		r.grow(5); +		highlight.setBounds(r); +		highlight.show(); +	} else { +		highlight.hide(); +		controllerHighlight.hide(); +	} + +	overviewText.setTime(time * 3 + 2, 15); +	overviewText.redrawMovieWorld(); + +	_cursor->setCurrentFrameIndex(3); +	_cursor->show(); + +	_gfx->updateDisplay(); +	_gfx->doFadeInSync(); + +	for (;;) { +		InputDevice.getInput(input, kFilterAllInput); + +		if (input.anyInput() || shouldQuit() || _loadRequested || _saveRequested) +			break; + +		input.getInputLocation(cursorLoc); +		for (i = 0; i < kNumOverviewSpots; ++i) +			if (overviewSpots[i].contains(cursorLoc)) +				break; + +		if (i == kNumOverviewSpots) +			time = 5; +		else if (i > 4) +			time = i + 1; +		else +			time = i; + +		if (time == 2) { +			highlight.hide(); +			controllerHighlight.show(); +		} else if (i != kNumOverviewSpots) { +			controllerHighlight.hide(); +			Common::Rect r = overviewSpots[i]; +			r.grow(5); +			highlight.setBounds(r); +			highlight.show(); +		} else { +			highlight.hide(); +			controllerHighlight.hide(); +		} + +		overviewText.setTime(time * 3 + 2, 15); +		overviewText.redrawMovieWorld(); + +		refreshDisplay(); +	} + +	if (shouldQuit()) +		return; + +	highlight.hide(); +	_cursor->hide(); + +	_gfx->doFadeOutSync(); +	useMenu(new MainMenu()); +	_gfx->updateDisplay(); +	((MainMenu *)_gameMenu)->startMainMenuLoop(); +	_gfx->doFadeInSync(); + +	_saveRequested = false; +	_loadRequested = false; +} + +void PegasusEngine::showTempScreen(const Common::String &fileName) { +	_gfx->doFadeOutSync(); + +	Picture picture(0); +	picture.initFromPICTFile(fileName); +	picture.setDisplayOrder(kMaxAvailableOrder); +	picture.startDisplaying(); +	picture.show(); +	_gfx->updateDisplay(); + +	_gfx->doFadeInSync(); + +	// Wait for the next event +	bool done = false; +	while (!shouldQuit() && !done) { +		Common::Event event; +		while (_eventMan->pollEvent(event)) { +			switch (event.type) { +			case Common::EVENT_LBUTTONUP: +			case Common::EVENT_RBUTTONUP: +			case Common::EVENT_KEYDOWN: +				done = true; +				break; +			default: +				break; +			} +		} + +		_system->delayMillis(10); +	} +} + +void PegasusEngine::refreshDisplay() { +	giveIdleTime(); +	_gfx->updateDisplay(); +} + +void PegasusEngine::resetEnergyDeathReason() { +	switch (getCurrentNeighborhoodID()) { +	case kMarsID: +		_deathReason = kDeathArrestedInMars; +		break; +	case kNoradAlphaID: +	case kNoradDeltaID: +		_deathReason = kDeathArrestedInNorad; +		break; +	case kWSCID: +		_deathReason = kDeathArrestedInWSC; +		break; +	default: +		_deathReason = kDeathStranded; +		break; +	} +} + +bool PegasusEngine::playerHasItem(const Item *item) { +	return playerHasItemID(item->getObjectID()); +} + +bool PegasusEngine::playerHasItemID(const ItemID itemID) { +	return itemInInventory(itemID) || itemInBiochips(itemID); +} + +InventoryItem *PegasusEngine::getCurrentInventoryItem() { +	if (g_interface) +		return g_interface->getCurrentInventoryItem(); + +	return 0; +} + +bool PegasusEngine::itemInInventory(InventoryItem *item) { +	return _items.itemInInventory(item); +} + +bool PegasusEngine::itemInInventory(ItemID id) { +	return _items.itemInInventory(id); +} + +BiochipItem *PegasusEngine::getCurrentBiochip() { +	if (g_interface) +		return g_interface->getCurrentBiochip(); + +	return 0; +} + +bool PegasusEngine::itemInBiochips(BiochipItem *item) { +	return _biochips.itemInInventory(item); +} + +bool PegasusEngine::itemInBiochips(ItemID id) { +	return _biochips.itemInInventory(id); +} + +bool PegasusEngine::playerAlive() { +	return (_shellNotification.getNotificationFlags() & kPlayerDiedFlag) == 0; +} + +Common::String PegasusEngine::getBriefingMovie() { +	if (_neighborhood) +		return _neighborhood->getBriefingMovie(); + +	return Common::String(); +} + +Common::String PegasusEngine::getEnvScanMovie() { +	if (_neighborhood) +		return _neighborhood->getEnvScanMovie(); + +	return Common::String(); +} + +uint PegasusEngine::getNumHints() { +	if (_neighborhood) +		return _neighborhood->getNumHints(); + +	return 0; +} + +Common::String PegasusEngine::getHintMovie(uint hintNum) { +	if (_neighborhood) +		return _neighborhood->getHintMovie(hintNum); + +	return Common::String(); +} + +bool PegasusEngine::canSolve() { +	if (_neighborhood) +		return _neighborhood->canSolve(); + +	return false; +} + +void PegasusEngine::prepareForAIHint(const Common::String &movieName) { +	if (g_neighborhood) +		g_neighborhood->prepareForAIHint(movieName); +} + +void PegasusEngine::cleanUpAfterAIHint(const Common::String &movieName) { +	if (g_neighborhood) +		g_neighborhood->cleanUpAfterAIHint(movieName); +} + +void PegasusEngine::jumpToNewEnvironment(const NeighborhoodID neighborhoodID, const RoomID roomID, const DirectionConstant direction) { +	GameState.setNextLocation(neighborhoodID, roomID, direction); +	_shellNotification.setNotificationFlags(kNeedNewJumpFlag, kNeedNewJumpFlag); +} + +void PegasusEngine::checkFlashlight() { +	if (_neighborhood) +		_neighborhood->checkFlashlight(); +} + +bool PegasusEngine::playMovieScaled(Video::SeekableVideoDecoder *video, uint16 x, uint16 y) { +	bool skipped = false; + +	while (!shouldQuit() && !video->endOfVideo() && !skipped) { +		if (video->needsUpdate()) { +			const Graphics::Surface *frame = video->decodeNextFrame(); + +			if (frame) +				drawScaledFrame(frame, x, y); +		} + +		Input input; +		InputDevice.getInput(input, kFilterAllInput); +		if (input.anyInput() || _saveRequested || _loadRequested) +			skipped = true; + +		_system->delayMillis(10); +	} + +	return skipped; +} + +void PegasusEngine::die(const DeathReason reason) { +	Input dummy; +	if (isDragging()) +		_itemDragger.stopTracking(dummy); + +	_deathReason = reason; +	_shellNotification.setNotificationFlags(kPlayerDiedFlag, kPlayerDiedFlag); +} + +void PegasusEngine::doDeath() { +	_gfx->doFadeOutSync(); +	throwAwayEverything(); +	useMenu(new DeathMenu(_deathReason)); +	_gfx->updateDisplay(); +	_gfx->doFadeInSync(); +} + +void PegasusEngine::throwAwayEverything() { +	if (_items.getNumItems() != 0 && g_interface) +		_currentItemID = g_interface->getCurrentInventoryItem()->getObjectID(); +	else +		_currentItemID = kNoItemID; + +	if (_biochips.getNumItems() != 0 && g_interface) +		_currentItemID = g_interface->getCurrentBiochip()->getObjectID(); +	else +		_currentItemID = kNoItemID; + +	useMenu(0); +	useNeighborhood(0); + +	delete g_interface; +	g_interface = 0; +} + +void PegasusEngine::processShell() { +	checkCallBacks(); +	checkNotifications(); +	InputHandler::pollForInput(); +	refreshDisplay(); +} + +void PegasusEngine::createInterface() { +	if (!g_interface) +		new Interface(); + +	g_interface->createInterface(); +} + +void PegasusEngine::setGameMode(const GameMode newMode) { +	if (newMode != _gameMode && canSwitchGameMode(newMode, _gameMode)) { +		switchGameMode(newMode, _gameMode); +		_gameMode = newMode; +	} +} + +void PegasusEngine::switchGameMode(const GameMode newMode, const GameMode oldMode) { +	// Start raising panels before lowering panels, to give the activating panel time +	// to set itself up without cutting into the lowering panel's animation time. +	 +	if (_switchModesSync) { +		if (newMode == kModeInventoryPick) +			raiseInventoryDrawerSync(); +		else if (newMode == kModeBiochipPick) +			raiseBiochipDrawerSync(); +		else if (newMode == kModeInfoScreen) +			showInfoScreen(); +		 +		if (oldMode == kModeInventoryPick) +			lowerInventoryDrawerSync(); +		else if (oldMode == kModeBiochipPick) +			lowerBiochipDrawerSync(); +		else if (oldMode == kModeInfoScreen) +			hideInfoScreen(); +	} else { +		if (newMode == kModeInventoryPick) +			raiseInventoryDrawer(); +		else if (newMode == kModeBiochipPick) +			raiseBiochipDrawer(); +		else if (newMode == kModeInfoScreen) +			showInfoScreen(); +		 +		if (oldMode == kModeInventoryPick) +			lowerInventoryDrawer(); +		else if (oldMode == kModeBiochipPick) +			lowerBiochipDrawer(); +		else if (oldMode == kModeInfoScreen) +			hideInfoScreen(); +	} +} + +bool PegasusEngine::canSwitchGameMode(const GameMode newMode, const GameMode oldMode) { +	if (newMode == kModeInventoryPick && oldMode == kModeBiochipPick) +		return false; +	if (newMode == kModeBiochipPick && oldMode == kModeInventoryPick) +		return false; +	return true; +} + +bool PegasusEngine::itemInLocation(const ItemID itemID, const NeighborhoodID neighborhood, const RoomID room, const DirectionConstant direction) { +	NeighborhoodID itemNeighborhood; +	RoomID itemRoom; +	DirectionConstant itemDirection; +	 +	Item *item = g_allItems.findItemByID(itemID); +	item->getItemRoom(itemNeighborhood, itemRoom, itemDirection); + +	return itemNeighborhood == neighborhood && itemRoom == room && itemDirection == direction; +} + +InventoryResult PegasusEngine::addItemToInventory(InventoryItem *item) { +	InventoryResult result; +	 +	do { +		if (g_interface) +			result = g_interface->addInventoryItem(item); +		else +			result = _items.addItem(item); + +		if (result == kTooMuchWeight) +			destroyInventoryItem(pickItemToDestroy()); +	} while (result != kInventoryOK); + +	GameState.setTakenItem(item, true); +	if (g_neighborhood) +			g_neighborhood->pickedUpItem(item); + +	g_AIArea->checkMiddleArea(); + +	return result; +} + +void PegasusEngine::useNeighborhood(Neighborhood *neighborhood) { +	delete _neighborhood; +	_neighborhood = neighborhood; + +	if (_neighborhood) { +		InputHandler::setInputHandler(_neighborhood); +		_neighborhood->init(); +		_neighborhood->moveNavTo(kNavAreaLeft, kNavAreaTop); +		g_interface->setDate(_neighborhood->getDateResID()); +	} else { +		InputHandler::setInputHandler(this); +	} +} + +void PegasusEngine::performJump(NeighborhoodID neighborhoodID) { +	if (_neighborhood) +		useNeighborhood(0); + +	// Sub chase is special +	if (neighborhoodID == kNoradSubChaseID) { +		throwAwayEverything(); +		doSubChase(); + +		if (shouldQuit()) +			return; + +		neighborhoodID = kNoradDeltaID; +		GameState.setNextRoom(kNorad41); +		GameState.setNextDirection(kEast); +	} + +	Neighborhood *neighborhood; +	makeNeighborhood(neighborhoodID, neighborhood); +	useNeighborhood(neighborhood); + +	// Update the CD variable (used just for saves currently) +	_currentCD = getNeighborhoodCD(neighborhoodID); +} + +void PegasusEngine::startNeighborhood() { +	if (g_interface && _currentItemID != kNoItemID) +		g_interface->setCurrentInventoryItemID(_currentItemID); +	 +	if (g_interface && _currentBiochipID != kNoItemID) +		g_interface->setCurrentBiochipID(_currentBiochipID); +	 +	setGameMode(kModeNavigation); +	 +	if (_neighborhood) +		_neighborhood->start(); +} + +void PegasusEngine::startNewGame() { +	// WORKAROUND: The original game ignored the menu difficulty +	// setting. We're going to pass it through here so that +	// the menu actually makes sense now. +	bool isWalkthrough = GameState.getWalkthroughMode(); +	GameState.resetGameState(); +	GameState.setWalkthroughMode(isWalkthrough); + +	// TODO: Enable erase +	_gfx->doFadeOutSync(); +	useMenu(0); +	_gfx->updateDisplay(); + +	createInterface(); + +	if (isDemo()) { +		setLastEnergyValue(kFullEnergy); +		jumpToNewEnvironment(kPrehistoricID, kPrehistoric02, kSouth); +		GameState.setPrehistoricSeenTimeStream(false); +		GameState.setPrehistoricSeenFlyer1(false); +		GameState.setPrehistoricSeenFlyer2(false); +		GameState.setPrehistoricSeenBridgeZoom(false); +		GameState.setPrehistoricBreakerThrown(false); +	} else { +		jumpToNewEnvironment(kCaldoriaID, kCaldoria00, kEast); +	} + +	removeAllItemsFromInventory(); +	removeAllItemsFromBiochips(); + +	BiochipItem *biochip = (BiochipItem *)g_allItems.findItemByID(kAIBiochip); +	addItemToBiochips(biochip); + +	if (isDemo()) { +		biochip = (BiochipItem *)g_allItems.findItemByID(kPegasusBiochip); +		addItemToBiochips(biochip); +		biochip = (BiochipItem *)g_allItems.findItemByID(kMapBiochip); +		addItemToBiochips(biochip); +		InventoryItem *item = (InventoryItem *)g_allItems.findItemByID(kKeyCard); +		addItemToInventory(item); +		item = (InventoryItem *)g_allItems.findItemByID(kJourneymanKey); +		addItemToInventory(item); +		_currentItemID = kJourneymanKey; +	} else { +		_currentItemID = kNoItemID; +	} + +	_currentBiochipID = kAIBiochip; + +	// Clear jump notification flags and just perform the jump... +	_shellNotification.setNotificationFlags(0, kNeedNewJumpFlag); +	 +	performJump(GameState.getNextNeighborhood()); +	 +	startNeighborhood(); +} + +void PegasusEngine::makeNeighborhood(NeighborhoodID neighborhoodID, Neighborhood *&neighborhood) { +	// TODO: CD check + +	switch (neighborhoodID) { +	case kCaldoriaID: +		neighborhood = new Caldoria(g_AIArea, this); +		break; +	case kMarsID: +		neighborhood = new Mars(g_AIArea, this); +		break; +	case kPrehistoricID: +		neighborhood = new Prehistoric(g_AIArea, this); +		break; +	case kFullTSAID: +		neighborhood = new FullTSA(g_AIArea, this); +		break; +	case kTinyTSAID: +		neighborhood = new TinyTSA(g_AIArea, this); +		break; +	case kWSCID: +		neighborhood = new WSC(g_AIArea, this); +		break; +	case kNoradAlphaID: +		neighborhood = new NoradAlpha(g_AIArea, this); +		break; +	case kNoradDeltaID: +		createInterface(); +		neighborhood = new NoradDelta(g_AIArea, this); +		break; +	default: +		error("Unknown neighborhood %d", neighborhoodID); +	} +} + +bool PegasusEngine::wantsCursor() { +	return _gameMenu == 0; +} + +void PegasusEngine::updateCursor(const Common::Point, const Hotspot *cursorSpot) {	 +	if (_itemDragger.isTracking()) { +		_cursor->setCurrentFrameIndex(5); +	} else { +		if (!cursorSpot) { +			_cursor->setCurrentFrameIndex(0); +		} else { +			uint32 id = cursorSpot->getObjectID(); + +			switch (id) { +			case kCurrentItemSpotID: +				if (countInventoryItems() != 0) +					_cursor->setCurrentFrameIndex(4); +				else +					_cursor->setCurrentFrameIndex(0); +				break; +			default: +				HotSpotFlags flags = cursorSpot->getHotspotFlags(); + +				if (flags & kZoomInSpotFlag) +					_cursor->setCurrentFrameIndex(1); +				else if (flags & kZoomOutSpotFlag) +					_cursor->setCurrentFrameIndex(2); +				else if (flags & (kPickUpItemSpotFlag | kPickUpBiochipSpotFlag)) +					_cursor->setCurrentFrameIndex(4); +				else if (flags & kJMPClickingSpotFlags) +					_cursor->setCurrentFrameIndex(3); +				else +					_cursor->setCurrentFrameIndex(0); +			} +		} +	} +} + +void PegasusEngine::toggleInventoryDisplay() { +	if (_gameMode == kModeInventoryPick) +		setGameMode(kModeNavigation); +	else +		setGameMode(kModeInventoryPick); +} + +void PegasusEngine::toggleBiochipDisplay() { +	if (_gameMode == kModeBiochipPick) +		setGameMode(kModeNavigation); +	else +		setGameMode(kModeBiochipPick); +} + +void PegasusEngine::showInfoScreen() {	 +	if (g_neighborhood) { +		// Break the input handler chain... +		_savedHandler = InputHandler::getCurrentHandler(); +		InputHandler::setInputHandler(this); +		 +		Picture *pushPicture = ((Neighborhood *)g_neighborhood)->getTurnPushPicture(); +		 +		_bigInfoMovie.shareSurface(pushPicture); +		_smallInfoMovie.shareSurface(pushPicture); + +		g_neighborhood->hideNav(); + +		_smallInfoMovie.initFromMovieFile("Images/Items/Info Right Movie"); +		_smallInfoMovie.setDisplayOrder(kInfoSpinOrder); +		_smallInfoMovie.moveElementTo(kNavAreaLeft + 304, kNavAreaTop + 8); +		_smallInfoMovie.moveMovieBoxTo(304, 8); +		_smallInfoMovie.startDisplaying(); +		_smallInfoMovie.show(); + +		TimeValue startTime, stopTime; +		g_AIArea->getSmallInfoSegment(startTime, stopTime); +		_smallInfoMovie.setSegment(startTime, stopTime); +		_smallInfoMovie.setTime(startTime); +		_smallInfoMovie.setFlags(kLoopTimeBase); + +		_bigInfoMovie.initFromMovieFile("Images/Items/Info Left Movie"); +		_bigInfoMovie.setDisplayOrder(kInfoBackgroundOrder); +		_bigInfoMovie.moveElementTo(kNavAreaLeft, kNavAreaTop); +		_bigInfoMovie.startDisplaying(); +		_bigInfoMovie.show(); +		_bigInfoMovie.setTime(g_AIArea->getBigInfoTime()); + +		_bigInfoMovie.redrawMovieWorld(); +		_smallInfoMovie.redrawMovieWorld(); +		_smallInfoMovie.start(); +	} +} + +void PegasusEngine::hideInfoScreen() { +	if (g_neighborhood) { +		InputHandler::setInputHandler(_savedHandler); + +		_bigInfoMovie.hide(); +		_bigInfoMovie.stopDisplaying(); +		_bigInfoMovie.releaseMovie(); + +		_smallInfoMovie.hide(); +		_smallInfoMovie.stopDisplaying(); +		_smallInfoMovie.stop(); +		_smallInfoMovie.releaseMovie(); + +		g_neighborhood->showNav(); +	} +} + +void PegasusEngine::raiseInventoryDrawer() { +	if (g_interface) +		g_interface->raiseInventoryDrawer(); +} + +void PegasusEngine::raiseBiochipDrawer() { +	if (g_interface) +		g_interface->raiseBiochipDrawer(); +} + +void PegasusEngine::lowerInventoryDrawer() { +	if (g_interface) +		g_interface->lowerInventoryDrawer(); +} + +void PegasusEngine::lowerBiochipDrawer() { +	if (g_interface) +		g_interface->lowerBiochipDrawer(); +} + +void PegasusEngine::raiseInventoryDrawerSync() { +	if (g_interface) +		g_interface->raiseInventoryDrawerSync(); +} + +void PegasusEngine::raiseBiochipDrawerSync() { +	if (g_interface) +		g_interface->raiseBiochipDrawerSync(); +} + +void PegasusEngine::lowerInventoryDrawerSync() { +	if (g_interface) +		g_interface->lowerInventoryDrawerSync(); +} + +void PegasusEngine::lowerBiochipDrawerSync() { +	if (g_interface) +		g_interface->lowerBiochipDrawerSync(); +} + +void PegasusEngine::toggleInfo() { +	if (_gameMode == kModeInfoScreen) +		setGameMode(kModeNavigation); +	else if (_gameMode == kModeNavigation) +		setGameMode(kModeInfoScreen); +} + +void PegasusEngine::dragTerminated(const Input &) { +	Hotspot *finalSpot = _itemDragger.getLastHotspot(); +	InventoryResult result; + +	if (_dragType == kDragInventoryPickup) { +		if (finalSpot && finalSpot->getObjectID() == kInventoryDropSpotID) +			result = addItemToInventory((InventoryItem *)_draggingItem); +		else +			result = kTooMuchWeight; + +		if (result != kInventoryOK) +			autoDragItemIntoRoom(_draggingItem, _draggingSprite); +		else +			delete _draggingSprite; +	} else if (_dragType == kDragBiochipPickup) { +		if (finalSpot && finalSpot->getObjectID() == kBiochipDropSpotID) +			result = addItemToBiochips((BiochipItem *)_draggingItem); +		else +			result = kTooMuchWeight; + +		if (result != kInventoryOK) +			autoDragItemIntoRoom(_draggingItem, _draggingSprite); +		else +			delete _draggingSprite; +	} else if (_dragType == kDragInventoryUse) { +		if (finalSpot && (finalSpot->getHotspotFlags() & kDropItemSpotFlag) != 0) { +			// *** Need to decide on a case by case basis what to do here. +			// the crowbar should break the cover off the Mars reactor if its frozen, the +			// global transport card should slide through the slot, the oxygen mask should +			// attach to the filling station, and so on... +			_neighborhood->dropItemIntoRoom(_draggingItem, finalSpot); +			delete _draggingSprite; +		} else { +			autoDragItemIntoInventory(_draggingItem, _draggingSprite); +		} +	} + +	_dragType = kDragNoDrag; + +	if (g_AIArea) +		g_AIArea->unlockAI(); +} + + +void PegasusEngine::dragItem(const Input &input, Item *item, DragType type) {	 +	_draggingItem = item; +	_dragType = type; + +	// Create the sprite. +	_draggingSprite = _draggingItem->getDragSprite(kDraggingSpriteID); +	Common::Point where; +	input.getInputLocation(where); +	Common::Rect r1; +	_draggingSprite->getBounds(r1); +	r1 = Common::Rect::center(where.x, where.y, r1.width(), r1.height()); +	_draggingSprite->setBounds(r1); + +	// Set up drag constraints. +	DisplayElement *navMovie = _gfx->findDisplayElement(kNavMovieID); +	Common::Rect r2; +	navMovie->getBounds(r2); +	r2.left -= r1.width() / 3; +	r2.right += r1.width() / 3; +	r2.top -= r1.height() / 3; +	r2.bottom += r2.height() / 3; + +	r1 = Common::Rect(-30000, -30000, 30000, 30000); +	_itemDragger.setDragConstraints(r2, r1); + +	// Start dragging. +	_draggingSprite->setDisplayOrder(kDragSpriteOrder); +	_draggingSprite->startDisplaying(); +	_draggingSprite->show(); +	_itemDragger.setDragSprite(_draggingSprite); +	_itemDragger.setNextHandler(_neighborhood); +	_itemDragger.startTracking(input); + +	if (g_AIArea) +		g_AIArea->lockAIOut(); +} + +void PegasusEngine::shellGameInput(const Input &input, const Hotspot *cursorSpot) { +	if (_gameMode == kModeInfoScreen) { +		if (JMPPPInput::isToggleAIMiddleInput(input)) { +			LowerClientSignature middleOwner = g_AIArea->getMiddleAreaOwner(); +			g_AIArea->toggleMiddleAreaOwner(); + +			if (middleOwner != g_AIArea->getMiddleAreaOwner()) { +				_bigInfoMovie.setTime(g_AIArea->getBigInfoTime()); +				_smallInfoMovie.stop(); +				_smallInfoMovie.setFlags(0); + +				TimeValue startTime, stopTime; +				g_AIArea->getSmallInfoSegment(startTime, stopTime); +				_smallInfoMovie.setSegment(startTime, stopTime); +				_smallInfoMovie.setTime(startTime); +				_smallInfoMovie.setFlags(kLoopTimeBase); + +				_bigInfoMovie.redrawMovieWorld(); +				_smallInfoMovie.redrawMovieWorld(); +				_smallInfoMovie.start(); +			} +		} +	} else { +		if (JMPPPInput::isRaiseInventoryInput(input)) +			toggleInventoryDisplay(); + +		if (JMPPPInput::isRaiseBiochipsInput(input)) +			toggleBiochipDisplay(); + +		if (JMPPPInput::isTogglePauseInput(input) && _neighborhood) +			pauseMenu(!isPaused()); +	} + +	if (JMPPPInput::isToggleInfoInput(input)) +		toggleInfo(); +} + +void PegasusEngine::activateHotspots() { +	if (_gameMode == kModeInfoScreen) { +		g_allHotspots.activateOneHotspot(kInfoReturnSpotID); +	} else { +		// Set up hot spots. +		if (_dragType == kDragInventoryPickup) +			g_allHotspots.activateOneHotspot(kInventoryDropSpotID); +		else if (_dragType == kDragBiochipPickup) +			g_allHotspots.activateOneHotspot(kBiochipDropSpotID); +		else if (_dragType == kDragNoDrag) +			g_allHotspots.activateMaskedHotspots(kShellSpotFlag); +	} +} + +bool PegasusEngine::isClickInput(const Input &input, const Hotspot *cursorSpot) { +	if (_cursor->isVisible() && cursorSpot) +		return JMPPPInput::isClickInput(input); +	else +		return false; +} + +InputBits PegasusEngine::getClickFilter() { +	return JMPPPInput::getClickInputFilter(); +} + +void PegasusEngine::clickInHotspot(const Input &input, const Hotspot *clickedSpot) { +	if (clickedSpot->getObjectID() == kCurrentItemSpotID) { +		InventoryItem *currentItem = getCurrentInventoryItem(); +		if (currentItem) { +			removeItemFromInventory(currentItem); +			dragItem(input, currentItem, kDragInventoryUse); +		} +	} else if (clickedSpot->getObjectID() == kInfoReturnSpotID) { +		toggleInfo(); +	} +} + +InventoryResult PegasusEngine::removeItemFromInventory(InventoryItem *item) { +	InventoryResult result; +	 +	if (g_interface) +		result = g_interface->removeInventoryItem(item); +	else +		result = _items.removeItem(item); + +	// This should never happen +	assert(result == kInventoryOK); + +	return result; +} + +void PegasusEngine::removeAllItemsFromInventory() { +	if (g_interface) +		g_interface->removeAllItemsFromInventory(); +	else +		_items.removeAllItems(); +} + +///////////////////////////////////////////// +// +// Biochip handling. + +// Adding biochips to the biochip drawer is a little funny, because of two things: +//      --  We get the map biochip and pegasus biochip at the same time by dragging +//          one sprite in TSA +//      --  We can drag in more than one copy of the various biochips. +// Because of this we need to make sure that no more than one copy of each biochip +// is ever added. + +InventoryResult PegasusEngine::addItemToBiochips(BiochipItem *biochip) { +	InventoryResult result; + +	if (g_interface) +		result = g_interface->addBiochip(biochip); +	else +		result = _biochips.addItem(biochip); + +	// This can never happen +	assert(result == kInventoryOK); + +	GameState.setTakenItem(biochip, true); + +	if (g_neighborhood) +		g_neighborhood->pickedUpItem(biochip); + +	g_AIArea->checkMiddleArea(); + +	return result; +} + +void PegasusEngine::removeAllItemsFromBiochips() { +	if (g_interface) +		g_interface->removeAllItemsFromBiochips(); +	else +		_biochips.removeAllItems(); +} + +void PegasusEngine::setSoundFXLevel(uint16 fxLevel) { +	_FXLevel = fxLevel; +	if (_neighborhood) +		_neighborhood->setSoundFXLevel(fxLevel); +	if (g_AIArea) +		g_AIArea->setAIVolume(fxLevel); +} + +void PegasusEngine::setAmbienceLevel(uint16 ambientLevel) { +	_ambientLevel = ambientLevel; +	if (_neighborhood) +		_neighborhood->setAmbienceLevel(ambientLevel); +} + +void PegasusEngine::pauseMenu(bool menuUp) { +	if (menuUp) { +		pauseEngine(true); +		_screenDimmer.startDisplaying(); +		_screenDimmer.show(); +		_gfx->updateDisplay(); +		useMenu(new PauseMenu()); +	} else { +		pauseEngine(false); +		_screenDimmer.hide(); +		_screenDimmer.stopDisplaying(); +		useMenu(0); +		g_AIArea->checkMiddleArea(); +	} +} + +void PegasusEngine::autoDragItemIntoRoom(Item *item, Sprite *draggingSprite) {	 +	if (g_AIArea) +		g_AIArea->lockAIOut(); + +	Common::Point start, stop; +	draggingSprite->getLocation(start.x, start.y); + +	Hotspot *dropSpot = _neighborhood->getItemScreenSpot(item, draggingSprite); + +	if (dropSpot) { +		dropSpot->getCenter(stop.x, stop.y); +	} else { +		stop.x = kNavAreaLeft + 256; +		stop.y = kNavAreaTop + 128; +	} + +	Common::Rect bounds; +	draggingSprite->getBounds(bounds); +	stop.x -= bounds.width() >> 1; +	stop.y -= bounds.height() >> 1; + +	int dx = ABS(stop.x - start.x); +	int dy = ABS(stop.y - start.y); +	TimeValue time = MAX(dx, dy); + +	allowInput(false); +	_autoDragger.autoDrag(draggingSprite, start, stop, time, kDefaultTimeScale); + +	while (_autoDragger.isDragging()) { +		checkCallBacks(); +		refreshDisplay(); +		_system->delayMillis(10); +	} + +	_neighborhood->dropItemIntoRoom(_draggingItem, dropSpot); +	allowInput(true); +	delete _draggingSprite; + +	if (g_AIArea) +		g_AIArea->unlockAI(); +} + +void PegasusEngine::autoDragItemIntoInventory(Item *, Sprite *draggingSprite) {	 +	if (g_AIArea) +		g_AIArea->lockAIOut(); + +	Common::Point start; +	draggingSprite->getLocation(start.x, start.y); + +	Common::Rect r; +	draggingSprite->getBounds(r); + +	Common::Point stop((76 + 172 - r.width()) / 2, 334 - (2 * r.height() / 3)); + +	int dx = ABS(stop.x - start.x); +	int dy = ABS(stop.y - start.y); +	TimeValue time = MAX(dx, dy); + +	allowInput(false); +	_autoDragger.autoDrag(draggingSprite, start, stop, time, kDefaultTimeScale); + +	while (_autoDragger.isDragging()) { +		checkCallBacks(); +		refreshDisplay(); +		_system->delayMillis(10); +	} + +	addItemToInventory((InventoryItem *)_draggingItem); +	allowInput(true); +	delete _draggingSprite; + +	if (g_AIArea) +		g_AIArea->unlockAI(); +} + +NeighborhoodID PegasusEngine::getCurrentNeighborhoodID() const { +	if (_neighborhood) +		return _neighborhood->getObjectID(); + +	return kNoNeighborhoodID; +} + +void PegasusEngine::pauseEngineIntern(bool pause) { +	Engine::pauseEngineIntern(pause); + +	if (pause) { +		for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) +			(*it)->pause(); +	} else { +		for (Common::List<TimeBase *>::iterator it = _timeBases.begin(); it != _timeBases.end(); it++) +			(*it)->resume(); +	} +} + +uint PegasusEngine::getRandomBit() { +	return _rnd->getRandomBit(); +} + +uint PegasusEngine::getRandomNumber(uint max) { +	return _rnd->getRandomNumber(max); +} + +void PegasusEngine::shuffleArray(int32 *arr, int32 count) {	 +	if (count > 1) { +		for (int32 i = 1; i < count; ++i) { +			int32 j = _rnd->getRandomNumber(i); +			if (j != i) +				SWAP(arr[i], arr[j]); +		} +	} +} + +void PegasusEngine::playEndMessage() { +	if (g_interface) { +		allowInput(false); +		g_interface->playEndMessage(); +		allowInput(true); +	} + +	die(kPlayerWonGame); +} + +void PegasusEngine::doSubChase() { +	static const uint32 endTime = 133200 * 1000 / 600; + +	Video::SeekableVideoDecoder *video = new Video::QuickTimeDecoder(); +	if (!video->loadFile("Images/Norad Alpha/Sub Chase Movie")) +		error("Failed to load sub chase"); + +	while (!shouldQuit() && !video->endOfVideo() && video->getElapsedTime() < endTime) { +		if (video->needsUpdate()) { +			const Graphics::Surface *frame = video->decodeNextFrame(); + +			if (frame) +				drawScaledFrame(frame, 0, 0); +		} + +		Common::Event event; +		while (_eventMan->pollEvent(event)) +			; + +		_system->delayMillis(10); +	} + +	delete video; +} + +void PegasusEngine::drawScaledFrame(const Graphics::Surface *frame, uint16 x, uint16 y) { +	// Scale up the frame doing some simple scaling +	Graphics::Surface scaledFrame; +	scaledFrame.create(frame->w * 2, frame->h * 2, frame->format); +	const byte *src = (const byte *)frame->pixels; +	byte *dst1 = (byte *)scaledFrame.pixels; +	byte *dst2 = (byte *)scaledFrame.pixels + scaledFrame.pitch; + +	for (int i = 0; i < frame->h; i++) { +		for (int j = 0; j < frame->w; j++) { +			memcpy(dst1, src, frame->format.bytesPerPixel); +			dst1 += frame->format.bytesPerPixel; +			memcpy(dst1, src, frame->format.bytesPerPixel); +			dst1 += frame->format.bytesPerPixel; +			memcpy(dst2, src, frame->format.bytesPerPixel); +			dst2 += frame->format.bytesPerPixel; +			memcpy(dst2, src, frame->format.bytesPerPixel); +			dst2 += frame->format.bytesPerPixel; +			src += frame->format.bytesPerPixel; +		} + +		src += frame->pitch - frame->format.bytesPerPixel * frame->w; +		dst1 += scaledFrame.pitch * 2 - scaledFrame.format.bytesPerPixel * scaledFrame.w; +		dst2 += scaledFrame.pitch * 2 - scaledFrame.format.bytesPerPixel * scaledFrame.w; +	} + +	_system->copyRectToScreen((byte *)scaledFrame.pixels, scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h); +	_system->updateScreen(); +	scaledFrame.free(); +} + +void PegasusEngine::destroyInventoryItem(const ItemID itemID) { +	InventoryItem *item = (InventoryItem *)g_allItems.findItemByID(itemID); + +	ItemExtraEntry entry; + +	switch (itemID) { +	case kAirMask: +		item->findItemExtra(kRemoveAirMask, entry); +		item->setItemRoom(kMarsID, kMars49, kSouth); +		break; +	case kArgonCanister: +		item->findItemExtra(kRemoveArgon, entry); +		item->setItemRoom(kWSCID, kWSC02Morph, kSouth); +		break; +	case kCrowbar: +		item->findItemExtra(kRemoveCrowbar, entry); +		item->setItemRoom(kMarsID, kMars34, kSouth); +		break; +	case kJourneymanKey: +		item->findItemExtra(kRemoveJourneymanKey, entry); +		item->setItemRoom(kFullTSAID, kTSA22Red, kEast); +		break; +	case kMarsCard: +		item->findItemExtra(kRemoveMarsCard, entry); +		item->setItemRoom(kMarsID, kMars31South, kSouth); +		break; +	case kNitrogenCanister: +		item->findItemExtra(kRemoveNitrogen, entry); +		item->setItemRoom(kWSCID, kWSC02Messages, kSouth); +		break; +	case kOrangeJuiceGlassEmpty: +		item->findItemExtra(kRemoveGlass, entry); +		item->setItemRoom(kCaldoriaID, kCaldoriaReplicator, kNorth); +		break; +	case kPoisonDart: +		item->findItemExtra(kRemoveDart, entry); +		item->setItemRoom(kWSCID, kWSC01, kWest); +		break; +	case kSinclairKey: +		item->findItemExtra(kRemoveSinclairKey, entry); +		item->setItemRoom(kWSCID, kWSC02Morph, kSouth); +		break; +	default: +		return; +	} + +	g_interface->setCurrentInventoryItemID(itemID); +	g_AIArea->playAIAreaSequence(kInventorySignature, kMiddleAreaSignature, entry.extraStart, entry.extraStop); +	removeItemFromInventory(item); +} + +ItemID PegasusEngine::pickItemToDestroy() { +/* +	Must pick an item to destroy +	 +	Part I: Polite -- try to find an item that's been used +	Part II: Desperate -- return the first available item. +*/ + +	// Polite: +	if (playerHasItemID(kOrangeJuiceGlassEmpty)) +		return kOrangeJuiceGlassEmpty; +	if (playerHasItemID(kPoisonDart)) { +		if (GameState.getCurrentNeighborhood() != kWSCID || +				GameState.getWSCAnalyzedDart()) +			return kPoisonDart; +	} +	if (playerHasItemID(kJourneymanKey)) { +		if (GameState.getTSAState() >= kTSAPlayerGotHistoricalLog && +				GameState.getTSAState() != kPlayerOnWayToPrehistoric && +				GameState.getTSAState() != kPlayerWentToPrehistoric) +			return kJourneymanKey; +	} +	if (playerHasItemID(kMarsCard)) { +		if (GameState.getCurrentNeighborhood() != kMarsID || GameState.getMarsArrivedBelow()) +			return kMarsCard; +	} + +	// Don't want to deal with deleting the sinclair key and argon canister, since it's +	// impossible to pick them up one at a time. + +	if (playerHasItemID(kNitrogenCanister)) { +		if (GameState.getScoringGotCardBomb() && GameState.getCurrentNeighborhood() != kMarsID) +			return kNitrogenCanister; +	} +	if (playerHasItemID(kCrowbar)) { +		if (GameState.getCurrentNeighborhood() == kWSCID) { +			if (GameState.getCurrentRoom() >= kWSC62) +				return kCrowbar; +		} else if (GameState.getCurrentNeighborhood() == kMarsID) { +			if (GameState.getScoringGotCardBomb()) +				return kCrowbar; +		} else +			return kCrowbar; +	} +	if (playerHasItemID(kAirMask)) { +		if (GameState.getCurrentNeighborhood() == kMarsID) { +			if (g_neighborhood->getAirQuality(GameState.getCurrentRoom()) == kAirQualityGood) +				return kAirMask; +		} else if (GameState.getCurrentNeighborhood() != kNoradAlphaID && +				GameState.getCurrentNeighborhood() != kNoradDeltaID) { +			return kAirMask; +		} +	} + +	// Desperate: +	if (playerHasItemID(kPoisonDart)) +		return kPoisonDart; +	if (playerHasItemID(kJourneymanKey)) +		return kJourneymanKey; +	if (playerHasItemID(kMarsCard)) +		return kMarsCard; +	if (playerHasItemID(kNitrogenCanister)) +		return kNitrogenCanister; +	if (playerHasItemID(kCrowbar)) +		return kCrowbar; +	if (playerHasItemID(kAirMask)) +		return kAirMask; + +	// Should never get this far... +	error("Could not find item to delete"); + +	return kNoItemID; +} + +uint PegasusEngine::getNeighborhoodCD(const NeighborhoodID neighborhood) const { +	switch (neighborhood) { +	case kCaldoriaID: +	case kNoradAlphaID: +	case kNoradSubChaseID: +		return 1; +	case kFullTSAID: +	case kPrehistoricID: +		return 2; +	case kMarsID: +		return 3; +	case kWSCID: +	case kNoradDeltaID: +		return 4; +	case kTinyTSAID: +		// Tiny TSA exists on three of the CD's, so just continue +		// with the CD we're on +		return _currentCD; +	} + +	// Can't really happen, but it's a good fallback anyway :P +	return 1; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h new file mode 100644 index 0000000000..a1b4cff9ab --- /dev/null +++ b/engines/pegasus/pegasus.h @@ -0,0 +1,322 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_H +#define PEGASUS_H + +#include "common/list.h" +#include "common/macresman.h" +#include "common/rect.h" +#include "common/scummsys.h" +#include "common/system.h" +#include "common/util.h" + +#include "engines/engine.h" + +#include "pegasus/graphics.h" +#include "pegasus/hotspot.h" +#include "pegasus/input.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" +#include "pegasus/items/autodragger.h" +#include "pegasus/items/inventory.h" +#include "pegasus/items/itemdragger.h" +#include "pegasus/neighborhood/neighborhood.h" + +namespace Common { +	class RandomSource; +} + +namespace Video { +	class SeekableVideoDecoder; +} + +namespace Pegasus { + +class PegasusConsole; +struct PegasusGameDescription; +class SoundManager; +class GraphicsManager; +class Idler; +class Cursor; +class TimeBase; +class GameMenu; +class InventoryItem; +class BiochipItem; +class Neighborhood; + +class PegasusEngine : public ::Engine, public InputHandler, public NotificationManager { +friend class InputHandler; + +public: +	PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc); +	virtual ~PegasusEngine(); + +	// Engine stuff +	const PegasusGameDescription *_gameDescription; +	bool hasFeature(EngineFeature f) const; +	GUI::Debugger *getDebugger(); +	bool canLoadGameStateCurrently() { return _loadAllowed && !isDemo(); } +	bool canSaveGameStateCurrently() { return _saveAllowed && !isDemo(); } +	Common::Error loadGameState(int slot); +	Common::Error saveGameState(int slot, const Common::String &desc); + +	// Base classes +	GraphicsManager *_gfx; +	Common::MacResManager *_resFork; +	Cursor *_cursor; + +	// Menu +	void useMenu(GameMenu *menu); +	bool checkGameMenu(); + +	// Misc. +	bool isDemo() const; +	void addIdler(Idler *idler); +	void removeIdler(Idler *idler); +	void addTimeBase(TimeBase *timeBase); +	void removeTimeBase(TimeBase *timeBase); +	void delayShell(TimeValue time, TimeScale scale); +	void resetIntroTimer(); +	void introTimerExpired(); +	void refreshDisplay(); +	bool playerAlive(); +	void processShell(); +	void checkCallBacks(); +	void createInterface(); +	void setGameMode(const GameMode); +	GameMode getGameMode() const { return _gameMode; } +	uint getRandomBit(); +	uint getRandomNumber(uint max); +	void shuffleArray(int32 *arr, int32 count); +	void drawScaledFrame(const Graphics::Surface *frame, uint16 x, uint16 y); + +	// Energy +	void setLastEnergyValue(const int32 value) { _savedEnergyValue = value; } +	int32 getSavedEnergyValue() { return _savedEnergyValue; } + +	// Death +	void setEnergyDeathReason(const DeathReason reason) { _deathReason = reason; }  +	DeathReason getEnergyDeathReason() { return _deathReason; } +	void resetEnergyDeathReason(); +	void die(const DeathReason); +	void playEndMessage(); + +	// Volume +	uint16 getSoundFXLevel() { return _FXLevel; } +	void setSoundFXLevel(uint16); +	uint16 getAmbienceLevel() { return _ambientLevel; } +	void setAmbienceLevel(uint16); + +	// Items +	bool playerHasItem(const Item *); +	bool playerHasItemID(const ItemID); +	void checkFlashlight(); +	bool itemInLocation(const ItemID, const NeighborhoodID, const RoomID, const DirectionConstant); + +	// Inventory Items +	InventoryItem *getCurrentInventoryItem(); +	bool itemInInventory(InventoryItem *); +	bool itemInInventory(ItemID); +	Inventory *getItemsInventory() { return &_items; } +	InventoryResult addItemToInventory(InventoryItem *); +	void removeAllItemsFromInventory(); +	InventoryResult removeItemFromInventory(InventoryItem *); +	uint32 countInventoryItems() { return _items.getNumItems(); } + +	// Biochips +	BiochipItem *getCurrentBiochip(); +	bool itemInBiochips(BiochipItem *); +	bool itemInBiochips(ItemID); +	Inventory *getBiochipsInventory() { return &_biochips; } +	void removeAllItemsFromBiochips(); +	InventoryResult addItemToBiochips(BiochipItem *); + +	// AI +	Common::String getBriefingMovie(); +	Common::String getEnvScanMovie(); +	uint getNumHints(); +	Common::String getHintMovie(uint); +	bool canSolve(); +	void prepareForAIHint(const Common::String &); +	void cleanUpAfterAIHint(const Common::String &); +	Common::SeekableReadStream *_aiSaveStream; + +	// Neighborhood +	void jumpToNewEnvironment(const NeighborhoodID, const RoomID, const DirectionConstant); +	NeighborhoodID getCurrentNeighborhoodID() const; + +	// Dragging +	void dragItem(const Input &, Item *, DragType); +	bool isDragging() const { return _dragType != kDragNoDrag; } +	DragType getDragType() const { return _dragType; } +	Item *getDraggingItem() const { return _draggingItem; } +	void dragTerminated(const Input &); +	void autoDragItemIntoRoom(Item *, Sprite *); +	void autoDragItemIntoInventory(Item *, Sprite*); + +	// Save/Load +	void makeContinuePoint(); +	bool swapSaveAllowed(bool allow) { +		bool old = _saveAllowed; +		_saveAllowed = allow; +		return old; +	} +	bool swapLoadAllowed(bool allow) { +		bool old = _loadAllowed; +		_loadAllowed = allow; +		return old; +	} +	void requestSave() { _saveRequested = true; } +	bool saveRequested() const { return _saveRequested; } +	void requestLoad() { _loadRequested = true; } +	bool loadRequested() const { return _loadRequested; } + +protected: +	Common::Error run(); +	void pauseEngineIntern(bool pause); + +	Notification _shellNotification; +	virtual void receiveNotification(Notification *notification, const NotificationFlags flags); + +	void handleInput(const Input &input, const Hotspot *cursorSpot); +	virtual bool isClickInput(const Input &, const Hotspot *); +	virtual InputBits getClickFilter(); + +	void clickInHotspot(const Input &, const Hotspot *); +	void activateHotspots(void); + +	void updateCursor(const Common::Point, const Hotspot *); +	bool wantsCursor(); + +private: +	// Console +	PegasusConsole *_console; + +	// Intro +	void runIntro(); +	void stopIntroTimer(); +	bool detectOpeningClosingDirectory(); +	Common::String _introDirectory; +	FuseFunction *_introTimer; + +	// Idlers +	Idler *_idlerHead; +	void giveIdleTime(); + +	// Items +	void createItems(); +	void createItem(ItemID itemID, NeighborhoodID neighborhoodID, RoomID roomID, DirectionConstant direction); +	Inventory _items; +	Inventory _biochips; +	ItemID _currentItemID; +	ItemID _currentBiochipID; +	void destroyInventoryItem(const ItemID itemID); +	ItemID pickItemToDestroy(); + +	// TimeBases +	Common::List<TimeBase *> _timeBases; + +	// Save/Load +	bool loadFromStream(Common::ReadStream *stream); +	bool writeToStream(Common::WriteStream *stream, int saveType); +	void loadFromContinuePoint(); +	Common::ReadStream *_continuePoint; +	bool _saveAllowed, _loadAllowed; // It's so nice that this was in the original code already :P +	Common::Error showLoadDialog(); +	Common::Error showSaveDialog(); +	bool _saveRequested, _loadRequested; + +	// Misc. +	Hotspot _returnHotspot; +	InputHandler *_savedHandler; +	void showTempScreen(const Common::String &fileName); +	bool playMovieScaled(Video::SeekableVideoDecoder *video, uint16 x, uint16 y); +	void throwAwayEverything(); +	void shellGameInput(const Input &input, const Hotspot *cursorSpot); +	Common::RandomSource *_rnd; +	void doSubChase(); +	uint getNeighborhoodCD(const NeighborhoodID neighborhood) const; +	uint _currentCD; + +	// Menu +	GameMenu *_gameMenu; +	void doGameMenuCommand(const GameMenuCommand); +	void doInterfaceOverview(); +	ScreenDimmer _screenDimmer; +	void pauseMenu(bool menuUp); + +	// Energy +	int32 _savedEnergyValue; + +	// Death +	DeathReason _deathReason; +	void doDeath(); + +	// Neighborhood +	Neighborhood *_neighborhood; +	void useNeighborhood(Neighborhood *neighborhood); +	void performJump(NeighborhoodID start); +	void startNewGame(); +	void startNeighborhood(); +	void makeNeighborhood(NeighborhoodID, Neighborhood *&); + +	// Sound +	uint16 _ambientLevel; +	uint16 _FXLevel; + +	// Game Mode +	GameMode _gameMode; +	bool _switchModesSync; +	void switchGameMode(const GameMode, const GameMode); +	bool canSwitchGameMode(const GameMode, const GameMode); + +	// Dragging +	ItemDragger _itemDragger; +	Item *_draggingItem; +	Sprite *_draggingSprite; +	DragType _dragType; +	AutoDragger _autoDragger; + +	// Interface +	void toggleInventoryDisplay(); +	void toggleBiochipDisplay(); +	void raiseInventoryDrawer(); +	void raiseBiochipDrawer(); +	void lowerInventoryDrawer(); +	void lowerBiochipDrawer(); +	void raiseInventoryDrawerSync(); +	void raiseBiochipDrawerSync(); +	void lowerInventoryDrawerSync(); +	void lowerBiochipDrawerSync(); +	void showInfoScreen(); +	void hideInfoScreen(); +	void toggleInfo(); +	Movie _bigInfoMovie, _smallInfoMovie; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/scoring.h b/engines/pegasus/scoring.h new file mode 100644 index 0000000000..fbf8641ecb --- /dev/null +++ b/engines/pegasus/scoring.h @@ -0,0 +1,281 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_SCORING_H +#define PEGASUS_SCORING_H + +#include "pegasus/types.h" + +namespace Pegasus { + +///////////////////////////////////////////// +// +// Scoring. + +static const CoordType kDeathScreenScoreLeft = 151; +static const CoordType kDeathScreenScoreTop = 212; +static const CoordType kDeathScreenScoreWidth = 124; +static const CoordType kDeathScreenScoreHeight = 12; +static const CoordType kDeathScreenScoreSkipVert = -16; + +// Caldoria & TSA + +static const GameScoreType kSawINNScore					= 5; +static const GameScoreType kTookShowerScore				= 2; +static const GameScoreType kFixedHairScore				= 2; +static const GameScoreType kGotKeyCardScore				= 5; +static const GameScoreType kReadPaperScore				= 2; +static const GameScoreType kLookThroughTelescopeScore		= 2; +static const GameScoreType kSawCaldoriaKioskScore			= 2; +static const GameScoreType kGoToTSAScore					= 3; + +static const GameScoreType kEnterTSAScore					= 2; +static const GameScoreType kSawBust1Score					= 2; +static const GameScoreType kSawBust2Score					= 2; +static const GameScoreType kSawBust3Score					= 2; +static const GameScoreType kSawBust4Score					= 2; +static const GameScoreType kSawBust5Score					= 2; +static const GameScoreType kSawBust6Score					= 2; +static const GameScoreType kSawTheoryScore				= 4; +static const GameScoreType kSawBackgroundScore			= 4; +static const GameScoreType kSawProcedureScore				= 4; +static const GameScoreType kGotJourneymanKeyScore			= 5; +static const GameScoreType kGotPegasusBiochipScore		= 5; +static const GameScoreType kGotBiosuitScore				= 5; +static const GameScoreType kGoToPrehistoricScore			= 5; + +static const GameScoreType kPutLogInReaderScore			= 5; +static const GameScoreType kSawCaldoriaNormalScore		= 2; +static const GameScoreType kSawCaldoriaAlteredScore		= 2; +static const GameScoreType kSawNoradNormalScore			= 2; +static const GameScoreType kSawNoradAlteredScore			= 2; +static const GameScoreType kSawMarsNormalScore			= 2; +static const GameScoreType kSawMarsAlteredScore			= 2; +static const GameScoreType kSawWSCNormalScore				= 2; +static const GameScoreType kSawWSCAlteredScore			= 2; +static const GameScoreType kWentToReadyRoom2Score			= 5; +static const GameScoreType kWentAfterSinclairScore		= 5; +static const GameScoreType kUsedCardBombScore				= 10; +static const GameScoreType kShieldedCardBombScore			= 5; +static const GameScoreType kStunnedSinclairScore			= 10; +static const GameScoreType kDisarmedNukeScore				= 10; + +static const GameScoreType kMaxCaldoriaTSAScoreBefore		=	kSawINNScore + +														kTookShowerScore + +														kFixedHairScore + +														kGotKeyCardScore + +														kReadPaperScore + +														kLookThroughTelescopeScore + +														kSawCaldoriaKioskScore + +														kGoToTSAScore + +														kEnterTSAScore + +														kSawBust1Score + +														kSawBust2Score + +														kSawBust3Score + +														kSawBust4Score + +														kSawBust5Score + +														kSawBust6Score + +														kSawTheoryScore + +														kSawBackgroundScore + +														kSawProcedureScore + +														kGotJourneymanKeyScore + +														kGotPegasusBiochipScore + +														kGotBiosuitScore + +														kGoToPrehistoricScore + +														kPutLogInReaderScore + +														kSawCaldoriaNormalScore + +														kSawCaldoriaAlteredScore + +														kSawNoradNormalScore + +														kSawNoradAlteredScore + +														kSawMarsNormalScore + +														kSawMarsAlteredScore + +														kSawWSCNormalScore + +														kSawWSCAlteredScore + +														kWentToReadyRoom2Score; + +static const GameScoreType kMaxCaldoriaTSAScoreAfter		=	kWentAfterSinclairScore + +														kUsedCardBombScore + +														kShieldedCardBombScore + +														kStunnedSinclairScore + +														kDisarmedNukeScore; + +static const GameScoreType kMaxCaldoriaTSAScore			=	kMaxCaldoriaTSAScoreBefore + +														kMaxCaldoriaTSAScoreAfter; + +// Prehistoric + +static const GameScoreType kThrewBreakerScore				= 10; +static const GameScoreType kExtendedBridgeScore			= 10; +static const GameScoreType kGotHistoricalLogScore			= 5; +static const GameScoreType kFinishedPrehistoricScore		= 10; + +static const GameScoreType kMaxPrehistoricScore			=	kThrewBreakerScore + +														kExtendedBridgeScore + +														kGotHistoricalLogScore + +														kFinishedPrehistoricScore; + +// Mars + +static const GameScoreType kThrownByRobotScore			= 3; +static const GameScoreType kGotMarsCardScore				= 5; +static const GameScoreType kSawMarsKioskScore				= 2; +static const GameScoreType kSawTransportMapScore			= 2; +static const GameScoreType kGotCrowBarScore				= 5; +static const GameScoreType kTurnedOnTransportScore		= 5; +static const GameScoreType kGotOxygenMaskScore			= 5; +static const GameScoreType kAvoidedRobotScore				= 5; +static const GameScoreType kActivatedPlatformScore		= 2; +static const GameScoreType kUsedLiquidNitrogenScore		= 3; +static const GameScoreType kUsedCrowBarScore				= 3; +static const GameScoreType kFoundCardBombScore			= 4; +static const GameScoreType kDisarmedCardBombScore			= 8; +static const GameScoreType kGotCardBombScore				= 5; +static const GameScoreType kThreadedMazeScore				= 5; +static const GameScoreType kThreadedGearRoomScore			= 2; +static const GameScoreType kEnteredShuttleScore			= 2; +static const GameScoreType kEnteredLaunchTubeScore		= 4; +static const GameScoreType kStoppedRobotsShuttleScore		= 10; +static const GameScoreType kGotMarsOpMemChipScore			= 10; +static const GameScoreType kFinishedMarsScore				= 10; + +static const GameScoreType kMaxMarsScore					=	kThrownByRobotScore + +														kGotMarsCardScore + +														kSawMarsKioskScore + +														kSawTransportMapScore + +														kGotCrowBarScore + +														kTurnedOnTransportScore + +														kGotOxygenMaskScore + +														kAvoidedRobotScore + +														kActivatedPlatformScore + +														kUsedLiquidNitrogenScore + +														kUsedCrowBarScore + +														kFoundCardBombScore + +														kDisarmedCardBombScore + +														kGotCardBombScore + +														kThreadedMazeScore + +														kThreadedGearRoomScore + +														kEnteredShuttleScore + +														kEnteredLaunchTubeScore + +														kStoppedRobotsShuttleScore + +														kGotMarsOpMemChipScore + +														kFinishedMarsScore; + +// Norad + +static const GameScoreType kSawSecurityMonitorScore		= 5; +static const GameScoreType kFilledOxygenCanisterScore		= 5; +static const GameScoreType kFilledArgonCanisterScore		= 5; +static const GameScoreType kSawUnconsciousOperatorScore	= 5; +static const GameScoreType kWentThroughPressureDoorScore	= 5; +static const GameScoreType kPreppedSubScore				= 5; +static const GameScoreType kEnteredSubScore				= 5; +static const GameScoreType kExitedSubScore				= 10; +static const GameScoreType kSawRobotAt54NorthScore		= 5; +static const GameScoreType kPlayedWithClawScore			= 5; +static const GameScoreType kUsedRetinalChipScore			= 5; +static const GameScoreType kFinishedGlobeGameScore		= 10; +static const GameScoreType kStoppedNoradRobotScore		= 10; +static const GameScoreType kGotNoradOpMemChipScore		= 10; +static const GameScoreType kFinishedNoradScore			= 10; + +static const GameScoreType kMaxNoradScore					=	kSawSecurityMonitorScore + +														kFilledOxygenCanisterScore + +														kFilledArgonCanisterScore + +														kSawUnconsciousOperatorScore + +														kWentThroughPressureDoorScore + +														kPreppedSubScore + +														kEnteredSubScore + +														kExitedSubScore + +														kSawRobotAt54NorthScore + +														kPlayedWithClawScore + +														kUsedRetinalChipScore + +														kFinishedGlobeGameScore + +														kStoppedNoradRobotScore + +														kGotNoradOpMemChipScore + +														kFinishedNoradScore; + +// WSC + +static const GameScoreType kRemovedDartScore				= 5; +static const GameScoreType kAnalyzedDartScore				= 5; +static const GameScoreType kBuiltAntidoteScore			= 5; +static const GameScoreType kGotSinclairKeyScore			= 5; +static const GameScoreType kGotArgonCanisterScore 		= 5; +static const GameScoreType kGotNitrogenCanisterScore		= 5; +static const GameScoreType kPlayedWithMessagesScore		= 2; +static const GameScoreType kSawMorphExperimentScore		= 3; +static const GameScoreType kEnteredSinclairOfficeScore	= 2; +static const GameScoreType kSawBrochureScore				= 3; +static const GameScoreType kSawSinclairEntry1Score		= 3; +static const GameScoreType kSawSinclairEntry2Score		= 3; +static const GameScoreType kSawSinclairEntry3Score		= 3; +static const GameScoreType kSawWSCDirectoryScore			= 3; +static const GameScoreType kUsedCrowBarInWSCScore			= 5; +static const GameScoreType kFinishedPlasmaDodgeScore		= 10; +static const GameScoreType kOpenedCatwalkScore			= 3; +static const GameScoreType kStoppedWSCRobotScore			= 10; +static const GameScoreType kGotWSCOpMemChipScore			= 10; +static const GameScoreType kFinishedWSCScore				= 10; + +static const GameScoreType kMaxWSCScore					=	kRemovedDartScore + +														kAnalyzedDartScore + +														kBuiltAntidoteScore + +														kGotSinclairKeyScore + +														kGotArgonCanisterScore + +														kGotNitrogenCanisterScore + +														kPlayedWithMessagesScore + +														kSawMorphExperimentScore + +														kEnteredSinclairOfficeScore + +														kSawBrochureScore + +														kSawSinclairEntry1Score + +														kSawSinclairEntry2Score + +														kSawSinclairEntry3Score + +														kSawWSCDirectoryScore + +														kUsedCrowBarInWSCScore + +														kFinishedPlasmaDodgeScore + +														kOpenedCatwalkScore + +														kStoppedWSCRobotScore + +														kGotWSCOpMemChipScore + +														kFinishedWSCScore; + +// Gandhi + +static const GameScoreType kMarsGandhiScore				= 10; +static const GameScoreType kNoradGandhiScore				= 10; +static const GameScoreType kWSCGandhiScore				= 10; + +static const GameScoreType kMaxGandhiScore				=	kMarsGandhiScore + +														kNoradGandhiScore + +														kWSCGandhiScore; + +static const GameScoreType kMaxTotalScore					=	kMaxCaldoriaTSAScore + +														kMaxPrehistoricScore + +														kMaxMarsScore + +														kMaxNoradScore + +														kMaxWSCScore + +														kMaxGandhiScore; +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/sound.cpp b/engines/pegasus/sound.cpp new file mode 100644 index 0000000000..bf15858e80 --- /dev/null +++ b/engines/pegasus/sound.cpp @@ -0,0 +1,181 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "audio/audiostream.h" +#include "audio/decoders/aiff.h" +#include "audio/decoders/quicktime.h" +#include "common/file.h" +#include "common/system.h" + +#include "pegasus/fader.h" +#include "pegasus/sound.h" + +namespace Pegasus { + +Sound::Sound() { +	_stream = 0; +	_volume = 0xFF; +	_fader = 0; +} + +Sound::~Sound() { +	disposeSound(); +} + +void Sound::disposeSound() { +	stopSound(); +	delete _stream; _stream = 0; +} + +void Sound::initFromAIFFFile(const Common::String &fileName) { +	disposeSound(); + +	Common::File *file = new Common::File(); +	if (!file->open(fileName)) { +		warning("Failed to open AIFF file '%s'", fileName.c_str()); +		delete file; +		return; +	} + +	_stream = Audio::makeAIFFStream(file, DisposeAfterUse::YES); +} + +void Sound::initFromQuickTime(const Common::String &fileName) { +	disposeSound(); + +	_stream = Audio::makeQuickTimeStream(fileName); + +	if (!_stream) +		warning("Failed to open QuickTime file '%s'", fileName.c_str()); +} + +void Sound::attachFader(SoundFader *fader) { +	if (_fader) +		_fader->attachSound(0); + +	_fader = fader; + +	if (_fader) +		_fader->attachSound(this); +} + +void Sound::playSound() { +	if (!isSoundLoaded()) +		return; + +	stopSound(); + +	if (_fader) +		setVolume(_fader->getFaderValue()); + +	g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, _stream, -1, _volume, 0, DisposeAfterUse::NO); +} + +void Sound::loopSound() { +	if (!isSoundLoaded()) +		return; + +	stopSound(); + +	// Create a looping stream +	Audio::AudioStream *loopStream = new Audio::LoopingAudioStream(_stream, 0, DisposeAfterUse::NO); + +	// Assume that if there is a fader, we're going to fade the sound in. +	if (_fader) +		setVolume(0); + +	g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, loopStream, -1, _volume, 0, DisposeAfterUse::YES); +} + +void Sound::playSoundSegment(uint32 start, uint32 end) { +	if (!isSoundLoaded()) +		return; + +	stopSound(); + +	Audio::AudioStream *subStream = new Audio::SubSeekableAudioStream(_stream, Audio::Timestamp(0, start, 600), Audio::Timestamp(0, end, 600), DisposeAfterUse::NO); + +	g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_handle, subStream, -1, _volume, 0, DisposeAfterUse::YES); +} + +void Sound::stopSound() { +	g_system->getMixer()->stopHandle(_handle); +} + +void Sound::setVolume(const uint16 volume) { +	// Clipping the volume to [0x00, 0xFF] instead of Apple's [0, 0x100] +	// We store the volume in case SetVolume is called before the sound starts + +	_volume = (volume == 0x100) ? 0xFF : volume; +	g_system->getMixer()->setChannelVolume(_handle, _volume); +} + +bool Sound::isPlaying() { +	return isSoundLoaded() && g_system->getMixer()->isSoundHandleActive(_handle); +} + +bool Sound::isSoundLoaded() const { +	return _stream != 0; +} + +SoundTimeBase::SoundTimeBase() { +	setScale(600); +	_startScale = 600; +	_stopScale = 600; +	_setToStart = false; +} + +void SoundTimeBase::playSoundSegment(uint32 startTime, uint32 endTime) { +	_startTime = startTime; +	_stopTime = endTime; +	_setToStart = true; +	_time = Common::Rational(startTime, getScale()); +	setRate(1); +	Sound::playSoundSegment(startTime, endTime); +} + +void SoundTimeBase::updateTime() { +	if (_setToStart) { +		if (isPlaying()) { +			// Not at the end, let's get the time +			uint numFrames = g_system->getMixer()->getSoundElapsedTime(_handle) * 600 / 1000; + +			// WORKAROUND: Our mixer is woefully inaccurate and quite often returns +			// times that exceed the actual length of the clip. We'll just fake times +			// that are under the final time to ensure any trigger for the end time is +			// only sent when the sound has actually stopped. +			if (numFrames >= (_stopTime - _startTime)) +				numFrames = _stopTime - _startTime - 1; + +			_time = Common::Rational(_startTime + numFrames, getScale()); +		} else { +			// Assume we reached the end +			_setToStart = false; +			_time = Common::Rational(_stopTime, getScale()); +		} +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/sound.h b/engines/pegasus/sound.h new file mode 100644 index 0000000000..57cfd52e41 --- /dev/null +++ b/engines/pegasus/sound.h @@ -0,0 +1,108 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_SOUND_H +#define PEGASUS_SOUND_H + +#include "audio/mixer.h" +#include "common/str.h" +#include "pegasus/timers.h" + +namespace Audio { +	class SeekableAudioStream; +} + +namespace Pegasus { + +class SoundFader; + +// Things you might want to do with sound: +// - Start it +// - Stop it +// - Loop it +// - Pause it +// - Set the volume +// - Set the pitch (rate) +// - Pan the sound +// - Change these settings dynamically over time + +class Sound { +public: +	Sound(); +	~Sound(); + +	// We only have one access point here because we should +	// only be opening an AIFF file from a file name. We're +	// not using the resource fork string resources. +	void initFromAIFFFile(const Common::String &fileName); + +	// Unlike the original game, we're going to use a regular +	// audio stream for sound spots. The original treated them +	// as movies. +	void initFromQuickTime(const Common::String &fileName); + +	void disposeSound(); +	bool isSoundLoaded() const; +	void playSound(); +	void loopSound(); +	void playSoundSegment(uint32 start, uint32 end); +	void stopSound(); +	void setVolume(const uint16 volume); +	bool isPlaying(); + +	void attachFader(SoundFader *fader); + +protected: +	Audio::SeekableAudioStream *_stream; +	Audio::SoundHandle _handle; +	byte _volume; + +	SoundFader *_fader; +}; + +// TODO: Make this class follow TimeBase better +// Right now it's just a loose wrapper to plug callbacks +// into sounds. Since this is only used for spot sounds, +// I'm not too worried about it right now as its usage +// is very limited. +// At the very least, the regular TimeBase functions for +// setting/getting should be neutered. +class SoundTimeBase : public Sound, public TimeBase { +public: +	SoundTimeBase(); +	~SoundTimeBase() {} + +	void playSoundSegment(uint32 start, uint32 end); + +protected: +	void updateTime(); + +private: +	bool _setToStart; +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/surface.cpp b/engines/pegasus/surface.cpp new file mode 100644 index 0000000000..897305f2a3 --- /dev/null +++ b/engines/pegasus/surface.cpp @@ -0,0 +1,392 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/file.h" +#include "common/macresman.h" +#include "common/stream.h" +#include "common/system.h" +#include "graphics/surface.h" +#include "graphics/decoders/pict.h" +#include "video/video_decoder.h" + +#include "pegasus/pegasus.h" +#include "pegasus/surface.h" + +namespace Pegasus { + +Surface::Surface() { +	_ownsSurface = false; +	_surface = 0; +} + +Surface::~Surface() { +	deallocateSurface(); +} + +void Surface::deallocateSurface() { +	if (_surface) { +		if (_ownsSurface) { +			_surface->free(); +			delete _surface; +		} + +		_surface = 0; +		_bounds = Common::Rect(); +		_ownsSurface = false; +	} +} + +void Surface::shareSurface(Surface *surface) { +	deallocateSurface(); + +	if (surface) { +		_ownsSurface = false; +		_surface = surface->getSurface(); +		surface->getSurfaceBounds(_bounds); +	} +} + +void Surface::allocateSurface(const Common::Rect &bounds) { +	deallocateSurface(); + +	if (bounds.isEmpty()) +		return; + +	_bounds = bounds; +	_surface = new Graphics::Surface(); +	_surface->create(bounds.width(), bounds.height(), g_system->getScreenFormat()); +	_ownsSurface = true; +} + +void Surface::getImageFromPICTFile(const Common::String &fileName) { +	Common::File pict; +	if (!pict.open(fileName)) +		error("Could not open picture '%s'", fileName.c_str()); + +	getImageFromPICTStream(&pict); +} + +void Surface::getImageFromPICTResource(Common::MacResManager *resFork, uint16 id) { +	Common::SeekableReadStream *res = resFork->getResource(MKTAG('P', 'I', 'C', 'T'), id); +	if (!res) +		error("Could not open PICT resource %d from '%s'", id, resFork->getBaseFileName().c_str()); +	 +	getImageFromPICTStream(res); +	delete res; +} + +void Surface::getImageFromPICTStream(Common::SeekableReadStream *stream) { +	Graphics::PICTDecoder pict; + +	if (!pict.loadStream(*stream)) +		error("Failed to load PICT image"); + +	_surface = pict.getSurface()->convertTo(g_system->getScreenFormat(), pict.getPalette()); +	_ownsSurface = true; +	_bounds = Common::Rect(0, 0, _surface->w, _surface->h); +} + +void Surface::getImageFromMovieFrame(Video::SeekableVideoDecoder *video, TimeValue time) { +	video->seekToTime(Audio::Timestamp(0, time, 600)); +	const Graphics::Surface *frame = video->decodeNextFrame(); + +	if (frame) { +		if (!_surface) +			_surface = new Graphics::Surface(); + +		_surface->copyFrom(*frame); +		_ownsSurface = true; +		_bounds = Common::Rect(0, 0, _surface->w, _surface->h); +	} else { +		deallocateSurface(); +	} +} + +void Surface::copyToCurrentPort() const { +	copyToCurrentPort(_bounds); +} + +void Surface::copyToCurrentPortTransparent() const { +	copyToCurrentPortTransparent(_bounds); +} + +void Surface::copyToCurrentPort(const Common::Rect &rect) const { +	copyToCurrentPort(rect, rect); +} + +void Surface::copyToCurrentPortTransparent(const Common::Rect &rect) const { +	copyToCurrentPortTransparent(rect, rect); +} + +void Surface::copyToCurrentPort(const Common::Rect &srcRect, const Common::Rect &dstRect) const { +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); +	byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); +	byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + +	int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + +	for (int y = 0; y < srcRect.height(); y++) { +		memcpy(dst, src, lineSize); +		src += _surface->pitch; +		dst += screen->pitch; +	} +} + +void Surface::copyToCurrentPortTransparent(const Common::Rect &srcRect, const Common::Rect &dstRect) const { +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); +	byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); +	byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + +	int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + +	for (int y = 0; y < srcRect.height(); y++) { +		for (int x = 0; x < srcRect.width(); x++) { +			if (g_system->getScreenFormat().bytesPerPixel == 2) { +				uint16 color = READ_UINT16(src); +				if (!isTransparent(color)) +					memcpy(dst, src, 2); +			} else if (g_system->getScreenFormat().bytesPerPixel == 4) { +				uint32 color = READ_UINT32(src); +				if (!isTransparent(color)) +					memcpy(dst, src, 4); +			} + +			src += g_system->getScreenFormat().bytesPerPixel; +			dst += g_system->getScreenFormat().bytesPerPixel; +		} + +		src += _surface->pitch - lineSize; +		dst += screen->pitch - lineSize; +	} +} + +void Surface::copyToCurrentPortMasked(const Common::Rect &srcRect, const Common::Rect &dstRect, const Surface *mask) const { +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); +	byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); +	byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + +	int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + +	for (int y = 0; y < srcRect.height(); y++) { +		byte *maskSrc = (byte *)mask->getSurface()->getBasePtr(0, y); + +		for (int x = 0; x < srcRect.width(); x++) { +			if (g_system->getScreenFormat().bytesPerPixel == 2) { +				uint16 color = READ_UINT16(maskSrc); +				if (!isTransparent(color)) +					memcpy(dst, src, 2); +			} else if (g_system->getScreenFormat().bytesPerPixel == 4) { +				uint32 color = READ_UINT32(maskSrc); +				if (!isTransparent(color)) +					memcpy(dst, src, 4); +			} + +			src += g_system->getScreenFormat().bytesPerPixel; +			maskSrc += g_system->getScreenFormat().bytesPerPixel; +			dst += g_system->getScreenFormat().bytesPerPixel; +		} + +		src += _surface->pitch - lineSize; +		dst += screen->pitch - lineSize; +	} +} + +void Surface::copyToCurrentPortTransparentGlow(const Common::Rect &srcRect, const Common::Rect &dstRect) const { +	// This is the same as copyToCurrentPortTransparent(), but turns the red value of each +	// pixel all the way up. + +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); +	byte *src = (byte *)_surface->getBasePtr(srcRect.left, srcRect.top); +	byte *dst = (byte *)screen->getBasePtr(dstRect.left, dstRect.top); + +	int lineSize = srcRect.width() * _surface->format.bytesPerPixel; + +	for (int y = 0; y < srcRect.height(); y++) { +		for (int x = 0; x < srcRect.width(); x++) { +			if (g_system->getScreenFormat().bytesPerPixel == 2) { +				uint16 color = READ_UINT16(src); +				if (!isTransparent(color)) +					WRITE_UINT16(dst, getGlowColor(color)); +			} else if (g_system->getScreenFormat().bytesPerPixel == 4) { +				uint32 color = READ_UINT32(src); +				if (!isTransparent(color)) +					WRITE_UINT32(dst, getGlowColor(color)); +			} + +			src += g_system->getScreenFormat().bytesPerPixel; +			dst += g_system->getScreenFormat().bytesPerPixel; +		} + +		src += _surface->pitch - lineSize; +		dst += screen->pitch - lineSize; +	} +} + +void Surface::scaleTransparentCopy(const Common::Rect &srcRect, const Common::Rect &dstRect) const { +	// I'm doing simple linear scaling here +	// dstRect(x, y) = srcRect(x * srcW / dstW, y * srcH / dstH); + +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + +	int srcW = srcRect.width(); +	int srcH = srcRect.height(); +	int dstW = dstRect.width(); +	int dstH = dstRect.height(); + +	for (int y = 0; y < dstH; y++) { +		for (int x = 0; x < dstW; x++) { +			if (g_system->getScreenFormat().bytesPerPixel == 2) { +				uint16 color = READ_UINT16((byte *)_surface->getBasePtr( +						x * srcW / dstW + srcRect.left, +						y * srcH / dstH + srcRect.top)); +				if (!isTransparent(color)) +					WRITE_UINT16((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), color); +			} else if (g_system->getScreenFormat().bytesPerPixel == 4) { +				uint32 color = READ_UINT32((byte *)_surface->getBasePtr( +						x * srcW / dstW + srcRect.left, +						y * srcH / dstH + srcRect.top)); +				if (!isTransparent(color)) +					WRITE_UINT32((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), color); +			} +		} +	} +} + +void Surface::scaleTransparentCopyGlow(const Common::Rect &srcRect, const Common::Rect &dstRect) const { +	// This is the same as scaleTransparentCopy(), but turns the red value of each +	// pixel all the way up. + +	Graphics::Surface *screen = ((PegasusEngine *)g_engine)->_gfx->getCurSurface(); + +	int srcW = srcRect.width(); +	int srcH = srcRect.height(); +	int dstW = dstRect.width(); +	int dstH = dstRect.height(); + +	for (int y = 0; y < dstH; y++) { +		for (int x = 0; x < dstW; x++) { +			if (g_system->getScreenFormat().bytesPerPixel == 2) { +				uint16 color = READ_UINT16((byte *)_surface->getBasePtr( +						x * srcW / dstW + srcRect.left, +						y * srcH / dstH + srcRect.top)); +				if (!isTransparent(color)) +					WRITE_UINT16((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), getGlowColor(color)); +			} else if (g_system->getScreenFormat().bytesPerPixel == 4) { +				uint32 color = READ_UINT32((byte *)_surface->getBasePtr( +						x * srcW / dstW + srcRect.left, +						y * srcH / dstH + srcRect.top)); +				if (!isTransparent(color)) +					WRITE_UINT32((byte *)screen->getBasePtr(x + dstRect.left, y + dstRect.top), getGlowColor(color)); +			} +		} +	} +} + +uint32 Surface::getGlowColor(uint32 color) const { +	// Can't just 'or' it on like the original did :P +	byte r, g, b; +	g_system->getScreenFormat().colorToRGB(color, r, g, b); +	return g_system->getScreenFormat().RGBToColor(0xff, g, b); +} + +bool Surface::isTransparent(uint32 color) const { +	// HACK: Seems we're truncating some color data somewhere... +	uint32 transColor1 = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff); +	uint32 transColor2 = g_system->getScreenFormat().RGBToColor(0xf8, 0xf8, 0xf8); + +	return color == transColor1 || color == transColor2; +} + +PixelImage::PixelImage() { +	_transparent = false; +} + +void PixelImage::drawImage(const Common::Rect &sourceBounds, const Common::Rect &destBounds) { +	if (!isSurfaceValid()) +		return; + +	// Draw from sourceBounds to destBounds based on _transparent +	if (_transparent) +		copyToCurrentPortTransparent(sourceBounds, destBounds); +	else +		copyToCurrentPort(sourceBounds, destBounds); +} + +void Frame::initFromPICTFile(const Common::String &fileName, bool transparent) { +	getImageFromPICTFile(fileName); +	_transparent = transparent; +} + +void Frame::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent) { +	getImageFromPICTResource(resFork, id); +	_transparent = transparent; +} + +void Frame::initFromMovieFrame(Video::SeekableVideoDecoder *video, TimeValue time, bool transparent) { +	getImageFromMovieFrame(video, time); +	_transparent = transparent; +} + +void Picture::draw(const Common::Rect &r) { +	Common::Rect surfaceBounds; +	getSurfaceBounds(surfaceBounds); +	Common::Rect r1 = r; + +	Common::Rect bounds; +	getBounds(bounds); +	surfaceBounds.moveTo(bounds.left, bounds.top); +	r1 = r1.findIntersectingRect(surfaceBounds); +	getSurfaceBounds(surfaceBounds); + +	Common::Rect r2 = r1; +	r2.translate(surfaceBounds.left - bounds.left, surfaceBounds.top - bounds.top); +	drawImage(r2, r1); +} + +void Picture::initFromPICTFile(const Common::String &fileName, bool transparent) { +	Frame::initFromPICTFile(fileName, transparent); + +	Common::Rect surfaceBounds; +	getSurfaceBounds(surfaceBounds); +	sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +void Picture::initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent) { +	Frame::initFromPICTResource(resFork, id, transparent); + +	Common::Rect surfaceBounds; +	getSurfaceBounds(surfaceBounds); +	sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +void Picture::initFromMovieFrame(Video::SeekableVideoDecoder *video, TimeValue time, bool transparent) { +	Frame::initFromMovieFrame(video, time, transparent); + +	Common::Rect surfaceBounds; +	getSurfaceBounds(surfaceBounds); +	sizeElement(surfaceBounds.width(), surfaceBounds.height()); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/surface.h b/engines/pegasus/surface.h new file mode 100644 index 0000000000..34a88dbd53 --- /dev/null +++ b/engines/pegasus/surface.h @@ -0,0 +1,140 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_SURFACE_H +#define PEGASUS_SURFACE_H + +#include "common/rect.h" +#include "common/str.h" + +#include "pegasus/elements.h" +#include "pegasus/types.h" + +namespace Common { +	class MacResManager; +} + +namespace Graphics { +	struct Surface; +} + +namespace Video { +	class SeekableVideoDecoder; +} + +namespace Pegasus { + +// Surface bounds are always normalized. + +class Surface { +public: +	Surface(); +	virtual ~Surface(); +	 +	virtual void allocateSurface(const Common::Rect &); +	virtual void deallocateSurface(); +	virtual void shareSurface(Surface *surface); +	bool isSurfaceValid() const { return _surface != 0; } + +	Graphics::Surface *getSurface() const { return _surface; } +	void getSurfaceBounds(Common::Rect &r) { r = _bounds; } + +	// None of the copyToCurrentPort* functions do any sanity checks. +	// It's up to clients to make sure that the Surface is valid. +	void copyToCurrentPort() const; +	void copyToCurrentPortTransparent() const; +	void copyToCurrentPort(const Common::Rect &) const; +	void copyToCurrentPortTransparent(const Common::Rect &) const; +	void copyToCurrentPort(const Common::Rect &, const Common::Rect &) const; +	void copyToCurrentPortTransparent(const Common::Rect &, const Common::Rect &) const; +	void copyToCurrentPortMasked(const Common::Rect &, const Common::Rect &, const Surface *) const; +	void copyToCurrentPortTransparentGlow(const Common::Rect &, const Common::Rect &) const; +	void scaleTransparentCopy(const Common::Rect &, const Common::Rect &) const; +	void scaleTransparentCopyGlow(const Common::Rect &, const Common::Rect &) const; + +	virtual void getImageFromPICTFile(const Common::String &fileName); +	virtual void getImageFromPICTResource(Common::MacResManager *resFork, uint16 id); +	virtual void getImageFromMovieFrame(Video::SeekableVideoDecoder *, TimeValue); + +protected: +	bool _ownsSurface; +	Graphics::Surface *_surface; +	Common::Rect _bounds; + +private: +	void getImageFromPICTStream(Common::SeekableReadStream *stream); + +	uint32 getGlowColor(uint32 color) const; +	bool isTransparent(uint32 color) const; +}; + +class PixelImage : public Surface { +public: +	PixelImage(); +	virtual ~PixelImage() {} + +	void drawImage(const Common::Rect &, const Common::Rect &); + +protected: +	virtual void setTransparent(bool transparent) { _transparent = transparent; } +	 +	bool _transparent; +}; + +class Frame : public PixelImage { +public: +	Frame() {} +	virtual ~Frame() {} + +	virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false); +	virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false); +	virtual void initFromMovieFrame(Video::SeekableVideoDecoder *, TimeValue, bool transparent = false); +}; + +class SpriteFrame : public Frame { +friend class Sprite; +public: +	SpriteFrame() { _referenceCount = 0; } +	virtual ~SpriteFrame() {} + +protected: +	uint32 _referenceCount; +}; + +class Picture : public DisplayElement, public Frame { +public: +	Picture(const DisplayElementID id) : DisplayElement(id) {} +	virtual ~Picture() {} + +	virtual void initFromPICTFile(const Common::String &fileName, bool transparent = false); +	virtual void initFromPICTResource(Common::MacResManager *resFork, uint16 id, bool transparent = false); +	virtual void initFromMovieFrame(Video::SeekableVideoDecoder *, TimeValue, bool transparent = false); + +	virtual void draw(const Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/timers.cpp b/engines/pegasus/timers.cpp new file mode 100644 index 0000000000..d03ce917ed --- /dev/null +++ b/engines/pegasus/timers.cpp @@ -0,0 +1,429 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "pegasus/pegasus.h" +#include "pegasus/notification.h" +#include "pegasus/timers.h" + +namespace Pegasus { + +Idler::Idler() { +	_isIdling = false; +	_nextIdler = 0; +	_prevIdler = 0; +} + +Idler::~Idler() { +	stopIdling(); +} + +void Idler::startIdling() { +	if (!isIdling()) { +		((PegasusEngine *)g_engine)->addIdler(this); +		_isIdling = true; +	} +} + +void Idler::stopIdling() { +	if (isIdling()) { +		((PegasusEngine *)g_engine)->removeIdler(this); +		_isIdling = false; +	} +} + +TimeBase::TimeBase(const TimeScale preferredScale) { +	_preferredScale = preferredScale; +	_callBackList = 0; +	_paused = false; +	_flags = 0; +	_lastMillis = 0; +	_time = 0; +	_rate = 0; +	_startTime = 0; +	_startScale = 1; +	_stopTime = 0xffffffff; +	_stopScale = 1; +	_master = 0; +	_pausedRate = 0; +	_pauseStart = 0; +	 +	((PegasusEngine *)g_engine)->addTimeBase(this); +} + +TimeBase::~TimeBase() { +	if (_master) +		_master->_slaves.remove(this); + +	((PegasusEngine *)g_engine)->removeTimeBase(this); +	disposeAllCallBacks(); + +	// TODO: Remove slaves? Make them remove themselves? +} + +void TimeBase::setTime(const TimeValue time, const TimeScale scale) { +	_time = Common::Rational(time, (scale == 0) ? _preferredScale : scale); +	_lastMillis = 0; +} + +TimeValue TimeBase::getTime(const TimeScale scale) { +	return _time.getNumerator() * ((scale == 0) ? _preferredScale : scale) / _time.getDenominator(); +} + +void TimeBase::setRate(const Common::Rational rate) { +	_rate = rate; + +	if (_rate == 0) +		_paused = false; +} + +Common::Rational TimeBase::getEffectiveRate() const { +	return _rate * ((_master == 0) ? 1 : _master->getEffectiveRate()); +} + +void TimeBase::start() { +	if (_paused) +		_pausedRate = 1; +	else +		setRate(1); +} + +void TimeBase::stop() { +	setRate(0); +	_paused = false; +} + +void TimeBase::pause() { +	if (isRunning() && !_paused) { +		_pausedRate = getRate(); +		stop(); +		_paused = true; +		_pauseStart = g_system->getMillis(); +	} +} + +void TimeBase::resume() { +	if (_paused) { +		setRate(_pausedRate); +		_paused = false; + +		if (isRunning()) +			_lastMillis += g_system->getMillis() - _pauseStart; +	} +} + +bool TimeBase::isRunning() { +	if (_paused && _pausedRate != 0) +		return true; + +	Common::Rational rate = getRate(); + +	if (rate == 0) +		return false; + +	if (getFlags() & kLoopTimeBase) +		return true; + +	if (rate > 0) +		return getTime() != getStop(); + +	return getTime() != getStart(); +} + +void TimeBase::setStart(const TimeValue startTime, const TimeScale scale) { +	_startTime = startTime; +	_startScale = (scale == 0) ? _preferredScale : scale; +} + +TimeValue TimeBase::getStart(const TimeScale scale) const { +	if (scale) +		return _startTime * scale / _startScale; + +	return _startTime * _preferredScale / _startScale; +} + +void TimeBase::setStop(const TimeValue stopTime, const TimeScale scale) { +	_stopTime = stopTime; +	_stopScale = (scale == 0) ? _preferredScale : scale; +} + +TimeValue TimeBase::getStop(const TimeScale scale) const { +	if (scale) +		return _stopTime * scale / _stopScale; + +	return _stopTime * _preferredScale / _stopScale; +} + +void TimeBase::setSegment(const TimeValue startTime, const TimeValue stopTime, const TimeScale scale) { +	setStart(startTime, scale); +	setStop(stopTime, scale); +} + +void TimeBase::getSegment(TimeValue &startTime, TimeValue &stopTime, const TimeScale scale) const { +	startTime = getStart(scale); +	stopTime = getStop(scale); +} + +TimeValue TimeBase::getDuration(const TimeScale scale) const { +	TimeValue startTime, stopTime; +	getSegment(startTime, stopTime, scale); +	return stopTime - startTime; +} + +void TimeBase::setMasterTimeBase(TimeBase *tb) { +	// TODO: We're just ignoring the master (except for effective rate) +	// for now to simplify things +	if (_master) +		_master->_slaves.remove(this); + +	_master = tb; + +	if (_master) +		_master->_slaves.push_back(this); +} + +void TimeBase::updateTime() { +	if (_lastMillis == 0) { +		_lastMillis = g_system->getMillis(); +	} else { +		uint32 curTime = g_system->getMillis(); +		if (_lastMillis == curTime) // No change +			return; + +		_time += Common::Rational(curTime - _lastMillis, 1000) * getEffectiveRate(); +		_lastMillis = curTime; +	} +} + +void TimeBase::checkCallBacks() { +	// Nothing to do if we're paused or not running +	if (_paused || !isRunning()) +		return; + +	Common::Rational startTime = Common::Rational(_startTime, _startScale); +	Common::Rational stopTime = Common::Rational(_stopTime, _stopScale); + +	// First step: update the times +	updateTime(); + +	// Clip time to the boundaries +	if (_time >= stopTime) +		_time = stopTime; +	else if (_time <= startTime) +		_time = startTime; + +	// TODO: Update the slaves? + +	Common::Rational time = Common::Rational(getTime(), getScale()); + +	// Check if we've triggered any callbacks +	for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = runner->_nextCallBack) { +		if (runner->_hasBeenTriggered) +			continue; + +		if (runner->_type == kCallBackAtTime && runner->_trigger == kTriggerTimeFwd) { +			if (getTime() >= (runner->_param2 * _preferredScale / runner->_param3) && getRate() > 0) { +				uint param2 = runner->_param2, param3 = runner->_param3; +				runner->callBack(); +				// HACK: Only stop future time forward callbacks if the parameters have not been changed +				// This fixes striding callbacks. Since only striding callbacks do this kind of +				// craziness, I'm not too worried about this. +				runner->_hasBeenTriggered = (runner->_param2 == param2 && runner->_param3 == param3); +			} +		} else if (runner->_type == kCallBackAtExtremes) { +			if (runner->_trigger == kTriggerAtStop) { +				if (time == stopTime) { +					runner->callBack(); +					runner->_hasBeenTriggered = true; +				} +			} else if (runner->_trigger == kTriggerAtStart) { +				if (time == startTime) { +					runner->callBack(); +					runner->_hasBeenTriggered = true; +				} +			} +		} +	} + +	if (getFlags() & kLoopTimeBase) { +		// Loop if necessary +		if (getRate() < 0 && time == startTime) +			setTime(_stopTime, _stopScale); +		else if (getRate() > 0 && time == stopTime) +			setTime(_startTime, _startScale); +	} else { +		// Stop at the end +		if ((getRate() > 0 && time == stopTime) || (getRate() < 0 && time == startTime)) +			stop(); +	} +} + +// Protected functions only called by TimeBaseCallBack. + +void TimeBase::addCallBack(TimeBaseCallBack *callBack) { +	callBack->_nextCallBack = _callBackList; +	_callBackList = callBack; +} + +void TimeBase::removeCallBack(TimeBaseCallBack *callBack) {	 +	if (_callBackList == callBack) { +		_callBackList = callBack->_nextCallBack; +	} else { +		TimeBaseCallBack *runner, *prevRunner; +	 +		for (runner = _callBackList->_nextCallBack, prevRunner = _callBackList; runner != callBack; prevRunner = runner, runner = runner->_nextCallBack) +			; + +		prevRunner->_nextCallBack = runner->_nextCallBack; +	} + +	callBack->_nextCallBack = 0; +} + +void TimeBase::disposeAllCallBacks() { +	TimeBaseCallBack *nextRunner; +	 +	for (TimeBaseCallBack *runner = _callBackList; runner != 0; runner = nextRunner) { +		nextRunner = runner->_nextCallBack; +		runner->disposeCallBack(); +		runner->_nextCallBack = 0; +	} + +	_callBackList = 0; +} + +TimeBaseCallBack::TimeBaseCallBack() { +	_timeBase = 0; +	_nextCallBack = 0; +	_trigger = kTriggerNone; +	_type = kCallBackNone; +	_hasBeenTriggered = false; +} + +TimeBaseCallBack::~TimeBaseCallBack() { +	releaseCallBack(); +} + +void TimeBaseCallBack::initCallBack(TimeBase *tb, CallBackType type) { +	releaseCallBack(); +	_timeBase = tb; +	_timeBase->addCallBack(this); +	_type = type; +} + +void TimeBaseCallBack::releaseCallBack() { +	if (_timeBase) +		_timeBase->removeCallBack(this); +	disposeCallBack(); +} + +void TimeBaseCallBack::disposeCallBack() { +	_timeBase = 0; +	_hasBeenTriggered = false; +} + +void TimeBaseCallBack::scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3) { +	// TODO: Rename param2/param3? +	_trigger = trigger; +	_param2 = param2; +	_param3 = param3; +	_hasBeenTriggered = false; +} + +void TimeBaseCallBack::cancelCallBack() { +	_trigger = kTriggerNone; +	_hasBeenTriggered = false; +} + +IdlerTimeBase::IdlerTimeBase() { +	_lastTime = 0xffffffff; +	startIdling(); +} + +void IdlerTimeBase::useIdleTime() {	 +	uint32 currentTime = getTime(); +	if (currentTime != _lastTime) { +		_lastTime = currentTime; +		timeChanged(_lastTime); +	} +} + +NotificationCallBack::NotificationCallBack() { +	_callBackFlag = 0; +	_notifier = 0; +} + +void NotificationCallBack::callBack() { +	if (_notifier) +		_notifier->setNotificationFlags(_callBackFlag, _callBackFlag); +} + +static const NotificationFlags kFuseExpiredFlag = 1; + +Fuse::Fuse() : _fuseNotification(0, (NotificationManager *)((PegasusEngine *)g_engine)) { +	_fuseNotification.notifyMe(this, kFuseExpiredFlag, kFuseExpiredFlag); +	_fuseCallBack.setNotification(&_fuseNotification); +	_fuseCallBack.initCallBack(&_fuseTimer, kCallBackAtExtremes); +	_fuseCallBack.setCallBackFlag(kFuseExpiredFlag); +} + +void Fuse::primeFuse(const TimeValue frequency, const TimeScale scale) { +	stopFuse(); +	_fuseTimer.setScale(scale); +	_fuseTimer.setSegment(0, frequency); +	_fuseTimer.setTime(0); +} + +void Fuse::lightFuse() { +	if (!_fuseTimer.isRunning()) { +		_fuseCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); +		_fuseTimer.start(); +	} +} + +void Fuse::stopFuse() { +	_fuseTimer.stop(); +	_fuseCallBack.cancelCallBack(); +	// Make sure the fuse has not triggered but not been caught yet... +	_fuseNotification.setNotificationFlags(0, 0xffffffff); +} + +void Fuse::advanceFuse(const TimeValue time) { +	if (_fuseTimer.isRunning()) { +		_fuseTimer.stop(); +		_fuseTimer.setTime(_fuseTimer.getTime() + time); +		_fuseTimer.start(); +	} +} + +TimeValue Fuse::getTimeRemaining() { +	return _fuseTimer.getStop() - _fuseTimer.getTime(); +} + +void Fuse::receiveNotification(Notification *, const NotificationFlags) { +	stopFuse(); +	invokeAction(); +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/timers.h b/engines/pegasus/timers.h new file mode 100644 index 0000000000..1dd32de3c9 --- /dev/null +++ b/engines/pegasus/timers.h @@ -0,0 +1,256 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +  +#ifndef PEGASUS_TIMERS_H +#define PEGASUS_TIMERS_H + +#include "common/list.h" +#include "common/rational.h" + +#include "pegasus/constants.h" +#include "pegasus/notification.h" +#include "pegasus/util.h" + +namespace Pegasus { + +class Idler { +friend class PegasusEngine; + +public: +	Idler(); +	virtual ~Idler(); +	 +	virtual void startIdling(); +	virtual void stopIdling(); +	bool isIdling() const { return _isIdling; } +	 +protected: +	virtual void useIdleTime() {} + +	bool _isIdling; +	Idler *_nextIdler, *_prevIdler; +}; + +enum { +	kLoopTimeBase           = 1, +	kPalindromeLoopTimeBase = 2, +	kMaintainTimeBaseZero   = 4 +}; + +class TimeBaseCallBack; + +class TimeBase { +friend class TimeBaseCallBack; +public: +	TimeBase(const TimeScale = kDefaultTimeScale); +	virtual ~TimeBase(); +	 +	virtual void setTime(const TimeValue, const TimeScale = 0); +	virtual TimeValue getTime(const TimeScale = 0); +	 +	virtual void setScale(const TimeScale scale) { _preferredScale = scale; } +	virtual TimeScale getScale() const { return _preferredScale; } +	 +	virtual void setRate(const Common::Rational); +	virtual Common::Rational getRate() const { return _rate; } +	 +	virtual void start(); +	virtual void stop(); +	virtual bool isRunning(); +	 +	virtual void pause(); +	virtual void resume(); +	bool isPaused() const { return _paused; } +	 +	virtual void setFlags(const uint32 flags) { _flags = flags; } +	virtual uint32 getFlags() const { return _flags; } +	 +	virtual void setStart(const TimeValue, const TimeScale = 0); +	virtual TimeValue getStart(const TimeScale = 0) const; +	 +	virtual void setStop(const TimeValue, const TimeScale = 0); +	virtual TimeValue getStop(const TimeScale = 0) const; +	 +	virtual void setSegment(const TimeValue, const TimeValue, const TimeScale = 0); +	virtual void getSegment(TimeValue&, TimeValue&, const TimeScale = 0) const; +	 +	virtual TimeValue getDuration(const TimeScale = 0) const; + +	virtual void setMasterTimeBase(TimeBase *timeBase); +		 +	void disposeAllCallBacks(); + +	// ScummVM's API additions (to replace the need for actual timers) +	virtual void checkCallBacks(); + +protected: +	void addCallBack(TimeBaseCallBack *); +	void removeCallBack(TimeBaseCallBack *); +	virtual void updateTime(); + +	TimeBase *_master; +	TimeScale _preferredScale; +	TimeBaseCallBack *_callBackList; +	Common::Rational _rate, _pausedRate; +	bool _paused; +	uint32 _startTime, _startScale; +	uint32 _stopTime, _stopScale; +	uint32 _flags; + +	Common::Rational _time; +	uint32 _lastMillis, _pauseStart; + +private: +	Common::Rational getEffectiveRate() const; + +	Common::List<TimeBase *> _slaves; +}; + +// Type passed to initCallBack() +enum CallBackType { +	kCallBackNone = 0, +	kCallBackAtTime = 1, +	kCallBackAtExtremes = 4 +}; + +// Trigger passed to scheduleCallBack() +enum CallBackTrigger { +	kTriggerNone = 0, + +	// AtTime flags +	kTriggerTimeFwd = 1, + +	// AtExtremes flags +	kTriggerAtStart = 1, +	kTriggerAtStop = 2 +}; + +class TimeBaseCallBack { +friend class TimeBase; + +public: +	TimeBaseCallBack(); +	virtual ~TimeBaseCallBack(); + +	void initCallBack(TimeBase *, CallBackType type); + +	void releaseCallBack(); + +	void scheduleCallBack(CallBackTrigger trigger, uint32 param2, uint32 param3); +	void cancelCallBack(); + +protected: +	virtual void callBack() = 0; + +	TimeBase *_timeBase; +	 +	// Owned and operated by TimeBase; +	TimeBaseCallBack *_nextCallBack; + +	// Our storage of the QuickTime timer crap +	CallBackType _type; +	CallBackTrigger _trigger; +	uint32 _param2, _param3; +	bool _hasBeenTriggered; + +private: +	void disposeCallBack(); +}; + +class IdlerTimeBase : public Idler, public TimeBase { +public: +	IdlerTimeBase(); +	virtual ~IdlerTimeBase() { stopIdling(); } + +	TimeValue getLastTime() const { return _lastTime; } + +protected: +	virtual void useIdleTime(); +	virtual void timeChanged(const TimeValue) {} +	 +	TimeValue _lastTime; + +}; + +class NotificationCallBack : public TimeBaseCallBack { +public: +	NotificationCallBack(); +	virtual ~NotificationCallBack() {} +	 +	void setNotification(Notification *notifier) { _notifier = notifier; } + +	void setCallBackFlag(const NotificationFlags flag) { _callBackFlag = flag; } +	NotificationFlags getCallBackFlag() const { return _callBackFlag; } +	 +protected: +	void callBack(); +	 +	Notification *_notifier; +	NotificationFlags _callBackFlag; +}; + +class DynamicElement : public TimeBase { +public: +	TimeValue percentSeconds(const uint32 percent) { return getScale() * percent / 100; } +}; + +class Fuse : private NotificationReceiver { +public: +	Fuse(); +	virtual ~Fuse() {} + +	void primeFuse(const TimeValue, const TimeScale = 1); // An appropriately named function :P +	void lightFuse(); +	void stopFuse(); +	bool isFuseLit() { return _fuseTimer.isRunning() || _fuseTimer.isPaused(); } +	void advanceFuse(const TimeValue); +	TimeValue getTimeRemaining(); +	TimeScale getFuseScale() { return _fuseTimer.getScale(); } + +	void pauseFuse() { _fuseTimer.pause(); } +	void resumeFuse() { _fuseTimer.resume(); } +	bool isFusePaused() { return _fuseTimer.isPaused(); } + +protected: +	virtual void receiveNotification(Notification *, const NotificationFlags); +	virtual void invokeAction() {} + +	TimeBase _fuseTimer; +	NotificationCallBack _fuseCallBack; +	Notification _fuseNotification; +}; + +class FuseFunction : public Fuse, public FunctionPtr { +public: +	FuseFunction() {} +	virtual ~FuseFunction() {} + +protected: +	virtual void invokeAction() { callFunction(); } +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/transition.cpp b/engines/pegasus/transition.cpp new file mode 100644 index 0000000000..24e9aaf4a2 --- /dev/null +++ b/engines/pegasus/transition.cpp @@ -0,0 +1,150 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/system.h" + +#include "pegasus/transition.h" + +namespace Pegasus { + +ScreenFader::ScreenFader() { +	_fadeTarget = getBlack(); +	// Initially, assume screens are on at full brightness. +	Fader::setFaderValue(100); +} + +void ScreenFader::doFadeOutSync(const TimeValue duration, const TimeValue scale, const uint32 fadeTarget) { +	_fadeTarget = fadeTarget; + +	FaderMoveSpec spec; +	spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 0); +	startFaderSync(spec); +} + +void ScreenFader::doFadeInSync(const TimeValue duration, const TimeValue scale, const uint32 fadeTarget) { +	_fadeTarget = fadeTarget; + +	FaderMoveSpec spec; +	spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 100); +	startFaderSync(spec); +} + +void ScreenFader::setFaderValue(const int32 value) { +	if (value != getFaderValue()) { +		Fader::setFaderValue(value); + +		// TODO: Gamma fading +	} +} + +uint32 ScreenFader::getBlack() { +	return g_system->getScreenFormat().RGBToColor(0, 0, 0); +} + +Transition::Transition(const DisplayElementID id) : FaderAnimation(id) { +	_outPicture = 0; +	_inPicture = 0; +} + +void Transition::setBounds(const Common::Rect &r) { +	FaderAnimation::setBounds(r); +	_boundsWidth = _bounds.width(); +	_boundsHeight = _bounds.height(); +} + +void Transition::setInAndOutElements(DisplayElement *inElement, DisplayElement *outElement) { +	_inPicture = inElement; +	_outPicture = outElement; + +	Common::Rect r; + +	if (_outPicture) +		_outPicture->getBounds(r); +	else if (_inPicture) +		_inPicture->getBounds(r); + +	setBounds(r); +} + +void Slide::draw(const Common::Rect &r) { +	Common::Rect oldBounds, newBounds; +	 +	adjustSlideRects(oldBounds, newBounds); +	drawElements(r, oldBounds, newBounds); +} + +void Slide::adjustSlideRects(Common::Rect &oldBounds, Common::Rect &newBounds) { +	oldBounds = _bounds; +	newBounds = _bounds; +} + +void Slide::drawElements(const Common::Rect &drawRect, const Common::Rect &oldBounds, const Common::Rect &newBounds) { +	drawSlideElement(drawRect, oldBounds, _outPicture); +	drawSlideElement(drawRect, newBounds, _inPicture); +} + +void Slide::drawSlideElement(const Common::Rect &drawRect, const Common::Rect &oldBounds, DisplayElement *picture) { +	if (picture && drawRect.intersects(oldBounds)) { +		picture->moveElementTo(oldBounds.left, oldBounds.top); +		picture->draw(drawRect.findIntersectingRect(oldBounds)); +	} +} + +void Push::adjustSlideRects(Common::Rect &oldBounds, Common::Rect &newBounds) { +	switch (_direction & kSlideHorizMask) { +		case kSlideLeftMask: +			newBounds.left = oldBounds.right = _bounds.right - pegasusRound(getFaderValue() * _boundsWidth, kTransitionRange); +			newBounds.right = newBounds.left + _boundsWidth; +			oldBounds.left = oldBounds.right - _boundsWidth; +			break; +		case kSlideRightMask: +			oldBounds.left = newBounds.right = _bounds.left + pegasusRound(getFaderValue() * _boundsWidth, kTransitionRange); +			oldBounds.right = oldBounds.left + _boundsWidth; +			newBounds.left = newBounds.right - _boundsWidth; +			break; +		default: +			newBounds.left = oldBounds.left = _bounds.left; +			newBounds.right = oldBounds.right = _bounds.right; +			break; +	} +	switch (_direction & kSlideVertMask) { +		case kSlideDownMask: +			oldBounds.top = newBounds.bottom = _bounds.top + pegasusRound(getFaderValue() * _boundsHeight, kTransitionRange); +			oldBounds.bottom = oldBounds.top + _boundsHeight; +			newBounds.top = newBounds.bottom - _boundsHeight; +			break; +		case kSlideUpMask: +			newBounds.top = oldBounds.bottom = _bounds.bottom - pegasusRound(getFaderValue() * _boundsHeight, kTransitionRange); +			newBounds.bottom = newBounds.top + _boundsHeight; +			oldBounds.top = oldBounds.bottom - _boundsHeight; +			break; +		default: +			newBounds.top = oldBounds.top = _bounds.top; +			newBounds.bottom = oldBounds.bottom = _bounds.bottom; +			break; +	} +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/transition.h b/engines/pegasus/transition.h new file mode 100644 index 0000000000..5f2b797d88 --- /dev/null +++ b/engines/pegasus/transition.h @@ -0,0 +1,105 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_TRANSITION_H +#define PEGASUS_TRANSITION_H + +#include "pegasus/fader.h" + +namespace Pegasus { + +class ScreenFader : public Fader { +public: +	ScreenFader(); +	virtual ~ScreenFader() {} +	 +	void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, const uint32 = getBlack()); +	void doFadeInSync(const TimeValue = kHalfSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, const uint32 = getBlack()); + +	void setFaderValue(const int32);	 + +protected: +	uint32 _fadeTarget; + +private: +	static uint32 getBlack(); +}; + +// Transitions are faders that range over [0,1000], which makes their +// "resolution" one tenth of a percent + +static const int kTransitionBottom = 0; +static const int kTransitionTop = 1000; + +static const int kTransitionRange = kTransitionTop - kTransitionBottom; + +class Transition : public FaderAnimation { +public: +	Transition(const DisplayElementID id); +	virtual ~Transition() {} +	 +	virtual void setBounds(const Common::Rect &); +	 +	virtual void setInAndOutElements(DisplayElement *, DisplayElement *); +	DisplayElement *getInElement() { return _inPicture; } +	DisplayElement *getOutElement() { return _outPicture; } + +protected: +	DisplayElement *_outPicture; +	DisplayElement *_inPicture; + +	CoordType _boundsWidth, _boundsHeight; +}; + +class Slide : public Transition { +public: +	Slide(const DisplayElementID id) : Transition(id) {} +	virtual ~Slide() {} + +	virtual void setSlideDirection(SlideDirection dir) { _direction = dir; } +	virtual void draw(const Common::Rect &); + +	virtual void setDirection(const SlideDirection dir) { _direction = dir; } + +protected: +	virtual void adjustSlideRects(Common::Rect &, Common::Rect &); +	virtual void drawElements(const Common::Rect &, const Common::Rect &, const Common::Rect &); +	virtual void drawSlideElement(const Common::Rect &, const Common::Rect &, DisplayElement *); + +	SlideDirection _direction; +}; + +class Push : public Slide { +public: +	Push(const DisplayElementID id) : Slide(id) {} +	virtual ~Push() {} + +protected: +	virtual void adjustSlideRects(Common::Rect &, Common::Rect &); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/types.h b/engines/pegasus/types.h new file mode 100644 index 0000000000..73a4e98491 --- /dev/null +++ b/engines/pegasus/types.h @@ -0,0 +1,161 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +  +#ifndef PEGASUS_TYPES_H +#define PEGASUS_TYPES_H + +#include "common/scummsys.h" + +namespace Pegasus { + +// TODO: Probably all of these don't really need to be typedef'd... + +typedef int32 DisplayElementID; +typedef int32 DisplayOrder; + +typedef int16 HotSpotID; +typedef uint32 HotSpotFlags; + +typedef byte ButtonState; +typedef uint32 InputBits; + +typedef int32 NotificationID; +typedef uint32 NotificationFlags; + +// Mac types. +typedef int16 ResIDType; +typedef int16 CoordType; + +enum SlideDirection { +	kSlideLeftMask = 1, +	kSlideRightMask = kSlideLeftMask << 1, +	kSlideUpMask = kSlideRightMask << 1 << 1, +	kSlideDownMask = kSlideUpMask << 1, +	 +	kSlideHorizMask = kSlideLeftMask | kSlideRightMask, +	kSlideVertMask = kSlideUpMask | kSlideDownMask, + +	kSlideUpLeftMask = kSlideLeftMask | kSlideUpMask, +	kSlideUpRightMask = kSlideRightMask | kSlideUpMask, +	kSlideDownLeftMask = kSlideLeftMask | kSlideDownMask, +	kSlideDownRightMask = kSlideRightMask | kSlideDownMask +}; + +// ScummVM QuickTime/QuickDraw replacement types +typedef uint TimeValue; +typedef uint TimeScale; + +typedef int16 GameID; + +typedef GameID ItemID; +typedef GameID ActorID; +typedef GameID RoomID; +typedef GameID NeighborhoodID; +typedef byte AlternateID; +typedef int8 HotSpotActivationID; + +typedef int16 WeightType; + +typedef byte DirectionConstant; +typedef byte TurnDirection; + +// Meant to be room in low 16 bits and direction in high 16 bits. +typedef uint32 RoomViewID; + +#define MakeRoomView(room, direction) (((RoomViewID) (room)) | (((RoomViewID) (direction)) << 16)) + +typedef uint32 ExtraID; + +typedef int16 GameMode; + +typedef int16 WeightType; + +typedef int16 ItemState; + +typedef int8 DeathReason; + +typedef int32 GameMenuCommand; + +typedef int32 GameScoreType; + +typedef long CanMoveForwardReason; + +typedef long CanTurnReason; + +typedef long CanOpenDoorReason; + +enum InventoryResult { +	kInventoryOK, +	kTooMuchWeight, +	kItemNotInInventory +}; + +typedef int32 InteractionID; + +typedef int32 AIConditionID; + +enum EnergyStage { +	kStageNoStage, +	kStageCasual,       // more than 50% energy +	kStageWorried,      // more than 25% energy +	kStageNervous,      // more than 5% energy +	kStagePanicStricken // less than 5% energy +}; + +enum NoradSubPrepState { +	kSubNotPrepped, +	kSubPrepped, +	kSubDamaged +}; + +enum LowerClientSignature { +	kNoClientSignature, +	kInventorySignature, +	kBiochipSignature, +	kAISignature +}; + +enum LowerAreaSignature { +	kLeftAreaSignature, +	kMiddleAreaSignature, +	kRightAreaSignature +}; + +enum AirQuality { +	kAirQualityGood, +	kAirQualityDirty, +	kAirQualityVacuum +}; + +enum DragType { +	kDragNoDrag, +	kDragInventoryPickup, +	kDragBiochipPickup, +	kDragInventoryUse +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/util.cpp b/engines/pegasus/util.cpp new file mode 100644 index 0000000000..c38054d051 --- /dev/null +++ b/engines/pegasus/util.cpp @@ -0,0 +1,95 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/random.h" +#include "common/system.h" +#include "common/util.h" + +#include "pegasus/util.h" + +namespace Pegasus { + +IDObject::IDObject(const int32 id) { +	_objectID = id; +} + +IDObject::~IDObject() { +} + +int32 IDObject::getObjectID() const { +	return _objectID; +} + +int operator==(const IDObject &arg1, const IDObject &arg2) { +	return arg1.getObjectID() == arg2.getObjectID(); +} + +int operator!=(const IDObject &arg1, const IDObject &arg2) { +	return arg1.getObjectID() != arg2.getObjectID(); +} + +FunctionPtr::FunctionPtr() { +	_function = 0; +	_functionArg = 0; +} + +FunctionPtr::~FunctionPtr() { +} + +void FunctionPtr::setFunctionPtr(tFunctionPtr function, void *functionArg) { +	_function = function; +	_functionArg = functionArg; +} + +void FunctionPtr::callFunction() { +	if (_function != 0) +		(*_function)(this, _functionArg); +} + +inline int32 pegasusRound(const int32 a, const int32 b) { +	if (b < 0) +		if (a < 0) +			return -((a - (-b >> 1)) / -b); +		else +			return -((a + (-b >> 1)) / -b); +	else +		if (a < 0) +			return (a - (b >> 1)) / b; +		else +			return (a + (b >> 1)) / b; +} + +int32 linearInterp(const int32 start1, const int32 stop1, const int32 current1, const int32 start2, const int32 stop2) { +	if (start2 == stop2) +		return start2; +	else +		return start2 + pegasusRound((current1 - start1) * (stop2 - start2), (stop1 - start1)); +} + +uint32 tickCount() { +	return g_system->getMillis() * 60 / 1000; +} + +} // End of namespace Pegasus diff --git a/engines/pegasus/util.h b/engines/pegasus/util.h new file mode 100644 index 0000000000..57839fca7b --- /dev/null +++ b/engines/pegasus/util.h @@ -0,0 +1,135 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * Additional copyright for this file: + * Copyright (C) 1995-1997 Presto Studios, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef PEGASUS_UTIL_H +#define PEGASUS_UTIL_H + +#include "common/stream.h" + +#include "pegasus/types.h" + +namespace Common { +	class RandomSource; +} + +namespace Pegasus { + +class IDObject { +public: +	IDObject(const int32 id); +	~IDObject(); +	 +	int32 getObjectID() const; + +private: +	int32 _objectID; +}; + +class FunctionPtr; + +typedef void (*tFunctionPtr)(FunctionPtr *theFunction, void *functionArg); + +class FunctionPtr { +public: +	FunctionPtr(); +	virtual ~FunctionPtr(); +	 +	void setFunctionPtr(tFunctionPtr function, void *functionArg); + +protected: +	void callFunction(); +	 +	tFunctionPtr _function; +	void *_functionArg; +}; + +#define NUM_FLAGS (sizeof(Unit) * 8) +#define BIT_INDEX_SHIFT (sizeof(Unit) + 2 - (sizeof(Unit)) / 3) +#define BIT_INDEX_MASK (NUM_FLAGS - 1) + +template <typename Unit, uint32 kNumFlags> +class FlagsArray { +public: +	FlagsArray() { clearAllFlags(); } +	void clearAllFlags() { memset(_flags, 0, sizeof(_flags)); } +	void setAllFlags() { memset(_flags, ~((Unit)0), sizeof(_flags)); } +	void setFlag(uint32 flag) { _flags[flag >> BIT_INDEX_SHIFT] |= 1 << (flag & BIT_INDEX_MASK); } +	void clearFlag(uint32 flag) { _flags[flag >> BIT_INDEX_SHIFT] &= ~(1 << (flag & BIT_INDEX_MASK)); } +	void setFlag(uint32 flag, bool val) { if (val) setFlag(flag); else clearFlag(flag); } +	bool getFlag(uint32 flag) { return (_flags[flag >> BIT_INDEX_SHIFT] & (1 << (flag & BIT_INDEX_MASK))) != 0; } +	bool anyFlagSet() { +		for (uint32 i = 0; i < sizeof(_flags); i++) +			if (_flags[i] != 0) +				return true; +		return false; +	} + +	void readFromStream(Common::ReadStream *stream) { +		// Shortcut +		if (sizeof(Unit) == 1) { +			stream->read(_flags, sizeof(_flags)); +			return; +		} + +		for (uint32 i = 0; i < ARRAYSIZE(_flags); i++) { +			if (sizeof(Unit) == 2) +				_flags[i] = stream->readUint16BE(); +			else /* if (sizeof(Unit) == 4) */ +				_flags[i] = stream->readUint32BE(); +		} +	} + +	void writeToStream(Common::WriteStream *stream) { +		// Shortcut +		if (sizeof(Unit) == 1) { +			stream->write(_flags, sizeof(_flags)); +			return; +		} + +		for (uint32 i = 0; i < ARRAYSIZE(_flags); i++) { +			if (sizeof(Unit) == 2) +				stream->writeUint16BE(_flags[i]); +			else /* if (sizeof(Unit) == 4) */ +				stream->writeUint32BE(_flags[i]); +		} +	} + +private: +	Unit _flags[(kNumFlags - 1) / NUM_FLAGS + 1]; +}; + +#undef NUM_FLAGS +#undef BIT_INDEX_SHIFT +#undef BIT_INDEX_MASK + +int32 linearInterp(const int32 start1, const int32 stop1, const int32 current1, const int32 start2, const int32 stop2); + +int32 pegasusRound(const int32 a, const int32 b); + +uint32 tickCount(); + +} // End of namespace Pegasus + +#endif diff --git a/engines/plugins_table.h b/engines/plugins_table.h index fac956755e..bfd4371ef7 100644 --- a/engines/plugins_table.h +++ b/engines/plugins_table.h @@ -56,6 +56,9 @@ LINK_PLUGIN(MOHAWK)  #if PLUGIN_ENABLED_STATIC(PARALLACTION)  LINK_PLUGIN(PARALLACTION)  #endif +#if PLUGIN_ENABLED_STATIC(PEGASUS) +LINK_PLUGIN(PEGASUS) +#endif  #if PLUGIN_ENABLED_STATIC(QUEEN)  LINK_PLUGIN(QUEEN)  #endif diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp index 47b4b8ad33..798f703db6 100644 --- a/engines/scumm/he/wiz_he.cpp +++ b/engines/scumm/he/wiz_he.cpp @@ -2286,8 +2286,7 @@ void Wiz::fillWizLine(const WizParameters *params) {  			lineP.depth = bitDepth;  			if (params->processFlags & kWPFParams) { -				assert (params->params2 == 1); // Catch untested usage -				Graphics::drawThickLine(x1, y1, x2, y2, params->params1, color, drawProc, &lineP); +				Graphics::drawThickLine(x1, y1, x2, y2, params->params1, params->params2, color, drawProc, &lineP);  			} else {  				Graphics::drawLine(x1, y1, x2, y2, color, drawProc, &lineP);  			} diff --git a/graphics/primitives.cpp b/graphics/primitives.cpp index 9834af65ba..b88db39f36 100644 --- a/graphics/primitives.cpp +++ b/graphics/primitives.cpp @@ -61,59 +61,21 @@ void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, i  	}  } +void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data) { +	assert(penX > 0 && penY > 0); -// FIXME: This is a limited version of thick line drawing -// it draws striped lines at some angles. Better algorithm could -// be found here: -// -//   http://homepages.enterprise.net/murphy/thickline/index.html -// -// Feel free to replace it with better implementation -void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data) { -	const bool steep = ABS(y1 - y0) > ABS(x1 - x0); - -	if (steep) { -		SWAP(x0, y0); -		SWAP(x1, y1); -	} - -	float dx = x1 - x0; -	float dy = y1 - y0; -	float d = (float)sqrt(dx * dx + dy * dy); - -	if (!d) +	// Shortcut +	if (penX == 1 && penY == 1) { +		drawLine(x0, y0, x1, y1, color, plotProc, data);  		return; - -	int thickX = (int)((float)thickness * dy / d / 2); -	int thickY = (int)((float)thickness * dx / d / 2); - -	const int delta_x = ABS(x1 - x0); -	const int delta_y = ABS(y1 - y0); -	const int delta_err = delta_y; -	int x = x0; -	int y = y0; -	int err = 0; - -	const int x_step = (x0 < x1) ? 1 : -1; -	const int y_step = (y0 < y1) ? 1 : -1; - -	if (steep) -		drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data); -	else -		drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data); - -	while (x != x1) { -		x += x_step; -		err += delta_err; -		if (2 * err > delta_x) { -			y += y_step; -			err -= delta_x; -		} -		if (steep) -			drawLine(y - thickY, x + thickX, y + thickY, x - thickX, color, plotProc, data); -		else -			drawLine(x - thickX, y + thickY, x + thickX, y - thickY, color, plotProc, data);  	} + +	// TODO: Optimize this. It currently is a very naive way of handling +	// thick lines since quite often it will be drawing to the same pixel +	// multiple times. +	for (int x = 0; x < penX; x++) +		for (int y = 0; y < penY; y++) +			drawLine(x0 + x, y0 + y, x1 + x, y1 + y, color, plotProc, data);  }  }	// End of namespace Graphics diff --git a/graphics/primitives.h b/graphics/primitives.h index 0ab2dabcd8..f0780afc2e 100644 --- a/graphics/primitives.h +++ b/graphics/primitives.h @@ -25,7 +25,7 @@  namespace Graphics {  void drawLine(int x0, int y0, int x1, int y1, int color, void (*plotProc)(int, int, int, void *), void *data); -void drawThickLine(int x0, int y0, int x1, int y1, int thickness, int color, void (*plotProc)(int, int, int, void *), void *data); +void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, int color, void (*plotProc)(int, int, int, void *), void *data);  }	// End of namespace Graphics diff --git a/graphics/surface.cpp b/graphics/surface.cpp index c0f1046eae..a37dd57e61 100644 --- a/graphics/surface.cpp +++ b/graphics/surface.cpp @@ -49,6 +49,17 @@ void Surface::drawLine(int x0, int y0, int x1, int y1, uint32 color) {  		error("Surface::drawLine: bytesPerPixel must be 1, 2, or 4");  } +void Surface::drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color) { +	if (format.bytesPerPixel == 1) +		Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<byte>, this); +	else if (format.bytesPerPixel == 2) +		Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint16>, this); +	else if (format.bytesPerPixel == 4) +		Graphics::drawThickLine(x0, y0, x1, y1, penX, penY, color, plotPoint<uint32>, this); +	else +		error("Surface::drawThickLine: bytesPerPixel must be 1, 2, or 4"); +} +  void Surface::create(uint16 width, uint16 height, const PixelFormat &f) {  	free(); diff --git a/graphics/surface.h b/graphics/surface.h index eb8d1ac42e..9c8c040cbf 100644 --- a/graphics/surface.h +++ b/graphics/surface.h @@ -153,10 +153,26 @@ struct Surface {  	 * @param x1 The x coordinate of the end point.  	 * @param y1 The y coordinate of the end point.  	 * @param color The color of the line. +	 * @note This is just a wrapper around Graphics::drawLine  	 */  	void drawLine(int x0, int y0, int x1, int y1, uint32 color);  	/** +	 * Draw a thick line. +	 * +	 * @param x0 The x coordinate of the start point. +	 * @param y0 The y coordiante of the start point. +	 * @param x1 The x coordinate of the end point. +	 * @param y1 The y coordinate of the end point. +	 * @param penX The width of the pen (thickness in the x direction) +	 * @param penY The height of the pen (thickness in the y direction) +	 * @param color The color of the line. +	 * @note This is just a wrapper around Graphics::drawThickLine +	 * @note The x/y coordinates of the start and end points are the upper-left most part of the pen +	 */ +	void drawThickLine(int x0, int y0, int x1, int y1, int penX, int penY, uint32 color); + +	/**  	 * Draw a horizontal line.  	 *  	 * @param x The start x coordinate of the line. diff --git a/gui/credits.h b/gui/credits.h index ecfe280d20..928799efd7 100644 --- a/gui/credits.h +++ b/gui/credits.h @@ -157,6 +157,9 @@ static const char *credits[] = {  "C1""Parallaction",  "C0""peres",  "", +"C1""Pegasus", +"C0""Matthew Hoops", +"",  "C1""Queen",  "C0""David Eriksson",  "C2""(retired)", @@ -710,5 +713,7 @@ static const char *credits[] = {  "C0""",  "C0""Broken Sword 2.5 team for providing sources of their engine and their great support.",  "C0""", +"C0""Bob Bell, Michel Kripalani, Tommy Yune, from Presto Studios for providing the source code of The Journeyman Project: Pegasus Prime.", +"C0""",  "",  };  | 
