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