Skip to content

Commit

Permalink
ULTIMA8: Support a few more crusader intrinsics
Browse files Browse the repository at this point in the history
  • Loading branch information
mduggan committed Jul 8, 2020
1 parent 9704171 commit 5b7d732
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 35 deletions.
Expand Up @@ -78,7 +78,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
"byte Actor::I_isNPC(Item *)", // proably - actually checks is itemno < 256?
"byte Item::I_getZ(Item *)",
"void Item::I_destroy(Item *)", // probably? often called after creating a replacement object and setting it to the same position (eg, LUGGAGE::gotHit)
"int16 Actor::I_GetNPCDataField0x63_00B(Actor *)", // Maybe get HP? Called from ANDROID::calledFromAnim, goes to NPCDEATH
"int16 Actor::I_GetNPCDataField0x63_00B(Actor *)", // Some unknown value set for NPCs based on Q of egg.
"void Ultima8Engine::I_setAvatarInStasis(int)",
"byte Item::I_getDirToItem(Item *, itemno)", // based on disasm
"int16 Actor::I_turnToward(Actor *, direction, unk)", // TODO: work out what unk is
Expand Down Expand Up @@ -152,13 +152,13 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
"byte Item::I_IsOn(Item *, uint16 itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Item::I_getQHi(Item *)", // same as 026 based on same coff set 026, 045, 047, 049, 04B, 04D, 04F, 0AF, 0BE, 0C9, 0F0, 0F3, 0FB, 133
// 0050
"int16 I_GetNPCDataField0x2_050(Actor *)",
"int16 Actor::I_getCurrentActivityNo(Actor *)",
"void Actor::I_clrInCombat(Actor *)", // probably, based on disasm.
"void Actor::I_setDefaultActivity0(Actor *, int)",
"void Actor::I_setDefaultActivity1(Actor *, int)",
"void Actor::I_setDefaultActivity2(Actor *, int)",
"void Actor::I_setActivity(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
"void Intrinsic056(int itemno)", // Maybe set new target? TODO: check usecode to understand this.
"void I_setControlledNPCTo(int itemno)", // when you take over the Thermatron etc.
"int16 Item::I_getSurfaceWeight(Item *)",
"byte Item::I_isCentreOn(Item *, uint16 other)",
"void Item::I_setFrame(Item *, frame)", // based on same coff as 002
Expand Down Expand Up @@ -301,7 +301,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
"void PaletteFaderProcess::I_setPalToAllGrey(void)", // sets all colors to 0x3F3F3F
"void Actor::I_setActivity(Actor *, int)", // part of same coff set 055, 07D, 0CD, 0DB, 0F2, 131
"byte Item::I_isOn(Item *, itemno)", // part of same coff set 044, 046, 048, 04A, 04C, 04E, 0A5, 0BC, 0C5, 0DC, 0F1, 0FA, 12C
"int16 Actor::I_GetNPCDataField0x4_0DD(Actor *)",
"int16 Actor::I_getLastActivityNo(Actor *)",
"void Actor::I_setCombatTactic(Actor *, int)",
"int16 Actor::I_getEquip(6 bytes)", // based on disasm
// 00E0
Expand Down
14 changes: 7 additions & 7 deletions engines/ultima/ultima8/usecode/remorse_intrinsics.h
Expand Up @@ -46,7 +46,7 @@ Intrinsic RemorseIntrinsics[] = {
Actor::I_isNPC, // byte Intrinsic008(Item *)
Item::I_getZ, // byte Intrinsic009(Item *)
Item::I_destroy, // void Intrinsic00A(Item *)
0, // get something npcdata, maybe HP void Intrinsic00B(4 bytes)
Actor::I_getUnkByte, // get something about npcdata - struct byte 0x63 (99)
Ultima8Engine::I_setAvatarInStasis, // void Intrinsic00C(2 bytes)
Item::I_getDirToItem, // byte Intrinsic00D(6 bytes)
0, // TODO: Actor::I_turnToward(Actor *, direction, unk)
Expand Down Expand Up @@ -120,7 +120,7 @@ Intrinsic RemorseIntrinsics[] = {
Item::I_isOn,
Item::I_getQHi, // based on same coff set as 026
// 0x050
0, // void Intrinsic050(4 bytes)
0, // TODO: int16 Actor::I_getCurrentActivityNo // void Intrinsic050(4 bytes)
Actor::I_clrInCombat, // void Intrinsic051(4 bytes)
Actor::I_setDefaultActivity0, // void Intrinsic052(6 bytes)
Actor::I_setDefaultActivity1, // void Intrinsic053(6 bytes)
Expand Down Expand Up @@ -186,7 +186,7 @@ Intrinsic RemorseIntrinsics[] = {
Item::I_setBroken, // void Intrinsic08C(4 bytes)
Item::I_hurl, // void Intrinsic08D(12 bytes)
Item::I_getNpcNum, // based on same coff as 102 (-> variable name in TRIGGER::ordinal21)
0, // TODO: PaletteFaderProcess::I_setPalToAllBlack
0, // TODO: PaletteFaderProcess::I_setPalToAllBlack - should also resume cycle process.
// 0x090
MusicProcess::I_musicStop, // void Intrinsic090(void)
0, // void Intrinsic091(void)
Expand All @@ -198,7 +198,7 @@ Intrinsic RemorseIntrinsics[] = {
0, // TODO: PaletteFaderProcess:I_setScreenGreyscale(void) (converts all colors to their Y values on each channel)
0, // void Intrinsic098(void) // TODO: reset vargas health to 500.. weird.
Item::I_andStatus, // void Intrinsic099(6 bytes)
0, // TODO: PaletteFaderProcess::I_stopFadesAndResetToGamePal(void),
0, // TODO: PaletteFaderProcess::I_stopFadesAndResetToGamePal(void), - should also stop cycle process.
PaletteFaderProcess::I_fadeFromBlack, // fade to game pal with number of steps
0, // TODO: PaletteFaderProcess::I_fadeFromBlackWithParam
PaletteFaderProcess::I_fadeToBlack, // fade to black with number of steps
Expand Down Expand Up @@ -269,7 +269,7 @@ Intrinsic RemorseIntrinsics[] = {
0, // TODO: PaletteFaderProcess::I_setPalToAllGrey // sets all colors to 0x3F3F3F
Actor::I_setActivity, // void Intrinsic0DB(6 bytes)
Item::I_isOn,
0, // void Intrinsic0DD(4 bytes)
0, // TODO: Actor::I_getLastActivityNo void Intrinsic0DD(4 bytes)
Actor::I_setCombatTactic, // void Intrinsic0DE(6 bytes)
Actor::I_getEquip, // void Intrinsic0DF(6 bytes)
// 0x0E0
Expand All @@ -279,12 +279,12 @@ Intrinsic RemorseIntrinsics[] = {
Actor::I_getDefaultActivity2, // void Intrinsic0E3(4 bytes)
Actor::I_getLastAnimSet, // void Intrinsic0E4(4 bytes)
0, // TODO: Actor::I_attack(Actor *, uint16 target) (implement me)
0, // void Intrinsic0E6(6 bytes)
Actor::I_setUnkByte, // void Intrinsic0E6(6 bytes)
Actor::I_setDead,
Item::I_cast, // void Intrinsic0E8(6 bytes)
Item::I_andStatus, // void Intrinsic0E9(6 bytes)
Item::I_getQLo, // based on same coff set as 02B
0, // void Intrinsic0EB(void)
MainActor::I_getNumberOfCredits, // void Intrinsic0EB(void)
Item::I_popToEnd,
Item::I_popToContainer,
BatteryChargerProcess::I_create,
Expand Down
83 changes: 64 additions & 19 deletions engines/ultima/ultima8/world/actors/actor.cpp
Expand Up @@ -66,9 +66,10 @@ static const unsigned int BACKPACK_SHAPE = 529;
DEFINE_RUNTIME_CLASSTYPE_CODE(Actor)

Actor::Actor() : _strength(0), _dexterity(0), _intelligence(0),
_hitPoints(0), _mana(0), _alignment(0), _enemyAlignment(0),
_lastAnim(Animation::stand), _animFrame(0), _direction(0),
_fallStart(0), _unk0C(0), _actorFlags(0), _combatTactic(0) {
_hitPoints(0), _mana(0), _alignment(0), _enemyAlignment(0),
_lastAnim(Animation::stand), _animFrame(0), _direction(0),
_fallStart(0), _unkByte(0), _actorFlags(0), _combatTactic(0),
_homeX(0), _homeY(0), _homeZ(0) {
_defaultActivity[0] = 0;
_defaultActivity[1] = 0;
_defaultActivity[2] = 0;
Expand Down Expand Up @@ -674,6 +675,18 @@ uint16 Actor::getDefaultActivity(int no) const {
return _defaultActivity[no];
}

void Actor::setHomePosition(int32 x, int32 y, int32 z) {
_homeX = x;
_homeY = y;
_homeZ = z;
}

void Actor::getHomePosition(int32 &x, int32 &y, int32 &z) const {
x = _homeX;
y = _homeY;
z = _homeZ;
}

void Actor::receiveHit(uint16 other, int dir, int damage, uint16 damage_type) {
if (isDead())
return; // already dead, so don't bother
Expand Down Expand Up @@ -1110,20 +1123,28 @@ int32 Actor::collideMove(int32 x, int32 y, int32 z, bool teleport, bool force,
return result;
}

static Std::set<uint16> _notifiedItems;

void Actor::notifyNearbyItems() {
/*
/*
TODO: This is not right - maybe we want to trigger each item only when it gets close,
then reset the status after it moves away? Need to dig into the assembly more.
For now this is a temporary hack to trigger some usecode events so we can
debug more of the game.
*/
UCList uclist(2);
LOOPSCRIPT(script, LS_TOKEN_TRUE); // we want all items
CurrentMap *currentmap = World::get_instance()->getCurrentMap();
currentmap->areaSearch(&uclist, script, sizeof(script), this, 0x80, false);

for (unsigned int i = 0; i < uclist.getSize(); ++i) {
Item *item = getItem(uclist.getuint16(i));
if (_notifiedItems.find(item->getObjId()) != _notifiedItems.end())
continue;
item->callUsecodeEvent_npcNearby(_objId);
}*/
_notifiedItems.insert(item->getObjId());
}
}

bool Actor::areEnemiesNear() {
Expand Down Expand Up @@ -1210,13 +1231,16 @@ void Actor::saveData(Common::WriteStream *ws) {
ws->writeUint16LE(_direction);
ws->writeUint32LE(_fallStart);
ws->writeUint32LE(_actorFlags);
ws->writeByte(_unk0C);
ws->writeByte(_unkByte);

if (GAME_IS_CRUSADER) {
ws->writeUint16LE(_defaultActivity[0]);
ws->writeUint16LE(_defaultActivity[1]);
ws->writeUint16LE(_defaultActivity[2]);
ws->writeUint16LE(_combatTactic);
ws->writeUint32LE(_homeX);
ws->writeUint32LE(_homeY);
ws->writeUint32LE(_homeZ);
}
}

Expand All @@ -1235,13 +1259,16 @@ bool Actor::loadData(Common::ReadStream *rs, uint32 version) {
_direction = rs->readUint16LE();
_fallStart = rs->readUint32LE();
_actorFlags = rs->readUint32LE();
_unk0C = rs->readByte();
_unkByte = rs->readByte();

if (GAME_IS_CRUSADER) {
_defaultActivity[0] = rs->readUint16LE();
_defaultActivity[1] = rs->readUint16LE();
_defaultActivity[2] = rs->readUint16LE();
_combatTactic = rs->readUint16LE();
_homeX = rs->readUint32LE();
_homeY = rs->readUint32LE();
_homeZ = rs->readUint32LE();
}

return true;
Expand Down Expand Up @@ -1774,24 +1801,42 @@ uint32 Actor::I_createActorCru(const uint8 *args, unsigned int /*argsize*/) {
newactor->setDefaultActivity(1, item->getQuality() >> 8);
newactor->setDefaultActivity(2, other->getMapNum());

// TODO: once I know what these fields are...
/*
newactor->setField0x5c(0);
newactor->setField0x5e(x, y);
newactor->setField0x12(item->getNpcNum() >> 4);
newactor->setField0x63(item->getQuality() & 0xff);
newactor->setUnkByte(item->getQuality() & 0xff);

uint16 wpnType = npcData->getWpnType();
if (gameDifficulty == 4) {
wpnType = randomlyGetHigherWeaponType(shape, wpntype);
}
uint16 wpntype = npcData->getWpnType();
Item *weapon = ItemFactory::createItem(wpntype, 0, 0, 0, 0, newactor->getMapNum(), 0, true);
/* TODO:
if (gameDifficulty == 4) {
wpntype = randomlyGetHigherWeaponType(shape, wpntype);
}*/
// TODO: should this be addItemCru? If so need to move it from MainActor.
weapon->moveToContainer(newactor, false);
newactor->setCombatTactic(0);
newactor->setHomePosition(x, y, z);

// give weapon to NPC.
/*
TODO: once I know what this field is.. seems to never be used in game?
newactor->setField0x12(item->getNpcNum() >> 4);
*/

return newactor->getObjId();
}

uint32 Actor::I_setUnkByte(const uint8 *args, unsigned int /*argsize*/) {
ARG_ACTOR_FROM_PTR(actor);
ARG_UINT16(value);
if (actor)
actor->setUnkByte(static_cast<uint8>(value & 0xff));
return 0;
}

uint32 Actor::I_getUnkByte(const uint8 *args, unsigned int /*argsize*/) {
ARG_ACTOR_FROM_PTR(actor);
if (!actor) return 0;

return actor->getUnkByte();
}

uint32 Actor::I_setActivity(const uint8 *args, unsigned int /*argsize*/) {
ARG_ACTOR_FROM_PTR(actor);
ARG_UINT16(activity);
Expand Down
22 changes: 19 additions & 3 deletions engines/ultima/ultima8/world/actors/actor.h
Expand Up @@ -122,8 +122,11 @@ class Actor : public Container {
void setFallStart(int32 zp) {
_fallStart = zp;
}
void setUnk0C(uint8 b) {
_unk0C = b;
void setUnkByte(uint8 b) {
_unkByte = b;
}
uint8 getUnkByte() const {
return _unkByte;
}

bool hasActorFlags(uint32 flags) const {
Expand Down Expand Up @@ -170,6 +173,9 @@ class Actor : public Container {
void setDefaultActivity(int no, uint16 activity);
uint16 getDefaultActivity(int no) const;

void setHomePosition(int32 x, int32 y, int32 z);
void getHomePosition(int32 &x, int32 &y, int32 &z) const;

//! calculate the damage an attack against this Actor does.
//! \param other the attacker (can be zero)
//! \param damage base damage
Expand Down Expand Up @@ -294,6 +300,8 @@ class Actor : public Container {
INTRINSIC(I_getDefaultActivity1);
INTRINSIC(I_getDefaultActivity2);
INTRINSIC(I_setCombatTactic);
INTRINSIC(I_setUnkByte);
INTRINSIC(I_getUnkByte);

enum ActorFlags {
ACT_INVINCIBLE = 0x000001, // flags from npcdata byte 0x1B
Expand Down Expand Up @@ -329,7 +337,10 @@ class Actor : public Container {
uint16 _direction;

int32 _fallStart;
uint8 _unk0C; // unknown byte 0x0C from npcdata.dat

//! Unknown byte 0x0C from npcdata.dat in U8, or
//! Unknown byte 0x99 from NPC struct in Crusader.
uint8 _unkByte;

//! tactic being used in combat (for Crusader), the entry in the combat.dat flex.
uint16 _combatTactic;
Expand All @@ -339,6 +350,11 @@ class Actor : public Container {
//! the 3 default NPC activities from Crusader
uint16 _defaultActivity[3];

//! The "home" position used in some Crusader attack tactics
int32 _homeX;
int32 _homeY;
int32 _homeZ;

//! starts an activity (Ultima 8 version)
//! \return processID of process handling the activity or zero
uint16 setActivityU8(int activity);
Expand Down
12 changes: 11 additions & 1 deletion engines/ultima/ultima8/world/actors/main_actor.cpp
Expand Up @@ -175,7 +175,6 @@ int16 MainActor::addItemCru(Item *item, bool showtoast) {
item->moveToContainer(this);
if (!_activeWeapon)
_activeWeapon = item->getObjId();
warning("TODO: Set new weapon as active weapon if there is none");
if (showtoast)
pickupArea->addPickup(item);
}
Expand Down Expand Up @@ -803,6 +802,17 @@ uint32 MainActor::I_addItemCru(const uint8 *args,
return 0;
}

uint32 MainActor::I_getNumberOfCredits(const uint8 *args,
unsigned int /*argsize*/) {
MainActor *av = getMainActor();
if (av) {
Item *item = av->getFirstItemWithShape(0x4ed, true);
if (item)
return item->getQuality();
}
return 0;
}

void MainActor::useInventoryItem(uint32 shapenum) {
Item *item = getFirstItemWithShape(shapenum, true);
useInventoryItem(item);
Expand Down
1 change: 1 addition & 0 deletions engines/ultima/ultima8/world/actors/main_actor.h
Expand Up @@ -148,6 +148,7 @@ class MainActor : public Actor {
INTRINSIC(I_hasKeycard);
INTRINSIC(I_clrKeycards);
INTRINSIC(I_addItemCru);
INTRINSIC(I_getNumberOfCredits);

void getWeaponOverlay(const WeaponOverlayFrame *&frame_, uint32 &shape_);

Expand Down
2 changes: 1 addition & 1 deletion engines/ultima/ultima8/world/world.cpp
Expand Up @@ -288,7 +288,7 @@ void World::loadItemCachNPCData(Common::SeekableReadStream *itemcach, Common::Se
uint8 align = npcds->readByte(); // 0x0B: alignments
actor->setAlignment(align & 0x0F);
actor->setEnemyAlignment(align & 0xF0);
actor->setUnk0C(npcds->readByte()); // 0x0C: unknown;
actor->setUnkByte(npcds->readByte()); // 0x0C: unknown;
// 0x0C is almost always zero, except for
// the avatar (0xC0) and
// Malchir, Vardion, Gorgrond, Beren (0xE0)
Expand Down

0 comments on commit 5b7d732

Please sign in to comment.