[Scummvm-git-logs] scummvm master -> 4232c57339cd3210d83a95e0e8c733e0a633f706
mduggan
mgithub at guarana.org
Wed Jun 23 23:52:02 UTC 2021
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:
c1b58fac91 ULTIMA8: Make Crusader keypad cheat code work
3afa936638 ULTIMA8: Fix Vargas receiveHit special case for No Regret
b697358e2e ULTIMA8: Implement Crusader: No Remorse credits
fbc4da2d83 IMAGE: Mention usage of bmp decoder by ultima8 engine
62cd4febd2 DEVTOOLS: ULTIMA8: Fix font spacing for Crusader inventory
f8410eca57 ULTIMA8: Show credits in Crusader inventory
9790fe6b96 ULTIMA8: Add support for the datalink video frame in Cruasder
716af7bf67 ULTIMA8: Fix isPlaying to check mixer
8013549807 ULTIMA8: Implement incremental display for Crusader computer gumps
1fad9c883f ULTIMA8: Destroy items that fall through the floor
3c2fa9dfd7 ULTIMA8: Small fixes to Crusader fireDistance intrinsic
4232c57339 ULTIMA8: Small fixes for Crusader pathfinder and attack processes
Commit: c1b58fac91c821159951b99e193c9e9fec765053
https://github.com/scummvm/scummvm/commit/c1b58fac91c821159951b99e193c9e9fec765053
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:08+09:00
Commit Message:
ULTIMA8: Make Crusader keypad cheat code work
Changed paths:
engines/ultima/ultima8/gumps/keypad_gump.cpp
diff --git a/engines/ultima/ultima8/gumps/keypad_gump.cpp b/engines/ultima/ultima8/gumps/keypad_gump.cpp
index 4ac9e51668..187243bb33 100644
--- a/engines/ultima/ultima8/gumps/keypad_gump.cpp
+++ b/engines/ultima/ultima8/gumps/keypad_gump.cpp
@@ -104,7 +104,7 @@ bool KeypadGump::OnKeyDown(int key, int mod) {
case Common::KEYCODE_RETURN:
if (_value == _targetValue || _value == CHEAT_CODE_VAL) {
sfxno = SFXNO_CORRECT;
- SetResult(_value);
+ SetResult(_targetValue);
} else {
// wrong.
sfxno = SFXNO_WRONG;
Commit: 3afa9366384ad69ea4f40faa1ac1fd3706a93dde
https://github.com/scummvm/scummvm/commit/3afa9366384ad69ea4f40faa1ac1fd3706a93dde
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:08+09:00
Commit Message:
ULTIMA8: Fix Vargas receiveHit special case for No Regret
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 c6a4296bcb..6fe95402fb 100644
--- a/engines/ultima/ultima8/world/actors/actor.cpp
+++ b/engines/ultima/ultima8/world/actors/actor.cpp
@@ -908,13 +908,16 @@ void Actor::receiveHitCru(uint16 other, Direction dir, int damage, uint16 damage
// Special case for Vargas, who has a shield.
if (GAME_IS_REMORSE && shape == 0x3ac && world->getVargasShield() > 0) {
+ uint16 currentanim = 0;
if (isBusy()) {
ActorAnimProcess *proc = dynamic_cast<ActorAnimProcess *>(Kernel::get_instance()->findProcess(_objId, ActorAnimProcess::ACTOR_ANIM_PROC_TYPE));
- if (proc->getAction() == Animation::teleportIn || proc->getAction() == Animation::teleportOut || proc->getAction() == Animation::teleportInReplacement || proc->getAction() == Animation::teleportOutReplacement)
+ Animation::Sequence action = proc->getAction();
+ if (action == Animation::teleportIn || action == Animation::teleportOut || action == Animation::teleportInReplacement || action == Animation::teleportOutReplacement)
return;
+ currentanim = proc->getPid();
}
- ProcId teleout = doAnim(Animation::teleportOutReplacement, dir_current);
+ ProcId teleout = doAnimAfter(Animation::teleportOutReplacement, dir_current, currentanim);
doAnimAfter(Animation::teleportInReplacement, dir_current, teleout);
int newval = MAX(0, static_cast<int>(world->getVargasShield()) - damage);
world->setVargasShield(static_cast<uint32>(newval));
Commit: b697358e2e7a58b80daa1df8d9a3870a311e92e1
https://github.com/scummvm/scummvm/commit/b697358e2e7a58b80daa1df8d9a3870a311e92e1
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:08+09:00
Commit Message:
ULTIMA8: Implement Crusader: No Remorse credits
Changed paths:
A engines/ultima/ultima8/gumps/remorse_credits_gump.cpp
A engines/ultima/ultima8/gumps/remorse_credits_gump.h
engines/ultima/module.mk
engines/ultima/ultima8/games/game.cpp
engines/ultima/ultima8/games/remorse_game.cpp
diff --git a/engines/ultima/module.mk b/engines/ultima/module.mk
index b5f517824e..aa9236de01 100644
--- a/engines/ultima/module.mk
+++ b/engines/ultima/module.mk
@@ -480,6 +480,7 @@ MODULE_OBJS := \
ultima8/gumps/quit_gump.o \
ultima8/gumps/readable_gump.o \
ultima8/gumps/remorse_menu_gump.o \
+ ultima8/gumps/remorse_credits_gump.o \
ultima8/gumps/resizable_gump.o \
ultima8/gumps/scroll_gump.o \
ultima8/gumps/shape_viewer_gump.o \
diff --git a/engines/ultima/ultima8/games/game.cpp b/engines/ultima/ultima8/games/game.cpp
index e4d576643d..d81b8899b7 100644
--- a/engines/ultima/ultima8/games/game.cpp
+++ b/engines/ultima/ultima8/games/game.cpp
@@ -80,13 +80,7 @@ uint32 Game::I_playEndgame(const uint8 *args, unsigned int /*argsize*/) {
}
uint32 Game::I_playCredits(const uint8 *args, unsigned int /*argsize*/) {
- perr << "TODO: Implement I_playCredits";
- // TODO: need a process to wait for here.
- // Should fade out, credits, fade in.
- // Double-check in the disasm that this should just play credits and
- // not also endgame movie.
- // Game::get_instance()->playCredits();
-
+ Game::get_instance()->playCredits();
return 0;
}
diff --git a/engines/ultima/ultima8/games/remorse_game.cpp b/engines/ultima/ultima8/games/remorse_game.cpp
index b4ee824347..81f4ea88f2 100644
--- a/engines/ultima/ultima8/games/remorse_game.cpp
+++ b/engines/ultima/ultima8/games/remorse_game.cpp
@@ -29,6 +29,8 @@
#include "ultima/ultima8/graphics/palette_manager.h"
#include "ultima/ultima8/gumps/movie_gump.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
+#include "ultima/ultima8/gumps/main_menu_process.h"
+#include "ultima/ultima8/gumps/remorse_credits_gump.h"
#include "ultima/ultima8/kernel/object_manager.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/world/world.h"
@@ -70,10 +72,9 @@ bool RemorseGame::loadFiles() {
if (!loadPalette("static/gamepal.pal", PaletteManager::Pal_Game))
return false;
- if (GAME_IS_REGRET) {
- // This one is not used at the moment, so allowed to fail
- loadPalette("static/cred.pal", PaletteManager::Pal_Cred);
- }
+ // This one is not always present and only needed for the credits,
+ // let it fail if needed.
+ loadPalette("static/cred.pal", PaletteManager::Pal_Cred);
if (!loadPalette("static/diff.pal", PaletteManager::Pal_Diff))
return false;
if (!loadPalette("static/misc.pal", PaletteManager::Pal_Misc))
@@ -167,6 +168,32 @@ ProcId RemorseGame::playEndgameMovie(bool fade) {
void RemorseGame::playCredits() {
warning("TODO: RemorseGame::playCredits: Implement Crusader credits");
+ Process *menuproc = new MainMenuProcess();
+ Kernel::get_instance()->addProcess(menuproc);
+
+ static const Std::string txt_filename = "static/credits.dat";
+ static const Std::string bmp_filename = "static/cred.dat";
+ Common::SeekableReadStream *txtrs = FileSystem::get_instance()->ReadFile(txt_filename);
+ Common::SeekableReadStream *bmprs = FileSystem::get_instance()->ReadFile(bmp_filename);
+
+ if (!txtrs) {
+ perr << "RemorseGame::playCredits: error opening credits text: "
+ << txt_filename << Std::endl;
+ return;
+ }
+ if (!bmprs) {
+ perr << "RemorseGame::playCredits: error opening credits background: "
+ << bmp_filename << Std::endl;
+ return;
+ }
+ Gump *creditsgump = new RemorseCreditsGump(txtrs, bmprs);
+ creditsgump->InitGump(nullptr);
+ creditsgump->CreateNotifier();
+ Process *notifyproc = creditsgump->GetNotifyProcess();
+
+ if (notifyproc) {
+ menuproc->waitFor(notifyproc);
+ }
}
void RemorseGame::writeSaveInfo(Common::WriteStream *ws) {
diff --git a/engines/ultima/ultima8/gumps/remorse_credits_gump.cpp b/engines/ultima/ultima8/gumps/remorse_credits_gump.cpp
new file mode 100644
index 0000000000..8dc1faa457
--- /dev/null
+++ b/engines/ultima/ultima8/gumps/remorse_credits_gump.cpp
@@ -0,0 +1,207 @@
+/* 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 "common/config-manager.h"
+#include "image/bmp.h"
+
+#include "ultima/ultima8/gumps/remorse_credits_gump.h"
+
+#include "ultima/ultima8/kernel/mouse.h"
+#include "ultima/ultima8/graphics/render_surface.h"
+#include "ultima/ultima8/graphics/palette_manager.h"
+#include "ultima/ultima8/graphics/fonts/rendered_text.h"
+#include "ultima/ultima8/graphics/fonts/font.h"
+#include "ultima/ultima8/graphics/fonts/font_manager.h"
+#include "ultima/ultima8/graphics/fonts/shape_font.h"
+#include "ultima/ultima8/audio/music_process.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+DEFINE_RUNTIME_CLASSTYPE_CODE(RemorseCreditsGump)
+
+RemorseCreditsGump::RemorseCreditsGump()
+ : ModalGump(), _timer(0), _background(nullptr), _nextScreenStart(0),
+ _screenNo(-1) {
+}
+
+RemorseCreditsGump::RemorseCreditsGump(Common::SeekableReadStream *txtrs,
+ Common::SeekableReadStream *bmprs,
+ uint32 flags, int32 layer)
+ : ModalGump(0, 0, 640, 480, 0, flags, layer),
+ _timer(0), _background(nullptr), _nextScreenStart(0), _screenNo(-1)
+{
+ Image::BitmapDecoder decoder;
+ _background = RenderSurface::CreateSecondaryRenderSurface(640, 480);
+ _background->Fill32(0xFF000000, 0, 0, 640, 480); // black background
+
+ if (decoder.loadStream(*bmprs)) {
+ // This does an extra copy via the ManagedSurface, but it's a once-off.
+ const Graphics::Surface *bmpsurf = decoder.getSurface();
+ Graphics::ManagedSurface *ms = new Graphics::ManagedSurface(bmpsurf);
+ ms->setPalette(decoder.getPalette(), decoder.getPaletteStartIndex(), decoder.getPaletteColorCount());
+ _background->Blit(ms, 0, 0, 640, 480, 0, 0);
+ } else {
+ warning("couldn't load bitmap background for credits.");
+ }
+
+ // Lots of extra copies here, but it's only 4kb of text so it's fine.
+ CredScreen screen;
+ CredLine credline;
+
+ // not sure what these 4 bytes are?
+ txtrs->readUint32LE();
+ while (!txtrs->eos()) {
+ Common::String line = txtrs->readString();
+ if (!line.size())
+ break;
+ credline._text = line.substr(1);
+ switch (line[0]) {
+ case '@':
+ credline._lineType = kCredTitle;
+ screen._lines.push_back(credline);
+ break;
+ case '$':
+ credline._lineType = kCredName;
+ screen._lines.push_back(credline);
+ break;
+ case '*': {
+ unsigned int i = 1;
+ while (i < line.size() && line[i] == '*')
+ i++;
+ screen._delay = 60 * i;
+ _screens.push_back(screen);
+ screen._lines.clear();
+ break;
+ }
+ default:
+ if (line.size())
+ debug(6, "unhandled line in credits: %s", line.c_str());
+ break;
+ }
+ }
+}
+
+RemorseCreditsGump::~RemorseCreditsGump() {
+ delete _background;
+
+ for (Common::Array<RenderedText *>::iterator iter = _currentLines.begin(); iter != _currentLines.end(); iter++) {
+ delete *iter;
+ }
+}
+
+void RemorseCreditsGump::InitGump(Gump *newparent, bool take_focus) {
+ ModalGump::InitGump(newparent, take_focus);
+
+ Mouse::get_instance()->pushMouseCursor();
+ Mouse::get_instance()->setMouseCursor(Mouse::MOUSE_NONE);
+
+ MusicProcess *musicproc = MusicProcess::get_instance();
+ if (musicproc) {
+ musicproc->playMusic(19);
+ }
+}
+
+void RemorseCreditsGump::Close(bool no_del) {
+ Mouse::get_instance()->popMouseCursor();
+
+ ModalGump::Close(no_del);
+
+ // Just let it play out?
+ //MusicProcess *musicproc = MusicProcess::get_instance();
+ //if (musicproc) musicproc->restoreMusic();
+}
+
+void RemorseCreditsGump::run() {
+ ModalGump::run();
+
+ _timer++;
+
+ if (_timer < _nextScreenStart)
+ return;
+
+ _screenNo++;
+ if (_screenNo >= static_cast<int>(_screens.size())) {
+ Close();
+ return;
+ }
+
+ _nextScreenStart += _screens[_screenNo]._delay;
+ for (Common::Array<RenderedText *>::iterator iter = _currentLines.begin();
+ iter != _currentLines.end(); iter++) {
+ delete *iter;
+ }
+ _currentLines.clear();
+
+ const Common::Array<CredLine> &lines = _screens[_screenNo]._lines;
+
+ Font *titlefont = FontManager::get_instance()->getGameFont(16, true);
+ Font *namefont = FontManager::get_instance()->getGameFont(17, true);
+ Palette *pal = PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Cred);
+
+ ShapeFont *titleshapefont = dynamic_cast<ShapeFont *>(titlefont);
+ if (pal && titleshapefont)
+ titleshapefont->setPalette(pal);
+ ShapeFont *nameshapefont = dynamic_cast<ShapeFont *>(namefont);
+ if (pal && nameshapefont)
+ nameshapefont->setPalette(pal);
+
+ for (Common::Array<CredLine>::const_iterator iter = lines.begin();
+ iter != lines.end(); iter++) {
+ Font *linefont = (iter->_lineType == kCredTitle) ? titlefont : namefont;
+
+ unsigned int remaining;
+ RenderedText *rendered = linefont->renderText(iter->_text, remaining, 640, 0, Font::TEXT_CENTER);
+ _currentLines.push_back(rendered);
+ }
+}
+
+void RemorseCreditsGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
+ surf->Blit(_background->getRawSurface(), 0, 0, 640, 480, 0, 0);
+
+ unsigned int nlines = _currentLines.size();
+ if (!nlines)
+ return;
+
+ int width, height;
+ _currentLines[0]->getSize(width, height);
+ int vlead = _currentLines[0]->getVlead();
+
+ int total = nlines * (height + vlead);
+ int yoffset = 240 - total / 2;
+
+ for (Common::Array<RenderedText *>::iterator iter = _currentLines.begin();
+ iter != _currentLines.end(); iter++) {
+ (*iter)->draw(surf, 0, yoffset);
+ yoffset += (height + vlead);
+ }
+}
+
+bool RemorseCreditsGump::OnKeyDown(int key, int mod) {
+ if (key == Common::KEYCODE_ESCAPE)
+ Close();
+
+ return true;
+}
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/gumps/remorse_credits_gump.h b/engines/ultima/ultima8/gumps/remorse_credits_gump.h
new file mode 100644
index 0000000000..f04c00dd77
--- /dev/null
+++ b/engines/ultima/ultima8/gumps/remorse_credits_gump.h
@@ -0,0 +1,96 @@
+/* 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_REMORSECREDITSGUMP_H
+#define ULTIMA8_GUMPS_REMORSECREDITSGUMP_H
+
+#include "ultima/ultima8/gumps/modal_gump.h"
+#include "ultima/ultima8/misc/classtype.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+class RenderedText;
+
+/**
+ * Full-screen gump for the credits roll in Crusader: No Remorse
+ */
+class RemorseCreditsGump : public ModalGump {
+public:
+ ENABLE_RUNTIME_CLASSTYPE()
+
+ RemorseCreditsGump();
+ RemorseCreditsGump(Common::SeekableReadStream *txtrs, Common::SeekableReadStream *bmprs,
+ uint32 flags = FLAG_PREVENT_SAVE, int32 layer = LAYER_MODAL);
+ ~RemorseCreditsGump() 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;
+
+protected:
+ enum CredLineType {
+ kCredTitle,
+ kCredName
+ };
+
+ struct CredLine {
+ Common::String _text;
+ enum CredLineType _lineType;
+ };
+
+ struct CredScreen {
+ //! The lines of text for this screen
+ Common::Array<CredLine> _lines;
+ //! How long to display this screen, in engine ticks
+ unsigned int _delay;
+ };
+
+ //! Number of clock ticks the gump has run
+ int _timer;
+ //! Clock tick where the next screen should be shown
+ int _nextScreenStart;
+ //! Current screen number
+ int _screenNo;
+
+ //! Pre-rendered text
+ Common::Array<RenderedText *> _currentLines;
+
+ //! The starry background picture
+ RenderSurface *_background;
+
+ //! Screen text data
+ Common::Array<CredScreen> _screens;
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
Commit: fbc4da2d834b7d6f97cec5d22180965f0400780d
https://github.com/scummvm/scummvm/commit/fbc4da2d834b7d6f97cec5d22180965f0400780d
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:08+09:00
Commit Message:
IMAGE: Mention usage of bmp decoder by ultima8 engine
Changed paths:
image/bmp.h
diff --git a/image/bmp.h b/image/bmp.h
index 017c4ef4bc..64ec185f4d 100644
--- a/image/bmp.h
+++ b/image/bmp.h
@@ -58,6 +58,7 @@ namespace Image {
* - Mohawk
* - Petka
* - Wintermute
+ * - Ultima8
* @{
*/
Commit: 62cd4febd2a9a2f4be3c535b4e31bfd091301817
https://github.com/scummvm/scummvm/commit/62cd4febd2a9a2f4be3c535b4e31bfd091301817
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:08+09:00
Commit Message:
DEVTOOLS: ULTIMA8: Fix font spacing for Crusader inventory
Changed paths:
devtools/create_ultima/files/ultima8/remorse.ini
diff --git a/devtools/create_ultima/files/ultima8/remorse.ini b/devtools/create_ultima/files/ultima8/remorse.ini
index ab42bb783c..04bf6f5425 100644
--- a/devtools/create_ultima/files/ultima8/remorse.ini
+++ b/devtools/create_ultima/files/ultima8/remorse.ini
@@ -1,4 +1,4 @@
[fontleads]
6=0,3
-12=0,1
+12=-1,2
13=-1,-1
Commit: f8410eca57447eb0665ddb15d025e6ec04848b7d
https://github.com/scummvm/scummvm/commit/f8410eca57447eb0665ddb15d025e6ec04848b7d
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:08+09:00
Commit Message:
ULTIMA8: Show credits in Crusader inventory
Changed paths:
engines/ultima/ultima8/world/actors/main_actor.cpp
diff --git a/engines/ultima/ultima8/world/actors/main_actor.cpp b/engines/ultima/ultima8/world/actors/main_actor.cpp
index f42568ac14..41b07c25ae 100644
--- a/engines/ultima/ultima8/world/actors/main_actor.cpp
+++ b/engines/ultima/ultima8/world/actors/main_actor.cpp
@@ -724,6 +724,11 @@ void MainActor::nextInvItem() {
Std::vector<Item *> items;
getItemsWithShapeFamily(items, ShapeInfo::SF_CRUINVITEM, true);
getItemsWithShapeFamily(items, ShapeInfo::SF_CRUBOMB, true);
+ if (GAME_IS_REMORSE) {
+ Item *credits = getFirstItemWithShape(0x4ed, true);
+ if (credits)
+ items.push_back(credits);
+ }
_activeInvItem = getIdOfNextItemInList(items, _activeInvItem);
}
Commit: 9790fe6b96a164a86f4acfb31a666731b05e0b6c
https://github.com/scummvm/scummvm/commit/9790fe6b96a164a86f4acfb31a666731b05e0b6c
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:08+09:00
Commit Message:
ULTIMA8: Add support for the datalink video frame in Cruasder
Changed paths:
engines/ultima/ultima8/games/remorse_game.cpp
engines/ultima/ultima8/graphics/avi_player.cpp
engines/ultima/ultima8/graphics/avi_player.h
engines/ultima/ultima8/graphics/movie_player.h
engines/ultima/ultima8/gumps/movie_gump.cpp
engines/ultima/ultima8/gumps/movie_gump.h
engines/ultima/ultima8/gumps/weasel_gump.cpp
diff --git a/engines/ultima/ultima8/games/remorse_game.cpp b/engines/ultima/ultima8/games/remorse_game.cpp
index 81f4ea88f2..604f76c02a 100644
--- a/engines/ultima/ultima8/games/remorse_game.cpp
+++ b/engines/ultima/ultima8/games/remorse_game.cpp
@@ -143,7 +143,7 @@ bool RemorseGame::startInitialUsecode(int saveSlot) {
static ProcId playMovie(const char *movieID, bool fade, bool noScale) {
- MovieGump *gump = MovieGump::CruMovieViewer(movieID, 640, 480, nullptr, nullptr);
+ MovieGump *gump = MovieGump::CruMovieViewer(movieID, 640, 480, nullptr, nullptr, 0);
if (!gump) {
pout << "RemorseGame::playIntro: movie " << movieID << " not found." << Std::endl;
return 0;
diff --git a/engines/ultima/ultima8/graphics/avi_player.cpp b/engines/ultima/ultima8/graphics/avi_player.cpp
index 63c2926d94..a693995dc6 100644
--- a/engines/ultima/ultima8/graphics/avi_player.cpp
+++ b/engines/ultima/ultima8/graphics/avi_player.cpp
@@ -128,5 +128,10 @@ int AVIPlayer::getFrameNo() const {
return _decoder->getCurFrame();
}
+void AVIPlayer::setOffset(int xoff, int yoff) {
+ _xoff = xoff;
+ _yoff = yoff;
+}
+
} // End of namespace Ultima8
} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/graphics/avi_player.h b/engines/ultima/ultima8/graphics/avi_player.h
index 5c68b38c96..4a8dc666fb 100644
--- a/engines/ultima/ultima8/graphics/avi_player.h
+++ b/engines/ultima/ultima8/graphics/avi_player.h
@@ -55,6 +55,9 @@ public:
int getFrameNo() const;
+ // Adjust the offsets by the given values
+ void setOffset(int xoff, int yoff) override;
+
private:
bool _playing;
diff --git a/engines/ultima/ultima8/graphics/movie_player.h b/engines/ultima/ultima8/graphics/movie_player.h
index de63050c4e..231ff7eff1 100644
--- a/engines/ultima/ultima8/graphics/movie_player.h
+++ b/engines/ultima/ultima8/graphics/movie_player.h
@@ -39,6 +39,7 @@ public:
virtual void start() = 0;
virtual void stop() = 0;
virtual bool isPlaying() const = 0;
+ virtual void setOffset(int x, int y) {};
};
diff --git a/engines/ultima/ultima8/gumps/movie_gump.cpp b/engines/ultima/ultima8/gumps/movie_gump.cpp
index b45ab72120..558f7807ce 100644
--- a/engines/ultima/ultima8/gumps/movie_gump.cpp
+++ b/engines/ultima/ultima8/gumps/movie_gump.cpp
@@ -24,9 +24,13 @@
#include "ultima/ultima8/graphics/avi_player.h"
#include "ultima/ultima8/graphics/skf_player.h"
+#include "ultima/ultima8/graphics/gump_shape_archive.h"
+#include "ultima/ultima8/graphics/shape.h"
+#include "ultima/ultima8/graphics/shape_frame.h"
#include "ultima/ultima8/graphics/palette_manager.h"
#include "ultima/ultima8/graphics/fade_to_modal_process.h"
#include "ultima/ultima8/ultima8.h"
+#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/usecode/uc_machine.h"
@@ -178,6 +182,7 @@ void MovieGump::run() {
}
void MovieGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
+ Gump::PaintThis(surf, lerp_factor, scaled);
_player->paint(surf, lerp_factor);
}
@@ -194,6 +199,12 @@ bool MovieGump::OnKeyDown(int key, int mod) {
return true;
}
+void MovieGump::ClearPlayerOffset() {
+ if (!_shape || !_player)
+ return;
+ _player->setOffset(0, 0);
+}
+
/*static*/
ProcId MovieGump::U8MovieViewer(Common::SeekableReadStream *rs, bool fade, bool introMusicHack, bool noScale) {
ModalGump *gump;
@@ -216,13 +227,26 @@ ProcId MovieGump::U8MovieViewer(Common::SeekableReadStream *rs, bool fade, bool
}
}
-/*static*/ MovieGump *MovieGump::CruMovieViewer(const Std::string fname, int x, int y, const byte *pal, Gump *parent) {
+/*static*/ MovieGump *MovieGump::CruMovieViewer(const Std::string fname, int x, int y, const byte *pal, Gump *parent, uint16 frameshape) {
Common::SeekableReadStream *rs = _tryLoadCruAVI(fname);
if (!rs)
return nullptr;
MovieGump *gump = new MovieGump(x, y, rs, false, false, pal);
+
gump->InitGump(parent, true);
+
+ if (frameshape) {
+ GumpShapeArchive *gumpshapes = GameData::get_instance()->getGumps();
+ if (!gumpshapes) {
+ warning("failed to add movie frame: no gump shape archive");
+ } else {
+ gump->SetShape(gumpshapes->getShape(frameshape), 0);
+ gump->UpdateDimsFromShape();
+ gump->ClearPlayerOffset();
+ }
+ }
+
gump->setRelativePosition(CENTER);
gump->loadSubtitles(_tryLoadCruSubtitle(fname));
return gump;
@@ -286,7 +310,7 @@ uint32 MovieGump::I_playMovieOverlay(const uint8 *args,
const Palette *pal = palman->getPalette(PaletteManager::Pal_Game);
assert(pal);
- CruMovieViewer(name, x, y, pal->_palette, nullptr);
+ CruMovieViewer(name, x, y, pal->_palette, nullptr, 52);
}
return 0;
@@ -299,7 +323,7 @@ uint32 MovieGump::I_playMovieCutscene(const uint8 *args, unsigned int /*argsize*
ARG_UINT16(y);
if (item) {
- CruMovieViewer(name, x * 3, y * 3, nullptr, nullptr);
+ CruMovieViewer(name, x * 3, y * 3, nullptr, nullptr, 0);
}
return 0;
@@ -324,7 +348,7 @@ uint32 MovieGump::I_playMovieCutsceneAlt(const uint8 *args, unsigned int /*argsi
warning("MovieGump::I_playMovieCutsceneAlt: TODO: This intrinsic should pause and fade the background to grey (%s, %d)",
name.c_str(), item ? item->getObjId() : 0);
- CruMovieViewer(name, x, y, nullptr, nullptr);
+ CruMovieViewer(name, x, y, nullptr, nullptr, 0);
return 0;
}
@@ -335,7 +359,7 @@ uint32 MovieGump::I_playMovieCutsceneRegret(const uint8 *args, unsigned int /*ar
warning("MovieGump::I_playMovieCutsceneRegret: TODO: use fade argument %d", fade);
- CruMovieViewer(name, 640, 480, nullptr, nullptr);
+ CruMovieViewer(name, 640, 480, nullptr, nullptr, 0);
return 0;
}
diff --git a/engines/ultima/ultima8/gumps/movie_gump.h b/engines/ultima/ultima8/gumps/movie_gump.h
index 38ed46d88d..3b99bab0cf 100644
--- a/engines/ultima/ultima8/gumps/movie_gump.h
+++ b/engines/ultima/ultima8/gumps/movie_gump.h
@@ -56,7 +56,7 @@ public:
bool OnKeyDown(int key, int mod) override;
static ProcId U8MovieViewer(Common::SeekableReadStream *rs, bool fade, bool introMusicHack, bool noScale);
- static MovieGump *CruMovieViewer(const Std::string fname, int x, int y, const byte *pal, Gump *parent);
+ static MovieGump *CruMovieViewer(const Std::string fname, int x, int y, const byte *pal, Gump *parent, uint16 frameshape);
bool loadData(Common::ReadStream *rs);
void saveData(Common::WriteStream *ws) override;
@@ -78,6 +78,9 @@ protected:
// Load subtitles from a iff file (No Regret format)
void loadIFFSubs(Common::SeekableReadStream *rs);
+ // Update the offset of the player if a shape has been set
+ void ClearPlayerOffset();
+
Common::HashMap<int, Common::String> _subtitles;
uint16 _subtitleWidget;
int _lastFrameNo;
diff --git a/engines/ultima/ultima8/gumps/weasel_gump.cpp b/engines/ultima/ultima8/gumps/weasel_gump.cpp
index 96af97739f..b253ee4cfa 100644
--- a/engines/ultima/ultima8/gumps/weasel_gump.cpp
+++ b/engines/ultima/ultima8/gumps/weasel_gump.cpp
@@ -196,7 +196,7 @@ void WeaselGump::InitGump(Gump *newparent, bool take_focus) {
}
Gump *WeaselGump::playMovie(const Std::string &filename) {
- MovieGump *gump = MovieGump::CruMovieViewer(filename, 600, 450, nullptr, this);
+ MovieGump *gump = MovieGump::CruMovieViewer(filename, 600, 450, nullptr, this, 0);
if (!gump) {
warning("Couldn't load flic %s", filename.c_str());
return nullptr;
Commit: 716af7bf679af336c73a23cb6750f81732e4b3aa
https://github.com/scummvm/scummvm/commit/716af7bf679af336c73a23cb6750f81732e4b3aa
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:08+09:00
Commit Message:
ULTIMA8: Fix isPlaying to check mixer
Previously it could return true for sounds that have already finished if the
game is paused (because the AudioProcess is not run when paused). This only
really affects modal gumps.
Changed paths:
devtools/create_ultima/files/ultima8/remorse.ini
engines/ultima/ultima8/audio/audio_process.cpp
diff --git a/devtools/create_ultima/files/ultima8/remorse.ini b/devtools/create_ultima/files/ultima8/remorse.ini
index 04bf6f5425..559c6d33aa 100644
--- a/devtools/create_ultima/files/ultima8/remorse.ini
+++ b/devtools/create_ultima/files/ultima8/remorse.ini
@@ -1,4 +1,4 @@
[fontleads]
-6=0,3
+6=-1,3
12=-1,2
13=-1,-1
diff --git a/engines/ultima/ultima8/audio/audio_process.cpp b/engines/ultima/ultima8/audio/audio_process.cpp
index 027ee61ca7..745e97d9fe 100644
--- a/engines/ultima/ultima8/audio/audio_process.cpp
+++ b/engines/ultima/ultima8/audio/audio_process.cpp
@@ -304,9 +304,10 @@ void AudioProcess::stopSFX(int sfxNum, ObjId objId) {
}
bool AudioProcess::isSFXPlaying(int sfxNum) {
+ AudioMixer *mixer = AudioMixer::get_instance();
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end(); ++it) {
- if (it->_sfxNum == sfxNum)
+ if (it->_sfxNum == sfxNum && mixer->isPlaying(it->_channel))
return true;
}
@@ -314,9 +315,10 @@ bool AudioProcess::isSFXPlaying(int sfxNum) {
}
bool AudioProcess::isSFXPlayingForObject(int sfxNum, ObjId objId) {
+ AudioMixer *mixer = AudioMixer::get_instance();
Std::list<SampleInfo>::iterator it;
for (it = _sampleInfo.begin(); it != _sampleInfo.end(); ++it) {
- if (it->_sfxNum == sfxNum && (objId == it->_objId))
+ if (it->_sfxNum == sfxNum && (objId == it->_objId) && mixer->isPlaying(it->_channel))
return true;
}
Commit: 80135498078d4ab897714a7d7340bce4f9c861f1
https://github.com/scummvm/scummvm/commit/80135498078d4ab897714a7d7340bce4f9c861f1
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:08+09:00
Commit Message:
ULTIMA8: Implement incremental display for Crusader computer gumps
Changed paths:
engines/ultima/ultima8/gumps/computer_gump.cpp
engines/ultima/ultima8/gumps/computer_gump.h
diff --git a/engines/ultima/ultima8/gumps/computer_gump.cpp b/engines/ultima/ultima8/gumps/computer_gump.cpp
index 2dddd130b0..7bcdf0c319 100644
--- a/engines/ultima/ultima8/gumps/computer_gump.cpp
+++ b/engines/ultima/ultima8/gumps/computer_gump.cpp
@@ -20,13 +20,18 @@
*
*/
+#include "common/keyboard.h"
+
#include "ultima/ultima8/gumps/computer_gump.h"
-#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/graphics/shape.h"
#include "ultima/ultima8/graphics/gump_shape_archive.h"
#include "ultima/ultima8/graphics/shape_frame.h"
+#include "ultima/ultima8/graphics/fonts/rendered_text.h"
+#include "ultima/ultima8/graphics/fonts/font.h"
+#include "ultima/ultima8/graphics/fonts/font_manager.h"
+#include "ultima/ultima8/graphics/fonts/shape_font.h"
#include "ultima/ultima8/usecode/uc_machine.h"
namespace Ultima {
@@ -37,17 +42,57 @@ DEFINE_RUNTIME_CLASSTYPE_CODE(ComputerGump)
static const int COMPUTER_FONT = 6;
static const int COMPUTER_GUMP_SHAPE = 30;
static const int COMPUTER_GUMP_SOUND = 0x33;
+static const int MAX_LINE_LEN = 19;
-ComputerGump::ComputerGump()
- : ModalGump(), _textWidget(nullptr) {
+static const int TEXT_XOFF = 41;
+static const int TEXT_YOFF = 38;
+ComputerGump::ComputerGump()
+ : ModalGump(), _curTextLine(0), _charOff(0), _nextCharTick(0), _paused(false), _curDisplayLine(0), _tick(0) {
+ for (int i = 0; i < ARRAYSIZE(_renderedLines); i++) {
+ _renderedLines[i] = nullptr;
+ }
}
ComputerGump::ComputerGump(const Std::string &msg) :
- ModalGump(0, 0, 100, 100), _text(msg), _textWidget(nullptr) {
+ ModalGump(0, 0, 100, 100), _curTextLine(0), _curDisplayLine(0),
+ _charOff(0), _nextCharTick(0), _paused(false), _tick(0) {
+ for (int i = 0; i < ARRAYSIZE(_renderedLines); i++) {
+ _renderedLines[i] = nullptr;
+ }
+
+ // Split the string on ^ or flow to 20 char lines.
+ debug("M '%s'", msg.c_str());
+ uint32 start = 0;
+ uint32 end = 0;
+ for (uint32 i = 0; i < msg.size(); i++) {
+ if (msg[i] == '^') {
+ _textLines.push_back(msg.substr(start, end - start));
+ debug("^ %d %d %d '%s'", i, start, end, _textLines.back().c_str());
+ end = i + 1;
+ start = i + 1;
+ continue;
+ }
+ end++;
+ if (end - start >= MAX_LINE_LEN) {
+ while (end > start && msg[end] != ' ')
+ end--;
+ _textLines.push_back(msg.substr(start, end - start));
+ debug("L %d %d %d '%s'", i, start, end, _textLines.back().c_str());
+ i = end;
+ end = i + 1;
+ start = i + 1;
+ }
+ }
+ if (start < msg.size())
+ _textLines.push_back(msg.substr(start));
}
ComputerGump::~ComputerGump(void) {
+ for (int i = 0; i < ARRAYSIZE(_renderedLines); i++) {
+ if (_renderedLines[i])
+ delete _renderedLines[i];
+ }
}
void ComputerGump::InitGump(Gump *newparent, bool take_focus) {
@@ -79,50 +124,129 @@ void ComputerGump::InitGump(Gump *newparent, bool take_focus) {
botGump->SetShape(shape, 1);
botGump->InitGump(this, false);
- _textWidget = new TextWidget(41, 38, _text, true, COMPUTER_FONT, _dims.width() - 100,
- _dims.height() - 100, Font::TEXT_LEFT);
- _textWidget->InitGump(this);
+}
+
+void ComputerGump::run() {
+ ModalGump::run();
+
+ _tick++;
+ if (_paused || _tick < _nextCharTick)
+ return;
+
+ bool playsound = nextChar();
AudioProcess *audio = AudioProcess::get_instance();
- if (audio) {
+ if (playsound && audio) {
+ if (audio->isSFXPlaying(COMPUTER_GUMP_SOUND))
+ audio->stopSFX(COMPUTER_GUMP_SOUND, 0);
audio->playSFX(COMPUTER_GUMP_SOUND, 0x80, 0, 1);
}
}
-void ComputerGump::run() {
- ModalGump::run();
- TextWidget *widget = dynamic_cast<TextWidget *>(_textWidget);
- assert(widget);
- //widget->setupNextText();
-
- // TODO:
- // * Implement gradual display of the text
- // * Pause on '^' char
- // * Play sound 0x33 for each letter
- // * Add <MORE> if there is too many lines of text
+
+bool ComputerGump::nextChar() {
+ Font *computerfont = FontManager::get_instance()->getGameFont(COMPUTER_FONT, true);
+
+ if (_charOff >= _textLines[_curTextLine].size()) {
+ _curTextLine++;
+ _curDisplayLine++;
+ _charOff = 0;
+
+ if (_curTextLine >= _textLines.size()) {
+ _paused = true;
+ return false;
+ }
+ }
+
+ _nextCharTick = _tick + 2;
+
+ Common::String display;
+ if (_curDisplayLine == ARRAYSIZE(_renderedLines) - 1) {
+ display = "<MORE>";
+ _paused = true;
+ } else {
+ const Common::String &curline = _textLines[_curTextLine];
+ if (curline[_charOff] == '*') {
+ _nextCharTick += 10;
+ _charOff++;
+ return false;
+ }
+ _charOff++;
+ for (uint32 i = 0; i < _charOff; i++) {
+ char next = curline[i];
+ if (next == '*')
+ display += ' ';
+ else
+ display += next;
+ }
+ }
+
+ // Render the new line
+ unsigned int remaining;
+ RenderedText *rendered = computerfont->renderText(display, remaining);
+
+ if (_renderedLines[_curDisplayLine] != nullptr) {
+ delete _renderedLines[_curDisplayLine];
+ }
+ _renderedLines[_curDisplayLine] = rendered;
+
+ return true;
}
-void ComputerGump::nextText() {
- TextWidget *textWidget = dynamic_cast<TextWidget *>(_textWidget);
- if (!textWidget)
- return;
+void ComputerGump::Paint(RenderSurface *surf, int32 lerp_factor, bool scaled) {
+ ModalGump::Paint(surf, lerp_factor, scaled);
+ for (int i = 0; i < ARRAYSIZE(_renderedLines); i++) {
+ if (_renderedLines[i] != nullptr)
+ _renderedLines[i]->draw(surf, _x + TEXT_XOFF, _y + TEXT_YOFF + i * 9);
+ }
+}
+
+void ComputerGump::nextScreen() {
+ _nextCharTick = 0;
+ _charOff = 0;
+ _paused = false;
+ _curTextLine++;
+ _curDisplayLine = 0;
- if (!textWidget->setupNextText())
+ for (int i = 0; i < ARRAYSIZE(_renderedLines); i++) {
+ if (_renderedLines[i] != nullptr) {
+ delete _renderedLines[i];
+ _renderedLines[i] = nullptr;
+ }
+ }
+
+ if (_curTextLine >= _textLines.size())
Close();
}
Gump *ComputerGump::onMouseDown(int button, int32 mx, int32 my) {
- nextText();
+ if (_paused) {
+ nextScreen();
+ } else {
+ // Not super efficient but it does the job.
+ while (!_paused)
+ nextChar();
+ }
return this;
}
bool ComputerGump::OnKeyDown(int key, int mod) {
- nextText();
- return true;
-}
+ if (key == Common::KEYCODE_ESCAPE) {
+ _paused = true;
+ Close();
+ }
+ if (_paused) {
+ nextScreen();
+ } else {
+ // Not super efficient but it does the job.
+ while (!_paused)
+ nextChar();
+ }
+ return true;
+}
uint32 ComputerGump::I_readComputer(const uint8 *args, unsigned int /*argsize*/) {
ARG_STRING(str);
diff --git a/engines/ultima/ultima8/gumps/computer_gump.h b/engines/ultima/ultima8/gumps/computer_gump.h
index 8db1032970..78ef48ce64 100644
--- a/engines/ultima/ultima8/gumps/computer_gump.h
+++ b/engines/ultima/ultima8/gumps/computer_gump.h
@@ -24,6 +24,7 @@
#define ULTIMA8_GUMPS_COMPUTERGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
+#include "ultima/ultima8/graphics/fonts/rendered_text.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
@@ -34,7 +35,6 @@ namespace Ultima8 {
* The gump for showing the computer with text in Crusader
*/
class ComputerGump : public ModalGump {
- Std::string _text;
public:
ENABLE_RUNTIME_CLASSTYPE()
@@ -51,22 +51,39 @@ public:
void run() override;
+ void Paint(RenderSurface *, int32 lerp_factor, bool scaled) override;
+
INTRINSIC(I_readComputer);
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
private:
- void nextText();
+ void nextScreen();
+
+ bool nextChar();
+
+ RenderedText *_renderedLines[14];
+
+ Common::Array<Common::String> _textLines;
+
+ //! The current line from the full text
+ uint32 _curTextLine;
+
+ //! The current line in the rendered lines array
+ uint32 _curDisplayLine;
+
+ //! The current char within the current line
+ uint32 _charOff;
+
+ //! The frame when the next character will be added
+ uint32 _nextCharTick;
- /*
- TODO: Implement stepping through the text
- int _charOff;
- int _charX;
- int _charY;
- */
+ //! Tick now (timed separately to the kernel as this is run when game is paused)
+ uint32 _tick;
- Gump *_textWidget;
+ //! Whether display is currently paused waiting for input (with "MORE" at the bottom)
+ bool _paused;
};
} // End of namespace Ultima8
Commit: 1fad9c883fa1a4570ab95aaba662e5142ab40ba6
https://github.com/scummvm/scummvm/commit/1fad9c883fa1a4570ab95aaba662e5142ab40ba6
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:09+09:00
Commit Message:
ULTIMA8: Destroy items that fall through the floor
Changed paths:
engines/ultima/ultima8/world/gravity_process.cpp
diff --git a/engines/ultima/ultima8/world/gravity_process.cpp b/engines/ultima/ultima8/world/gravity_process.cpp
index b54ed2d56d..68444c7c43 100644
--- a/engines/ultima/ultima8/world/gravity_process.cpp
+++ b/engines/ultima/ultima8/world/gravity_process.cpp
@@ -104,6 +104,8 @@ void GravityProcess::run() {
// Shouldn't happen as item should always hit the floor.
warning("Item %d fell too far, stopping GravityProcess", _itemNum);
terminate();
+ _itemNum = 0;
+ item->destroy();
return;
}
Commit: 3c2fa9dfd7cfc3060339e62e11d140191c89ff2b
https://github.com/scummvm/scummvm/commit/3c2fa9dfd7cfc3060339e62e11d140191c89ff2b
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:09+09:00
Commit Message:
ULTIMA8: Small fixes to Crusader fireDistance intrinsic
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 6c0d7f19f1..03ef6430df 100644
--- a/engines/ultima/ultima8/world/item.cpp
+++ b/engines/ultima/ultima8/world/item.cpp
@@ -1419,10 +1419,12 @@ uint16 Item::fireDistance(const Item *other, Direction dir, int16 xoff, int16 yo
Std::list<CurrentMap::SweepItem> collisions;
Std::list<CurrentMap::SweepItem>::iterator it;
cm->sweepTest(start, end, dims, ShapeInfo::SI_SOLID,
- _objId, false, &collisions);
+ _objId, true, &collisions);
for (it = collisions.begin(); it != collisions.end(); it++) {
if (it->_item == getObjId())
continue;
+ if (it->_touching)
+ continue;
if (it->_item != other->getObjId())
break;
int32 out[3];
@@ -1432,7 +1434,10 @@ uint16 Item::fireDistance(const Item *other, Direction dir, int16 xoff, int16 yo
}
}
}
- return dist / 32;
+
+ if (!dist)
+ return 0;
+ return dist < 32 ? 1 : dist / 32;
}
int32 Item::getTargetZRelativeToAttackerZ(int32 otherz) const {
@@ -2715,6 +2720,11 @@ uint32 Item::I_setShape(const uint8 *args, unsigned int /*argsize*/) {
ARG_UINT16(shape);
if (!item) return 0;
+#if 0
+ debug(6, "Item::setShape: objid %04X shape (%d -> %d)",
+ item->getObjId(), item->getShape(), shape);
+#endif
+
item->setShape(shape);
return 0;
}
@@ -3348,8 +3358,8 @@ uint32 Item::I_create(const uint8 *args, unsigned int /*argsize*/) {
uint16 objID = newitem->getObjId();
#if 0
- pout << "Item::create: created item " << objID << " (" << shape
- << "," << frame << ")" << Std::endl;
+ debug(6, "Item::create: objid %04X shape (%d, %d)",
+ objID, shape, frame);
#endif
newitem->moveToEtherealVoid();
Commit: 4232c57339cd3210d83a95e0e8c733e0a633f706
https://github.com/scummvm/scummvm/commit/4232c57339cd3210d83a95e0e8c733e0a633f706
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2021-06-24T08:44:09+09:00
Commit Message:
ULTIMA8: Small fixes for Crusader pathfinder and attack processes
Most changes are renaming variables to make frame/tick distinction more clear
in timers.
Changed paths:
engines/ultima/ultima8/world/actors/actor.cpp
engines/ultima/ultima8/world/actors/actor.h
engines/ultima/ultima8/world/actors/attack_process.cpp
engines/ultima/ultima8/world/actors/cru_pathfinder_process.cpp
engines/ultima/ultima8/world/actors/rolling_thunder_process.cpp
engines/ultima/ultima8/world/super_sprite_process.cpp
diff --git a/engines/ultima/ultima8/world/actors/actor.cpp b/engines/ultima/ultima8/world/actors/actor.cpp
index 6fe95402fb..729df400ee 100644
--- a/engines/ultima/ultima8/world/actors/actor.cpp
+++ b/engines/ultima/ultima8/world/actors/actor.cpp
@@ -72,8 +72,8 @@ Actor::Actor() : _strength(0), _dexterity(0), _intelligence(0),
_lastAnim(Animation::stand), _animFrame(0), _direction(dir_north),
_fallStart(0), _unkByte(0), _actorFlags(0), _combatTactic(0),
_homeX(0), _homeY(0), _homeZ(0), _currentActivityNo(0),
- _lastActivityNo(0), _activeWeapon(0), _lastTimeWasHit(0),
- _attackMoveStartTime(0), _attackMoveTimeout(0),
+ _lastActivityNo(0), _activeWeapon(0), _lastTickWasHit(0),
+ _attackMoveStartFrame(0), _attackMoveTimeout(0),
_attackMoveDodgeFactor(1), _attackAimFlag(false) {
_defaultActivity[0] = 0;
_defaultActivity[1] = 0;
@@ -510,7 +510,7 @@ uint16 Actor::doAnim(Animation::Sequence anim, Direction dir, unsigned int steps
switch(anim) {
case Animation::walk:
case Animation::retreat: // SmallWeapon
- _attackMoveStartTime = frameno;
+ _attackMoveStartFrame = frameno;
_attackMoveTimeout = 120;
_attackMoveDodgeFactor = 3;
break;
@@ -526,19 +526,19 @@ uint16 Actor::doAnim(Animation::Sequence anim, Direction dir, unsigned int steps
//case Animation::startRunSmallWeapon:
//case Animation::startRunLargeWeapon:
case Animation::startRun:
- _attackMoveStartTime = frameno;
+ _attackMoveStartFrame = frameno;
_attackMoveTimeout = 120;
_attackMoveDodgeFactor = 2;
break;
case Animation::slideLeft:
case Animation::slideRight:
- _attackMoveStartTime = frameno;
+ _attackMoveStartFrame = frameno;
_attackMoveTimeout = 60;
_attackMoveDodgeFactor = 3;
break;
case Animation::startKneeling:
case Animation::stopKneeling:
- _attackMoveStartTime = frameno;
+ _attackMoveStartFrame = frameno;
_attackMoveTimeout = 75;
_attackMoveDodgeFactor = 3;
break;
@@ -939,7 +939,7 @@ void Actor::receiveHitCru(uint16 other, Direction dir, int damage, uint16 damage
if (isDead())
return;
- _lastTimeWasHit = Kernel::get_instance()->getTickNum();
+ _lastTickWasHit = Kernel::get_instance()->getTickNum();
if (shape != 1 && this != getControlledActor()) {
Actor *controlled = getControlledActor();
@@ -1931,9 +1931,9 @@ void Actor::saveData(Common::WriteStream *ws) {
ws->writeUint16LE(_currentActivityNo);
ws->writeUint16LE(_lastActivityNo);
ws->writeUint16LE(_activeWeapon);
- ws->writeSint32LE(_lastTimeWasHit);
+ ws->writeSint32LE(_lastTickWasHit);
ws->writeByte(0); // unused, keep for backward compatibility
- ws->writeUint32LE(_attackMoveStartTime);
+ ws->writeUint32LE(_attackMoveStartFrame);
ws->writeUint32LE(_attackMoveTimeout);
ws->writeUint16LE(_attackMoveDodgeFactor);
ws->writeByte(_attackAimFlag ? 1 : 0);
@@ -1968,9 +1968,9 @@ bool Actor::loadData(Common::ReadStream *rs, uint32 version) {
_currentActivityNo = rs->readUint16LE();
_lastActivityNo = rs->readUint16LE();
_activeWeapon = rs->readUint16LE();
- _lastTimeWasHit = rs->readSint32LE();
+ _lastTickWasHit = rs->readSint32LE();
rs->readByte(); // unused, keep for backward compatibility
- _attackMoveStartTime = rs->readUint32LE();
+ _attackMoveStartFrame = rs->readUint32LE();
_attackMoveTimeout = rs->readUint32LE();
_attackMoveDodgeFactor = rs->readUint16LE();
_attackAimFlag = rs->readByte() != 0;
diff --git a/engines/ultima/ultima8/world/actors/actor.h b/engines/ultima/ultima8/world/actors/actor.h
index b26bb95472..477522ceb2 100644
--- a/engines/ultima/ultima8/world/actors/actor.h
+++ b/engines/ultima/ultima8/world/actors/actor.h
@@ -229,8 +229,8 @@ public:
_lastActivityNo = 0;
}
- int32 getLastTimeWasHit() const {
- return _lastTimeWasHit;
+ int32 getLastTickWasHit() const {
+ return _lastTickWasHit;
}
//! run the given animation
@@ -304,8 +304,8 @@ public:
//! Add the x/y/z fire offsets given the current state of the actor
void addFireAnimOffsets(int32 &x, int32 &y, int32 &z);
- uint32 getAttackMoveTimeoutFinish() const {
- return _attackMoveStartTime + _attackMoveTimeout;
+ uint32 getAttackMoveTimeoutFinishFrame() const {
+ return _attackMoveStartFrame + _attackMoveTimeout;
}
uint16 getAttackMoveDodgeFactor() const {
@@ -452,11 +452,11 @@ protected:
uint16 _activeWeapon;
//! Kernel timer last time NPC was hit (only used in Crusader)
- int32 _lastTimeWasHit;
+ int32 _lastTickWasHit;
//! The frame certain animations last happened (for Crusader).
//! Used in calcualting how hard controlled actor is to hit.
- uint32 _attackMoveStartTime;
+ uint32 _attackMoveStartFrame;
//! The number of frames the above effect lasts for.
uint32 _attackMoveTimeout;
//! A spread divisor used by shots targeting the controlled actor when they
diff --git a/engines/ultima/ultima8/world/actors/attack_process.cpp b/engines/ultima/ultima8/world/actors/attack_process.cpp
index 108d19e795..0a29a77579 100644
--- a/engines/ultima/ultima8/world/actors/attack_process.cpp
+++ b/engines/ultima/ultima8/world/actors/attack_process.cpp
@@ -140,6 +140,7 @@ _soundTimestamp(0), _fireTimestamp(0) {
_type = ATTACK_PROCESS_TYPE;
setTacticNo(actor->getCombatTactic());
+ actor->setToStartOfAnim(Animation::stand);
}
AttackProcess::~AttackProcess() {
@@ -238,7 +239,7 @@ void AttackProcess::run() {
int32 x, y, z;
a->getHomePosition(x, y, z);
ProcId pid = Kernel::get_instance()->addProcess(
- new CruPathfinderProcess(a, x, y, z, 100, 0x40, true));
+ new CruPathfinderProcess(a, x, y, z, 100, 0x80, true));
waitFor(pid);
return;
}
@@ -248,7 +249,7 @@ void AttackProcess::run() {
int32 x, y, z;
target->getLocation(x, y, z);
ProcId pid = Kernel::get_instance()->addProcess(
- new CruPathfinderProcess(a, x, y, z, 12, 0x40, true));
+ new CruPathfinderProcess(a, x, y, z, 12, 0x80, true));
waitFor(pid);
return;
}
@@ -263,7 +264,7 @@ void AttackProcess::run() {
int32 y = (ty + ay) / 2;
int32 z = (tz + az) / 2;
ProcId pid = Kernel::get_instance()->addProcess(
- new CruPathfinderProcess(a, x, y, z, 12, 0x40, true));
+ new CruPathfinderProcess(a, x, y, z, 12, 0x80, true));
waitFor(pid);
return;
}
@@ -391,7 +392,7 @@ void AttackProcess::run() {
if (itemFrame == targetFrame && (targetQ == 0 || itemQlo == targetQ)) {
ProcId pid = Kernel::get_instance()->addProcess(
- new CruPathfinderProcess(a, founditem, 100, 0x40, true));
+ new CruPathfinderProcess(a, founditem, 100, 0x80, true));
waitFor(pid);
break;
}
@@ -529,7 +530,7 @@ void AttackProcess::genericAttack() {
AudioProcess *audio = AudioProcess::get_instance();
const Direction curdir = a->getDir();
- const int32 now = Kernel::get_instance()->getTickNum();
+ const int32 ticknow = Kernel::get_instance()->getTickNum();
int wpnField8 = wpn ? wpn->getShapeInfo()->_weaponInfo->_field8 : 1;
const uint16 controlledNPC = World::get_instance()->getControlledNPCNum();
Direction targetdir = dir_invalid;
@@ -552,7 +553,7 @@ void AttackProcess::genericAttack() {
y += -0x1ff + randomOf(0x400);
_field96 = true;
const ProcId pid = Kernel::get_instance()->addProcess(
- new CruPathfinderProcess(a, x, y, z, 12, 0x40, true));
+ new CruPathfinderProcess(a, x, y, z, 12, 0x80, true));
// add a tiny delay to avoid tight loops
Process *delayproc = new DelayProcess(2);
Kernel::get_instance()->addProcess(delayproc);
@@ -569,7 +570,7 @@ void AttackProcess::genericAttack() {
}
DirectionMode standDirMode = a->animDirMode(anim);
if (_timer3set) {
- if (_timer3 >= now) {
+ if (_timer3 >= ticknow) {
if (a->isInCombat()) {
if (randomOf(3) != 0) {
const Animation::Sequence lastanim = a->getLastAnim();
@@ -604,9 +605,9 @@ void AttackProcess::genericAttack() {
a->setAttackAimFlag(true);
_wpnField8 *= 4;
}
- _fireTimestamp = now;
+ _fireTimestamp = ticknow;
if (_timer4 == 0)
- _timer4 = now;
+ _timer4 = ticknow;
const ProcId animpid = a->doAnim(Animation::attack, dir_current); // fire small weapon.
if (animpid == 0) {
@@ -643,10 +644,10 @@ void AttackProcess::genericAttack() {
if (targetdir == curdir) {
const uint16 rnd = randomOf(10);
const uint32 frameno = Kernel::get_instance()->getFrameNum();
- const uint32 timeoutfinish = target->getAttackMoveTimeoutFinish();
+ const uint32 timeoutfinish = target->getAttackMoveTimeoutFinishFrame();
if (!onscreen ||
- (!_field96 && !timer4and5Update(now) && frameno < timeoutfinish
+ (!_field96 && !timer4and5Update(ticknow) && frameno < timeoutfinish
&& rnd > 2 && (!_isActivityAorB || rnd > 3))) {
sleep(0x14);
return;
@@ -654,12 +655,12 @@ void AttackProcess::genericAttack() {
_field96 = false;
bool ready;
- if (now - a->getLastTimeWasHit() < 120)
+ if (ticknow - a->getLastTickWasHit() <= 120)
ready = true;
else
- ready = checkReady(now, targetdir);
+ ready = checkReady(ticknow, targetdir);
- if (_timer2set && (randomOf(5) == 0 || checkTimer2PlusDelayElapsed(now))) {
+ if (_timer2set && (randomOf(5) == 0 || checkTimer2PlusDelayElapsed(ticknow))) {
_timer2set = false;
}
@@ -671,10 +672,10 @@ void AttackProcess::genericAttack() {
return;
}
- checkRandomAttackSound(now, a->getShape());
+ checkRandomAttackSound(ticknow, a->getShape());
if (!a->hasActorFlags(Actor::ACT_WEAPONREADY)) {
- _timer4 = now;
+ _timer4 = ticknow;
a->doAnim(Animation::readyWeapon, dir_current); // ready small wpn
return;
}
@@ -686,9 +687,9 @@ void AttackProcess::genericAttack() {
_soundNo = -1;
}
- const int32 t5elapsed = now - _timer5;
+ const int32 t5elapsed = ticknow - _timer5;
if (t5elapsed > _wpnBasedTimeout) {
- const int32 fireelapsed = now - _fireTimestamp;
+ const int32 fireelapsed = ticknow - _fireTimestamp;
if (fireelapsed <= _difficultyBasedTimeout) {
sleep(_difficultyBasedTimeout - fireelapsed);
return;
@@ -704,9 +705,9 @@ void AttackProcess::genericAttack() {
}
}
- _fireTimestamp = now;
+ _fireTimestamp = ticknow;
if (_timer4 == 0) {
- _timer4 = now;
+ _timer4 = ticknow;
}
const ProcId firepid = a->doAnim(Animation::attack, dir_current); // Fire SmallWpn
@@ -725,22 +726,22 @@ void AttackProcess::genericAttack() {
}
} else {
bool ready;
- if (!timer4and5Update(now) && !_field7f) {
+ if (!timer4and5Update(ticknow) && !_field7f) {
if (standDirMode != dirmode_16dirs) {
targetdir = a->getDirToItemCentre(*target);
}
ready = a->fireDistance(target, targetdir, 0, 0, 0);
if (ready)
- timeNowToTimerVal2(now);
+ timeNowToTimerVal2(ticknow);
} else {
- timeNowToTimerVal2(now);
+ timeNowToTimerVal2(ticknow);
ready = true;
_field7f = false;
}
// 5a flag 1 set?
if (!a->hasActorFlags(Actor::ACT_WEAPONREADY) && ready) {
- _timer4 = now;
+ _timer4 = ticknow;
a->doAnim(Animation::readyWeapon, dir_current); // ready SmallWpn
return;
}
@@ -836,7 +837,7 @@ void AttackProcess::pathfindToItemInNPCData() {
Actor *a = getActor(_itemNum);
Actor *target = getActor(_target);
- Process *pathproc = new CruPathfinderProcess(a, target, 12, 0x40, false);
+ Process *pathproc = new CruPathfinderProcess(a, target, 12, 0x80, false);
// In case pathfinding fails delay for a bit to ensure we don't get
// stuck in a tight loop using all the cpu
Process *delayproc = new DelayProcess(10);
diff --git a/engines/ultima/ultima8/world/actors/cru_pathfinder_process.cpp b/engines/ultima/ultima8/world/actors/cru_pathfinder_process.cpp
index 5c43c64412..571c730b39 100644
--- a/engines/ultima/ultima8/world/actors/cru_pathfinder_process.cpp
+++ b/engines/ultima/ultima8/world/actors/cru_pathfinder_process.cpp
@@ -73,6 +73,9 @@ CruPathfinderProcess::CruPathfinderProcess(Actor *actor, Item *target, int maxst
// TODO: check if flag already set? kill other pathfinders?
assert(!actor->hasActorFlags(Actor::ACT_PATHFINDING));
actor->setActorFlag(Actor::ACT_PATHFINDING);
+
+ if (actor->isInCombat() && actor->hasActorFlags(Actor::ACT_WEAPONREADY))
+ actor->doAnim(Animation::unreadyWeapon, dir_current);
}
CruPathfinderProcess::CruPathfinderProcess(Actor *actor, int32 x, int32 y, int32 z, int maxsteps, int stopdistance, bool turnatend) :
@@ -95,6 +98,9 @@ CruPathfinderProcess::CruPathfinderProcess(Actor *actor, int32 x, int32 y, int32
// TODO: check if flag already set? kill other pathfinders?
assert(!actor->hasActorFlags(Actor::ACT_PATHFINDING));
actor->setActorFlag(Actor::ACT_PATHFINDING);
+
+ if (actor->isInCombat() && actor->hasActorFlags(Actor::ACT_WEAPONREADY))
+ actor->doAnim(Animation::unreadyWeapon, dir_current);
}
CruPathfinderProcess::~CruPathfinderProcess() {
@@ -116,7 +122,7 @@ void CruPathfinderProcess::terminate() {
if (_targetItem == 0) {
destdir = Direction_GetWorldDir(_targetY - iy, _targetX - ix, dirmode_8dirs);
} else {
- Item *target = getItem(_targetItem);
+ const Item *target = getItem(_targetItem);
if (target) {
int32 tx, ty, tz;
target->getLocationAbsolute(tx, ty, tz);
@@ -216,12 +222,10 @@ Direction CruPathfinderProcess::nextDirFromPoint(struct Point3 &npcpt) {
state._combat = npc->isInCombat();
animresult = npc->tryAnim(anim, _nextDir2, 0, &state);
- // Note: this will never trigger in our code -
- // "tryAnim" code seems to behave differently in original?
- /*if (_solidObject && ((_result >> 1) & 1)) {
+ if (_solidObject && animresult == Animation::FAILURE) {
_turnAtEnd = true;
return dir_invalid;
- }*/
+ }
if (_stopDistance && (MAX(abs(_targetX - state._x), abs(_targetY - state._y)) <= _stopDistance)) {
_turnAtEnd = true;
@@ -302,7 +306,7 @@ void CruPathfinderProcess::run() {
if (_nextDir == dir_invalid) {
_dir16Flag = true;
} else {
- if (_currentStep == _maxSteps) {
+ if (_currentStep >= _maxSteps) {
terminate(); //0
return;
}
diff --git a/engines/ultima/ultima8/world/actors/rolling_thunder_process.cpp b/engines/ultima/ultima8/world/actors/rolling_thunder_process.cpp
index 47903c85ab..b91a854f0e 100644
--- a/engines/ultima/ultima8/world/actors/rolling_thunder_process.cpp
+++ b/engines/ultima/ultima8/world/actors/rolling_thunder_process.cpp
@@ -113,7 +113,7 @@ void RollingThunderProcess::run() {
if (dirtotarget == actordir) {
uint32 now = Kernel::get_instance()->getTickNum();
- if (now - actor->getLastTimeWasHit() >= 120) {
+ if (now - actor->getLastTickWasHit() >= 120) {
if (actor->fireDistance(target, dirtotarget, 0, 0, 0)) {
actor->doAnim(Animation::attack, dir_current);
return;
diff --git a/engines/ultima/ultima8/world/super_sprite_process.cpp b/engines/ultima/ultima8/world/super_sprite_process.cpp
index 59f9b64ef2..2fa4a35a28 100644
--- a/engines/ultima/ultima8/world/super_sprite_process.cpp
+++ b/engines/ultima/ultima8/world/super_sprite_process.cpp
@@ -80,7 +80,7 @@ SuperSpriteProcess::SuperSpriteProcess(int shape, int frame, int sx, int sy, int
Actor *srcnpc = dynamic_cast<Actor *>(srcitem);
Actor *controlled = getControlledActor();
const uint32 frameno = Kernel::get_instance()->getFrameNum();
- const uint32 timeoutfinish = controlled ? controlled->getAttackMoveTimeoutFinish() : 0;
+ const uint32 timeoutfinish = controlled ? controlled->getAttackMoveTimeoutFinishFrame() : 0;
if (!srcnpc || !srcnpc->getAttackAimFlag()) {
if (!srcnpc || frameno < timeoutfinish) {
if (!srcnpc && (controlled && controlled->isKneeling())) {
More information about the Scummvm-git-logs
mailing list