614 lines
17 KiB
C++
614 lines
17 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 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/world/world.h"
|
|
#include "ultima/ultima8/world/map.h"
|
|
#include "ultima/ultima8/world/current_map.h"
|
|
#include "ultima/ultima8/filesys/flex_file.h"
|
|
#include "ultima/ultima8/filesys/raw_archive.h"
|
|
#include "ultima/ultima8/world/item_factory.h"
|
|
#include "ultima/ultima8/world/actors/main_actor.h"
|
|
#include "ultima/ultima8/world/actors/scheduler_process.h"
|
|
#include "ultima/ultima8/world/loop_script.h"
|
|
#include "ultima/ultima8/usecode/uc_list.h"
|
|
#include "ultima/ultima8/misc/direction_util.h"
|
|
#include "ultima/ultima8/games/game_data.h"
|
|
#include "ultima/ultima8/kernel/kernel.h"
|
|
#include "ultima/ultima8/kernel/object_manager.h"
|
|
#include "ultima/ultima8/world/camera_process.h" // for resetting the camera
|
|
#include "ultima/ultima8/gumps/gump.h" // For CloseItemDependents notification
|
|
#include "ultima/ultima8/world/get_object.h"
|
|
#include "ultima/ultima8/world/target_reticle_process.h"
|
|
#include "ultima/ultima8/audio/audio_process.h"
|
|
#include "ultima/ultima8/world/snap_process.h"
|
|
#include "ultima/ultima8/gfx/main_shape_archive.h"
|
|
|
|
namespace Ultima {
|
|
namespace Ultima8 {
|
|
|
|
//#define DUMP_ITEMS
|
|
|
|
World *World::_world = nullptr;
|
|
|
|
World::World() : _currentMap(nullptr), _alertActive(false), _difficulty(3),
|
|
_controlledNPCNum(1), _vargasShield(5000) {
|
|
debug(1, "Creating World...");
|
|
|
|
_world = this;
|
|
}
|
|
|
|
|
|
World::~World() {
|
|
debug(1, "Destroying World...");
|
|
clear();
|
|
|
|
_world = nullptr;
|
|
}
|
|
|
|
|
|
void World::clear() {
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < _maps.size(); ++i) {
|
|
delete _maps[i];
|
|
}
|
|
_maps.clear();
|
|
|
|
_ethereal.clear();
|
|
|
|
if (_currentMap)
|
|
delete _currentMap;
|
|
_currentMap = nullptr;
|
|
|
|
_alertActive = false;
|
|
_controlledNPCNum = 1;
|
|
_vargasShield = 5000;
|
|
}
|
|
|
|
void World::reset() {
|
|
debug(1, "Resetting World...");
|
|
|
|
clear();
|
|
|
|
initMaps();
|
|
}
|
|
|
|
void World::initMaps() {
|
|
// Q: How do we determine which Maps to create? Only create those
|
|
// with non-zero size in fixed.dat?
|
|
|
|
_maps.resize(256);
|
|
for (unsigned int i = 0; i < 256; ++i) {
|
|
_maps[i] = new Map(i);
|
|
}
|
|
|
|
_currentMap = new CurrentMap();
|
|
}
|
|
|
|
bool World::switchMap(uint32 newmap) {
|
|
assert(_currentMap);
|
|
|
|
if (_currentMap->getNum() == newmap)
|
|
return true;
|
|
|
|
if (newmap >= _maps.size() || _maps[newmap] == nullptr)
|
|
return false; // no such map
|
|
|
|
// Map switching procedure:
|
|
|
|
// stop all sound effects (except speech, such as Guardian barks)
|
|
// notify all gumps of a map change
|
|
// delete any ethereal objects
|
|
// write back CurrentMap to the old map, which
|
|
// deletes all disposable items
|
|
// deletes the EggHatcher
|
|
// resets all eggs
|
|
// swap out fixed items in old map
|
|
// kill all processes (except those of type 1 or of item 0)
|
|
// load fixed items in new map
|
|
// load new map into CurrentMap, which also
|
|
// assigns objIDs to fixed items
|
|
// assigns objIDs to nonfixed items
|
|
// creates an EggHatcher and notifies it of all eggs
|
|
// sets up all NPCs in the new map
|
|
// update camera if needed
|
|
|
|
AudioProcess *ap = AudioProcess::get_instance();
|
|
if (ap) ap->stopAllExceptSpeech();
|
|
|
|
// Notify all the gumps of the mapchange
|
|
Ultima8Engine *gui = Ultima8Engine::get_instance();
|
|
if (gui) {
|
|
Gump *desktop = gui->getDesktopGump();
|
|
if (desktop) desktop->CloseItemDependents();
|
|
}
|
|
|
|
// get rid of any remaining ethereal items
|
|
while (!_ethereal.empty()) {
|
|
uint16 eth = _ethereal.front();
|
|
_ethereal.pop_front();
|
|
Item *i = getItem(eth);
|
|
if (i) {
|
|
if (i->getFlags() & Item::FLG_ETHEREAL)
|
|
i->destroy();
|
|
else
|
|
warning("Not destroying ethereal item %d - it doesn't think it's ethereal", eth);
|
|
}
|
|
}
|
|
|
|
uint32 oldmap = _currentMap->getNum();
|
|
if (oldmap != 0) {
|
|
debug(1, "Unloading map %u", oldmap);
|
|
|
|
assert(oldmap < _maps.size() && _maps[oldmap] != nullptr);
|
|
|
|
_currentMap->writeback();
|
|
|
|
debug(1, "Unloading Fixed items from map %u", oldmap);
|
|
|
|
_maps[oldmap]->unloadFixed();
|
|
}
|
|
|
|
// Kill any processes that need killing
|
|
if (GAME_IS_U8) {
|
|
// U8 doesn't kill processes of object 0 *or* type 1 when changing map.
|
|
Kernel::get_instance()->killProcessesNotOfType(0, 1, true);
|
|
} else {
|
|
// Crusader kills processes even for object 0 when switching.
|
|
SnapProcess::get_instance()->clearEggs();
|
|
CameraProcess::ResetCameraProcess();
|
|
Kernel::get_instance()->killAllProcessesNotOfTypeExcludeCurrent(1, true);
|
|
Kernel::get_instance()->addProcess(new SchedulerProcess());
|
|
}
|
|
|
|
debug(1, "Loading Fixed items in map %u", newmap);
|
|
Common::SeekableReadStream *items = GameData::get_instance()->getFixed()
|
|
->get_datasource(newmap);
|
|
_maps[newmap]->loadFixed(items);
|
|
delete items;
|
|
|
|
_currentMap->loadMap(_maps[newmap]);
|
|
|
|
// Update camera
|
|
if (GAME_IS_U8) {
|
|
// TODO: This may not even be needed for U8, but reset in case camera
|
|
// was looking at something other than the avatar during teleport.
|
|
CameraProcess *camera = CameraProcess::GetCameraProcess();
|
|
if (camera && camera->getItemNum() != kMainActorId) {
|
|
CameraProcess::SetCameraProcess(new CameraProcess(kMainActorId));
|
|
}
|
|
CameraProcess::SetEarthquake(0);
|
|
} else {
|
|
// In Crusader, snap the camera to the avatar. The snap process will
|
|
// then find the right snap egg in the next frame.
|
|
CameraProcess::SetCameraProcess(new CameraProcess(kMainActorId));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void World::loadNonFixed(Common::SeekableReadStream *rs) {
|
|
FlexFile *f = new FlexFile(rs);
|
|
|
|
debug(1, "Loading NonFixed items");
|
|
|
|
for (unsigned int i = 0; i < f->getCount(); ++i) {
|
|
|
|
// items in this map?
|
|
if (f->getSize(i) > 0) {
|
|
assert(_maps.size() > i);
|
|
assert(_maps[i] != nullptr);
|
|
|
|
Common::SeekableReadStream *items = f->getDataSource(i);
|
|
|
|
_maps[i]->loadNonFixed(items);
|
|
|
|
delete items;
|
|
|
|
}
|
|
}
|
|
|
|
delete f;
|
|
}
|
|
|
|
void World::loadItemCachNPCData(Common::SeekableReadStream *itemcach, Common::SeekableReadStream *npcdata) {
|
|
FlexFile *itemcachflex = new FlexFile(itemcach);
|
|
FlexFile *npcdataflex = new FlexFile(npcdata);
|
|
|
|
Common::SeekableReadStream *itemds = itemcachflex->getDataSource(0);
|
|
Common::SeekableReadStream *npcds = npcdataflex->getDataSource(0);
|
|
|
|
delete itemcachflex;
|
|
delete npcdataflex;
|
|
|
|
debug(1, "Loading NPCs");
|
|
|
|
for (uint32 i = 1; i < 256; ++i) { // Get rid of constants?
|
|
// These are ALL unsigned on disk
|
|
itemds->seek(0x00000 + i * 2);
|
|
int32 x = static_cast<int32>(itemds->readUint16LE());
|
|
itemds->seek(0x04800 + i * 2);
|
|
int32 y = static_cast<int32>(itemds->readUint16LE());
|
|
itemds->seek(0x09000 + i * 1);
|
|
int32 z = static_cast<int32>(itemds->readByte());
|
|
|
|
itemds->seek(0x0B400 + i * 2);
|
|
uint32 shape = itemds->readUint16LE();
|
|
itemds->seek(0x0FC00 + i * 1);
|
|
uint32 frame = itemds->readByte();
|
|
itemds->seek(0x12000 + i * 2);
|
|
uint16 flags = itemds->readUint16LE();
|
|
itemds->seek(0x16800 + i * 2);
|
|
uint16 quality = itemds->readUint16LE();
|
|
itemds->seek(0x1B000 + i * 1);
|
|
uint16 npcnum = static_cast<uint8>(itemds->readByte());
|
|
itemds->seek(0x1D400 + i * 1);
|
|
uint16 mapnum = static_cast<uint8>(itemds->readByte());
|
|
itemds->seek(0x1F800 + i * 2);
|
|
//uint16 next;
|
|
(void)itemds->readUint16LE();
|
|
|
|
// half the frame number is stored in npcdata.dat
|
|
npcds->seek(7 + i * 0x31);
|
|
frame += npcds->readByte() << 8;
|
|
|
|
if (shape == 0) {
|
|
// U8's itemcach has a lot of garbage in it.
|
|
// Ignore it.
|
|
continue;
|
|
}
|
|
|
|
#ifdef DUMP_ITEMS
|
|
debugC(kDebugObject, "%u,%u:\t(%d, %d, %d),\t%04X, %u, %u, u",
|
|
shape, frame, x, y, z, flags, quality, npcnum, mapnum);
|
|
#endif
|
|
|
|
Actor *actor = ItemFactory::createActor(shape, frame, quality,
|
|
flags | Item::FLG_IN_NPC_LIST,
|
|
npcnum, mapnum,
|
|
Item::EXT_PERMANENT_NPC, false);
|
|
if (!actor) {
|
|
warning("Couldn't create actor");
|
|
continue;
|
|
}
|
|
ObjectManager::get_instance()->assignActorObjId(actor, i);
|
|
|
|
actor->setLocation(x, y, z);
|
|
|
|
// read npcdata:
|
|
npcds->seek(i * 0x31);
|
|
actor->setStr(npcds->readByte()); // 0x00: strength
|
|
actor->setDex(npcds->readByte()); // 0x01: dexterity
|
|
actor->setInt(npcds->readByte()); // 0x02: intelligence
|
|
actor->setHP(npcds->readByte()); // 0x03: hitpoints
|
|
actor->setDir(Direction_FromUsecodeDir(npcds->readByte())); // 0x04: direction
|
|
uint16 la = npcds->readUint16LE(); // 0x05,0x06: last anim
|
|
actor->setLastAnim(static_cast<Animation::Sequence>(la));
|
|
npcds->skip(1); // 0x07: high byte of framenum
|
|
npcds->skip(1); // 0x08: current anim frame
|
|
npcds->skip(1); // 0x09: start Z of current fall
|
|
npcds->skip(1); // 0x0A: unknown, always zero
|
|
uint8 align = npcds->readByte(); // 0x0B: alignments
|
|
actor->setAlignment(align & 0x0F);
|
|
actor->setEnemyAlignment(align & 0xF0);
|
|
actor->setUnkByte(npcds->readByte()); // 0x0C: unknown;
|
|
// 0x0C is almost always zero, except for
|
|
// the avatar (0xC0) and
|
|
// Malchir, Vardion, Gorgrond, Beren (0xE0)
|
|
npcds->skip(14); // 0x0D-0x1A: unknown, always zero
|
|
actor->clearActorFlag(0xFF);
|
|
actor->setActorFlag(npcds->readByte()); // 0x1B: flags
|
|
npcds->skip(1); // 0x1C: unknown, always zero
|
|
npcds->skip(16); // 0x1D-0x2C: equipment
|
|
int16 mana = static_cast<int16>(npcds->readUint16LE()); // 0x2D,0x2E: mana
|
|
actor->setMana(mana);
|
|
actor->clearActorFlag(0xFFFF00);
|
|
uint32 flags2F = npcds->readByte(); // 0x2F: flags
|
|
actor->setActorFlag(flags2F << 8);
|
|
uint32 flags30 = npcds->readByte(); // 0x30: flags
|
|
actor->setActorFlag(flags30 << 16);
|
|
}
|
|
|
|
delete itemds;
|
|
delete npcds;
|
|
}
|
|
|
|
|
|
void World::worldStats() const {
|
|
unsigned int i, mapcount = 0;
|
|
|
|
for (i = 0; i < _maps.size(); i++) {
|
|
if (_maps[i] != nullptr && !_maps[i]->isEmpty())
|
|
mapcount++;
|
|
}
|
|
|
|
g_debugger->debugPrintf("World memory stats:\n");
|
|
g_debugger->debugPrintf("Maps : %u/256\n", mapcount);
|
|
|
|
const Actor *av = getMainActor();
|
|
g_debugger->debugPrintf("Avatar pos.: ");
|
|
if (av) {
|
|
g_debugger->debugPrintf("map %d, (", av->getMapNum());
|
|
Point3 pt = av->getLocation();
|
|
g_debugger->debugPrintf("%d,%d,%d)\n", pt.x, pt.y, pt.z);
|
|
} else {
|
|
g_debugger->debugPrintf("missing (null)\n");
|
|
}
|
|
}
|
|
|
|
void World::save(Common::WriteStream *ws) {
|
|
ws->writeUint32LE(_currentMap->getNum());
|
|
|
|
ws->writeUint16LE(_currentMap->_eggHatcher);
|
|
|
|
if (GAME_IS_CRUSADER) {
|
|
ws->writeByte(_alertActive ? 1 : 0);
|
|
ws->writeByte(_difficulty);
|
|
ws->writeUint16LE(_controlledNPCNum);
|
|
ws->writeUint32LE(_vargasShield);
|
|
}
|
|
|
|
uint16 es = static_cast<uint16>(_ethereal.size());
|
|
ws->writeUint32LE(es);
|
|
|
|
// empty stack and refill it again
|
|
uint16 *e = new uint16[es];
|
|
Std::list<ObjId>::const_iterator it = _ethereal.begin();
|
|
unsigned int i;
|
|
for (i = 0; i < es; ++i) {
|
|
e[es - i] = *it;
|
|
++it;
|
|
}
|
|
|
|
for (i = 0; i < es; ++i) {
|
|
ws->writeUint16LE(e[i]);
|
|
}
|
|
delete[] e;
|
|
}
|
|
|
|
// load items
|
|
bool World::load(Common::ReadStream *rs, uint32 version) {
|
|
uint16 curmapnum = rs->readUint32LE();
|
|
_currentMap->setMap(_maps[curmapnum]);
|
|
|
|
_currentMap->_eggHatcher = rs->readUint16LE();
|
|
|
|
if (GAME_IS_CRUSADER) {
|
|
_alertActive = (rs->readByte() != 0);
|
|
_difficulty = rs->readByte();
|
|
_controlledNPCNum = rs->readUint16LE();
|
|
_vargasShield = rs->readUint32LE();
|
|
}
|
|
|
|
uint32 etherealcount = rs->readUint32LE();
|
|
for (unsigned int i = 0; i < etherealcount; ++i) {
|
|
_ethereal.push_front(rs->readUint16LE());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void World::saveMaps(Common::WriteStream *ws) {
|
|
ws->writeUint32LE(static_cast<uint32>(_maps.size()));
|
|
for (unsigned int i = 0; i < _maps.size(); ++i) {
|
|
_maps[i]->save(ws);
|
|
}
|
|
}
|
|
|
|
bool World::loadMaps(Common::ReadStream *rs, uint32 version) {
|
|
uint32 mapcount = rs->readUint32LE();
|
|
|
|
// Integrity check
|
|
if (mapcount > _maps.size()) {
|
|
warning("Invalid mapcount in save: %d. Corrupt save?", mapcount);
|
|
return false;
|
|
}
|
|
|
|
// Map objects have already been created by reset()
|
|
for (unsigned int i = 0; i < mapcount; ++i) {
|
|
bool res = _maps[i]->load(rs, version);
|
|
if (!res) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void World::setAlertActive(bool active)
|
|
{
|
|
assert(GAME_IS_CRUSADER);
|
|
_alertActive = active;
|
|
|
|
if (GAME_IS_REMORSE) {
|
|
setAlertActiveRemorse(active);
|
|
} else {
|
|
setAlertActiveRegret(active);
|
|
}
|
|
}
|
|
|
|
void World::setAlertActiveRemorse(bool active)
|
|
{
|
|
// Replicate the behavior of the original game.
|
|
LOOPSCRIPT(script,
|
|
LS_OR(
|
|
LS_OR(
|
|
LS_OR(
|
|
LS_OR(LS_SHAPE_EQUAL(0x49), LS_SHAPE_EQUAL(0x21)),
|
|
LS_SHAPE_EQUAL(0x174)),
|
|
LS_SHAPE_EQUAL(0x271)),
|
|
LS_SHAPE_EQUAL(0x477))
|
|
);
|
|
|
|
UCList itemlist(2);
|
|
_world->getCurrentMap()->areaSearch(&itemlist, script, sizeof(script),
|
|
nullptr, 0xffff, false);
|
|
for (uint32 i = 0; i < itemlist.getSize(); i++) {
|
|
uint16 itemid = itemlist.getuint16(i);
|
|
Item *item = getItem(itemid);
|
|
assert(item);
|
|
int frame = item->getFrame();
|
|
if (_alertActive) {
|
|
if (item->getShape() == 0x477) {
|
|
if (frame < 2)
|
|
item->setFrame(frame + 2);
|
|
} else if (frame == 0) {
|
|
item->setFrame(1);
|
|
}
|
|
} else {
|
|
if (item->getShape() == 0x477) {
|
|
if (frame > 1)
|
|
item->setFrame(frame - 2);
|
|
} else if (frame == 1) {
|
|
item->setFrame(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void World::setAlertActiveRegret(bool active)
|
|
{
|
|
setAlertActiveRemorse(active);
|
|
|
|
LOOPSCRIPT(offscript, LS_OR(LS_SHAPE_EQUAL(0x660), LS_SHAPE_EQUAL(0x661)));
|
|
LOOPSCRIPT(onscript, LS_OR(LS_SHAPE_EQUAL(0x662), LS_SHAPE_EQUAL(0x663)));
|
|
|
|
const uint8 *script = active ? onscript : offscript;
|
|
// note: size should be the same, but just to be explicit.
|
|
int scriptlen = active ? sizeof(onscript) : sizeof(offscript);
|
|
|
|
UCList itemlist(2);
|
|
_world->getCurrentMap()->areaSearch(&itemlist, script, scriptlen,
|
|
nullptr, 0xffff, false);
|
|
for (uint32 i = 0; i < itemlist.getSize(); i++) {
|
|
uint16 itemid = itemlist.getuint16(i);
|
|
Item *item = getItem(itemid);
|
|
assert(item);
|
|
switch (item->getShape()) {
|
|
case 0x660:
|
|
item->setShape(0x663);
|
|
break;
|
|
case 0x661:
|
|
item->setShape(0x662);
|
|
break;
|
|
case 0x662:
|
|
item->setShape(0x661);
|
|
break;
|
|
case 0x663:
|
|
item->setShape(0x660);
|
|
break;
|
|
default:
|
|
warning("unexpected shape %d returned from search", item->getShape());
|
|
break;
|
|
}
|
|
item->setFrame(0);
|
|
}
|
|
}
|
|
|
|
void World::setGameDifficulty(uint8 difficulty) {
|
|
_difficulty = difficulty;
|
|
if (GAME_IS_REMORSE) {
|
|
// HACK: Set ammo data for BA-40 in higher 2 difficulty levels
|
|
// This would be better handled in the ini file somehow?
|
|
const ShapeInfo *si = GameData::get_instance()->getMainShapes()->getShapeInfo(0x32E);
|
|
if (si && si->_weaponInfo) {
|
|
WeaponInfo *wi = si->_weaponInfo;
|
|
wi->_clipSize = 20;
|
|
if (difficulty > 1) {
|
|
wi->_ammoShape = 0x33D;
|
|
wi->_ammoType = 1;
|
|
} else {
|
|
wi->_ammoShape = 0;
|
|
wi->_ammoType = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void World::setControlledNPCNum(uint16 num) {
|
|
uint16 oldnpc = _controlledNPCNum;
|
|
_controlledNPCNum = num;
|
|
Actor *previous = getActor(oldnpc);
|
|
if (previous && !previous->isDead() && previous->isInCombat()) {
|
|
previous->clearInCombat();
|
|
}
|
|
|
|
Actor *controlled = getActor(num);
|
|
if (controlled) {
|
|
if (num != 1) {
|
|
Kernel::get_instance()->killProcesses(num, Kernel::PROC_TYPE_ALL, true);
|
|
if (controlled->isInCombat())
|
|
controlled->clearInCombat();
|
|
}
|
|
Point3 pt = controlled->getCentre();
|
|
CameraProcess::SetCameraProcess(new CameraProcess(pt));
|
|
}
|
|
|
|
TargetReticleProcess *t = TargetReticleProcess::get_instance();
|
|
if (t) {
|
|
t->avatarMoved();
|
|
}
|
|
}
|
|
|
|
|
|
uint32 World::I_getAlertActive(const uint8 * /*args*/,
|
|
unsigned int /*argsize*/) {
|
|
return get_instance()->_world->isAlertActive() ? 1 : 0;
|
|
}
|
|
|
|
uint32 World::I_setAlertActive(const uint8 * /*args*/,
|
|
unsigned int /*argsize*/) {
|
|
get_instance()->_world->setAlertActive(true);
|
|
return 0;
|
|
}
|
|
|
|
uint32 World::I_clrAlertActive(const uint8 * /*args*/,
|
|
unsigned int /*argsize*/) {
|
|
get_instance()->_world->setAlertActive(false);
|
|
return 0;
|
|
}
|
|
|
|
uint32 World::I_gameDifficulty(const uint8 * /*args*/,
|
|
unsigned int /*argsize*/) {
|
|
return get_instance()->_world->getGameDifficulty();
|
|
}
|
|
|
|
uint32 World::I_getControlledNPCNum(const uint8 * /*args*/,
|
|
unsigned int /*argsize*/) {
|
|
return get_instance()->_world->getControlledNPCNum();
|
|
}
|
|
|
|
uint32 World::I_setControlledNPCNum(const uint8 *args,
|
|
unsigned int /*argsize*/) {
|
|
ARG_UINT16(num);
|
|
get_instance()->_world->setControlledNPCNum(num);
|
|
return 0;
|
|
}
|
|
|
|
uint32 World::I_resetVargasShield(const uint8 * /*args*/,
|
|
unsigned int /*argsize*/) {
|
|
get_instance()->setVargasShield(500);
|
|
return 0;
|
|
}
|
|
|
|
} // End of namespace Ultima8
|
|
} // End of namespace Ultima
|