1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 /*
24 * Based on
25 * WebVenture (c) 2010, Sean Kasun
26 * https://github.com/mrkite/webventure, http://seancode.com/webventure/
27 *
28 * Used with explicit permission from the author
29 */
30
31 #include "macventure/world.h"
32 #include "macventure/macventure.h"
33
34 #include "common/file.h"
35
36 namespace MacVenture {
37
World(MacVentureEngine * engine,Common::MacResManager * resMan)38 World::World(MacVentureEngine *engine, Common::MacResManager *resMan) {
39 _resourceManager = resMan;
40 _engine = engine;
41 _saveGame = NULL;
42 _gameText = NULL;
43
44 startNewGame();
45
46 _objectConstants = new Container(_engine->getFilePath(kObjectPathID));
47 calculateObjectRelations();
48
49 _gameText = new Container(_engine->getFilePath(kTextPathID));
50 }
51
52
~World()53 World::~World() {
54
55 if (_saveGame)
56 delete _saveGame;
57
58 if (_objectConstants)
59 delete _objectConstants;
60
61 if (_gameText)
62 delete _gameText;
63 }
64
startNewGame()65 void World::startNewGame() {
66 if (_saveGame)
67 delete _saveGame;
68
69 if ((_startGameFileName = _engine->getStartGameFileName()) == "")
70 error("WORLD: Could not load initial game configuration");
71
72 Common::File saveGameFile;
73 if (!saveGameFile.open(_startGameFileName))
74 error("WORLD: Could not load initial game configuration");
75
76 debugC(2, kMVDebugMain, "Loading save game state from %s", _startGameFileName.c_str());
77 Common::SeekableReadStream *saveGameRes = saveGameFile.readStream(saveGameFile.size());
78
79 _saveGame = new SaveGame(_engine, saveGameRes);
80
81 calculateObjectRelations();
82
83 delete saveGameRes;
84 saveGameFile.close();
85 }
86
getObjAttr(ObjID objID,uint32 attrID)87 uint32 World::getObjAttr(ObjID objID, uint32 attrID) {
88 uint res;
89 uint32 index = _engine->getGlobalSettings()._attrIndices[attrID];
90 // HACK, but if I try to initialize it in the else clause, it goes out of scope and segfaults
91 Common::SeekableReadStream *objStream = _objectConstants->getItem(objID);
92 if (!(index & 0x80)) { // It's not a constant
93 res = _saveGame->getAttr(objID, index);
94 } else {
95 index &= 0x7F;
96 if (objStream->size() == 0) {
97 return 0;
98 }
99 // Look for the right attribute inside the object
100 objStream->skip(index * 2);
101 res = objStream->readByte() << 8;
102 res |= objStream->readByte();
103 }
104 res &= _engine->getGlobalSettings()._attrMasks[attrID];
105 res >>= _engine->getGlobalSettings()._attrShifts[attrID];
106 if (res & 0x8000)
107 res = -((int)((res ^ 0xffff) + 1));
108 debugC(5, kMVDebugMain, "Attribute %x from object %x is %x", attrID, objID, res);
109 delete objStream;
110 return res;
111 }
112
setObjAttr(ObjID objID,uint32 attrID,Attribute value)113 void World::setObjAttr(ObjID objID, uint32 attrID, Attribute value) {
114 if (attrID == kAttrPosX || attrID == kAttrPosY) {
115 // Round to scale
116 // Intentionally empty, we don't seem to require this functionality
117 }
118
119 if (attrID == kAttrParentObject)
120 setParent(objID, value);
121
122 if (attrID < kAttrOtherDoor)
123 _engine->enqueueObject(kUpdateObject, objID);
124
125 uint32 idx = _engine->getGlobalSettings()._attrIndices[attrID];
126 value <<= _engine->getGlobalSettings()._attrShifts[attrID];
127 value &= _engine->getGlobalSettings()._attrMasks[attrID];
128 Attribute oldVal = _saveGame->getAttr(objID, idx);
129 oldVal &= ~_engine->getGlobalSettings()._attrMasks[attrID];
130 _saveGame->setAttr(idx, objID, (value | oldVal));
131 _engine->gameChanged();
132 }
133
isObjActive(ObjID obj)134 bool MacVenture::World::isObjActive(ObjID obj) {
135 ObjID destObj = _engine->getDestObject();
136 Common::Point p = _engine->getDeltaPoint();
137 ControlAction selectedControl = _engine->getSelectedControl();
138 if (!getAncestor(obj)) {
139 return false; // If our ancestor is the garbage (obj 0), we're inactive
140 }
141 if (_engine->getInvolvedObjects() >= 2 && // If (we need > 1 objs for the command) &&
142 destObj > 0 && // we have a destination object &&
143 !getAncestor(destObj)) { // but that destination object is in the garbage
144 return false;
145 }
146 if (selectedControl != kMoveObject) {
147 return true; // We only need one
148 }
149 // Handle move object
150 if (!isObjDraggable(obj)) {
151 return false; // We can't move it
152 }
153 if (getObjAttr(1, kAttrParentObject) != destObj) {
154 return true; // if the target is not the player's parent, we can go
155 }
156 Common::Rect rect(kScreenWidth, kScreenHeight);
157 rect.top -= getObjAttr(obj, kAttrPosY) + p.y;
158 rect.left -= getObjAttr(obj, kAttrPosX) + p.x;
159 return intersects(obj, rect);
160 }
161
getAncestor(ObjID objID)162 ObjID World::getAncestor(ObjID objID) {
163 ObjID root = getObjAttr(1, kAttrParentObject);
164 while (objID != 0 && objID != 1 && objID != root) {
165 objID = getObjAttr(objID, kAttrParentObject);
166 }
167 return objID;
168 }
169
getFamily(ObjID objID,bool recursive)170 Common::Array<ObjID> World::getFamily(ObjID objID, bool recursive) {
171 Common::Array<ObjID> res;
172 res.push_back(objID);
173 res.push_back(getChildren(objID, recursive));
174 return res;
175 }
176
getChildren(ObjID objID,bool recursive)177 Common::Array<ObjID> World::getChildren(ObjID objID, bool recursive) {
178 Common::Array<ObjID> res;
179 ObjID child = _relations[objID * 2];
180 while (child) {
181 res.push_back(child);
182 if (!recursive)
183 res.push_back(getChildren(child, false));
184 child = _relations[child * 2 + 1];
185 }
186 return res;
187 }
188
getGlobal(uint32 attrID)189 Attribute World::getGlobal(uint32 attrID) {
190 return _saveGame->getGlobals()[attrID];
191 }
192
setGlobal(uint32 attrID,Attribute value)193 void World::setGlobal(uint32 attrID, Attribute value) {
194 _saveGame->setGlobal(attrID, value);
195 }
196
updateObj(ObjID objID)197 void World::updateObj(ObjID objID) {
198 WindowReference win;
199 if (getObjAttr(1, kAttrParentObject) == objID) {
200 win = kMainGameWindow;
201 } else {
202 win = _engine->getObjWindow(objID);
203 }
204 if (win) {
205 _engine->focusObjWin(objID);
206 _engine->runObjQueue();
207 _engine->updateWindow(win);
208 }
209 }
210
captureChildren(ObjID objID)211 void World::captureChildren(ObjID objID) {
212 warning("Capture children unimplemented!");
213 }
214
releaseChildren(ObjID objID)215 void World::releaseChildren(ObjID objID) {
216 warning("Release children unimplemented!");
217 }
218
getText(ObjID objID,ObjID source,ObjID target)219 Common::String World::getText(ObjID objID, ObjID source, ObjID target) {
220 if (objID & 0x8000) {
221 return _engine->getUserInput();
222 }
223 TextAsset text = TextAsset(_engine, objID, source, target, _gameText, _engine->isOldText(), _engine->getDecodingHuffman());
224
225 return *text.decode();
226 }
227
228
isObjDraggable(ObjID objID)229 bool World::isObjDraggable(ObjID objID) {
230 return (getObjAttr(objID, kAttrInvisible) == 0 &&
231 getObjAttr(objID, kAttrUnclickable) == 0 &&
232 getObjAttr(objID, kAttrUndraggable) == 0);
233 }
234
intersects(ObjID objID,Common::Rect rect)235 bool World::intersects(ObjID objID, Common::Rect rect) {
236 return _engine->getObjBounds(objID).intersects(rect);
237 }
238
calculateObjectRelations()239 void World::calculateObjectRelations() {
240 _relations.clear();
241 ObjID val, next;
242 uint32 numObjs = _engine->getGlobalSettings()._numObjects;
243 const AttributeGroup &parents = *_saveGame->getGroup(0);
244 for (uint i = 0; i < numObjs * 2; i++) {
245 _relations.push_back(0);
246 }
247 for (uint i = numObjs - 1; i > 0; i--) {
248 val = parents[i];
249 next = _relations[val * 2];
250 if (next) {
251 _relations[i * 2 + 1] = next;
252 }
253 _relations[val * 2] = i;
254 }
255 }
256
setParent(ObjID child,ObjID newParent)257 void World::setParent(ObjID child, ObjID newParent) {
258 ObjID old = _saveGame->getAttr(child, kAttrParentObject);
259 if (newParent == child)
260 return;
261
262 ObjID oldNdx = old * 2;
263 old = _relations[oldNdx];
264 while (old != child) {
265 oldNdx = (old * 2) + 1;
266 old = _relations[oldNdx];
267 }
268 _relations[oldNdx] = _relations[(old * 2) + 1];
269 oldNdx = newParent * 2;
270 old = _relations[oldNdx];
271 while (old && old <= child) {
272 oldNdx = (old * 2) + 1;
273 old = _relations[oldNdx];
274 }
275 _relations[child * 2 + 1] = old;
276 _relations[oldNdx] = child;
277 }
278
loadGameFrom(Common::InSaveFile * file)279 void World::loadGameFrom(Common::InSaveFile *file) {
280 if (_saveGame) {
281 delete _saveGame;
282 }
283 _saveGame = new SaveGame(_engine, file);
284 calculateObjectRelations();
285 }
286
saveGameInto(Common::OutSaveFile * file)287 void World::saveGameInto(Common::OutSaveFile *file) {
288 _saveGame->saveInto(file);
289 }
290
291 // SaveGame
SaveGame(MacVentureEngine * engine,Common::SeekableReadStream * res)292 SaveGame::SaveGame(MacVentureEngine *engine, Common::SeekableReadStream *res) {
293 _groups = Common::Array<AttributeGroup>();
294 loadGroups(engine, res);
295 _globals = Common::Array<uint16>();
296 loadGlobals(engine, res);
297 _text = Common::String();
298 loadText(engine, res);
299 }
300
~SaveGame()301 SaveGame::~SaveGame() {
302 }
303
304
getAttr(ObjID objID,uint32 attrID)305 Attribute SaveGame::getAttr(ObjID objID, uint32 attrID) {
306 return _groups[attrID][objID];
307 }
308
setAttr(uint32 attrID,ObjID objID,Attribute value)309 void SaveGame::setAttr(uint32 attrID, ObjID objID, Attribute value) {
310 _groups[attrID][objID] = value;
311 }
312
getGroups()313 const Common::Array<AttributeGroup> &MacVenture::SaveGame::getGroups() {
314 return _groups;
315 }
316
getGroup(uint32 groupID)317 const AttributeGroup *SaveGame::getGroup(uint32 groupID) {
318 assert(groupID < _groups.size());
319 return &(_groups[groupID]);
320 }
321
setGlobal(uint32 attrID,Attribute value)322 void SaveGame::setGlobal(uint32 attrID, Attribute value) {
323 _globals[attrID] = value;
324 }
325
getGlobals()326 const Common::Array<uint16> &SaveGame::getGlobals() {
327 return _globals;
328 }
329
getText()330 const Common::String &SaveGame::getText() {
331 return _text;
332 }
333
saveInto(Common::OutSaveFile * file)334 void SaveGame::saveInto(Common::OutSaveFile *file) {
335 warning("Saving the game not yet tested!");
336 // Save attibutes
337 Common::Array<AttributeGroup>::const_iterator itg;
338 for (itg = _groups.begin(); itg != _groups.end(); itg++) {
339 Common::Array<Attribute>::const_iterator ita;
340 for (ita = itg->begin(); ita != itg->end(); ita++) {
341 file->writeUint16BE((*ita));
342 }
343 }
344 // Save globals
345 Common::Array<uint16>::const_iterator global;
346 for (global = _globals.begin(); global != _globals.end(); global++) {
347 file->writeUint16BE((*global));
348 }
349 // Save text
350 // TODO: Insert text from GUI console
351 _text = "Hello";
352 file->write(_text.c_str(), _text.size());
353 }
354
loadGroups(MacVentureEngine * engine,Common::SeekableReadStream * res)355 void SaveGame::loadGroups(MacVentureEngine *engine, Common::SeekableReadStream *res) {
356 GlobalSettings settings = engine->getGlobalSettings();
357 for (int i = 0; i < settings._numGroups; ++i) {
358 AttributeGroup g;
359 for (int j = 0; j < settings._numObjects; ++j) {
360 g.push_back(res->readUint16BE());
361 }
362
363 _groups.push_back(g);
364 }
365 }
366
loadGlobals(MacVentureEngine * engine,Common::SeekableReadStream * res)367 void SaveGame::loadGlobals(MacVentureEngine *engine, Common::SeekableReadStream *res) {
368 GlobalSettings settings = engine->getGlobalSettings();
369 for (int i = 0; i < settings._numGlobals; ++i) {
370 _globals.push_back(res->readUint16BE());
371 }
372 }
373
loadText(MacVentureEngine * engine,Common::SeekableReadStream * res)374 void SaveGame::loadText(MacVentureEngine *engine, Common::SeekableReadStream *res) {
375 // TODO: Load console text. For now, the GUI doesn't even look at this.
376 _text = "Placeholder Console Text";
377 }
378
379
380 } // End of namespace MacVenture
381