/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 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, see .
*
*/
#include "pink/archive.h"
#include "pink/cursor_mgr.h"
#include "pink/pink.h"
#include "pink/screen.h"
#include "pink/objects/actions/action.h"
#include "pink/objects/actors/supporting_actor.h"
#include "pink/objects/actors/lead_actor.h"
#include "pink/objects/pages/game_page.h"
#include "pink/objects/sequences/sequence_context.h"
#include "pink/objects/sequences/sequencer.h"
namespace Pink {
LeadActor::LeadActor()
: _state(kReady), _nextState(kUndefined), _stateBeforeInventory(kUndefined),
_stateBeforePDA(kUndefined), _isHaveItem(false), _recipient(nullptr),
_cursorMgr(nullptr), _walkMgr(nullptr), _sequencer(nullptr),
_audioInfoMgr(this) {}
void LeadActor::deserialize(Archive &archive) {
_state = kReady;
Actor::deserialize(archive);
_cursorMgr = static_cast(archive.readObject());
_walkMgr = static_cast(archive.readObject());
_sequencer = static_cast(archive.readObject());
}
void LeadActor::toConsole() const {
debugC(6, kPinkDebugLoadingObjects, "LeadActor: _name = %s", _name.c_str());
for (uint i = 0; i < _actions.size(); ++i) {
_actions[i]->toConsole();
}
}
void LeadActor::loadState(Archive &archive) {
_state = (State)archive.readByte();
_nextState = (State)archive.readByte();
_stateBeforeInventory = (State)archive.readByte();
_stateBeforePDA = (State)archive.readByte();
_isHaveItem = archive.readByte();
Common::String recipient = archive.readString();
if (!recipient.empty())
_recipient = _page->findActor(recipient);
else
_recipient = nullptr;
_sequencer->loadState(archive);
_walkMgr->loadState(archive);
_page->getGame()->getPdaMgr().loadState(archive);
_audioInfoMgr.loadState(archive);
}
void LeadActor::saveState(Archive &archive) {
archive.writeByte(_state);
archive.writeByte(_nextState);
archive.writeByte(_stateBeforeInventory);
archive.writeByte(_stateBeforePDA);
archive.writeByte(_isHaveItem);
if (_recipient)
archive.writeString(_recipient->getName());
else
archive.writeString(Common::String());
_sequencer->saveState(archive);
_walkMgr->saveState(archive);
_page->getGame()->getPdaMgr().saveState(archive);
_audioInfoMgr.saveState(archive);
}
void LeadActor::init(bool paused) {
if (_state == kUndefined)
_state = kReady;
getInventoryMgr()->setLeadActor(this);
_page->getGame()->setLeadActor(this);
Actor::init(paused);
}
void LeadActor::start(bool isHandler) {
if (isHandler && _state != kPlayingExitSequence) {
_state = kPlayingSequence;
_nextState = kReady;
}
switch (_state) {
case kInventory:
startInventory(1);
break;
case kPDA:
if (_stateBeforePDA == kInventory)
startInventory(1);
_page->getGame()->getScreen()->saveStage();
loadPDA(_page->getGame()->getPdaMgr().getSavedPageName());
break;
default:
forceUpdateCursor();
break;
}
}
void LeadActor::update() {
switch (_state) {
case kMoving:
_walkMgr->update();
// fall through
case kReady:
_sequencer->update();
_cursorMgr->update();
break;
case kPlayingSequence:
_sequencer->update();
if (!_sequencer->isPlaying()) {
_state = _nextState;
_nextState = kUndefined;
forceUpdateCursor();
}
break;
case kInventory:
getInventoryMgr()->update();
break;
case kPDA:
getPage()->getGame()->getPdaMgr().update();
break;
case kPlayingExitSequence:
_sequencer->update();
if (!_sequencer->isPlaying()) {
_state = kUndefined;
_page->getGame()->changeScene();
}
break;
default:
break;
}
}
void LeadActor::loadPDA(const Common::String &pageName) {
if (_state != kPDA) {
if (_state == kMoving)
cancelInteraction();
if (_state != kInventory && !_page->getGame()->getScreen()->isMenuActive())
_page->pause(true);
_stateBeforePDA = _state;
_state = kPDA;
_page->getGame()->getScreen()->saveStage();
}
_page->getGame()->getPdaMgr().setLead(this);
_page->getGame()->getPdaMgr().goToPage(pageName);
}
void LeadActor::onActionClick(Common::CustomEventType action) {
switch (_state) {
case kMoving:
switch (action) {
case kActionSkipWalkAndCancelInteraction:
cancelInteraction();
// fall through
case kActionSkipWalk:
_walkMgr->skip();
break;
default:
break;
}
break;
case kPlayingSequence:
case kPlayingExitSequence:
switch (action) {
case kActionSkipSubSequence:
_sequencer->skipSubSequence();
break;
case kActionSkipSequence:
_sequencer->skipSequence();
break;
case kActionRestartSequence:
_sequencer->restartSequence();
break;
default:
break;
}
break;
default:
break;
}
}
void LeadActor::onLeftButtonClick(Common::Point point) {
switch (_state) {
case kReady:
case kMoving: {
Actor *clickedActor = getActorByPoint(point);
if (!clickedActor)
return;
if (this == clickedActor) {
_audioInfoMgr.stop();
onLeftClickMessage();
} else if (clickedActor->isSupporting()) {
if (isInteractingWith(clickedActor)) {
_recipient = clickedActor;
_audioInfoMgr.stop();
if (!startWalk()) {
if (_isHaveItem)
sendUseClickMessage(clickedActor);
else
sendLeftClickMessage(clickedActor);
}
}
} else
clickedActor->onLeftClickMessage();
break;
}
case kPDA:
_page->getGame()->getPdaMgr().onLeftButtonClick(point);
break;
case kInventory:
getInventoryMgr()->onClick(point);
break;
default:
break;
}
}
void LeadActor::onLeftButtonUp() {
if (_state == kPDA)
_page->getGame()->getPdaMgr().onLeftButtonUp();
}
void LeadActor::onRightButtonClick(Common::Point point) {
if (_state == kReady || _state == kMoving) {
Actor *clickedActor = getActorByPoint(point);
if (clickedActor && isInteractingWith(clickedActor)) {
_audioInfoMgr.start(clickedActor);
}
if (_state == kMoving)
cancelInteraction();
}
}
void LeadActor::onMouseMove(Common::Point point) {
if (_state != kPDA)
updateCursor(point);
else
_page->getGame()->getPdaMgr().onMouseMove(point);
}
void LeadActor::onMouseOverWithItem(Common::Point point, const Common::String &itemName, CursorMgr *cursorMgr) {
_cursorMgr->setCursor(kHoldingItemCursor, point, itemName + kClickable);
}
void LeadActor::onMouseOver(Common::Point point, CursorMgr *mgr) {
if (getInventoryMgr()->isPinkOwnsAnyItems())
_cursorMgr->setCursor(kClickableFirstFrameCursor, point, Common::String());
else
Actor::onMouseOver(point, mgr);
}
void LeadActor::onLeftClickMessage() {
if (_isHaveItem) {
_isHaveItem = false;
_nextState = _state != kMoving ? kUndefined : kReady;
forceUpdateCursor();
} else {
if (_state == kMoving)
cancelInteraction();
startInventory(0);
}
}
void LeadActor::onInventoryClosed(bool isItemClicked) {
_isHaveItem = isItemClicked;
_state = _stateBeforeInventory;
_stateBeforeInventory = kUndefined;
_page->pause(false);
forceUpdateCursor();
}
void LeadActor::onWalkEnd(const Common::String &stopName) {
State oldNextState = _nextState;
_state = kReady;
_nextState = kUndefined;
if (_recipient && oldNextState == kPlayingSequence) {
if (_isHaveItem)
sendUseClickMessage(_recipient);
else
sendLeftClickMessage(_recipient);
} else { // on ESC button
Action *action = findAction(stopName);
assert(action);
setAction(action);
}
}
void LeadActor::onPDAClose() {
_page->initPalette();
_page->getGame()->getScreen()->loadStage();
_state = _stateBeforePDA;
_stateBeforePDA = kUndefined;
if (_state != kInventory)
_page->pause(false);
}
bool LeadActor::isInteractingWith(const Actor *actor) const {
if (!_isHaveItem)
return actor->isLeftClickHandlers();
return actor->isUseClickHandlers(getInventoryMgr()->getCurrentItem());
}
void LeadActor::setNextExecutors(const Common::String &nextModule, const Common::String &nextPage) {
if (_state == kReady || _state == kMoving || _state == kPlayingSequence || _state == kInventory || _state == kPDA) {
_state = kPlayingExitSequence;
_page->getGame()->setNextExecutors(nextModule, nextPage);
}
}
void LeadActor::forceUpdateCursor() {
PinkEngine *vm =_page->getGame();
vm->getScreen()->update(); // we have actions, that should be drawn to properly update cursor
Common::Point point = vm->getEventManager()->getMousePos();
updateCursor(point);
}
void LeadActor::updateCursor(Common::Point point) {
switch (_state) {
case kReady:
case kMoving: {
Actor *actor = getActorByPoint(point);
InventoryItem *item = getInventoryMgr()->getCurrentItem();
if (_isHaveItem) {
if (actor) {
actor->onMouseOverWithItem(point, item->getName(), _cursorMgr);
} else
_cursorMgr->setCursor(kHoldingItemCursor, point, item->getName());
} else if (actor)
actor->onMouseOver(point, _cursorMgr);
else
_cursorMgr->setCursor(kDefaultCursor, point, Common::String());
break;
}
case kPlayingSequence:
case kPlayingExitSequence:
_cursorMgr->setCursor(kNotClickableCursor, point, Common::String());
break;
case kPDA:
case kInventory:
_cursorMgr->setCursor(kDefaultCursor, point, Common::String());
break;
default:
break;
}
}
void LeadActor::sendUseClickMessage(Actor *actor) {
InventoryMgr *mgr = getInventoryMgr();
assert(_state != kPlayingExitSequence);
_nextState = kReady;
_state = kPlayingSequence;
InventoryItem *item = mgr->getCurrentItem();
actor->onUseClickMessage(mgr->getCurrentItem(), mgr);
if (item->getCurrentOwner() != this->_name)
_isHaveItem = false;
forceUpdateCursor();
}
void LeadActor::sendLeftClickMessage(Actor *actor) {
assert(_state != kPlayingExitSequence);
_nextState = kReady;
_state = kPlayingSequence;
actor->onLeftClickMessage();
forceUpdateCursor();
}
WalkLocation *LeadActor::getWalkDestination() {
return _walkMgr->findLocation(_recipient->getLocation());
}
Actor *LeadActor::getActorByPoint(Common::Point point) {
return _page->getGame()->getScreen()->getActorByPoint(point);
}
void LeadActor::startInventory(bool paused) {
if (!getInventoryMgr()->start(paused))
return;
if (!paused) {
_isHaveItem = false;
_stateBeforeInventory = _state;
_state = kInventory;
forceUpdateCursor();
}
_page->pause(true);
}
bool LeadActor::startWalk() {
WalkLocation *location = getWalkDestination();
if (location) {
_state = kMoving;
_nextState = kPlayingSequence;
_walkMgr->start(location);
return true;
}
return false;
}
void LeadActor::cancelInteraction() {
_recipient = nullptr;
_nextState = kReady;
}
Actor *LeadActor::findActor(const Common::String &name) {
return _page->findActor(name);
}
void ParlSqPink::toConsole() const {
debugC(6, kPinkDebugLoadingObjects, "ParlSqPink: _name = %s", _name.c_str());
for (uint i = 0; i < _actions.size(); ++i) {
_actions[i]->toConsole();
}
}
WalkLocation *ParlSqPink::getWalkDestination() {
if (_recipient->getName() == kBoy && _page->checkValueOfVariable(kBoyBlocked, kUndefinedValue))
return _walkMgr->findLocation(kSirBaldley);
return LeadActor::getWalkDestination();
}
void PubPink::toConsole() const {
debugC(6, kPinkDebugLoadingObjects, "PubPink: _name = %s", _name.c_str());
for (uint i = 0; i < _actions.size(); ++i) {
_actions[i]->toConsole();
}
}
void PubPink::onLeftClickMessage() {
if (!playingMiniGame())
LeadActor::onLeftClickMessage();
}
void PubPink::onVariableSet() {
if (playingMiniGame())
_isHaveItem = true;
}
void PubPink::updateCursor(Common::Point point) {
if (playingMiniGame()) {
Actor *actor = getActorByPoint(point);
assert(actor);
if (_state == kReady && actor->isUseClickHandlers(getInventoryMgr()->getCurrentItem())) {
_cursorMgr->setCursor(kClickableFirstFrameCursor, point, Common::String());
} else
_cursorMgr->setCursor(kDefaultCursor, point, Common::String());
} else {
LeadActor::updateCursor(point);
}
}
void PubPink::sendUseClickMessage(Actor *actor) {
LeadActor::sendUseClickMessage(actor);
if (playingMiniGame())
_isHaveItem = true;
}
WalkLocation *PubPink::getWalkDestination() {
if (playingMiniGame())
return nullptr;
if (_recipient->getName() == kJackson && !_page->checkValueOfVariable(kDrunkLocation, kBolted))
return _walkMgr->findLocation(_page->findActor(kDrunk)->getName());
return LeadActor::getWalkDestination();
}
bool PubPink::playingMiniGame() {
return !(_page->checkValueOfVariable(kFoodPuzzle, kTrueValue) ||
_page->checkValueOfVariable(kFoodPuzzle, kUndefinedValue));
}
void PubPink::onRightButtonClick(Common::Point point) {
if (!playingMiniGame())
LeadActor::onRightButtonClick(point);
}
} // End of namespace Pink