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 #include "ultima/ultima8/misc/pent_include.h"
24 #include "ultima/ultima8/world/world.h"
25 #include "ultima/ultima8/world/map.h"
26 #include "ultima/ultima8/world/current_map.h"
27 #include "ultima/ultima8/filesys/flex_file.h"
28 #include "ultima/ultima8/filesys/raw_archive.h"
29 #include "ultima/ultima8/world/item_factory.h"
30 #include "ultima/ultima8/world/actors/main_actor.h"
31 #include "ultima/ultima8/world/loop_script.h"
32 #include "ultima/ultima8/usecode/uc_list.h"
33 #include "ultima/ultima8/misc/direction_util.h"
34 #include "ultima/ultima8/games/game_data.h"
35 #include "ultima/ultima8/kernel/kernel.h"
36 #include "ultima/ultima8/kernel/object_manager.h"
37 #include "ultima/ultima8/world/camera_process.h" // for resetting the camera
38 #include "ultima/ultima8/gumps/gump.h" // For CloseItemDependents notification
39 #include "ultima/ultima8/world/get_object.h"
40 #include "ultima/ultima8/world/target_reticle_process.h"
41 #include "ultima/ultima8/audio/audio_process.h"
42 #include "ultima/ultima8/world/snap_process.h"
43 #include "ultima/ultima8/graphics/main_shape_archive.h"
44 
45 namespace Ultima {
46 namespace Ultima8 {
47 
48 //#define DUMP_ITEMS
49 
50 World *World::_world = nullptr;
51 
World()52 World::World() : _currentMap(nullptr), _alertActive(false), _difficulty(3),
53 				 _controlledNPCNum(1), _vargasShield(5000) {
54 	debugN(MM_INFO, "Creating World...\n");
55 
56 	_world = this;
57 }
58 
59 
~World()60 World::~World() {
61 	debugN(MM_INFO, "Destroying World...\n");
62 	clear();
63 
64 	_world = nullptr;
65 }
66 
67 
clear()68 void World::clear() {
69 	unsigned int i;
70 
71 	for (i = 0; i < _maps.size(); ++i) {
72 		delete _maps[i];
73 	}
74 	_maps.clear();
75 
76 	while (!_ethereal.empty())
77 		_ethereal.pop_front();
78 
79 	if (_currentMap)
80 		delete _currentMap;
81 	_currentMap = nullptr;
82 
83 	_alertActive = false;
84 	_controlledNPCNum = 1;
85 	_vargasShield = 5000;
86 }
87 
reset()88 void World::reset() {
89 	debugN(MM_INFO, "Resetting World...\n");
90 
91 	clear();
92 
93 	initMaps();
94 }
95 
initMaps()96 void World::initMaps() {
97 	// Q: How do we determine which Maps to create? Only create those
98 	// with non-zero size in fixed.dat?
99 
100 	_maps.resize(256);
101 	for (unsigned int i = 0; i < 256; ++i) {
102 		_maps[i] = new Map(i);
103 	}
104 
105 	_currentMap = new CurrentMap();
106 }
107 
switchMap(uint32 newmap)108 bool World::switchMap(uint32 newmap) {
109 	assert(_currentMap);
110 
111 	if (_currentMap->getNum() == newmap)
112 		return true;
113 
114 	if (newmap >= _maps.size() || _maps[newmap] == nullptr)
115 		return false; // no such map
116 
117 	// Map switching procedure:
118 
119 	// stop all sound effects (except speech, such as Guardian barks)
120 	// notify all gumps of a map change
121 	// delete any ethereal objects
122 	// write back CurrentMap to the old map, which
123 	//   deletes all disposable items
124 	//   deletes the EggHatcher
125 	//   resets all eggs
126 	// swap out fixed items in old map
127 	// kill all processes (except those of type 1 or of item 0)
128 	// load fixed items in new map
129 	// load new map into CurrentMap, which also
130 	//   assigns objIDs to fixed items
131 	//   assigns objIDs to nonfixed items
132 	//   creates an EggHatcher and notifies it of all eggs
133 	//   sets up all NPCs in the new map
134 	// update camera if needed
135 
136 	AudioProcess *ap = AudioProcess::get_instance();
137 	if (ap) ap->stopAllExceptSpeech();
138 
139 	// Notify all the gumps of the mapchange
140 	Ultima8Engine *gui = Ultima8Engine::get_instance();
141 	if (gui) {
142 		Gump *desktop = gui->getDesktopGump();
143 		if (desktop) desktop->CloseItemDependents();
144 	}
145 
146 	// get rid of any remaining _ethereal items
147 	while (!_ethereal.empty()) {
148 		uint16 eth = _ethereal.front();
149 		_ethereal.pop_front();
150 		Item *i = getItem(eth);
151 		if (i) i->destroy();
152 	}
153 
154 	uint32 oldmap = _currentMap->getNum();
155 	if (oldmap != 0) {
156 		perr << "Unloading map " << oldmap << Std::endl;
157 
158 		assert(oldmap < _maps.size() && _maps[oldmap] != nullptr);
159 
160 		_currentMap->writeback();
161 
162 		perr << "Unloading Fixed items from map " << oldmap << Std::endl;
163 
164 		_maps[oldmap]->unloadFixed();
165 	}
166 
167 	// Kill any processes that need killing (those with type != 1 && item != 0)
168 	Kernel::get_instance()->killProcessesNotOfType(0, 1, true);
169 
170 	pout << "Loading Fixed items in map " << newmap << Std::endl;
171 	Common::SeekableReadStream *items = GameData::get_instance()->getFixed()
172 	                     ->get_datasource(newmap);
173 	_maps[newmap]->loadFixed(items);
174 	delete items;
175 
176 	_currentMap->loadMap(_maps[newmap]);
177 
178 	// update camera if needed (u8 only)
179 	// TODO: This may not even be needed, but do it just in case the
180 	// camera was looking at something else during teleport.
181 	if (GAME_IS_U8) {
182 		CameraProcess *camera = CameraProcess::GetCameraProcess();
183 		if (camera && camera->getItemNum() != 1) {
184 			CameraProcess::SetCameraProcess(new CameraProcess(1));
185 		}
186 		CameraProcess::SetEarthquake(0);
187 	}
188 
189 	return true;
190 }
191 
loadNonFixed(Common::SeekableReadStream * rs)192 void World::loadNonFixed(Common::SeekableReadStream *rs) {
193 	FlexFile *f = new FlexFile(rs);
194 
195 	pout << "Loading NonFixed items" << Std::endl;
196 
197 	for (unsigned int i = 0; i < f->getCount(); ++i) {
198 
199 		// items in this map?
200 		if (f->getSize(i) > 0) {
201 			assert(_maps.size() > i);
202 			assert(_maps[i] != nullptr);
203 
204 			Common::SeekableReadStream *items = f->getDataSource(i);
205 
206 			_maps[i]->loadNonFixed(items);
207 
208 			delete items;
209 
210 		}
211 	}
212 
213 	delete f;
214 }
215 
loadItemCachNPCData(Common::SeekableReadStream * itemcach,Common::SeekableReadStream * npcdata)216 void World::loadItemCachNPCData(Common::SeekableReadStream *itemcach, Common::SeekableReadStream *npcdata) {
217 	FlexFile *itemcachflex = new FlexFile(itemcach);
218 	FlexFile *npcdataflex = new FlexFile(npcdata);
219 
220 	Common::SeekableReadStream *itemds = itemcachflex->getDataSource(0);
221 	Common::SeekableReadStream *npcds = npcdataflex->getDataSource(0);
222 
223 	delete itemcachflex;
224 	delete npcdataflex;
225 
226 	pout << "Loading NPCs" << Std::endl;
227 
228 	for (uint32 i = 1; i < 256; ++i) { // Get rid of constants?
229 		// These are ALL unsigned on disk
230 		itemds->seek(0x00000 + i * 2);
231 		int32 x = static_cast<int32>(itemds->readUint16LE());
232 		itemds->seek(0x04800 + i * 2);
233 		int32 y = static_cast<int32>(itemds->readUint16LE());
234 		itemds->seek(0x09000 + i * 1);
235 		int32 z = static_cast<int32>(itemds->readByte());
236 
237 		itemds->seek(0x0B400 + i * 2);
238 		uint32 shape = itemds->readUint16LE();
239 		itemds->seek(0x0FC00 + i * 1);
240 		uint32 frame = itemds->readByte();
241 		itemds->seek(0x12000 + i * 2);
242 		uint16 flags = itemds->readUint16LE();
243 		itemds->seek(0x16800 + i * 2);
244 		uint16 quality = itemds->readUint16LE();
245 		itemds->seek(0x1B000 + i * 1);
246 		uint16 npcnum = static_cast<uint8>(itemds->readByte());
247 		itemds->seek(0x1D400 + i * 1);
248 		uint16 mapnum = static_cast<uint8>(itemds->readByte());
249 		itemds->seek(0x1F800 + i * 2);
250 		//uint16 next;
251 		(void)itemds->readUint16LE();
252 
253 		// half the frame number is stored in npcdata.dat
254 		npcds->seek(7 + i * 0x31);
255 		frame += npcds->readByte() << 8;
256 
257 		if (shape == 0) {
258 			// U8's itemcach has a lot of garbage in it.
259 			// Ignore it.
260 			continue;
261 		}
262 
263 #ifdef DUMP_ITEMS
264 		pout << shape << "," << frame << ":\t(" << x << "," << y << "," << z << "),\t" << Std::hex << flags << Std::dec << ", " << quality << ", " << npcnum << ", " << mapnum << ", " << next << Std::endl;
265 #endif
266 
267 		Actor *actor = ItemFactory::createActor(shape, frame, quality,
268 		                                        flags | Item::FLG_IN_NPC_LIST,
269 		                                        npcnum, mapnum,
270 		                                        Item::EXT_PERMANENT_NPC, false);
271 		if (!actor) {
272 #ifdef DUMP_ITEMS
273 			pout << "Couldn't create actor" << Std::endl;
274 #endif
275 			continue;
276 		}
277 		ObjectManager::get_instance()->assignActorObjId(actor, i);
278 
279 		actor->setLocation(x, y, z);
280 
281 		// read npcdata:
282 		npcds->seek(i * 0x31);
283 		actor->setStr(npcds->readByte()); // 0x00: strength
284 		actor->setDex(npcds->readByte()); // 0x01: dexterity
285 		actor->setInt(npcds->readByte()); // 0x02: intelligence
286 		actor->setHP(npcds->readByte());  // 0x03: hitpoints
287 		actor->setDir(Direction_FromUsecodeDir(npcds->readByte())); // 0x04: direction
288 		uint16 la = npcds->readUint16LE();    // 0x05,0x06: last anim
289 		actor->setLastAnim(static_cast<Animation::Sequence>(la));
290 		npcds->skip(1); // 0x07: high byte of framenum
291 		npcds->skip(1); // 0x08: current anim frame
292 		npcds->skip(1); // 0x09: start Z of current fall
293 		npcds->skip(1); // 0x0A: unknown, always zero
294 		uint8 align = npcds->readByte(); // 0x0B: alignments
295 		actor->setAlignment(align & 0x0F);
296 		actor->setEnemyAlignment(align & 0xF0);
297 		actor->setUnkByte(npcds->readByte()); // 0x0C: unknown;
298 		// 0x0C is almost always zero, except for
299 		// the avatar (0xC0) and
300 		// Malchir, Vardion, Gorgrond, Beren (0xE0)
301 		npcds->skip(14); // 0x0D-0x1A: unknown, always zero
302 		actor->clearActorFlag(0xFF);
303 		actor->setActorFlag(npcds->readByte()); // 0x1B: flags
304 		npcds->skip(1);  // 0x1C: unknown, always zero
305 		npcds->skip(16); // 0x1D-0x2C: equipment
306 		int16 mana = static_cast<int16>(npcds->readUint16LE()); // 0x2D,0x2E: mana
307 		actor->setMana(mana);
308 		actor->clearActorFlag(0xFFFF00);
309 		uint32 flags2F = npcds->readByte(); // 0x2F: flags
310 		actor->setActorFlag(flags2F << 8);
311 		uint32 flags30 = npcds->readByte(); // 0x30: flags
312 		actor->setActorFlag(flags30 << 16);
313 	}
314 
315 	delete itemds;
316 	delete npcds;
317 }
318 
319 
worldStats() const320 void World::worldStats() const {
321 	unsigned int i, mapcount = 0;
322 
323 	for (i = 0; i < _maps.size(); i++) {
324 		if (_maps[i] != nullptr && !_maps[i]->isEmpty())
325 			mapcount++;
326 	}
327 
328 	g_debugger->debugPrintf("World memory stats:\n");
329 	g_debugger->debugPrintf("Maps       : %u/256\n", mapcount);
330 
331 	const Actor *av = getMainActor();
332 	g_debugger->debugPrintf("Avatar pos.: ");
333 	if (av) {
334 		g_debugger->debugPrintf("map %d, (", av->getMapNum());
335 		int32 x, y, z;
336 		av->getLocation(x, y, z);
337 		g_debugger->debugPrintf("%d,%d,%d)\n", x, y, z);
338 	} else {
339 		g_debugger->debugPrintf("missing (null)\n");
340 	}
341 }
342 
save(Common::WriteStream * ws)343 void World::save(Common::WriteStream *ws) {
344 	ws->writeUint32LE(_currentMap->getNum());
345 
346 	ws->writeUint16LE(_currentMap->_eggHatcher);
347 
348 	if (GAME_IS_CRUSADER) {
349 		ws->writeByte(_alertActive ? 1 : 0);
350 		ws->writeByte(_difficulty);
351 		ws->writeUint16LE(_controlledNPCNum);
352 		ws->writeUint32LE(_vargasShield);
353 	}
354 
355 	uint16 es = static_cast<uint16>(_ethereal.size());
356 	ws->writeUint32LE(es);
357 
358 	// empty stack and refill it again
359 	uint16 *e = new uint16[es];
360 	Std::list<ObjId>::const_iterator it = _ethereal.begin();
361 	unsigned int i;
362 	for (i = 0; i < es; ++i) {
363 		e[es - i] = *it;
364 		++it;
365 	}
366 
367 	for (i = 0; i < es; ++i) {
368 		ws->writeUint16LE(e[i]);
369 	}
370 	delete[] e;
371 }
372 
373 // load items
load(Common::ReadStream * rs,uint32 version)374 bool World::load(Common::ReadStream *rs, uint32 version) {
375 	uint16 curmapnum = rs->readUint32LE();
376 	_currentMap->setMap(_maps[curmapnum]);
377 
378 	_currentMap->_eggHatcher = rs->readUint16LE();
379 
380 	if (GAME_IS_CRUSADER) {
381 		_alertActive = (rs->readByte() != 0);
382 		_difficulty = rs->readByte();
383 		_controlledNPCNum = rs->readUint16LE();
384 		_vargasShield = rs->readUint32LE();
385 	}
386 
387 	uint32 etherealcount = rs->readUint32LE();
388 	for (unsigned int i = 0; i < etherealcount; ++i) {
389 		_ethereal.push_front(rs->readUint16LE());
390 	}
391 
392 	return true;
393 }
394 
saveMaps(Common::WriteStream * ws)395 void World::saveMaps(Common::WriteStream *ws) {
396 	ws->writeUint32LE(static_cast<uint32>(_maps.size()));
397 	for (unsigned int i = 0; i < _maps.size(); ++i) {
398 		_maps[i]->save(ws);
399 	}
400 }
401 
loadMaps(Common::ReadStream * rs,uint32 version)402 bool World::loadMaps(Common::ReadStream *rs, uint32 version) {
403 	uint32 mapcount = rs->readUint32LE();
404 
405 	// Integrity check
406 	if (mapcount > _maps.size()) {
407 		warning("Invalid mapcount in save: %d.  Corrupt save?", mapcount);
408 		return false;
409 	}
410 
411 	// Map objects have already been created by reset()
412 	for (unsigned int i = 0; i < mapcount; ++i) {
413 		bool res = _maps[i]->load(rs, version);
414 		if (!res) return false;
415 	}
416 
417 	return true;
418 }
419 
setAlertActive(bool active)420 void World::setAlertActive(bool active)
421 {
422 	assert(GAME_IS_CRUSADER);
423 	_alertActive = active;
424 
425 	if (GAME_IS_REMORSE) {
426 		setAlertActiveRemorse(active);
427 	} else {
428 		setAlertActiveRegret(active);
429 	}
430 }
431 
setAlertActiveRemorse(bool active)432 void World::setAlertActiveRemorse(bool active)
433 {
434 	// Replicate the behavior of the original game.
435 	LOOPSCRIPT(script,
436 		LS_OR(
437 			LS_OR(
438 				LS_OR(
439 					LS_OR(LS_SHAPE_EQUAL(0x49), LS_SHAPE_EQUAL(0x21)),
440 					LS_SHAPE_EQUAL(0x174)),
441 				LS_SHAPE_EQUAL(0x271)),
442 			  LS_SHAPE_EQUAL(0x477))
443 	);
444 
445 	UCList itemlist(2);
446 	_world->getCurrentMap()->areaSearch(&itemlist, script, sizeof(script),
447 										nullptr, 0xffff, false);
448 	for (uint32 i = 0; i < itemlist.getSize(); i++) {
449 		uint16 itemid = itemlist.getuint16(i);
450 		Item *item = getItem(itemid);
451 		assert(item);
452 		int frame = item->getFrame();
453 		if (_alertActive) {
454 			if (item->getShape() == 0x477) {
455 				if (frame < 2)
456 					item->setFrame(frame + 2);
457 			} else if (frame == 0) {
458 				item->setFrame(1);
459 			}
460 		} else {
461 			if (item->getShape() == 0x477) {
462 				if (frame > 1)
463 					item->setFrame(frame - 2);
464 			} else if (frame == 1) {
465 				item->setFrame(0);
466 			}
467 		}
468 	}
469 }
470 
setAlertActiveRegret(bool active)471 void World::setAlertActiveRegret(bool active)
472 {
473 	setAlertActiveRemorse(active);
474 
475 	LOOPSCRIPT(offscript, LS_OR(LS_SHAPE_EQUAL(0x660), LS_SHAPE_EQUAL(0x661)));
476 	LOOPSCRIPT(onscript, LS_OR(LS_SHAPE_EQUAL(0x662), LS_SHAPE_EQUAL(0x663)));
477 
478 	const uint8 *script = active ? onscript : offscript;
479 	// note: size should be the same, but just to be explicit.
480 	int scriptlen = active ? sizeof(onscript) : sizeof(offscript);
481 
482 	UCList itemlist(2);
483 	_world->getCurrentMap()->areaSearch(&itemlist, script, scriptlen,
484 										nullptr, 0xffff, false);
485 	for (uint32 i = 0; i < itemlist.getSize(); i++) {
486 		uint16 itemid = itemlist.getuint16(i);
487 		Item *item = getItem(itemid);
488 		assert(item);
489 		switch (item->getShape()) {
490 			case 0x660:
491 				item->setShape(0x663);
492 				break;
493 			case 0x661:
494 				item->setShape(0x662);
495 				break;
496 			case 0x662:
497 				item->setShape(0x661);
498 				break;
499 			case 0x663:
500 				item->setShape(0x660);
501 				break;
502 			default:
503 				warning("unexpected shape %d returned from search", item->getShape());
504 				break;
505 		}
506 		item->setFrame(0);
507 	}
508 }
509 
setGameDifficulty(uint8 difficulty)510 void World::setGameDifficulty(uint8 difficulty) {
511    _difficulty = difficulty;
512    if (GAME_IS_REMORSE) {
513 	   // HACK: Set ammo data for BA-40 in higher 2 difficulty levels
514 	   // This would be better handled in the ini file somehow?
515 	   const ShapeInfo *si = GameData::get_instance()->getMainShapes()->getShapeInfo(0x32E);
516 	   if (si && si->_weaponInfo) {
517 		   WeaponInfo *wi = si->_weaponInfo;
518 		   wi->_clipSize = 20;
519 		   if (difficulty > 1) {
520 			   wi->_ammoShape = 0x33D;
521 			   wi->_ammoType = 1;
522 		   } else {
523 			   wi->_ammoShape = 0;
524 			   wi->_ammoType = 0;
525 		   }
526 	   }
527    }
528 }
529 
530 
setControlledNPCNum(uint16 num)531 void World::setControlledNPCNum(uint16 num) {
532 	uint16 oldnpc = _controlledNPCNum;
533 	_controlledNPCNum = num;
534 	Actor *previous = getActor(oldnpc);
535 	if (previous && !previous->isDead() && previous->isInCombat()) {
536 		previous->clearInCombat();
537 	}
538 
539 	Actor *controlled = getActor(num);
540 	if (controlled) {
541 		if (num != 1) {
542 			Kernel::get_instance()->killProcesses(num, Kernel::PROC_TYPE_ALL, true);
543 			if (controlled->isInCombat())
544 				controlled->clearInCombat();
545 		}
546 		int32 x, y, z;
547 		controlled->getCentre(x, y, z);
548 		CameraProcess::SetCameraProcess(new CameraProcess(x, y, z));
549 	}
550 
551 	TargetReticleProcess *t = TargetReticleProcess::get_instance();
552 	if (t) {
553 		t->avatarMoved();
554 	}
555 }
556 
557 
I_getAlertActive(const uint8 *,unsigned int)558 uint32 World::I_getAlertActive(const uint8 * /*args*/,
559 	unsigned int /*argsize*/) {
560 	return get_instance()->_world->isAlertActive() ? 1 : 0;
561 }
562 
I_setAlertActive(const uint8 *,unsigned int)563 uint32 World::I_setAlertActive(const uint8 * /*args*/,
564 	unsigned int /*argsize*/) {
565 	get_instance()->_world->setAlertActive(true);
566 	return 0;
567 }
568 
I_clrAlertActive(const uint8 *,unsigned int)569 uint32 World::I_clrAlertActive(const uint8 * /*args*/,
570 	unsigned int /*argsize*/) {
571 	get_instance()->_world->setAlertActive(false);
572 	return 0;
573 }
574 
I_gameDifficulty(const uint8 *,unsigned int)575 uint32 World::I_gameDifficulty(const uint8 * /*args*/,
576 	unsigned int /*argsize*/) {
577 	return get_instance()->_world->getGameDifficulty();
578 }
579 
I_getControlledNPCNum(const uint8 *,unsigned int)580 uint32 World::I_getControlledNPCNum(const uint8 * /*args*/,
581 	unsigned int /*argsize*/) {
582 	return get_instance()->_world->getControlledNPCNum();
583 }
584 
I_setControlledNPCNum(const uint8 * args,unsigned int)585 uint32 World::I_setControlledNPCNum(const uint8 *args,
586 	unsigned int /*argsize*/) {
587 	ARG_UINT16(num);
588 	get_instance()->_world->setControlledNPCNum(num);
589 	return 0;
590 }
591 
I_resetVargasShield(const uint8 *,unsigned int)592 uint32 World::I_resetVargasShield(const uint8 * /*args*/,
593 	unsigned int /*argsize*/) {
594 	get_instance()->setVargasShield(500);
595 	return 0;
596 }
597 
598 } // End of namespace Ultima8
599 } // End of namespace Ultima
600