/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * Additional copyright for this file: * Copyright (C) 1995-1997 Presto Studios, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "common/memstream.h" #include "pegasus/cursor.h" #include "pegasus/pegasus.h" #include "pegasus/ai/ai_area.h" #include "pegasus/items/biochips/aichip.h" #include "pegasus/items/biochips/biochipitem.h" #include "pegasus/items/biochips/opticalchip.h" #include "pegasus/items/biochips/pegasuschip.h" #include "pegasus/items/inventory/airmask.h" #include "pegasus/items/inventory/inventoryitem.h" namespace Pegasus { AIArea *g_AIArea = 0; AIArea::AIArea(InputHandler *nextHandler) : InputHandler(nextHandler), _leftAreaMovie(kAILeftAreaID), _middleAreaMovie(kAIMiddleAreaID), _rightAreaMovie(kAIRightAreaID), _AIMovie(kAIMovieID) { g_AIArea = this; _leftAreaOwner = kNoClientSignature; _middleAreaOwner = kNoClientSignature; _rightAreaOwner = kNoClientSignature; _leftInventoryTime = 0xffffffff; _middleInventoryTime = 0xffffffff; _middleBiochipTime = 0xffffffff; _rightBiochipTime = 0xffffffff; _lockCount = 0; startIdling(); } AIArea::~AIArea() { if (_middleAreaOwner == kBiochipSignature) { BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); if (currentBiochip && currentBiochip->isSelected()) currentBiochip->giveUpSharedArea(); } else if (_middleAreaOwner == kInventorySignature) { InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); if (currentItem && currentItem->isSelected()) currentItem->giveUpSharedArea(); } stopIdling(); for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) delete *it; g_AIArea = 0; } // Save last state of AI rules... void AIArea::saveAIState() { PegasusEngine *vm = (PegasusEngine *)g_engine; delete vm->_aiSaveStream; Common::MemoryWriteStreamDynamic out; writeAIRules(&out); vm->_aiSaveStream = new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES); } void AIArea::restoreAIState() { PegasusEngine *vm = (PegasusEngine *)g_engine; if (vm->_aiSaveStream) readAIRules(vm->_aiSaveStream); } void AIArea::writeAIRules(Common::WriteStream *stream) { _AIRules.writeAIRules(stream); } void AIArea::readAIRules(Common::ReadStream *stream) { _AIRules.readAIRules(stream); } void AIArea::initAIArea() { allocateSurface(Common::Rect(0, 0, 384, 96)); _leftAreaMovie.shareSurface(this); _leftAreaMovie.initFromMovieFile("Images/Items/Left Area Movie"); _leftAreaMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop); _leftAreaMovie.setDisplayOrder(kAILeftAreaOrder); _leftAreaMovie.startDisplaying(); _leftAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); _middleAreaMovie.shareSurface(this); _middleAreaMovie.initFromMovieFile("Images/Items/Middle Area Movie"); _middleAreaMovie.moveElementTo(kAIMiddleAreaLeft, kAIMiddleAreaTop); _middleAreaMovie.moveMovieBoxTo(kAIMiddleAreaLeft - kAILeftAreaLeft, 0); _middleAreaMovie.setDisplayOrder(kAIMiddleAreaOrder); _middleAreaMovie.startDisplaying(); _middleAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); _rightAreaMovie.shareSurface(this); _rightAreaMovie.initFromMovieFile("Images/Items/Right Area Movie"); _rightAreaMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop); _rightAreaMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0); _rightAreaMovie.setDisplayOrder(kAIRightAreaOrder); _rightAreaMovie.startDisplaying(); _rightAreaMovie.setVolume(((PegasusEngine *)g_engine)->getSoundFXLevel()); _AIMovie.setDisplayOrder(kAIMovieOrder); } void AIArea::setAIVolume(const uint16 volume) { _leftAreaMovie.setVolume(volume); _middleAreaMovie.setVolume(volume); _rightAreaMovie.setVolume(volume); } // There are only so many legal combinations of client/area. // Here is the list of supported pairs: // kInventorySignature kLeftAreaSignature // kInventorySignature kMiddleAreaSignature // kBiochipSignature kMiddleAreaSignature // kBiochipSignature kRightAreaSignature // kAISignature kLeftAreaSignature // Further, the kAISignature never sets a static frame time in the left area, // but only plays a sequence. // If this function is called while a sequence is playing, it will just "remember" // the time value, so that when the sequence finishes, the new time is asserted. void AIArea::setAIAreaToTime(const LowerClientSignature client, const LowerAreaSignature area, const TimeValue time) { switch (area) { case kLeftAreaSignature: // Only support kInventorySignature client, since AI never calls SetAIAreaToTime. _leftAreaMovie.setSegment(0, _leftAreaMovie.getDuration()); if (time == 0xffffffff) { _leftAreaMovie.hide(); _leftAreaOwner = kNoClientSignature; } else { setLeftMovieTime(time); } break; case kMiddleAreaSignature: // Only support kInventorySignature and kBiochipSignature clients. _middleAreaMovie.stop(); _middleAreaMovie.setFlags(0); _middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration()); if (time == 0xffffffff) { if (client == kInventorySignature) { if (_middleBiochipTime != 0xffffffff) { setMiddleMovieTime(kBiochipSignature, _middleBiochipTime); } else { _middleAreaMovie.hide(); _middleAreaOwner = kNoClientSignature; } } else { // client == kBiochipSignature if (_middleInventoryTime != 0xffffffff) { setMiddleMovieTime(kInventorySignature, _middleInventoryTime); } else { _middleAreaMovie.hide(); _middleAreaOwner = kNoClientSignature; } } } else { setMiddleMovieTime(client, time); } break; case kRightAreaSignature: // Only support kBiochipSignature client. _rightAreaMovie.setSegment(0, _rightAreaMovie.getDuration()); if (time == 0xffffffff) { _rightAreaMovie.hide(); _rightAreaOwner = kNoClientSignature; } else { setRightMovieTime(time); } break; } } // Plays a sequence on an area. When the sequence ends, the previous image // is restored. // Also, is input disabled or not? // Easy answer: yes. // There are only so many legal combinations of client/area. // Here is the list of supported pairs: // kBiochipSignature kMiddleAreaSignature // kBiochipSignature kRightAreaSignature // kInventorySignature kMiddleAreaSignature void AIArea::playAIAreaSequence(const LowerClientSignature, const LowerAreaSignature area, const TimeValue start, const TimeValue stop) { PegasusEngine *vm = (PegasusEngine *)g_engine; lockAIOut(); switch (area) { case kLeftAreaSignature: break; case kMiddleAreaSignature: if (_middleAreaOwner == kInventorySignature) _middleInventoryTime = _middleAreaMovie.getTime(); else if (_middleAreaOwner == kBiochipSignature) _middleBiochipTime = _middleAreaMovie.getTime(); _middleAreaMovie.stop(); _middleAreaMovie.setFlags(0); _middleAreaMovie.setSegment(start, stop); _middleAreaMovie.setTime(start); _middleAreaMovie.show(); _middleAreaMovie.start(); vm->_cursor->hide(); while (_middleAreaMovie.isRunning()) { InputDevice.pumpEvents(); vm->checkCallBacks(); vm->refreshDisplay(); g_system->delayMillis(10); } _middleAreaMovie.stop(); vm->_cursor->hideUntilMoved(); if (_middleAreaOwner == kInventorySignature) setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleInventoryTime); else if (_middleAreaOwner == kBiochipSignature) setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, _middleBiochipTime); else setAIAreaToTime(_middleAreaOwner, kMiddleAreaSignature, 0xffffffff); break; case kRightAreaSignature: _rightBiochipTime = _rightAreaMovie.getTime(); _rightAreaMovie.setSegment(start, stop); _rightAreaMovie.setTime(start); _rightAreaMovie.show(); _rightAreaMovie.start(); vm->_cursor->hide(); while (_rightAreaMovie.isRunning()) { InputDevice.pumpEvents(); vm->checkCallBacks(); vm->refreshDisplay(); g_system->delayMillis(10); } _rightAreaMovie.stop(); vm->_cursor->hideUntilMoved(); setAIAreaToTime(_rightAreaOwner, kRightAreaSignature, _rightBiochipTime); break; } unlockAI(); } bool AIArea::playAIMovie(const LowerAreaSignature area, const Common::String &movieName, bool keepLastFrame, const InputBits interruptFilter) { PegasusEngine *vm = (PegasusEngine *)g_engine; lockAIOut(); InputDevice.waitInput(interruptFilter); if (_AIMovie.isMovieValid()) _AIMovie.releaseMovie(); _AIMovie.shareSurface(this); _AIMovie.initFromMovieFile(movieName); if (area == kLeftAreaSignature) { _AIMovie.moveElementTo(kAILeftAreaLeft, kAILeftAreaTop); _leftAreaMovie.hide(); } else { _AIMovie.moveElementTo(kAIRightAreaLeft, kAIRightAreaTop); _AIMovie.moveMovieBoxTo(kAIRightAreaLeft - kAILeftAreaLeft, 0); _rightAreaMovie.hide(); } _AIMovie.setTime(0); _AIMovie.startDisplaying(); _AIMovie.show(); _AIMovie.redrawMovieWorld(); _AIMovie.setVolume(vm->getSoundFXLevel()); _AIMovie.start(); vm->_cursor->hide(); bool result = true; bool saveAllowed = vm->swapSaveAllowed(false); bool openAllowed = vm->swapLoadAllowed(false); while (_AIMovie.isRunning()) { Input input; InputDevice.getInput(input, interruptFilter); if (input.anyInput() || vm->shouldQuit() || vm->saveRequested() || vm->loadRequested()) { result = false; break; } vm->checkCallBacks(); vm->refreshDisplay(); g_system->delayMillis(10); } _AIMovie.stop(); vm->swapSaveAllowed(saveAllowed); vm->swapLoadAllowed(openAllowed); // This used to keep the last frame up even if the movie was interrupted. // However, this only occurs in the recalibration, where interruption means skip the // whole thing, so skipping causes the AI to go away even when keepLastFrame is true. if (!(result && keepLastFrame)) { _AIMovie.stopDisplaying(); _AIMovie.releaseMovie(); if (area == kLeftAreaSignature) { _leftAreaMovie.setTime(_leftInventoryTime); _leftAreaMovie.show(); _leftAreaMovie.redrawMovieWorld(); } else { _rightAreaMovie.setTime(_rightBiochipTime); _rightAreaMovie.show(); _rightAreaMovie.redrawMovieWorld(); } } vm->_cursor->hideUntilMoved(); unlockAI(); return result; } // Only implemented for kMiddleAreaSignature, kInventorySignature void AIArea::loopAIAreaSequence(const LowerClientSignature owner, const LowerAreaSignature area, const TimeValue start, const TimeValue stop) { if (area == kMiddleAreaSignature && owner == kInventorySignature && owner == _middleAreaOwner) { _middleAreaMovie.stop(); _middleAreaMovie.setFlags(0); _middleAreaMovie.setSegment(start, stop); _middleAreaMovie.setFlags(kLoopTimeBase); _middleAreaMovie.setTime(start); _middleAreaMovie.show(); _middleAreaMovie.start(); } } // Only called by kInventorySignature. void AIArea::setLeftMovieTime(const TimeValue time) { if (!_AIMovie.isSurfaceValid()) { _leftAreaMovie.setTime(time); _leftAreaMovie.show(); _leftAreaMovie.redrawMovieWorld(); } _leftAreaOwner = kInventorySignature; _leftInventoryTime = time; } void AIArea::setMiddleMovieTime(const LowerClientSignature client, const TimeValue time) { if (client == kInventorySignature) { _middleInventoryTime = time; if (_middleAreaOwner == kBiochipSignature) { BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); if (currentBiochip && currentBiochip->isSelected()) currentBiochip->giveUpSharedArea(); } } else { _middleBiochipTime = time; if (_middleAreaOwner == kInventorySignature) { InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); if (currentItem && currentItem->isSelected()) currentItem->giveUpSharedArea(); } } _middleAreaMovie.setSegment(0, _middleAreaMovie.getDuration()); _middleAreaMovie.stop(); _middleAreaMovie.setFlags(0); _middleAreaMovie.setTime(time); _middleAreaMovie.show(); _middleAreaMovie.redrawMovieWorld(); _middleAreaOwner = client; } // Only called by kBiochipSignature. void AIArea::setRightMovieTime(const TimeValue time) { if (!_AIMovie.isSurfaceValid()) { // Can't do it when the AI movie is up... _rightAreaMovie.setTime(time); _rightAreaMovie.show(); _rightAreaMovie.redrawMovieWorld(); } _rightAreaOwner = kBiochipSignature; _rightBiochipTime = time; } void AIArea::handleInput(const Input &input, const Hotspot *cursorSpot) { if (JMPPPInput::isToggleAIMiddleInput(input)) toggleMiddleAreaOwner(); else InputHandler::handleInput(input, cursorSpot); } void AIArea::toggleMiddleAreaOwner() { if (_middleAreaOwner == kInventorySignature) { BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); if (currentBiochip) { setMiddleMovieTime(kBiochipSignature, currentBiochip->getSharedAreaTime()); currentBiochip->takeSharedArea(); } } else if (_middleAreaOwner == kBiochipSignature) { InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); if (currentItem) { setMiddleMovieTime(kInventorySignature, currentItem->getSharedAreaTime()); currentItem->takeSharedArea(); } } } void AIArea::activateHotspots() { PegasusEngine *vm = (PegasusEngine *)g_engine; if (_middleAreaOwner == kBiochipSignature) { BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); if (currentBiochip) switch (currentBiochip->getObjectID()) { case kAIBiochip: ((AIChip *)currentBiochip)->activateAIHotspots(); break; case kPegasusBiochip: if (!vm->isDemo()) ((PegasusChip *)currentBiochip)->activatePegasusHotspots(); break; case kOpticalBiochip: ((OpticalChip *)currentBiochip)->activateOpticalHotspots(); break; } } else if (_middleAreaOwner == kInventorySignature) { InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); if (currentItem && currentItem->getObjectID() == kAirMask) ((AirMask *)currentItem)->activateAirMaskHotspots(); } InputHandler::activateHotspots(); } void AIArea::clickInHotspot(const Input &input, const Hotspot *hotspot) { PegasusEngine *vm = (PegasusEngine *)g_engine; bool handled = false; if (_middleAreaOwner == kBiochipSignature) { BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); if (currentBiochip) { switch (currentBiochip->getObjectID()) { case kAIBiochip: if ((hotspot->getHotspotFlags() & kAIBiochipSpotFlag) != 0) { ((AIChip *)currentBiochip)->clickInAIHotspot(hotspot->getObjectID()); handled = true; } break; case kPegasusBiochip: if (!vm->isDemo() && ((hotspot->getHotspotFlags() & kPegasusBiochipSpotFlag) != 0)) { ((PegasusChip *)currentBiochip)->clickInPegasusHotspot(); handled = true; } break; case kOpticalBiochip: if ((hotspot->getHotspotFlags() & kOpticalBiochipSpotFlag) != 0) { ((OpticalChip *)currentBiochip)->clickInOpticalHotspot(hotspot->getObjectID()); handled = true; } break; } } } else if (_middleAreaOwner == kInventorySignature) { InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); if (currentItem) { switch (currentItem->getObjectID()) { case kAirMask: if ((hotspot->getHotspotFlags() & kAirMaskSpotFlag) != 0) { ((AirMask *)currentItem)->clickInAirMaskHotspot(); handled = true; } break; } } } if (!handled) InputHandler::clickInHotspot(input, hotspot); } void AIArea::lockAIOut() { if (_lockCount == 0) stopIdling(); _lockCount++; } void AIArea::unlockAI() { if (_lockCount > 0) { _lockCount--; if (_lockCount == 0) startIdling(); } } void AIArea::forceAIUnlocked() { if (_lockCount > 0) { _lockCount = 1; unlockAI(); } } void AIArea::checkRules() { if (_lockCount == 0 && ((PegasusEngine *)g_engine)->playerAlive()) for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) if ((*it)->fireRule()) break; } void AIArea::useIdleTime() { checkRules(); } void AIArea::addAIRule(AIRule *rule) { _AIRules.push_back(rule); } void AIArea::removeAllRules() { for (AIRuleList::iterator it = _AIRules.begin(); it != _AIRules.end(); it++) delete *it; _AIRules.clear(); } void AIArea::checkMiddleArea() { BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); if (currentBiochip) { if (_middleAreaOwner == kBiochipSignature) { switch (currentBiochip->getObjectID()) { case kAIBiochip: ((AIChip *)currentBiochip)->setUpAIChip(); break; case kPegasusBiochip: ((PegasusChip *)currentBiochip)->setUpPegasusChip(); break; } } else { switch (currentBiochip->getObjectID()) { case kAIBiochip: ((AIChip *)currentBiochip)->setUpAIChipRude(); break; case kPegasusBiochip: ((PegasusChip *)currentBiochip)->setUpPegasusChipRude(); break; } } } } TimeValue AIArea::getBigInfoTime() { if (_middleAreaOwner == kInventorySignature) { InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); return currentItem->getInfoLeftTime(); } else if (_middleAreaOwner == kBiochipSignature) { BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); return currentBiochip->getInfoLeftTime(); } return 0xffffffff; } void AIArea::getSmallInfoSegment(TimeValue &start, TimeValue &stop) { if (_middleAreaOwner == kInventorySignature) { InventoryItem *currentItem = ((PegasusEngine *)g_engine)->getCurrentInventoryItem(); currentItem->getInfoRightTimes(start, stop); } else if (_middleAreaOwner == kBiochipSignature) { BiochipItem *currentBiochip = ((PegasusEngine *)g_engine)->getCurrentBiochip(); currentBiochip->getInfoRightTimes(start, stop); } else { start = 0xffffffff; stop = 0xffffffff; } } LowerClientSignature AIArea::getMiddleAreaOwner() { return _middleAreaOwner; } } // End of namespace Pegasus