[Scummvm-git-logs] scummvm master -> f021cfaff3c1cfd2185fe15641866dc0cd502e3f
mduggan
mgithub at guarana.org
Mon Jul 27 06:28:41 UTC 2020
This automated email contains information about 7 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
95d0fc6364 ULTIMA8: Keep track of the crosshair process instance
18e7da15e3 ULTIMA8: Add ability to return blocker from isValidPosition
8f48861acc ULTIMA8: Document WorldPoint struct
ada0315e8f ULTIMA8: Make pathfinder proc type visible
82c651fcab ULTIMA8: JANITORIAL: Whitespace.
8e72e3c7ef ULTIMA8: Add a simple 3d point struct
f021cfaff3 ULTIMA8: Basic code for firing weapons in Crusader
Commit: 95d0fc63646669a57941305e4b5b1a0e59f83035
https://github.com/scummvm/scummvm/commit/95d0fc63646669a57941305e4b5b1a0e59f83035
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-27T13:47:00+09:00
Commit Message:
ULTIMA8: Keep track of the crosshair process instance
Changed paths:
engines/ultima/ultima8/world/crosshair_process.cpp
engines/ultima/ultima8/world/crosshair_process.h
diff --git a/engines/ultima/ultima8/world/crosshair_process.cpp b/engines/ultima/ultima8/world/crosshair_process.cpp
index e0585c53ad..3c25519cc7 100644
--- a/engines/ultima/ultima8/world/crosshair_process.cpp
+++ b/engines/ultima/ultima8/world/crosshair_process.cpp
@@ -39,7 +39,10 @@ DEFINE_RUNTIME_CLASSTYPE_CODE(CrosshairProcess)
static const uint32 CROSSHAIR_SHAPE = 0x4CC;
static const float CROSSHAIR_DIST = 400.0;
+CrosshairProcess *CrosshairProcess::_instance = nullptr;
+
CrosshairProcess::CrosshairProcess() : Process() {
+ _instance = this;
}
void CrosshairProcess::run() {
diff --git a/engines/ultima/ultima8/world/crosshair_process.h b/engines/ultima/ultima8/world/crosshair_process.h
index d06ef09b41..8bf706e1f7 100644
--- a/engines/ultima/ultima8/world/crosshair_process.h
+++ b/engines/ultima/ultima8/world/crosshair_process.h
@@ -45,6 +45,13 @@ public:
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
+
+ static CrosshairProcess *get_instance() {
+ return _instance;
+ }
+
+private:
+ static CrosshairProcess *_instance;
};
} // End of namespace Ultima8
Commit: 18e7da15e3fb2f0f97359bcb45fc8e5ebeb75dad
https://github.com/scummvm/scummvm/commit/18e7da15e3fb2f0f97359bcb45fc8e5ebeb75dad
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-27T13:47:00+09:00
Commit Message:
ULTIMA8: Add ability to return blocker from isValidPosition
Changed paths:
engines/ultima/ultima8/world/current_map.cpp
engines/ultima/ultima8/world/current_map.h
diff --git a/engines/ultima/ultima8/world/current_map.cpp b/engines/ultima/ultima8/world/current_map.cpp
index 2bb616e631..7dc266a7f8 100644
--- a/engines/ultima/ultima8/world/current_map.cpp
+++ b/engines/ultima/ultima8/world/current_map.cpp
@@ -683,7 +683,7 @@ const Std::list<Item *> *CurrentMap::getItemList(int32 gx, int32 gy) const {
bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
uint32 shape,
ObjId item, const Item **support,
- ObjId *roof) const {
+ ObjId *roof, const Item **blocker) const {
const ShapeInfo *si = GameData::get_instance()->
getMainShapes()->getShapeInfo(shape);
int32 xd, yd, zd;
@@ -693,18 +693,18 @@ bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
return isValidPosition(x, y, z,
INT_MAX_VALUE / 2, INT_MAX_VALUE / 2, INT_MAX_VALUE / 2,
xd, yd, zd,
- si->_flags, item, support, roof);
+ si->_flags, item, support, roof, blocker);
}
bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
int xd, int yd, int zd,
uint32 shapeflags,
- ObjId item_, const Item **support_,
- ObjId *roof_) const {
+ ObjId item, const Item **support,
+ ObjId *roof, const Item **blocker) const {
return isValidPosition(x, y, z,
INT_MAX_VALUE / 2, INT_MAX_VALUE / 2, INT_MAX_VALUE / 2,
xd, yd, zd,
- shapeflags, item_, support_, roof_);
+ shapeflags, item, support, roof, blocker);
}
@@ -713,13 +713,14 @@ bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
int xd, int yd, int zd,
uint32 shapeflags,
ObjId item_, const Item **support_,
- ObjId *roof_) const {
+ ObjId *roof_, const Item **blocker_) const {
const uint32 flagmask = (ShapeInfo::SI_SOLID | ShapeInfo::SI_DAMAGING |
ShapeInfo::SI_ROOF);
const uint32 blockflagmask = (ShapeInfo::SI_SOLID | ShapeInfo::SI_DAMAGING);
bool valid = true;
const Item *support = nullptr;
+ const Item *blocker = nullptr;
ObjId roof = 0;
int32 roofz = INT_MAX_VALUE;
@@ -772,6 +773,9 @@ bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
#if 0
item->dumpInfo();
#endif
+ if (blocker == nullptr) {
+ blocker = item;
+ }
valid = false;
}
@@ -796,6 +800,8 @@ bool CurrentMap::isValidPosition(int32 x, int32 y, int32 z,
if (support_)
*support_ = support;
+ if (blocker_)
+ *blocker_ = blocker;
if (roof_)
*roof_ = roof;
@@ -1311,8 +1317,8 @@ uint32 CurrentMap::I_canExistAtPoint(const uint8 *args, unsigned int /*argsize*/
return 0;
if (GAME_IS_CRUSADER) {
- pt.setX(pt.getX());
- pt.setY(pt.getY());
+ pt.setX(pt.getX() * 2);
+ pt.setY(pt.getY() * 2);
}
const CurrentMap *cm = World::get_instance()->getCurrentMap();
diff --git a/engines/ultima/ultima8/world/current_map.h b/engines/ultima/ultima8/world/current_map.h
index 7f24f23e7d..9b6350cbf3 100644
--- a/engines/ultima/ultima8/world/current_map.h
+++ b/engines/ultima/ultima8/world/current_map.h
@@ -107,30 +107,33 @@ public:
// Collision detection. Returns true if the box [x,y,z]-[x-xd,y-yd,z+zd]
// does not collide with any solid items.
- // Additionally, if support is not NULL, *support is set to the item
- // supporting the given box, or 0 if it isn't supported.
- // If under_roof is not NULL, *roof is set to the roof item with the lowest
- // z coordinate that's over the box, or 0 if there is no roof above box.
+ // Additionally:
+ // * If support is not NULL, *support is set to the item supporting
+ // the given box, or 0 if it isn't supported.
+ // * If roof is not NULL, *roof is set to the roof item with the lowest
+ // z coordinate that's over the box, or 0 if there is no roof above box.
+ // * If blocker is not NULL, *blocker will be set to an item blocking
+ // the whole box if there is one, or 0 if there is no such item.
// Ignores collisions which were already occurring at the start position.
// NB: isValidPosition doesn't consider item 'item'.
bool isValidPosition(int32 x, int32 y, int32 z,
int32 startx, int32 starty, int32 startz,
int xd, int yd, int zd, uint32 shapeflags,
- ObjId item,
- const Item **support = 0, ObjId *roof = 0) const;
+ ObjId item, const Item **support = 0,
+ ObjId *roof = 0, const Item **blocker = 0) const;
// Note that this version of isValidPosition does not look for start
// position collisions.
bool isValidPosition(int32 x, int32 y, int32 z,
int xd, int yd, int zd, uint32 shapeflags,
- ObjId item,
- const Item **support = 0, ObjId *roof = 0) const;
+ ObjId item, const Item **support = 0,
+ ObjId *roof = 0, const Item **blocker = 0) const;
// Note that this version of isValidPosition can not take 'flipped'
// into account!
bool isValidPosition(int32 x, int32 y, int32 z, uint32 shape,
ObjId item, const Item **support = 0,
- ObjId *roof = 0) const;
+ ObjId *roof = 0, const Item **blocker = 0) const;
//! Scan for a valid position for item in directions orthogonal to movedir
bool scanForValidPosition(int32 x, int32 y, int32 z, const Item *item,
Commit: 8f48861acc0b0ccab28cb70ffc5099ab0565210e
https://github.com/scummvm/scummvm/commit/8f48861acc0b0ccab28cb70ffc5099ab0565210e
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-27T13:47:00+09:00
Commit Message:
ULTIMA8: Document WorldPoint struct
Changed paths:
engines/ultima/ultima8/world/world_point.h
diff --git a/engines/ultima/ultima8/world/world_point.h b/engines/ultima/ultima8/world/world_point.h
index 1cf057046c..58d375b6e6 100644
--- a/engines/ultima/ultima8/world/world_point.h
+++ b/engines/ultima/ultima8/world/world_point.h
@@ -23,6 +23,10 @@
#ifndef ULTIMA8_WORLD_WORLDPOINT_H
#define ULTIMA8_WORLD_WORLDPOINT_H
+/**
+ * A 3D point in the format needed for passing back and forth to usecode.
+ * Not intended for general engine use, as it has a sort of clunky interface.
+ */
struct WorldPoint {
uint8 _buf[5];
Commit: ada0315e8f8b8734f6670d7b92063c6e2ba85730
https://github.com/scummvm/scummvm/commit/ada0315e8f8b8734f6670d7b92063c6e2ba85730
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-27T13:47:00+09:00
Commit Message:
ULTIMA8: Make pathfinder proc type visible
Changed paths:
engines/ultima/ultima8/world/actors/pathfinder_process.cpp
engines/ultima/ultima8/world/actors/pathfinder_process.h
diff --git a/engines/ultima/ultima8/world/actors/pathfinder_process.cpp b/engines/ultima/ultima8/world/actors/pathfinder_process.cpp
index 6427295379..96e2876fac 100644
--- a/engines/ultima/ultima8/world/actors/pathfinder_process.cpp
+++ b/engines/ultima/ultima8/world/actors/pathfinder_process.cpp
@@ -33,6 +33,8 @@ namespace Ultima8 {
static const unsigned int PATH_OK = 1;
static const unsigned int PATH_FAILED = 0;
+const uint16 PathfinderProcess::PATHFINDER_PROC_TYPE = 0x204;
+
// p_dynamic_cast stuff
DEFINE_RUNTIME_CLASSTYPE_CODE(PathfinderProcess)
@@ -46,7 +48,7 @@ PathfinderProcess::PathfinderProcess(Actor *actor, ObjId item_, bool hit) :
_targetX(0), _targetY(0), _targetZ(0) {
assert(actor);
_itemNum = actor->getObjId();
- _type = 0x0204; // CONSTANT !
+ _type = PATHFINDER_PROC_TYPE; // CONSTANT !
Item *item = getItem(item_);
if (!item) {
diff --git a/engines/ultima/ultima8/world/actors/pathfinder_process.h b/engines/ultima/ultima8/world/actors/pathfinder_process.h
index 729bd3560f..104c4a2d7a 100644
--- a/engines/ultima/ultima8/world/actors/pathfinder_process.h
+++ b/engines/ultima/ultima8/world/actors/pathfinder_process.h
@@ -57,6 +57,9 @@ protected:
Std::vector<PathfindingAction> _path;
unsigned int _currentStep;
+
+public:
+ static const uint16 PATHFINDER_PROC_TYPE;
};
} // End of namespace Ultima8
Commit: 82c651fcab2f2c96496921bf2fcef7024ac14b19
https://github.com/scummvm/scummvm/commit/82c651fcab2f2c96496921bf2fcef7024ac14b19
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-27T13:47:00+09:00
Commit Message:
ULTIMA8: JANITORIAL: Whitespace.
Changed paths:
engines/ultima/ultima8/ultima8.cpp
diff --git a/engines/ultima/ultima8/ultima8.cpp b/engines/ultima/ultima8/ultima8.cpp
index 248642ad32..c009487807 100644
--- a/engines/ultima/ultima8/ultima8.cpp
+++ b/engines/ultima/ultima8/ultima8.cpp
@@ -1084,8 +1084,6 @@ void Ultima8Engine::setupCoreGumps() {
_gameMapGump->InitGump(0);
}
-
-
// TODO: clean this up
if (GAME_IS_U8) {
assert(_desktopGump->getObjId() == 256);
Commit: 8e72e3c7ef8df08182c24803586169638960e7a7
https://github.com/scummvm/scummvm/commit/8e72e3c7ef8df08182c24803586169638960e7a7
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-27T15:25:06+09:00
Commit Message:
ULTIMA8: Add a simple 3d point struct
Changed paths:
A engines/ultima/ultima8/misc/point3.h
diff --git a/engines/ultima/ultima8/misc/point3.h b/engines/ultima/ultima8/misc/point3.h
new file mode 100644
index 0000000000..e4560507dc
--- /dev/null
+++ b/engines/ultima/ultima8/misc/point3.h
@@ -0,0 +1,71 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ULTIMA8_MISC_POINT3_H
+#define ULTIMA8_MISC_POINT3_H
+
+namespace Ultima {
+namespace Ultima8 {
+
+struct Point3 {
+ int32 x, y, z;
+
+ Point3() : x(0), y(0), z(0) {}
+ Point3(int32 nx, int32 ny, int32 nz) : x(nx), y(ny), z(nz) {}
+
+ int maxDistXYZ(const Point3 &other) const {
+ int xdiff = abs(x - other.x);
+ int ydiff = abs(y - other.y);
+ int zdiff = abs(z - other.z);
+ return MAX(xdiff, MAX(ydiff, zdiff));
+ }
+
+ void set(int32 nx, int32 ny, int32 nz) {
+ x = nx;
+ y = ny;
+ z = nz;
+ }
+
+ void move(int32 dx, int32 dy, int32 dz) {
+ x += dx;
+ y += dy;
+ z += dz;
+ }
+
+ bool loadData(Common::ReadStream *rs, uint32 version) {
+ x = rs->readSint32LE();
+ y = rs->readSint32LE();
+ z = rs->readSint32LE();
+ return true;
+ }
+
+ void saveData(Common::WriteStream *ws) {
+ ws->writeSint32LE(x);
+ ws->writeSint32LE(y);
+ ws->writeSint32LE(z);
+ }
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
Commit: f021cfaff3c1cfd2185fe15641866dc0cd502e3f
https://github.com/scummvm/scummvm/commit/f021cfaff3c1cfd2185fe15641866dc0cd502e3f
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-07-27T15:27:05+09:00
Commit Message:
ULTIMA8: Basic code for firing weapons in Crusader
Still needs a lot of work, but this is a first cut and works (with many bugs).
Changed paths:
A engines/ultima/ultima8/world/super_sprite_process.cpp
A engines/ultima/ultima8/world/super_sprite_process.h
engines/ultima/module.mk
engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
engines/ultima/ultima8/misc/direction.h
engines/ultima/ultima8/usecode/remorse_intrinsics.h
engines/ultima/ultima8/world/actors/actor.cpp
engines/ultima/ultima8/world/actors/actor.h
engines/ultima/ultima8/world/actors/main_actor.cpp
engines/ultima/ultima8/world/actors/main_actor.h
engines/ultima/ultima8/world/fire_type.cpp
engines/ultima/ultima8/world/fire_type.h
engines/ultima/ultima8/world/item.cpp
engines/ultima/ultima8/world/item.h
diff --git a/engines/ultima/module.mk b/engines/ultima/module.mk
index fa9af239a8..d61212c8d7 100644
--- a/engines/ultima/module.mk
+++ b/engines/ultima/module.mk
@@ -549,6 +549,7 @@ MODULE_OBJS := \
ultima8/world/snap_process.o \
ultima8/world/split_item_process.o \
ultima8/world/sprite_process.o \
+ ultima8/world/super_sprite_process.o \
ultima8/world/target_reticle_process.o \
ultima8/world/teleport_egg.o \
ultima8/world/world.o \
diff --git a/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h b/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
index d7fce24aa6..c5a074d5c8 100644
--- a/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
+++ b/engines/ultima/ultima8/convert/crusader/convert_usecode_crusader.h
@@ -98,7 +98,7 @@ const char* const ConvertUsecodeCrusader::_intrinsics[] = {
"int16 World::I_getControlledNPCNum()",
"byte Actor::I_getDir(4 bytes)", // based on disasm. same coff as 112, 121
"int16 Actor::I_getLastAnimSet(4 bytes)", // based on disasm. part of same coff set 01D, 05A, 0B9, 0D7, 0E4, 124
- "int16 Actor::I_maybeFire(Actor *, x, y, z, byte, int, byte)",
+ "int16 Item::I_fireWeapon(Item *, x, y, z, byte, int, byte)",
"byte Item::I_create(Item *, uint16 shapenum, uint16 framenum)", // probably - used in MISS1EGG referencing keycards and NPCDEATH in creating blood spills
// 0020
"void Item::I_popToCoords(Item *, uint16 x, uint16 y, uint16 z)", // set coords, used after creating blood spills in NPCDEATH
diff --git a/engines/ultima/ultima8/misc/direction.h b/engines/ultima/ultima8/misc/direction.h
index a81db522f6..39fa0af7ac 100644
--- a/engines/ultima/ultima8/misc/direction.h
+++ b/engines/ultima/ultima8/misc/direction.h
@@ -96,6 +96,44 @@ inline Direction Get_WorldDirection(int deltay, int deltax) {
return dydx >= -424 ? west : dydx >= -2472 ? southwest : south;
}
+inline Direction Get_WorldDirectionClosestInRange(int deltay, int deltax, uint16 ndirs, uint16 mindir, uint16 maxdir) {
+ // TODO: Implement proper 16 directions here.
+ uint32 dir = static_cast<uint32>(Get_WorldDirection(deltay, deltax));
+ if (ndirs == 16) {
+ dir *= 2;
+ }
+
+ if ((dir < mindir) || (dir > maxdir)) {
+ int32 dmin1 = dir - mindir;
+ int32 dmin2 = mindir - dir;
+ if (dmin1 < 0) {
+ dmin1 = dmin1 + ndirs;
+ }
+ if (dmin2 < 0) {
+ dmin2 = dmin2 + ndirs;
+ }
+ int32 dist_to_min = MIN(dmin1, dmin2);
+
+ int dmax1 = dir - maxdir;
+ int dmax2 = maxdir - dir;
+ if (dmax1 < 0) {
+ dmax1 = dmax1 + ndirs;
+ }
+ if (dmax2 < 0) {
+ dmax2 = dmax2 + ndirs;
+ }
+ int32 dist_to_max = MIN(dmax1, dmax2);
+
+ if (dist_to_min < dist_to_max) {
+ return static_cast<Direction>(mindir);
+ } else {
+ return static_cast<Direction>(maxdir);
+ }
+ }
+
+ return static_cast<Direction>(dir);
+}
+
} // End of namespace Ultima8
} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/usecode/remorse_intrinsics.h b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
index 8a3672032c..dd30f8ba8d 100644
--- a/engines/ultima/ultima8/usecode/remorse_intrinsics.h
+++ b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
@@ -66,7 +66,7 @@ Intrinsic RemorseIntrinsics[] = {
World::I_getControlledNPCNum, // int16 Intrinsic01B(void)
Actor::I_getDir, // byte Intrinsic01C(4 bytes)
Actor::I_getLastAnimSet, // int Intrinsic01D(4 bytes)
- 0, // int Intrinsic01E(16 bytes)
+ Item::I_fireWeapon, // int Intrinsic01E(16 bytes)
Item::I_create,
// 0x020
Item::I_popToCoords, // void Intrinsic020(10 bytes)
diff --git a/engines/ultima/ultima8/world/actors/actor.cpp b/engines/ultima/ultima8/world/actors/actor.cpp
index bd9e44f6a9..b3a6392c27 100644
--- a/engines/ultima/ultima8/world/actors/actor.cpp
+++ b/engines/ultima/ultima8/world/actors/actor.cpp
@@ -57,6 +57,7 @@
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/world/loop_script.h"
+#include "ultima/ultima8/world/fire_type.h"
namespace Ultima {
namespace Ultima8 {
@@ -156,16 +157,16 @@ bool Actor::loadMonsterStatsU8() {
bool Actor::giveTreasure() {
MainShapeArchive *mainshapes = GameData::get_instance()->getMainShapes();
- ShapeInfo *shapeinfo = getShapeInfo();
+ const ShapeInfo *shapeinfo = getShapeInfo();
MonsterInfo *mi = nullptr;
if (shapeinfo) mi = shapeinfo->_monsterInfo;
if (!mi)
return false;
- Std::vector<TreasureInfo> &treasure = mi->_treasure;
+ const Std::vector<TreasureInfo> &treasure = mi->_treasure;
for (unsigned int i = 0; i < treasure.size(); ++i) {
- TreasureInfo &ti = treasure[i];
+ const TreasureInfo &ti = treasure[i];
Item *item;
// check map
@@ -193,7 +194,7 @@ bool Actor::giveTreasure() {
// NB: this is rather biased towards weapons with low _shapes...
for (unsigned int s = 0; s < mainshapes->getCount(); ++s) {
- ShapeInfo *si = mainshapes->getShapeInfo(s);
+ const ShapeInfo *si = mainshapes->getShapeInfo(s);
if (!si->_weaponInfo) continue;
int chance = si->_weaponInfo->_treasureChance;
@@ -319,7 +320,7 @@ bool Actor::giveTreasure() {
if (ti._shapes.size() == 1) {
uint32 shapeNum = ti._shapes[0];
- ShapeInfo *si = mainshapes->getShapeInfo(shapeNum);
+ const ShapeInfo *si = mainshapes->getShapeInfo(shapeNum);
if (!si) {
perr << "Trying to create treasure with an invalid shapeNum ("
<< shapeNum << ")" << Std::endl;
@@ -727,7 +728,121 @@ void Actor::getHomePosition(int32 &x, int32 &y, int32 &z) const {
z = _homeZ;
}
+
void Actor::receiveHit(uint16 other, int dir, int damage, uint16 damage_type) {
+ if (GAME_IS_U8) {
+ receiveHitU8(other, dir, damage, damage_type);
+ } else {
+ receiveHitCru(other, dir, damage, damage_type);
+ }
+}
+
+void Actor::receiveHitCru(uint16 other, int dir, int damage, uint16 damage_type) {
+ //
+ // This is a big stack of constants and hard-coded things.
+ // It's like that in the original game.
+ //
+ Actor *attacker = getActor(other);
+ AudioProcess *audio = AudioProcess::get_instance();
+ Kernel *kernel = Kernel::get_instance();
+ uint32 shape = getShape();
+
+ if (shape == 0x3ac && _hitPoints > 0) {
+ // TODO: Finish special case for Vargas. Should not do any damage
+ // if there is a particular anim process running. Also, check if the
+ // same special case exists in REGRET.
+ doAnim(static_cast<Animation::Sequence>(0x21), getDir());
+ doAnim(static_cast<Animation::Sequence>(0x20), getDir());
+ _hitPoints -= damage;
+ return;
+ }
+
+ if (isDead())
+ return;
+
+ if (shape != 1 && this != getControlledActor()) {
+ //Actor *controlled = getControlledActor();
+ if (!isInCombat()) {
+ setActivity(getDefaultActivity(2)); // get activity from field 0xA
+ if (!isInCombat()) {
+ setInCombat();
+ // TODO: attack the currently controlled NPC.
+ }
+ } else {
+ if (getCurrentActivityNo() == 8) {
+ setActivity(5);
+ }
+ // TODO: attack the currently controlled NPC.
+ }
+
+ // If the attacker is the controlled npc and this actor is not pathfinding
+ if (attacker && attacker == getControlledActor() &&
+ kernel->findProcess(_objId, PathfinderProcess::PATHFINDER_PROC_TYPE) != nullptr) {
+ int32 x, y, z;
+ int32 ox, oy, oz;
+ getLocation(x, y, z);
+ attacker->getLocation(ox, oy, oz);
+ int32 maxdiff = MAX(MAX(abs(x - ox), abs(y - oy)), abs(z - oz));
+ if (maxdiff < 641 && isOnScreen()) {
+ // TODO: implement the equivalent of this function. For now, we always
+ // cancel pathfinding for the NPC.
+ // uint32 direction = static_cast<uint32>(Get_WorldDirection(y - oy, x - ox));
+ // int result = FUN_1128_1755(this, attacker, direction, 0, 0, 0);
+ // if (result) {
+ kernel->killProcesses(_objId, PathfinderProcess::PATHFINDER_PROC_TYPE, true);
+ // }
+ }
+ }
+ } else {
+ damage = receiveShieldHit(damage, damage_type);
+ }
+
+ if (hasActorFlags(ACT_IMMORTAL))
+ damage = 0;
+
+ if (damage > _hitPoints)
+ damage = _hitPoints;
+
+ setHP(static_cast<uint16>(_hitPoints - damage));
+
+ if (_hitPoints == 0) {
+ // Die!
+ die(damage_type);
+ } else if (damage) {
+ // Not dead yet.
+ if (!isRobotCru()) {
+ uint16 sfxno;
+ if (hasExtFlags(EXT_FEMALE)) {
+ sfxno = 0xd8; // female scream
+ } else {
+ sfxno = 0x8f; // male scream
+ }
+ if (audio && !audio->isSFXPlayingForObject(sfxno, other)) {
+ audio->playSFX(sfxno, 0x10, other, 1, false);
+ }
+ }
+ if (damage_type == 0xf || damage_type == 7) {
+ if (shape == 1) {
+ kernel->killProcesses(_objId, 0x204, true);
+ doAnim(static_cast<Animation::Sequence>(0x37), getDir());
+ } else if (shape == 0x4e6 || shape == 0x338 || shape == 0x385 || shape == 899) {
+ if (!(getRandom() % 3)) {
+ // Randomly stun the NPC for these damage types.
+ // CHECK ME: is this time accurate?
+ Process *attack = kernel->findProcess(_objId, 0x259);
+ uint stun = ((getRandom() % 10) + 8) * 60;
+ if (attack && stun) {
+ Process *delay = new DelayProcess(stun);
+ kernel->addProcess(delay);
+ attack->waitFor(delay);
+ }
+ }
+ }
+ }
+ }
+}
+
+void Actor::receiveHitU8(uint16 other, int dir, int damage, uint16 damage_type) {
if (isDead())
return; // already dead, so don't bother
@@ -885,7 +1000,7 @@ ProcId Actor::die(uint16 damageType) {
MainActor *avatar = getMainActor();
// if hostile to avatar
- if (getEnemyAlignment() & avatar->getAlignment()) {
+ if (GAME_IS_U8 && (getEnemyAlignment() & avatar->getAlignment())) {
if (avatar->isInCombat()) {
// play victory fanfare
MusicProcess::get_instance()->playCombatMusic(109);
@@ -894,7 +1009,6 @@ ProcId Actor::die(uint16 damageType) {
}
}
-
destroyContents();
giveTreasure();
@@ -1799,8 +1913,7 @@ uint32 Actor::I_createActorCru(const uint8 *args, unsigned int /*argsize*/) {
if (!item || !other)
return 0;
- // TODO: get game difficulty here.
- static const int gameDifficulty = 1;
+ const int gameDifficulty = World::get_instance()->getGameDifficulty();
int npcDifficulty = (item->getMapNum() & 3) + 1;
if (gameDifficulty < npcDifficulty)
@@ -1851,10 +1964,10 @@ uint32 Actor::I_createActorCru(const uint8 *args, unsigned int /*argsize*/) {
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);
- }*/
+ if (World::get_instance()->getGameDifficulty() == 4) {
+ wpntype = NPCDat::randomlyGetStrongerWeaponTypes(shape);
+ }
+
// TODO: should this be addItemCru? If so need to move it from MainActor.
weapon->moveToContainer(newactor, false);
newactor->setCombatTactic(0);
diff --git a/engines/ultima/ultima8/world/actors/actor.h b/engines/ultima/ultima8/world/actors/actor.h
index f6e7ecf46c..9b366c5543 100644
--- a/engines/ultima/ultima8/world/actors/actor.h
+++ b/engines/ultima/ultima8/world/actors/actor.h
@@ -256,6 +256,15 @@ public:
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
+ //! take a hit and optionally adjust it with the shields for this NPC.
+ virtual int receiveShieldHit(int damage, uint16 damage_type) {
+ return damage;
+ }
+
+ virtual uint8 getShieldType() const {
+ return 0;
+ }
+
ENABLE_RUNTIME_CLASSTYPE()
INTRINSIC(I_isNPC);
@@ -389,6 +398,8 @@ protected:
bool loadMonsterStatsU8();
bool loadMonsterStatsCru();
+ void receiveHitU8(uint16 other, int dir, int damage, uint16 type);
+ void receiveHitCru(uint16 other, int dir, int damage, uint16 type);
};
} // End of namespace Ultima8
diff --git a/engines/ultima/ultima8/world/actors/main_actor.cpp b/engines/ultima/ultima8/world/actors/main_actor.cpp
index 17425aa2b4..6b82a1ae4f 100644
--- a/engines/ultima/ultima8/world/actors/main_actor.cpp
+++ b/engines/ultima/ultima8/world/actors/main_actor.cpp
@@ -45,6 +45,8 @@
#include "ultima/ultima8/usecode/uc_list.h"
#include "ultima/ultima8/usecode/uc_machine.h"
#include "ultima/ultima8/world/loop_script.h"
+#include "ultima/ultima8/world/fire_type.h"
+#include "ultima/ultima8/world/sprite_process.h"
#include "ultima/ultima8/world/actors/avatar_gravity_process.h"
#include "ultima/ultima8/audio/music_process.h"
@@ -56,7 +58,7 @@ DEFINE_RUNTIME_CLASSTYPE_CODE(MainActor)
MainActor::MainActor() : _justTeleported(false), _accumStr(0), _accumDex(0),
_accumInt(0), _cruBatteryType(ChemicalBattery), _keycards(0),
- _activeWeapon(0), _activeInvItem(0) {
+ _activeWeapon(0), _activeInvItem(0), _shieldType(0), _shieldSpriteProc(0) {
}
MainActor::~MainActor() {
@@ -661,6 +663,8 @@ void MainActor::saveData(Common::WriteStream *ws) {
ws->writeUint32LE(_keycards);
ws->writeUint16LE(_activeWeapon);
ws->writeUint16LE(_activeInvItem);
+ ws->writeUint16LE(_shieldType);
+ ws->writeUint16LE(_shieldSpriteProc);
}
uint8 namelength = static_cast<uint8>(_name.size());
@@ -683,6 +687,8 @@ bool MainActor::loadData(Common::ReadStream *rs, uint32 version) {
_keycards = rs->readUint32LE();
_activeWeapon = rs->readUint16LE();
_activeInvItem = rs->readUint16LE();
+ _shieldType = rs->readUint16LE();
+ _shieldSpriteProc = rs->readUint16LE();
}
uint8 namelength = rs->readByte();
@@ -854,5 +860,71 @@ void MainActor::useInventoryItem(Item *item) {
}
}
+int MainActor::receiveShieldHit(int damage, uint16 damage_type) {
+ uint8 shieldtype = getShieldType();
+ if (shieldtype == 3) {
+ shieldtype = 4;
+ }
+
+ const FireType *firetype = GameData::get_instance()->getFireType(damage_type);
+ int energy = getMana();
+ Kernel *kernel = Kernel::get_instance();
+
+ if (shieldtype && firetype && firetype->getShieldCost() && (firetype->getShieldMask() & shieldtype) && damage < energy) {
+ setMana(energy - damage);
+ damage = 0;
+ AudioProcess *audio = AudioProcess::get_instance();
+ audio->playSFX(0x48, 0x10, _objId, 1, true);
+
+ // If there's no active shield sprite, create a new one.
+ if (!_shieldSpriteProc || kernel->getProcess(_shieldSpriteProc) == nullptr) {
+ // Create the shield damage sprite
+ uint16 shieldsprite;
+ uint16 shieldstartframe;
+ uint16 shieldendframe;
+ bool remembersprite;
+ int32 x, y, z;
+
+ switch (shieldtype) {
+ case 1:
+ shieldsprite = 0x5a9;
+ shieldstartframe = 7;
+ shieldendframe = 0xd;
+ remembersprite = false;
+ // NOTE: In the game, this is put in the location of the
+ // hit. For now just put in centre.
+ getCentre(x, y, z);
+ break;
+ case 2:
+ shieldsprite = 0x5a9;
+ shieldstartframe = 0;
+ shieldendframe = 6;
+ remembersprite = false;
+ getCentre(x, y, z);
+ break;
+ default:
+ shieldsprite = 0x52b;
+ shieldstartframe = 0;
+ shieldendframe = 8;
+ getLocation(x, y, z);
+ x += 0x10;
+ y += 0x18;
+ remembersprite = false;
+ break;
+ }
+ Process *p = new SpriteProcess(shieldsprite, shieldstartframe,
+ shieldendframe, 1, 4, x, y, z);
+ kernel->addProcess(p);
+ if (remembersprite) {
+ _shieldSpriteProc = p->getPid();
+ } else {
+ _shieldSpriteProc = 0;
+ }
+ }
+ }
+ return damage;
+}
+
+
} // End of namespace Ultima8
} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/world/actors/main_actor.h b/engines/ultima/ultima8/world/actors/main_actor.h
index c4f6ac4070..e7915f05e7 100644
--- a/engines/ultima/ultima8/world/actors/main_actor.h
+++ b/engines/ultima/ultima8/world/actors/main_actor.h
@@ -132,6 +132,9 @@ public:
//!< Swap to the next inventory item (in Crusader)
void nextInvItem();
+ //! Check if we can absorb a hit with the shield. Returns the modified damage value.
+ int receiveShieldHit(int damage, uint16 damage_type) override;
+
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
@@ -170,6 +173,9 @@ protected:
Std::string _name;
+ uint16 _shieldSpriteProc;
+ uint16 _shieldType;
+
};
} // End of namespace Ultima8
diff --git a/engines/ultima/ultima8/world/fire_type.cpp b/engines/ultima/ultima8/world/fire_type.cpp
index 44622eb7cc..e18bcd74c6 100644
--- a/engines/ultima/ultima8/world/fire_type.cpp
+++ b/engines/ultima/ultima8/world/fire_type.cpp
@@ -24,7 +24,15 @@
#include "ultima/ultima8/world/sprite_process.h"
#include "ultima/ultima8/world/fire_type.h"
+#include "ultima/ultima8/world/item.h"
+#include "ultima/ultima8/world/current_map.h"
+#include "ultima/ultima8/world/loop_script.h"
+#include "ultima/ultima8/world/get_object.h"
+#include "ultima/ultima8/world/world.h"
+#include "ultima/ultima8/world/actors/actor.h"
+#include "ultima/ultima8/usecode/uc_list.h"
#include "ultima/ultima8/kernel/kernel.h"
+#include "ultima/ultima8/misc/point3.h"
#include "ultima/ultima8/audio/audio_process.h"
namespace Ultima {
@@ -112,15 +120,18 @@ void FireType::makeBulletSplashShapeAndPlaySound(int32 x, int32 y, int32 z) cons
case 0x56b:
firstframe = (getRandom() % 3) * 6;
lastframe = firstframe + 5;
- break;
+ break;
case 0x537:
lastframe = 10;
+ break;
case 0x578:
firstframe = (getRandom() % 3) * 6;
lastframe = firstframe + 4;
+ break;
case 0x59b:
firstframe = (getRandom() % 2) * 4;
lastframe = firstframe + 3;
+ break;
case 0x1d8: {
switch (getRandom() % 4) {
case 0:
@@ -152,5 +163,43 @@ void FireType::makeBulletSplashShapeAndPlaySound(int32 x, int32 y, int32 z) cons
}
}
+void FireType::applySplashDamageAround(const Point3 &pt, int damage, const Item *exclude, const Item *src) const {
+ if (!getRange())
+ return;
+ static const uint32 BULLET_SPLASH_SHAPE = 0x1d9;
+
+ CurrentMap *currentmap = World::get_instance()->getCurrentMap();
+
+ //
+ // Find items in range and apply splash damage
+ //
+ UCList uclist(2);
+ LOOPSCRIPT(script, LS_TOKEN_TRUE); // we want all items
+ currentmap->areaSearch(&uclist, script, sizeof(script), nullptr,
+ getRange() * 16, true);
+ for (unsigned int i = 0; i < uclist.getSize(); ++i) {
+ Item *splashitem = getItem(uclist.getuint16(i));
+ assert(splashitem);
+ //
+ // Other items don't get splash damage from their own fire.. but the
+ // player does. Life is not fair..
+ //
+ if (splashitem == exclude || (splashitem == src && src != getControlledActor()) ||
+ splashitem->getShape() == BULLET_SPLASH_SHAPE)
+ continue;
+ int splashitemdamage = damage;
+ if (_typeNo == 3 || _typeNo == 4 || _typeNo == 10) {
+ Point3 pt2;
+ splashitem->getLocation(pt2);
+ int splashrange = pt.maxDistXYZ(pt2);
+ splashrange = (splashrange / 16) / 3;
+ if (splashrange)
+ splashitemdamage /= splashrange;
+ }
+ int splashdir = src->getDirToItemCentre(pt);
+ splashitem->receiveHit(0, splashdir, splashitemdamage, _typeNo);
+ }
+}
+
}
}
diff --git a/engines/ultima/ultima8/world/fire_type.h b/engines/ultima/ultima8/world/fire_type.h
index b31d8a1d5e..f7cd30bba2 100644
--- a/engines/ultima/ultima8/world/fire_type.h
+++ b/engines/ultima/ultima8/world/fire_type.h
@@ -26,6 +26,9 @@
namespace Ultima {
namespace Ultima8 {
+class Item;
+class Point3;
+
/**
* A structure to hold data about the fire that comes from various weapons
*/
@@ -81,6 +84,8 @@ public:
uint16 getRandomDamage() const;
+ void applySplashDamageAround(const Point3 &pt, int damage, const Item *exclude, const Item *src) const;
+
void makeBulletSplashShapeAndPlaySound(int32 x, int32 y, int32 z) const;
private:
diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index 5e52cda7c4..8c8a29fe84 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -40,6 +40,7 @@
#include "ultima/ultima8/graphics/shape_info.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/world/current_map.h"
+#include "ultima/ultima8/world/fire_type.h"
#include "ultima/ultima8/usecode/uc_stack.h"
#include "ultima/ultima8/misc/direction.h"
#include "ultima/ultima8/gumps/bark_gump.h"
@@ -59,10 +60,12 @@
#include "ultima/ultima8/world/destroy_item_process.h"
#include "ultima/ultima8/world/target_reticle_process.h"
#include "ultima/ultima8/world/snap_process.h"
+#include "ultima/ultima8/world/super_sprite_process.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/games/game_info.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/missile_tracker.h"
+#include "ultima/ultima8/world/crosshair_process.h"
namespace Ultima {
namespace Ultima8 {
@@ -152,6 +155,10 @@ void Item::setLocation(int32 X, int32 Y, int32 Z) {
_z = Z;
}
+void Item::move(const Point3 &pt) {
+ move(pt.x, pt.y, pt.z);
+}
+
void Item::move(int32 X, int32 Y, int32 Z) {
bool no_lerping = false;
CurrentMap *map = World::get_instance()->getCurrentMap();
@@ -161,6 +168,9 @@ void Item::move(int32 X, int32 Y, int32 Z) {
perr.Print("Warning: moving avatar below Z=0. (%d,%d,%d)\n", X, Y, Z);
}
+ // TODO: In Crusader, if we are moving the avatar the game also checks whether
+ // there is an active "shield zap" sprite that needs moving at the same time.
+
// It's currently in the ethereal void, remove it from there
if (_flags & FLG_ETHEREAL) {
@@ -621,6 +631,14 @@ int Item::getDirToItemCentre(const Item &item2) const {
return Get_WorldDirection(i2y - yv, i2x - xv);
}
+int Item::getDirToItemCentre(const Point3 &pt) const {
+ int32 xv, yv, zv;
+ getCentre(xv, yv, zv);
+
+ return Get_WorldDirection(pt.y - yv, pt.x - xv);
+}
+
+
int Item::getRange(const Item &item2, bool checkz) const {
int32 thisX, thisY, thisZ;
int32 otherX, otherY, otherZ;
@@ -1134,6 +1152,144 @@ int32 Item::collideMove(int32 dx, int32 dy, int32 dz, bool teleport, bool force,
return 0;
}
+uint16 Item::fireWeapon(int32 x, int32 y, int32 z, int dir, int firetype, char someflag) {
+ int32 ix, iy, iz;
+ getLocation(ix, iy, iz);
+
+ if (!GAME_IS_CRUSADER)
+ return 0;
+
+ static const uint32 BULLET_SPLASH_SHAPE = 0x1d9;
+
+ ix += x;
+ iy += y;
+ iz += z;
+
+ CurrentMap *currentmap = World::get_instance()->getCurrentMap();
+ const FireType *firetypedat = GameData::get_instance()->getFireType(firetype);
+
+ if (!firetypedat)
+ return 0;
+
+ int damage = firetypedat->getRandomDamage();
+
+ const Item *blocker = nullptr;
+ bool isvalid = currentmap->isValidPosition(ix, iy, iz, BULLET_SPLASH_SHAPE, 0, nullptr, nullptr, &blocker);
+ if (!isvalid && blocker) {
+ Item *block = getItem(blocker->getObjId());
+ Point3 blockpt;
+ block->getLocation(blockpt);
+ int damagedir = Get_WorldDirection(blockpt.y - iy, blockpt.x - ix);
+ block->receiveHit(getObjId(), damagedir, damage, firetype);
+ int splashdamage = firetypedat->getRandomDamage();
+ firetypedat->applySplashDamageAround(blockpt, splashdamage, block, this);
+
+ firetypedat->makeBulletSplashShapeAndPlaySound(ix, iy, iz);
+ return 0;
+ } else {
+ int spriteframe = 0;
+ switch (firetype) {
+ case 3:
+ case 9:
+ case 10:
+ spriteframe = dir + 0x11;
+ break;
+ case 5:
+ spriteframe = dir + 1;
+ break;
+ case 6:
+ spriteframe = 0x46;
+ break;
+ case 0xe:
+ spriteframe = 0x47 + getRandom() % 5;
+ break;
+ case 0xf:
+ spriteframe = 0x4c;
+ break;
+ }
+
+ // HACK: this should be fixed to use inheritence so the behavior
+ // is clean for both Item and Actor.
+ const Actor *thisactor = dynamic_cast<Actor *>(this);
+ if (thisactor) {
+ // TODO: Get damage for active inventory item, and do something with
+ // animation here (lines 185~208 of disasm)
+ }
+
+ Item *target;
+ if (someflag) {
+ target = getControlledActor();
+ } else {
+ target = currentmap->findBestTargetItem(x, y, dir);
+ }
+
+ int32 tx, ty, tz;
+ if (target) {
+ int32 tsx, tsy, tsz;
+ target->getCentre(tx, ty, tz);
+ target->getFootpadData(tsx, tsy, tsz);
+
+ tz = target->getZ() + tsz * 8;
+
+ if (tsz < 3) {
+ if (tsz)
+ tz -= 8;
+ } else {
+ int32 targetz = tz;
+ int32 thisz = getZ();
+ tz -= 16;
+ if (thisz - targetz < -0x2f) {
+ tz += 8;
+ } else if (thisz - targetz > 0x2f) {
+ if (tsz == 6) {
+ tz -= 16;
+ } else if (tsz >= 7) {
+ tz -= 24;
+ }
+ }
+ }
+ } else {
+ tx = -1;
+ }
+
+ // TODO: check if we need the equivalent of FUN_1130_0299 here..
+ // maybe not? It seems to reset the target reticle etc which we
+ // handle differently.
+
+ int numshots = firetypedat->getNumShots();
+ for (int i = 0; i < numshots; i++) {
+ SuperSpriteProcess *ssp;
+ CrosshairProcess *chp = CrosshairProcess::get_instance();
+ assert(chp);
+ Item *crosshair = getItem(chp->getItemNum());
+ int32 ssx, ssy, ssz;
+ if (tx != -1) {
+ // Shoot toward the target
+ ssx = tx;
+ ssy = ty;
+ ssz = tz;
+ } else if (this == getControlledActor() && crosshair) {
+ // Shoot toward the crosshair
+ crosshair->getLocation(ssx, ssy, ssz);
+ } else {
+ // Just send the projectile off into the distance
+ // CHECKME: This is not how the game does it - it has different
+ // tables (at 1478:129b and 1478:12ac). Check the logic here.
+ ssx = ix + x_fact[dir] * 0x500;
+ ssy = iy + x_fact[dir] * 0x500;
+ ssz = iz;
+ }
+
+ ssp = new SuperSpriteProcess(BULLET_SPLASH_SHAPE, spriteframe,
+ ix, iy, iz, ssx, ssy, ssz, firetype,
+ damage, _objId, target->getObjId(), someflag);
+ Kernel::get_instance()->addProcess(ssp);
+ }
+ }
+
+ return 0;
+}
+
unsigned int Item::countNearby(uint32 shape_, uint16 range) {
CurrentMap *currentmap = World::get_instance()->getCurrentMap();
UCList itemlist(2);
@@ -1169,12 +1325,6 @@ uint32 Item::callUsecodeEvent(uint32 event, const uint8 *args, int argsize) {
uint32 offset = u->get_class_event(class_id, event);
if (!offset) return 0; // event not found
- /*if (GAME_IS_CRUSADER && event != 1) {
- debug(6, "Crusader: not running event %d for item %d shape %d",
- event, _objId, _shape);
- return 0;
- }*/
-
debug(10, "Item: %d (shape %d) calling usecode event %d @ %04X:%04X",
_objId, _shape, event, class_id, offset);
@@ -1841,7 +1991,6 @@ void Item::receiveHitCru(uint16 other, int dir, int damage, uint16 type) {
int yhurl = 10 + getRandom() % 15;
// nothing special, so just hurl the item
- // TODO: hurl item in direction, with speed depending on damage
hurl(-xhurl * x_fact[dir], -yhurl * y_fact[dir], 16, 4); //!! constants
}
@@ -2069,12 +2218,10 @@ int Item::scaleReceivedDamageCru(int damage, uint16 type) const {
uint8 difficulty = World::get_instance()->getGameDifficulty();
const Actor *actor = dynamic_cast<const Actor *>(this);
//
- // TODO: this should check for current controlled NPC *or* avatar.
- //
// For difficulty 1 and 2, we scale damage to others *up* and damage
// to avatar *down*.
//
- if (!actor || getObjId() != 1) {
+ if (!actor || (this != getMainActor() && this != getControlledActor())) {
if (difficulty == 1) {
damage *= 5;
} else if (difficulty == 2) {
@@ -2455,7 +2602,7 @@ uint32 Item::I_getTypeFlag(const uint8 *args, unsigned int /*argsize*/) {
ARG_UINT16(typeflag);
if (!item) return 0;
- ShapeInfo *info = item->getShapeInfo();
+ const ShapeInfo *info = item->getShapeInfo();
if (GAME_IS_U8 && typeflag >= 64)
perr << "Invalid TypeFlag greater than 63 requested (" << typeflag << ") by Usecode" << Std::endl;
@@ -3206,41 +3353,7 @@ uint32 Item::I_getClosestDirectionInRange(const uint8 *args, unsigned int /*args
ARG_UINT16(mindir);
ARG_UINT16(maxdir);
- // TODO: Implement proper 16 directions here.
- uint32 dir = static_cast<uint32>(Get_WorldDirection(y2 - y1, x2 - x1));
- if (ndirs == 16) {
- dir *= 2;
- }
-
- if ((dir < mindir) || (dir > maxdir)) {
- int32 dmin1 = dir - mindir;
- int32 dmin2 = mindir - dir;
- if (dmin1 < 0) {
- dmin1 = dmin1 + ndirs;
- }
- if (dmin2 < 0) {
- dmin2 = dmin2 + ndirs;
- }
- int32 dist_to_min = MIN(dmin1, dmin2);
-
- int dmax1 = dir - maxdir;
- int dmax2 = maxdir - dir;
- if (dmax1 < 0) {
- dmax1 = dmax1 + ndirs;
- }
- if (dmax2 < 0) {
- dmax2 = dmax2 + ndirs;
- }
- int32 dist_to_max = MIN(dmax1, dmax2);
-
- if (dist_to_min < dist_to_max) {
- return mindir;
- } else {
- return maxdir;
- }
- }
-
- return dir;
+ return Get_WorldDirectionClosestInRange(y2 - y1, x2 - x1, ndirs, mindir, maxdir);
}
uint32 Item::I_hurl(const uint8 *args, unsigned int /*argsize*/) {
@@ -3497,6 +3610,19 @@ uint32 Item::I_isOnScreen(const uint8 *args, unsigned int /*argsize*/) {
return item->isOnScreen();
}
+uint32 Item::I_fireWeapon(const uint8 *args, unsigned int /*argsize*/) {
+ ARG_ITEM_FROM_PTR(item);
+ ARG_SINT16(x);
+ ARG_SINT16(y);
+ ARG_SINT16(z);
+ ARG_UINT16(dir);
+ ARG_UINT16(firetype);
+ ARG_UINT16(unkflag);
+
+ if (!item) return 0;
+
+ return item->fireWeapon(x * 2, y * 2, z, dir, firetype, unkflag);
+}
} // End of namespace Ultima8
} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/world/item.h b/engines/ultima/ultima8/world/item.h
index c28c9d9862..58935aac91 100644
--- a/engines/ultima/ultima8/world/item.h
+++ b/engines/ultima/ultima8/world/item.h
@@ -28,6 +28,7 @@
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/box.h"
+#include "ultima/ultima8/misc/point3.h"
namespace Ultima {
namespace Ultima8 {
@@ -73,6 +74,9 @@ public:
//! CurrentMap and fastArea if necessary.
void move(int32 x, int32 y, int32 z);
+ //! Move, but with a point struct.
+ void move(const Point3 &pt);
+
//! Move an item. This moves an item to a container and updates
//! CurrentMap and fastArea if necessary.
//! \param container The container this item should be placed in
@@ -96,6 +100,9 @@ public:
//! 'usable' coordinates if the Item is contained or equipped.
inline void getLocation(int32 &x, int32 &y, int32 &z) const;
+ //! Get the Item's location using a Point3 struct.
+ inline void getLocation(Point3 &pt) const;
+
//! Get this Item's Z coordinate.
int32 getZ() const;
@@ -287,6 +294,9 @@ public:
//! Undefined if either item is contained or equipped.
int getDirToItemCentre(const Item &item2) const;
+ //! Same as above, but from a fixed point.
+ int getDirToItemCentre(const Point3 &pt) const;
+
//! get 'distance' to other item. This is the maximum of the differences
//! between the x, y (and possibly z) coordinates of the items.
int getRange(const Item &item2, bool checkz = false) const;
@@ -375,6 +385,9 @@ public:
//! \param type The type of damage done. Zero for default
virtual void receiveHit(ObjId other, int dir, int damage, uint16 type);
+ //! fire the given weapon type in the given direction from location x, y, z.
+ uint16 fireWeapon(int32 x, int32 y, int32 z, int dir, int firetype, char someflag);
+
//! get damage points, used in Crusader for item damage.
uint8 getDamagePoints() const {
return _damagePoints;
@@ -573,6 +586,7 @@ public:
INTRINSIC(I_unequip);
INTRINSIC(I_avatarStoleSomething);
INTRINSIC(I_isOnScreen);
+ INTRINSIC(I_fireWeapon);
private:
uint32 _shape; // DO NOT modify this directly! Always use setShape()!
@@ -697,6 +711,12 @@ inline void Item::getLocation(int32 &X, int32 &Y, int32 &Z) const {
Z = _z;
}
+inline void Item::getLocation(Point3 &pt) const {
+ pt.x = _x;
+ pt.y = _y;
+ pt.z = _z;
+}
+
} // End of namespace Ultima8
} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/world/super_sprite_process.cpp b/engines/ultima/ultima8/world/super_sprite_process.cpp
new file mode 100644
index 0000000000..7631a1ff76
--- /dev/null
+++ b/engines/ultima/ultima8/world/super_sprite_process.cpp
@@ -0,0 +1,433 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "ultima/ultima8/misc/pent_include.h"
+#include "ultima/ultima8/world/super_sprite_process.h"
+
+#include "ultima/ultima8/games/game_data.h"
+#include "ultima/ultima8/graphics/shape.h"
+#include "ultima/ultima8/kernel/kernel.h"
+#include "ultima/ultima8/kernel/core_app.h"
+#include "ultima/ultima8/kernel/delay_process.h"
+#include "ultima/ultima8/misc/direction.h"
+#include "ultima/ultima8/usecode/uc_list.h"
+#include "ultima/ultima8/world/loop_script.h"
+#include "ultima/ultima8/world/current_map.h"
+#include "ultima/ultima8/world/fire_type.h"
+#include "ultima/ultima8/world/get_object.h"
+#include "ultima/ultima8/world/item_factory.h"
+#include "ultima/ultima8/world/item.h"
+#include "ultima/ultima8/world/world.h"
+#include "ultima/ultima8/world/actors/actor.h"
+#include "ultima/ultima8/world/sprite_process.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+// p_dynamic_class stuff
+DEFINE_RUNTIME_CLASSTYPE_CODE(SuperSpriteProcess)
+
+SuperSpriteProcess::SuperSpriteProcess() : Process(),
+ _shape(0), _frame(0), _fireType(0), _damage(0), _source(0),
+ _target(0), _counter(1), _item0x77(0), _spriteNo(0),
+ _startedAsFiretype9(false), _xstep(0), _ystep(0), _zstep(0),
+ _expired(false) {
+}
+
+SuperSpriteProcess::SuperSpriteProcess(int shape, int frame, int sx, int sy, int sz,
+ int dx, int dy, int dz,
+ uint16 firetype, uint16 damage, uint16 source,
+ uint16 target, uint8 spread) :
+ _shape(shape), _frame(frame), _startpt(sx, sy, sz), _destpt(dx, dy, dz),
+ _nextpt(sx, sy, sz), _fireType(firetype), _damage(damage), _source(source),
+ _target(target), _counter(1), _item0x77(0), _spriteNo(0),
+ _startedAsFiretype9(firetype == 9), _xstep(0), _ystep(0), _zstep(0),
+ _expired(false) {
+
+ const FireType *firetypedat = GameData::get_instance()->getFireType(firetype);
+ assert(firetypedat);
+ if (firetypedat->getAccurate()) {
+ spread = 0;
+ }
+ if (spread) {
+ int rng = _startpt.maxDistXYZ(_destpt);
+ Item *srcitem = getItem(source);
+ if (srcitem == getControlledActor()) {
+ if (firetype == 2 || firetype == 13)
+ rng /= 8;
+ else if (firetype == 5)
+ rng /= 12;
+ else if (firetype == 10)
+ rng /= 5;
+ else
+ rng /= 10;
+ } else {
+ if (dynamic_cast<Actor *>(srcitem) != nullptr) {
+ rng /= 2;
+ } else {
+ // TODO: various other flags are checked in the game (around 1138:0bd1)
+ // to make it either 5 or 8. For now just use 5.
+ rng /= 5;
+ }
+ }
+
+ if (rng > 0x50)
+ rng = 0x50;
+ int xoff = -rng + (getRandom() % (rng * 2 + 1));
+ int yoff = -rng + (getRandom() % (rng * 2 + 1));
+ rng /= 3;
+ if (rng > 0x18)
+ rng = 0x18;
+ int zoff = -rng + (getRandom() % (rng * 2 + 1));
+
+ _destpt.move(xoff, yoff, zoff);
+ if (_destpt.z > 0xfa)
+ _destpt.z = 0xfa;
+ else if (_destpt.z < 0)
+ _destpt.z = 0;
+ }
+
+ float travel = _destpt.maxDistXYZ(_nextpt);
+ float speed = firetypedat->getCellsPerRound() * 32.0f;
+ float rounds = travel / speed;
+ if (rounds < 1)
+ rounds = 1;
+
+ _xstep = (_destpt.x - sx) / rounds;
+ _ystep = (_destpt.y - sy) / rounds;
+ _zstep = (_destpt.z - sz) / rounds;
+
+ if (_fireType == 2 || _fireType == 0xd) {
+ _destpt.x += travel / 5;
+ _destpt.y += travel / 5;
+ _destpt.z += travel / 5;
+ }
+
+}
+
+SuperSpriteProcess::~SuperSpriteProcess(void) {
+ Item *item = getItem(_itemNum);
+ if (item) item->destroy();
+}
+
+void SuperSpriteProcess::move(int x, int y, int z) {
+ _nowpt.set(x, y, z);
+
+ Item *item = getItem(_itemNum);
+ if (item)
+ item->move(_nowpt);
+}
+
+void SuperSpriteProcess::run() {
+ CurrentMap *map = World::get_instance()->getCurrentMap();
+ int mapChunkSize = map->getChunkSize();
+ const FireType *firetypedat = GameData::get_instance()->getFireType(_fireType);
+
+ if (!firetypedat || !map->isChunkFast(_nextpt.x / mapChunkSize, _nextpt.y / mapChunkSize)) {
+ destroyItemOrTerminate();
+ return;
+ }
+
+ _nowpt = _nextpt;
+
+ Point3 newpt(_startpt);
+ if (!_startedAsFiretype9) {
+ newpt.x += _counter * _xstep;
+ newpt.y += _counter * _ystep;
+ newpt.z += _counter * _zstep;
+ } else {
+ int targetz = 0;
+ if (_counter > firetypedat->getRoundDuration()) {
+ if (!_expired) {
+ if (_target == 0) {
+ targetz = _nowpt.z;
+ } else {
+ Item *target = getItem(_target);
+ if (target) {
+ int32 tx, ty, tz;
+ int32 cx, cy, cz;
+ target->getLocation(tx, ty, tz);
+ target->getCentre(cx, cy, cz);
+ targetz = cz + 8;
+ }
+ }
+
+ // TODO: Apply point adjustments for firetype 9 here
+ // (lines 134~214 of disasm)
+ }
+ } else {
+ _expired = true;
+ }
+
+ if (!_expired) {
+ if (_nowpt.z < targetz) {
+ _zstep++;
+ } else if (targetz < _nowpt.z) {
+ _zstep--;
+ }
+ } else {
+ _zstep--;
+ }
+
+ _xstep = CLIP(_xstep, -32.0f, 32.0f);
+ _ystep = CLIP(_ystep, -32.0f, 32.0f);
+ _zstep = CLIP(_zstep, -16.0f, 16.0f);
+
+ newpt.x += _counter * _xstep;
+ newpt.y += _counter * _ystep;
+ newpt.z += _counter * _zstep;
+
+ if (_fireType == 9 && !_expired) {
+ if (_nowpt.x != newpt.x || _nowpt.y != newpt.y) {
+ uint8 dir = Get_WorldDirection(_nowpt.y - newpt.y, _nowpt.x - newpt.x);
+ Item *item;
+ if (_itemNum == 0) {
+ item = getItem(_spriteNo);
+ } else {
+ item = getItem(_itemNum);
+ }
+ if (item) {
+ item->setFrame(dir + 0x11);
+ }
+ }
+ }
+ }
+ _pt3 = newpt;
+
+ _counter++;
+
+ if (!_itemNum && _counter > 1) {
+ Item *sprite = ItemFactory::createItem(_shape, _frame, 0, Item::FLG_DISPOSABLE,
+ 0, 0, Item::EXT_SPRITE, true);
+ _spriteNo = sprite->getObjId();
+ sprite->move(_nowpt);
+ }
+
+ if (_pt3.z != 0 && _pt3.z != 0xfa) {
+ int32 duration = firetypedat->getRoundDuration();
+
+ if (_counter >= duration) {
+ // disasm ~line 311
+ if (!map->isChunkFast(_nowpt.x / mapChunkSize, _nowpt.y / mapChunkSize)) {
+ destroyItemOrTerminate();
+ return;
+ }
+ if (areaSearch()) {
+ advanceFrame();
+ Process *delay = new DelayProcess(_fireType == 9 ? 3 : 2);
+ ProcId delayid = Kernel::get_instance()->addProcess(delay);
+ waitFor(delayid);
+ return;
+ }
+ if (_item0x77 && _fireType == 5) {
+ Item *item = getItem(_item0x77);
+ assert(item);
+ const ShapeInfo *info = item->getShapeInfo();
+ if (info->is_roof() && _fireType == 5) {
+ makeBulletSplash(_pt3);
+ // Finish disasm lines 347~381 here.
+ warning("TODO: SuperSpriteProcess::run: Implement special case for firetype 5 hitting a roof");
+ terminate();
+ return;
+ }
+ }
+ }
+ }
+ if (_source && (_item0x77 == _source && _counter < 5)) {
+ Item *source = getItem(_source);
+ assert(source);
+ // CHECKME: This appears to be a hack in the original game
+ // to avoid hitting the source item.. for now reproduce it.
+ source->moveToEtherealVoid();
+ run();
+ source->returnFromEtherealVoid();
+ }
+ hitAndFinish();
+}
+
+void SuperSpriteProcess::makeBulletSplash(const Point3 &pt) {
+ const FireType *firetypedat = GameData::get_instance()->getFireType(_fireType);
+
+ if (firetypedat->getRange()) {
+ Item *item = getItem(_item0x77);
+ Item *src = getItem(_source);
+ firetypedat->applySplashDamageAround(pt, _damage, item, src);
+ }
+ firetypedat->makeBulletSplashShapeAndPlaySound(pt.x, pt.y, pt.z);
+}
+
+void SuperSpriteProcess::hitAndFinish() {
+ Point3 pt(_nowpt);
+ //int dist = _nowpt.maxDistXYZ(_pt3);
+
+ CurrentMap *map = World::get_instance()->getCurrentMap();
+ Std::list<CurrentMap::SweepItem> hits;
+ int32 start[3] = {_nowpt.x, _nowpt.y, _nowpt.z};
+ int32 end[3] = {_pt3.x, _pt3.y, _pt3.z};
+ int32 dims[3] = {1, 1, 1};
+ bool collision = map->sweepTest(start, end, dims, ShapeInfo::SI_SOLID,
+ _source, true, &hits);
+
+ if (collision && hits.size()) {
+ _item0x77 = hits.front()._item;
+ }
+
+ Item *item = getItem(_item0x77);
+ if (item) {
+ int32 ifx, ify, ifz;
+ item->getFootpadData(ifx, ify, ifz);
+ int32 ix, iy, iz;
+ item->getLocation(ix, iy, iz);
+
+ if (ifx > 2 && ify > 2 && ifz > 2) {
+ int32 ixsize = (ifx - 2) * 16;
+ int32 iysize = (ifx - 2) * 16;
+ if (pt.x < ix - ixsize)
+ pt.x = ix - ixsize;
+ if (pt.y < iy - iysize)
+ pt.y = iy - iysize;
+ }
+
+ //Actor *actor = dynamic_cast<Actor *>(item);
+ // CHECKME: This is not exactly the same as the game, but
+ // it should work? See disasm 1138:1384, lines 142 ~ 172
+ // There is some random factor added for non-actor items
+ // which needs checking
+ int dir = Get_WorldDirection(iy - _nowpt.y, ix - _nowpt.x);
+ item->receiveHit(_itemNum, dir, _damage, _fireType);
+ }
+ makeBulletSplash(pt);
+ destroyItemOrTerminate();
+}
+
+void SuperSpriteProcess::destroyItemOrTerminate() {
+ if (_itemNum) {
+ Item *item = getItem(_itemNum);
+ if (item)
+ item->destroy();
+ _itemNum = 0;
+ } else {
+ terminate();
+ }
+}
+
+// Implementation of fn at 1138:0f9e
+void SuperSpriteProcess::advanceFrame() {
+ _nextpt = _pt3;
+
+ if (_itemNum) {
+ warning("TODO: SuperSpriteProcess::advanceFrame: Implement area search 10e0_123a");
+ // TODO: do an area search for something here.
+ // AreaSearch_10e0_123a
+ }
+
+ if (_spriteNo) {
+ Item *sprite = getItem(_spriteNo);
+ assert(sprite);
+ sprite->move(_nextpt);
+ uint32 frame = sprite->getFrame();
+ frame++;
+ if (frame > 0x4b)
+ frame = 0x47;
+ sprite->setFrame(frame);
+ }
+
+ if (_fireType == 3) {
+ if (_pt5.x != -1) {
+ // Create a little sparkly thing
+ Process *p = new SpriteProcess(0x426, 0, 9, 0, 3, _pt5.x, _pt5.y, _pt5.z);
+ Kernel::get_instance()->addProcess(p);
+ }
+ _pt5 = _nextpt;
+ }
+}
+
+bool SuperSpriteProcess::areaSearch() {
+ // int x1, int y1, int z1, int x2, int y2, int z2
+ CurrentMap *map = World::get_instance()->getCurrentMap();
+
+ warning("TODO: SuperSpriteProcess::areaSearch: Implement area search 1138_0ee8");
+ // TODO: Implement SuperSprite_AreaSearch_1138_0ee8
+ uint16 lsrange = _pt3.maxDistXYZ(_nextpt);
+ UCList uclist(2);
+ LOOPSCRIPT(script, LS_TOKEN_TRUE); // we want all items
+ map->areaSearch(&uclist, script, sizeof(script), nullptr,
+ lsrange, true, _nextpt.x, _nextpt.y);
+ for (unsigned int i = 0; i < uclist.getSize(); ++i) {
+ Item *searchitem = getItem(uclist.getuint16(i));
+ assert(searchitem);
+ }
+
+ return false;
+}
+
+
+void SuperSpriteProcess::saveData(Common::WriteStream *ws) {
+ Process::saveData(ws);
+
+ ws->writeUint32LE(static_cast<uint32>(_shape));
+ ws->writeUint32LE(_frame);
+ _nowpt.saveData(ws);
+ _nextpt.saveData(ws);
+ _pt3.saveData(ws);
+ _startpt.saveData(ws);
+ _pt5.saveData(ws);
+ _destpt.saveData(ws);
+ ws->writeUint16LE(_fireType);
+ ws->writeUint16LE(_damage);
+ ws->writeUint16LE(_source);
+ ws->writeUint16LE(_target);
+ ws->writeUint16LE(_item0x77);
+ ws->writeUint16LE(_spriteNo);
+ ws->writeFloatLE(_xstep);
+ ws->writeFloatLE(_ystep);
+ ws->writeFloatLE(_zstep);
+
+ // TODO: Update save and load functions once this is finished.
+}
+
+bool SuperSpriteProcess::loadData(Common::ReadStream *rs, uint32 version) {
+ if (!Process::loadData(rs, version)) return false;
+
+ _shape = static_cast<int>(rs->readUint32LE());
+ _frame = rs->readUint16LE();
+ _nowpt.loadData(rs, version);
+ _nextpt.loadData(rs, version);
+ _pt3.loadData(rs, version);
+ _startpt.loadData(rs, version);
+ _pt5.loadData(rs, version);
+ _destpt.loadData(rs, version);
+ _fireType = rs->readUint16LE();
+ _damage = rs->readUint16LE();
+ _source = rs->readUint16LE();
+ _target = rs->readUint16LE();
+ _item0x77 = rs->readUint16LE();
+ _spriteNo = rs->readUint16LE();
+ _xstep = rs->readFloatLE();
+ _ystep = rs->readFloatLE();
+ _zstep = rs->readFloatLE();
+
+ return true;
+}
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/world/super_sprite_process.h b/engines/ultima/ultima8/world/super_sprite_process.h
new file mode 100644
index 0000000000..a59455e2f4
--- /dev/null
+++ b/engines/ultima/ultima8/world/super_sprite_process.h
@@ -0,0 +1,104 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ULTIMA8_WORLD_SUPERSPRITEPROCESS_H
+#define ULTIMA8_WORLD_SUPERSPRITEPROCESS_H
+
+#include "ultima/ultima8/kernel/process.h"
+#include "ultima/ultima8/usecode/intrinsics.h"
+#include "ultima/ultima8/misc/point3.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+//! Creates a damaging sprite like a rocket or something, for Crusader
+class SuperSpriteProcess : public Process {
+private:
+ int _shape;
+ struct Point3 _nowpt; //!< The current x,y,z position (1st pt in struct)
+ struct Point3 _nextpt; //!< The next x,y,z position (2nd pt in struct)
+ struct Point3 _pt3; //!< The ?? x,y,z position (3rd pt in struct)
+ struct Point3 _startpt; //!< The start x,y,z position (4th pt in struct)
+ struct Point3 _pt5; //!< The ?? x,y,z position (5th pt in struct)
+ struct Point3 _destpt; //!< The destination x,y,z position
+
+ uint16 _frame;
+ uint16 _fireType;
+ uint16 _damage;
+ uint16 _source;
+ uint16 _target;
+ int32 _counter;
+
+ uint16 _item0x77; //!< TODO: what is this?
+ uint16 _spriteNo;
+
+ float _xstep;
+ float _ystep;
+ float _zstep;
+
+ bool _startedAsFiretype9;
+ bool _expired;
+
+public:
+ // p_dynamic_class stuff
+ ENABLE_RUNTIME_CLASSTYPE()
+
+ SuperSpriteProcess();
+
+ //! SuperSpriteProcess Constructor
+ //! \param shape The shape to use
+ //! \param frame The initial/first frame of the sprite animation
+ //! \param sx Start X coord of the sprite in the world
+ //! \param sy Start Y coord of the sprite in the world
+ //! \param sz Start Z coord of the sprite in the world
+ //! \param dx Dest X coord of the sprite in the world
+ //! \param dy Dest Y coord of the sprite in the world
+ //! \param dz Dest Z coord of the sprite in the world
+ //!
+ SuperSpriteProcess(int shape, int frame, int sx, int sy, int sz,
+ int dx, int dy, int dz, uint16 firetype,
+ uint16 damage, uint16 source, uint16 target, uint8 flag);
+
+ //! The SuperSpriteProcess destructor
+ ~SuperSpriteProcess(void) override;
+
+ //! The SpriteProcess run function
+ void run() override;
+
+ //! Move the sprite to a new location
+ void move(int x, int y, int z);
+
+ bool loadData(Common::ReadStream *rs, uint32 version);
+ void saveData(Common::WriteStream *ws) override;
+
+protected:
+ bool areaSearch();
+ void advanceFrame();
+ void hitAndFinish();
+ void makeBulletSplash(const Point3 &pt);
+ void destroyItemOrTerminate();
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
More information about the Scummvm-git-logs
mailing list