File: object_manager.cpp

package info (click to toggle)
scummvm 2.9.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 450,580 kB
  • sloc: cpp: 4,299,825; asm: 28,322; python: 12,901; sh: 11,302; java: 9,289; xml: 7,895; perl: 2,639; ansic: 2,465; yacc: 1,670; javascript: 1,020; makefile: 933; lex: 578; awk: 275; objc: 82; sed: 11; php: 1
file content (393 lines) | stat: -rw-r--r-- 11,814 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/* 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 "ultima/ultima8/misc/debugger.h"
#include "ultima/ultima8/kernel/object_manager.h"
#include "ultima/ultima8/misc/id_man.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/monster_egg.h"
#include "ultima/ultima8/world/teleport_egg.h"
#include "ultima/ultima8/world/glob_egg.h"
#include "ultima/ultima8/gumps/ask_gump.h"
#include "ultima/ultima8/gumps/bark_gump.h"
#include "ultima/ultima8/gumps/paperdoll_gump.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/gumps/widgets/sliding_widget.h"
#include "ultima/ultima8/gumps/mini_stats_gump.h"
#include "ultima/ultima8/gumps/minimap_gump.h"
#include "ultima/ultima8/gumps/cru_status_gump.h"
#include "ultima/ultima8/gumps/cru_pickup_gump.h"
#include "ultima/ultima8/gumps/cru_pickup_area_gump.h"
#include "ultima/ultima8/gumps/translucent_gump.h"

namespace Ultima {
namespace Ultima8 {

ObjectManager *ObjectManager::_objectManager = nullptr;


// a template class  to prevent having to write a load function for
// every object separately
template<class T>
struct ObjectLoader {
	static Object *load(Common::ReadStream *rs, uint32 version) {
		T *p = new T();
		bool ok = p->loadData(rs, version);
		if (!ok) {
			delete p;
			p = nullptr;
		}
		return p;
	}
};

ObjectManager::ObjectManager() {
	debug(1, "Creating ObjectManager...");

	_objectManager = this;

	setupLoaders();

	_objects.resize(65536);

	//!CONSTANTS
	_objIDs = new idMan(256, 32766, 8192); // Want range of 256 to 32766
	_actorIDs = new idMan(1, 255, 255);
}

ObjectManager::~ObjectManager() {
	reset();
	debug(1, "Destroying ObjectManager...");

	_objectManager = nullptr;

	delete _objIDs;
	delete _actorIDs;
}

void ObjectManager::reset() {
	debug(1, "Resetting ObjectManager...");

	unsigned int i;

	for (i = 0; i < _objects.size(); ++i) {
		if (_objects[i] == nullptr) continue;
#if 0
		Item *item = dynamic_cast<Item *>(_objects[i]);
		if (item && item->getParent()) continue; // will be deleted by parent
#endif
		Gump *gump = dynamic_cast<Gump *>(_objects[i]);
		if (gump && gump->GetParent()) continue; // will be deleted by parent
		delete _objects[i];
	}

	for (i = 0; i < _objects.size(); ++i) {
		assert(_objects[i] == nullptr);
	}


	//!CONSTANTS
	_objects.clear();
	_objects.resize(65536);
	_objIDs->clearAll(32766);
	_objIDs->reserveID(kGuardianId);     // Reserved for the Guardian Bark hack
	_actorIDs->clearAll();
}

void ObjectManager::objectStats() {
	unsigned int i, npccount = 0, objcount = 0;

	//!constants
	for (i = 1; i < 256; i++) {
		if (_objects[i] != nullptr)
			npccount++;
	}
	for (i = 256; i < _objects.size(); i++) {
		if (_objects[i] != nullptr)
			objcount++;
	}

	g_debugger->debugPrintf("Object memory stats:\n");
	g_debugger->debugPrintf("NPCs       : %u/255\n", npccount);
	g_debugger->debugPrintf("Objects    : %u/32511\n", objcount);
}

void ObjectManager::objectTypes() {
	g_debugger->debugPrintf("Current object types:\n");
	Common::HashMap<Common::String, unsigned int> objecttypes;
	for (unsigned int i = 1; i < _objects.size(); ++i) {
		Object *o = _objects[i];
		if (!o) continue;
		objecttypes[o->GetClassType()._className]++;
	}

	Common::HashMap<Common::String, unsigned int>::const_iterator iter;
	for (iter = objecttypes.begin(); iter != objecttypes.end(); ++iter) {
		g_debugger->debugPrintf("%s: %u\n", (*iter)._key.c_str(), (*iter)._value);
	}
}

uint16 ObjectManager::assignObjId(Object *obj, ObjId new_objid) {
	if (new_objid == 0xFFFF)
		new_objid = _objIDs->getNewID();
	else
		_objIDs->reserveID(new_objid);

	// failure???
	if (new_objid != 0) {
		assert(_objects[new_objid] == nullptr);
		_objects[new_objid] = obj;
	}
	return new_objid;
}

uint16 ObjectManager::assignActorObjId(Actor *actor, ObjId new_objid) {
	if (new_objid == 0xFFFF)
		new_objid = _actorIDs->getNewID();
	else
		_actorIDs->reserveID(new_objid);

	// failure???
	if (new_objid != 0) {
		assert(_objects[new_objid] == 0);
		_objects[new_objid] = actor;
	}
	return new_objid;
}

bool ObjectManager::reserveObjId(ObjId objid) {
	if (objid >= 256) // !constant
		return _objIDs->reserveID(objid);
	else
		return _actorIDs->reserveID(objid);
}

void ObjectManager::clearObjId(ObjId objid) {
	// need to make this assert check only permanent NPCs
//	assert(objid >= 256); // !constant
	if (objid >= 256) // !constant
		_objIDs->clearID(objid);
	else
		_actorIDs->clearID(objid);

	_objects[objid] = nullptr;
}

Object *ObjectManager::getObject(ObjId objid) const {
	return _objects[objid];
}

void ObjectManager::allow64kObjects() {
	_objIDs->setNewMax(65534);
}


void ObjectManager::save(Common::WriteStream *ws) {
	_objIDs->save(ws);
	_actorIDs->save(ws);

	for (unsigned int i = 0; i < _objects.size(); ++i) {
		Object *object = _objects[i];
		if (!object) continue;

		// child items/gumps are saved by their parent.
		Item *item = dynamic_cast<Item *>(object);
		if (item && item->getParent()) continue;
		Gump *gump = dynamic_cast<Gump *>(object);

		// don't save Gumps with DONT_SAVE and Gumps with parents, unless
		// the parent is a core gump
		// FIXME: This leaks _objIDs. See comment in ObjectManager::load().
		if (gump && !gump->mustSave(true)) continue;

		saveObject(ws, object);
	}

	ws->writeUint16LE(0);
}


bool ObjectManager::load(Common::ReadStream *rs, uint32 version) {
	if (!_objIDs->load(rs, version)) return false;
	if (!_actorIDs->load(rs, version)) return false;

	do {
		// peek ahead for terminator
		uint16 classlen = rs->readUint16LE();
		if (classlen == 0) break;
		char *buf = new char[classlen + 1];
		rs->read(buf, classlen);
		buf[classlen] = 0;

		Std::string classname = buf;
		delete[] buf;

		Object *obj = loadObject(rs, classname, version);
		if (!obj) return false;

		// top level gumps have to be added to the correct core gump
		Gump *gump = dynamic_cast<Gump *>(obj);
		if (gump) {
			assert(gump->GetParent() == nullptr);
			Ultima8Engine::get_instance()->addGump(gump);
		}

	} while (true);

	// ObjectManager::save() doesn't save Gumps with the DONT_SAVE flag, but
	// their IDs are still marked in use in _objIDs.
	// As a workaround, we clear all IDs still in use without actual _objects.
	// We only do this with IDs >= 1024 because below there are truly reserved
	// _objIDs (up to 511 is reserved by U8Game, 666 is reserved for Guardian
	// barks).
	// FIXME: Properly fix this objID leak and increment the savegame number.
	//        This check can then be turned into a savegame corruption check
	//        for saves with the new savegame version.
	// We also fail loading when we're out of _objIDs since this could
	// have caused serious issues when critical _objects haven't been created.
	if (_objIDs->isFull()) {
		warning("Savegame has been corrupted by running out of _objIDs.");
		return false;
	}
	unsigned int count = 0;
	for (unsigned int i = 1024; i < _objects.size(); i++) {
		if (_objects[i] == nullptr && _objIDs->isIDUsed(i)) {
			_objIDs->clearID(i);
			count++;
		}
	}
	debug(1, "Reclaimed %u _objIDs on load.", count);

	// Integrity check items - their ids should match, and if they have
	// parents, those should be valid.
	for (unsigned int i = 0; i < _objects.size(); i++) {
		if (!_objects[i])
			continue;
		const Object *obj = _objects[i];
		ObjId oid = obj->getObjId();
		if (oid != i) {
			warning("Corrupt save? Object %d thinks its id is %d", i, oid);
			return false;
		}

		const Item *it = dynamic_cast<const Item *>(obj);
		if (it) {
			ObjId parent = it->getParent();
			if (parent && !_objects[parent]) {
				warning("Corrupt save? Object %d has parent %d which no longer exists", i, parent);
				return false;
			}
		}
	}

	return true;
}

void ObjectManager::saveObject(Common::WriteStream *ws, Object *obj) const {
	const Std::string & classname = obj->GetClassType()._className; // note: virtual

	Common::HashMap<Common::String, ObjectLoadFunc>::iterator iter;
	iter = _objectLoaders.find(classname);
	if (iter == _objectLoaders.end()) {
		error("Object class cannot save without registered loader: %s", classname.c_str());
	}

	ws->writeUint16LE(classname.size());
	ws->write(classname.c_str(), classname.size());
	obj->saveData(ws);
}

Object *ObjectManager::loadObject(Common::ReadStream *rs, uint32 version) {
	uint16 classlen = rs->readUint16LE();
	char *buf = new char[classlen + 1];
	rs->read(buf, classlen);
	buf[classlen] = 0;

	Std::string classname = buf;
	delete[] buf;

	return loadObject(rs, classname, version);
}

Object *ObjectManager::loadObject(Common::ReadStream *rs, Std::string classname,
								  uint32 version) {
	Common::HashMap<Common::String, ObjectLoadFunc>::iterator iter;
	iter = _objectLoaders.find(classname);

	if (iter == _objectLoaders.end()) {
		warning("Unknown Object class: %s", classname.c_str());
		return nullptr;
	}

	Object *obj = (*(iter->_value))(rs, version);

	if (!obj) {
		warning("Error loading object of type %s", classname.c_str());
		return nullptr;
	}
	uint16 objid = obj->getObjId();

	if (objid != 0xFFFF) {
		_objects[objid] = obj;
		bool used;
		if (objid >= 256)
			used = _objIDs->isIDUsed(objid);
		else
			used = _actorIDs->isIDUsed(objid);
		if (!used) {
			warning("Error: object ID %u used but marked available.", objid);
			return nullptr;
		}
	}

	return obj;
}

void ObjectManager::setupLoaders() {
	addObjectLoader("Item", ObjectLoader<Item>::load);
	addObjectLoader("Container", ObjectLoader<Container>::load);
	addObjectLoader("Actor", ObjectLoader<Actor>::load);
	addObjectLoader("MainActor", ObjectLoader<MainActor>::load);
	addObjectLoader("Egg", ObjectLoader<Egg>::load);
	addObjectLoader("MonsterEgg", ObjectLoader<MonsterEgg>::load);
	addObjectLoader("TeleportEgg", ObjectLoader<TeleportEgg>::load);
	addObjectLoader("GlobEgg", ObjectLoader<GlobEgg>::load);
	addObjectLoader("Gump", ObjectLoader<Gump>::load);
	addObjectLoader("ItemRelativeGump", ObjectLoader<ItemRelativeGump>::load);
	addObjectLoader("AskGump", ObjectLoader<AskGump>::load);
	addObjectLoader("BarkGump", ObjectLoader<BarkGump>::load);
	addObjectLoader("ContainerGump", ObjectLoader<ContainerGump>::load);
	addObjectLoader("PaperdollGump", ObjectLoader<PaperdollGump>::load);
	addObjectLoader("TextWidget", ObjectLoader<TextWidget>::load);
	addObjectLoader("ButtonWidget", ObjectLoader<ButtonWidget>::load);
	addObjectLoader("SlidingWidget", ObjectLoader<SlidingWidget>::load);
	addObjectLoader("MiniStatsGump", ObjectLoader<MiniStatsGump>::load);
	addObjectLoader("MiniMapGump", ObjectLoader<MiniMapGump>::load);
	addObjectLoader("CruStatusGump", ObjectLoader<CruStatusGump>::load);
	addObjectLoader("CruPickupAreaGump", ObjectLoader<CruPickupAreaGump>::load);
	addObjectLoader("CruPickupGump", ObjectLoader<CruPickupGump>::load);
	addObjectLoader("TranslucentGump", ObjectLoader<TranslucentGump>::load);
}

} // End of namespace Ultima8
} // End of namespace Ultima