[Scummvm-git-logs] scummvm master -> 3fd36166ba9f64f54ff7f199b73c5c5f5f0469ae
mduggan
mgithub at guarana.org
Sat Dec 26 23:46:49 UTC 2020
This automated email contains information about 12 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
2b3864e2ce ULTIMA8: Fix compiler warning
ba4e20b075 ULTIMA8: Fix offset to Crusader attack data
23aed36cb6 ULTIMA8: Add convenience function for gump visibility and close notification
19397a7b62 ULTIMA8: Slight refactor of MovieGump
d66745b5fd ULTIMA8: Add Weasel (shop) gump for Crusader
9e8b97159d ULTIMA8: Workaround to avoid No Regret crashing
00b0438c34 ULTIMA8: Fixes for Remorse startup
a3340fe6a6 ULTIMA8: Fix Crusader KeypadGump to actually work
b566a9b2e3 ULTIMA8: Delete some crusader hacks from Actor
d0b9f00be8 ULTIMA8: Hide Crusader status gumps in movies
cc1ad1749f ULTIMA8: Don't save Crusader stat gumps
3fd36166ba ULTIMA8: Fix Crusader anim dat flags, again
Commit: 2b3864e2cedb32828472a0717f61858969cb1206
https://github.com/scummvm/scummvm/commit/2b3864e2cedb32828472a0717f61858969cb1206
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:43:37+09:00
Commit Message:
ULTIMA8: Fix compiler warning
Changed paths:
engines/ultima/ultima8/world/actors/actor.cpp
diff --git a/engines/ultima/ultima8/world/actors/actor.cpp b/engines/ultima/ultima8/world/actors/actor.cpp
index 270640b7ae..4edfb20709 100644
--- a/engines/ultima/ultima8/world/actors/actor.cpp
+++ b/engines/ultima/ultima8/world/actors/actor.cpp
@@ -1464,9 +1464,9 @@ void Actor::clearInCombat() {
clearActorFlag(ACT_INCOMBAT);
}
-int32 Actor::collideMove(int32 x, int32 y, int32 z, bool teleport, bool force,
+int32 Actor::collideMove(int32 x, int32 y, int32 z, bool teleports, bool force,
ObjId *hititem, uint8 *dirs) {
- int32 result = Item::collideMove(x, y, z, teleport, force, hititem, dirs);
+ int32 result = Item::collideMove(x, y, z, teleports, force, hititem, dirs);
if (_objId == 1 && GAME_IS_CRUSADER) {
notifyNearbyItems();
TargetReticleProcess::get_instance()->avatarMoved();
Commit: ba4e20b075d1498ddbbb5e729c7f8e8491dfa3db
https://github.com/scummvm/scummvm/commit/ba4e20b075d1498ddbbb5e729c7f8e8491dfa3db
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:45:22+09:00
Commit Message:
ULTIMA8: Fix offset to Crusader attack data
Changed paths:
engines/ultima/ultima8/world/actors/attack_process.cpp
diff --git a/engines/ultima/ultima8/world/actors/attack_process.cpp b/engines/ultima/ultima8/world/actors/attack_process.cpp
index 83871801af..8f6bfa7887 100644
--- a/engines/ultima/ultima8/world/actors/attack_process.cpp
+++ b/engines/ultima/ultima8/world/actors/attack_process.cpp
@@ -815,14 +815,14 @@ bool AttackProcess::checkTimer2PlusDelayElapsed(int now) {
void AttackProcess::setAttackData(uint16 off, uint16 val) {
if (off >= MAGIC_DATA_OFF && off < MAGIC_DATA_OFF + ARRAYSIZE(_dataArray) - 1)
- _dataArray[off] = val;
+ _dataArray[off - MAGIC_DATA_OFF] = val;
warning("Invalid offset to setAttackDataArray %d %d", off, val);
}
uint16 AttackProcess::getAttackData(uint16 off) const {
if (off >= MAGIC_DATA_OFF && off < MAGIC_DATA_OFF + ARRAYSIZE(_dataArray) - 1)
- return _dataArray[off];
+ return _dataArray[off - MAGIC_DATA_OFF];
warning("Invalid offset to getAttackDataArray: %d", off);
return 0;
Commit: 23aed36cb61af208a738879b863d72fbc8befd07
https://github.com/scummvm/scummvm/commit/23aed36cb61af208a738879b863d72fbc8befd07
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:45:36+09:00
Commit Message:
ULTIMA8: Add convenience function for gump visibility and close notification
Changed paths:
engines/ultima/ultima8/gumps/gump.cpp
engines/ultima/ultima8/gumps/gump.h
diff --git a/engines/ultima/ultima8/gumps/gump.cpp b/engines/ultima/ultima8/gumps/gump.cpp
index 8845fafcfc..93b08bb6e5 100644
--- a/engines/ultima/ultima8/gumps/gump.cpp
+++ b/engines/ultima/ultima8/gumps/gump.cpp
@@ -123,12 +123,14 @@ void Gump::Close(bool no_del) {
}
_notifier = 0;
+ _flags |= FLAG_CLOSING;
if (!_parent) {
- _flags |= FLAG_CLOSING;
- if (!no_del) delete this;
+ if (!no_del)
+ delete this;
} else {
- _flags |= FLAG_CLOSING;
- if (!no_del) _flags |= FLAG_CLOSE_AND_DEL;
+ _parent->ChildNotify(this, Gump::GUMP_CLOSING);
+ if (!no_del)
+ _flags |= FLAG_CLOSE_AND_DEL;
}
}
diff --git a/engines/ultima/ultima8/gumps/gump.h b/engines/ultima/ultima8/gumps/gump.h
index cd4efb696b..93f8e8c88a 100644
--- a/engines/ultima/ultima8/gumps/gump.h
+++ b/engines/ultima/ultima8/gumps/gump.h
@@ -38,8 +38,8 @@ class Item;
class GumpNotifyProcess;
class Gump;
-typedef bool (*FindGumpPredicate)(Gump *g);
-template<class T> inline bool IsOfType(Gump *g) { return dynamic_cast<T*>(g) != nullptr; }
+typedef bool (*FindGumpPredicate)(const Gump *g);
+template<class T> inline bool IsOfType(const Gump *g) { return dynamic_cast<const T*>(g) != nullptr; }
/**
* A Gump is a single GUI element within the game, like the backpack window, menu,
@@ -445,6 +445,12 @@ public:
virtual void UnhideGump() {
_flags &= ~FLAG_HIDDEN;
}
+ void SetVisibility(bool visible) {
+ if (visible)
+ UnhideGump();
+ else
+ HideGump();
+ }
bool mustSave(bool toplevel) const;
@@ -460,6 +466,10 @@ public:
LAYER_CONSOLE = 16 // Layer for the console
};
+ enum Message {
+ GUMP_CLOSING = 0x100
+ };
+
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
Commit: 19397a7b622235204a575cdafe0c08601e77caaa
https://github.com/scummvm/scummvm/commit/19397a7b622235204a575cdafe0c08601e77caaa
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:45:36+09:00
Commit Message:
ULTIMA8: Slight refactor of MovieGump
Changed paths:
engines/ultima/ultima8/gumps/movie_gump.cpp
diff --git a/engines/ultima/ultima8/gumps/movie_gump.cpp b/engines/ultima/ultima8/gumps/movie_gump.cpp
index 01c1745dd1..cba32bb5e4 100644
--- a/engines/ultima/ultima8/gumps/movie_gump.cpp
+++ b/engines/ultima/ultima8/gumps/movie_gump.cpp
@@ -161,6 +161,22 @@ static Std::string _fixCrusaderMovieName(const Std::string &s) {
return s;
}
+static Common::SeekableReadStream *_tryLoadCruMovie(const Std::string &filename) {
+ const Std::string path = Std::string::format("@game/flics/%s.avi", filename.c_str());
+ FileSystem *filesys = FileSystem::get_instance();
+ Common::SeekableReadStream *rs = filesys->ReadFile(path);
+ if (!rs) {
+ // Try with a "0" in the name
+ const Std::string adjustedfn = Std::string::format("@game/flics/0%s.avi", filename.c_str());
+ rs = filesys->ReadFile(adjustedfn);
+ if (!rs) {
+ warning("movie %s not found", filename.c_str());
+ return 0;
+ }
+ }
+ return rs;
+}
+
uint32 MovieGump::I_playMovieOverlay(const uint8 *args,
unsigned int /*argsize*/) {
ARG_ITEM_FROM_PTR(item);
@@ -178,16 +194,12 @@ uint32 MovieGump::I_playMovieOverlay(const uint8 *args,
const Palette *pal = palman->getPalette(PaletteManager::Pal_Game);
assert(pal);
- const Std::string filename = Std::string::format("@game/flics/%s.avi", name.c_str());
- FileSystem *filesys = FileSystem::get_instance();
- Common::SeekableReadStream *rs = filesys->ReadFile(filename);
- if (!rs) {
- warning("couldn't create gump for unknown movie %s", name.c_str());
- return 0;
+ Common::SeekableReadStream *rs = _tryLoadCruMovie(name);
+ if (rs) {
+ Gump *gump = new MovieGump(x, y, rs, false, pal->_palette);
+ gump->InitGump(nullptr, true);
+ gump->setRelativePosition(CENTER);
}
- Gump *gump = new MovieGump(x, y, rs, false, pal->_palette);
- gump->InitGump(nullptr, true);
- gump->setRelativePosition(CENTER);
}
return 0;
@@ -199,24 +211,14 @@ uint32 MovieGump::I_playMovieCutscene(const uint8 *args, unsigned int /*argsize*
ARG_UINT16(x);
ARG_UINT16(y);
- FileSystem *filesys = FileSystem::get_instance();
if (item) {
- const Std::string filename = Std::string::format("@game/flics/%s.avi", name.c_str());
- Common::SeekableReadStream *rs = filesys->ReadFile(filename);
- if (!rs) {
- // Try with a "0" in the name
- const Std::string adjustedfn = Std::string::format("@game/flics/0%s.avi", name.c_str());
- rs = filesys->ReadFile(adjustedfn);
- if (!rs) {
- warning("I_playMovieCutscene: movie %s not found", name.c_str());
- return 0;
- }
+ Common::SeekableReadStream *rs = _tryLoadCruMovie(name);
+ if (rs) {
+ // TODO: Support playback with gap lines for the CRT effect
+ Gump *gump = new MovieGump(x * 3, y * 3, rs, false);
+ gump->InitGump(nullptr, true);
+ gump->setRelativePosition(CENTER);
}
-
- // TODO: Support playback with gap lines for the CRT effect
- Gump *gump = new MovieGump(x * 3, y * 3, rs, false);
- gump->InitGump(nullptr, true);
- gump->setRelativePosition(CENTER);
}
return 0;
Commit: d66745b5fde4482cd8f1ba4819fe5dcd28b3c4a3
https://github.com/scummvm/scummvm/commit/d66745b5fde4482cd8f1ba4819fe5dcd28b3c4a3
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:45:36+09:00
Commit Message:
ULTIMA8: Add Weasel (shop) gump for Crusader
Changed paths:
A engines/ultima/ultima8/gumps/weasel_dat.cpp
A engines/ultima/ultima8/gumps/weasel_dat.h
A engines/ultima/ultima8/gumps/weasel_gump.cpp
A engines/ultima/ultima8/gumps/weasel_gump.h
engines/ultima/module.mk
engines/ultima/ultima8/games/game_data.cpp
engines/ultima/ultima8/games/game_data.h
engines/ultima/ultima8/ultima8.cpp
engines/ultima/ultima8/usecode/remorse_intrinsics.h
diff --git a/engines/ultima/module.mk b/engines/ultima/module.mk
index ac343490e0..8d3b0c7737 100644
--- a/engines/ultima/module.mk
+++ b/engines/ultima/module.mk
@@ -498,6 +498,8 @@ MODULE_OBJS := \
ultima8/gumps/target_gump.o \
ultima8/gumps/translucent_gump.o \
ultima8/gumps/u8_save_gump.o \
+ ultima8/gumps/weasel_dat.o \
+ ultima8/gumps/weasel_gump.o \
ultima8/gumps/widgets/button_widget.o \
ultima8/gumps/widgets/edit_widget.o \
ultima8/gumps/widgets/sliding_widget.o \
diff --git a/engines/ultima/ultima8/games/game_data.cpp b/engines/ultima/ultima8/games/game_data.cpp
index 1a4987fdf2..152ea29af2 100644
--- a/engines/ultima/ultima8/games/game_data.cpp
+++ b/engines/ultima/ultima8/games/game_data.cpp
@@ -41,6 +41,7 @@
#include "ultima/ultima8/conf/config_file_manager.h"
#include "ultima/ultima8/graphics/fonts/font_manager.h"
#include "ultima/ultima8/games/game_info.h"
+#include "ultima/ultima8/gumps/weasel_dat.h"
#include "ultima/ultima8/conf/setting_manager.h"
#include "ultima/ultima8/convert/crusader/convert_shape_crusader.h"
#include "ultima/ultima8/audio/music_flex.h"
@@ -504,6 +505,13 @@ const CombatDat *GameData::getCombatDat(uint16 entry) const {
return nullptr;
}
+const WeaselDat *GameData::getWeaselDat(uint16 entry) const {
+ if (entry < _weaselData.size()) {
+ return _weaselData[entry];
+ }
+ return nullptr;
+}
+
const FireType *GameData::getFireType(uint16 type) const {
return FireTypeTable::get(type);
}
@@ -671,6 +679,10 @@ void GameData::loadRemorseData() {
// 14 blocks of 323 bytes, references like W01 and I07
// (presumably weapon and inventory)
// shop data?
+ while (!stuffds->eos()) {
+ WeaselDat *data = new WeaselDat(stuffds);
+ _weaselData.push_back(data);
+ }
delete stuffds;
diff --git a/engines/ultima/ultima8/games/game_data.h b/engines/ultima/ultima8/games/game_data.h
index d0a54f9814..f53866bdc5 100644
--- a/engines/ultima/ultima8/games/game_data.h
+++ b/engines/ultima/ultima8/games/game_data.h
@@ -43,6 +43,7 @@ class NPCDat;
class CombatDat;
class FireType;
class ShapeFrame;
+class WeaselDat;
class SoundFlex;
class SpeechFlex;
struct GameInfo;
@@ -101,6 +102,8 @@ public:
const FireType *getFireType(uint16 type) const;
+ const WeaselDat *getWeaselDat(uint16 level) const;
+
Std::string translate(const Std::string &text);
FrameID translate(FrameID frame);
@@ -125,6 +128,7 @@ private:
WpnOvlayDat *_weaponOverlay;
Std::vector<NPCDat *> _npcTable;
Std::vector<CombatDat *> _combatData;
+ Std::vector<WeaselDat *> _weaselData;
SoundFlex *_soundFlex;
Std::vector<SpeechFlex **> _speech;
diff --git a/engines/ultima/ultima8/gumps/weasel_dat.cpp b/engines/ultima/ultima8/gumps/weasel_dat.cpp
new file mode 100644
index 0000000000..2accff62b7
--- /dev/null
+++ b/engines/ultima/ultima8/gumps/weasel_dat.cpp
@@ -0,0 +1,80 @@
+/* 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/gumps/weasel_dat.h"
+#include "common/util.h"
+#include "common/stream.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+static const int BLOCKS = 20;
+
+WeaselDat::WeaselDat(Common::ReadStream *rs) {
+ uint16 numentries = rs->readUint16LE();
+ if (numentries > BLOCKS)
+ numentries = BLOCKS;
+
+ // each block is 16 bytes
+ for (uint i = 0; i < numentries; i++) {
+ WeaselEntry entry;
+ // 4 byte string ID
+ for (int j = 0; j < 4; j++)
+ entry._id[j] = rs->readByte();
+
+ // Unknown 4 bytes
+ rs->readUint16LE();
+ rs->readUint16LE();
+
+ // Shapeno (2 bytes)
+ entry._shapeNo = rs->readUint16LE();
+ // Cost (2 bytes)
+ entry._cost = rs->readUint16LE();
+ entry._entryNo = rs->readUint16LE();
+ entry._unk = rs->readUint16LE();
+ if (entry._id[0] == 'W')
+ entry._type = kWeapon;
+ else if (entry._id[0] == 'I')
+ entry._type = kItem;
+ else
+ entry._type = kUnknown;
+
+ _items.push_back(entry);
+ }
+
+ const uint skip = (BLOCKS - numentries) * 16;
+ for (uint i = 0; i < skip; i++)
+ rs->readByte();
+}
+
+uint16 WeaselDat::getNumOfType(WeaselType type) const {
+ int count = 0;
+ for (Std::vector<WeaselEntry>::const_iterator iter = _items.begin(); iter != _items.end(); iter++) {
+ if (iter->_type == type)
+ count++;
+ }
+ return count;
+}
+
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/gumps/weasel_dat.h b/engines/ultima/ultima8/gumps/weasel_dat.h
new file mode 100644
index 0000000000..67a8c8e9cb
--- /dev/null
+++ b/engines/ultima/ultima8/gumps/weasel_dat.h
@@ -0,0 +1,73 @@
+/* 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_GUMPS_WEASELDAT_H
+#define ULTIMA8_GUMPS_WEASELDAT_H
+
+#include "common/stream.h"
+#include "ultima/shared/std/containers.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+/**
+ * Data for the Weasel (shop) gump on a single level. Contains a list of things you can buy.
+ */
+class WeaselDat {
+public:
+
+ enum WeaselType {
+ kUnknown,
+ kWeapon,
+ kItem,
+ };
+
+ /** A single item in the shop */
+ struct WeaselEntry {
+ char _id[4]; // eg, "W01", "I02", etc
+ uint16 _shapeNo;
+ uint32 _cost;
+ uint16 _entryNo;
+ uint16 _unk;
+ enum WeaselType _type;
+ };
+
+ WeaselDat(Common::ReadStream *rs);
+
+ uint16 getNumItems() const {
+ return _items.size();
+ }
+
+ uint16 getNumOfType(WeaselType type) const;
+
+ const Std::vector<WeaselEntry> &getItems() const {
+ return _items;
+ }
+
+private:
+ Std::vector<WeaselEntry> _items;
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
diff --git a/engines/ultima/ultima8/gumps/weasel_gump.cpp b/engines/ultima/ultima8/gumps/weasel_gump.cpp
new file mode 100644
index 0000000000..31b59b3031
--- /dev/null
+++ b/engines/ultima/ultima8/gumps/weasel_gump.cpp
@@ -0,0 +1,580 @@
+/* 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/gumps/weasel_gump.h"
+#include "ultima/ultima8/gumps/weasel_dat.h"
+#include "ultima/ultima8/games/game_data.h"
+#include "ultima/ultima8/graphics/gump_shape_archive.h"
+#include "ultima/ultima8/graphics/main_shape_archive.h"
+#include "ultima/ultima8/graphics/shape.h"
+#include "ultima/ultima8/graphics/shape_frame.h"
+#include "ultima/ultima8/ultima8.h"
+#include "ultima/ultima8/gumps/widgets/button_widget.h"
+#include "ultima/ultima8/gumps/widgets/text_widget.h"
+#include "ultima/ultima8/gumps/gump_notify_process.h"
+#include "ultima/ultima8/gumps/movie_gump.h"
+#include "ultima/ultima8/games/game.h"
+#include "ultima/ultima8/world/actors/main_actor.h"
+#include "ultima/ultima8/graphics/fonts/rendered_text.h"
+#include "ultima/ultima8/graphics/palette_manager.h"
+#include "ultima/ultima8/audio/audio_process.h"
+#include "ultima/ultima8/world/get_object.h"
+#include "ultima/ultima8/world/item_factory.h"
+#include "ultima/ultima8/meta_engine.h"
+#include "ultima/ultima8/filesys/file_system.h"
+#include "engines/dialogs.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+DEFINE_RUNTIME_CLASSTYPE_CODE(WeaselGump)
+
+static const uint16 WEASEL_CANT_BUY_SFXNO = 0xb0;
+static const int WEASEL_FONT = 6;
+static const int WEASEL_SHAPE_TOP = 22;
+
+enum WeaselUiElements {
+ kBtnLeft = 0,
+ kBtnBlank = 1,
+ kBtnRight = 2,
+ kBtnYes = 3,
+ kBtnNo = 4,
+ kBtnBuy = 5,
+ kBtnAmmo = 6,
+ kBtnWeapons = 7,
+ kBtnExit = 8,
+ kTxtCredits = 9,
+ kIconItem = 10,
+ kTxtItemName = 11,
+ kTxtItemCost = 12,
+ kTxtItemPurch = 13,
+ kTxtItemOwned = 14,
+ kTxtQuestion = 15
+};
+// Coords and shapes for above list of buttons
+static const int WEASEL_BTN_X[] = { 14, 76, 138, 18, 113, 20, 19, 19, 44};
+static const int WEASEL_BTN_Y[] = {213, 213, 213, 237, 237, 280, 319, 319, 368};
+static const int WEASEL_BTN_SHAPES[] = {13, 26, 14, 16, 15, 28, 27, 83, 29};
+
+static const char *FIRST_INTRO_MOVIE = "17A";
+static const char *INTRO_MOVIES[] = {"18A", "18B", "18C"};
+static const char *BUYMORE_MOVIES[] = {"21A", "21B"};
+static const char *CONFIRM_BUY_MOVIES[] = {"21A", "21B"};
+static const char *CANCELLED_PURCHASE_MOVIES[] = {"19C", "19D"};
+static const char *COMPLETED_PURCHASE_MOVIES[] = {"21C", "21D"};
+static const char *INSUFFICIENT_FUND_MOVIES[] = {"20C", "20D"};
+
+template<int T> bool FindUiElem(const Gump *g) { return g->GetIndex() == T; }
+
+namespace {
+// A small container gump that doesn't do anything except pass notifications to the parent
+class WeaselUIContainerGump : public Gump {
+ void ChildNotify(Gump *child, uint32 message) override {
+ _parent->ChildNotify(child, message);
+ }
+};
+
+static void _closeIfExists(Gump *gump) {
+ if (gump)
+ gump->Close();
+}
+
+static const char *_getRandomMovie(const char **movies, int nmovies) {
+ int offset = Ultima8Engine::get_instance()->getRandomNumber(nmovies - 1);
+ return movies[offset];
+}
+}
+
+bool WeaselGump::_playedIntroMovie = false;
+
+WeaselGump::WeaselGump(uint16 level)
+ : ModalGump(0, 0, 640, 480, 0, FLAG_DONT_SAVE), _credits(0),
+ _level(level), _state(kWeaselStart), _curItem(0), _ammoMode(false),
+ _curItemCost(1), _curItemShape(0), _ui(nullptr), _movie(nullptr) {
+ Mouse *mouse = Mouse::get_instance();
+ mouse->pushMouseCursor();
+ mouse->setMouseCursor(Mouse::MOUSE_HAND);
+}
+
+WeaselGump::~WeaselGump() {
+}
+
+
+void WeaselGump::Close(bool no_del) {
+ Mouse *mouse = Mouse::get_instance();
+ mouse->popMouseCursor();
+ ModalGump::Close(no_del);
+}
+
+void WeaselGump::InitGump(Gump *newparent, bool take_focus) {
+ ModalGump::InitGump(newparent, take_focus);
+
+ GumpShapeArchive *shapeArchive = GameData::get_instance()->getGumps();
+
+ Shape *top = shapeArchive->getShape(WEASEL_SHAPE_TOP);
+ Shape *midhi = shapeArchive->getShape(WEASEL_SHAPE_TOP + 1);
+ Shape *midlo = shapeArchive->getShape(WEASEL_SHAPE_TOP + 2);
+ Shape *bot = shapeArchive->getShape(WEASEL_SHAPE_TOP + 3);
+
+ if (!top || !midhi || !midlo || !bot) {
+ error("Couldn't load shapes for weasel");
+ return;
+ }
+
+ const ShapeFrame *tFrame = top->getFrame(0);
+ const ShapeFrame *mhFrame = midhi->getFrame(0);
+ const ShapeFrame *mlFrame = midlo->getFrame(0);
+ const ShapeFrame *bFrame = bot->getFrame(0);
+ if (!tFrame || !mhFrame || !mlFrame || !bFrame) {
+ error("Couldn't load shape frames for weasel");
+ return;
+ }
+
+ _ui = new WeaselUIContainerGump();
+ _ui->SetDims(Rect(0, 0, mhFrame->_width,
+ tFrame->_height + mhFrame->_height + mlFrame->_height + bFrame->_height));
+ _ui->InitGump(this, false);
+ _ui->setRelativePosition(CENTER);
+
+ Gump *tGump = new Gump(3, 0, tFrame->_width, tFrame->_height);
+ tGump->SetShape(top, 0);
+ tGump->InitGump(_ui, false);
+ Gump *mhGump = new Gump(0, tFrame->_height, mhFrame->_width, mhFrame->_height);
+ mhGump->SetShape(midhi, 0);
+ mhGump->InitGump(_ui, false);
+ Gump *mlGump = new Gump(5, tFrame->_height + mhFrame->_height, mlFrame->_width, mlFrame->_height);
+ mlGump->SetShape(midlo, 0);
+ mlGump->InitGump(_ui, false);
+ Gump *bGump = new Gump(9, tFrame->_height + mhFrame->_height + mlFrame->_height, bFrame->_width, bFrame->_height);
+ bGump->SetShape(bot, 0);
+ bGump->InitGump(_ui, false);
+
+ for (int i = 0; i < ARRAYSIZE(WEASEL_BTN_X); i++) {
+ uint32 buttonShapeNum = WEASEL_BTN_SHAPES[i];
+ Shape *buttonShape = shapeArchive->getShape(buttonShapeNum);
+ if (!buttonShape) {
+ error("Couldn't load shape for weasel button %d", i);
+ return;
+ }
+
+ const ShapeFrame *buttonFrame = buttonShape->getFrame(0);
+ if (!buttonFrame || buttonShape->frameCount() != 2) {
+ error("Couldn't load shape frame for weasel button %d", i);
+ return;
+ }
+
+ FrameID frame_up(GameData::GUMPS, buttonShapeNum, 0);
+ FrameID frame_down(GameData::GUMPS, buttonShapeNum, 1);
+ Gump *widget = new ButtonWidget(WEASEL_BTN_X[i], WEASEL_BTN_Y[i], frame_up, frame_down, false);
+ widget->InitGump(_ui, false);
+ widget->SetIndex(i);
+ // some buttons start hidden, the browsingMode() call below does that.
+ }
+
+ MainActor *av = getMainActor();
+ assert(av);
+ Item *item = av->getFirstItemWithShape(0x4ed, true);
+ if (item)
+ _credits = item->getQuality();
+
+ // TODO: remove me (for testing)
+ _credits += 10000;
+
+ _weaselDat = GameData::get_instance()->getWeaselDat(_level);
+ if (!_weaselDat || _weaselDat->getNumItems() == 0)
+ Close();
+}
+
+Gump *WeaselGump::playMovie(const Std::string &filename) {
+ const Std::string path = Std::string::format("@game/flics/%s.avi", filename.c_str());
+ FileSystem *filesys = FileSystem::get_instance();
+ Common::SeekableReadStream *rs = filesys->ReadFile(path);
+ Gump *gump = new MovieGump(600, 450, rs, false);
+ gump->InitGump(this, true);
+ gump->setRelativePosition(CENTER);
+ gump->CreateNotifier();
+ return gump;
+}
+
+void WeaselGump::run() {
+ ModalGump::run();
+ // Don't do much while a movie is playing.
+ if (_movie)
+ return;
+ _ui->UnhideGump();
+ switch (_state) {
+ case kWeaselStart:
+ _state = kWeaselShowIntro;
+ break;
+ case kWeaselShowIntro: {
+ if (_level == 1 && !_playedIntroMovie) {
+ _movie = playMovie(FIRST_INTRO_MOVIE);
+ _playedIntroMovie = true;
+ } else {
+ _movie = playMovie(_getRandomMovie(INTRO_MOVIES, ARRAYSIZE(INTRO_MOVIES)));
+ }
+ _state = kWeaselBrowsing;
+ browsingMode(true);
+ break;
+ }
+ case kWeaselCheckBuyMoreMovie:
+ _movie = playMovie(_getRandomMovie(BUYMORE_MOVIES, ARRAYSIZE(BUYMORE_MOVIES)));
+ _state = kWeaselCheckBuyMoreText;
+ break;
+ case kWeaselCheckBuyMoreText:
+ checkBuyMore();
+ break;
+ case kWeaselClosing:
+ Close();
+ break;
+ case kWeaselConfirmPurchaseMovie:
+ _movie = playMovie(_getRandomMovie(CONFIRM_BUY_MOVIES, ARRAYSIZE(CONFIRM_BUY_MOVIES)));
+ _state = kWeaselConfirmPurchaseText;
+ break;
+ case kWeaselConfirmPurchaseText:
+ confirmPurchase();
+ break;
+ case kWeaselCancelledPurchaseMovie:
+ browsingMode(true);
+ _movie = playMovie(_getRandomMovie(CANCELLED_PURCHASE_MOVIES, ARRAYSIZE(CANCELLED_PURCHASE_MOVIES)));
+ _state = kWeaselBrowsing;
+ break;
+ case kWeaselCompletedPurchase:
+ _movie = playMovie(_getRandomMovie(COMPLETED_PURCHASE_MOVIES, ARRAYSIZE(COMPLETED_PURCHASE_MOVIES)));
+ _state = kWeaselCheckBuyMoreText;
+ break;
+ case kWeaselInsufficientFunds:
+ // TODO: how does it get to this situation?
+ _movie = playMovie(_getRandomMovie(INSUFFICIENT_FUND_MOVIES, ARRAYSIZE(INSUFFICIENT_FUND_MOVIES)));
+ break;
+ case kWeaselBrowsing:
+ _ui->UnhideGump();
+ default:
+ break;
+ }
+ if (_movie) {
+ _ui->HideGump();
+ }
+}
+
+void WeaselGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
+ Gump::PaintThis(surf, lerp_factor, scaled);
+}
+
+bool WeaselGump::OnKeyDown(int key, int mod) {
+ if (Gump::OnKeyDown(key, mod)) return true;
+
+ // TODO: support keyboard input
+
+ return true;
+}
+
+void WeaselGump::ChildNotify(Gump *child, uint32 message) {
+ ButtonWidget *buttonWidget = dynamic_cast<ButtonWidget *>(child);
+ MovieGump *movieGump = dynamic_cast<MovieGump *>(child);
+ if (buttonWidget && message == ButtonWidget::BUTTON_CLICK) {
+ onButtonClick(child->GetIndex());
+ } else if (movieGump && message == Gump::GUMP_CLOSING) {
+ // Movie has finished.
+ _movie = nullptr;
+ }
+}
+
+void WeaselGump::onButtonClick(int entry) {
+ switch (entry) {
+ case kBtnWeapons:
+ _ammoMode = false;
+ updateAmmoButtons();
+ break;
+ case kBtnAmmo:
+ _ammoMode = true;
+ updateAmmoButtons();
+ break;
+ case kBtnLeft:
+ prevItem();
+ break;
+ case kBtnRight:
+ nextItem();
+ break;
+ case kBtnBuy:
+ buyItem();
+ break;
+ case kBtnExit:
+ checkClose();
+ break;
+ case kBtnYes:
+ if (_state == kWeaselConfirmPurchaseText)
+ completePurchase();
+ else if (_state == kWeaselCheckBuyMoreText)
+ browsingMode(true);
+ break;
+ case kBtnNo:
+ if (_state == kWeaselConfirmPurchaseText)
+ abortPurchase();
+ else if (_state == kWeaselCheckBuyMoreText)
+ Close();
+ break;
+ case kBtnBlank:
+ default:
+ break;
+ }
+}
+
+void WeaselGump::updateAmmoButtons() {
+ _ammoMode = !_ammoMode;
+ Gump *ammobtn = _ui->FindGump(&FindUiElem<kBtnAmmo>);
+ Gump *wpnbtn = _ui->FindGump(&FindUiElem<kBtnWeapons>);
+ assert(ammobtn && wpnbtn);
+ ammobtn->SetVisibility(_ammoMode);
+ wpnbtn->SetVisibility(!_ammoMode);
+ _curItem = 0;
+ updateItemDisplay();
+}
+
+void WeaselGump::prevItem() {
+ WeaselDat::WeaselType curtype = _ammoMode ? WeaselDat::kItem : WeaselDat::kWeapon;
+ int itemcount = _weaselDat->getNumOfType(curtype);
+ _curItem--;
+ if (_curItem < 0)
+ _curItem = itemcount - 1;
+ updateItemDisplay();
+}
+
+void WeaselGump::nextItem() {
+ WeaselDat::WeaselType curtype = _ammoMode ? WeaselDat::kItem : WeaselDat::kWeapon;
+ int itemcount = _weaselDat->getNumOfType(curtype);
+ _curItem++;
+ if (_curItem >= itemcount)
+ _curItem = 0;
+ updateItemDisplay();
+}
+
+void WeaselGump::buyItem() {
+ if (_curItemCost < _credits) {
+ _purchases.push_back(_curItemShape);
+ _credits -= _curItemCost;
+ } else {
+ AudioProcess::get_instance()->playSFX(WEASEL_CANT_BUY_SFXNO, 0x80, 0, 0);
+ }
+ updateItemDisplay();
+}
+
+void WeaselGump::confirmPurchase() {
+ static const char *confirm = "Are you sure you want to buy this?";
+ setYesNoQuestion(confirm);
+}
+
+void WeaselGump::checkClose() {
+ if (_purchases.size()) {
+ _state = kWeaselConfirmPurchaseMovie;
+ } else {
+ Close();
+ }
+}
+
+void WeaselGump::completePurchase() {
+ assert(_state == kWeaselConfirmPurchaseText);
+ MainActor *av = getMainActor();
+ uint16 mapno = av->getMapNum();
+ assert(av);
+ Item *item = av->getFirstItemWithShape(0x4ed, true);
+ if (item)
+ item->setQuality(_credits);
+ for (Std::vector<uint16>::const_iterator iter = _purchases.begin();
+ iter != _purchases.end(); iter++) {
+ Item *newitem = ItemFactory::createItem(*iter, 0, 0, 0, 0, mapno, 0, true);
+ av->addItemCru(newitem, false);
+ }
+ _state = kWeaselCompletedPurchase;
+}
+
+void WeaselGump::checkBuyMore() {
+ static const char *buymore = "Do you want anything else?";
+ setYesNoQuestion(buymore);
+}
+
+void WeaselGump::setYesNoQuestion(const Std::string &msg) {
+ browsingMode(false);
+ _closeIfExists(_ui->FindGump(&FindUiElem<kTxtQuestion>));
+ TextWidget *textWidget = new TextWidget(30, 100, msg, true, WEASEL_FONT, 150);
+ textWidget->InitGump(_ui);
+ textWidget->SetIndex(kTxtQuestion);
+}
+
+void WeaselGump::browsingMode(bool browsing) {
+ _ui->UnhideGump();
+
+ updateAmmoButtons();
+ updateItemDisplay();
+
+ // Note: all these searches are not super effieient but it's
+ // not a time-sensitive function and the search is relatively short
+ Gump *yesbtn = _ui->FindGump(&FindUiElem<kBtnYes>);
+ Gump *nobtn = _ui->FindGump(&FindUiElem<kBtnNo>);
+ Gump *qtxt = _ui->FindGump(&FindUiElem<kTxtQuestion>);
+
+ Gump *buybtn = _ui->FindGump(&FindUiElem<kBtnBuy>);
+ Gump *wpnbtn = _ui->FindGump(&FindUiElem<kBtnWeapons>);
+ Gump *ammobtn = _ui->FindGump(&FindUiElem<kBtnAmmo>);
+ Gump *exitbtn = _ui->FindGump(&FindUiElem<kBtnExit>);
+ Gump *blankbtn = _ui->FindGump(&FindUiElem<kBtnBlank>);
+ Gump *leftbtn = _ui->FindGump(&FindUiElem<kBtnLeft>);
+ Gump *rightbtn = _ui->FindGump(&FindUiElem<kBtnRight>);
+ Gump *credtxt = _ui->FindGump(&FindUiElem<kTxtCredits>);
+ Gump *nametxt = _ui->FindGump(&FindUiElem<kTxtItemName>);
+ Gump *costtxt = _ui->FindGump(&FindUiElem<kTxtItemCost>);
+ Gump *purchtxt = _ui->FindGump(&FindUiElem<kTxtItemPurch>);
+ Gump *ownedtxt = _ui->FindGump(&FindUiElem<kTxtItemOwned>);
+ Gump *icon = _ui->FindGump(&FindUiElem<kIconItem>);
+
+ yesbtn->SetVisibility(!browsing);
+ nobtn->SetVisibility(!browsing);
+ if (qtxt)
+ qtxt->SetVisibility(!browsing);
+
+ buybtn->SetVisibility(browsing);
+ wpnbtn->SetVisibility(browsing && !_ammoMode);
+ ammobtn->SetVisibility(browsing && _ammoMode);
+ exitbtn->SetVisibility(browsing);
+ blankbtn->SetVisibility(browsing);
+ leftbtn->SetVisibility(browsing);
+ rightbtn->SetVisibility(browsing);
+ credtxt->SetVisibility(browsing);
+ nametxt->SetVisibility(browsing);
+ costtxt->SetVisibility(browsing);
+ purchtxt->SetVisibility(browsing);
+ ownedtxt->SetVisibility(browsing);
+ icon->SetVisibility(browsing);
+}
+
+void WeaselGump::abortPurchase() {
+ assert(_state == kWeaselConfirmPurchaseText);
+ _state = kWeaselCancelledPurchaseMovie;
+ _purchases.clear();
+}
+
+int WeaselGump::purchasedCount(uint16 shape) const {
+ int count = 0;
+ for (Std::vector<uint16>::const_iterator iter = _purchases.begin();
+ iter != _purchases.end(); iter++) {
+ if (*iter == shape)
+ count++;
+ }
+ return count;
+}
+
+void WeaselGump::updateItemDisplay() {
+ WeaselDat::WeaselType curtype = _ammoMode ? WeaselDat::kItem : WeaselDat::kWeapon;
+ const Std::vector<WeaselDat::WeaselEntry> &items = _weaselDat->getItems();
+ Std::vector<WeaselDat::WeaselEntry>::const_iterator iter = items.begin();
+
+ int i = 0;
+ for (; iter != items.end(); iter++) {
+ if (iter->_type == curtype) {
+ if (i == _curItem)
+ break;
+ else
+ i++;
+ }
+ }
+
+ // should always find the item..
+ assert(iter != items.end());
+
+ _curItemCost = iter->_cost;
+ _curItemShape = iter->_shapeNo;
+
+ const ShapeInfo *shapeinfo = GameData::get_instance()->getMainShapes()->getShapeInfo(_curItemShape);
+ if (!shapeinfo || !shapeinfo->_weaponInfo) {
+ warning("Weasel: no info for shape %d", _curItemShape);
+ }
+ Shape *shape = GameData::get_instance()->getGumps()->getShape(shapeinfo->_weaponInfo->_displayGumpShape);
+
+ _closeIfExists(_ui->FindGump(&FindUiElem<kTxtCredits>));
+ _closeIfExists(_ui->FindGump(&FindUiElem<kTxtItemName>));
+ _closeIfExists(_ui->FindGump(&FindUiElem<kTxtItemCost>));
+ _closeIfExists(_ui->FindGump(&FindUiElem<kTxtItemPurch>));
+ _closeIfExists(_ui->FindGump(&FindUiElem<kTxtItemOwned>));
+ _closeIfExists(_ui->FindGump(&FindUiElem<kIconItem>));
+
+ Std::string credstr = Std::string::format("Credits:%d", _credits);
+ TextWidget *textWidget = new TextWidget(30, 57, credstr, true, WEASEL_FONT);
+ textWidget->InitGump(_ui);
+ textWidget->SetIndex(kTxtCredits);
+
+ const ShapeFrame *frame = shape->getFrame(shapeinfo->_weaponInfo->_displayGumpFrame);
+ Gump *icon = new Gump(105 - frame->_xoff, 120 - frame->_yoff, 200, 200);
+ icon->SetShape(shape, shapeinfo->_weaponInfo->_displayGumpFrame);
+ icon->UpdateDimsFromShape();
+ icon->setRelativePosition(Position::CENTER);
+ icon->InitGump(_ui, false);
+ icon->SetIndex(kIconItem);
+
+ Std::string coststr = Std::string::format("Cost:%d", _curItemCost);
+ Std::string purchstr = Std::string::format("Purchased:%02d", purchasedCount(_curItemShape));
+
+ MainActor *av = getMainActor();
+ const Item *item = av->getFirstItemWithShape(_curItemShape, true);
+ int count = 0;
+ if (item) {
+ if (shapeinfo->_family == ShapeInfo::SF_CRUWEAPON) {
+ count = 1;
+ } else {
+ count = item->getQuality();
+ }
+ }
+ Std::string ownedstr = Std::string::format("Owned:%02d", count);
+
+ TextWidget *nametxt = new TextWidget(27, 161, shapeinfo->_weaponInfo->_name, true, WEASEL_FONT);
+ nametxt->InitGump(_ui, false);
+ nametxt->SetIndex(kTxtItemName);
+ TextWidget *costtxt = new TextWidget(27, 171, coststr, true, WEASEL_FONT);
+ costtxt->InitGump(_ui, false);
+ costtxt->SetIndex(kTxtItemCost);
+ TextWidget *purchtxt = new TextWidget(27, 181, purchstr, true, WEASEL_FONT);
+ purchtxt->InitGump(_ui, false);
+ purchtxt->SetIndex(kTxtItemPurch);
+ TextWidget *ownedtxt = new TextWidget(27, 191, ownedstr, true, WEASEL_FONT);
+ ownedtxt->InitGump(_ui, false);
+ ownedtxt->SetIndex(kTxtItemOwned);
+}
+
+bool WeaselGump::OnTextInput(int unicode) {
+ if (Gump::OnTextInput(unicode)) return true;
+
+ return true;
+}
+
+//static
+uint32 WeaselGump::I_showWeaselGump(const uint8 *args, unsigned int /*argsize*/) {
+ ARG_UINT16(level);
+
+ WeaselGump *gump = new WeaselGump(level);
+ gump->InitGump(0);
+ gump->setRelativePosition(CENTER);
+
+ return 0;
+}
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/gumps/weasel_gump.h b/engines/ultima/ultima8/gumps/weasel_gump.h
new file mode 100644
index 0000000000..7e6a3e23a8
--- /dev/null
+++ b/engines/ultima/ultima8/gumps/weasel_gump.h
@@ -0,0 +1,132 @@
+/* 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_GUMPS_REMORSEMENUGUMP_H
+#define ULTIMA8_GUMPS_REMORSEMENUGUMP_H
+
+#include "ultima/ultima8/gumps/modal_gump.h"
+#include "ultima/ultima8/misc/p_dynamic_cast.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+class WeaselDat;
+
+/**
+ * Weasel weapon seller Crusader.
+ */
+class WeaselGump : public ModalGump {
+public:
+ ENABLE_RUNTIME_CLASSTYPE()
+
+ enum WeaselGumpState {
+ kWeaselStart,
+ kWeaselConfirmPurchaseMovie,
+ kWeaselConfirmPurchaseText,
+ kWeaselCancelledPurchaseMovie,
+ kWeaselCancelledPurchaseText,
+ kWeaselCompletedPurchase,
+ kWeaselInsufficientFunds,
+ kWeaselBrowsing,
+ kWeaselClosing,
+ kWeaselCheckBuyMoreMovie,
+ kWeaselCheckBuyMoreText,
+ kWeaselShowIntro
+ };
+
+ WeaselGump(uint16 level);
+ ~WeaselGump() override;
+
+ // Init the gump, call after construction
+ void InitGump(Gump *newparent, bool take_focus = true) override;
+ void Close(bool no_del = false) override;
+
+ void run() override;
+
+ // Paint the Gump
+ void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
+
+ bool OnKeyDown(int key, int mod) override;
+ bool OnTextInput(int unicode) override;
+ void ChildNotify(Gump *child, uint32 message) override;
+
+ static uint32 I_showWeaselGump(const uint8 *args, unsigned int /*argsize*/);
+
+private:
+
+ void onButtonClick(int entry);
+
+ void prevItem();
+ void nextItem();
+ void buyItem();
+ void updateAmmoButtons();
+ void checkClose();
+ void completePurchase();
+ void abortPurchase();
+ void checkBuyMore();
+ void confirmPurchase();
+ void setYesNoQuestion(const Std::string &msg);
+ void browsingMode(bool browsing);
+ int purchasedCount(uint16 shape) const;
+
+ void updateItemDisplay();
+ Gump *playMovie(const Std::string &filename);
+
+ /// Gump to hold all the UI items (not the movies)
+ Gump *_ui;
+
+ /// Gump for playing movies
+ Gump *_movie;
+
+ /// The menu of items on offer
+ uint16 _level;
+
+ /// Current gump state
+ WeaselGumpState _state;
+
+ const WeaselDat *_weaselDat;
+
+ /// Remaining balance including pending purchases
+ int32 _credits;
+
+ /// The list of pending purchases (shape nums)
+ Std::vector<uint16> _purchases;
+
+ /// The current item num being browsed
+ int _curItem;
+
+ /// Whether we're browsing ammo or weapons
+ bool _ammoMode;
+
+ /// Cost of current item
+ int32 _curItemCost;
+
+ /// Shape of current item
+ uint16 _curItemShape;
+
+ static bool _playedIntroMovie;
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
diff --git a/engines/ultima/ultima8/ultima8.cpp b/engines/ultima/ultima8/ultima8.cpp
index 913c748d3c..0805181c96 100644
--- a/engines/ultima/ultima8/ultima8.cpp
+++ b/engines/ultima/ultima8/ultima8.cpp
@@ -63,6 +63,7 @@
#include "ultima/ultima8/gumps/menu_gump.h"
#include "ultima/ultima8/gumps/cru_status_gump.h"
#include "ultima/ultima8/gumps/movie_gump.h"
+#include "ultima/ultima8/gumps/weasel_gump.h"
// For gump positioning... perhaps shouldn't do it this way....
#include "ultima/ultima8/gumps/bark_gump.h"
diff --git a/engines/ultima/ultima8/usecode/remorse_intrinsics.h b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
index 08994a618d..dd1faf6aa1 100644
--- a/engines/ultima/ultima8/usecode/remorse_intrinsics.h
+++ b/engines/ultima/ultima8/usecode/remorse_intrinsics.h
@@ -362,7 +362,7 @@ Intrinsic RemorseIntrinsics[] = {
Actor::I_setActivity, // void Intrinsic131(6 bytes)
Item::I_andStatus, // void Intrinsic132(6 bytes)
Item::I_getQHi, // based on same coff set as 026
- 0, // void Intrinsic134(2 bytes)
+ WeaselGump::I_showWeaselGump, // void Intrinsic134(2 bytes)
Actor::I_setDead,
0, // void UNUSEDInt136()
0 // void UNUSEDInt137()
Commit: 9e8b97159dd780858eaeeca46de9ebf09b9f70cd
https://github.com/scummvm/scummvm/commit/9e8b97159dd780858eaeeca46de9ebf09b9f70cd
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:45:36+09:00
Commit Message:
ULTIMA8: Workaround to avoid No Regret crashing
Changed paths:
engines/ultima/ultima8/world/item.cpp
diff --git a/engines/ultima/ultima8/world/item.cpp b/engines/ultima/ultima8/world/item.cpp
index 37cdad8185..99e4610b7c 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -1316,6 +1316,7 @@ uint16 Item::fireDistance(Item *other, Direction dir, int16 xoff, int16 yoff, in
else
anim = Animation::kneelAndFireLargeWeapon;
} else {
+ // TODO: fire2 seems to be different in Regret, check me.
if (ma || smallwpn)
anim = Animation::attack;
else
@@ -1324,7 +1325,8 @@ uint16 Item::fireDistance(Item *other, Direction dir, int16 xoff, int16 yoff, in
bool first_offsets = false;
const AnimAction *action = GameData::get_instance()->getMainShapes()->getAnim(_shape, static_cast<int32>(anim));
- for (unsigned int i = 0; i < action->getSize(); i++) {
+ unsigned int nframes = action ? action->getSize() : 0;
+ for (unsigned int i = 0; i < nframes; i++) {
const AnimFrame &frame = action->getFrame(dir, i);
if (frame.is_cruattack()) {
if (!first_offsets) {
Commit: 00b0438c34e78c504224c41080537f11aaf5068d
https://github.com/scummvm/scummvm/commit/00b0438c34e78c504224c41080537f11aaf5068d
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:45:36+09:00
Commit Message:
ULTIMA8: Fixes for Remorse startup
Changed paths:
engines/ultima/ultima8/games/start_crusader_process.cpp
diff --git a/engines/ultima/ultima8/games/start_crusader_process.cpp b/engines/ultima/ultima8/games/start_crusader_process.cpp
index b89ef76375..1225babc01 100644
--- a/engines/ultima/ultima8/games/start_crusader_process.cpp
+++ b/engines/ultima/ultima8/games/start_crusader_process.cpp
@@ -89,26 +89,23 @@ void StartCrusaderProcess::run() {
Ultima8Engine::get_instance()->setCheatMode(true);
if (!_skipStart) {
+ MainActor *avatar = getMainActor();
+ int mapnum = avatar->getMapNum();
+
+ // These items are the same in Regret and Remorse
+ Item *datalink = ItemFactory::createItem(0x4d4, 0, 0, 0, 0, mapnum, 0, true);
+ avatar->addItemCru(datalink, false);
+ Item *smiley = ItemFactory::createItem(0x598, 0, 0, 0, 0, mapnum, 0, true);
+ smiley->moveToContainer(avatar);
+
if (GAME_IS_REMORSE) {
- MainActor *avatar = getMainActor();
- int mapnum = avatar->getMapNum();
- // The game doesn't do the weapon this way, but it's the same for our purposes..
- Item *weapon = ItemFactory::createItem(0x32E, 0, 0, 0, 0, mapnum, 0, true);
- avatar->addItemCru(weapon, false);
- Item *datalink = ItemFactory::createItem(0x4d4, 0, 0, 0, 0, mapnum, 0, true);
- avatar->addItemCru(datalink, false);
- Item *smiley = ItemFactory::createItem(0x598, 0, 0, 0, 0, mapnum, 0, true);
- smiley->moveToContainer(avatar);
-
- avatar->setDir(dir_east);
+ // TODO: The game actually teleports to egg 0x1e (30) which has another
+ // egg to teleport to egg 99. Is there any purpose to that?
+ Kernel::get_instance()->addProcess(new TeleportToEggProcess(1, 99));
} else if (GAME_IS_REGRET) {
- // TODO: Give the appropriate startup objects to the avatar
+ Kernel::get_instance()->addProcess(new TeleportToEggProcess(1, 0x1e));
}
- // TODO: The game actually teleports to egg 0x1f (31) which has another
- // egg to teleport to egg 99. Is there any purpose to that?
- Kernel::get_instance()->addProcess(new TeleportToEggProcess(1, 99));
-
Process *fader = new PaletteFaderProcess(0x003F3F3F, true, 0x7FFF, 60, false);
Kernel::get_instance()->addProcess(fader);
}
Commit: a3340fe6a6a68ddc0053c2bbb90da64ec4d3c2e4
https://github.com/scummvm/scummvm/commit/a3340fe6a6a68ddc0053c2bbb90da64ec4d3c2e4
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:45:36+09:00
Commit Message:
ULTIMA8: Fix Crusader KeypadGump to actually work
Changed paths:
engines/ultima/ultima8/gumps/keypad_gump.cpp
engines/ultima/ultima8/gumps/keypad_gump.h
diff --git a/engines/ultima/ultima8/gumps/keypad_gump.cpp b/engines/ultima/ultima8/gumps/keypad_gump.cpp
index 5c8381fd0d..3ca0483b60 100644
--- a/engines/ultima/ultima8/gumps/keypad_gump.cpp
+++ b/engines/ultima/ultima8/gumps/keypad_gump.cpp
@@ -28,18 +28,27 @@
#include "ultima/ultima8/graphics/gump_shape_archive.h"
#include "ultima/ultima8/graphics/shape.h"
#include "ultima/ultima8/graphics/shape_frame.h"
+#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/gumps/desktop_gump.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
+#include "ultima/ultima8/usecode/uc_process.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(KeypadGump)
-KeypadGump::KeypadGump(int targetValue): ModalGump(0, 0, 5, 5),
- _value(0), _targetValue(targetValue) {
+template<int T> bool FindUiElem(const Gump *g) { return g->GetIndex() == T; }
+
+static const int TXT_CONTAINER_IDX = 0x100;
+// Actually the max val where we will allow another digit to be entered
+static const int MAX_CODE_VAL = 9999999;
+static const int CHEAT_CODE_VAL = 74697689;
+
+KeypadGump::KeypadGump(int targetValue, uint16 ucnotifypid): ModalGump(0, 0, 5, 5),
+ _value(0), _targetValue(targetValue), _ucNotifyPid(ucnotifypid) {
Mouse *mouse = Mouse::get_instance();
mouse->pushMouseCursor();
mouse->setMouseCursor(Mouse::MOUSE_HAND);
@@ -73,6 +82,8 @@ void KeypadGump::InitGump(Gump *newparent, bool take_focus) {
_buttons[bnum] = widget->getObjId();
}
}
+ // Default result is 0xff
+ SetResult(0xff);
}
void KeypadGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
@@ -83,7 +94,26 @@ void KeypadGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled)
bool KeypadGump::OnKeyDown(int key, int mod) {
switch (key) {
case Common::KEYCODE_ESCAPE: {
+ _value = 0xff;
Close();
+ break;
+ }
+ case Common::KEYCODE_0:
+ case Common::KEYCODE_1:
+ case Common::KEYCODE_2:
+ case Common::KEYCODE_3:
+ case Common::KEYCODE_4:
+ case Common::KEYCODE_5:
+ case Common::KEYCODE_6:
+ case Common::KEYCODE_7:
+ case Common::KEYCODE_8:
+ case Common::KEYCODE_9: {
+ onDigit(key - (int)Common::KEYCODE_0);
+ updateDigitDisplay();
+ AudioProcess *audio = AudioProcess::get_instance();
+ if (audio)
+ audio->playSFX(0x3b, 0x10, _objId, 1);
+ break;
}
break;
default:
@@ -93,40 +123,94 @@ bool KeypadGump::OnKeyDown(int key, int mod) {
return true;
}
+void KeypadGump::onDigit(int digit) {
+ assert(digit >= 0 && digit <= 9);
+ if (_value < MAX_CODE_VAL) {
+ _value *= 10;
+ _value += digit;
+ }
+}
+
void KeypadGump::ChildNotify(Gump *child, uint32 message) {
//ObjId cid = child->getObjId();
+ bool update = true;
if (message == ButtonWidget::BUTTON_CLICK) {
- uint16 sfxno = 0;
+ uint16 sfxno = 0x3b;
int buttonNo = child->GetIndex();
if (buttonNo < 9) {
- _value *= 10;
- _value += buttonNo + 1;
- sfxno = 0x3b;
+ onDigit(buttonNo + 1);
} else if (buttonNo == 10) {
- _value *= 10;
- sfxno = 0x3b;
+ onDigit(0);
} else if (buttonNo == 9) {
_value /= 10;
sfxno = 0x3a;
} else if (buttonNo == 11) {
- SetResult(_value);
- // TODO: Do something as a result of this other than just play a sound.
- if (_value == _targetValue) {
+ update = false;
+ if (_value == _targetValue || _value == CHEAT_CODE_VAL) {
sfxno = 0x32;
- Close();
- // Note: careful, this is now deleted.
+ SetResult(_value);
} else {
// wrong.
sfxno = 0x31;
- _value = 0;
+ SetResult(0);
}
+ Close();
+ // Note: careful, this is now deleted.
}
AudioProcess *audio = AudioProcess::get_instance();
if (audio && sfxno)
audio->playSFX(sfxno, 0x10, _objId, 1);
}
+ if (update) {
+ updateDigitDisplay();
+ }
+}
+
+void KeypadGump::updateDigitDisplay() {
+ Gump *txt = Gump::FindGump(&FindUiElem<TXT_CONTAINER_IDX>);
+ if (txt)
+ txt->Close();
+ txt = new Gump(25, 12, 200, 12);
+ txt->InitGump(this);
+ txt->SetIndex(TXT_CONTAINER_IDX);
+
+ Std::vector<Gump *> digits;
+ Shape *digitshape = GameData::get_instance()->getGumps()->getShape(12);
+ int val = _value;
+ while (val) {
+ int digitval = val % 10;
+ if (digitval == 0)
+ digitval = 10;
+ Gump *digit = new Gump(0, 0, 6, 12);
+ digit->SetShape(digitshape, digitval - 1);
+ digit->InitGump(txt);
+ digits.push_back(digit);
+ val /= 10;
+ }
+
+ int xoff = 0;
+ while (digits.size()) {
+ Gump *digit = digits.back();
+ digits.pop_back();
+ digit->setRelativePosition(Position::TOP_LEFT, xoff);
+ xoff += 6;
+ }
+}
+
+void KeypadGump::Close(bool no_del) {
+ _processResult = _value;
+
+ if (_ucNotifyPid) {
+ UCProcess *ucp = dynamic_cast<UCProcess *>(Kernel::get_instance()->getProcess(_ucNotifyPid));
+ assert(ucp);
+ ucp->setReturnValue(_value);
+ ucp->wakeUp(_value);
+ }
+
+ ModalGump::Close(no_del);
}
+
bool KeypadGump::OnTextInput(int unicode) {
if (!(unicode & 0xFF80)) {
//char c = unicode & 0x7F;
@@ -137,12 +221,17 @@ bool KeypadGump::OnTextInput(int unicode) {
uint32 KeypadGump::I_showKeypad(const uint8 *args, unsigned int /*argsize*/) {
ARG_UINT16(target)
- ModalGump *gump = new KeypadGump(target);
+
+ UCProcess *current = dynamic_cast<UCProcess *>(Kernel::get_instance()->getRunningProcess());
+ assert(current);
+
+ ModalGump *gump = new KeypadGump(target, current->getPid());
gump->InitGump(0);
gump->setRelativePosition(CENTER);
- gump->CreateNotifier();
- return gump->GetNotifyProcess()->getPid();
+ current->suspend();
+
+ return 0;
}
bool KeypadGump::loadData(Common::ReadStream *rs) {
diff --git a/engines/ultima/ultima8/gumps/keypad_gump.h b/engines/ultima/ultima8/gumps/keypad_gump.h
index bb8b75e3c9..2ce472ab22 100644
--- a/engines/ultima/ultima8/gumps/keypad_gump.h
+++ b/engines/ultima/ultima8/gumps/keypad_gump.h
@@ -37,9 +37,11 @@ class KeypadGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
- KeypadGump(int targetValue);
+ KeypadGump(int targetValue, uint16 ucnotifypid);
~KeypadGump() override;
+ void Close(bool no_del=false) override;
+
void InitGump(Gump *newparent, bool take_focus = true) override;
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
@@ -54,10 +56,15 @@ public:
void saveData(Common::WriteStream *ws) override;
protected:
+ void updateDigitDisplay();
+
+ void onDigit(int digit);
+
ObjId _buttons[12];
int _value;
int _targetValue;
+ uint16 _ucNotifyPid;
};
} // End of namespace Ultima8
Commit: b566a9b2e3c3aa78386a5e6abfc4e424c42a1b54
https://github.com/scummvm/scummvm/commit/b566a9b2e3c3aa78386a5e6abfc4e424c42a1b54
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:45:53+09:00
Commit Message:
ULTIMA8: Delete some crusader hacks from Actor
Changed paths:
engines/ultima/ultima8/world/actors/actor.cpp
engines/ultima/ultima8/world/actors/actor.h
diff --git a/engines/ultima/ultima8/world/actors/actor.cpp b/engines/ultima/ultima8/world/actors/actor.cpp
index 4edfb20709..9628ab4fc7 100644
--- a/engines/ultima/ultima8/world/actors/actor.cpp
+++ b/engines/ultima/ultima8/world/actors/actor.cpp
@@ -462,8 +462,6 @@ void Actor::teleport(int newmap, int32 newx, int32 newy, int32 newz) {
_y = newy;
_z = newz;
}
- if (GAME_IS_CRUSADER)
- notifyNearbyItems();
}
uint16 Actor::doAnim(Animation::Sequence anim, Direction dir, unsigned int steps) {
@@ -1467,39 +1465,13 @@ void Actor::clearInCombat() {
int32 Actor::collideMove(int32 x, int32 y, int32 z, bool teleports, bool force,
ObjId *hititem, uint8 *dirs) {
int32 result = Item::collideMove(x, y, z, teleports, force, hititem, dirs);
- if (_objId == 1 && GAME_IS_CRUSADER) {
- notifyNearbyItems();
+ if (this == getControlledActor() && GAME_IS_CRUSADER) {
TargetReticleProcess::get_instance()->avatarMoved();
ItemSelectionProcess::get_instance()->avatarMoved();
}
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_equipWithParam(_objId);
- _notifiedItems.insert(item->getObjId());
- }*/
-}
-
bool Actor::activeWeaponIsSmall() const {
const Item *wpn = getItem(_activeWeapon);
if (wpn) {
diff --git a/engines/ultima/ultima8/world/actors/actor.h b/engines/ultima/ultima8/world/actors/actor.h
index 5a97a25bc9..7358e69c34 100644
--- a/engines/ultima/ultima8/world/actors/actor.h
+++ b/engines/ultima/ultima8/world/actors/actor.h
@@ -201,9 +201,6 @@ public:
//! check if NPCs are near which are in combat mode and hostile
bool areEnemiesNear();
- //! check if NPCs are near which are in combat mode and hostile
- void notifyNearbyItems();
-
//! starts an activity
//! \return processID of process handling the activity or zero
uint16 setActivity(int activity);
Commit: d0b9f00be86199b53b87f96d12ce283d833080b4
https://github.com/scummvm/scummvm/commit/d0b9f00be86199b53b87f96d12ce283d833080b4
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:45:53+09:00
Commit Message:
ULTIMA8: Hide Crusader status gumps in movies
Changed paths:
engines/ultima/ultima8/gumps/movie_gump.cpp
diff --git a/engines/ultima/ultima8/gumps/movie_gump.cpp b/engines/ultima/ultima8/gumps/movie_gump.cpp
index cba32bb5e4..57dd4234df 100644
--- a/engines/ultima/ultima8/gumps/movie_gump.cpp
+++ b/engines/ultima/ultima8/gumps/movie_gump.cpp
@@ -36,6 +36,7 @@
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/gumps/desktop_gump.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
+#include "ultima/ultima8/gumps/cru_status_gump.h"
#include "ultima/ultima8/filesys/file_system.h"
@@ -72,11 +73,21 @@ void MovieGump::InitGump(Gump *newparent, bool take_focus) {
Mouse::get_instance()->pushMouseCursor();
Mouse::get_instance()->setMouseCursor(Mouse::MOUSE_NONE);
+
+ CruStatusGump *statusgump = CruStatusGump::get_instance();
+ if (statusgump) {
+ statusgump->HideGump();
+ }
}
void MovieGump::Close(bool no_del) {
Mouse::get_instance()->popMouseCursor();
+ CruStatusGump *statusgump = CruStatusGump::get_instance();
+ if (statusgump) {
+ statusgump->UnhideGump();
+ }
+
_player->stop();
ModalGump::Close(no_del);
Commit: cc1ad1749fdd4d21f317f5bed52abfdb6202cb94
https://github.com/scummvm/scummvm/commit/cc1ad1749fdd4d21f317f5bed52abfdb6202cb94
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:46:02+09:00
Commit Message:
ULTIMA8: Don't save Crusader stat gumps
Changed paths:
engines/ultima/ultima8/gumps/cru_stat_gump.cpp
engines/ultima/ultima8/gumps/translucent_gump.cpp
diff --git a/engines/ultima/ultima8/gumps/cru_stat_gump.cpp b/engines/ultima/ultima8/gumps/cru_stat_gump.cpp
index 01557fdb1f..1c041353d4 100644
--- a/engines/ultima/ultima8/gumps/cru_stat_gump.cpp
+++ b/engines/ultima/ultima8/gumps/cru_stat_gump.cpp
@@ -40,7 +40,7 @@ CruStatGump::CruStatGump() : TranslucentGump() {
}
CruStatGump::CruStatGump(Shape *shape, int x)
- : TranslucentGump(x, 0, 5, 5, 0) {
+ : TranslucentGump(x, 0, 5, 5, 0, FLAG_DONT_SAVE) {
_shape = shape;
}
diff --git a/engines/ultima/ultima8/gumps/translucent_gump.cpp b/engines/ultima/ultima8/gumps/translucent_gump.cpp
index ea7d25fbc9..c916ca328f 100644
--- a/engines/ultima/ultima8/gumps/translucent_gump.cpp
+++ b/engines/ultima/ultima8/gumps/translucent_gump.cpp
@@ -33,7 +33,7 @@ TranslucentGump::TranslucentGump() : Gump() {
TranslucentGump::TranslucentGump(int x, int y, int width, int height,
uint16 owner, uint32 flags, int32 layer) :
- Gump(x, y, width, height, owner, flags ,layer) {
+ Gump(x, y, width, height, owner, flags, layer) {
}
TranslucentGump::~TranslucentGump() {
Commit: 3fd36166ba9f64f54ff7f199b73c5c5f5f0469ae
https://github.com/scummvm/scummvm/commit/3fd36166ba9f64f54ff7f199b73c5c5f5f0469ae
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-12-27T08:46:02+09:00
Commit Message:
ULTIMA8: Fix Crusader anim dat flags, again
Changed paths:
engines/ultima/ultima8/graphics/anim_dat.cpp
engines/ultima/ultima8/world/actors/anim_action.h
diff --git a/engines/ultima/ultima8/graphics/anim_dat.cpp b/engines/ultima/ultima8/graphics/anim_dat.cpp
index 4ced82e13b..d05e5be74b 100644
--- a/engines/ultima/ultima8/graphics/anim_dat.cpp
+++ b/engines/ultima/ultima8/graphics/anim_dat.cpp
@@ -189,11 +189,19 @@ void AnimDat::load(Common::SeekableReadStream *rs) {
a->_actions[action]->_size = actionsize;
// byte 1: flags low byte
uint32 rawflags = rs->readByte();
- // byte 2: frame repeat
- a->_actions[action]->_frameRepeat = rs->readByte();
+ // byte 2: frame repeat and rotated flag
+ byte repeatAndRotateFlag = rs->readByte();
+ a->_actions[action]->_frameRepeat = repeatAndRotateFlag & 0xf;
+ if (GAME_IS_U8 && (repeatAndRotateFlag & 0xf0)) {
+ // This should never happen..
+ error("Anim data: frame repeat byte should never be > 0xf");
+ }
// byte 3: flags high byte
rawflags |= rs->readByte() << 8;
+ // Only one flag in this byte in crusader.. the "rotate" flag.
+ rawflags |= (repeatAndRotateFlag & 0xf0) << 12;
+
a->_actions[action]->_flags = AnimAction::loadAnimActionFlags(rawflags);
unsigned int dirCount = 8;
diff --git a/engines/ultima/ultima8/world/actors/anim_action.h b/engines/ultima/ultima8/world/actors/anim_action.h
index 5420eadfc6..490163030d 100644
--- a/engines/ultima/ultima8/world/actors/anim_action.h
+++ b/engines/ultima/ultima8/world/actors/anim_action.h
@@ -158,9 +158,9 @@ public:
AAF_ENDLOOP_U8 = 0x0020, // TODO: This starts a new anim at the end if pathfinding
AAF_ENDLOOP_CRU = 0x0040, // TODO: This starts a new anim at the end if pathfinding
AAF_HANGING = 0x0080,
- AAF_ROTATED = 0x1000, // Cru only
AAF_16DIRS = 0x4000, // Cru only
AAF_DESTROYACTOR = 0x8000, // destroy actor after animation finishes
+ AAF_ROTATED = 0x100000, // Cru only
AAF_COMMONFLAGS = (AAF_TWOSTEP | AAF_LOOPING | AAF_UNSTOPPABLE | AAF_HANGING | AAF_DESTROYACTOR)
};
More information about the Scummvm-git-logs
mailing list