mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-04 08:17:40 +00:00
381 lines
10 KiB
C++
381 lines
10 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 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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Based on
|
|
* WebVenture (c) 2010, Sean Kasun
|
|
* https://github.com/mrkite/webventure, http://seancode.com/webventure/
|
|
*
|
|
* Used with explicit permission from the author
|
|
*/
|
|
|
|
#include "macventure/world.h"
|
|
#include "macventure/macventure.h"
|
|
|
|
#include "common/file.h"
|
|
|
|
namespace MacVenture {
|
|
|
|
World::World(MacVentureEngine *engine, Common::MacResManager *resMan) {
|
|
_resourceManager = resMan;
|
|
_engine = engine;
|
|
_saveGame = NULL;
|
|
_gameText = NULL;
|
|
|
|
startNewGame();
|
|
|
|
_objectConstants = new Container(_engine->getFilePath(kObjectPathID));
|
|
calculateObjectRelations();
|
|
|
|
_gameText = new Container(_engine->getFilePath(kTextPathID));
|
|
}
|
|
|
|
|
|
World::~World() {
|
|
|
|
if (_saveGame)
|
|
delete _saveGame;
|
|
|
|
if (_objectConstants)
|
|
delete _objectConstants;
|
|
|
|
if (_gameText)
|
|
delete _gameText;
|
|
}
|
|
|
|
void World::startNewGame() {
|
|
if (_saveGame)
|
|
delete _saveGame;
|
|
|
|
if ((_startGameFileName = _engine->getStartGameFileName()) == "")
|
|
error("WORLD: Could not load initial game configuration");
|
|
|
|
Common::File saveGameFile;
|
|
if (!saveGameFile.open(_startGameFileName))
|
|
error("WORLD: Could not load initial game configuration");
|
|
|
|
debugC(2, kMVDebugMain, "Loading save game state from %s", _startGameFileName.c_str());
|
|
Common::SeekableReadStream *saveGameRes = saveGameFile.readStream(saveGameFile.size());
|
|
|
|
_saveGame = new SaveGame(_engine, saveGameRes);
|
|
|
|
calculateObjectRelations();
|
|
|
|
delete saveGameRes;
|
|
saveGameFile.close();
|
|
}
|
|
|
|
uint32 World::getObjAttr(ObjID objID, uint32 attrID) {
|
|
uint res;
|
|
uint32 index = _engine->getGlobalSettings()._attrIndices[attrID];
|
|
// HACK, but if I try to initialize it in the else clause, it goes out of scope and segfaults
|
|
Common::SeekableReadStream *objStream = _objectConstants->getItem(objID);
|
|
if (!(index & 0x80)) { // It's not a constant
|
|
res = _saveGame->getAttr(objID, index);
|
|
} else {
|
|
index &= 0x7F;
|
|
if (objStream->size() == 0) {
|
|
return 0;
|
|
}
|
|
// Look for the right attribute inside the object
|
|
objStream->skip(index * 2);
|
|
res = objStream->readByte() << 8;
|
|
res |= objStream->readByte();
|
|
}
|
|
res &= _engine->getGlobalSettings()._attrMasks[attrID];
|
|
res >>= _engine->getGlobalSettings()._attrShifts[attrID];
|
|
if (res & 0x8000)
|
|
res = -((res ^ 0xffff) + 1);
|
|
debugC(5, kMVDebugMain, "Attribute %x from object %x is %x", attrID, objID, res);
|
|
delete objStream;
|
|
return res;
|
|
}
|
|
|
|
void World::setObjAttr(ObjID objID, uint32 attrID, Attribute value) {
|
|
if (attrID == kAttrPosX || attrID == kAttrPosY) {
|
|
// Round to scale
|
|
// Intentionally empty, we don't seem to require this functionality
|
|
}
|
|
|
|
if (attrID == kAttrParentObject)
|
|
setParent(objID, value);
|
|
|
|
if (attrID < kAttrOtherDoor)
|
|
_engine->enqueueObject(kUpdateObject, objID);
|
|
|
|
uint32 idx = _engine->getGlobalSettings()._attrIndices[attrID];
|
|
value <<= _engine->getGlobalSettings()._attrShifts[attrID];
|
|
value &= _engine->getGlobalSettings()._attrMasks[attrID];
|
|
Attribute oldVal = _saveGame->getAttr(objID, idx);
|
|
oldVal &= ~_engine->getGlobalSettings()._attrMasks[attrID];
|
|
_saveGame->setAttr(idx, objID, (value | oldVal));
|
|
_engine->gameChanged();
|
|
}
|
|
|
|
bool MacVenture::World::isObjActive(ObjID obj) {
|
|
ObjID destObj = _engine->getDestObject();
|
|
Common::Point p = _engine->getDeltaPoint();
|
|
ControlAction selectedControl = _engine->getSelectedControl();
|
|
if (!getAncestor(obj)) {
|
|
return false; // If our ancestor is the garbage (obj 0), we're inactive
|
|
}
|
|
if (_engine->getInvolvedObjects() >= 2 && // If (we need > 1 objs for the command) &&
|
|
destObj > 0 && // we have a destination object &&
|
|
!getAncestor(destObj)) { // but that destination object is in the garbage
|
|
return false;
|
|
}
|
|
if (selectedControl != kMoveObject) {
|
|
return true; // We only need one
|
|
}
|
|
// Handle move object
|
|
if (!isObjDraggable(obj)) {
|
|
return false; // We can't move it
|
|
}
|
|
if (getObjAttr(1, kAttrParentObject) != destObj) {
|
|
return true; // if the target is not the player's parent, we can go
|
|
}
|
|
Common::Rect rect(kScreenWidth, kScreenHeight);
|
|
rect.top -= getObjAttr(obj, kAttrPosY) + p.y;
|
|
rect.left -= getObjAttr(obj, kAttrPosX) + p.x;
|
|
return intersects(obj, rect);
|
|
}
|
|
|
|
ObjID World::getAncestor(ObjID objID) {
|
|
ObjID root = getObjAttr(1, kAttrParentObject);
|
|
while (objID != 0 && objID != 1 && objID != root) {
|
|
objID = getObjAttr(objID, kAttrParentObject);
|
|
}
|
|
return objID;
|
|
}
|
|
|
|
Common::Array<ObjID> World::getFamily(ObjID objID, bool recursive) {
|
|
Common::Array<ObjID> res;
|
|
res.push_back(objID);
|
|
res.push_back(getChildren(objID, recursive));
|
|
return res;
|
|
}
|
|
|
|
Common::Array<ObjID> World::getChildren(ObjID objID, bool recursive) {
|
|
Common::Array<ObjID> res;
|
|
ObjID child = _relations[objID * 2];
|
|
while (child) {
|
|
res.push_back(child);
|
|
if (!recursive)
|
|
res.push_back(getChildren(child, false));
|
|
child = _relations[child * 2 + 1];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Attribute World::getGlobal(uint32 attrID) {
|
|
return _saveGame->getGlobals()[attrID];
|
|
}
|
|
|
|
void World::setGlobal(uint32 attrID, Attribute value) {
|
|
_saveGame->setGlobal(attrID, value);
|
|
}
|
|
|
|
void World::updateObj(ObjID objID) {
|
|
WindowReference win;
|
|
if (getObjAttr(1, kAttrParentObject) == objID) {
|
|
win = kMainGameWindow;
|
|
} else {
|
|
win = _engine->getObjWindow(objID);
|
|
}
|
|
if (win) {
|
|
_engine->focusObjWin(objID);
|
|
_engine->runObjQueue();
|
|
_engine->updateWindow(win);
|
|
}
|
|
}
|
|
|
|
void World::captureChildren(ObjID objID) {
|
|
warning("Capture children unimplemented!");
|
|
}
|
|
|
|
void World::releaseChildren(ObjID objID) {
|
|
warning("Release children unimplemented!");
|
|
}
|
|
|
|
Common::String World::getText(ObjID objID, ObjID source, ObjID target) {
|
|
if (objID & 0x8000) {
|
|
return _engine->getUserInput();
|
|
}
|
|
TextAsset text = TextAsset(_engine, objID, source, target, _gameText, _engine->isOldText(), _engine->getDecodingHuffman());
|
|
|
|
return *text.decode();
|
|
}
|
|
|
|
|
|
bool World::isObjDraggable(ObjID objID) {
|
|
return (getObjAttr(objID, kAttrInvisible) == 0 &&
|
|
getObjAttr(objID, kAttrUnclickable) == 0 &&
|
|
getObjAttr(objID, kAttrUndraggable) == 0);
|
|
}
|
|
|
|
bool World::intersects(ObjID objID, Common::Rect rect) {
|
|
return _engine->getObjBounds(objID).intersects(rect);
|
|
}
|
|
|
|
void World::calculateObjectRelations() {
|
|
_relations.clear();
|
|
ObjID val, next;
|
|
uint32 numObjs = _engine->getGlobalSettings()._numObjects;
|
|
const AttributeGroup &parents = *_saveGame->getGroup(0);
|
|
for (uint i = 0; i < numObjs * 2; i++) {
|
|
_relations.push_back(0);
|
|
}
|
|
for (uint i = numObjs - 1; i > 0; i--) {
|
|
val = parents[i];
|
|
next = _relations[val * 2];
|
|
if (next) {
|
|
_relations[i * 2 + 1] = next;
|
|
}
|
|
_relations[val * 2] = i;
|
|
}
|
|
}
|
|
|
|
void World::setParent(ObjID child, ObjID newParent) {
|
|
ObjID old = _saveGame->getAttr(child, kAttrParentObject);
|
|
if (newParent == child)
|
|
return;
|
|
|
|
ObjID oldNdx = old * 2;
|
|
old = _relations[oldNdx];
|
|
while (old != child) {
|
|
oldNdx = (old * 2) + 1;
|
|
old = _relations[oldNdx];
|
|
}
|
|
_relations[oldNdx] = _relations[(old * 2) + 1];
|
|
oldNdx = newParent * 2;
|
|
old = _relations[oldNdx];
|
|
while (old && old <= child) {
|
|
oldNdx = (old * 2) + 1;
|
|
old = _relations[oldNdx];
|
|
}
|
|
_relations[child * 2 + 1] = old;
|
|
_relations[oldNdx] = child;
|
|
}
|
|
|
|
void World::loadGameFrom(Common::InSaveFile *file) {
|
|
if (_saveGame) {
|
|
delete _saveGame;
|
|
}
|
|
_saveGame = new SaveGame(_engine, file);
|
|
calculateObjectRelations();
|
|
}
|
|
|
|
void World::saveGameInto(Common::OutSaveFile *file) {
|
|
_saveGame->saveInto(file);
|
|
}
|
|
|
|
// SaveGame
|
|
SaveGame::SaveGame(MacVentureEngine *engine, Common::SeekableReadStream *res) {
|
|
_groups = Common::Array<AttributeGroup>();
|
|
loadGroups(engine, res);
|
|
_globals = Common::Array<uint16>();
|
|
loadGlobals(engine, res);
|
|
_text = Common::String();
|
|
loadText(engine, res);
|
|
}
|
|
|
|
SaveGame::~SaveGame() {
|
|
}
|
|
|
|
|
|
Attribute SaveGame::getAttr(ObjID objID, uint32 attrID) {
|
|
return _groups[attrID][objID];
|
|
}
|
|
|
|
void SaveGame::setAttr(uint32 attrID, ObjID objID, Attribute value) {
|
|
_groups[attrID][objID] = value;
|
|
}
|
|
|
|
const Common::Array<AttributeGroup> &MacVenture::SaveGame::getGroups() {
|
|
return _groups;
|
|
}
|
|
|
|
const AttributeGroup *SaveGame::getGroup(uint32 groupID) {
|
|
assert(groupID < _groups.size());
|
|
return &(_groups[groupID]);
|
|
}
|
|
|
|
void SaveGame::setGlobal(uint32 attrID, Attribute value) {
|
|
_globals[attrID] = value;
|
|
}
|
|
|
|
const Common::Array<uint16> &SaveGame::getGlobals() {
|
|
return _globals;
|
|
}
|
|
|
|
const Common::String &SaveGame::getText() {
|
|
return _text;
|
|
}
|
|
|
|
void SaveGame::saveInto(Common::OutSaveFile *file) {
|
|
warning("Saving the game not yet tested!");
|
|
// Save attibutes
|
|
Common::Array<AttributeGroup>::const_iterator itg;
|
|
for (itg = _groups.begin(); itg != _groups.end(); itg++) {
|
|
Common::Array<Attribute>::const_iterator ita;
|
|
for (ita = itg->begin(); ita != itg->end(); ita++) {
|
|
file->writeUint16BE((*ita));
|
|
}
|
|
}
|
|
// Save globals
|
|
Common::Array<uint16>::const_iterator global;
|
|
for (global = _globals.begin(); global != _globals.end(); global++) {
|
|
file->writeUint16BE((*global));
|
|
}
|
|
// Save text
|
|
// TODO: Insert text from GUI console
|
|
_text = "Hello";
|
|
file->write(_text.c_str(), _text.size());
|
|
}
|
|
|
|
void SaveGame::loadGroups(MacVentureEngine *engine, Common::SeekableReadStream *res) {
|
|
GlobalSettings settings = engine->getGlobalSettings();
|
|
for (int i = 0; i < settings._numGroups; ++i) {
|
|
AttributeGroup g;
|
|
for (int j = 0; j < settings._numObjects; ++j) {
|
|
g.push_back(res->readUint16BE());
|
|
}
|
|
|
|
_groups.push_back(g);
|
|
}
|
|
}
|
|
|
|
void SaveGame::loadGlobals(MacVentureEngine *engine, Common::SeekableReadStream *res) {
|
|
GlobalSettings settings = engine->getGlobalSettings();
|
|
for (int i = 0; i < settings._numGlobals; ++i) {
|
|
_globals.push_back(res->readUint16BE());
|
|
}
|
|
}
|
|
|
|
void SaveGame::loadText(MacVentureEngine *engine, Common::SeekableReadStream *res) {
|
|
// TODO: Load console text. For now, the GUI doesn't even look at this.
|
|
_text = "Placeholder Console Text";
|
|
}
|
|
|
|
|
|
} // End of namespace MacVenture
|