mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
322 lines
8.2 KiB
C++
322 lines
8.2 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.
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "tinsel/noir/notebook.h"
|
|
|
|
#include "tinsel/background.h"
|
|
#include "tinsel/dialogs.h"
|
|
#include "tinsel/film.h"
|
|
#include "tinsel/handle.h"
|
|
#include "tinsel/multiobj.h"
|
|
#include "tinsel/noir/sysreel.h"
|
|
#include "tinsel/pdisplay.h"
|
|
#include "tinsel/polygons.h"
|
|
#include "tinsel/timers.h"
|
|
|
|
namespace Tinsel {
|
|
|
|
Notebook::Notebook() {
|
|
_polygons = instantiateNoteBookPolygons();
|
|
}
|
|
|
|
Notebook::~Notebook() {
|
|
delete _polygons;
|
|
}
|
|
|
|
void Notebook::addHyperlink(int32 id1, int32 id2) {
|
|
auto *invObject = _vm->_dialogs->getInvObjectT3(id1);
|
|
|
|
if (invObject->getTitle() != 0) {
|
|
error("A clue can only be hyperlinked if it only has one title!");
|
|
return;
|
|
}
|
|
|
|
invObject = _vm->_dialogs->getInvObjectT3(id2);
|
|
|
|
if (invObject->getTitle() != 0) {
|
|
error("A clue can only be hyperlinked if it only has one title!");
|
|
return;
|
|
}
|
|
|
|
uint32 i;
|
|
for (i = 0; i < MAX_HYPERS; ++i) {
|
|
int32 curr_id1 = _hyperlinks[i].id1;
|
|
if (curr_id1 == 0) {
|
|
_hyperlinks[i].id1 = id1;
|
|
_hyperlinks[i].id2 = id2;
|
|
return;
|
|
}
|
|
|
|
if ((curr_id1 == id1) || (id1 == _hyperlinks[i].id2)) {
|
|
if ((curr_id1 != id2) && (id2 != _hyperlinks[i].id2)) {
|
|
error("A clue/title can only be hyperlinked to one other clue/title!");
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// No free hyperlink entry was found
|
|
error("Too many hyperlinks");
|
|
}
|
|
|
|
void Notebook::clearNotebookPage() {
|
|
if (_prevPage != -1) {
|
|
_pages[_prevPage].clear();
|
|
}
|
|
_prevPage = -1;
|
|
_pages[_currentPage].clear();
|
|
}
|
|
|
|
void Notebook::refresh() {
|
|
auto reel = (_currentPage == 0 ? SysReel::NOTEPAD_CLOSED : SysReel::NOTEPAD_OPEN);
|
|
auto film = GetSystemReelFilm(reel);
|
|
InitStepAnimScript(&_anim, _object, film->reels->script, ONE_SECOND / film->frate);
|
|
clearNotebookPage();
|
|
if (_currentPage != 0) {
|
|
_pages[_currentPage].fillIn();
|
|
}
|
|
}
|
|
|
|
int Notebook::addTitle(const InventoryObjectT3 &invObject) {
|
|
int id = invObject.getId();
|
|
assert(invObject.isNotebookTitle());
|
|
for (uint32 i = 0; i < _numPages; i++) {
|
|
if (_pages[i].getTitle() == id) {
|
|
return i;
|
|
}
|
|
}
|
|
int linkedFromPage = invObject.getUnknown();
|
|
|
|
// 0 page is the closed notebook, has no entries.
|
|
if (linkedFromPage != 0) {
|
|
// Allocate a line on the linked from page.
|
|
assert(_pages[linkedFromPage].getTitle() != 0);
|
|
_pages[linkedFromPage].addLine(id);
|
|
}
|
|
int pageIndex = _numPages++;
|
|
_pages[pageIndex].setTitle(id);
|
|
return pageIndex;
|
|
}
|
|
|
|
void Notebook::addClue(const InventoryObjectT3 &invObject) {
|
|
if (invObject.getUnknown() == 0) {
|
|
// This affects two clues, that should get special treatment.
|
|
warning("TODO: Handle clues with no parent page");
|
|
return;
|
|
}
|
|
// Add title if missing, otherwise just get the page it's on.
|
|
auto titleObject = _vm->_dialogs->getInvObjectT3(invObject.getUnknown());
|
|
int pageIndex = addTitle(*titleObject);
|
|
_pages[pageIndex].addLine(invObject.getId());
|
|
if (invObject.getTitle() != 0) {
|
|
auto secondTitleObject = _vm->_dialogs->getInvObjectT3(invObject.getTitle());
|
|
pageIndex = addTitle(*secondTitleObject);
|
|
_pages[pageIndex].addLine(invObject.getId());
|
|
}
|
|
}
|
|
|
|
void Notebook::addClue(int id) {
|
|
auto invObject = _vm->_dialogs->getInvObjectT3(id);
|
|
if (invObject->isNotebookTitle()) {
|
|
addTitle(*invObject);
|
|
} else {
|
|
addClue(*invObject);
|
|
}
|
|
}
|
|
|
|
int Notebook::getPageWithTitle(int id) {
|
|
for (uint32 i = 0; i < _numPages; i++) {
|
|
if (_pages[i].getTitle() == id) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Notebook::crossClue(int id) {
|
|
auto invObject = _vm->_dialogs->getInvObjectT3(id);
|
|
if (invObject->isNotebookTitle()) {
|
|
return;
|
|
}
|
|
int titles[2] = {
|
|
invObject->getUnknown(),
|
|
invObject->getTitle()
|
|
};
|
|
for (int i = 0; i < 2; i++) {
|
|
if (titles[i] == 0) {
|
|
continue;
|
|
}
|
|
int page = getPageWithTitle(titles[i]);
|
|
if (page != -1) {
|
|
_pages[page].crossClue(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
void InitNotebookAnim(OBJECT **obj, ANIM &anim, SysReel reel, int zPosition) {
|
|
auto film = GetSystemReelFilm(reel);
|
|
MultiDeleteObjectIfExists(FIELD_STATUS, obj);
|
|
*obj = InsertReelObj(film->reels);
|
|
MultiSetZPosition(*obj, zPosition);
|
|
InitStepAnimScript(&anim, *obj, film->reels->script, ONE_SECOND / film->frate);
|
|
}
|
|
|
|
void Notebook::setNextPage(int pageIndex) {
|
|
assert(_prevPage == -1 || _prevPage == (int32)_currentPage); // Check that we've cleaned any outstanding page.
|
|
_prevPage = _currentPage;
|
|
_currentPage = pageIndex;
|
|
}
|
|
|
|
void Notebook::pageFlip(bool up) {
|
|
int nextPage = _currentPage + (up ? -1 : 1);
|
|
if (nextPage <= 0) {
|
|
setNextPage(0);
|
|
refresh();
|
|
return;
|
|
} else if (nextPage == 1) {
|
|
// TODO: Should possibly just call whatever function we use to open.
|
|
InitNotebookAnim(&_object, _anim, SysReel::NOTEPAD_OPENING, Z_INV_RFRAME);
|
|
_state = BOOKSTATE::OPEN_ANIMATING;
|
|
setNextPage(nextPage);
|
|
return;
|
|
}
|
|
setNextPage(nextPage);
|
|
SysReel reel = (up ? SysReel::NOTEPAD_FLIPUP : SysReel::NOTEPAD_FLIPDOWN);
|
|
InitNotebookAnim(&_pageObject, _pageAnim, reel, 19);
|
|
_state = BOOKSTATE::PAGEFLIP;
|
|
}
|
|
|
|
void Notebook::show(bool isOpen) {
|
|
auto reel = (isOpen ? SysReel::NOTEPAD_OPEN : SysReel::NOTEPAD_OPENING);
|
|
InitNotebookAnim(&_object, _anim, reel, Z_INV_MFRAME);
|
|
|
|
_state = (isOpen ? BOOKSTATE::OPENED : BOOKSTATE::OPEN_ANIMATING);
|
|
setNextPage(1);
|
|
refresh();
|
|
DisableTags(); // Tags disabled in Notebook
|
|
DisablePointing(); // Pointing disabled in Notebook
|
|
}
|
|
|
|
bool Notebook::isOpen() const {
|
|
return _state != BOOKSTATE::CLOSED;
|
|
}
|
|
|
|
void Notebook::close() {
|
|
clearNotebookPage();
|
|
MultiDeleteObjectIfExists(FIELD_STATUS, &_object);
|
|
MultiDeleteObjectIfExists(FIELD_STATUS, &_pageObject);
|
|
_state = BOOKSTATE::CLOSED;
|
|
if (_vm->_dialogs->inventoryOrNotebookActive()) {
|
|
EnablePointing();
|
|
EnableTags();
|
|
}
|
|
}
|
|
|
|
void Notebook::stepAnimScripts() {
|
|
if (_state == BOOKSTATE::OPEN_ANIMATING) {
|
|
auto state = StepAnimScript(&_anim);
|
|
if (state == ScriptFinished) {
|
|
_state = BOOKSTATE::OPENED;
|
|
refresh();
|
|
}
|
|
}
|
|
if (_state == BOOKSTATE::PAGEFLIP) {
|
|
auto state = StepAnimScript(&_pageAnim);
|
|
if (state == ScriptFinished) {
|
|
MultiDeleteObjectIfExists(FIELD_STATUS, &_pageObject);
|
|
_state = BOOKSTATE::OPENED;
|
|
refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 Notebook::GetPointedClue(const Common::Point &point) const {
|
|
if (_currentPage == 0 || _currentPage > _numPages) {
|
|
return 0;
|
|
}
|
|
return _pages[_currentPage].getClueForLine(_polygons->lineHit(point));
|
|
}
|
|
|
|
bool Notebook::handlePointer(const Common::Point &point) {
|
|
if (!isOpen()) {
|
|
return 0;
|
|
}
|
|
auto inside = _polygons->isInsideNotebook(point);
|
|
if (inside) {
|
|
auto hit = _polygons->lineHit(point);
|
|
_pages[_currentPage].handlePointAtLine(hit);
|
|
return true; // We handled the pointer
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Notebook::handleEvent(PLR_EVENT pEvent, const Common::Point &coOrds) {
|
|
if (!isOpen()) { // TODO: Clicking outside should close the notebook
|
|
return false;
|
|
}
|
|
auto inside = _polygons->isInsideNotebook(coOrds);
|
|
switch(pEvent) {
|
|
case PLR_ACTION:
|
|
if (inside) {
|
|
return true;
|
|
}
|
|
return false;
|
|
case PLR_LOOK:
|
|
if (inside) {
|
|
return true;
|
|
}
|
|
return false;
|
|
case PLR_WALKTO: {
|
|
// Handle clue-clicks
|
|
auto poly = _polygons->mostSpecificHit(coOrds);
|
|
switch (poly) {
|
|
case NoteBookPoly::NEXT:
|
|
handleEvent(PLR_PGUP, coOrds);
|
|
return true;
|
|
case NoteBookPoly::PREV:
|
|
handleEvent(PLR_PGDN, coOrds);
|
|
return true;
|
|
case NoteBookPoly::NONE:
|
|
handleEvent(PLR_ESCAPE, coOrds);
|
|
return true;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
case PLR_ESCAPE:
|
|
close();
|
|
return true;
|
|
case PLR_PGUP:
|
|
pageFlip(true);
|
|
return true;
|
|
case PLR_PGDN:
|
|
pageFlip(false);
|
|
return true;
|
|
case PLR_HOME:
|
|
case PLR_END:
|
|
default:
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // End of namespace Tinsel
|