scummvm/engines/pegasus/ai/ai_area.cpp

616 lines
18 KiB
C++

/* 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(DisposeAfterUse::NO);
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