From 59f7e1deeaa15c87adbe073105ea512d1972cde0 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sat, 24 Sep 2011 14:10:54 -0400 Subject: PEGASUS: Import AI code and relevant items --- engines/pegasus/ai/ai_action.cpp | 78 +++ engines/pegasus/ai/ai_action.h | 136 +++++ engines/pegasus/ai/ai_area.cpp | 589 ++++++++++++++++++++++ engines/pegasus/ai/ai_area.h | 164 ++++++ engines/pegasus/ai/ai_condition.cpp | 293 +++++++++++ engines/pegasus/ai/ai_condition.h | 287 +++++++++++ engines/pegasus/ai/ai_rule.cpp | 78 +++ engines/pegasus/ai/ai_rule.h | 86 ++++ engines/pegasus/constants.h | 49 ++ engines/pegasus/gamestate.h | 57 +-- engines/pegasus/input.h | 4 + engines/pegasus/items/biochips/aichip.cpp | 279 ++++++++++ engines/pegasus/items/biochips/aichip.h | 69 +++ engines/pegasus/items/biochips/biochipitem.cpp | 7 +- engines/pegasus/items/biochips/opticalchip.cpp | 189 +++++++ engines/pegasus/items/biochips/opticalchip.h | 71 +++ engines/pegasus/items/biochips/pegasuschip.cpp | 178 +++++++ engines/pegasus/items/biochips/pegasuschip.h | 55 ++ engines/pegasus/items/inventory/airmask.cpp | 248 +++++++++ engines/pegasus/items/inventory/airmask.h | 76 +++ engines/pegasus/items/inventory/inventoryitem.cpp | 7 +- engines/pegasus/items/item.cpp | 46 +- engines/pegasus/items/item.h | 6 +- engines/pegasus/items/itemlist.cpp | 14 +- engines/pegasus/items/itemlist.h | 4 +- engines/pegasus/module.mk | 8 + engines/pegasus/neighborhood/neighborhood.cpp | 46 ++ engines/pegasus/neighborhood/neighborhood.h | 16 + engines/pegasus/pegasus.cpp | 113 ++++- engines/pegasus/pegasus.h | 51 +- engines/pegasus/timers.h | 4 +- engines/pegasus/util.h | 65 ++- 32 files changed, 3271 insertions(+), 102 deletions(-) create mode 100755 engines/pegasus/ai/ai_action.cpp create mode 100755 engines/pegasus/ai/ai_action.h create mode 100755 engines/pegasus/ai/ai_area.cpp create mode 100755 engines/pegasus/ai/ai_area.h create mode 100755 engines/pegasus/ai/ai_condition.cpp create mode 100755 engines/pegasus/ai/ai_condition.h create mode 100755 engines/pegasus/ai/ai_rule.cpp create mode 100755 engines/pegasus/ai/ai_rule.h create mode 100755 engines/pegasus/items/biochips/aichip.cpp create mode 100755 engines/pegasus/items/biochips/aichip.h create mode 100755 engines/pegasus/items/biochips/opticalchip.cpp create mode 100755 engines/pegasus/items/biochips/opticalchip.h create mode 100755 engines/pegasus/items/biochips/pegasuschip.cpp create mode 100755 engines/pegasus/items/biochips/pegasuschip.h create mode 100755 engines/pegasus/items/inventory/airmask.cpp create mode 100755 engines/pegasus/items/inventory/airmask.h (limited to 'engines/pegasus') diff --git a/engines/pegasus/ai/ai_action.cpp b/engines/pegasus/ai/ai_action.cpp new file mode 100755 index 0000000000..36367eb1d6 --- /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 tInputBits 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 100755 index 0000000000..09a1d13699 --- /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 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 tInputBits = kWarningInterruption); + + virtual void performAIAction(AIRule *); + +protected: + Common::String _movieName; + tInputBits _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 100755 index 0000000000..0b7e435043 --- /dev/null +++ b/engines/pegasus/ai/ai_area.cpp @@ -0,0 +1,589 @@ +/* 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/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() { + // TODO +} + +void AIArea::restoreAIState() { + // TODO +} + +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 tLowerClientSignature client, const tLowerAreaSignature 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 tLowerClientSignature, const tLowerAreaSignature 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->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->refreshDisplay(); + g_system->delayMillis(10); + } + + _rightAreaMovie.stop(); + vm->_cursor->hideUntilMoved(); + setAIAreaToTime(_rightAreaOwner, kRightAreaSignature, _rightBiochipTime); + break; + } + + unlockAI(); +} + +bool AIArea::playAIMovie(const tLowerAreaSignature area, const Common::String &movieName, bool keepLastFrame, const tInputBits interruptFilter) { + PegasusEngine *vm = (PegasusEngine *)g_engine; + + lockAIOut(); + + InputHandler::getCurrentInputDevice()->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); + _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; + InputHandler::getCurrentInputDevice()->getInput(input, interruptFilter); + + if (input.anyInput() || vm->shouldQuit()) { + result = false; + break; + } + + 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 tLowerClientSignature owner, const tLowerAreaSignature 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 tLowerClientSignature 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; + } +} + +tLowerClientSignature 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 100755 index 0000000000..0338f2b96c --- /dev/null +++ b/engines/pegasus/ai/ai_area.h @@ -0,0 +1,164 @@ +/* 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" + +/* + + 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 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 tLowerClientSignature, const tLowerAreaSignature, const TimeValue); + + // The "Play" functions play the requested sequence synchronously. + void playAIAreaSequence(const tLowerClientSignature, const tLowerAreaSignature, 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 tLowerAreaSignature, const Common::String &movieName, bool keepLastFrame, const tInputBits); + + // Loop the requested sequence indefinitely. + void loopAIAreaSequence(const tLowerClientSignature, const tLowerAreaSignature, const TimeValue, const TimeValue); + + void addAIRule(AIRule *); + + // Remove and delete all rules. + void removeAllRules(); + + void lockAIOut(); + void unlockAI(); + void forceAIUnlocked(); + + void checkMiddleArea(); + void checkRules(); + + tLowerClientSignature getMiddleAreaOwner(); + void toggleMiddleAreaOwner(); + + TimeValue getBigInfoTime(); + void getSmallInfoSegment(TimeValue &, TimeValue &); + +protected: + void useIdleTime(); + + void setLeftMovieTime(const TimeValue); + void setMiddleMovieTime(const tLowerClientSignature, const TimeValue); + void setRightMovieTime(const TimeValue); + + Movie _leftAreaMovie; + Movie _middleAreaMovie; + Movie _rightAreaMovie; + Movie _AIMovie; + + tLowerClientSignature _leftAreaOwner; + tLowerClientSignature _middleAreaOwner; + tLowerClientSignature _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 100755 index 0000000000..dbb66ddf0f --- /dev/null +++ b/engines/pegasus/ai/ai_condition.cpp @@ -0,0 +1,293 @@ +/* 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/ai/ai_condition.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 tRoomViewID[maxLocations]; +} + +AILocationCondition::~AILocationCondition() { + delete[] _locations; +} + +void AILocationCondition::addLocation(const tRoomViewID location) { + if (_numLocations < _maxLocations) + _locations[_numLocations++] = location; +} + +bool AILocationCondition::fireCondition() { + tRoomViewID 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; + tRoomViewID *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 tRoomViewID[maxLocations]; + _maxLocations = maxLocations; + } + + _numLocations = stream->readUint32BE(); + + uint32 i; + tRoomViewID *p; + for (i = 0, p = _locations; i < _numLocations; i++, p++) + *p = stream->readUint32BE(); +} + +AIDoorOpenedCondition::AIDoorOpenedCondition(tRoomViewID doorLocation) { + _doorLocation = doorLocation; +} + +bool AIDoorOpenedCondition::fireCondition() { + return GameState.getCurrentRoomAndView() == _doorLocation && GameState.isCurrentDoorOpen(); +} + +AIHasItemCondition::AIHasItemCondition(const tItemID item) { + _item = item; +} + +bool AIHasItemCondition::fireCondition() { + return _item == kNoItemID || GameState.isTakenItemID(_item); +} + +AIDoesntHaveItemCondition::AIDoesntHaveItemCondition(const tItemID item) { + _item = item; +} + +bool AIDoesntHaveItemCondition::fireCondition() { + return _item == kNoItemID || !GameState.isTakenItemID(_item); +} + +AICurrentItemCondition::AICurrentItemCondition(const tItemID item) { + _item = item; +} + +bool AICurrentItemCondition::fireCondition() { + // TODO + //InventoryItem *item = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); + InventoryItem *item = 0; + + if (_item == kNoItemID) + return item == 0; + + return item != 0 && item->getObjectID() == _item; +} + +AICurrentBiochipCondition::AICurrentBiochipCondition(const tItemID biochip) { + _biochip = biochip; +} + +bool AICurrentBiochipCondition::fireCondition() { + // TODO + ///BiochipItem *biochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); + BiochipItem *biochip = 0; + + if (_biochip == kNoItemID) + return biochip == 0; + + return biochip != 0 && biochip->getObjectID() == _biochip; +} + +AIItemStateCondition::AIItemStateCondition(const tItemID item, const tItemState 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 tExtraID lastExtra) { + _lastExtra = lastExtra; +} + +bool AILastExtraCondition::fireCondition() { + // TODO + return false; +} + +AICondition *makeLocationAndDoesntHaveItemCondition(const tRoomID room, const tDirectionConstant direction, const tItemID 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 100755 index 0000000000..559ec55c80 --- /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(tRoomViewID); + virtual bool fireCondition(); + + virtual void writeAICondition(Common::WriteStream *); + virtual void readAICondition(Common::ReadStream *); + +protected: + uint32 _numLocations, _maxLocations; + tRoomViewID *_locations; +}; + +///////////////////////////////////////////// +// +// AIDoorOpenedCondition + +class AIDoorOpenedCondition : public AICondition { +public: + AIDoorOpenedCondition(tRoomViewID); + virtual ~AIDoorOpenedCondition() {} + + virtual bool fireCondition(); + +protected: + tRoomViewID _doorLocation; +}; + +///////////////////////////////////////////// +// +// AIHasItemCondition + +class AIHasItemCondition : public AICondition { +public: + AIHasItemCondition(const tItemID); + + virtual bool fireCondition(); + +protected: + tItemID _item; +}; + +///////////////////////////////////////////// +// +// AIDoesntHaveItemCondition + +class AIDoesntHaveItemCondition : public AICondition { +public: + AIDoesntHaveItemCondition(const tItemID); + + virtual bool fireCondition(); + +protected: + tItemID _item; +}; + +///////////////////////////////////////////// +// +// AICurrentItemCondition + +class AICurrentItemCondition : public AICondition { +public: + AICurrentItemCondition(const tItemID); + + virtual bool fireCondition(); + +protected: + tItemID _item; +}; + +///////////////////////////////////////////// +// +// AICurrentBiochipCondition + +class AICurrentBiochipCondition : public AICondition { +public: + AICurrentBiochipCondition(const tItemID); + + virtual bool fireCondition(); + +protected: + tItemID _biochip; +}; + +///////////////////////////////////////////// +// +// AIItemStateCondition + +class AIItemStateCondition : public AICondition { +public: + AIItemStateCondition(const tItemID, const tItemState); + + virtual bool fireCondition(); + +protected: + tItemID _item; + tItemState _state; +}; + +///////////////////////////////////////////// +// +// AIEnergyMonitorCondition + +class AIEnergyMonitorCondition : public AICondition { +public: + AIEnergyMonitorCondition(const int32); + + virtual bool fireCondition(); + +protected: + int32 _energyThreshold; +}; + +///////////////////////////////////////////// +// +// AILastExtraCondition + +class AILastExtraCondition : public AICondition { +public: + AILastExtraCondition(const tExtraID); + + virtual bool fireCondition(); + +protected: + tExtraID _lastExtra; +}; + +///////////////////////////////////////////// +// +// Helper functions + +AICondition *makeLocationAndDoesntHaveItemCondition(const tRoomID room, const tDirectionConstant direction, const tItemID 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 100755 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 100755 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 { +public: + AIRuleList() {} + ~AIRuleList() {} + + void writeAIRules(Common::WriteStream *); + void readAIRules(Common::ReadStream *); +}; + +} // End of namespace Pegasus + +#endif diff --git a/engines/pegasus/constants.h b/engines/pegasus/constants.h index 519e97e9ec..4af10bf574 100755 --- a/engines/pegasus/constants.h +++ b/engines/pegasus/constants.h @@ -33,6 +33,8 @@ namespace Pegasus { +// TODO: Organize these + const tGameID kGameIDNothing = -1; const tActorID kNoActorID = kGameIDNothing; @@ -683,6 +685,53 @@ enum { kPlayerWonGame }; +static const tCoordType kAILeftAreaLeft = 76; +static const tCoordType kAILeftAreaTop = 334; + +static const tCoordType kAILeftAreaWidth = 96; +static const tCoordType kAILeftAreaHeight = 96; + +static const tCoordType kAIMiddleAreaLeft = 172; +static const tCoordType kAIMiddleAreaTop = 334; + +static const tCoordType kAIMiddleAreaWidth = 192; +static const tCoordType kAIMiddleAreaHeight = 96; + +static const tCoordType kAIRightAreaLeft = 364; +static const tCoordType kAIRightAreaTop = 334; + +static const tCoordType kAIRightAreaWidth = 96; +static const tCoordType 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 +}; + +static const tDirectionConstant kNorth = 0; +static const tDirectionConstant kSouth = 1; +static const tDirectionConstant kEast = 2; +static const tDirectionConstant kWest = 3; + +// TODO: Remove me +const tRoomID kTSA37 = 42; +const tRoomID kTinyTSA37 = 0; + } // End of namespace Pegasus #endif diff --git a/engines/pegasus/gamestate.h b/engines/pegasus/gamestate.h index f3568295d6..b95a5d7fdd 100755 --- a/engines/pegasus/gamestate.h +++ b/engines/pegasus/gamestate.h @@ -30,6 +30,7 @@ #include "common/util.h" #include "pegasus/types.h" +#include "pegasus/util.h" #include "pegasus/items/item.h" namespace Common { @@ -853,60 +854,6 @@ private: tDirectionConstant _openDoorDirection; // Pegasus Prime - #define NUM_FLAGS (sizeof(Unit) * 8) - #define BIT_INDEX_SHIFT (sizeof(Unit) + 2 - (sizeof(Unit)) / 3) - #define BIT_INDEX_MASK (NUM_FLAGS - 1) - template - 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]; - }; - FlagsArray _globalFlags; FlagsArray _scoringFlags; FlagsArray _itemTakenFlags; @@ -934,6 +881,6 @@ private: } // End of namespace Pegasus -#define GameState (::Pegasus::GameStateManager::instance()) +#define GameState (::Pegasus::GameStateManager::instance()) #endif diff --git a/engines/pegasus/input.h b/engines/pegasus/input.h index d70a573bc0..298a78fb53 100755 --- a/engines/pegasus/input.h +++ b/engines/pegasus/input.h @@ -267,6 +267,10 @@ enum { kFilterMod3Button }; +static const tInputBits kHintInterruption = kFilterAllInputNoAuto; +static const tInputBits kWarningInterruption = kFilterNoInput; +static const tInputBits kOpticalInterruption = kFilterAllInputNoAuto; + /* Buttons are defined as: diff --git a/engines/pegasus/items/biochips/aichip.cpp b/engines/pegasus/items/biochips/aichip.cpp new file mode 100755 index 0000000000..d4e425921b --- /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 tItemState 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 tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant 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; + } + + tItemState 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; + } + + tItemState newState = s_highlightState[vm->getNumHints()][numSolves][kAIScanSpotID - kAIHint1SpotID + 1]; + + if (newState != -1) + setItemState(newState); +} + +void AIChip::clearClicked() { + _playingMovie = false; + setUpAIChip(); +} + +void AIChip::clickInAIHotspot(tHotSpotID 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; + } + + tItemState state = getItemState(); + + if (!movieName.empty()) { + _playingMovie = true; + + uint numSolves; + if (GameState.getWalkthroughMode()) { + if (vm->canSolve()) + numSolves = 2; + else + numSolves = 1; + } else { + numSolves = 0; + } + + tItemState 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 100755 index 0000000000..96d2fb2c01 --- /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 tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + 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(tHotSpotID); + + 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 \ No newline at end of file diff --git a/engines/pegasus/items/biochips/biochipitem.cpp b/engines/pegasus/items/biochips/biochipitem.cpp index 71065f1efe..99e6050c85 100755 --- a/engines/pegasus/items/biochips/biochipitem.cpp +++ b/engines/pegasus/items/biochips/biochipitem.cpp @@ -27,6 +27,7 @@ #include "common/stream.h" #include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" #include "pegasus/items/biochips/biochipitem.h" namespace Pegasus { @@ -80,13 +81,15 @@ TimeValue BiochipItem::getRightAreaTime() const { void BiochipItem::select() { Item::select(); - // TODO: AI + if (g_AIArea) + g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, getRightAreaTime()); } void BiochipItem::deselect() { Item::deselect(); - // TODO: AI + if (g_AIArea) + g_AIArea->setAIAreaToTime(kBiochipSignature, kRightAreaSignature, 0xffffffff); } } // End of namespace Pegasus diff --git a/engines/pegasus/items/biochips/opticalchip.cpp b/engines/pegasus/items/biochips/opticalchip.cpp new file mode 100755 index 0000000000..f6519c7f61 --- /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 tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant 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(tHotSpotID id) { + playOpMemMovie(id); +} + +void OpticalChip::playOpMemMovie(tHotSpotID 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; + } + + tItemState 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 100755 index 0000000000..c44a5eaf63 --- /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 tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~OpticalChip(); + + virtual void writeToStream(Common::WriteStream *); + virtual void readFromStream(Common::ReadStream *); + + void addAries(); + void addMercury(); + void addPoseidon(); + + void activateOpticalHotspots(); + void clickInOpticalHotspot(tHotSpotID); + void playOpMemMovie(tHotSpotID); + +protected: + enum { + kOpticalAriesExposed, + kOpticalMercuryExposed, + kOpticalPoseidonExposed, + kNumOpticalChipFlags + }; + + void setUpOpticalChip(); + + FlagsArray _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 100755 index 0000000000..5d0c9915d8 --- /dev/null +++ b/engines/pegasus/items/biochips/pegasuschip.cpp @@ -0,0 +1,178 @@ +/* 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" + +namespace Pegasus { + +PegasusChip::PegasusChip(const tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant 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; + + tItemState thisState = getItemState(); + tItemState 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; + } + + setItemState(hiliteState); + + 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 100755 index 0000000000..1ee35b78a1 --- /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 tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + 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/inventory/airmask.cpp b/engines/pegasus/items/inventory/airmask.cpp new file mode 100755 index 0000000000..682927deaf --- /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 tItemID id, const tNeighborhoodID neighborhood, const tRoomID room, const tDirectionConstant 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() { + tAirQuality airQuality; + + if (g_neighborhood) + airQuality = g_neighborhood->getAirQuality(GameState.getCurrentRoom()); + else + airQuality = kAirQualityGood; + + uint airLevel = getAirLeft(); + tItemState newState = getItemState(); + tItemState 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(); + tItemState newState = getItemState(); + tItemState 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 tItemState 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(((_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 100755 index 0000000000..0e71380baf --- /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 tItemID, const tNeighborhoodID, const tRoomID, const tDirectionConstant); + virtual ~AirMask(); + + virtual void writeToStream(Common::WriteStream *); + virtual void readFromStream(Common::ReadStream *); + + virtual void setItemState(const tItemState); + 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/inventoryitem.cpp b/engines/pegasus/items/inventory/inventoryitem.cpp index ffa745acef..17f07050b3 100755 --- a/engines/pegasus/items/inventory/inventoryitem.cpp +++ b/engines/pegasus/items/inventory/inventoryitem.cpp @@ -26,6 +26,7 @@ #include "common/stream.h" #include "pegasus/pegasus.h" +#include "pegasus/ai/ai_area.h" #include "pegasus/items/inventory/inventoryitem.h" namespace Pegasus { @@ -90,13 +91,15 @@ TimeValue InventoryItem::getAnimationTime() const { void InventoryItem::select() { Item::select(); - // TODO: AI + if (g_AIArea) + g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, getLeftAreaTime()); } void InventoryItem::deselect() { Item::deselect(); - // TODO: AI + if (g_AIArea) + g_AIArea->setAIAreaToTime(kInventorySignature, kLeftAreaSignature, 0xffffffff); } void InventoryItem::getPanelTimes(TimeValue &start, TimeValue &stop) const { diff --git a/engines/pegasus/items/item.cpp b/engines/pegasus/items/item.cpp index e4f1b712d7..aba9741940 100755 --- a/engines/pegasus/items/item.cpp +++ b/engines/pegasus/items/item.cpp @@ -30,8 +30,11 @@ #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 { @@ -128,30 +131,20 @@ Item::~Item() { delete[] _itemExtras.entries; } -Common::Error Item::writeToStream(Common::WriteStream *stream) { +void Item::writeToStream(Common::WriteStream *stream) { stream->writeUint16BE(_itemNeighborhood); stream->writeUint16BE(_itemRoom); stream->writeByte(_itemDirection); stream->writeUint16BE(_itemOwnerID); stream->writeUint16BE(_itemState); - - if (stream->err()) - return Common::kWritingFailed; - - return Common::kNoError; } -Common::Error Item::readFromStream(Common::ReadStream *stream) { +void Item::readFromStream(Common::ReadStream *stream) { _itemNeighborhood = stream->readUint16BE(); _itemRoom = stream->readUint16BE(); _itemDirection = stream->readByte(); _itemOwnerID = stream->readUint16BE(); _itemState = stream->readUint16BE(); - - if (stream->err()) - return Common::kReadingFailed; - - return Common::kNoError; } tActorID Item::getItemOwner() const { @@ -181,7 +174,11 @@ tItemState Item::getItemState() const { void Item::setItemState(const tItemState state) { if (state != _itemState) { _itemState = state; - // TODO: Selection + + if (getItemType() == kInventoryItemType && ((PegasusEngine *)g_engine)->getCurrentInventoryItem() == (InventoryItem *)this) + select(); + else if (getItemType() == kBiochipItemType && ((PegasusEngine *)g_engine)->getCurrentBiochip() == (BiochipItem *)this) + select(); } } @@ -224,14 +221,24 @@ TimeValue Item::getSharedAreaTime() const { void Item::select() { _isSelected = true; - // TODO: AI + 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; - // TODO: AI + 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, tItemState &state, TimeValue &time) { @@ -286,4 +293,13 @@ Sprite *Item::getDragSprite(const tDisplayElementID id) const { return result; } +TimeValue Item::getInfoLeftTime() const { + return _itemInfo.infoLeftTime; +} + +void Item::getInfoRightTimes(TimeValue &start, TimeValue &stop) const { + start = _itemInfo.infoRightStart; + stop = _itemInfo.infoRightStop; +} + } // End of namespace Pegasus diff --git a/engines/pegasus/items/item.h b/engines/pegasus/items/item.h index 1373913892..b74dfc4e96 100755 --- a/engines/pegasus/items/item.h +++ b/engines/pegasus/items/item.h @@ -294,8 +294,8 @@ public: // WriteToStream writes everything EXCEPT the item's ID. // It is assumed that the calling function will write and read the ID. - virtual Common::Error writeToStream(Common::WriteStream *stream); - virtual Common::Error readFromStream(Common::ReadStream *stream); + virtual void writeToStream(Common::WriteStream *stream); + virtual void readFromStream(Common::ReadStream *stream); virtual tActorID getItemOwner() const; virtual void setItemOwner(const tActorID owner); @@ -312,7 +312,7 @@ public: virtual tItemType getItemType() = 0; TimeValue getInfoLeftTime() const; - void getInfoRightTimes(TimeValue&, TimeValue&) const; + void getInfoRightTimes(TimeValue &, TimeValue &) const; TimeValue getSharedAreaTime() const; Sprite *getDragSprite(const tDisplayElementID) const; diff --git a/engines/pegasus/items/itemlist.cpp b/engines/pegasus/items/itemlist.cpp index 2b980d6f5f..cbe48285e9 100755 --- a/engines/pegasus/items/itemlist.cpp +++ b/engines/pegasus/items/itemlist.cpp @@ -40,32 +40,22 @@ ItemList::ItemList() { ItemList::~ItemList() { } -Common::Error ItemList::writeToStream(Common::WriteStream *stream) { +void ItemList::writeToStream(Common::WriteStream *stream) { stream->writeUint32BE(size()); for (ItemIterator it = begin(); it != end(); it++) { stream->writeUint16BE((*it)->getObjectID()); (*it)->writeToStream(stream); } - - if (stream->err()) - return Common::kWritingFailed; - - return Common::kNoError; } -Common::Error ItemList::readFromStream(Common::ReadStream *stream) { +void ItemList::readFromStream(Common::ReadStream *stream) { uint32 itemCount = stream->readUint32BE(); for (uint32 i = 0; i < itemCount; i++) { tItemID itemID = stream->readUint16BE(); g_allItems.findItemByID(itemID)->readFromStream(stream); } - - if (stream->err()) - return Common::kReadingFailed; - - return Common::kNoError; } Item *ItemList::findItemByID(const tItemID id) { diff --git a/engines/pegasus/items/itemlist.h b/engines/pegasus/items/itemlist.h index dd9a4ba023..df31b5c262 100755 --- a/engines/pegasus/items/itemlist.h +++ b/engines/pegasus/items/itemlist.h @@ -44,8 +44,8 @@ public: ItemList(); virtual ~ItemList(); - virtual Common::Error writeToStream(Common::WriteStream *stream); - virtual Common::Error readFromStream(Common::ReadStream *stream); + virtual void writeToStream(Common::WriteStream *stream); + virtual void readFromStream(Common::ReadStream *stream); Item *findItemByID(const tItemID id); }; diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk index 2909a22b68..3af207ff3c 100644 --- a/engines/pegasus/module.mk +++ b/engines/pegasus/module.mk @@ -20,11 +20,19 @@ MODULE_OBJS = \ timers.o \ transition.o \ util.o \ + ai/ai_action.o \ + ai/ai_area.o \ + ai/ai_condition.o \ + ai/ai_rule.o \ items/inventory.o \ items/inventorypicture.o \ items/item.o \ items/itemlist.o \ + items/biochips/aichip.o \ items/biochips/biochipitem.o \ + items/biochips/opticalchip.o \ + items/biochips/pegasuschip.o \ + items/inventory/airmask.o \ items/inventory/inventoryitem.o \ neighborhood/door.o \ neighborhood/exit.o \ diff --git a/engines/pegasus/neighborhood/neighborhood.cpp b/engines/pegasus/neighborhood/neighborhood.cpp index 75a8df9028..6f0bf79959 100644 --- a/engines/pegasus/neighborhood/neighborhood.cpp +++ b/engines/pegasus/neighborhood/neighborhood.cpp @@ -33,10 +33,13 @@ namespace Pegasus { +Neighborhood *g_neighborhood = 0; + Neighborhood::Neighborhood(InputHandler *nextHandler, PegasusEngine *vm, const Common::String &resName, tNeighborhoodID id) : InputHandler(nextHandler), IDObject(id), _vm(vm), _resName(resName) { GameState.setOpenDoorLocation(kNoRoomID, kNoDirection); _currentAlternate = 0; _interruptionFilter = kFilterAllInput; + g_neighborhood = this; } Neighborhood::~Neighborhood() { @@ -44,6 +47,7 @@ Neighborhood::~Neighborhood() { g_allHotspots.remove(*it); _neighborhoodHotspots.deleteHotspots(); + g_neighborhood = 0; } void Neighborhood::init() { @@ -389,4 +393,46 @@ bool operator!=(const tQueueRequest &arg1, const tQueueRequest &arg2) { return !operator==(arg1, arg2); } +Common::String Neighborhood::getBriefingMovie() { + // TODO: Interaction check + + return Common::String(); +} + +Common::String Neighborhood::getEnvScanMovie() { + // TODO: Interaction check + + return Common::String(); +} + +uint Neighborhood::getNumHints() { + // TODO: Interaction check + + return 0; +} + +Common::String Neighborhood::getHintMovie(uint hintNum) { + // TODO: Interaction check + + return Common::String(); +} + +bool Neighborhood::canSolve() { + // TODO: Interaction check + + return false; +} + +void Neighborhood::doSolve() { + // TODO: Interaction check +} + +bool Neighborhood::okayToJump() { + return !_vm->playerHasItemID(kGasCanister) && !_vm->playerHasItemID(kMachineGun); +} + +tAirQuality Neighborhood::getAirQuality(const tRoomID) { + return kAirQualityGood; +} + } // End of namespace Pegasus diff --git a/engines/pegasus/neighborhood/neighborhood.h b/engines/pegasus/neighborhood/neighborhood.h index d294ffe7f0..f42171803b 100644 --- a/engines/pegasus/neighborhood/neighborhood.h +++ b/engines/pegasus/neighborhood/neighborhood.h @@ -113,6 +113,20 @@ public: virtual bool actionQueueEmpty() { return _actionQueue.empty(); } + 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 tAirQuality getAirQuality(const tRoomID); + virtual void checkAirMask() {} + protected: virtual void receiveNotification(Notification *, const tNotificationFlags); @@ -146,6 +160,8 @@ protected: tInputBits _interruptionFilter; }; +extern Neighborhood *g_neighborhood; + } // End of namespace Pegasus #endif diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp index 816d01cf55..1db0dc64b7 100644 --- a/engines/pegasus/pegasus.cpp +++ b/engines/pegasus/pegasus.cpp @@ -41,8 +41,13 @@ #include "pegasus/pegasus.h" #include "pegasus/timers.h" #include "pegasus/items/itemlist.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" +#include "pegasus/neighborhood/neighborhood.h" namespace Pegasus { @@ -52,6 +57,7 @@ PegasusEngine::PegasusEngine(OSystem *syst, const PegasusGameDescription *gamede _saveAllowed = _loadAllowed = true; _gameMenu = 0; _deathReason = kDeathStranded; + _neighborhood = 0; } PegasusEngine::~PegasusEngine() { @@ -178,16 +184,24 @@ void PegasusEngine::createItem(tItemID itemID, tNeighborhoodID neighborhoodID, t case kInterfaceBiochip: // Unused in game, but still in the data - no need to load it break; - case kMapBiochip: 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: case kRetinalScanBiochip: case kShieldBiochip: - case kOpticalBiochip: - // TODO: Specialized biochip classes + // TODO: Rest of specialized biochip classes new BiochipItem(itemID, neighborhoodID, roomID, direction); break; case kAirMask: + new AirMask(itemID, neighborhoodID, roomID, direction); + break; case kKeyCard: case kGasCanister: // TODO: Specialized inventory item classes @@ -829,4 +843,97 @@ void PegasusEngine::resetEnergyDeathReason() { _deathReason = kDeathStranded; } +uint16 PegasusEngine::getSoundFXLevel() { + // TODO + return 0x100; +} + +bool PegasusEngine::playerHasItem(const Item *item) { + return playerHasItemID(item->getObjectID()); +} + +bool PegasusEngine::playerHasItemID(const tItemID itemID) { + return itemInInventory(itemID) || itemInBiochips(itemID); +} + +InventoryItem *PegasusEngine::getCurrentInventoryItem() { + // TODO + return 0; +} + +bool PegasusEngine::itemInInventory(InventoryItem *item) { + return _items.itemInInventory(item); +} + +bool PegasusEngine::itemInInventory(tItemID id) { + return _items.itemInInventory(id); +} + +BiochipItem *PegasusEngine::getCurrentBiochip() { + // TODO + return 0; +} + +bool PegasusEngine::itemInBiochips(BiochipItem *item) { + return _biochips.itemInInventory(item); +} + +bool PegasusEngine::itemInBiochips(tItemID 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 tNeighborhoodID neighborhoodID, const tRoomID roomID, const tDirectionConstant direction) { + GameState.setNextLocation(neighborhoodID, roomID, direction); + _shellNotification.setNotificationFlags(kNeedNewJumpFlag, kNeedNewJumpFlag); +} + } // End of namespace Pegasus diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h index 2990811e60..e6902bf7a5 100644 --- a/engines/pegasus/pegasus.h +++ b/engines/pegasus/pegasus.h @@ -53,6 +53,9 @@ 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; @@ -73,6 +76,7 @@ public: // Base classes GraphicsManager *_gfx; Common::MacResManager *_resFork; + Cursor *_cursor; // Menu void useMenu(GameMenu *menu); @@ -84,11 +88,20 @@ public: void removeIdler(Idler *idler); void addTimeBase(TimeBase *timeBase); void removeTimeBase(TimeBase *timeBase); - void swapSaveAllowed(bool allow) { _saveAllowed = allow; } - void swapLoadAllowed(bool allow) { _loadAllowed = allow; } + bool swapSaveAllowed(bool allow) { + bool old = _saveAllowed; + _saveAllowed = allow; + return old; + } + bool swapLoadAllowed(bool allow) { + bool old = _loadAllowed; + _loadAllowed = allow; + return old; + } void delayShell(TimeValue time, TimeScale scale); void resetIntroTimer(); void refreshDisplay(); + bool playerAlive(); // Energy void setLastEnergyValue(const int32 value) { _savedEnergyValue = value; } @@ -99,11 +112,38 @@ public: tDeathReason getEnergyDeathReason() { return _deathReason; } void resetEnergyDeathReason(); + // Volume + uint16 getSoundFXLevel(); + + // Items + bool playerHasItem(const Item *); + bool playerHasItemID(const tItemID); + + // Inventory Items + InventoryItem *getCurrentInventoryItem(); + bool itemInInventory(InventoryItem *); + bool itemInInventory(tItemID); + + // Biochips + BiochipItem *getCurrentBiochip(); + bool itemInBiochips(BiochipItem *); + bool itemInBiochips(tItemID); + + // 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 &); + + // Neighborhood + void jumpToNewEnvironment(const tNeighborhoodID, const tRoomID, const tDirectionConstant); + protected: Common::Error run(); - Cursor *_cursor; - Notification _shellNotification; virtual void receiveNotification(Notification *notification, const tNotificationFlags flags); @@ -155,6 +195,9 @@ private: // Death tDeathReason _deathReason; + + // Neighborhood + Neighborhood *_neighborhood; }; } // End of namespace Pegasus diff --git a/engines/pegasus/timers.h b/engines/pegasus/timers.h index a40a2c179b..4ad2df0587 100755 --- a/engines/pegasus/timers.h +++ b/engines/pegasus/timers.h @@ -241,8 +241,8 @@ protected: class FuseFunction : public Fuse, public FunctionPtr { public: - FuseFunction(); - virtual ~FuseFunction(); + FuseFunction() {} + virtual ~FuseFunction() {} protected: virtual void invokeAction() { callFunction(); } diff --git a/engines/pegasus/util.h b/engines/pegasus/util.h index 459c1380cb..6369c49fdb 100755 --- a/engines/pegasus/util.h +++ b/engines/pegasus/util.h @@ -23,8 +23,10 @@ * */ -#ifndef PEGASUS_MMSHELL_MMIDOBJECT_H -#define PEGASUS_MMSHELL_MMIDOBJECT_H +#ifndef PEGASUS_UTIL_H +#define PEGASUS_UTIL_H + +#include "common/stream.h" #include "pegasus/types.h" @@ -63,6 +65,65 @@ protected: 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 +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); void shuffleArray(int32 *arr, int32 count, Common::RandomSource &random); -- cgit v1.2.3