Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,156 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/ask_gump.h"
#include "ultima/ultima8/gumps/bark_gump.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/usecode/uc_list.h"
#include "ultima/ultima8/usecode/uc_machine.h"
namespace Ultima {
namespace Ultima8 {
// p_dynamic_class stuff
DEFINE_RUNTIME_CLASSTYPE_CODE(AskGump)
AskGump::AskGump() : ItemRelativeGump(), _answers(0) {
}
AskGump::AskGump(uint16 owner, UCList *answers) :
ItemRelativeGump(0, 0, 0, 0, owner, FLAG_KEEP_VISIBLE, LAYER_ABOVE_NORMAL),
_answers(new UCList(2)) {
_answers->copyStringList(*answers);
}
AskGump::~AskGump() {
_answers->freeStrings();
delete _answers;
}
// Init the gump, call after construction
void AskGump::InitGump(Gump *newparent, bool take_focus) {
// OK, this is a bit of a hack, but it's how it has to be
int fontnum = BarkGump::dialogFontForActor(_owner);
int px = 0, py = 0;
// This is a hack. We init the gump twice...
ItemRelativeGump::InitGump(newparent, take_focus);
for (unsigned int i = 0; i < _answers->getSize(); ++i) {
Std::string str_answer = "@ ";
str_answer += UCMachine::get_instance()->getString(_answers->getStringIndex(i));
ButtonWidget *child = new ButtonWidget(px, py, str_answer,
true, fontnum);
child->InitGump(this);
child->SetIndex(i);
Common::Rect32 cd = child->getDims();
if (i + 1 < _answers->getSize())
cd.setHeight(cd.height() + child->getVlead());
if (px + cd.width() > 160 && px != 0) {
py = _dims.height();
px = 0;
child->Move(px, py);
}
if (cd.width() + px > _dims.width())
_dims.setWidth(cd.width() + px);
if (cd.height() + py > _dims.height())
_dims.setHeight(cd.height() + py);
px += cd.width() + 4;
}
// Wait with ItemRelativeGump initialization until we calculated our size.
ItemRelativeGump::InitGump(newparent, take_focus);
}
void AskGump::ChildNotify(Gump *child, uint32 message) {
if (message == ButtonWidget::BUTTON_CLICK) {
uint16 s = _answers->getStringIndex(child->GetIndex());
_processResult = s;
// _answers' strings are going to be deleted, so make sure
// the response string won't be deleted
_answers->removeString(s, true); //!! assuming that answers doesn't
//!! contain two identical strings
Close();
}
}
void AskGump::saveData(Common::WriteStream *ws) {
ItemRelativeGump::saveData(ws);
_answers->save(ws);
}
bool AskGump::loadData(Common::ReadStream *rs, uint32 version) {
if (!ItemRelativeGump::loadData(rs, version)) return false;
_answers = new UCList(2);
_answers->load(rs, version);
// HACK ALERT
int px = 0, py = 0;
_dims.setWidth(0);
_dims.setHeight(0);
for (unsigned int i = 0; i < _answers->getSize(); ++i) {
ButtonWidget *child = nullptr;
for (auto *g : _children) {
if (g->GetIndex() != (int)i)
continue;
child = dynamic_cast<ButtonWidget *>(g);
if (!child)
continue;
}
if (!child) return false;
Common::Rect32 cd = child->getDims();
if (px + cd.width() > 160 && px != 0) {
py = _dims.height();
px = 0;
}
child->Move(px, py);
if (cd.width() + px > _dims.width())
_dims.setWidth(cd.width() + px);
if (cd.height() + py > _dims.height())
_dims.setHeight(cd.height() + py);
px += cd.width() + 4;
}
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,58 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_ASKGUMP_H
#define ULTIMA8_GUMPS_ASKGUMP_H
#include "ultima/ultima8/gumps/item_relative_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class UCList;
/**
* Represents asking a question while talking to an NPC
*/
class AskGump : public ItemRelativeGump {
UCList *_answers;
public:
// p_dynamic_class stuff
ENABLE_RUNTIME_CLASSTYPE()
AskGump();
AskGump(uint16 owner, UCList *answers);
~AskGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
void ChildNotify(Gump *child, uint32 message) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,249 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "ultima/ultima8/gumps/bark_gump.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
static const int INT_MAX_VALUE = 0x7fffffff;
static const int NO_SPEECH_LENGTH = 480;
static const int MILLIS_PER_TICK = (1000 / Kernel::TICKS_PER_SECOND) + 1;
DEFINE_RUNTIME_CLASSTYPE_CODE(BarkGump)
BarkGump::BarkGump() : ItemRelativeGump(), _counter(0), _textWidget(0),
_speechShapeNum(0), _speechLength(0),
_subtitles(false), _speechMute(false), _talkSpeed(0) {
_subtitles = ConfMan.getBool("subtitles");
_speechMute = ConfMan.getBool("speech_mute");
_talkSpeed = ConfMan.getInt("talkspeed");
}
BarkGump::BarkGump(uint16 owner, const Std::string &msg, uint32 speechShapeNum) :
ItemRelativeGump(0, 0, 100, 100, owner, FLAG_KEEP_VISIBLE, LAYER_ABOVE_NORMAL),
_barked(msg), _counter(100), _speechShapeNum(speechShapeNum),
_speechLength(0), _textWidget(0),
_subtitles(false), _speechMute(false), _talkSpeed(0) {
_subtitles = ConfMan.getBool("subtitles");
_speechMute = ConfMan.getBool("speech_mute");
_talkSpeed = ConfMan.getInt("talkspeed");
}
BarkGump::~BarkGump(void) {
}
int BarkGump::dialogFontForActor(uint16 actor) {
// OK, this is a bit of a hack, but it's how it has to be
if (actor == kMainActorId || actor == kGuardianId)
return 6;
if (actor > 256)
return 8;
switch (actor % 3) {
case 1:
return 5;
case 2:
return 7;
default:
return 0;
}
}
void BarkGump::InitGump(Gump *newparent, bool take_focus) {
int fontnum = dialogFontForActor(_owner);
//.Set a reasonable minimum speed for text speed when not in stasis
if (_talkSpeed < 10 && !Ultima8Engine::get_instance()->isAvatarInStasis()) {
_talkSpeed = 10;
}
// This is a hack. We init the gump twice...
ItemRelativeGump::InitGump(newparent, take_focus);
// Create the TextWidget
TextWidget *widget = new TextWidget(0, 0, _barked, true, fontnum, 194, 55);
widget->InitGump(this);
_textWidget = widget->getObjId();
// see if we need to play speech
AudioProcess *ap = AudioProcess::get_instance();
_speechLength = 0;
if (!_speechMute && _speechShapeNum && ap) {
if (ap->playSpeech(_barked, _speechShapeNum, _owner)) {
_speechLength = ap->getSpeechLength(_barked, _speechShapeNum);
if (_speechLength == 0)
_speechLength = 1;
if (!_subtitles) {
widget->SetVisibility(false);
}
}
}
Common::Rect32 d = widget->getDims();
_dims.setHeight(d.height());
_dims.setWidth(d.width());
_counter = calculateTicks();
// Wait with ItemRelativeGump initialization until we calculated our size.
ItemRelativeGump::InitGump(newparent, take_focus);
}
void BarkGump::Close(bool no_del) {
Item *item = getItem(_owner);
if (item)
item->clearBark();
ItemRelativeGump::Close(no_del);
}
bool BarkGump::NextText() {
TextWidget *widget = dynamic_cast<TextWidget *>(getGump(_textWidget));
assert(widget);
if (widget->setupNextText()) {
Common::Rect32 d = widget->getDims();
_dims.setHeight(d.height());
_dims.setWidth(d.width());
_counter = calculateTicks();
return true;
}
return false;
}
int BarkGump::calculateTicks() {
uint start = 0, end = 0;
TextWidget *widget = dynamic_cast<TextWidget *>(getGump(_textWidget));
widget->getCurrentText(start, end);
uint length = end - start;
int ticks = INT_MAX_VALUE;
if (length > 0) {
if (_speechLength && !_barked.empty()) {
ticks = (length * _speechLength) / (_barked.size() * MILLIS_PER_TICK);
} else if (_talkSpeed) {
ticks = (length * NO_SPEECH_LENGTH) / _talkSpeed;
}
} else if (_talkSpeed) {
ticks = NO_SPEECH_LENGTH / _talkSpeed;
}
return ticks;
}
void BarkGump::run() {
ItemRelativeGump::run();
// Auto close
if (!Kernel::get_instance()->isPaused()) {
if (!--_counter) {
// try next text
bool done = !NextText();
if (done) {
bool speechplaying = false;
if (!_speechMute && _speechLength) {
// waiting for speech to finish?
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
speechplaying = ap->isSpeechPlaying(_barked,
_speechShapeNum);
}
// if speech done too, close
if (!speechplaying)
Close();
else
_counter = calculateTicks();
}
}
}
}
Gump *BarkGump::onMouseDown(int button, int32 mx, int32 my) {
Gump *g = ItemRelativeGump::onMouseDown(button, mx, my);
if (g) return g;
// Scroll to next text, if possible
if (!NextText()) {
if (!_speechMute && _speechLength) {
AudioProcess *ap = AudioProcess::get_instance();
if (ap) ap->stopSpeech(_barked, _speechShapeNum, _owner);
}
Close();
}
return this;
}
void BarkGump::saveData(Common::WriteStream *ws) {
ItemRelativeGump::saveData(ws);
ws->writeUint32LE(static_cast<uint32>(_counter));
ws->writeUint16LE(_textWidget);
ws->writeUint32LE(_speechShapeNum);
ws->writeUint32LE(_speechLength);
ws->writeUint32LE(0); // Unused
ws->writeUint32LE(static_cast<uint32>(_barked.size()));
ws->write(_barked.c_str(), _barked.size());
}
bool BarkGump::loadData(Common::ReadStream *rs, uint32 version) {
if (!ItemRelativeGump::loadData(rs, version)) return false;
_counter = static_cast<int32>(rs->readUint32LE());
_textWidget = rs->readUint16LE();
_speechShapeNum = rs->readUint32LE();
_speechLength = rs->readUint32LE();
rs->readUint32LE(); // Unused
uint32 slen = rs->readUint32LE();
if (slen > 0) {
char *buf = new char[slen + 1];
rs->read(buf, slen);
buf[slen] = 0;
_barked = buf;
delete[] buf;
} else {
_barked = "";
}
TextWidget *widget = dynamic_cast<TextWidget *>(getGump(_textWidget));
if (!widget)
return false;
Common::Rect32 d = widget->getDims();
_dims.setHeight(d.height());
_dims.setWidth(d.width());
_counter = calculateTicks();
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,81 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_BARKGUMP_H
#define ULTIMA8_GUMPS_BARKGUMP_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/item_relative_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* Represents text which appears on the screen to show the name of an item, etc
*/
class BarkGump : public ItemRelativeGump {
protected:
Std::string _barked;
int32 _counter;
ObjId _textWidget;
uint32 _speechShapeNum;
uint32 _speechLength;
bool _subtitles;
bool _speechMute;
int _talkSpeed;
public:
ENABLE_RUNTIME_CLASSTYPE()
BarkGump();
BarkGump(uint16 owner, const Std::string &msg, uint32 speechShapeNum = 0);
~BarkGump() override;
// Run the gump (decrement the counter)
void run() override;
// Got to the next page on mouse click
Gump *onMouseDown(int button, int32 mx, int32 my) override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Close the gump
void Close(bool no_del = false) override;
/// Get the font that should be used from dialog from this actor
static int dialogFontForActor(uint16 actor);
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
protected:
//! show next text.
//! returns false if no more text available
bool NextText();
int calculateTicks();
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,147 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/keyboard.h"
#include "ultima/ultima8/gumps/book_gump.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/usecode/uc_machine.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(BookGump)
BookGump::BookGump()
: ModalGump(), _textWidgetL(0), _textWidgetR(0) {
}
BookGump::BookGump(ObjId owner, const Std::string &msg) :
ModalGump(0, 0, 100, 100, owner), _text(msg),
_textWidgetL(0), _textWidgetR(0) {
}
BookGump::~BookGump(void) {
}
void BookGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
//
// WORKAROUND (HACK), ScummVM bug #12503
// The original game usecode has a bug which does not display the correct text for the
// The Spell of Resurrection book. This bug only exists in some versions of the game.
//
// Original book text is in the config as "translations" for "spell of resurrection".
//
Item *item = getItem(_owner);
if (item && item->getShape() == 0x120 && item->getQuality() == 0x66) {
const Std::string placeholder = "spell of resurrection";
const Std::string replacement = _TL_(placeholder);
if (replacement != placeholder)
_text = replacement;
}
// Create the TextWidgets (NOTE: they _must_ have exactly the same _dims)
TextWidget *widget = new TextWidget(9, 5, _text, true, 9, 123, 129, Font::TEXT_LEFT, true); //!! constants
widget->InitGump(this);
_textWidgetL = widget->getObjId();
widget = new TextWidget(150, 5, _text, true, 9, 123, 129, Font::TEXT_LEFT, true); //!! constants
widget->InitGump(this);
_textWidgetR = widget->getObjId();
widget->setupNextText();
_text.clear(); // no longer need this
//!! constant
Shape *shapeP = GameData::get_instance()->getGumps()->getShape(6);
SetShape(shapeP, 0);
UpdateDimsFromShape();
}
void BookGump::NextText() {
TextWidget *widgetL = dynamic_cast<TextWidget *>(getGump(_textWidgetL));
TextWidget *widgetR = dynamic_cast<TextWidget *>(getGump(_textWidgetR));
assert(widgetL);
assert(widgetR);
if (!widgetR->setupNextText()) {
Close();
}
widgetL->setupNextText();
widgetL->setupNextText();
widgetR->setupNextText();
}
void BookGump::onMouseClick(int button, int32 mx, int32 my) {
// Scroll to next text, if possible
NextText();
}
void BookGump::onMouseDouble(int button, int32 mx, int32 my) {
Close();
}
bool BookGump::OnKeyDown(int key, int mod) {
switch (key) {
case Common::KEYCODE_ESCAPE:
Close();
break;
case Common::KEYCODE_SPACE:
NextText();
break;
default:
break;
}
return true;
}
uint32 BookGump::I_readBook(const uint8 *args, unsigned int /*argsize*/) {
ARG_ITEM_FROM_PTR(item);
ARG_STRING(str);
assert(item);
Gump *gump = new BookGump(item->getObjId(), str);
gump->InitGump(0);
gump->setRelativePosition(CENTER);
return gump->GetNotifyProcess()->getPid();
}
void BookGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
bool BookGump::loadData(Common::ReadStream *rs, uint32 version) {
warning("Trying to load ModalGump");
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,71 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_BOOKGUMP_H
#define ULTIMA8_GUMPS_BOOKGUMP_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* The gump to display the text of a book
*/
class BookGump : public ModalGump {
Std::string _text;
ObjId _textWidgetL;
ObjId _textWidgetR;
public:
ENABLE_RUNTIME_CLASSTYPE()
BookGump();
BookGump(ObjId owner, const Std::string &msg);
~BookGump() override;
// Go to the next page on mouse click
void onMouseClick(int button, int32 mx, int32 my) override;
// Close on double click
void onMouseDouble(int button, int32 mx, int32 my) override;
bool OnKeyDown(int key, int mod) override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
INTRINSIC(I_readBook);
protected:
void NextText();
public:
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,274 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/keyboard.h"
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/computer_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/gfx/fonts/font.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/gfx/fonts/shape_font.h"
#include "ultima/ultima8/usecode/uc_machine.h"
namespace Ultima {
namespace Ultima8 {
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;
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), _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 >= msg.size())
break;
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) {
ModalGump::InitGump(newparent, take_focus);
Shape *shape = GameData::get_instance()->getGumps()->
getShape(COMPUTER_GUMP_SHAPE);
if (!shape) {
error("Couldn't load shape for computer");
return;
}
const ShapeFrame *topFrame = shape->getFrame(0);
const ShapeFrame *botFrame = shape->getFrame(1);
if (!topFrame || !botFrame) {
error("Couldn't load shape frames for computer");
return;
}
_dims.left = 0;
_dims.top = 0;
_dims.setWidth(topFrame->_width);
_dims.setHeight(topFrame->_height + botFrame->_height);
Gump *topGump = new Gump(0, 0, topFrame->_width, topFrame->_height);
topGump->SetShape(shape, 0);
topGump->InitGump(this, false);
Gump *botGump = new Gump(0, topFrame->_height, botFrame->_width, botFrame->_height);
botGump->SetShape(shape, 1);
botGump->InitGump(this, false);
}
void ComputerGump::run() {
ModalGump::run();
_tick++;
if (_paused || _tick < _nextCharTick)
return;
bool playsound = nextChar();
AudioProcess *audio = AudioProcess::get_instance();
if (playsound && audio) {
if (audio->isSFXPlaying(COMPUTER_GUMP_SOUND))
audio->stopSFX(COMPUTER_GUMP_SOUND, 0);
audio->playSFX(COMPUTER_GUMP_SOUND, 0x80, 0, 1);
}
}
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 (_charOff < curline.size() && curline[_charOff] == '*') {
_nextCharTick += 10;
_charOff++;
return false;
}
_charOff++;
for (uint32 i = 0; i < _charOff && i < curline.size(); 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::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;
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) {
if (_paused) {
nextScreen();
} else {
// Not super efficient but it does the job.
while (!_paused)
nextChar();
}
return this;
}
bool ComputerGump::OnKeyDown(int key, int mod) {
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);
Gump *gump = new ComputerGump(str);
gump->InitGump(0);
gump->setRelativePosition(CENTER);
return 0;
}
void ComputerGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
bool ComputerGump::loadData(Common::ReadStream *rs, uint32 version) {
warning("Trying to load ModalGump");
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,91 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_COMPUTERGUMP_H
#define ULTIMA8_GUMPS_COMPUTERGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* The gump for showing the computer with text in Crusader
*/
class ComputerGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
ComputerGump();
ComputerGump(const Std::string &msg);
~ComputerGump() override;
// Close on mouse click on key press
Gump *onMouseDown(int button, int32 mx, int32 my) override;
bool OnKeyDown(int key, int mod) override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
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 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;
//! Tick now (timed separately to the kernel as this is run when game is paused)
uint32 _tick;
//! Whether display is currently paused waiting for input (with "MORE" at the bottom)
bool _paused;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,603 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima.h"
#include "ultima/ultima8/gumps/container_gump.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/main_shape_archive.h"
#include "ultima/ultima8/gumps/slider_gump.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/world/split_item_process.h"
#include "ultima/ultima8/gumps/game_map_gump.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(ContainerGump)
ContainerGump::ContainerGump()
: ItemRelativeGump(), _displayDragging(false), _draggingShape(0),
_draggingFrame(0), _draggingFlags(0), _draggingX(0), _draggingY(0) {
}
ContainerGump::ContainerGump(const Shape *shape, uint32 frameNum, uint16 owner,
uint32 flags, int32 layer)
: ItemRelativeGump(0, 0, 5, 5, owner, flags, layer),
_displayDragging(false), _draggingShape(0), _draggingFrame(0),
_draggingFlags(0), _draggingX(0), _draggingY(0) {
_shape = shape;
_frameNum = frameNum;
}
ContainerGump::~ContainerGump() {
}
void ContainerGump::InitGump(Gump *newparent, bool take_focus) {
UpdateDimsFromShape();
// Wait with ItemRelativeGump initialization until we calculated our size.
ItemRelativeGump::InitGump(newparent, take_focus);
// make every item enter the fast area
Container *c = getContainer(_owner);
if (!c) return; // Container gone!?
for (auto *item : c->_contents) {
item->enterFastArea();
}
// Position isn't like in the original
// U8 puts a container gump slightly to the left of an object
}
void ContainerGump::run() {
Gump::run();
Container *c = getContainer(_owner);
if (!c) {
// Container gone!?
Close();
return;
}
for (auto *item : c->_contents) {
int32 itemx, itemy;
item->getGumpLocation(itemx, itemy);
const Shape *sh = item->getShapeObject();
assert(sh);
const ShapeFrame *fr = sh->getFrame(item->getFrame());
assert(fr);
// Ensure item locations within item area.
int32 minx = fr->_xoff;
int32 miny = fr->_yoff;
int32 maxx = _itemArea.width() + fr->_xoff - fr->_width;
int32 maxy = _itemArea.height() + fr->_yoff - fr->_height;
if (itemx == 0xFF && itemy == 0xFF) {
// randomize position
// TODO: maybe try to put it somewhere where it doesn't overlap others?
Common::RandomSource &rs = Ultima8Engine::get_instance()->getRandomSource();
itemx = rs.getRandomNumberRng(minx, maxx);
itemy = rs.getRandomNumberRng(miny, maxy);
item->setGumpLocation(itemx, itemy);
}
if (itemx < minx) {
itemx = minx;
item->setGumpLocation(itemx, itemy);
}
if (itemx > maxx) {
itemx = maxx;
item->setGumpLocation(itemx, itemy);
}
if (itemy < miny) {
itemy = miny;
item->setGumpLocation(itemx, itemy);
}
if (itemy > maxy) {
itemy = maxy;
item->setGumpLocation(itemx, itemy);
}
}
}
void ContainerGump::getItemCoords(Item *item, int32 &itemx, int32 &itemy) {
item->getGumpLocation(itemx, itemy);
itemx += _itemArea.left;
itemy += _itemArea.top;
}
void ContainerGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
// paint self
ItemRelativeGump::PaintThis(surf, lerp_factor, scaled);
Container *c = getContainer(_owner);
if (!c) {
// Container gone!?
Close();
return;
}
int32 gameframeno = Kernel::get_instance()->getFrameNum();
//!! TODO: check these painting commands (flipped? translucent?)
bool showEditorItems = Ultima8Engine::get_instance()->isShowEditorItems();
for (auto *item : c->_contents) {
item->setupLerp(gameframeno);
if (!showEditorItems && item->getShapeInfo()->is_editor())
continue;
int32 itemx, itemy;
getItemCoords(item, itemx, itemy);
const Shape *s = item->getShapeObject();
assert(s);
surf->Paint(s, item->getFrame(), itemx, itemy);
}
if (_displayDragging) {
int32 itemx, itemy;
itemx = _draggingX + _itemArea.left;
itemy = _draggingY + _itemArea.top;
Shape *s = GameData::get_instance()->getMainShapes()->
getShape(_draggingShape);
assert(s);
surf->PaintInvisible(s, _draggingFrame, itemx, itemy, false, (_draggingFlags & Item::FLG_FLIPPED) != 0);
}
}
// Find object (if any) at (mx,my)
// (mx,my) are relative to parent
uint16 ContainerGump::TraceObjId(int32 mx, int32 my) {
uint16 objId_ = Gump::TraceObjId(mx, my);
if (objId_ && objId_ != 65535) return objId_;
ParentToGump(mx, my);
Container *c = getContainer(_owner);
if (!c)
return 0; // Container gone!?
bool showEditorItems = Ultima8Engine::get_instance()->isShowEditorItems();
Std::list<Item *> &contents = c->_contents;
Std::list<Item *>::iterator iter;
// iterate backwards, since we're painting from begin() to end()
for (iter = contents.reverse_begin(); iter != contents.end(); --iter) {
Item *item = *iter;
if (!showEditorItems && item->getShapeInfo()->is_editor())
continue;
int32 itemx, itemy;
getItemCoords(item, itemx, itemy);
const Shape *s = item->getShapeObject();
assert(s);
const ShapeFrame *frame = s->getFrame(item->getFrame());
if (frame->hasPoint(mx - itemx, my - itemy)) {
// found it
return item->getObjId();
}
}
// didn't find anything, so return self
return getObjId();
}
// get item coords relative to self
bool ContainerGump::GetLocationOfItem(uint16 itemid, int32 &gx, int32 &gy,
int32 lerp_factor) {
Item *item = getItem(itemid);
if (!item) return false;
Item *parent = item->getParentAsContainer();
if (!parent) return false;
if (parent->getObjId() != _owner) return false;
//!!! need to use lerp_factor
int32 itemx, itemy;
getItemCoords(item, itemx, itemy);
gx = itemx;
gy = itemy;
return false;
}
// we don't want our position to depend on Gump of parent container
// so change the default ItemRelativeGump behaviour
void ContainerGump::GetItemLocation(int32 lerp_factor) {
Item *it = getItem(_owner);
if (!it) {
// This shouldn't ever happen, the GumpNotifyProcess should
// close us before we get here
Close();
return;
}
int32 gx, gy;
Container *root = it->getRootContainer();
Item *topitem = root ? root : it;
Gump *gump = GetRootGump()->FindGump<GameMapGump>();
assert(gump);
gump->GetLocationOfItem(topitem->getObjId(), gx, gy, lerp_factor);
// Convert the GumpSpaceCoord relative to the world/item gump
// into screenspace coords
gy = gy - it->getShapeInfo()->_z * 8 - 16;
gump->GumpToScreenSpace(gx, gy);
// Convert the screenspace coords into the coords of us
if (_parent) _parent->ScreenSpaceToGump(gx, gy);
// Set x and y, and center us over it
_ix = gx - _dims.width() / 2;
_iy = gy - _dims.height();
}
void ContainerGump::Close(bool no_del) {
// close any gumps belonging to contents
// and make every item leave the fast area
Container *c = getContainer(_owner);
if (!c) return; // Container gone!?
Std::list<Item *> &contents = c->_contents;
Std::list<Item *>::iterator iter = contents.begin();
while (iter != contents.end()) {
Item *item = *iter;
++iter;
Gump *g = getGump(item->getGump());
if (g) {
g->Close(); //!! what about no_del?
}
item->leaveFastArea(); // Can destroy the item
}
Item *o = getItem(_owner);
if (o)
o->clearGump(); //!! is this the appropriate place?
ItemRelativeGump::Close(no_del);
}
Container *ContainerGump::getTargetContainer(Item *item, int mx, int my) {
int32 px = mx, py = my;
GumpToParent(px, py);
Container *targetcontainer = getContainer(TraceObjId(px, py));
if (targetcontainer && targetcontainer->getObjId() == item->getObjId())
targetcontainer = nullptr;
if (targetcontainer) {
const ShapeInfo *targetinfo = targetcontainer->getShapeInfo();
if ((targetcontainer->getObjId() == item->getObjId()) ||
targetinfo->is_land() ||
targetcontainer->hasFlags(Item::FLG_IN_NPC_LIST)) {
targetcontainer = nullptr;
}
}
if (!targetcontainer)
targetcontainer = getContainer(_owner);
return targetcontainer;
}
Gump *ContainerGump::onMouseDown(int button, int32 mx, int32 my) {
Gump *handled = Gump::onMouseDown(button, mx, my);
if (handled) return handled;
// only interested in left clicks
if (button == Mouse::BUTTON_LEFT)
return this;
return nullptr;
}
void ContainerGump::onMouseClick(int button, int32 mx, int32 my) {
if (button == Mouse::BUTTON_LEFT) {
uint16 objID = TraceObjId(mx, my);
Item *item = getItem(objID);
if (item) {
debugC(kDebugObject, "%s", item->dumpInfo().c_str());
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugC(kDebugObject, "Can't look: avatarInStasis");
} else {
item->callUsecodeEvent_look();
}
}
}
}
void ContainerGump::onMouseDouble(int button, int32 mx, int32 my) {
if (button == Mouse::BUTTON_LEFT) {
uint16 objID = TraceObjId(mx, my);
if (objID == getObjId()) {
objID = _owner; // use container when double click on self
}
Item *item = getItem(objID);
if (item) {
debugC(kDebugObject, "%s", item->dumpInfo().c_str());
if (objID == _owner) {
// call the 'use' event
item->use();
return;
}
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugC(kDebugObject, "Can't use: avatarInStasis");
return;
}
MainActor *avatar = getMainActor();
if (avatar->canReach(item, 128)) { // CONSTANT!
// call the 'use' event
item->use();
} else {
Mouse::get_instance()->flashCrossCursor();
}
}
}
}
bool ContainerGump::StartDraggingItem(Item *item, int mx, int my) {
// probably don't need to check if item can be moved, since it shouldn't
// be in a container otherwise
Container *c = getContainer(_owner);
assert(c);
// check if the container the item is in is in range
MainActor *avatar = getMainActor();
if (!avatar->canReach(c, 128)) return false;
int32 itemx, itemy;
getItemCoords(item, itemx, itemy);
Mouse::get_instance()->setDraggingOffset(mx - itemx, my - itemy);
return true;
}
bool ContainerGump::DraggingItem(Item *item, int mx, int my) {
Container *c = getContainer(_owner);
assert(c);
// check if the container the item is in is in range
MainActor *avatar = getMainActor();
if (!avatar->canReach(c, 128)) {
_displayDragging = false;
return false;
}
int32 dox, doy;
Mouse::get_instance()->getDraggingOffset(dox, doy);
Mouse::get_instance()->setMouseCursor(Mouse::MOUSE_TARGET);
_displayDragging = true;
_draggingShape = item->getShape();
_draggingFrame = item->getFrame();
_draggingFlags = item->getFlags();
// determine target location and set dragging_x/y
_draggingX = mx - _itemArea.left - dox;
_draggingY = my - _itemArea.top - doy;
const Shape *sh = item->getShapeObject();
assert(sh);
const ShapeFrame *fr = sh->getFrame(_draggingFrame);
assert(fr);
if (_draggingX - fr->_xoff < 0 ||
_draggingX - fr->_xoff + fr->_width > _itemArea.width() ||
_draggingY - fr->_yoff < 0 ||
_draggingY - fr->_yoff + fr->_height > _itemArea.height()) {
_displayDragging = false;
return false;
}
// check if item will fit (weight/volume/adding container to itself)
Container *target = getTargetContainer(item, mx, my);
if (!target || !target->CanAddItem(item, true)) {
_displayDragging = false;
return false;
}
return true;
}
void ContainerGump::DraggingItemLeftGump(Item *item) {
_displayDragging = false;
}
void ContainerGump::StopDraggingItem(Item *item, bool moved) {
if (!moved) return; // nothing to do
}
void ContainerGump::DropItem(Item *item, int mx, int my) {
_displayDragging = false;
int32 px = mx, py = my;
GumpToParent(px, py);
// see what the item is being dropped on
Item *targetitem = getItem(TraceObjId(px, py));
Container *targetcontainer = dynamic_cast<Container *>(targetitem);
if (item->getShapeInfo()->hasQuantity() &&
item->getQuality() > 1) {
// more than one, so see if we should ask if we should split it up
Item *splittarget = nullptr;
// also try to combine
if (targetitem && item->canMergeWith(targetitem)) {
splittarget = targetitem;
}
if (!splittarget) {
// create new item
splittarget = ItemFactory::createItem(
item->getShape(), item->getFrame(), 0,
item->getFlags() & (Item::FLG_DISPOSABLE | Item::FLG_OWNED | Item::FLG_INVISIBLE | Item::FLG_FLIPPED | Item::FLG_FAST_ONLY | Item::FLG_LOW_FRICTION), item->getNpcNum(), item->getMapNum(),
item->getExtFlags() & (Item::EXT_SPRITE | Item::EXT_HIGHLIGHT | Item::EXT_TRANSPARENT), true);
if (!splittarget) {
warning("ContainerGump failed to create item (%u,%u) while splitting",
item->getShape(), item->getFrame());
return;
}
if (targetcontainer) {
splittarget->moveToContainer(targetcontainer);
splittarget->randomGumpLocation();
} else {
splittarget->moveToContainer(getContainer(_owner));
splittarget->setGumpLocation(_draggingX, _draggingY);
}
}
SliderGump *slidergump = new SliderGump(100, 100,
0, item->getQuality(),
item->getQuality());
slidergump->InitGump(0);
slidergump->CreateNotifier(); // manually create notifier
Process *notifier = slidergump->GetNotifyProcess();
SplitItemProcess *splitproc = new SplitItemProcess(item, splittarget);
Kernel::get_instance()->addProcess(splitproc);
splitproc->waitFor(notifier);
return;
}
if (targetitem && item->getShapeInfo()->hasQuantity()) {
// try to combine items
if (item->canMergeWith(targetitem)) {
uint16 newquant = targetitem->getQuality() + item->getQuality();
if (newquant > Item::MAX_QUANTITY) {
item->setQuality(newquant - Item::MAX_QUANTITY);
targetitem->setQuality(Item::MAX_QUANTITY);
// maybe this isn't needed? original doesn't do it here..
targetitem->callUsecodeEvent_combine();
} else {
targetitem->setQuality(newquant);
targetitem->callUsecodeEvent_combine();
// combined, so delete other
item->destroy();
}
return;
}
}
targetcontainer = getTargetContainer(item, mx, my);
assert(targetcontainer);
if (targetcontainer->getObjId() != _owner) {
if (item->getParent() == targetcontainer->getObjId()) {
// already in this container, so move item to let it be drawn
// on top of all other items
targetcontainer->moveItemToEnd(item);
} else {
item->moveToContainer(targetcontainer);
item->randomGumpLocation();
}
} else {
// add item to self
if (item->getParent() == _owner) {
targetcontainer->moveItemToEnd(item);
} else {
item->moveToContainer(targetcontainer);
}
int32 dox, doy;
Mouse::get_instance()->getDraggingOffset(dox, doy);
_draggingX = mx - _itemArea.left - dox;
_draggingY = my - _itemArea.top - doy;
item->setGumpLocation(_draggingX, _draggingY);
}
}
void ContainerGump::saveData(Common::WriteStream *ws) {
ItemRelativeGump::saveData(ws);
ws->writeUint32LE(static_cast<uint32>(_itemArea.left));
ws->writeUint32LE(static_cast<uint32>(_itemArea.top));
ws->writeUint32LE(static_cast<uint32>(_itemArea.width()));
ws->writeUint32LE(static_cast<uint32>(_itemArea.height()));
}
bool ContainerGump::loadData(Common::ReadStream *rs, uint32 version) {
if (!ItemRelativeGump::loadData(rs, version)) return false;
int32 iax = static_cast<int32>(rs->readUint32LE());
int32 iay = static_cast<int32>(rs->readUint32LE());
int32 iaw = static_cast<int32>(rs->readUint32LE());
int32 iah = static_cast<int32>(rs->readUint32LE());
_itemArea.moveTo(iax, iay);
_itemArea.setWidth(iaw);
_itemArea.setHeight(iah);
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,102 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CONTAINERGUMP_H
#define ULTIMA8_GUMPS_CONTAINERGUMP_H
#include "ultima/ultima8/gumps/item_relative_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class Shape;
class Container;
/**
* Base gump class for all containers (backpack, barrel, etc)
*/
class ContainerGump : public ItemRelativeGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
ContainerGump();
ContainerGump(const Shape *shape, uint32 frameNum, uint16 owner,
uint32 flags = FLAG_DRAGGABLE, int32 layer = LAYER_NORMAL);
~ContainerGump() override;
void setItemArea(Common::Rect32 *itemArea) {
_itemArea = *itemArea;
}
// Close the gump
void Close(bool no_del = false) override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
void run() override;
// Paint the Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
// Trace a click, and return ObjId
uint16 TraceObjId(int32 mx, int32 my) override;
// Get the location of an item in the gump (coords relative to this).
// Returns false on failure.
bool GetLocationOfItem(uint16 itemid, int32 &gx, int32 &gy,
int32 lerp_factor = 256) override;
bool StartDraggingItem(Item *item, int mx, int my) override;
bool DraggingItem(Item *item, int mx, int my) override;
void DraggingItemLeftGump(Item *item) override;
void StopDraggingItem(Item *item, bool moved) override;
void DropItem(Item *item, int mx, int my) override;
Gump *onMouseDown(int button, int32 mx, int32 my) override;
void onMouseClick(int button, int32 mx, int32 my) override;
void onMouseDouble(int button, int32 mx, int32 my) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
protected:
void GetItemLocation(int32 lerp_factor) override;
virtual Container *getTargetContainer(Item *item, int mx, int my);
void getItemCoords(Item *item, int32 &itemx, int32 &itemy);
Common::Rect32 _itemArea;
bool _displayDragging;
uint32 _draggingShape;
uint32 _draggingFrame;
uint32 _draggingFlags;
int32 _draggingX, _draggingY;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,409 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "common/events.h"
#include "ultima/ultima8/gumps/credits_gump.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/gfx/fonts/font.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/audio/music_process.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(CreditsGump)
CreditsGump::CreditsGump()
: ModalGump(), _parSkip(0), _timer(0), _title(nullptr),
_nextTitle(nullptr), _state(CS_PLAYING),
_nextTitleSurf(0), _currentSurface(0), _currentY(0) {
for (int i = 0; i < 4; i++) {
_scroll[i] = nullptr;
_scrollHeight[i] = 0;
}
}
CreditsGump::CreditsGump(const Std::string &text, int parskip,
uint32 flags, int32 layer)
: ModalGump(0, 0, 320, 200, 0, flags, layer), _text(text), _parSkip(parskip),
_timer(0), _title(nullptr), _nextTitle(nullptr), _state(CS_PLAYING),
_nextTitleSurf(0), _currentSurface(0), _currentY(0) {
for (int i = 0; i < 4; i++) {
_scroll[i] = nullptr;
_scrollHeight[i] = 0;
}
}
CreditsGump::~CreditsGump() {
delete _scroll[0];
delete _scroll[1];
delete _scroll[2];
delete _scroll[3];
delete _title;
delete _nextTitle;
}
void CreditsGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
Graphics::Screen *screen = Ultima8Engine::get_instance()->getScreen();
uint32 width = 256;
uint32 height = 280;
_scroll[0] = new RenderSurface(width, height, screen->format);
_scroll[1] = new RenderSurface(width, height, screen->format);
_scroll[2] = new RenderSurface(width, height, screen->format);
_scroll[3] = new RenderSurface(width, height, screen->format);
uint32 color = TEX32_PACK_RGB(0, 0, 0);
_scroll[0]->fill32(color, 0, 0, width, height); // black background
_scroll[1]->fill32(color, 0, 0, width, height);
_scroll[2]->fill32(color, 0, 0, width, height);
_scroll[3]->fill32(color, 0, 0, width, height);
_scrollHeight[0] = 156;
_scrollHeight[1] = 0;
_scrollHeight[2] = 0;
_scrollHeight[3] = 0;
_currentSurface = 0;
_currentY = 0;
Mouse::get_instance()->pushMouseCursor(Mouse::MOUSE_NONE);
}
void CreditsGump::Close(bool no_del) {
Mouse::get_instance()->popMouseCursor();
ModalGump::Close(no_del);
MusicProcess *musicproc = MusicProcess::get_instance();
if (musicproc) musicproc->playMusic(0);
}
void CreditsGump::extractLine(Std::string &text,
char &modifier, Std::string &line) {
if (!text.empty() && (text[0] == '+' || text[0] == '&' || text[0] == '}' ||
text[0] == '~' || text[0] == '@')) {
modifier = text[0];
text.erase(0, 1);
} else {
modifier = 0;
}
if (text.empty()) {
line = "";
return;
}
Std::string::size_type starpos = text.find('*');
line = text.substr(0, starpos);
// replace '%%' by '%'.
// (Original interpreted these strings as format strings??)
Std::string::size_type ppos;
while ((ppos = line.find("%%")) != Std::string::npos) {
line.replace(ppos, 2, "%");
}
if (starpos != Std::string::npos) starpos++;
text.erase(0, starpos);
}
void CreditsGump::run() {
ModalGump::run();
if (_timer) {
_timer--;
return;
}
if (_state == CS_CLOSING) {
Close();
return;
}
_timer = 1;
int available = _scrollHeight[_currentSurface] - _currentY;
int nextblock = -1;
for (int i = 1; i < 4; i++) {
available += _scrollHeight[(_currentSurface + i) % 4];
if (nextblock == -1 && _scrollHeight[(_currentSurface + i) % 4] == 0)
nextblock = (_currentSurface + i) % 4;
}
if (available == 0) nextblock = 0;
if (_state == CS_FINISHING && available <= 156) {
debug(6, "CreditsGump: waiting before closing");
_timer = 120;
_state = CS_CLOSING;
if (!_configKey.empty()) {
ConfMan.setBool(_configKey, true);
ConfMan.flushToDisk();
}
return;
}
if (_state == CS_PLAYING && available <= 160) {
// This shouldn't happen, but just in case..
if (nextblock == -1)
nextblock = 0;
// time to render next block
Common::Rect32 bounds = _scroll[nextblock]->getSurfaceDims();
uint32 color = TEX32_PACK_RGB(0, 0, 0);
_scroll[nextblock]->fill32(color, 0, 0, bounds.width(), bounds.height());
//color = TEX32_PACK_RGB(0xFF, 0xFF, 0xFF);
//_scroll[nextblock]->fill32(color, 0, 0, bounds.width(), 2); // block marker
_scrollHeight[nextblock] = 0;
Font *redfont, *yellowfont;
redfont = FontManager::get_instance()->getGameFont(6, true);
yellowfont = FontManager::get_instance()->getGameFont(8, true);
bool done = false;
bool firstline = true;
while (!_text.empty() && !done) {
Std::string::size_type endline = _text.find('\n');
Std::string line = _text.substr(0, endline);
if (line.empty()) {
_text.erase(0, 1);
continue;
}
debug(6, "Rendering paragraph: %s", line.c_str());
if (line[0] == '+') {
// set _title
if (!firstline) {
// if this isn't the first line of the block,
// postpone setting _title until next block
done = true;
continue;
}
Std::string titletext;
char modifier;
extractLine(line, modifier, titletext);
unsigned int remaining;
_nextTitle = redfont->renderText(titletext, remaining, 192, 0,
Font::TEXT_CENTER);
if (!_title) {
_title = _nextTitle;
_nextTitle = nullptr;
} else {
_nextTitleSurf = nextblock;
_scrollHeight[nextblock] = 160; // skip some space
}
} else {
int height = 0;
Font *font = redfont;
Font::TextAlign align = Font::TEXT_LEFT;
int indent = 0;
while (!line.empty()) {
Std::string outline;
char modifier;
unsigned int remaining;
extractLine(line, modifier, outline);
debug(6, "Rendering line: %s", outline.c_str());
switch (modifier) {
case '&':
font = yellowfont;
align = Font::TEXT_CENTER;
break;
case '}':
font = redfont;
align = Font::TEXT_CENTER;
break;
case '~':
font = yellowfont;
align = Font::TEXT_LEFT;
indent = 32;
break;
case '@':
debug(6, "CreditsGump: done, finishing");
_state = CS_FINISHING;
break;
default:
break;
}
if (!modifier && outline.empty()) {
height += 48;
continue;
}
if (outline.hasPrefix("&")) {
// horizontal line
if (_scrollHeight[nextblock] + height + 7 > bounds.height()) {
done = true;
break;
}
int linewidth = outline.size() * 8;
if (linewidth > 192) linewidth = 192;
color = TEX32_PACK_RGB(0xD4, 0x30, 0x30);
_scroll[nextblock]->fill32(color, 128 - (linewidth / 2),
_scrollHeight[nextblock] + height + 3,
linewidth, 1);
height += 7;
continue;
}
RenderedText *rt = font->renderText(outline, remaining,
bounds.width() - indent, 0,
align);
int xd, yd;
rt->getSize(xd, yd);
if (_scrollHeight[nextblock] + height + yd > bounds.height()) {
delete rt;
done = true;
break;
}
rt->draw(_scroll[nextblock], indent,
_scrollHeight[nextblock] + height +
font->getBaseline());
height += yd + rt->getVlead();
delete rt;
}
if (_state == CS_PLAYING)
height += _parSkip;
if (_scrollHeight[nextblock] + height > bounds.height()) {
if (firstline) {
height = bounds.height() - _scrollHeight[nextblock];
assert(height >= 0);
} else {
done = true;
}
}
if (done) break; // no room
_scrollHeight[nextblock] += height;
}
if (endline != Std::string::npos) endline++;
_text.erase(0, endline);
firstline = false;
}
}
_currentY++;
if (_currentY >= _scrollHeight[_currentSurface]) {
// next surface
_currentY -= _scrollHeight[_currentSurface];
_scrollHeight[_currentSurface] = 0;
_currentSurface = (_currentSurface + 1) % 4;
if (_nextTitle && _currentSurface == _nextTitleSurf) {
delete _title;
_title = _nextTitle;
_nextTitle = nullptr;
}
}
}
void CreditsGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
uint32 color = TEX32_PACK_RGB(0, 0, 0);
surf->fill32(color, 0, 0, 320, 200);
// line between _title and scroller
color = TEX32_PACK_RGB(0xD4, 0x30, 0x30);
surf->fill32(color, 64, 41, 192, 1);
if (_title)
_title->draw(surf, 64, 34);
int h = _scrollHeight[_currentSurface] - _currentY;
if (h > 156) h = 156;
if (h > 0) {
Graphics::ManagedSurface* ms = _scroll[_currentSurface]->getRawSurface();
Common::Rect srcRect(0, _currentY, ms->w, _currentY + h);
surf->Blit(*ms, srcRect, 32, 44);
}
int y = h;
for (int i = 1; i < 4; i++) {
if (h == 156) break;
int s = (_currentSurface + i) % 4;
h = _scrollHeight[s];
if (h > 156 - y) h = 156 - y;
if (h > 0) {
Graphics::ManagedSurface* ms = _scroll[s]->getRawSurface();
Common::Rect srcRect(0, 0, ms->w, h);
surf->Blit(*ms, srcRect, 32, 44 + y);
}
y += h;
}
}
bool CreditsGump::OnKeyDown(int key, int mod) {
switch (key) {
case Common::KEYCODE_ESCAPE: {
Close();
}
break;
default:
break;
}
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,93 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CREDITSGUMP_H
#define ULTIMA8_GUMPS_CREDITSGUMP_H
#include "ultima/shared/std/string.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 U8
*/
class CreditsGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CreditsGump();
CreditsGump(const Std::string &text, int parskip = 24,
uint32 flags = FLAG_PREVENT_SAVE, int32 layer = LAYER_MODAL);
~CreditsGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Set a configuration option to true when user watches entire sequence
void SetFlagWhenFinished(Std::string configKey) {
_configKey = configKey;
}
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:
void extractLine(Std::string &text, char &modifier, Std::string &line);
Std::string _text;
int _parSkip;
enum CreditsState {
CS_PLAYING,
CS_FINISHING,
CS_CLOSING
} _state;
int _timer;
RenderedText *_title;
RenderedText *_nextTitle;
int _nextTitleSurf;
RenderSurface *_scroll[4];
int _scrollHeight[4];
int _currentSurface;
int _currentY;
Std::string _configKey;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,140 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/cru_ammo_gump.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(CruAmmoGump)
static const int REM_FONT_NUM = 15;
static const int REG_FONT_NUM = 8;
static const int REM_XOFF = 22;
static const int REG_XOFF = 38;
static const int REM_YOFF = 3;
static const int REG_YOFF = 6;
CruAmmoGump::CruAmmoGump() : CruStatGump(), _clipsText(nullptr), _bulletsText(nullptr) {
}
CruAmmoGump::CruAmmoGump(Shape *shape, int x) : CruStatGump(shape, x),
_clipsText(nullptr), _bulletsText(nullptr) {
_frameNum = 1;
}
CruAmmoGump::~CruAmmoGump() {
}
void CruAmmoGump::InitGump(Gump *newparent, bool take_focus) {
CruStatGump::InitGump(newparent, take_focus);
}
void CruAmmoGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
MainActor *a = getMainActor();
if (!a) {
// avatar gone??
return;
}
uint16 active = a->getActiveWeapon();
uint16 ammoitem = 0;
int bullets = -1;
uint16 clips = 0;
if (active) {
Item *item = getItem(active);
if (item) {
const WeaponInfo *weaponinfo = item->getShapeInfo()->_weaponInfo;
//uint16 frameno = 0;
if (weaponinfo && weaponinfo->_ammoType) {
//frameno = weaponinfo->_ammoType;
ammoitem = weaponinfo->_ammoShape;
bullets = item->getQuality();
}
}
}
// Only paint if this weapon has bullets that get used up.
if (bullets >= 0 && a == getControlledActor()) {
const int xoff = GAME_IS_REMORSE ? REM_XOFF : REG_XOFF;
const int yoff = GAME_IS_REMORSE ? REM_YOFF : REG_YOFF;
const int fontno = GAME_IS_REMORSE ? REM_FONT_NUM : REG_FONT_NUM;
const Std::string bulletstr = Std::string::format("%d", bullets);
if (!_bulletsText || !bulletstr.equals(_bulletsText->getText())) {
if (_bulletsText) {
RemoveChild(_bulletsText);
_bulletsText->Close();
}
_bulletsText = new TextWidget(xoff, _dims.height() / 2 - yoff, bulletstr, true, fontno);
_bulletsText->InitGump(this, false);
}
if (ammoitem) {
Item *item = a->getFirstItemWithShape(ammoitem, true);
if (item) {
clips = item->getQuality();
} else {
clips = 0;
}
}
const Std::string clipstr = Std::string::format("%d", clips);
if (!_clipsText || !clipstr.equals(_clipsText->getText())) {
if (_clipsText) {
RemoveChild(_clipsText);
_clipsText->Close();
}
_clipsText = new TextWidget(_dims.width() / 2 + xoff, _dims.height() / 2 - yoff, clipstr, true, fontno);
_clipsText->InitGump(this, false);
}
CruStatGump::PaintThis(surf, lerp_factor, scaled);
} else {
if (_bulletsText) {
RemoveChild(_bulletsText);
_bulletsText->Close();
_bulletsText = nullptr;
}
if (_clipsText) {
RemoveChild(_clipsText);
_clipsText->Close();
_clipsText = nullptr;
}
}
}
void CruAmmoGump::saveData(Common::WriteStream *ws) {
CruStatGump::saveData(ws);
}
bool CruAmmoGump::loadData(Common::ReadStream *rs, uint32 version) {
return CruStatGump::loadData(rs, version);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,61 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUAMMOGUMP_H
#define ULTIMA8_GUMPS_CRUAMMOGUMP_H
#include "ultima/ultima8/gumps/cru_stat_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class TextWidget;
/**
* Second box along the bottom of the screen, shows current ammo
*/
class CruAmmoGump : public CruStatGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CruAmmoGump();
CruAmmoGump(Shape *shape, int x);
~CruAmmoGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Paint this Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
private:
TextWidget *_bulletsText;
TextWidget *_clipsText;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,218 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "image/bmp.h"
#include "ultima/ultima8/gumps/cru_credits_gump.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/gfx/fonts/font.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/gfx/fonts/shape_font.h"
#include "ultima/ultima8/audio/music_process.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(CruCreditsGump)
CruCreditsGump::CruCreditsGump()
: ModalGump(), _timer(0), _background(nullptr), _nextScreenStart(0),
_screenNo(-1) {
}
CruCreditsGump::CruCreditsGump(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;
Graphics::Screen *sc = Ultima8Engine::get_instance()->getScreen();
_background = new RenderSurface(640, 480, sc->format);
uint32 color = TEX32_PACK_RGB(0, 0, 0);
_background->fill32(color, 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;
ms.copyFrom(*bmpsurf);
ms.setPalette(decoder.getPalette().data(), 0, decoder.getPalette().size());
Common::Rect srcRect(640, 480);
_background->Blit(ms, srcRect, 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;
}
}
}
CruCreditsGump::~CruCreditsGump() {
delete _background;
for (auto *line : _currentLines) {
delete line;
}
}
void CruCreditsGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
Mouse::get_instance()->pushMouseCursor(Mouse::MOUSE_NONE);
MusicProcess *musicproc = MusicProcess::get_instance();
if (musicproc) {
if (GAME_IS_REMORSE)
musicproc->playMusic(19);
else
musicproc->playMusic(17);
}
}
void CruCreditsGump::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 CruCreditsGump::run() {
ModalGump::run();
_timer++;
if (_timer < _nextScreenStart)
return;
_screenNo++;
if (_screenNo >= static_cast<int>(_screens.size())) {
Close();
return;
}
_nextScreenStart += _screens[_screenNo]._delay;
for (auto *line : _currentLines) {
delete line;
}
_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 (const auto &line : lines) {
Font *linefont = (line._lineType == kCredTitle) ? titlefont : namefont;
if (!linefont) {
// shouldn't happen.. just to be sure?
warning("can't render credits line type %d, font is null", line._lineType);
break;
}
unsigned int remaining;
RenderedText *rendered = linefont->renderText(line._text, remaining, 640, 0, Font::TEXT_CENTER);
_currentLines.push_back(rendered);
}
}
void CruCreditsGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Common::Rect srcRect(640, 480);
surf->Blit(*_background->getRawSurface(), srcRect, 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 (auto *line : _currentLines) {
line->draw(surf, 0, yoffset);
yoffset += (height + vlead);
}
}
bool CruCreditsGump::OnKeyDown(int key, int mod) {
if (key == Common::KEYCODE_ESCAPE)
Close();
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,95 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUCREDITSGUMP_H
#define ULTIMA8_GUMPS_CRUCREDITSGUMP_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 CruCreditsGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CruCreditsGump();
CruCreditsGump(Common::SeekableReadStream *txtrs, Common::SeekableReadStream *bmprs,
uint32 flags = FLAG_PREVENT_SAVE, int32 layer = LAYER_MODAL);
~CruCreditsGump() 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

View File

@@ -0,0 +1,108 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "image/bmp.h"
#include "ultima/ultima8/gumps/cru_demo_gump.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/audio/music_process.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(CruDemoGump)
CruDemoGump::CruDemoGump()
: ModalGump(), _background(nullptr) {
}
CruDemoGump::CruDemoGump(Common::SeekableReadStream *bmprs, uint32 flags, int32 layer)
: ModalGump(0, 0, 640, 480, 0, flags, layer), _background(nullptr)
{
Image::BitmapDecoder decoder;
Graphics::Screen *screen = Ultima8Engine::get_instance()->getScreen();
_background = new RenderSurface(640, 480, screen->format);
uint32 color = TEX32_PACK_RGB(0, 0, 0);
_background->fill32(color, 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;
ms.copyFrom(*bmpsurf);
ms.setPalette(decoder.getPalette().data(), 0, decoder.getPalette().size());
Common::Rect srcRect(640, 480);
_background->Blit(ms, srcRect, 0, 0);
} else {
warning("couldn't load bitmap background for demo screen.");
}
}
CruDemoGump::~CruDemoGump() {
delete _background;
}
void CruDemoGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
Mouse::get_instance()->pushMouseCursor(Mouse::MOUSE_NONE);
MusicProcess *musicproc = MusicProcess::get_instance();
if (musicproc) {
if (GAME_IS_REMORSE) {
musicproc->playMusic(21);
} else {
// TODO: What music do we play for Regret demo?
musicproc->playMusic(18);
}
}
}
void CruDemoGump::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 CruDemoGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Common::Rect srcRect(640, 480);
surf->Blit(*_background->getRawSurface(), srcRect, 0, 0);
}
bool CruDemoGump::OnKeyDown(int key, int mod) {
if (key == Common::KEYCODE_ESCAPE)
Close();
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,61 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUDEMOGUMP_H
#define ULTIMA8_GUMPS_CRUDEMOGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* Full-screen gump for showing the "buy me" screen
*/
class CruDemoGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CruDemoGump();
CruDemoGump(Common::SeekableReadStream *bmprs,
uint32 flags = FLAG_PREVENT_SAVE, int32 layer = LAYER_MODAL);
~CruDemoGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
void Close(bool no_del = false) override;
// Paint the Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
bool OnKeyDown(int key, int mod) override;
protected:
//! The background picture
RenderSurface *_background;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,88 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/cru_energy_gump.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
static const uint ENERGY_BAR_COLOR = 245;
DEFINE_RUNTIME_CLASSTYPE_CODE(CruEnergyGump)
CruEnergyGump::CruEnergyGump() : CruStatGump() {
}
CruEnergyGump::CruEnergyGump(Shape *shape, int x)
: CruStatGump(shape, x) {
_frameNum = 3;
}
CruEnergyGump::~CruEnergyGump() {
}
void CruEnergyGump::InitGump(Gump *newparent, bool take_focus) {
CruStatGump::InitGump(newparent, take_focus);
}
void CruEnergyGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
const Actor *a = getControlledActor();
if (!a) {
// avatar gone??
return;
}
int16 energy = a->getMana();
int16 max_energy = a->getMaxMana();
// Don't display for NPCs without energy
if (!max_energy)
return;
CruStatGump::PaintThis(surf, lerp_factor, scaled);
int width = (energy * 67) / max_energy;
const Palette *gamepal = PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game);
if (!gamepal)
return;
Common::Rect32 rect(34, 7, 34 + width, 21);
surf->fillRect(rect, gamepal->_native[ENERGY_BAR_COLOR]);
}
void CruEnergyGump::saveData(Common::WriteStream *ws) {
CruStatGump::saveData(ws);
}
bool CruEnergyGump::loadData(Common::ReadStream *rs, uint32 version) {
return CruStatGump::loadData(rs, version);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,55 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUENERGYGUMP_H
#define ULTIMA8_GUMPS_CRUENERGYGUMP_H
#include "ultima/ultima8/gumps/cru_stat_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* Energy meter, the right-most box along the bottom of the screen.
*/
class CruEnergyGump : public CruStatGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CruEnergyGump();
CruEnergyGump(Shape *shape, int x);
~CruEnergyGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Paint this Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/cru_health_gump.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
static const uint HEALTH_BAR_COLOR = 65;
DEFINE_RUNTIME_CLASSTYPE_CODE(CruHealthGump)
CruHealthGump::CruHealthGump() : CruStatGump() {
}
CruHealthGump::CruHealthGump(Shape *shape, int x)
: CruStatGump(shape, x) {
_frameNum = 2;
}
CruHealthGump::~CruHealthGump() {
}
void CruHealthGump::InitGump(Gump *newparent, bool take_focus) {
CruStatGump::InitGump(newparent, take_focus);
}
void CruHealthGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
CruStatGump::PaintThis(surf, lerp_factor, scaled);
const Actor *a = getControlledActor();
int current_hp = a ? a->getHP() : 0;
int max_hp = a ? a->getMaxHP() : 1;
// max width = 67
int width = max_hp ? ((current_hp * 67) / max_hp) : 67;
const Palette *gamepal = PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game);
if (!gamepal)
return;
Common::Rect32 rect(34, 7, 34 + width, 21);
surf->fillRect(rect, gamepal->_native[HEALTH_BAR_COLOR]);
}
void CruHealthGump::saveData(Common::WriteStream *ws) {
CruStatGump::saveData(ws);
}
bool CruHealthGump::loadData(Common::ReadStream *rs, uint32 version) {
return CruStatGump::loadData(rs, version);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,55 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUHEALTHGUMP_H
#define ULTIMA8_GUMPS_CRUHEALTHGUMP_H
#include "ultima/ultima8/gumps/cru_stat_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* Health bar, the 4th box along the bottom of the screen
*/
class CruHealthGump : public CruStatGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CruHealthGump();
CruHealthGump(Shape *shape, int x);
~CruHealthGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Paint this Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,139 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/cru_inventory_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
namespace Ultima {
namespace Ultima8 {
static const int INVENTORY_TEXT_FONT = 12;
DEFINE_RUNTIME_CLASSTYPE_CODE(CruInventoryGump)
CruInventoryGump::CruInventoryGump() : CruStatGump(), _inventoryItemGump(nullptr), _inventoryText(nullptr) {
}
CruInventoryGump::CruInventoryGump(Shape *shape, int x)
: CruStatGump(shape, x), _inventoryItemGump(nullptr), _inventoryText(nullptr) {
_frameNum = 0;
}
CruInventoryGump::~CruInventoryGump() {
}
void CruInventoryGump::InitGump(Gump *newparent, bool take_focus) {
CruStatGump::InitGump(newparent, take_focus);
GumpShapeArchive *gumpshapes = GameData::get_instance()->getGumps();
if (!gumpshapes) {
warning("failed to init stat gump: no gump shape archive");
return;
}
_inventoryItemGump = new Gump();
_inventoryItemGump->InitGump(this, false);
// we'll set the shape for this gump later.
resetText();
}
void CruInventoryGump::resetText() {
if (_inventoryText) {
RemoveChild(_inventoryText);
_inventoryText->Close();
}
_inventoryText = new TextWidget();
_inventoryText->InitGump(this, false);
}
void CruInventoryGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
const MainActor *a = getMainActor();
if (!a) {
// avatar gone??
return;
}
uint16 activeitem = a->getActiveInvItem();
if (!activeitem || a != getControlledActor()) {
resetText();
_inventoryItemGump->SetShape(0, 0);
} else {
Item *item = getItem(activeitem);
if (!item) {
resetText();
_inventoryItemGump->SetShape(0, 0);
} else {
GumpShapeArchive *gumpshapes = GameData::get_instance()->getGumps();
if (!gumpshapes) {
warning("failed to paint stat gump: no gump shape archive");
return;
}
const ShapeInfo *shapeinfo = item->getShapeInfo();
if (!shapeinfo->_weaponInfo) {
warning("no weapon info for active inventory item %d", item->getShape());
return;
}
Shape *invshape = gumpshapes->getShape(shapeinfo->_weaponInfo->_displayGumpShape);
_inventoryItemGump->SetShape(invshape, shapeinfo->_weaponInfo->_displayGumpFrame);
_inventoryItemGump->UpdateDimsFromShape();
_inventoryItemGump->setRelativePosition(CENTER);
uint16 q = item->getQuality();
if (q > 1) {
// This isn't the most efficient way to work out if we need to make new
// text, but it works..
const Std::string qtext = Std::string::format("%d", q);
const Std::string &currenttext = _inventoryText->getText();
if (!qtext.equals(currenttext)) {
RemoveChild(_inventoryText);
_inventoryText->Close();
_inventoryText = new TextWidget(_dims.width() / 2 + 22, _dims.height() / 2 + 3, qtext, true, INVENTORY_TEXT_FONT);
_inventoryText->InitGump(this, false);
}
} else {
if (!_inventoryText->getText().empty()) {
resetText();
}
}
}
// Now that the shape is configured, we can paint.
CruStatGump::PaintThis(surf, lerp_factor, scaled);
}
}
void CruInventoryGump::saveData(Common::WriteStream *ws) {
CruStatGump::saveData(ws);
}
bool CruInventoryGump::loadData(Common::ReadStream *rs, uint32 version) {
return CruStatGump::loadData(rs, version);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,63 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUINVENTORYGUMP_H
#define ULTIMA8_GUMPS_CRUINVENTORYGUMP_H
#include "ultima/ultima8/gumps/cru_stat_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class TextWidget;
/**
* Inventory box, the 3rd box along the bottom of the screen
*/
class CruInventoryGump : public CruStatGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CruInventoryGump();
CruInventoryGump(Shape *shape, int x);
~CruInventoryGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Paint this Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
private:
Gump *_inventoryItemGump;
TextWidget *_inventoryText;
void resetText();
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,238 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/cru_menu_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/gumps/quit_gump.h"
#include "ultima/ultima8/gumps/difficulty_gump.h"
#include "ultima/ultima8/games/cru_game.h"
#include "ultima/ultima8/games/game.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/metaengine.h"
#include "engines/dialogs.h"
#include "gui/saveload.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(CruMenuGump)
static const int MENU_MUSIC_REMORSE = 20;
static const int MENU_MUSIC_REGRET = 18;
CruMenuGump::CruMenuGump()
: ModalGump(0, 0, 640, 480, 0, FLAG_DONT_SAVE) {
Mouse *mouse = Mouse::get_instance();
mouse->pushMouseCursor(Mouse::MOUSE_HAND);
// Save old music state
MusicProcess *musicprocess = MusicProcess::get_instance();
if (musicprocess) {
musicprocess->saveTrackState();
int track = GAME_IS_REMORSE ? MENU_MUSIC_REMORSE : MENU_MUSIC_REGRET;
// Play the menu music
musicprocess->playMusic(track);
}
}
CruMenuGump::~CruMenuGump() {
}
void CruMenuGump::Close(bool no_del) {
// Restore old music state and palette.
// Music state can be changed by the Intro and Credits
MusicProcess *musicprocess = MusicProcess::get_instance();
if (musicprocess)
musicprocess->restoreTrackState();
Mouse *mouse = Mouse::get_instance();
mouse->popMouseCursor();
ModalGump::Close(no_del);
}
static const int FRAME_TOP_LEFT = 54;
static const int FIRST_MENU_ENTRY = 58;
static const int NUM_MENU_ENTRIES = 6;
static const int MENU_ENTRY_X_REM[] = {45, 45, 45, 446, 488, 550};
static const int MENU_ENTRY_Y_REM[] = {50, 101, 151, 58, 151, 198};
static const int MENU_ENTRY_X_REG[] = {45, 45, 45, 446, 489, 550};
static const int MENU_ENTRY_Y_REG[] = {95, 147, 197, 103, 196, 243};
void CruMenuGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
GumpShapeArchive *shapeArchive = GameData::get_instance()->getGumps();
Shape *topLeft = shapeArchive->getShape(FRAME_TOP_LEFT);
Shape *topRight = shapeArchive->getShape(FRAME_TOP_LEFT + 1);
Shape *botLeft = shapeArchive->getShape(FRAME_TOP_LEFT + 2);
Shape *botRight = shapeArchive->getShape(FRAME_TOP_LEFT + 3);
if (!topLeft || !topRight || !botLeft || !botRight) {
error("Couldn't load shapes for menu background");
return;
}
PaletteManager *palman = PaletteManager::get_instance();
assert(palman);
const Palette *pal = palman->getPalette(PaletteManager::Pal_Misc);
assert(pal);
topLeft->setPalette(pal);
topRight->setPalette(pal);
botLeft->setPalette(pal);
botRight->setPalette(pal);
const ShapeFrame *tlFrame = topLeft->getFrame(0);
const ShapeFrame *trFrame = topRight->getFrame(0);
const ShapeFrame *blFrame = botLeft->getFrame(0);
const ShapeFrame *brFrame = botRight->getFrame(0);
if (!tlFrame || !trFrame || !blFrame || !brFrame) {
error("Couldn't load shape frames for menu background");
return;
}
_dims.left = 0;
_dims.top = 0;
_dims.setWidth(tlFrame->_width + trFrame->_width);
_dims.setHeight(tlFrame->_height + brFrame->_height);
Gump *tlGump = new Gump(0, 0, tlFrame->_width, tlFrame->_height, 0, 0, _layer);
tlGump->SetShape(topLeft, 0);
tlGump->InitGump(this, false);
Gump *trGump = new Gump(tlFrame->_width, 0, trFrame->_width, trFrame->_height, 0, 0, _layer);
trGump->SetShape(topRight, 0);
trGump->InitGump(this, false);
Gump *blGump = new Gump(0, tlFrame->_height, blFrame->_width, blFrame->_height, 0, 0, _layer);
blGump->SetShape(botLeft, 0);
blGump->InitGump(this, false);
Gump *brGump = new Gump(blFrame->_width, trFrame->_height, brFrame->_width, brFrame->_height, 0, 0, _layer);
brGump->SetShape(botRight, 0);
brGump->InitGump(this, false);
const int *MENU_ENTRY_X = GAME_IS_REMORSE ? MENU_ENTRY_X_REM : MENU_ENTRY_X_REG;
const int *MENU_ENTRY_Y = GAME_IS_REMORSE ? MENU_ENTRY_Y_REM : MENU_ENTRY_Y_REG;
for (int i = 0; i < NUM_MENU_ENTRIES; i++) {
uint32 entryShapeNum = FIRST_MENU_ENTRY + i;
Shape *menuEntry = shapeArchive->getShape(entryShapeNum);
if (!menuEntry) {
error("Couldn't load shape for menu entry %d", i);
return;
}
menuEntry->setPalette(pal);
const ShapeFrame *menuEntryFrame = menuEntry->getFrame(0);
if (!menuEntryFrame || menuEntry->frameCount() != 2) {
error("Couldn't load shape frame for menu entry %d", i);
return;
}
FrameID frame_up(GameData::GUMPS, entryShapeNum, 0);
FrameID frame_down(GameData::GUMPS, entryShapeNum, 1);
Gump *widget = new ButtonWidget(MENU_ENTRY_X[i], MENU_ENTRY_Y[i],
frame_up, frame_down, true, _layer + 1);
widget->InitGump(this, false);
widget->SetIndex(i + 1);
}
}
void CruMenuGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Gump::PaintThis(surf, lerp_factor, scaled);
}
bool CruMenuGump::OnKeyDown(int key, int mod) {
if (Gump::OnKeyDown(key, mod)) return true;
if (key == Common::KEYCODE_ESCAPE) {
// FIXME: this check should probably be in Game or GUIApp
MainActor *av = getMainActor();
if (av && !av->hasActorFlags(Actor::ACT_DEAD))
Close(); // don't allow closing if dead/game over
} else if (key >= Common::KEYCODE_1 && key <= Common::KEYCODE_6) {
selectEntry(key - Common::KEYCODE_1 + 1);
}
return true;
}
void CruMenuGump::ChildNotify(Gump *child, uint32 message) {
ButtonWidget *buttonWidget = dynamic_cast<ButtonWidget *>(child);
if (buttonWidget && message == ButtonWidget::BUTTON_CLICK) {
selectEntry(child->GetIndex());
}
}
void CruMenuGump::selectEntry(int entry) {
switch (entry) {
case 1: { // New Game
Ultima8Engine::get_instance()->newGame(-1);
// When starting a new game from the menu, we skip intro movies.
CruGame *game = dynamic_cast<CruGame *>(Game::get_instance());
assert(game);
game->setSkipIntroMovie();
break;
}
case 2:
Ultima8Engine::get_instance()->loadGameDialog();
break;
case 3: // Load/Save Game
Ultima8Engine::get_instance()->saveGameDialog();
break;
case 4: {
// Options - show the ScummVM options dialog
GUI::ConfigDialog dlg;
dlg.runModal();
}
break;
case 5: { // Credits
CruGame *game = dynamic_cast<CruGame *>(Game::get_instance());
assert(game);
game->playCreditsNoMenu();
break;
}
case 6: // Quit
QuitGump::verifyQuit();
break;
default:
break;
}
}
bool CruMenuGump::OnTextInput(int unicode) {
return Gump::OnTextInput(unicode);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,60 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_REMORSEMENUGUMP_H
#define ULTIMA8_GUMPS_REMORSEMENUGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* The game menu for Crusader: No Remorse. Different enough to the U8 menu that it's implemented separately.
*/
class CruMenuGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CruMenuGump();
~CruMenuGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
void Close(bool no_del = false) 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;
protected:
virtual void selectEntry(int entry);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,101 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/cru_pickup_area_gump.h"
#include "ultima/ultima8/gumps/cru_pickup_gump.h"
#include "ultima/ultima8/world/item.h"
namespace Ultima {
namespace Ultima8 {
static const int PICKUP_GUMP_GAP = 5;
static const int PICKUP_GUMP_HEIGHT = 30;
CruPickupAreaGump *CruPickupAreaGump::_instance = nullptr;
DEFINE_RUNTIME_CLASSTYPE_CODE(CruPickupAreaGump)
CruPickupAreaGump::CruPickupAreaGump() : Gump() { }
CruPickupAreaGump::CruPickupAreaGump(bool unused) : Gump(PICKUP_GUMP_GAP, PICKUP_GUMP_GAP, 200, 500, 0, 0, LAYER_ABOVE_NORMAL) {
}
CruPickupAreaGump::~CruPickupAreaGump() {
_instance = nullptr;
}
void CruPickupAreaGump::InitGump(Gump *newparent, bool take_focus) {
Gump::InitGump(newparent, take_focus);
assert(!_instance || _instance == this);
_instance = this;
}
void CruPickupAreaGump::addPickup(const Item *item, bool showCount) {
if (!item)
return;
uint32 shapeno = item->getShape();
// Find the location to draw the gump for the new item,
// or an existing gump to recycle if we have one already
// for that shape
int32 maxy = PICKUP_GUMP_GAP;
for (auto *i : _children) {
CruPickupGump *pug = dynamic_cast<CruPickupGump *>(i);
if (!pug)
continue;
if (pug->getShapeNo() == shapeno) {
// Already a notification for this object, update it
pug->updateForNewItem(item);
return;
}
int32 x, y;
pug->getLocation(x, y);
maxy = MAX(maxy, y + PICKUP_GUMP_GAP + PICKUP_GUMP_HEIGHT);
}
// didn't find one, create a new one at the bottom.
Gump *newgump = new CruPickupGump(item, maxy, showCount);
newgump->InitGump(this, false);
}
void CruPickupAreaGump::saveData(Common::WriteStream *ws) {
Gump::saveData(ws);
}
bool CruPickupAreaGump::loadData(Common::ReadStream *rs, uint32 version) {
if (!Gump::loadData(rs, version))
return false;
if (_instance && _instance != this)
delete _instance;
_instance = this;
return true;
}
CruPickupAreaGump *CruPickupAreaGump::get_instance() {
return _instance;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,63 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUPICKUPAREAGUMP_H
#define ULTIMA8_GUMPS_CRUPICKUPAREAGUMP_H
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class TextWidget;
/**
* The area that manages all the "pickup" gumps (the notifications that a new
* item has been picked up)
*/
class CruPickupAreaGump : public Gump {
public:
ENABLE_RUNTIME_CLASSTYPE()
// default constructor only for use when loading savegame
CruPickupAreaGump();
// Normal constructor
CruPickupAreaGump(bool unused);
~CruPickupAreaGump();
void InitGump(Gump *newparent, bool take_focus = false) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
void addPickup(const Item *item, bool showCount);
static CruPickupAreaGump *get_instance();
private:
static CruPickupAreaGump *_instance;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,176 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/cru_pickup_gump.h"
#include "ultima/ultima8/gumps/translucent_gump.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
namespace Ultima {
namespace Ultima8 {
static const int PICKUP_GUMP_SHAPE = 2;
static const int COUNT_TEXT_FONT = 12;
static const int ITEM_TEXT_FONT = 13;
static const int ITEM_AREA_WIDTH = 60;
// A high "random" index so we can always find this..
static const int COUNT_TEXT_INDEX = 0x100;
DEFINE_RUNTIME_CLASSTYPE_CODE(CruPickupGump)
CruPickupGump::CruPickupGump() : Gump(), _startFrame(0), _itemShapeNo(0), _q(0),
_gumpShapeNo(0), _gumpFrameNo(0), _showCount(false) {
}
CruPickupGump::CruPickupGump(const Item *item, int y, bool showCount) : Gump(0, y, 5, 5, 0),
_startFrame(0), _showCount(showCount) {
const WeaponInfo *weaponInfo = item->getShapeInfo()->_weaponInfo;
if (weaponInfo) {
_itemShapeNo = item->getShape();
if (item->getShapeInfo()->_family == ShapeInfo::SF_CRUAMMO)
_q = 1;
else
_q = item->getQuality();
_itemName = weaponInfo->_name;
_gumpShapeNo = weaponInfo->_displayGumpShape;
_gumpFrameNo = weaponInfo->_displayGumpFrame;
// Special case for keycard - display depends on the card type
if (_itemShapeNo == 0x111)
_gumpFrameNo += item->getFrame();
} else {
_itemShapeNo = 0;
_q = 0;
_gumpShapeNo = 0;
_gumpFrameNo = 0;
}
}
void CruPickupGump::InitGump(Gump *newparent, bool take_focus) {
Gump::InitGump(newparent, take_focus);
if (!_itemShapeNo)
return;
_startFrame = Kernel::get_instance()->getFrameNum();
// Get the shapes we will need..
GumpShapeArchive *gumpshapes = GameData::get_instance()->getGumps();
if (!gumpshapes) {
warning("failed to init stat gump: no gump shape archive");
return;
}
const Shape *background = gumpshapes->getShape(PICKUP_GUMP_SHAPE);
if (!background || !background->getFrame(0)) {
warning("failed to init stat gump: no pickup background shape");
return;
}
const ShapeFrame *bgframe = background->getFrame(0);
Shape* itemshape = gumpshapes->getShape(_gumpShapeNo);
if (!itemshape || !itemshape->getFrame(_gumpFrameNo)) {
warning("failed to init stat gump: no item shape");
return;
}
// Paint a semi-transparent background
const FrameID bfgrameid(GameData::GUMPS, PICKUP_GUMP_SHAPE, 0);
// TODO: The game uses a variable number of these depending on the text length
// For now 5 is ok.
for (int i = 0; i < 5; i++) {
Gump *gump = new TranslucentGump(i * bgframe->_width, 0, bgframe->_width, bgframe->_height);
gump->SetShape(bfgrameid, false);
gump->InitGump(this, false);
}
_dims.setWidth(bgframe->_width * 5);
_dims.setHeight(bgframe->_height);
// Paint the item name text
TextWidget *text = new TextWidget(ITEM_AREA_WIDTH, bgframe->_height / 2 - 5, _itemName, true, ITEM_TEXT_FONT);
text->InitGump(this, false);
// Paint the count if needed
addCountText();
// Paint the item in the mid-left item area.
const ShapeFrame *itemframe = itemshape->getFrame(_gumpFrameNo);
Gump *itemgump = new Gump(0, _dims.height() / 2 - itemframe->_height / 2, itemframe->_width, itemframe->_height, 0, 0, LAYER_ABOVE_NORMAL);
itemgump->SetShape(itemshape, _gumpFrameNo);
itemgump->InitGump(this, false);
itemgump->UpdateDimsFromShape();
itemgump->Move(ITEM_AREA_WIDTH / 2 - itemframe->_width / 2, _dims.height() / 2 - itemframe->_height / 2);
}
void CruPickupGump::updateForNewItem(const Item *item) {
assert(item);
assert(item->getShape() == _itemShapeNo);
TextWidget *oldtext = dynamic_cast<TextWidget *>(FindGump(&FindByIndex<COUNT_TEXT_INDEX>));
if (oldtext) {
RemoveChild(oldtext);
oldtext->Close();
}
// Always show count for repeat objects.
_showCount = true;
// If we're updating the existing count, add 1 or special-case credits
if (_itemShapeNo == 0x4ed)
_q += item->getQuality();
else
_q++;
addCountText();
}
void CruPickupGump::addCountText() {
if (_q <= 1 || !_showCount)
return;
Std::string qstr = Std::string::format("%d", _q);
TextWidget *count = new TextWidget(ITEM_AREA_WIDTH / 2 + 22, _dims.height() / 2 + 3, qstr, true, COUNT_TEXT_FONT);
count->InitGump(this, false);
count->SetIndex(COUNT_TEXT_INDEX);
}
void CruPickupGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
uint32 frameno = Kernel::get_instance()->getFrameNum();
if (!_itemShapeNo || frameno - _startFrame > 90) {
Close();
return;
}
}
void CruPickupGump::saveData(Common::WriteStream *ws) {
Gump::saveData(ws);
}
bool CruPickupGump::loadData(Common::ReadStream *rs, uint32 version) {
return Gump::loadData(rs, version);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUPICKUPGUMP_H
#define ULTIMA8_GUMPS_CRUPICKUPGUMP_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class TextWidget;
/**
* Pickup box, the box that appears in the top left when a new item is picked up
*/
class CruPickupGump : public Gump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CruPickupGump();
CruPickupGump(const Item *item, int y, bool showCount);
~CruPickupGump() override {};
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Paint this Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
uint32 getShapeNo() const {
return _itemShapeNo;
}
uint16 getQ() {
return _q;
}
//! Update for a second item pickup - updates existing count text.
void updateForNewItem(const Item *item);
void addCountText();
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
private:
uint32 _startFrame;
uint32 _itemShapeNo;
uint16 _gumpShapeNo;
uint16 _gumpFrameNo;
Std::string _itemName;
uint16 _q;
bool _showCount;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,57 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/cru_stat_gump.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(CruStatGump)
CruStatGump::CruStatGump() : TranslucentGump() {
}
CruStatGump::CruStatGump(Shape *shape, int x)
: TranslucentGump(x, 0, 5, 5, 0, FLAG_DONT_SAVE) {
_shape = shape;
}
CruStatGump::~CruStatGump() {
}
void CruStatGump::InitGump(Gump *newparent, bool take_focus) {
TranslucentGump::InitGump(newparent, take_focus);
UpdateDimsFromShape();
}
void CruStatGump::saveData(Common::WriteStream *ws) {
TranslucentGump::saveData(ws);
}
bool CruStatGump::loadData(Common::ReadStream *rs, uint32 version) {
return TranslucentGump::loadData(rs, version);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,52 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUSTATGUMP_H
#define ULTIMA8_GUMPS_CRUSTATGUMP_H
#include "ultima/ultima8/gumps/translucent_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* Superclass for the 5 status boxes along the bottom of the screen
*/
class CruStatGump : public TranslucentGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CruStatGump();
CruStatGump(Shape *shape, int x);
~CruStatGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,147 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/cru_status_gump.h"
#include "ultima/ultima8/gumps/cru_weapon_gump.h"
#include "ultima/ultima8/gumps/cru_ammo_gump.h"
#include "ultima/ultima8/gumps/cru_inventory_gump.h"
#include "ultima/ultima8/gumps/cru_health_gump.h"
#include "ultima/ultima8/gumps/cru_energy_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(CruStatusGump)
static const int PX_FROM_BOTTOM = 2; //! gap (y) between bottom of screen and bottom of a single item
static const int PX_FROM_LEFT = 15; //! gap (x) from left of screen to weapon box
static const int PX_GAP = 17; //! gap (x) between boxes in status bar
static const int FRAME_GUMP_SHAPE = 1;
CruStatusGump *CruStatusGump::_instance = nullptr;
CruStatusGump::CruStatusGump() : Gump() { }
// Start with an approximate width/height which we will adjust later..
CruStatusGump::CruStatusGump(bool unused) : Gump(PX_FROM_LEFT, PX_FROM_BOTTOM, 500, 100, 0, 0, LAYER_ABOVE_NORMAL) {
assert(!_instance);
_instance = this;
}
CruStatusGump::~CruStatusGump() {
assert(!_instance || _instance == this);
_instance = nullptr;
}
void CruStatusGump::InitGump(Gump *newparent, bool take_focus) {
Gump::InitGump(newparent, take_focus);
createStatusItems();
}
void CruStatusGump::createStatusItems() {
assert(_children.size() == 0);
GumpShapeArchive *gumpshapes = GameData::get_instance()->getGumps();
if (!gumpshapes) {
warning("failed to init stats gump: no gump shape archive");
return;
}
Shape *frameShape = gumpshapes->getShape(FRAME_GUMP_SHAPE);
if (!frameShape || !frameShape->getFrame(0)) {
warning("failed to init stats gump: no gump frame");
return;
}
int w = frameShape->getFrame(0)->_width;
int h = frameShape->getFrame(0)->_height;
int xoff = 0;
Gump *weaponGump = new CruWeaponGump(frameShape, xoff);
weaponGump->InitGump(this);
Gump *ammoGump = new CruAmmoGump(frameShape, xoff + w + PX_GAP);
ammoGump->InitGump(this);
Gump *inventoryGump = new CruInventoryGump(frameShape, xoff + (w + PX_GAP) * 2);
inventoryGump->InitGump(this);
Gump *health = new CruHealthGump(frameShape, xoff + (w + PX_GAP) * 3);
health->InitGump(this);
Gump *energyGump = new CruEnergyGump(frameShape, xoff + (w + PX_GAP) * 4);
energyGump->InitGump(this);
_dims.setWidth(w * 5 + PX_GAP * 4);
_dims.setHeight(h);
setRelativePosition(BOTTOM_LEFT, PX_FROM_LEFT, -PX_FROM_BOTTOM);
}
void CruStatusGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
if (_children.empty()) {
// children deliberately aren't saved, recreate them if needed.
createStatusItems();
}
Gump::PaintThis(surf, lerp_factor, scaled);
// All the painting logic is in the children.
}
void CruStatusGump::saveData(Common::WriteStream *ws) {
Gump::saveData(ws);
}
bool CruStatusGump::loadData(Common::ReadStream *rs, uint32 version) {
if (!Gump::loadData(rs, version))
return false;
if (_instance && _instance != this)
delete _instance;
_instance = this;
return true;
}
uint32 CruStatusGump::I_hideStatusGump(const uint8 * /*args*/,
unsigned int /*argsize*/) {
CruStatusGump *instance = get_instance();
if (instance) {
instance->Close();
_instance = nullptr;
}
return 0;
}
uint32 CruStatusGump::I_showStatusGump(const uint8 * /*args*/,
unsigned int /*argsize*/) {
CruStatusGump *instance = get_instance();
if (!instance) {
instance = new CruStatusGump(true);
instance->InitGump(nullptr, false);
assert(_instance);
}
return 0;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUSTATUSGUMP_H
#define ULTIMA8_GUMPS_CRUSTATUSGUMP_H
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* Represents the collection of status boxes along the bottom of the screen.
* Each of the individual items is a class of CruStatGump.
*/
class CruStatusGump : public Gump {
public:
ENABLE_RUNTIME_CLASSTYPE()
// default constructor for save game loading
CruStatusGump();
// need a parameter to differentiate the non-default constructor..
CruStatusGump(bool unused);
~CruStatusGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Paint this Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
void createStatusItems();
static CruStatusGump *get_instance() {
return _instance;
}
INTRINSIC(I_hideStatusGump);
INTRINSIC(I_showStatusGump);
private:
static CruStatusGump *_instance;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,120 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/cru_weapon_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
static const int WEAPON_GUMP_SHAPE = 3;
DEFINE_RUNTIME_CLASSTYPE_CODE(CruWeaponGump)
CruWeaponGump::CruWeaponGump() : CruStatGump(), _weaponShape(nullptr) {
}
CruWeaponGump::CruWeaponGump(Shape *shape, int x)
: CruStatGump(shape, x), _weaponShape(nullptr) {
_frameNum = 0;
GumpShapeArchive *gumpshapes = GameData::get_instance()->getGumps();
if (!gumpshapes) {
warning("failed to init stat gump: no gump shape archive");
return;
}
_weaponShape = gumpshapes->getShape(WEAPON_GUMP_SHAPE);
if (!_weaponShape || !_weaponShape->getFrame(0)) {
warning("failed to init stat gump: no weapon shape");
return;
}
}
CruWeaponGump::~CruWeaponGump() {
}
void CruWeaponGump::InitGump(Gump *newparent, bool take_focus) {
CruStatGump::InitGump(newparent, take_focus);
// We will fill out the shape to paint for this later.
Gump *weaponGump = new Gump();
weaponGump->InitGump(this, false);
weaponGump->SetIndex(1);
}
void CruWeaponGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
const MainActor *a = getMainActor();
if (!a) {
// avatar gone??
return;
}
Gump *weaponGump = _children.front();
if (a != getControlledActor()) {
// Only paint when controlling avatar
weaponGump->HideGump();
return;
} else {
weaponGump->UnhideGump();
}
assert(weaponGump);
uint16 active = a->getActiveWeapon();
if (!active) {
weaponGump->SetShape(0, 0);
} else {
Item *item = getItem(active);
if (!item) {
weaponGump->SetShape(0, 0);
} else {
const WeaponInfo *weaponinfo = item->getShapeInfo()->_weaponInfo;
uint16 frameno = 0;
if (weaponinfo) {
// this should be a weapon, otherwise why are we here?
assert(WEAPON_GUMP_SHAPE == weaponinfo->_displayGumpShape);
frameno = weaponinfo->_displayGumpFrame;
}
weaponGump->SetShape(_weaponShape, frameno);
weaponGump->UpdateDimsFromShape();
weaponGump->setRelativePosition(CENTER);
}
}
CruStatGump::PaintThis(surf, lerp_factor, scaled);
}
void CruWeaponGump::saveData(Common::WriteStream *ws) {
CruStatGump::saveData(ws);
}
bool CruWeaponGump::loadData(Common::ReadStream *rs, uint32 version) {
return CruStatGump::loadData(rs, version);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,58 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_CRUWEAPONGUMP_H
#define ULTIMA8_GUMPS_CRUWEAPONGUMP_H
#include "ultima/ultima8/gumps/cru_stat_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* Superclass for the 5 status boxes along the bottom of the screen
*/
class CruWeaponGump : public CruStatGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
CruWeaponGump();
CruWeaponGump(Shape *shape, int x);
~CruWeaponGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Paint this Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
private:
Shape *_weaponShape;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,87 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/desktop_gump.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gumps/target_gump.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(DesktopGump)
bool DesktopGump::_fadedModal = true;
DesktopGump::DesktopGump()
: Gump() {
}
DesktopGump::DesktopGump(int32 x, int32 y, int32 width, int32 height) :
Gump(x, y, width, height, 0, FLAG_DONT_SAVE | FLAG_CORE_GUMP,
LAYER_DESKTOP) {
}
DesktopGump::~DesktopGump(void) {
}
void DesktopGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
}
void DesktopGump::PaintChildren(RenderSurface *surf, int32 lerp_factor, bool scaled) {
for (auto *g : _children) {
// Paint if not closing
if (!g->IsClosing()) {
// If background blanking on modal is enabled...
// Background is partially transparent
if (_fadedModal && dynamic_cast<ModalGump *>(g) &&
!dynamic_cast<TargetGump *>(g) && !g->IsHidden())
surf->fillBlended(TEX32_PACK_RGBA(0, 0, 0, 0x80), _dims);
g->Paint(surf, lerp_factor, scaled);
}
}
}
void DesktopGump::RenderSurfaceChanged() {
// Resize the desktop gump to match the parent
if (_parent) {
Common::Rect32 new_dims = _parent->getDims();
_dims.setWidth(new_dims.width());
_dims.setHeight(new_dims.height());
}
Gump::RenderSurfaceChanged();
}
void DesktopGump::saveData(Common::WriteStream *ws) {
warning("Trying to save DesktopGump");
}
bool DesktopGump::loadData(Common::ReadStream *rs, uint32 version) {
warning("Trying to load DesktopGump");
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,59 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_DESKTOPGUMP_H
#define ULTIMA8_GUMPS_DESKTOPGUMP_H
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* A virtual gump which contains all the regular game components.
*/
class DesktopGump : public Gump {
static bool _fadedModal;
public:
ENABLE_RUNTIME_CLASSTYPE()
DesktopGump();
DesktopGump(int32 x, int32 y, int32 width, int32 height);
~DesktopGump() override;
void RenderSurfaceChanged() override;
void PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) override;
void PaintChildren(RenderSurface *surf, int32 lerp_factor, bool scaled) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
static void SetFadedModal(bool set) {
_fadedModal = set;
}
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,189 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/difficulty_gump.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(DifficultyGump)
DifficultyGump::DifficultyGump()
: ModalGump(0, 0, 5, 5), _highlighted(3), _buttonWidth(0),
_buttonHeight(0) {
}
DifficultyGump::~DifficultyGump() {
}
static const int BUTTON_X = 158;
static const int BUTTON_Y = 120;
static const int BUTTON_SPACE = 17;
static const int BUTTON_HEIGHT = 42;
static const int RIGHT_FRAME_IDX_OFFSET = 16;
// gumps: 73: "difficulty level"
// 74-77: levels, 2 frames per level (left/right)
void DifficultyGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
Mouse *mouse = Mouse::get_instance();
mouse->pushMouseCursor(Mouse::MOUSE_HAND);
_dims.top = 0;
_dims.left = 0;
_dims.bottom = 480;
_dims.right = 640;
GumpShapeArchive *shapeArchive = GameData::get_instance()->getGumps();
Shape *difficultyLevel = shapeArchive->getShape(73);
Shape *levelShape[4];
for (int s = 74; s < 78; s++) {
levelShape[s - 74] = shapeArchive->getShape(s);
}
if (!difficultyLevel || !levelShape[0] || !levelShape[1] ||
!levelShape[2] || !levelShape[3]) {
error("Couldn't load shapes for difficulty level");
return;
}
PaletteManager *palman = PaletteManager::get_instance();
assert(palman);
const Palette *pal = palman->getPalette(PaletteManager::Pal_Diff);
assert(pal);
difficultyLevel->setPalette(pal);
for (int s = 0; s < 4; s++) {
levelShape[s]->setPalette(pal);
}
const ShapeFrame *difficultyFrame = difficultyLevel->getFrame(0);
if (!difficultyFrame) {
error("Couldn't load shape frame for difficulty level");
return;
}
Gump *diffGump = new Gump(185, 77, difficultyFrame->_width, difficultyFrame->_height);
diffGump->SetShape(difficultyLevel, 0);
diffGump->InitGump(this, false);
for (int s = 0; s < 4; s++) {
const int y = BUTTON_Y + (BUTTON_SPACE + BUTTON_HEIGHT) * s;
const ShapeFrame *leftFrame = levelShape[s]->getFrame(0);
const ShapeFrame *rightFrame = levelShape[s]->getFrame(1);
if (!leftFrame || !rightFrame) {
error("Couldn't load shape frame for difficulty level %d", s);
return;
}
Gump *lGump = new Gump(BUTTON_X, y, leftFrame->_width, leftFrame->_height);
lGump->SetShape(levelShape[s], 0);
lGump->InitGump(this, false);
lGump->SetIndex(s + 1);
Gump *rGump = new Gump(BUTTON_X + leftFrame->_width, y, rightFrame->_width, rightFrame->_height);
rGump->SetShape(levelShape[s], 1);
rGump->InitGump(this, false);
rGump->SetIndex(s + 1 + RIGHT_FRAME_IDX_OFFSET);
_buttonHeight = MAX(_buttonHeight, leftFrame->_height);
_buttonHeight = MAX(_buttonHeight, rightFrame->_height);
_buttonWidth = MAX(_buttonWidth, static_cast<int16>(leftFrame->_width + rightFrame->_width));
}
// remove focus from children (just in case)
if (_focusChild) _focusChild->OnFocus(false);
_focusChild = 0;
}
void DifficultyGump::Close(bool no_del) {
Mouse *mouse = Mouse::get_instance();
mouse->popMouseCursor();
ModalGump::Close(no_del);
}
void DifficultyGump::OnFocus(bool gain) {
}
void DifficultyGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
// Paint a highlight around the current level
int highlihght_y = BUTTON_Y + ((_highlighted - 1) * (BUTTON_SPACE + BUTTON_HEIGHT));
uint32 color = TEX32_PACK_RGB(0x80, 0x80, 0x80);
surf->fill32(color, BUTTON_X - 1, highlihght_y - 1, _buttonWidth + 2, _buttonHeight + 2);
ModalGump::PaintThis(surf, lerp_factor, scaled);
}
void DifficultyGump::onMouseClick(int button, int32 mx, int32 my) {
if (button == Mouse::BUTTON_LEFT) {
Gump *gump = FindGump(mx, my);
if (gump && gump->GetIndex() > 0) {
int idx = gump->GetIndex();
if (idx > RIGHT_FRAME_IDX_OFFSET)
idx -= RIGHT_FRAME_IDX_OFFSET;
selectEntry(idx);
}
}
}
bool DifficultyGump::OnKeyDown(int key, int mod) {
if (ModalGump::OnKeyDown(key, mod)) return true;
if (key == Common::KEYCODE_ESCAPE) {
// Don't allow closing, we have to choose a difficulty.
return true;
} else if (key >= Common::KEYCODE_1 && key <= Common::KEYCODE_4) {
selectEntry(key - Common::KEYCODE_1 + 1);
} else if (key == Common::KEYCODE_UP) {
_highlighted--;
if (_highlighted < 1)
_highlighted = 4;
} else if (key == Common::KEYCODE_DOWN) {
_highlighted++;
if (_highlighted > 4)
_highlighted = 1;
} else if (key == Common::KEYCODE_RETURN) {
selectEntry(_highlighted);
} else {
return false;
}
return true;
}
void DifficultyGump::selectEntry(int num) {
debug(6, "selected difficulty %d", num);
World::get_instance()->setGameDifficulty(num);
Close();
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,59 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_DIFFICULTYGUMP_H
#define ULTIMA8_GUMPS_DIFFICULTYGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
namespace Ultima {
namespace Ultima8 {
/**
* Difficulty selector menu for Crusader: No Remorse
*/
class DifficultyGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
DifficultyGump();
~DifficultyGump() override;
void InitGump(Gump *newparent, bool take_focus = true) override;
void Close(bool no_del = false) override;
void onMouseClick(int button, int32 mx, int32 my) override;
bool OnKeyDown(int key, int mod) override;
void OnFocus(bool gain) override;
void PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) override;
private:
int _highlighted;
int16 _buttonWidth;
int16 _buttonHeight;
void selectEntry(int num);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,83 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/fast_area_vis_gump.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/world/current_map.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(FastAreaVisGump)
FastAreaVisGump::FastAreaVisGump(void) : Gump(0, 0, MAP_NUM_CHUNKS + 2, MAP_NUM_CHUNKS + 2, 0, FLAG_DRAGGABLE | FLAG_DONT_SAVE, LAYER_NORMAL) {
}
FastAreaVisGump::~FastAreaVisGump(void) {
}
void FastAreaVisGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
World *world = World::get_instance();
CurrentMap *currentmap = world->getCurrentMap();
uint32 color = TEX32_PACK_RGB(0xFF, 0, 0);
surf->frameRect32(color, _dims);
color = TEX32_PACK_RGB(0, 0, 0);
surf->fill32(color, 1, 1, MAP_NUM_CHUNKS, MAP_NUM_CHUNKS);
color = TEX32_PACK_RGB(0xFF, 0xFF, 0xFF);
for (int yp = 0; yp < MAP_NUM_CHUNKS; yp++)
for (int xp = 0; xp < MAP_NUM_CHUNKS; xp++)
if (currentmap->isChunkFast(xp, yp))
surf->fill32(color, xp + 1, yp + 1, 1, 1);
// Put a red dot where the avatar is
Item *avatar = getItem(1);
if (avatar) {
Point3 pt = avatar->getLocation();
int chunksize = currentmap->getChunkSize();
pt.x /= chunksize;
pt.y /= chunksize;
if (pt.x >= 0 && pt.x < MAP_NUM_CHUNKS && pt.y >= 0 && pt.y < MAP_NUM_CHUNKS) {
color = TEX32_PACK_RGB(0xFF, 0x10, 0x10);
surf->fill32(color, pt.x + 1, pt.y + 1, 1, 1);
}
}
}
uint16 FastAreaVisGump::TraceObjId(int32 mx, int32 my) {
uint16 objId_ = Gump::TraceObjId(mx, my);
if (!objId_ || objId_ == 65535)
if (PointOnGump(mx, my))
objId_ = getObjId();
return objId_;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,49 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_FASTAREAVISGUMP_H
#define ULTIMA8_GUMPS_FASTAREAVISGUMP_H
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* A gump for debugging which shows all the things in the "fast" area (the area around the avatar
* which is being calculated for pathfinding etc)
*/
class FastAreaVisGump : public Gump {
public:
ENABLE_RUNTIME_CLASSTYPE()
FastAreaVisGump();
~FastAreaVisGump() override;
void PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) override;
uint16 TraceObjId(int32 mx, int32 my) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,624 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima.h"
#include "ultima/ultima8/gumps/game_map_gump.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/gumps/slider_gump.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/misc/direction_util.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/world/current_map.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/world/item_factory.h"
#include "ultima/ultima8/world/item_sorter.h"
#include "ultima/ultima8/world/camera_process.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/actors/avatar_mover_process.h"
#include "ultima/ultima8/world/actors/pathfinder_process.h"
#include "ultima/ultima8/world/missile_tracker.h"
#include "ultima/ultima8/world/split_item_process.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(GameMapGump)
bool GameMapGump::_highlightItems = false;
bool GameMapGump::_showFootpads = false;
int GameMapGump::_gridlines = 0;
GameMapGump::GameMapGump() :
Gump(), _displayDragging(false), _displayList(0), _draggingShape(0),
_draggingFrame(0), _draggingFlags(0) {
_displayList = new ItemSorter(2048);
}
GameMapGump::GameMapGump(int x, int y, int width, int height) :
Gump(x, y, width, height, 0, FLAG_DONT_SAVE | FLAG_CORE_GUMP, LAYER_GAMEMAP),
_displayList(0), _displayDragging(false), _draggingShape(0), _draggingFrame(0),
_draggingFlags(0) {
// Offset the gump. We want 0,0 to be the centre
_dims.moveTo(-_dims.width() / 2, -_dims.height() / 2);
_displayList = new ItemSorter(2048);
}
GameMapGump::~GameMapGump() {
delete _displayList;
}
Point3 GameMapGump::GetCameraLocation(int lerp_factor) {
CameraProcess *camera = CameraProcess::GetCameraProcess();
if (camera) {
return camera->GetLerped(lerp_factor);
}
return CameraProcess::GetCameraLocation();
}
void GameMapGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
World *world = World::get_instance();
if (!world) return; // Is it possible the world doesn't exist?
CurrentMap *map = world->getCurrentMap();
if (!map) return; // Is it possible the map doesn't exist?
// Get the camera location
Point3 loc = GetCameraLocation(lerp_factor);
CameraProcess *camera = CameraProcess::GetCameraProcess();
int zlimit = 1 << 16; // should be high enough
const Item *roof = nullptr;
if (camera) {
uint16 roofid = camera->findRoof(lerp_factor);
roof = getItem(roofid);
} else {
const Actor *av = getMainActor();
Box b = av->getWorldBox();
PositionInfo info = map->getPositionInfo(b, b, 0, 1);
roof = info.roof;
}
if (roof) {
zlimit = roof->getZ();
}
Common::Rect32 clipWindow = surf->getClippingRect();
_displayList->BeginDisplayList(clipWindow, loc);
uint32 gametick = Kernel::get_instance()->getFrameNum();
bool showEditorItems = Ultima8Engine::get_instance()->isShowEditorItems();
// Get all the required items
for (int cy = 0; cy < MAP_NUM_CHUNKS; cy++) {
for (int cx = 0; cx < MAP_NUM_CHUNKS; cx++) {
// Not fast, ignore
if (!map->isChunkFast(cx, cy)) continue;
const Std::list<Item *> *items = map->getItemList(cx, cy);
if (!items) continue;
Std::list<Item *>::const_iterator it = items->begin();
Std::list<Item *>::const_iterator end = items->end();
for (; it != end; ++it) {
Item *item = *it;
if (!item) continue;
item->setupLerp(gametick);
item->doLerp(lerp_factor);
if (item->getZ() >= zlimit && !item->getShapeInfo()->is_draw())
continue;
if (!showEditorItems && item->getShapeInfo()->is_editor())
continue;
if (item->hasFlags(Item::FLG_INVISIBLE)) {
// special case: invisible avatar _is_ drawn
// HACK: unless EXT_TRANSPARENT is also set.
// (Used for hiding the avatar when drawing a full area map)
if (item->getObjId() == kMainActorId) {
if (item->hasExtFlags(Item::EXT_TRANSPARENT))
continue;
_displayList->AddItem(item->getLerped(), item->getShape(), item->getFrame(),
item->getFlags() & ~Item::FLG_INVISIBLE, item->getExtFlags() | Item::EXT_TRANSPARENT, 1);
}
continue;
}
_displayList->AddItem(item);
}
}
}
// Dragging:
if (_displayDragging) {
_displayList->AddItem(_draggingPos, _draggingShape, _draggingFrame,
_draggingFlags, Item::EXT_TRANSPARENT);
}
int gridlines = _gridlines;
if (gridlines < 0) {
gridlines = map->getChunkSize();
}
_displayList->PaintDisplayList(surf, _highlightItems, _showFootpads, gridlines);
}
// Trace a click, and return ObjId
uint16 GameMapGump::TraceObjId(int32 mx, int32 my) {
uint16 objId_ = Gump::TraceObjId(mx, my);
if (objId_ && objId_ != 65535) return objId_;
ParentToGump(mx, my);
return _displayList->Trace(mx, my, 0, _highlightItems);
}
uint16 GameMapGump::TraceCoordinates(int mx, int my, Point3 &coords,
int offsetx, int offsety, Item *item) {
int32 dxd = 0, dyd = 0, dzd = 0;
if (item)
item->getFootpadWorld(dxd, dyd, dzd);
Point3 c = GetCameraLocation();
ItemSorter::HitFace face;
ObjId trace = _displayList->Trace(mx, my, &face);
Item *hit = getItem(trace);
if (!hit) // strange...
return 0;
int32 hxd, hyd, hzd;
Point3 h = hit->getLocation();
hit->getFootpadWorld(hxd, hyd, hzd);
// adjust mx (if dragged item wasn't 'picked up' at its origin)
mx -= offsetx;
my -= offsety;
// mx = (coords.x - c.x - coords.y + c.y) / 4
// my = (coords.x - c.x + coords.y - c.y) / 8 - coords.z + c.z
// the below expressions solve these two equations to two of the coords,
// while fixing the other coord
switch (face) {
case ItemSorter::Z_FACE:
coords.x = 2 * mx + 4 * (my + h.z + hzd) + c.x - 4 * c.z;
coords.y = -2 * mx + 4 * (my + h.z + hzd) + c.y - 4 * c.z;
coords.z = h.z + hzd;
break;
case ItemSorter::X_FACE:
coords.x = h.x + dxd;
coords.y = -4 * mx + h.x + dxd - c.x + c.y;
coords.z = -my + (h.x + dxd) / 4 - mx / 2 - c.x / 4 + c.z;
break;
case ItemSorter::Y_FACE:
coords.x = 4 * mx + h.y + dyd + c.x - c.y;
coords.y = h.y + dyd;
coords.z = -my + mx / 2 + (h.y + dyd) / 4 - c.y / 4 + c.z;
break;
}
return trace;
}
bool GameMapGump::GetLocationOfItem(uint16 itemid, int32 &gx, int32 &gy,
int32 lerp_factor) {
Item *item = getItem(itemid);
if (!item)
return false;
Container *root = item->getRootContainer();
if (root)
item = root;
if (item->hasFlags(Item::FLG_ETHEREAL))
return false;
// Hacks be us. Force the item into the fast area
item->setupLerp(Kernel::get_instance()->getFrameNum());
item->doLerp(lerp_factor);
Point3 i = item->getLerped();
// Get the camera's location
Point3 c;
CameraProcess *cam = CameraProcess::GetCameraProcess();
if (cam)
c = cam->GetLerped(lerp_factor, true);
else
c = CameraProcess::GetCameraLocation();
// Screenspace bounding box bottom x coord (RNB x coord)
gx = (i.x - i.y) / 4;
// Screenspace bounding box bottom extent (RNB y coord)
gy = (i.x + i.y) / 8 - i.z;
// Screenspace bounding box bottom x coord (RNB x coord)
gx -= (c.x - c.y) / 4;
// Screenspace bounding box bottom extent (RNB y coord)
gy -= (c.x + c.y) / 8 - c.z;
return true;
}
Gump *GameMapGump::onMouseDown(int button, int32 mx, int32 my) {
int32 sx = mx, sy = my;
ParentToGump(sx, sy);
GumpToScreenSpace(sx, sy);
AvatarMoverProcess *amp = Ultima8Engine::get_instance()->getAvatarMoverProcess();
if (button == Mouse::BUTTON_RIGHT || button == Mouse::BUTTON_LEFT) {
amp->onMouseDown(button, sx, sy);
}
if (button == Mouse::BUTTON_LEFT || button == Mouse::BUTTON_RIGHT ||
button == Mouse::BUTTON_MIDDLE) {
// we take all clicks
return this;
}
return nullptr;
}
void GameMapGump::onMouseUp(int button, int32 mx, int32 my) {
AvatarMoverProcess *amp = Ultima8Engine::get_instance()->getAvatarMoverProcess();
if (button == Mouse::BUTTON_RIGHT || button == Mouse::BUTTON_LEFT) {
amp->onMouseUp(button);
}
}
void GameMapGump::onMouseClick(int button, int32 mx, int32 my) {
MainActor *avatar = getMainActor();
switch (button) {
case Mouse::BUTTON_LEFT: {
if (avatar->isInCombat()) break;
if (Mouse::get_instance()->isMouseDownEvent(Mouse::BUTTON_RIGHT)) break;
uint16 objID = TraceObjId(mx, my);
Item *item = getItem(objID);
if (item) {
debugC(kDebugObject, "%s", item->dumpInfo().c_str());
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugC(kDebugObject, "Can't look: avatarInStasis");
} else {
item->callUsecodeEvent_look();
}
}
break;
}
case Mouse::BUTTON_MIDDLE: {
ParentToGump(mx, my);
Point3 coords;
uint16 objID = TraceCoordinates(mx, my, coords);
Item *item = getItem(objID);
if (item) {
debugC(kDebugObject, "%s", item->dumpInfo().c_str());
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugC(kDebugObject, "Can't move: avatarInStasis");
} else {
Actor *avatarControlled = getControlledActor();
PathfinderProcess *pfp = new PathfinderProcess(avatarControlled, coords);
Kernel::get_instance()->killProcesses(avatarControlled->getObjId(), PathfinderProcess::PATHFINDER_PROC_TYPE, true);
Kernel::get_instance()->addProcess(pfp);
}
}
}
default:
break;
}
}
void GameMapGump::onMouseDouble(int button, int32 mx, int32 my) {
MainActor *avatar = getMainActor();
switch (button) {
case Mouse::BUTTON_LEFT: {
if (avatar->isInCombat()) break;
if (Mouse::get_instance()->isMouseDownEvent(Mouse::BUTTON_RIGHT)) break;
uint16 objID = TraceObjId(mx, my);
Item *item = getItem(objID);
if (item) {
debugC(kDebugObject, "%s", item->dumpInfo().c_str());
int range = 128; // CONSTANT!
if (GAME_IS_CRUSADER) {
range = 512;
}
if (Ultima8Engine::get_instance()->isAvatarInStasis()) {
debugC(kDebugObject, "Can't use: avatarInStasis");
break;
}
if (dynamic_cast<Actor *>(item) ||
avatar->canReach(item, range)) {
// call the 'use' event
item->use();
} else {
Mouse::get_instance()->flashCrossCursor();
}
}
break;
}
default:
break;
}
}
void GameMapGump::IncSortOrder(int count) {
_displayList->IncSortLimit(count);
}
bool GameMapGump::StartDraggingItem(Item *item, int mx, int my) {
// ParentToGump(mx, my);
bool hackMover = Ultima8Engine::get_instance()->isHackMoverEnabled();
if (!hackMover) {
if (!item->canDrag())
return false;
MainActor *avatar = getMainActor();
if (!avatar->canReach(item, 128))
return false; // CONSTANT!
}
// get item offset
int32 itemx = 0;
int32 itemy = 0;
GetLocationOfItem(item->getObjId(), itemx, itemy);
Mouse::get_instance()->setDraggingOffset(mx - itemx, my - itemy);
return true;
}
bool GameMapGump::DraggingItem(Item *item, int mx, int my) {
// determine target location and set dragging_x/y/z
int32 dox, doy;
Mouse::get_instance()->getDraggingOffset(dox, doy);
_draggingShape = item->getShape();
_draggingFrame = item->getFrame();
_draggingFlags = item->getFlags();
_displayDragging = true;
// determine if item can be dropped here
ObjId trace = TraceCoordinates(mx, my, _draggingPos, dox, doy, item);
if (!trace)
return false;
MainActor *avatar = getMainActor();
if (trace == kMainActorId) { // dropping on self
ObjId bp = avatar->getEquip(ShapeInfo::SE_BACKPACK);
Container *backpack = getContainer(bp);
return backpack->CanAddItem(item, true);
}
bool hackMover = Ultima8Engine::get_instance()->isHackMoverEnabled();
if (hackMover) {
Mouse::get_instance()->setMouseCursor(Mouse::MOUSE_TARGET);
return true;
}
bool throwing = false;
if (!avatar->canReach(item, 128, // CONSTANT!
_draggingPos.x, _draggingPos.y, _draggingPos.z)) {
// can't reach, so see if we can throw
int throwrange = item->getThrowRange();
if (throwrange && avatar->canReach(item, throwrange, _draggingPos.x,
_draggingPos.y, _draggingPos.z)) {
int speed = 64 - item->getTotalWeight() + avatar->getStr();
if (speed < 1) speed = 1;
Point3 pt = avatar->getLocation();
MissileTracker t(item, 1, pt.x, pt.y, pt.z,
_draggingPos.x, _draggingPos.y, _draggingPos.z,
speed, 4);
if (t.isPathClear())
throwing = true;
else
return false;
} else {
return false;
}
}
if (!item->canExistAt(_draggingPos))
return false;
if (throwing)
Mouse::get_instance()->setMouseCursor(Mouse::MOUSE_TARGET);
return true;
}
void GameMapGump::DraggingItemLeftGump(Item *item) {
_displayDragging = false;
}
void GameMapGump::StopDraggingItem(Item *item, bool moved) {
_displayDragging = false;
if (!moved) return; // nothing to do
// make items on top of item fall and call release on supporting items
item->grab();
}
void GameMapGump::DropItem(Item *item, int mx, int my) {
int32 dox, doy;
Mouse::get_instance()->getDraggingOffset(dox, doy);
_displayDragging = false;
Actor *avatar = getMainActor();
ObjId trace = TraceCoordinates(mx, my, _draggingPos, dox, doy, item);
Item *targetitem = getItem(trace);
bool canReach = avatar->canReach(item, 128, // CONSTANT!
_draggingPos.x, _draggingPos.y, _draggingPos.z);
bool hackMover = Ultima8Engine::get_instance()->isHackMoverEnabled();
if (hackMover)
canReach = true;
if (item->getShapeInfo()->hasQuantity()) {
if (item->getQuality() > 1) {
// more than one, so see if we should ask if we should split it up
Item *splittarget = nullptr;
// also try to combine
if (canReach && targetitem && item->canMergeWith(targetitem)) {
splittarget = targetitem;
}
if (!splittarget) {
// create new item
splittarget = ItemFactory::createItem(
item->getShape(), item->getFrame(), 0,
item->getFlags() & (Item::FLG_DISPOSABLE | Item::FLG_OWNED | Item::FLG_INVISIBLE | Item::FLG_FLIPPED | Item::FLG_FAST_ONLY | Item::FLG_LOW_FRICTION), item->getNpcNum(), item->getMapNum(),
item->getExtFlags() & (Item::EXT_SPRITE | Item::EXT_HIGHLIGHT | Item::EXT_TRANSPARENT), true);
if (!splittarget) {
warning("ContainerGump failed to create item (%u,%u) while splitting",
item->getShape(), item->getFrame());
return;
}
}
SliderGump *slidergump = new SliderGump(100, 100,
0, item->getQuality(),
item->getQuality());
slidergump->InitGump(0);
slidergump->CreateNotifier(); // manually create notifier
Process *notifier = slidergump->GetNotifyProcess();
SplitItemProcess *splitproc = new SplitItemProcess(item, splittarget);
Kernel::get_instance()->addProcess(splitproc);
splitproc->waitFor(notifier);
item = splittarget;
} else {
// try to combine items
if (canReach && targetitem && item->canMergeWith(targetitem)) {
uint16 newquant = targetitem->getQuality() + item->getQuality();
if (newquant > Item::MAX_QUANTITY) {
item->setQuality(newquant - Item::MAX_QUANTITY);
targetitem->setQuality(Item::MAX_QUANTITY);
// maybe this isn't needed? original doesn't do it here..
targetitem->callUsecodeEvent_combine();
} else {
targetitem->setQuality(newquant);
targetitem->callUsecodeEvent_combine();
// combined, so delete other
item->destroy();
}
return;
}
}
}
if (trace == kMainActorId) { // dropping on self
ObjId bp = avatar->getEquip(ShapeInfo::SE_BACKPACK);
Container *backpack = getContainer(bp);
if (backpack && item->moveToContainer(backpack)) {
debugC(kDebugObject, "Dropped item in backpack");
item->randomGumpLocation();
return;
}
}
if (!canReach) {
// can't reach, so throw
debugC(kDebugObject, "Throwing item to (%d, %d, %d)",
_draggingPos.x, _draggingPos.y, _draggingPos.z);
int speed = 64 - item->getTotalWeight() + avatar->getStr();
if (speed < 1)
speed = 1;
Point3 pt = avatar->getLocation();
// CHECKME: correct position to throw from?
// CHECKME: correct events triggered when doing this move?
item->move(pt.x, pt.y, pt.z + 24);
int32 tx, ty;
tx = _draggingPos.x;
ty = _draggingPos.y;
int inaccuracy = 4 * (30 - avatar->getDex());
if (inaccuracy < 20)
inaccuracy = 20; // just in case dex > 25
Common::RandomSource &rs = Ultima8Engine::get_instance()->getRandomSource();
tx += rs.getRandomNumberRngSigned(-inaccuracy, inaccuracy);
ty += rs.getRandomNumberRngSigned(-inaccuracy, inaccuracy);
MissileTracker t(item, 1, tx, ty, _draggingPos.z,
speed, 4);
t.launchItem();
Direction dir = Direction_GetWorldDir(_draggingPos.y - pt.y,
_draggingPos.x - pt.x,
dirmode_8dirs);
avatar->doAnim(Animation::stand, dir);
} else {
debugC(kDebugObject, "Dropping item at (%d, %d, %d)",
_draggingPos.x, _draggingPos.y, _draggingPos.z);
// CHECKME: collideMove and grab (in StopDraggingItem)
// both call release on supporting items.
item->collideMove(_draggingPos.x, _draggingPos.y, _draggingPos.z,
true, true); // teleport item
item->fall();
}
}
void GameMapGump::RenderSurfaceChanged() {
// Resize the desktop gump to match the parent
Common::Rect32 new_dims = _parent->getDims();
_dims.setWidth(new_dims.width());
_dims.setHeight(new_dims.height());
// Offset the gump. We want 0,0 to be the centre
_dims.moveTo(-_dims.width() / 2, -_dims.height() / 2);
Gump::RenderSurfaceChanged();
}
void GameMapGump::saveData(Common::WriteStream *ws) {
warning("Trying to save GameMapGump");
}
bool GameMapGump::loadData(Common::ReadStream *rs, uint32 version) {
warning("Trying to load GameMapGump");
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,118 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_GAMEMAPGUMP_H
#define ULTIMA8_GUMPS_GAMEMAPGUMP_H
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
#include "ultima/ultima8/misc/point3.h"
namespace Ultima {
namespace Ultima8 {
class ItemSorter;
class CameraProcess;
/**
* The gump which holds all the game map elements (floor, avatar, objects, etc)
*/
class GameMapGump : public Gump {
protected:
ItemSorter *_displayList;
public:
ENABLE_RUNTIME_CLASSTYPE()
GameMapGump();
GameMapGump(int x, int y, int w, int h);
~GameMapGump() override;
void PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) override;
Point3 GetCameraLocation(int lerp_factor = 256);
// Trace a click, and return ObjId (_parent coord space)
uint16 TraceObjId(int32 mx, int32 my) override;
// Trace a click, return ObjId, and the coordinates of the mouse click (gump coord space)
virtual uint16 TraceCoordinates(int mx, int my, Point3 &coords,
int offsetx = 0, int offsety = 0,
Item *item = 0);
// Get the location of an item in the gump (coords relative to this).
// Returns false on failure
bool GetLocationOfItem(uint16 itemid, int32 &gx, int32 &gy,
int32 lerp_factor = 256) override;
bool StartDraggingItem(Item *item, int mx, int my) override;
bool DraggingItem(Item *item, int mx, int my) override;
void DraggingItemLeftGump(Item *item) override;
void StopDraggingItem(Item *item, bool moved) override;
void DropItem(Item *item, int mx, int my) override;
Gump *onMouseDown(int button, int32 mx, int32 my) override;
void onMouseUp(int button, int32 mx, int32 my) override;
void onMouseClick(int button, int32 mx, int32 my) override;
void onMouseDouble(int button, int32 mx, int32 my) override;
void IncSortOrder(int count);
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
static void Set_highlightItems(bool highlight) {
_highlightItems = highlight;
}
static bool is_highlightItems() {
return _highlightItems;
}
static bool getShowFootpads() {
return _showFootpads;
}
static void setShowFootpads(bool value) {
_showFootpads = value;
}
static int getGridlines() {
return _gridlines;
}
static void setGridlines(int gridlines) {
_gridlines = gridlines;
}
void RenderSurfaceChanged() override;
protected:
bool _displayDragging;
uint32 _draggingShape;
uint32 _draggingFrame;
uint32 _draggingFlags;
Point3 _draggingPos;
static bool _highlightItems;
static bool _showFootpads;
static int _gridlines;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,866 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/shape_archive.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/kernel/object_manager.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(Gump)
Gump::Gump() : Object(), _parent(nullptr), _owner(0),
_x(0), _y(0), _flags(0), _layer(0), _index(-1),
_shape(nullptr), _frameNum(0), _focusChild(nullptr),
_notifier(0), _processResult(0) {
}
Gump::Gump(int inX, int inY, int width, int height, uint16 inOwner,
uint32 inFlags, int32 inLayer) :
Object(), _owner(inOwner), _parent(nullptr), _x(inX), _y(inY),
_dims(0, 0, width, height), _flags(inFlags), _layer(inLayer), _index(-1),
_shape(nullptr), _frameNum(0), _children(), _focusChild(nullptr),
_notifier(0), _processResult(0) {
assignObjId(); // gumps always get an objid
}
Gump::~Gump() {
// Get rid of focus
if (_focusChild) _focusChild->OnFocus(false);
_focusChild = nullptr;
// Delete all children
for (auto *g : _children) {
delete g;
}
}
void Gump::InitGump(Gump *newparent, bool take_focus) {
if (newparent)
newparent->AddChild(this, take_focus);
else
Ultima8Engine::get_instance()->addGump(this);
if (_owner && !_notifier) CreateNotifier();
}
void Gump::SetShape(FrameID frame, bool adjustsize) {
_shape = GameData::get_instance()->getShape(frame);
_frameNum = frame._frameNum;
if (adjustsize && _shape) {
UpdateDimsFromShape();
}
}
void Gump::UpdateDimsFromShape() {
const ShapeFrame *sf = _shape->getFrame(_frameNum);
assert(sf);
_dims.left = -sf->_xoff;
_dims.top = -sf->_yoff;
_dims.setWidth(sf->_width);
_dims.setHeight(sf->_height);
}
void Gump::CreateNotifier() {
assert(_notifier == 0);
// Create us a GumpNotifyProcess
GumpNotifyProcess *p = new GumpNotifyProcess(_owner);
p->setGump(this);
_notifier = Kernel::get_instance()->addProcess(p);
}
void Gump::SetNotifyProcess(GumpNotifyProcess *proc) {
assert(_notifier == 0);
_notifier = proc->getPid();
}
GumpNotifyProcess *Gump::GetNotifyProcess() {
return dynamic_cast<GumpNotifyProcess *>(Kernel::get_instance()->
getProcess(_notifier));
}
void Gump::Close(bool no_del) {
GumpNotifyProcess *p = GetNotifyProcess();
if (p) {
p->notifyClosing(_processResult);
}
_notifier = 0;
_flags |= FLAG_CLOSING;
if (!_parent) {
if (!no_del)
delete this;
} else {
_parent->ChildNotify(this, Gump::GUMP_CLOSING);
if (!no_del)
_flags |= FLAG_CLOSE_AND_DEL;
}
}
void Gump::RenderSurfaceChanged() {
// Iterate all children
Std::list<Gump *>::iterator it = _children.reverse_begin();
Std::list<Gump *>::iterator end = _children.end();
while (it != end) {
(*it)->RenderSurfaceChanged();
--it;
}
}
void Gump::run() {
// Iterate all children
Std::list<Gump *>::iterator it = _children.begin();
Std::list<Gump *>::iterator end = _children.end();
while (it != end) {
Gump *g = *it;
// Run the child if it's not closing
if (!(g->_flags & FLAG_CLOSING))
g->run();
// If closing, we can kill it
if (g->_flags & FLAG_CLOSING) {
it = _children.erase(it);
FindNewFocusChild();
if (g->_flags & FLAG_CLOSE_AND_DEL) delete g;
} else {
++it;
}
}
}
void Gump::CloseItemDependents() {
// Close it, and return
if (_flags & FLAG_ITEM_DEPENDENT) {
Close();
return;
}
// Pass the MapChanged message to all the children
Std::list<Gump *>::iterator it = _children.begin();
Std::list<Gump *>::iterator end = _children.end();
while (it != end) {
Gump *g = *it;
// Pass to child if it's not closing
if (!(g->_flags & FLAG_CLOSING)) g->CloseItemDependents();
// If closing, we can kill it
if (g->_flags & FLAG_CLOSING) {
it = _children.erase(it);
FindNewFocusChild();
if (g->_flags & FLAG_CLOSE_AND_DEL) delete g;
} else {
++it;
}
}
}
bool Gump::GetMouseCursor(int32 mx, int32 my, Shape &shape, int32 &frame) {
ParentToGump(mx, my);
bool ret = false;
// This reverse iterates the children
Std::list<Gump *>::iterator it;
for (it = _children.reverse_begin(); it != _children.end(); --it)
{
Gump *g = *it;
// Not if closing or hidden
if (g->_flags & FLAG_CLOSING || g->IsHidden())
continue;
// It's got the point
if (g->PointOnGump(mx, my))
ret = g->GetMouseCursor(mx, my, shape, frame);
if (ret) break;
}
return ret;
}
void Gump::Paint(RenderSurface *surf, int32 lerp_factor, bool scaled) {
// Don't paint if hidden
if (IsHidden()) return;
// Get old Origin
int32 ox = 0, oy = 0;
surf->GetOrigin(ox, oy);
// Set the new Origin
int32 nx = 0, ny = 0;
GumpToParent(nx, ny);
surf->SetOrigin(ox + nx, oy + ny);
// Get Old Clipping Rect
Common::Rect32 old_rect = surf->getClippingRect();
// Set new clipping rect
Common::Rect32 new_rect(_dims);
new_rect.clip(old_rect);
surf->setClippingRect(new_rect);
// Paint This
PaintThis(surf, lerp_factor, scaled);
// Paint children
PaintChildren(surf, lerp_factor, scaled);
// Reset The Clipping Rect
surf->setClippingRect(old_rect);
// Reset The Origin
surf->SetOrigin(ox, oy);
}
void Gump::PaintThis(RenderSurface *surf, int32 /*lerp_factor*/, bool /*scaled*/) {
if (_shape)
surf->Paint(_shape, _frameNum, 0, 0);
}
void Gump::PaintChildren(RenderSurface *surf, int32 lerp_factor, bool scaled) {
for (auto *g : _children) {
// Paint if not closing
if (!(g->_flags & FLAG_CLOSING))
g->Paint(surf, lerp_factor, scaled);
}
}
void Gump::PaintCompositing(RenderSurface *surf, int32 lerp_factor,
int32 sx, int32 sy) {
// Don't paint if hidden
if (IsHidden()) return;
// Get old Origin
int32 ox = 0, oy = 0;
surf->GetOrigin(ox, oy);
// FIXME - Big accuracy problems here with the origin and clipping rect
// Set the new Origin
surf->SetOrigin(0, 0);
// Get Old Clipping Rect
Common::Rect32 old_rect = surf->getClippingRect();
// Set new clipping rect
Common::Rect32 new_rect(_dims);
GumpRectToScreenSpace(new_rect, ROUND_OUTSIDE);
new_rect.clip(old_rect);
surf->setClippingRect(new_rect);
// Iterate all children
Std::list<Gump *>::iterator it = _children.reverse_begin();
Std::list<Gump *>::iterator end = _children.end();
while (it != end) {
Gump *g = *it;
// Paint if not closing
if (!g->IsClosing())
g->PaintCompositing(surf, lerp_factor, sx, sy);
--it;
}
// Paint This
PaintComposited(surf, lerp_factor, sx, sy);
// Reset The Clipping Rect
surf->setClippingRect(old_rect);
// Reset The Origin
surf->SetOrigin(ox, oy);
}
void Gump::PaintComposited(RenderSurface * /*surf*/, int32 /*lerp_factor*/, int32 /*scalex*/, int32 /*scaley*/) {
}
Gump *Gump::FindGump(int mx, int my) {
int32 gx = mx, gy = my;
ParentToGump(gx, gy);
Gump *gump = nullptr;
// Iterate all children
Std::list<Gump *>::iterator it = _children.reverse_begin();
Std::list<Gump *>::iterator end = _children.end();
while (it != end && !gump) {
Gump *g = *it;
gump = g->FindGump(gx, gy);
--it;
}
// it's over a child
if (gump)
return gump;
// it's over this gump
if (PointOnGump(mx, my))
return this;
return nullptr;
}
void Gump::setRelativePosition(Gump::Position pos, int xoffset, int yoffset) {
if (_parent) {
Common::Rect32 rect = _parent->getDims();
switch (pos) {
case CENTER:
Move(rect.width() / 2 - _dims.width() / 2 + xoffset,
rect.height() / 2 - _dims.height() / 2 + yoffset);
break;
case TOP_LEFT:
Move(xoffset, yoffset);
break;
case TOP_RIGHT:
Move(rect.width() - _dims.width() + xoffset, yoffset);
break;
case BOTTOM_LEFT:
Move(xoffset, rect.height() - _dims.height() + yoffset);
break;
case BOTTOM_RIGHT:
Move(rect.width() - _dims.width() + xoffset, rect.height() - _dims.height() + yoffset);
break;
case TOP_CENTER:
Move(rect.width() / 2 - _dims.width() / 2 + xoffset, yoffset);
break;
case BOTTOM_CENTER:
Move(rect.width() / 2 - _dims.width() / 2 + xoffset, rect.height() - _dims.height() + yoffset);
break;
case LEFT_CENTER:
Move(xoffset, rect.height() / 2 - _dims.height() / 2 + yoffset);
break;
case RIGHT_CENTER:
Move(rect.width() - _dims.width() + xoffset, rect.height() / 2 - _dims.height() / 2 + yoffset);
break;
default:
break;
}
}
}
bool Gump::PointOnGump(int mx, int my) {
int32 gx = mx, gy = my;
ParentToGump(gx, gy);
// First check again rectangle
if (!_dims.contains(gx, gy)) {
return false;
}
if (!_shape) {
// no shape? Then if it's in the rectangle it's on the gump.
return true;
}
const ShapeFrame *sf = _shape->getFrame(_frameNum);
assert(sf);
if (sf->hasPoint(gx, gy)) {
return true;
}
// reverse-iterate children
Std::list<Gump *>::iterator it;
for (it = _children.reverse_begin(); it != _children.end(); --it) {
Gump *g = *it;
// It's got the point
if (g->PointOnGump(gx, gy)) return true;
}
return false;
}
// Convert a screen space point to a gump point
void Gump::ScreenSpaceToGump(int32 &sx, int32 &sy, PointRoundDir r) {
// This is a recursive operation. We get each
// parent to convert the point into their local
// coords.
if (_parent) _parent->ScreenSpaceToGump(sx, sy, r);
ParentToGump(sx, sy, r);
}
// Convert a gump point to a screen space point
void Gump::GumpToScreenSpace(int32 &gx, int32 &gy, PointRoundDir r) {
// This is a recursive operation. We get each
// gump to convert the point to their parent
GumpToParent(gx, gy, r);
if (_parent) _parent->GumpToScreenSpace(gx, gy, r);
}
// Convert a parent relative point to a gump point
void Gump::ParentToGump(int32 &px, int32 &py, PointRoundDir) {
px -= _x;
px += _dims.left;
py -= _y;
py += _dims.top;
}
// Convert a gump point to parent relative point
void Gump::GumpToParent(int32 &gx, int32 &gy, PointRoundDir) {
gx -= _dims.left;
gx += _x;
gy -= _dims.top;
gy += _y;
}
// Transform a rectangle to screenspace from gumpspace
void Gump::GumpRectToScreenSpace(Common::Rect32 &gr, RectRoundDir r) {
PointRoundDir tl = (r == ROUND_INSIDE ? ROUND_BOTTOMRIGHT : ROUND_TOPLEFT);
PointRoundDir br = (r == ROUND_OUTSIDE ? ROUND_BOTTOMRIGHT : ROUND_TOPLEFT);
int32 x1 = gr.left, y1 = gr.top;
int32 x2 = gr.right, y2 = gr.bottom;
GumpToScreenSpace(x1, y1, tl);
GumpToScreenSpace(x2, y2, br);
gr.moveTo(x1, y1);
if (gr.width() != 0)
gr.setWidth(x2 - x1);
if (gr.height() != 0)
gr.setHeight(y2 - y1);
}
// Transform a rectangle to gumpspace from screenspace
void Gump::ScreenSpaceToGumpRect(Common::Rect32 &sr, RectRoundDir r) {
PointRoundDir tl = (r == ROUND_INSIDE ? ROUND_BOTTOMRIGHT : ROUND_TOPLEFT);
PointRoundDir br = (r == ROUND_OUTSIDE ? ROUND_BOTTOMRIGHT : ROUND_TOPLEFT);
int32 x1 = sr.left, y1 = sr.top;
int32 x2 = sr.right, y2 = sr.bottom;
ScreenSpaceToGump(x1, y1, tl);
ScreenSpaceToGump(x2, y2, br);
sr.moveTo(x1, y1);
if (sr.width() != 0)
sr.setWidth(x2 - x1);
if (sr.height() != 0)
sr.setHeight(y2 - y1);
}
uint16 Gump::TraceObjId(int32 mx, int32 my) {
// Convert to local coords
int32 gx = mx, gy = my;
ParentToGump(gx, gy);
uint16 objId_ = 0;
// reverse-iterate children
Std::list<Gump *>::iterator it;
for (it = _children.reverse_begin(); it != _children.end(); --it) {
Gump *g = *it;
// Not if closing or hidden
if (g->_flags & FLAG_CLOSING || g->IsHidden())
continue;
// It's got the point
if (g->PointOnGump(gx, gy)) objId_ = g->TraceObjId(gx, gy);
if (objId_ && objId_ != 65535) break;
}
// if (!objId_ || objId_ == 65535)
// if (PointOnGump(mx,my))
// objId_ = getObjId();
return objId_;
}
bool Gump::GetLocationOfItem(uint16 itemid, int32 &gx, int32 &gy,
int32 lerp_factor) {
gx = 0;
gy = 0;
return false;
}
// Find a child gump that matches the matching function
Gump *Gump::FindGump(const FindGumpPredicate predicate, bool recursive) {
if (predicate(this))
return this;
for (auto *g : _children) {
// Not if closing
if (g->_flags & FLAG_CLOSING)
continue;
if (predicate(g))
return g;
}
if (!recursive)
return nullptr;
// Recursive Iterate all children
for (auto *g : _children) {
// Not if closing
if (g->_flags & FLAG_CLOSING)
continue;
Gump *match = g->FindGump(predicate, recursive);
if (match)
return match;
}
return nullptr;
}
// Makes this gump the focus
void Gump::MakeFocus() {
// By default we WON'T do anything
if (_parent) {
if (_parent->_focusChild) _parent->_focusChild->OnFocus(false);
_parent->_focusChild = this;
}
OnFocus(true);
}
void Gump::FindNewFocusChild() {
if (_focusChild)
_focusChild->OnFocus(false);
_focusChild = nullptr;
// Now add the gump to use as the new focus
Std::list<Gump *>::iterator it = _children.reverse_begin();
if (it != _children.end()) {
(*it)->MakeFocus();
}
}
// Adds a child to the list
void Gump::AddChild(Gump *gump, bool take_focus) {
if (!gump) return;
// Remove it if required
Gump *old_parent = gump->GetParent();
if (old_parent) old_parent->RemoveChild(gump);
// Now add the gump in the correct spot
Std::list<Gump *>::iterator it = _children.begin();
Std::list<Gump *>::iterator end = _children.end();
for (; it != end; ++it) {
Gump *other = *it;
// Why don't we check for FLAG_CLOSING here?
// Because we want to make sure that the sort order is always valid
// If we are same layer as focus and we won't take it, we will not be
// placed in front of it
if (!take_focus && other == _focusChild && other->_layer == gump->_layer)
break;
// Lower layers get placed before higher layers
if (other->_layer > gump->_layer) break;
}
// Now add it
_children.insert(it, gump);
gump->_parent = this;
// Make the gump the focus if needed
if (take_focus || !_focusChild) {
if (_focusChild) _focusChild->OnFocus(false);
gump->OnFocus(true);
_focusChild = gump;
}
}
// Remove a gump from the list
void Gump::RemoveChild(Gump *gump) {
if (!gump) return;
// Remove it
_children.remove(gump);
gump->_parent = nullptr;
// Remove focus, the give upper most gump the focus
if (gump == _focusChild) {
FindNewFocusChild();
}
}
void Gump::MoveChildToFront(Gump *gump) {
if (!gump) return;
_children.remove(gump);
Std::list<Gump *>::iterator it = _children.begin();
Std::list<Gump *>::iterator end = _children.end();
for (; it != end; ++it) {
Gump *other = *it;
// Lower layers get placed before higher layers
if (other->_layer > gump->_layer) break;
}
_children.insert(it, gump);
}
Gump *Gump::GetRootGump() {
if (!_parent) return this;
return _parent->GetRootGump();
}
bool Gump::onDragStart(int32 mx, int32 my) {
if (IsDraggable() && _parent) {
ParentToGump(mx, my);
Mouse::get_instance()->setDraggingOffset(mx, my);
_parent->MoveChildToFront(this);
return true;
}
return false;
}
void Gump::onDragStop(int32 mx, int32 my) {
}
void Gump::onDrag(int32 mx, int32 my) {
int32 dx, dy;
Mouse::get_instance()->getDraggingOffset(dx, dy);
Move(mx - dx, my - dy);
}
//
// Input handling
//
Gump *Gump::onMouseDown(int button, int32 mx, int32 my) {
// Convert to local coords
ParentToGump(mx, my);
Gump *handled = nullptr;
// Iterate children backwards
Std::list<Gump *>::iterator it;
for (it = _children.reverse_begin(); it != _children.end(); --it) {
Gump *g = *it;
// Not if closing or hidden
if (g->_flags & FLAG_CLOSING || g->IsHidden())
continue;
// It's got the point
if (g->PointOnGump(mx, my)) handled = g->onMouseDown(button, mx, my);
if (handled) break;
}
return handled;
}
Gump *Gump::onMouseMotion(int32 mx, int32 my) {
// Convert to local coords
ParentToGump(mx, my);
Gump *handled = nullptr;
// Iterate children backwards
Std::list<Gump *>::iterator it;
for (it = _children.reverse_begin(); it != _children.end(); --it) {
Gump *g = *it;
// Not if closing or hidden
if (g->_flags & FLAG_CLOSING || g->IsHidden())
continue;
// It's got the point
if (g->PointOnGump(mx, my)) handled = g->onMouseMotion(mx, my);
if (handled) break;
}
// All gumps need to handle mouse motion
if (!handled) handled = this;
return handled;
}
//
// KeyInput
//
bool Gump::OnKeyDown(int key, int mod) {
bool handled = false;
if (_focusChild) handled = _focusChild->OnKeyDown(key, mod);
return handled;
}
bool Gump::OnKeyUp(int key) {
bool handled = false;
if (_focusChild) handled = _focusChild->OnKeyUp(key);
return handled;
}
bool Gump::OnTextInput(int unicode) {
bool handled = false;
if (_focusChild) handled = _focusChild->OnTextInput(unicode);
return handled;
}
bool Gump::mustSave(bool toplevel) const {
// DONT_SAVE flag
if (_flags & FLAG_DONT_SAVE)
return false;
// don't save when ready for deletion
if (_flags & FLAG_CLOSE_AND_DEL)
return false;
if (toplevel) {
// don't save gumps with parents, unless parent is a core gump
if (_parent && !(_parent->_flags & FLAG_CORE_GUMP))
return false;
}
return true;
}
void Gump::saveData(Common::WriteStream *ws) {
Object::saveData(ws);
ws->writeUint16LE(_owner);
ws->writeUint32LE(static_cast<uint32>(_x));
ws->writeUint32LE(static_cast<uint32>(_y));
ws->writeUint32LE(static_cast<uint32>(_dims.left));
ws->writeUint32LE(static_cast<uint32>(_dims.top));
ws->writeUint32LE(static_cast<uint32>(_dims.width()));
ws->writeUint32LE(static_cast<uint32>(_dims.height()));
ws->writeUint32LE(_flags);
ws->writeUint32LE(static_cast<uint32>(_layer));
ws->writeUint32LE(static_cast<uint32>(_index));
uint16 flex = 0;
uint32 shapenum = 0;
if (_shape) {
_shape->getShapeId(flex, shapenum);
}
ws->writeUint16LE(flex);
ws->writeUint32LE(shapenum);
ws->writeUint32LE(_frameNum);
if (_focusChild)
ws->writeUint16LE(_focusChild->getObjId());
else
ws->writeUint16LE(0);
ws->writeUint16LE(_notifier);
ws->writeUint32LE(_processResult);
unsigned int childcount = 0;
for (auto *g : _children) {
if (!g->mustSave(false))
continue;
childcount++;
}
// write children:
ws->writeUint32LE(childcount);
for (auto *g : _children) {
if (!g->mustSave(false))
continue;
ObjectManager::get_instance()->saveObject(ws, g);
}
}
bool Gump::loadData(Common::ReadStream *rs, uint32 version) {
if (!Object::loadData(rs, version)) return false;
_owner = rs->readUint16LE();
_x = static_cast<int32>(rs->readUint32LE());
_y = static_cast<int32>(rs->readUint32LE());
int dx = static_cast<int32>(rs->readUint32LE());
int dy = static_cast<int32>(rs->readUint32LE());
int dw = static_cast<int32>(rs->readUint32LE());
int dh = static_cast<int32>(rs->readUint32LE());
_dims.moveTo(dx, dy);
_dims.setWidth(dw);
_dims.setHeight(dh);
_flags = rs->readUint32LE();
_layer = static_cast<int32>(rs->readUint32LE());
_index = static_cast<int32>(rs->readUint32LE());
_shape = nullptr;
ShapeArchive *flex = GameData::get_instance()->getShapeFlex(rs->readUint16LE());
uint32 shapenum = rs->readUint32LE();
if (flex) {
_shape = flex->getShape(shapenum);
if (shapenum > 0 && !_shape) {
warning("Gump shape %d is not valid. Corrupt save?", shapenum);
return false;
}
}
_frameNum = rs->readUint32LE();
uint16 focusid = rs->readUint16LE();
_focusChild = nullptr;
_notifier = rs->readUint16LE();
_processResult = rs->readUint32LE();
// read children
uint32 childcount = rs->readUint32LE();
if (childcount > 65535) {
warning("Improbable gump child count %d. Corrupt save?", childcount);
return false;
}
for (unsigned int i = 0; i < childcount; ++i) {
Object *obj = ObjectManager::get_instance()->loadObject(rs, version);
Gump *child = dynamic_cast<Gump *>(obj);
if (!child) return false;
AddChild(child, false);
if (child->getObjId() == focusid)
_focusChild = child;
}
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,490 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_GUMP_H
#define ULTIMA8_GUMPS_GUMP_H
#include "common/rect.h"
#include "ultima/ultima8/kernel/object.h"
#include "ultima/ultima8/gfx/frame_id.h"
#include "ultima/shared/std/containers.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class RenderSurface;
class Shape;
class Item;
class GumpNotifyProcess;
class Gump;
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,
* conversation text, etc. Like most windowing systems, gumps nest.
*/
class Gump : public Object {
protected:
uint16 _owner; // Owner item
Gump *_parent; // Parent gump
int32 _x, _y; // Gump's position in parent.
// Always the upper left corner!
Common::Rect32 _dims; // The dimensions/coord space of the gump
uint32 _flags; // Gump flags
int32 _layer; // gump ordering layer
int32 _index; // 'Index'
const Shape *_shape; // The gumps shape (always painted at 0,0)
uint32 _frameNum;
//! The Gump list for this gump. This will contain all child gumps,
//! as well as all gump widgets.
Std::list<Gump *> _children; // List of all gumps
Gump *_focusChild; // The child that has focus
uint16 _notifier; // Process to notify when we're closing
uint32 _processResult; // Result for the notifier process
public:
ENABLE_RUNTIME_CLASSTYPE()
Gump();
Gump(int x, int y, int width, int height, uint16 owner = 0,
uint32 flags = 0, int32 layer = LAYER_NORMAL);
~Gump() override;
public:
virtual void CreateNotifier();
void SetNotifyProcess(GumpNotifyProcess *proc);
GumpNotifyProcess *GetNotifyProcess();
inline uint32 GetResult() {
return _processResult;
}
void SetResult(uint32 res) {
_processResult = res;
}
//! Set the Gump's shape/frame
inline void SetShape(const Shape *shape, uint32 frameNum) {
_shape = shape;
_frameNum = frameNum;
}
void SetShape(FrameID frame, bool adjustsize = false);
//! Update the width/height to match the gump's current shape frame
void UpdateDimsFromShape();
//! Set the Gump's frame
inline void Set_frameNum(uint32 frameNum) {
_frameNum = frameNum;
}
//! Init the gump and add it to parent; call after construction
//! When newparent is 0, this will call Ultima8Engine::addGump().
//! \param newparent The Gump's new parent or 0.
//! \param takefocus If true, set parent's _focusChild to this
virtual void InitGump(Gump *newparent, bool take_focus = true);
//! Find a gump of that matches a predicate function (this or child)
//! \param predicate Function to check if a gump is a match
//! \param recursive Recursively search through children?
//! \return the desired Gump, or NULL if not found
virtual Gump *FindGump(FindGumpPredicate predicate, bool recursive = true);
//! Find a gump of the specified type (this or child)
//! \param T Type of gump to look for
//! \param recursive Recursively search through children?
//! \return the desired Gump, or NULL if not found
template<class T> Gump *FindGump(bool recursive = true) {
return FindGump(&IsOfType<T>, recursive);
}
//! A predicate to find a ui element by its index
template<int T> static bool FindByIndex(const Gump *g) { return g->GetIndex() == T; }
//! Find gump (this, child or NULL) at parent coordinates (mx,my)
//! \return the Gump at these coordinates, or NULL if none
virtual Gump *FindGump(int mx, int my);
//! Get the mouse cursor for position mx, my relative to parents position.
//! If this gump doesn't want to set the cursor, the gump list will
//! attempt to get the cursor shape from the next lower gump.
//! \return true if this gump wants to set the cursor, false otherwise
virtual bool GetMouseCursor(int32 mx, int32 my, Shape &shape, int32 &frame);
// Notify gumps the render surface changed.
virtual void RenderSurfaceChanged();
//! Run the gump
virtual void run();
//! Close item-dependent gumps (recursively).
//! Called when there is a map change (so the gumps can self terminate
//! among other things), or when backspace is pressed by the user.
virtual void CloseItemDependents();
//! Paint the Gump (RenderSurface is relative to parent).
//! Calls PaintThis and PaintChildren
// \param surf The RenderSurface to paint to
// \param lerp_factor The lerp_factor to paint at (0-256)
// \param scaled Set if the gump is being drawn scaled.
virtual void Paint(RenderSurface *surf, int32 lerp_factor, bool scaled);
//! Paint the unscaled compontents of the Gump with compositing (RenderSurface is relative to parent).
//! Calls PaintComposited on self and PaintCompositing on children
// \param surf The RenderSurface to paint to
// \param lerp_factor The lerp_factor to paint at (0-256)
// \param scalex Fixed point scaling factor for x coord
// \param scaley Fixed point scaling factor for y coord
virtual void PaintCompositing(RenderSurface *surf, int32 lerp_factor, int32 scalex, int32 scaley);
protected:
//! Overloadable method to Paint just this Gump (RenderSurface is relative to this)
// \param surf The RenderSurface to paint to
// \param lerp_factor The lerp_factor to paint at (0-256)
// \param scaled Set if the gump is being drawn scaled.
virtual void PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled);
//! Paint the Gumps Children (RenderSurface is relative to this)
// \param surf The RenderSurface to paint to
// \param lerp_factor The lerp_factor to paint at (0-256)
// \param scaled Set if the gump is being drawn scaled.
virtual void PaintChildren(RenderSurface *surf, int32 lerp_factor, bool scaled);
//! Overloadable method to Paint just this gumps unscaled components that require compositing (RenderSurface is relative to parent).
// \param surf The RenderSurface to paint to
// \param lerp_factor The lerp_factor to paint at (0-256)
// \param scalex Fixed point scaling factor for x coord
// \param scaley Fixed point scaling factor for y coord
virtual void PaintComposited(RenderSurface *surf, int32 lerp_factor, int32 scalex, int32 scaley);
static inline int32 ScaleCoord(int32 c, int32 factor) {
return ((c * factor) + (1 << 15)) >> 16;
}
static inline int32 UnscaleCoord(int32 c, int32 factor) {
return (c << 16) / factor;
}
public:
//! Close the gump
//! \param no_del If true, do not delete after closing
virtual void Close(bool no_del = false);
//! Check to see if a Gump is Closing
bool IsClosing() const {
return (_flags & FLAG_CLOSING) != 0;
}
//! Move this gump
virtual void Move(int32 x, int32 y) {
_x = x;
_y = y;
}
//! Move this gump relative to its current position
virtual void MoveRelative(int x, int y) {
_x += x;
_y += y;
}
void getLocation(int32 &x, int32 &y) const {
x = _x;
y = _y;
}
enum Position {
CENTER = 1,
TOP_LEFT = 2,
TOP_RIGHT = 3,
BOTTOM_LEFT = 4,
BOTTOM_RIGHT = 5,
TOP_CENTER = 6,
BOTTOM_CENTER = 7,
LEFT_CENTER = 8,
RIGHT_CENTER = 9
};
//! Moves this gump to a relative location on the parent gump
// \param pos the postition on the parent gump
// \param xoffset an offset from the position on the x-axis
// \param yoffset an offset from the position on the y-axis
virtual void setRelativePosition(Position pos, int xoffset = 0, int yoffset = 0);
//
// Points and Coords
//
//! Get the _dims
const Common::Rect32 &getDims() const {
return _dims;
}
//! Set the _dims
void setDims(const Common::Rect32 &d) {
_dims = d;
}
//! Detect if a point is on the gump
virtual bool PointOnGump(int mx, int my);
enum PointRoundDir {
ROUND_TOPLEFT = 0,
ROUND_BOTTOMRIGHT = 1
};
enum RectRoundDir {
ROUND_INSIDE,
ROUND_OUTSIDE
};
//! Convert a screen space point to a gump point
virtual void ScreenSpaceToGump(int32 &sx, int32 &sy,
PointRoundDir r = ROUND_TOPLEFT);
//! Convert a gump point to a screen space point
virtual void GumpToScreenSpace(int32 &gx, int32 &gy,
PointRoundDir r = ROUND_TOPLEFT);
//! Convert a parent relative point to a gump point
virtual void ParentToGump(int32 &px, int32 &py,
PointRoundDir r = ROUND_TOPLEFT);
//! Convert a gump point to parent relative point
virtual void GumpToParent(int32 &gx, int32 &gy,
PointRoundDir r = ROUND_TOPLEFT);
//! Transform a rectangle to screenspace from gumpspace
virtual void GumpRectToScreenSpace(Common::Rect32 &gr, RectRoundDir r = ROUND_OUTSIDE);
//! Transform a rectangle to gumpspace from screenspace
virtual void ScreenSpaceToGumpRect(Common::Rect32 &sr, RectRoundDir r = ROUND_OUTSIDE);
//! Trace a click, and return ObjId
virtual uint16 TraceObjId(int32 mx, int32 my);
//! Get the location of an item in the gump (coords relative to this).
//! \return false on failure
virtual bool GetLocationOfItem(uint16 itemid, int32 &gx, int32 &gy,
int32 lerp_factor = 256);
//
// Some event handlers. In theory they 'should' be able to be mapped to
// Usecode classes.
//
// mx and my are relative to parents position
//
// onMouseDown returns the Gump that handled the Input, if it was handled.
// The MouseUp,MouseDouble events will be sent to the same gump.
//
// onMouseMotion works like onMouseDown,
// but independently of the other methods.
//
// Unhandled input will be passed down to the next lower gump.
//
// A mouse click on a gump will make it focus, IF it wants it.
//
// It is often preferrable to handle both click and double events
// rather than only the up event to avoid unintended clicks after
// performing intended action.
// Return Gump that handled event
virtual Gump *onMouseDown(int button, int32 mx, int32 my);
virtual void onMouseUp(int button, int32 mx, int32 my) { }
virtual void onMouseClick(int button, int32 mx, int32 my) { }
virtual void onMouseDouble(int button, int32 mx, int32 my) { }
virtual Gump *onMouseMotion(int32 mx, int32 my);
// onMouseOver is only call when the mouse first passes over the gump
// onMouseLeft is call as the mouse leaves the gump.
virtual void onMouseOver() { };
virtual void onMouseLeft() { };
// Keyboard input gets sent to the FocusGump. Or if there isn't one, it
// will instead get sent to the default key handler. TextInput requires
// that text mode be enabled. Return true if handled, false if not.
// Default, returns false, unless handled by focus child
virtual bool OnKeyDown(int key, int mod);
virtual bool OnKeyUp(int key);
virtual bool OnTextInput(int unicode);
// This is for detecting focus changes for keyboard input. Gets called true
// when the this gump is being set as the focus focus gump. It is called
// false when focus is being taken away.
virtual void OnFocus(bool /*gain*/) { }
// Makes this gump the focus
virtual void MakeFocus();
// Is this gump the focus?
inline bool IsFocus() {
return _parent ? _parent->_focusChild == this : false;
}
// Get the child in focus
inline Gump *GetFocusChild() {
return _focusChild;
}
// Find a new Child to be the focus
void FindNewFocusChild();
//
// Child gump related
//
//! Add a gump to the child list.
virtual void AddChild(Gump *, bool take_focus = true);
//! Remove a gump from the child list
virtual void RemoveChild(Gump *);
//! Move child to front (within its layer)
virtual void MoveChildToFront(Gump *);
//! Get the parent
inline Gump *GetParent() {
return _parent;
}
//! Get the root gump (or self)
Gump *GetRootGump();
//! This function is used by our children to notifty us of 'something'
//! Think of it as a generic call back function
virtual void ChildNotify(Gump *child, uint32 message) { }
void SetIndex(int32 i) {
_index = i;
}
int32 GetIndex() const {
return _index;
}
//! Called when a gump starts to be dragged.
//! \return false if the gump isn't allowed to be dragged.
virtual bool onDragStart(int32 mx, int32 my);
virtual void onDragStop(int32 mx, int32 my);
virtual void onDrag(int32 mx, int32 my);
//! This will be called when an item in this gump starts to be dragged.
//! \return false if the item isn't allowed to be dragged.
virtual bool StartDraggingItem(Item *item, int mx, int my) {
return false;
}
//! Called when an item is being dragged over the gump.
//! Note: this may be called on a different gump than StartDraggingItem.
//! \return false if the item can't be dragged to this location.
virtual bool DraggingItem(Item *item, int mx, int my) {
return false;
}
//! Called when an item that was being dragged over the gump left the gump
virtual void DraggingItemLeftGump(Item *item) { }
//! Called when a drag operation finished.
//! This is called on the same gump that received StartDraggingItem
//! \param moved If true, the item was actually dragged somewhere else.
//! If false, the drag was cancelled.
virtual void StopDraggingItem(Item *item, bool moved) { }
//! Called when an item has been dropped on a gump.
//! This is called after StopDraggingItem has been called, but possibly
//! on a different gump.
//! It's guaranteed that a gump will only receive a DropItem at a location
//! if a DraggingItem there returned true.
virtual void DropItem(Item *item, int mx, int my) { }
public:
//
// Gump Flags
//
enum GumpFlags {
FLAG_DRAGGABLE = 0x0001, // When set, the gump can be dragged
FLAG_HIDDEN = 0x0002, // When set, the gump will not be drawn
FLAG_CLOSING = 0x0004, // When set, the gump is closing
FLAG_CLOSE_AND_DEL = 0x0008, // When set, the gump is closing and will be deleted
FLAG_ITEM_DEPENDENT = 0x0010, // When set, the gump will be deleted on MapChange
FLAG_DONT_SAVE = 0x0020, // When set, don't save this gump. Be very careful with this one!
FLAG_CORE_GUMP = 0x0040, // core gump (only children are saved)
FLAG_KEEP_VISIBLE = 0x0080, // Keep this gump on-screen. (only for ItemRelativeGumps)
FLAG_PREVENT_SAVE = 0x0100 // When set, prevent game from saving
};
//! Does this gump have any of the given flags mask set
inline bool hasFlags(uint flags) const {
return (_flags & flags) != 0;
}
inline bool IsHidden() const {
return (_flags & FLAG_HIDDEN) || (_parent && _parent->IsHidden());
}
bool IsDraggable() const {
return _flags & FLAG_DRAGGABLE;
}
virtual void HideGump() {
_flags |= FLAG_HIDDEN;
}
virtual void UnhideGump() {
_flags &= ~FLAG_HIDDEN;
}
void SetVisibility(bool visible) {
if (visible)
UnhideGump();
else
HideGump();
}
bool mustSave(bool toplevel) const;
//
// Gump Layers
//
enum GumpLayers {
LAYER_DESKTOP = -16, // Layer for Desktop 'bottom most'
LAYER_GAMEMAP = -8, // Layer for the World Gump
LAYER_NORMAL = 0, // Layer for Normal gumps
LAYER_ABOVE_NORMAL = 8, // Layer for Always on top Gumps
LAYER_MODAL = 12, // Layer for Modal Gumps
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;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,88 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(GumpNotifyProcess)
GumpNotifyProcess::GumpNotifyProcess()
: Process(), _gump(0) {
}
GumpNotifyProcess::GumpNotifyProcess(uint16 it) : Process(it), _gump(0) {
_result = 0;
_type = 0x200; // CONSTANT!
}
GumpNotifyProcess::~GumpNotifyProcess(void) {
}
void GumpNotifyProcess::setGump(Gump *g) {
_gump = g->getObjId();
}
void GumpNotifyProcess::notifyClosing(int res) {
_gump = 0;
_result = res;
if (!(_flags & PROC_TERMINATED)) terminate();
}
void GumpNotifyProcess::terminate() {
Process::terminate();
if (_gump) {
Gump *g = Ultima8::getGump(_gump);
assert(g);
g->Close();
}
}
void GumpNotifyProcess::run() {
}
Common::String GumpNotifyProcess::dumpInfo() const {
return Process::dumpInfo() +
Common::String::format(", gump: %u", _gump);
}
void GumpNotifyProcess::saveData(Common::WriteStream *ws) {
Process::saveData(ws);
ws->writeUint16LE(_gump);
}
bool GumpNotifyProcess::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
_gump = rs->readUint16LE();
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,63 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_GUMPNOTIFYPROCESS_H
#define ULTIMA8_GUMPS_GUMPNOTIFYPROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class Gump;
class GumpNotifyProcess : public Process {
uint16 _gump;
public:
ENABLE_RUNTIME_CLASSTYPE()
GumpNotifyProcess();
GumpNotifyProcess(uint16 it);
~GumpNotifyProcess() override;
void setGump(Gump *g);
uint16 getGump() const {
return _gump;
}
virtual void notifyClosing(int res);
void terminate() override;
void run() override;
Common::String dumpInfo() const override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,138 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/inverter_gump.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(InverterGump)
InverterGump::InverterGump(int32 x, int32 y, int32 width, int32 height)
: DesktopGump(x, y, width, height) {
_buffer = nullptr;
}
InverterGump::~InverterGump() {
delete _buffer;
}
static inline int getLine(int index, int n) {
index = index % (2 * n);
if (index >= n)
return 2 * n - 1 - 2 * (index - n);
else
return 2 * index;
}
static inline int getIndex(int line, int n) {
if (line % 2 == 0)
return line / 2;
else
return 2 * n - 1 - (line / 2);
}
void InverterGump::Paint(RenderSurface *surf, int32 lerp_factor, bool scaled) {
// Skip the clipping rect/origin setting, since they will already be set
// correctly by our parent.
// (Or maybe I'm just to lazy to figure out the correct coordinates
// to use to compensate for the flipping... -wjp :-) )
// Don't paint if hidden
if (IsHidden()) return;
// Paint This
PaintThis(surf, lerp_factor, scaled);
// Paint children
PaintChildren(surf, lerp_factor, scaled);
}
void InverterGump::PaintChildren(RenderSurface *surf, int32 lerp_factor, bool scaled) {
unsigned int state = Ultima8Engine::get_instance()->getInversion();
if (state == 0) {
DesktopGump::PaintChildren(surf, lerp_factor, scaled);
return;
} else if (state == 0x8000) {
bool old_flipped = surf->IsFlipped();
surf->SetFlipped(!old_flipped);
DesktopGump::PaintChildren(surf, lerp_factor, scaled);
surf->SetFlipped(old_flipped);
return;
}
int width = _dims.width(), height = _dims.height();
// need a backbuffer
if (!_buffer) {
Graphics::Screen *screen = Ultima8Engine::get_instance()->getScreen();
_buffer = new RenderSurface(width, height, screen->format);
}
_buffer->BeginPainting();
DesktopGump::PaintChildren(_buffer, lerp_factor, scaled);
_buffer->EndPainting();
// now invert-blit _buffer to screen
int t = (state * height) / 0x10000;
for (int i = 0; i < height; ++i) {
int src = getLine(getIndex(i, height / 2) + t, height / 2);
Common::Rect srcRect(0, src, width, src + 1);
surf->Blit(*_buffer->getRawSurface(), srcRect, 0, i);
}
}
// Convert a parent relative point to a gump point
void InverterGump::ParentToGump(int32 &px, int32 &py, PointRoundDir) {
px -= _x;
px += _dims.left;
py -= _y;
if (Ultima8Engine::get_instance()->isInverted()) py = _dims.height() - py - 1;
py += _dims.top;
}
// Convert a gump point to parent relative point
void InverterGump::GumpToParent(int32 &gx, int32 &gy, PointRoundDir) {
gx -= _dims.left;
gx += _x;
gy -= _dims.top;
if (Ultima8Engine::get_instance()->isInverted()) gy = _dims.height() - gy - 1;
gy += _y;
}
void InverterGump::RenderSurfaceChanged() {
DesktopGump::RenderSurfaceChanged();
delete _buffer;
_buffer = nullptr;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,60 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_INVERTERGUMP_H
#define ULTIMA8_GUMPS_INVERTERGUMP_H
#include "ultima/ultima8/gumps/desktop_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class RenderSurface;
/**
* A gump to vertically invert the desktop contents - happens when certain switches in the game are switched.
*/
class InverterGump : public DesktopGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
InverterGump(int32 x, int32 y, int32 width, int32 height);
~InverterGump() override;
void Paint(RenderSurface *surf, int32 lerp_factor, bool scaled) override;
void PaintChildren(RenderSurface *surf, int32 lerp_factor, bool scaled) override;
void ParentToGump(int32 &px, int32 &py,
PointRoundDir r = ROUND_TOPLEFT) override;
void GumpToParent(int32 &gx, int32 &gy,
PointRoundDir r = ROUND_TOPLEFT) override;
void RenderSurfaceChanged() override;
protected:
RenderSurface *_buffer;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,187 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/item_relative_gump.h"
#include "ultima/ultima8/gumps/game_map_gump.h"
#include "ultima/ultima8/world/container.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(ItemRelativeGump)
ItemRelativeGump::ItemRelativeGump() : Gump(), _ix(0), _iy(0) {
}
ItemRelativeGump::ItemRelativeGump(int32 x, int32 y, int32 width, int32 height,
uint16 owner, uint32 flags, int32 layer)
: Gump(x, y, width, height, owner, flags, layer), _ix(0), _iy(0) {
}
ItemRelativeGump::~ItemRelativeGump(void) {
}
void ItemRelativeGump::InitGump(Gump *newparent, bool take_focus) {
Gump::InitGump(newparent, take_focus);
GetItemLocation(0);
if (!newparent && _parent)
MoveOnScreen();
}
void ItemRelativeGump::MoveOnScreen() {
assert(_parent);
Common::Rect32 sd = _parent->getDims();
// first move back to our desired location
_x = 0;
_y = 0;
// get rectangle that gump occupies in desktops's coordinate space
int32 left, right, top, bottom;
left = -_dims.left;
right = left + _dims.width();
top = -_dims.top;
bottom = top + _dims.height();
GumpToParent(left, top);
GumpToParent(right, bottom);
int32 movex = 0, movey = 0;
if (left < -sd.left)
movex = -sd.left - left;
else if (right > -sd.left + sd.width())
movex = -sd.left + sd.width() - right;
if (top < -sd.top)
movey = -sd.top - top;
else if (bottom > -sd.top + sd.height())
movey = -sd.top + sd.height() - bottom;
Move(left + movex, top + movey);
}
// Paint the Gump (RenderSurface is relative to parent).
// Calls PaintThis and PaintChildren
void ItemRelativeGump::Paint(RenderSurface *surf, int32 lerp_factor, bool scaled) {
GetItemLocation(lerp_factor);
Gump::Paint(surf, lerp_factor, scaled);
}
// Convert a parent relative point to a gump point
void ItemRelativeGump::ParentToGump(int32 &px, int32 &py, PointRoundDir r) {
px -= _ix;
py -= _iy;
Gump::ParentToGump(px, py, r);
}
// Convert a gump point to parent relative point
void ItemRelativeGump::GumpToParent(int32 &gx, int32 &gy, PointRoundDir r) {
Gump::GumpToParent(gx, gy, r);
gx += _ix;
gy += _iy;
}
void ItemRelativeGump::GetItemLocation(int32 lerp_factor) {
Gump *gump = nullptr;
Item *it = getItem(_owner);
if (!it) {
// This shouldn't ever happen, the GumpNotifyProcess should
// close us before we get here
Close();
return;
}
Item *next;
Item *prev = nullptr;
while ((next = it->getParentAsContainer()) != nullptr) {
prev = it;
it = next;
gump = getGump(it->getGump());
if (gump) break;
}
int32 gx, gy;
bool found;
if (!gump) {
gump = GetRootGump()->FindGump<GameMapGump>();
if (!gump) {
warning("ItemRelativeGump::GetItemLocation(): Unable to find GameMapGump");
return;
}
found = gump->GetLocationOfItem(_owner, gx, gy, lerp_factor);
} else {
assert(prev);
found = gump->GetLocationOfItem(prev->getObjId(), gx, gy, lerp_factor);
}
if (found) {
// Convert the GumpSpaceCoord relative to the world/item gump
// into screenspace coords
gy = gy - it->getShapeInfo()->_z * 8 - 16;
} else {
// If location not found show near bottom center
Common::Rect32 r = gump->getDims();
gx = (r.left + r.right) / 2;
gy = r.bottom - 8;
}
gump->GumpToScreenSpace(gx, gy);
// Convert the screenspace coords into the coords of us
if (_parent)
_parent->ScreenSpaceToGump(gx, gy);
// Set x and y, and center us over it
_ix = gx - _dims.width() / 2;
_iy = gy - _dims.height();
if (_flags & FLAG_KEEP_VISIBLE)
MoveOnScreen();
}
void ItemRelativeGump::Move(int32 x, int32 y) {
ParentToGump(x, y);
_x += x;
_y += y;
}
void ItemRelativeGump::saveData(Common::WriteStream *ws) {
Gump::saveData(ws);
}
bool ItemRelativeGump::loadData(Common::ReadStream *rs, uint32 version) {
if (!Gump::loadData(rs, version)) return false;
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,71 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_ITEMRELATIVEGUMP_H
#define ULTIMA8_GUMPS_ITEMRELATIVEGUMP_H
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* Base class for gumps whose location is relative to an owning item (eg, inventory, bark, etc)
*/
class ItemRelativeGump : public Gump {
protected:
int32 _ix, _iy;
public:
ENABLE_RUNTIME_CLASSTYPE()
ItemRelativeGump();
ItemRelativeGump(int32 x, int32 y, int32 width, int32 height, uint16 owner, uint32 flags = 0, int32 layer = LAYER_NORMAL);
~ItemRelativeGump() override;
void InitGump(Gump *newparent, bool take_focus = true) override;
// Paint the Gump (RenderSurface is relative to parent).
// Calls PaintThis and PaintChildren.
void Paint(RenderSurface *, int32 lerp_factor, bool scaled) override;
void ParentToGump(int32 &px, int32 &py,
PointRoundDir r = ROUND_TOPLEFT) override;
void GumpToParent(int32 &gx, int32 &gy,
PointRoundDir r = ROUND_TOPLEFT) override;
void Move(int32 x, int32 y) override;
bool loadData(Common::ReadStream *rs, uint32 version);
protected:
void saveData(Common::WriteStream *ws) override;
virtual void GetItemLocation(int32 lerp_factor);
//! Move Gump so that it totally overlaps parent.
void MoveOnScreen();
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,268 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/events.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/gumps/keypad_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/usecode/uc_process.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(KeypadGump)
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;
static const uint16 SFXNO_BUTTON = 0x3b;
static const uint16 SFXNO_CORRECT = 0x32;
static const uint16 SFXNO_WRONG = 0x31;
static const uint16 SFXNO_DEL = 0x3a;
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::MOUSE_HAND);
for (int i = 0; i < 12; i++) {
_buttons[i] = 0;
}
}
KeypadGump::~KeypadGump() {
Mouse::get_instance()->popMouseCursor();
}
void KeypadGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
_shape = GameData::get_instance()->getGumps()->getShape(10);
UpdateDimsFromShape();
static const int buttonShapeNum = 11;
static const uint16 xoffs[] = {0xc, 0x27, 0x42};
static const uint16 yoffs[] = {0x19, 0x32, 0x4a, 0x62};
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 3; x++) {
int bnum = y * 3 + x;
FrameID button_up(GameData::GUMPS, buttonShapeNum, bnum);
FrameID button_down(GameData::GUMPS, buttonShapeNum, bnum + 12);
Gump *widget = new ButtonWidget(xoffs[x], yoffs[y], button_up, button_down);
widget->InitGump(this);
widget->SetIndex(bnum);
_buttons[bnum] = widget->getObjId();
}
}
// Default result is 0xff
SetResult(0xff);
}
void KeypadGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Gump::PaintThis(surf, lerp_factor, scaled);
}
bool KeypadGump::OnKeyDown(int key, int mod) {
uint16 sfxno = 0;
bool shouldclose = false;
switch (key) {
case Common::KEYCODE_ESCAPE:
_value = -1;
shouldclose = true;
break;
case Common::KEYCODE_BACKSPACE:
_value /= 10;
sfxno = SFXNO_DEL;
break;
case Common::KEYCODE_RETURN:
if (_value == _targetValue || _value == CHEAT_CODE_VAL) {
sfxno = SFXNO_CORRECT;
_value = _targetValue;
SetResult(_targetValue);
} else {
// wrong.
sfxno = SFXNO_WRONG;
SetResult(0);
}
shouldclose = true;
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();
sfxno = SFXNO_BUTTON;
break;
default:
break;
}
AudioProcess *audio = AudioProcess::get_instance();
if (audio && sfxno)
audio->playSFX(sfxno, 0x10, _objId, 1);
if (shouldclose)
Close();
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) {
bool update = true;
bool shouldclose = false;
if (message == ButtonWidget::BUTTON_CLICK) {
uint16 sfxno = SFXNO_BUTTON;
int buttonNo = child->GetIndex();
if (buttonNo < 9) {
onDigit(buttonNo + 1);
} else if (buttonNo == 10) {
onDigit(0);
} else if (buttonNo == 9) {
// Backspace key
_value /= 10;
sfxno = SFXNO_DEL;
} else if (buttonNo == 11) {
update = false;
if (_value == _targetValue || _value == CHEAT_CODE_VAL) {
sfxno = SFXNO_CORRECT;
_value = _targetValue;
SetResult(_targetValue);
} else {
// wrong.
sfxno = SFXNO_WRONG;
SetResult(0);
}
shouldclose = true;
}
AudioProcess *audio = AudioProcess::get_instance();
if (audio && sfxno)
audio->playSFX(sfxno, 0x10, _objId, 1);
}
if (update)
updateDigitDisplay();
if (shouldclose)
Close();
}
void KeypadGump::updateDigitDisplay() {
Gump *txt = Gump::FindGump(&FindByIndex<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(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;
// Could also accept numeric keyboard inputs here.
// For now we do it in OnKeyDown.
}
return true;
}
uint32 KeypadGump::I_showKeypad(const uint8 *args, unsigned int /*argsize*/) {
ARG_UINT16(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);
current->suspend();
return 0;
}
bool KeypadGump::loadData(Common::ReadStream *rs) {
warning("Trying to load ModalGump");
return true;
}
void KeypadGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,72 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_KEYPADGUMP_H
#define ULTIMA8_GUMPS_KEYPADGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* The keypad with numbers gump
*/
class KeypadGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
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;
bool OnKeyDown(int key, int mod) override;
bool OnTextInput(int unicode) override;
void ChildNotify(Gump *child, uint32 message) override;
INTRINSIC(I_showKeypad);
bool loadData(Common::ReadStream *rs);
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
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,64 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/main_menu_process.h"
#include "ultima/ultima8/gumps/menu_gump.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/world/actors/main_actor.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(MainMenuProcess)
MainMenuProcess::MainMenuProcess() : Process() {
_flags |= PROC_PREVENT_SAVE;
}
void MainMenuProcess::run() {
MainActor *avatar = getMainActor();
if (avatar && avatar->isDead()) {
// stop death music
MusicProcess::get_instance()->playCombatMusic(0);
}
MenuGump::showMenu();
terminate();
}
void MainMenuProcess::saveData(Common::WriteStream *ws) {
warning("Attempted save of process with prevent save flag");
Process::saveData(ws);
}
bool MainMenuProcess::loadData(Common::ReadStream *rs, uint32 version) {
if (!Process::loadData(rs, version)) return false;
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,48 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_MAINMENUPROCESS_H
#define ULTIMA8_GUMPS_MAINMENUPROCESS_H
#include "ultima/ultima8/kernel/process.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class Item;
class MainMenuProcess : public Process {
public:
MainMenuProcess();
ENABLE_RUNTIME_CLASSTYPE()
void run() override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,286 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/config-manager.h"
#include "ultima/ultima8/gumps/menu_gump.h"
#include "ultima/ultima8/gumps/cru_menu_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/gumps/quit_gump.h"
#include "ultima/ultima8/games/game.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/audio/music_process.h"
#include "ultima/ultima8/gumps/widgets/edit_widget.h"
#include "ultima/ultima8/gumps/u8_save_gump.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/metaengine.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(MenuGump)
MenuGump::MenuGump(bool nameEntryMode)
: ModalGump(0, 0, 5, 5, 0, FLAG_DONT_SAVE) {
_nameEntryMode = nameEntryMode;
Mouse *mouse = Mouse::get_instance();
if (!_nameEntryMode)
mouse->pushMouseCursor(Mouse::MOUSE_HAND);
else
mouse->pushMouseCursor(Mouse::MOUSE_NONE);
// Save old music state
MusicProcess *musicprocess = MusicProcess::get_instance();
if (musicprocess) {
musicprocess->saveTrackState();
// Stop any playing music.
musicprocess->playCombatMusic(0);
}
// Save old palette transform
PaletteManager *palman = PaletteManager::get_instance();
palman->getTransformMatrix(_oldPalTransform, PaletteManager::Pal_Game);
palman->untransformPalette(PaletteManager::Pal_Game);
}
MenuGump::~MenuGump() {
}
void MenuGump::Close(bool no_del) {
// Restore old music state and palette.
// Music state can be changed by the Intro and Credits
MusicProcess *musicprocess = MusicProcess::get_instance();
if (musicprocess)
musicprocess->restoreTrackState();
PaletteManager *palman = PaletteManager::get_instance();
palman->transformPalette(PaletteManager::Pal_Game, _oldPalTransform);
Mouse *mouse = Mouse::get_instance();
mouse->popMouseCursor();
ModalGump::Close(no_del);
}
static const int gumpShape = 35;
static const int paganShape = 32;
static const int menuEntryShape = 37;
static const char *MENU_TXT[] = {
"1.Intro",
"2.Read Diary",
"3.Write Diary",
"4.Options",
"5.Credits",
"6.Quit",
"7.Quotes",
"8.End Game"
};
void MenuGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
_shape = GameData::get_instance()->getGumps()->getShape(gumpShape);
UpdateDimsFromShape();
Shape *logoShape;
logoShape = GameData::get_instance()->getGumps()->getShape(paganShape);
const ShapeFrame *sf = logoShape->getFrame(0);
assert(sf);
Gump *logo = new Gump(42, 10, sf->_width, sf->_height);
logo->SetShape(logoShape, 0);
logo->InitGump(this, false);
if (!_nameEntryMode) {
bool endgame = ConfMan.getBool("endgame");
bool quotes = ConfMan.getBool("quotes");
int x = _dims.width() / 2 + 14;
int y = 18;
for (int i = 0; i < 8; ++i) {
if ((quotes || i != 6) && (endgame || i != 7)) {
FrameID frame_up(GameData::GUMPS, menuEntryShape, i * 2);
FrameID frame_down(GameData::GUMPS, menuEntryShape, i * 2 + 1);
frame_up = _TL_SHP_(frame_up);
frame_down = _TL_SHP_(frame_down);
Gump *widget;
if (frame_up._shapeNum) {
widget = new ButtonWidget(x, y, frame_up, frame_down, true);
} else {
// JA U8 has text labels
widget = new ButtonWidget(x, y, _TL_(MENU_TXT[i]), true, 0);
}
widget->InitGump(this, false);
widget->SetIndex(i + 1);
}
y += 14;
}
const MainActor *av = getMainActor();
Std::string name;
if (av)
name = av->getName();
if (!name.empty()) {
Gump *widget = new TextWidget(0, 0, name, true, 6);
widget->InitGump(this, false);
Common::Rect32 rect = widget->getDims();
widget->Move(90 - rect.width() / 2, _dims.height() - 40);
}
} else {
Gump *widget;
widget = new TextWidget(0, 0, _TL_("Give thy name:"), true, 6); // CONSTANT!
widget->InitGump(this, false);
widget->Move(_dims.width() / 2 + 6, 10);
Common::Rect32 textdims = widget->getDims();
widget = new EditWidget(0, 0, "", true, 6, 110, 40, 15); // CONSTANTS!
widget->InitGump(this, true);
widget->Move(_dims.width() / 2 + 6, 10 + textdims.height());
widget->MakeFocus();
}
}
void MenuGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Gump::PaintThis(surf, lerp_factor, scaled);
}
void MenuGump::onMouseDouble(int button, int32 mx, int32 my) {
// FIXME: this check should probably be in Game or GUIApp
MainActor *av = getMainActor();
if (av && !av->hasActorFlags(Actor::ACT_DEAD))
Close(); // don't allow closing if dead/game over
}
bool MenuGump::OnKeyDown(int key, int mod) {
if (Gump::OnKeyDown(key, mod)) return true;
if (!_nameEntryMode) {
if (key == Common::KEYCODE_ESCAPE) {
// FIXME: this check should probably be in Game or GUIApp
MainActor *av = getMainActor();
if (av && !av->hasActorFlags(Actor::ACT_DEAD))
Close(); // don't allow closing if dead/game over
} else if (key >= Common::KEYCODE_1 && key <= Common::KEYCODE_9) {
selectEntry(key - Common::KEYCODE_1 + 1);
}
}
return true;
}
void MenuGump::ChildNotify(Gump *child, uint32 message) {
EditWidget *editwidget = dynamic_cast<EditWidget *>(child);
if (editwidget && message == EditWidget::EDIT_ENTER) {
Std::string name = editwidget->getText();
if (!name.empty()) {
MainActor *av = getMainActor();
av->setName(name);
Close();
}
}
ButtonWidget *buttonWidget = dynamic_cast<ButtonWidget *>(child);
if (buttonWidget) {
if (message == ButtonWidget::BUTTON_CLICK || message == ButtonWidget::BUTTON_DOUBLE) {
selectEntry(buttonWidget->GetIndex());
}
}
}
void MenuGump::selectEntry(int entry) {
bool endgame = ConfMan.getBool("endgame");
bool quotes = ConfMan.getBool("quotes");
switch (entry) {
case 1: // Intro
Game::get_instance()->playIntroMovie(true);
break;
case 2:
case 3: // Read/Write Diary
U8SaveGump::showLoadSaveGump(this, entry == 3);
break;
case 4: {
Ultima8Engine::get_instance()->openConfigDialog();
}
break;
case 5: // Credits
Game::get_instance()->playCredits();
break;
case 6: // Quit
QuitGump::verifyQuit();
break;
case 7: // Quotes
if (quotes) Game::get_instance()->playQuotes();
break;
case 8: // End Game
if (endgame) Game::get_instance()->playEndgameMovie(true);
break;
default:
break;
}
}
bool MenuGump::OnTextInput(int unicode) {
if (Gump::OnTextInput(unicode)) return true;
return true;
}
//static
void MenuGump::showMenu() {
ModalGump *gump;
if (GAME_IS_U8)
gump = new MenuGump();
else
gump = new CruMenuGump();
gump->InitGump(0);
gump->setRelativePosition(CENTER);
}
//static
void MenuGump::inputName() {
ModalGump *gump;
if (GAME_IS_U8)
gump = new MenuGump(true);
else
gump = new CruMenuGump();
gump->InitGump(0);
gump->setRelativePosition(CENTER);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,69 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_MENUGUMP_H
#define ULTIMA8_GUMPS_MENUGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class RenderedText;
/**
* The game menu - with entries like Read Diary, Write Diary, Credits, etc.
*/
class MenuGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
MenuGump(bool nameEntryMode = false);
~MenuGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
void Close(bool no_del = false) override;
// Paint the Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
void onMouseDouble(int button, int32 mx, int32 my) override;
bool OnKeyDown(int key, int mod) override;
bool OnTextInput(int unicode) override;
void ChildNotify(Gump *child, uint32 message) override;
static void showMenu();
static void inputName();
protected:
bool _nameEntryMode;
int16 _oldPalTransform[12];
virtual void selectEntry(int entry);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,168 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/message_box_gump.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(MessageBoxGump)
MessageBoxGump::MessageBoxGump()
: ModalGump(), _titleColour(TEX32_PACK_RGB(0, 0, 0)) {
}
MessageBoxGump::MessageBoxGump(const Std::string &title, const Std::string &message, uint32 titleColour,
Std::vector<Std::string> *buttons) :
ModalGump(0, 0, 100, 100), _title(title), _message(message), _titleColour(titleColour) {
if (buttons)
buttons->swap(_buttons);
if (_buttons.empty())
_buttons.push_back(Std::string("Ok"));
}
MessageBoxGump::~MessageBoxGump(void) {
}
#define MBG_PADDING 16
void MessageBoxGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
// work out sizes of the text
Font *font = FontManager::get_instance()->getTTFont(0);
int32 width, height;
unsigned int rem;
// Title width
font->getStringSize(_title, width, height);
int title_w = width;
// Width of _buttons
int buttons_w = MBG_PADDING;
for (size_t i = 0; i < _buttons.size(); i++) {
width = height = 0;
font->getStringSize(_buttons[i], width, height);
buttons_w += width + MBG_PADDING;
}
// Message size
font->getTextSize(_message, width, height, rem);
_dims.setWidth(MBG_PADDING + width + MBG_PADDING);
if (_dims.width() < MBG_PADDING + title_w + MBG_PADDING) _dims.setWidth(MBG_PADDING + title_w + MBG_PADDING);
if (_dims.width() < buttons_w) _dims.setWidth(buttons_w);
_dims.setHeight(23 + MBG_PADDING + height + MBG_PADDING + 28);
// Title
Gump *w = new TextWidget(MBG_PADDING, 2, _title, false, 0);
w->InitGump(this, false);
// Message
w = new TextWidget(MBG_PADDING, 23 + MBG_PADDING, _message, false, 0, width, height);
w->InitGump(this, false);
// Buttons (right aligned)
int off = _dims.width() - buttons_w;
for (size_t i = 0; i < _buttons.size(); i++) {
w = new ButtonWidget(off, _dims.height() - 23, _buttons[i], false, 1, TEX32_PACK_RGBA(0xD0, 0x00, 0xD0, 0x80));
w->SetIndex(static_cast<int32>(i));
w->InitGump(this, false);
width = height = 0;
font->getStringSize(_buttons[i], width, height);
off += width + MBG_PADDING;
}
Mouse *mouse = Mouse::get_instance();
mouse->pushMouseCursor(Mouse::MOUSE_HAND);
}
void MessageBoxGump::Close(bool no_del) {
Mouse *mouse = Mouse::get_instance();
mouse->popMouseCursor();
ModalGump::Close(no_del);
}
void MessageBoxGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool /*scaled*/) {
// Background is partially transparent
surf->fillBlended(TEX32_PACK_RGBA(0, 0, 0, 0x80), _dims);
uint32 color = TEX32_PACK_RGB(0xFF, 0xFF, 0xFF);
if (!IsFocus())
color = TEX32_PACK_RGB(0x7F, 0x7F, 0x7F);
// outer border
surf->fill32(color, 0, 0, _dims.width(), 1);
surf->fill32(color, 0, 0, 1, _dims.height());
surf->fill32(color, 0, _dims.height() - 1, _dims.width(), 1);
surf->fill32(color, _dims.width() - 1, 0, 1, _dims.height());
// line above _buttons
surf->fill32(color, 0, _dims.height() - 28, _dims.width(), 1);
// line below _title
surf->fill32(color, 0, 23, _dims.width(), 1);
// Highlight behind _message
color = IsFocus() ? _titleColour : TEX32_PACK_RGB(0, 0, 0);
surf->fill32(color, 1, 1, _dims.width() - 2, 22);
}
void MessageBoxGump::ChildNotify(Gump *child, uint32 msg) {
ButtonWidget *buttonWidget = dynamic_cast<ButtonWidget *>(child);
if (buttonWidget && (msg == ButtonWidget::BUTTON_CLICK || msg == ButtonWidget::BUTTON_DOUBLE)) {
_processResult = child->GetIndex();
Close();
}
}
ProcId MessageBoxGump::Show(Std::string _title, Std::string _message, uint32 titleColour, Std::vector<Std::string> *_buttons) {
Gump *gump = new MessageBoxGump(_title, _message, titleColour, _buttons);
gump->InitGump(0);
gump->setRelativePosition(CENTER);
gump->CreateNotifier();
return gump->GetNotifyProcess()->getPid();
}
void MessageBoxGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
bool MessageBoxGump::loadData(Common::ReadStream *rs, uint32 version) {
warning("Trying to load ModalGump");
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,78 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_MESSAGEBOXGUMP_H
#define ULTIMA8_GUMPS_MESSAGEBOXGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/shared/std/string.h"
#include "ultima/shared/std/containers.h"
#include "ultima/ultima8/misc/classtype.h"
#include "ultima/ultima8/gfx/texture.h"
namespace Ultima {
namespace Ultima8 {
/**
* A modal message box (for errors, etc)
* In Crusader, this should be used for things like the "Targeting reticle [in]active." message
*/
class MessageBoxGump : public ModalGump {
Std::string _title;
Std::string _message;
Std::vector<Std::string> _buttons;
int _titleColour;
public:
ENABLE_RUNTIME_CLASSTYPE()
MessageBoxGump();
MessageBoxGump(const Std::string &title, const Std::string &message, uint32 title_colour, Std::vector<Std::string> *buttons);
~MessageBoxGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
void Close(bool no_del = false) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
//! Create a Message Box
//! \param title Title of the message box
//! \param message Message to be displayed in box
//! \param titleColour The colour to be displayed behind the title bar
//! \param buttons Array of button names to be displayed. Default is "Ok"
//! \return Pid of process that will have the result when finished
static ProcId Show(Std::string title, Std::string message, uint32 titleColour, Std::vector<Std::string> *buttons = 0);
static ProcId Show(Std::string title, Std::string message, Std::vector<Std::string> *buttons) {
return Show(title, message, TEX32_PACK_RGB(0x30, 0x30, 0x8F), buttons);
}
void ChildNotify(Gump *child, uint32 msg) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,137 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/mini_stats_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(MiniStatsGump)
static const int gumpshape = 33;
static const int hpx = 6;
static const int manax = 13;
static const int bary = 19;
static const int barheight = 14;
// TODO: Confirm palette colors for use on mini stats gump
// These values were closest to previously defined RGB values
static const uint hpcolour[3] = {41, 39, 37};
static const uint manacolour[3] = {138, 139, 141};
MiniStatsGump::MiniStatsGump() : Gump() {
}
MiniStatsGump::MiniStatsGump(int x, int y, uint32 flags, int32 layer)
: Gump(x, y, 5, 5, 0, flags, layer) {
}
MiniStatsGump::~MiniStatsGump() {
}
void MiniStatsGump::InitGump(Gump *newparent, bool take_focus) {
Gump::InitGump(newparent, take_focus);
_shape = GameData::get_instance()->getGumps()->getShape(gumpshape);
UpdateDimsFromShape();
}
void MiniStatsGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Gump::PaintThis(surf, lerp_factor, scaled);
Actor *a = getMainActor();
assert(a);
int16 maxmana = a->getMaxMana();
int16 mana = a->getMana();
uint16 maxhp = a->getMaxHP();
uint16 hp = a->getHP();
int manaheight, hpheight;
if (maxmana == 0)
manaheight = 0;
else
manaheight = (mana * barheight) / maxmana;
if (maxhp == 0)
hpheight = 0;
else
hpheight = (hp * barheight) / maxhp;
Palette *pal = PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game);
for (int i = 0; i < 3; ++i) {
Common::Rect32 hprect(hpx + i, bary - hpheight + 1, hpx + i + 1, bary + 1);
Common::Rect32 manarect(manax + i, bary - manaheight + 1, manax + i + 1, bary + 1);
surf->fillRect(hprect, pal->_native[hpcolour[i]]);
surf->fillRect(manarect, pal->_native[manacolour[i]]);
}
}
uint16 MiniStatsGump::TraceObjId(int32 mx, int32 my) {
uint16 objId_ = Gump::TraceObjId(mx, my);
if (objId_ && objId_ != 65535) return objId_;
if (PointOnGump(mx, my)) return getObjId();
return 0;
}
Gump *MiniStatsGump::onMouseDown(int button, int32 mx, int32 my) {
if (button == Mouse::BUTTON_LEFT)
return this;
return nullptr;
}
void MiniStatsGump::onMouseDouble(int button, int32 mx, int32 my) {
// check if there already is an open PaperdollGump
MainActor *av = getMainActor();
if (!av->getGump()) {
av->callUsecodeEvent_use();
}
Close();
}
void MiniStatsGump::saveData(Common::WriteStream *ws) {
Gump::saveData(ws);
}
bool MiniStatsGump::loadData(Common::ReadStream *rs, uint32 version) {
return Gump::loadData(rs, version);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,62 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_MINISTATSGUMP_H
#define ULTIMA8_GUMPS_MINISTATSGUMP_H
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* The tiny HP/mana bars shown on the desktop which can be activated from the paperdoll gump
*/
class MiniStatsGump : public Gump {
public:
ENABLE_RUNTIME_CLASSTYPE()
MiniStatsGump();
MiniStatsGump(int x, int y, uint32 flags = FLAG_DRAGGABLE,
int32 layer = LAYER_NORMAL);
~MiniStatsGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Paint this Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
// Trace a click, and return ObjId
uint16 TraceObjId(int32 mx, int32 my) override;
Gump *onMouseDown(int button, int32 mx, int32 my) override;
void onMouseDouble(int button, int32 mx, int32 my) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,249 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/minimap_gump.h"
#include "ultima/ultima8/world/minimap.h"
#include "ultima/ultima8/world/current_map.h"
#include "ultima/ultima8/world/world.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/kernel/mouse.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(MiniMapGump)
static const uint BACKGROUND_COLOR = 0;
static const uint NORMAL_COLOR = 53;
static const uint HIGHLIGHT_COLOR = 52;
static const uint KEY_COLOR = 255;
MiniMapGump::MiniMapGump(int x, int y) : ResizableGump(x, y, 120, 120), _minimaps(), _ax(0), _ay(0) {
setMinSize(60, 60);
}
MiniMapGump::MiniMapGump() : ResizableGump(), _minimaps(), _ax(0), _ay(0) {
setMinSize(60, 60);
}
MiniMapGump::~MiniMapGump(void) {
for (auto &i : _minimaps) {
delete i._value;
}
}
void MiniMapGump::run() {
Gump::run();
World *world = World::get_instance();
CurrentMap *currentmap = world->getCurrentMap();
int mapChunkSize = currentmap->getChunkSize();
MainActor *actor = getMainActor();
if (!actor || actor->isDead())
return;
uint32 mapNum = currentmap->getNum();
MiniMap *minimap = _minimaps[mapNum];
if (!minimap) {
minimap = new MiniMap(mapNum);
_minimaps[mapNum] = minimap;
}
Common::Point p = minimap->getItemLocation(*actor, mapChunkSize);
// Skip map update if location has not changed
if (p.x == _ax && p.y == _ay)
return;
_ax = p.x;
_ay = p.y;
minimap->update(*currentmap);
}
void MiniMapGump::generate() {
World *world = World::get_instance();
CurrentMap *currentmap = world->getCurrentMap();
currentmap->setWholeMapFast();
uint32 mapNum = currentmap->getNum();
MiniMap *minimap = _minimaps[mapNum];
if (!minimap) {
minimap = new MiniMap(mapNum);
_minimaps[mapNum] = minimap;
}
minimap->update(*currentmap);
}
void MiniMapGump::clear() {
for (auto &i : _minimaps) {
delete i._value;
}
_minimaps.clear();
}
bool MiniMapGump::dump(const Common::Path &filename) const {
World *world = World::get_instance();
CurrentMap *currentmap = world->getCurrentMap();
uint32 mapNum = currentmap->getNum();
MiniMap *minimap = _minimaps[mapNum];
return minimap ? minimap->dump(filename) : false;
}
void MiniMapGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Palette *pal = PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game);
uint32 *map = pal->_native;
uint32 color = map[NORMAL_COLOR];
if (_dragPosition != Gump::CENTER || _mousePosition != Gump::CENTER)
color = map[HIGHLIGHT_COLOR];
// Draw the border
surf->frameRect(_dims, color);
// Dimensions minus border
Common::Rect32 dims = _dims;
dims.grow(-1);
// Fill the background
surf->fillRect(dims, map[BACKGROUND_COLOR]);
// Center on avatar
int sx = _ax - dims.width() / 2;
int sy = _ay - dims.height() / 2;
int dx = 1;
int dy = 1;
World *world = World::get_instance();
CurrentMap *currentmap = world->getCurrentMap();
uint32 mapNum = currentmap->getNum();
MiniMap *minimap = _minimaps[mapNum];
if (!minimap) {
minimap = new MiniMap(mapNum);
_minimaps[mapNum] = minimap;
}
const Graphics::Surface *ms = minimap->getSurface();
Common::Rect r(sx, sy, sx + dims.width(), sy + dims.height());
if (r.left < 0) {
dx -= r.left;
r.left = 0;
}
if (r.right > ms->w) {
r.right = ms->w;
}
if (r.top < 0) {
dy -= r.top;
r.top = 0;
}
if (r.bottom > ms->h) {
r.bottom = ms->h;
}
if (!r.isEmpty()) {
surf->CrossKeyBlitMap(*ms, r, dx, dy, map, KEY_COLOR);
}
int32 ax = _ax - sx;
int32 ay = _ay - sy;
// Paint the avatar position marker
surf->drawLine(ax - 1, ay + 1, ax, ay + 1, color);
surf->drawLine(ax + 1, ay - 1, ax + 1, ay, color);
surf->drawLine(ax + 2, ay + 1, ax + 3, ay + 1, color);
surf->drawLine(ax + 1, ay + 2, ax + 1, ay + 3, color);
}
Gump *MiniMapGump::onMouseDown(int button, int32 mx, int32 my) {
Gump *handled = Gump::onMouseDown(button, mx, my);
if (handled)
return handled;
// only interested in left clicks
if (button == Mouse::BUTTON_LEFT)
return this;
return nullptr;
}
void MiniMapGump::onMouseDouble(int button, int32 mx, int32 my) {
if (button == Mouse::BUTTON_LEFT) {
HideGump();
}
}
void MiniMapGump::saveData(Common::WriteStream *ws) {
Gump::saveData(ws);
ws->writeUint32LE(static_cast<uint32>(_minimaps.size()));
for (const auto &i : _minimaps) {
const MiniMap *minimap = i._value;
ws->writeUint32LE(i._key);
minimap->save(ws);
}
}
bool MiniMapGump::loadData(Common::ReadStream *rs, uint32 version) {
if (!Gump::loadData(rs, version))
return false;
_ax = 0;
_ay = 0;
clear();
if (version >= 6) {
uint32 mapcount = rs->readUint32LE();
for (uint32 i = 0; i < mapcount; ++i) {
uint32 mapNum = rs->readUint32LE();
MiniMap *minimap = new MiniMap(mapNum);
if (!minimap->load(rs, version))
return false;
_minimaps[mapNum] = minimap;
}
}
return true;
}
uint16 MiniMapGump::TraceObjId(int32 mx, int32 my) {
uint16 objId = Gump::TraceObjId(mx, my);
if (!objId || objId == 65535)
if (PointOnGump(mx, my))
objId = getObjId();
return objId;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,68 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_MINIMAPGUMP_H
#define ULTIMA8_GUMPS_MINIMAPGUMP_H
#include "ultima/ultima8/gumps/resizable_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Common {
class Path;
}
namespace Ultima {
namespace Ultima8 {
class MiniMap;
class MiniMapGump : public ResizableGump {
private:
Common::HashMap<uint32, MiniMap *> _minimaps;
int32 _ax, _ay;
public:
ENABLE_RUNTIME_CLASSTYPE()
MiniMapGump();
MiniMapGump(int x, int y);
~MiniMapGump() override;
void run() override;
void generate();
void clear();
bool dump(const Common::Path &filename) const;
void PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) override;
uint16 TraceObjId(int32 mx, int32 my) override;
Gump *onMouseDown(int button, int32 mx, int32 my) override;
void onMouseDouble(int button, int32 mx, int32 my) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,106 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/audio/audio_process.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(ModalGump)
ModalGump::ModalGump() : Gump() {
}
ModalGump::ModalGump(int x, int y, int width, int height, uint16 owner,
uint32 flags, int32 layer, bool pauseGame)
: Gump(x, y, width, height, owner, flags, layer), _pauseGame(pauseGame) {
}
ModalGump::~ModalGump() {
}
void ModalGump::InitGump(Gump *newparent, bool take_focus) {
Gump::InitGump(newparent, take_focus);
if (_pauseGame) {
Kernel::get_instance()->pause();
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
ap->pauseAllSamples();
}
}
Gump *ModalGump::FindGump(int mx, int my) {
Gump *ret = Gump::FindGump(mx, my);
if (!ret) ret = this; // we take all mouse input
return ret;
}
bool ModalGump::PointOnGump(int mx, int my) {
return true; // we take all mouse input
}
uint16 ModalGump::TraceObjId(int32 mx, int32 my) {
uint16 objId = Gump::TraceObjId(mx, my);
if (!objId) objId = getObjId();
return objId;
}
void ModalGump::Close(bool no_del) {
if (_pauseGame) {
Kernel::get_instance()->unpause();
AudioProcess *ap = AudioProcess::get_instance();
if (ap)
ap->unpauseAllSamples();
}
Gump::Close(no_del);
}
Gump *ModalGump::onMouseDown(int button, int32 mx, int32 my) {
Gump *handled = Gump::onMouseDown(button, mx, my);
if (!handled) handled = this;
return handled;
}
void ModalGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
bool ModalGump::loadData(Common::ReadStream *rs, uint32 version) {
warning("Trying to load ModalGump");
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,65 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_MODALGUMP_H
#define ULTIMA8_GUMPS_MODALGUMP_H
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* Base class for any gump which blocks the game (menu, credits, movie, etc)
*/
class ModalGump : public Gump {
protected:
bool _pauseGame;
public:
ENABLE_RUNTIME_CLASSTYPE()
ModalGump();
ModalGump(int x, int y, int width, int height, uint16 owner = 0,
uint32 flags = FLAG_DONT_SAVE | FLAG_PREVENT_SAVE, int32 layer = LAYER_MODAL,
bool pauseGame = true);
~ModalGump() override;
void InitGump(Gump *newparent, bool take_focus = true) override;
void Close(bool no_del = false) override;
bool PointOnGump(int mx, int my) override;
Gump *FindGump(int mx, int my) override;
uint16 TraceObjId(int32 mx, int32 my) override;
Gump *onMouseDown(int button, int32 mx, int32 my) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,437 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "ultima/ultima8/gumps/movie_gump.h"
#include "ultima/ultima8/gfx/avi_player.h"
#include "ultima/ultima8/gfx/skf_player.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/gfx/fade_to_modal_process.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/games/game.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/usecode/uc_machine.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/gumps/cru_status_gump.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
namespace Ultima {
namespace Ultima8 {
// Some fourCCs used in IFF files
static const uint32 IFF_MAGIC = MKTAG('F', 'O', 'R', 'M');
static const uint32 IFF_LANG = MKTAG('L', 'A', 'N', 'G');
static const uint32 IFF_LANG_FR = MKTAG('F', 'R', 'E', 'N');
static const uint32 IFF_LANG_EN = MKTAG('E', 'N', 'G', 'L');
static const uint32 IFF_LANG_DE = MKTAG('G', 'E', 'R', 'M');
static Std::string _fixCrusaderMovieName(const Std::string &s) {
/*
HACK! The game comes with movies MVA01.AVI etc, but the usecode mentions both
MVA01 and MVA1. We do a translation here. These are the strings we need to fix:
008E: 0D push string "mva1"
036D: 0D push string "mva3a"
04E3: 0D push string "mva4"
0656: 0D push string "mva5a"
07BD: 0D push string "mva6"
0944: 0D push string "mva7"
0A68: 0D push string "mva8"
0B52: 0D push string "mva9"
*/
if (s.size() == 4)
return Std::string::format("mva0%c", s[3]);
else if (s.equals("mva3a"))
return "mva03a";
else if (s.equals("mva5a"))
return "mva05a";
return s;
}
static Common::SeekableReadStream *_tryLoadCruMovieFile(const Std::string &filename, const char *extn) {
const Std::string path = Std::string::format("flics/%s.%s", filename.c_str(), extn);
auto *rs = new Common::File();
if (!rs->open(path.c_str())) {
// Try with a "0" in the name
const Std::string adjustedfn = Std::string::format("flics/0%s.%s", filename.c_str(), extn);
if (!rs->open(adjustedfn.c_str())) {
delete rs;
return nullptr;
}
}
return rs;
}
static Common::SeekableReadStream *_tryLoadCruAVI(const Std::string &filename) {
Common::SeekableReadStream *rs = _tryLoadCruMovieFile(filename, "avi");
if (!rs)
warning("movie %s not found", filename.c_str());
return rs;
}
// Convenience function that tries to open both TXT (No Remorse)
// and IFF (No Regret) subtitle formats.
static Common::SeekableReadStream *_tryLoadCruSubtitle(const Std::string &filename) {
Common::SeekableReadStream *txtfile = _tryLoadCruMovieFile(filename, "txt");
if (txtfile)
return txtfile;
return _tryLoadCruMovieFile(filename, "iff");
}
DEFINE_RUNTIME_CLASSTYPE_CODE(MovieGump)
MovieGump::MovieGump() : ModalGump(), _player(nullptr), _subtitleWidget(0), _lastFrameNo(-1) {
}
MovieGump::MovieGump(int width, int height, Common::SeekableReadStream *rs,
bool introMusicHack, bool noScale, const byte *overridePal,
uint32 flags, int32 layer)
: ModalGump(50, 50, width, height, 0, flags, layer), _subtitleWidget(0), _lastFrameNo(-1) {
uint32 stream_id = rs->readUint32BE();
rs->seek(-4, SEEK_CUR);
if (stream_id == 0x52494646) {// 'RIFF' - crusader AVIs
_player = new AVIPlayer(rs, width, height, overridePal, noScale);
} else {
_player = new SKFPlayer(rs, width, height, introMusicHack);
}
}
MovieGump::~MovieGump() {
delete _player;
}
void MovieGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
_player->start();
Mouse::get_instance()->pushMouseCursor(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);
}
void MovieGump::run() {
ModalGump::run();
_player->run();
// TODO: It would be nice to refactor this
AVIPlayer *aviplayer = dynamic_cast<AVIPlayer *>(_player);
if (aviplayer) {
// The AVI player can skip frame numbers, so search back from the
// last frame to make sure we don't miss subtitles
const int frameno = aviplayer->getFrameNo();
for (int f = _lastFrameNo + 1; f <= frameno; f++) {
if (_subtitles.contains(f)) {
TextWidget *subtitle = dynamic_cast<TextWidget *>(getGump(_subtitleWidget));
if (subtitle)
subtitle->Close();
// Create a new TextWidget. No Regret uses font 3
int subtitle_font = GAME_IS_REMORSE ? 4 : 3;
TextWidget *widget = new TextWidget(0, 0, _subtitles[f], true, subtitle_font, 640, 10);
widget->InitGump(this);
widget->setRelativePosition(BOTTOM_CENTER, 0, -10);
// Subtitles should be white.
widget->setBlendColour(TEX32_PACK_RGBA(0xFF, 0xFF, 0xFF, 0xFF));
_subtitleWidget = widget->getObjId();
}
}
_lastFrameNo = frameno;
}
if (!_player->isPlaying()) {
Close();
}
}
void MovieGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Gump::PaintThis(surf, lerp_factor, scaled);
_player->paint(surf, lerp_factor);
// If displaying subtitles, put a black box behind them. The box should be ~600px across.
if (_subtitleWidget) {
TextWidget *subtitle = dynamic_cast<TextWidget *>(getGump(_subtitleWidget));
if (subtitle) {
int32 x, y;
subtitle->getLocation(x, y);
Common::Rect32 textdims = subtitle->getDims();
Common::Rect32 screendims = surf->getSurfaceDims();
surf->fill32(TEX32_PACK_RGB(0, 0, 0),
screendims.width() / 2 - 300 - screendims.left,
y - 3,
600,
textdims.height() + 5);
}
}
}
bool MovieGump::OnKeyDown(int key, int mod) {
switch (key) {
case Common::KEYCODE_ESCAPE: {
Close();
}
break;
default:
break;
}
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;
if (GAME_IS_U8)
gump = new MovieGump(320, 200, rs, introMusicHack, noScale);
else
gump = new MovieGump(640, 480, rs, introMusicHack, noScale);
if (fade) {
FadeToModalProcess *p = new FadeToModalProcess(gump);
Kernel::get_instance()->addProcess(p);
return p->getPid();
}
else
{
gump->InitGump(nullptr);
gump->setRelativePosition(CENTER);
gump->CreateNotifier();
return gump->GetNotifyProcess()->getPid();
}
}
/*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;
}
void MovieGump::loadSubtitles(Common::SeekableReadStream *rs) {
if (!rs)
return;
const uint32 id = rs->readUint32BE();
rs->seek(0);
if (id == IFF_MAGIC) {
loadIFFSubs(rs);
} else {
loadTXTSubs(rs);
}
}
void MovieGump::loadTXTSubs(Common::SeekableReadStream *rs) {
int frameno = 0;
while (!rs->eos()) {
Common::String line = rs->readLine();
if (line.hasPrefix("@frame ")) {
if (frameno > 0) {
// two @frame directives in a row means that the last
// subtitle should be turned *off* at the first frame
_subtitles[frameno] = "";
}
frameno = atoi(line.c_str() + 7);
} else if (frameno >= 0) {
_subtitles[frameno] = line;
frameno = -1;
}
}
}
void MovieGump::loadIFFSubs(Common::SeekableReadStream *rs) {
uint32 magic = rs->readUint32BE();
if (magic != IFF_MAGIC) {
warning("Error loading IFF file, invalid magic.");
return;
}
rs->skip(2);
uint16 totalsize = rs->readUint16BE();
if (totalsize != rs->size() - rs->pos()) {
warning("Error loading IFF file: size invalid.");
return;
}
uint32 lang_magic = rs->readUint32BE();
if (lang_magic != IFF_LANG) {
warning("Error loading IFF file: invalid magic.");
return;
}
const Common::Language lang = Ultima8Engine::get_instance()->getLanguage();
while (rs->pos() < rs->size()) {
uint32 lang_code = rs->readUint32BE();
uint32 lang_len = rs->readUint32BE();
uint32 lang_end = rs->pos() + lang_len;
if ((lang == Common::FR_FRA && lang_code == IFF_LANG_FR)
|| (lang == Common::DE_DEU && lang_code == IFF_LANG_DE)
|| (lang == Common::EN_ANY && lang_code == IFF_LANG_EN)) {
while (rs->pos() < lang_end) {
// Take care of the mix of LE and BE.
uint16 frameoff = rs->readUint16LE();
rs->skip(1); // what's this?
uint32 slen = rs->readUint16BE();
const Common::String line = rs->readString('\0', slen);
_subtitles[frameoff] = line;
}
} else {
rs->skip(lang_len);
}
}
}
bool MovieGump::loadData(Common::ReadStream *rs) {
return false;
}
void MovieGump::saveData(Common::WriteStream *ws) {
}
uint32 MovieGump::I_playMovieOverlay(const uint8 *args,
unsigned int /*argsize*/) {
ARG_ITEM_FROM_PTR(item);
ARG_STRING(name);
ARG_UINT16(x);
ARG_UINT16(y);
PaletteManager *palman = PaletteManager::get_instance();
if (item && palman) {
if (name.hasPrefix("mva")) {
name = _fixCrusaderMovieName(name);
}
const Palette *pal = palman->getPalette(PaletteManager::Pal_Game);
assert(pal);
CruMovieViewer(name, x, y, pal->data(), nullptr, 52);
}
return 0;
}
uint32 MovieGump::I_playMovieCutscene(const uint8 *args, unsigned int /*argsize*/) {
ARG_ITEM_FROM_PTR(item);
ARG_STRING(name);
ARG_UINT16(x);
ARG_UINT16(y);
if (item) {
CruMovieViewer(name, x * 3, y * 3, nullptr, nullptr, 0);
}
return 0;
}
uint32 MovieGump::I_playMovieCutsceneAlt(const uint8 *args, unsigned int /*argsize*/) {
ARG_ITEM_FROM_PTR(item); // TODO: Unused? Center on this first?
ARG_STRING(name);
ARG_UINT16(x);
ARG_UINT16(y);
if (!x)
x = 640;
else
x *= 3;
if (!y)
y = 480;
else
y *= 3;
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, 0);
return 0;
}
uint32 MovieGump::I_playMovieCutsceneRegret(const uint8 *args, unsigned int /*argsize*/) {
ARG_STRING(name);
ARG_UINT8(fade);
warning("MovieGump::I_playMovieCutsceneRegret: TODO: use fade argument %d", fade);
CruMovieViewer(name, 640, 480, nullptr, nullptr, 0);
return 0;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,97 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_MOVIEGUMP_H
#define ULTIMA8_GUMPS_MOVIEGUMP_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class RawArchive;
class MoviePlayer;
class MovieGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
MovieGump();
MovieGump(int width, int height, Common::SeekableReadStream *rs,
bool introMusicHack = false, bool noScale = false,
const byte *overridePal = nullptr,
uint32 flags = FLAG_PREVENT_SAVE, int32 layer = LAYER_MODAL);
~MovieGump() override;
void InitGump(Gump *newparent, bool take_focus = true) override;
void Close(bool no_del = false) override;
void run() override;
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
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, uint16 frameshape);
bool loadData(Common::ReadStream *rs);
void saveData(Common::WriteStream *ws) override;
INTRINSIC(I_playMovieOverlay);
INTRINSIC(I_playMovieCutscene);
INTRINSIC(I_playMovieCutsceneAlt);
INTRINSIC(I_playMovieCutsceneRegret);
protected:
MoviePlayer *_player;
/// Load subtitles with format detection
void loadSubtitles(Common::SeekableReadStream *rs);
/// Load subtitles from a txt file (No Remorse format)
void loadTXTSubs(Common::SeekableReadStream *rs);
/// 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();
/// Subtitles, by frame number. Only used for Crusader movies.
Common::HashMap<int, Common::String> _subtitles;
/// Last widget used for displaying subtitles.
uint16 _subtitleWidget;
/// Last frame that was displayed, so we can catch up subtitles.
int _lastFrameNo;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,186 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/events.h"
#include "ultima/ultima8/gumps/paged_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(PagedGump)
PagedGump::PagedGump(int left, int right, int top, int shape):
ModalGump(0, 0, 5, 5), _leftOff(left), _rightOff(right), _topOff(top),
_gumpShape(shape), _nextButton(nullptr), _prevButton(nullptr),
_current(0), _buttonsEnabled(true) {
}
PagedGump::~PagedGump(void) {
}
void PagedGump::Close(bool no_del) {
Mouse::get_instance()->popMouseCursor();
for (auto *g : _gumps) {
g->Close(no_del); // CHECKME: no_del?
}
ModalGump::Close(no_del);
}
static const int pageOverShape = 34;
void PagedGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
_shape = GameData::get_instance()->getGumps()->getShape(_gumpShape);
UpdateDimsFromShape();
FrameID buttonleft(GameData::GUMPS, pageOverShape, 0);
FrameID buttonright(GameData::GUMPS, pageOverShape, 1);
//!! Hardcoded gump
_nextButton = new ButtonWidget(0, 0, buttonright, buttonright, false,
LAYER_ABOVE_NORMAL);
_nextButton->InitGump(this);
_nextButton->setRelativePosition(TOP_RIGHT, _rightOff, _topOff);
_prevButton = new ButtonWidget(0, 0, buttonleft, buttonleft, false,
LAYER_ABOVE_NORMAL);
_prevButton->InitGump(this);
_prevButton->setRelativePosition(TOP_LEFT, _leftOff, _topOff);
_prevButton->HideGump();
Mouse *mouse = Mouse::get_instance();
mouse->pushMouseCursor(Mouse::MOUSE_HAND);
}
void PagedGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Gump::PaintThis(surf, lerp_factor, scaled);
}
void PagedGump::onMouseDouble(int button, int32 mx, int32 my) {
Close();
}
bool PagedGump::OnKeyDown(int key, int mod) {
if (_current < _gumps.size())
if (_gumps[_current]->OnKeyDown(key, mod))
return true;
switch (key) {
case Common::KEYCODE_ESCAPE:
Close();
return true;
default:
break;
}
return true;
}
void PagedGump::ChildNotify(Gump *child, uint32 message) {
if (!_buttonsEnabled) return;
if (_gumps.empty()) return;
ObjId cid = child->getObjId();
if (message == ButtonWidget::BUTTON_UP) {
if (cid == _nextButton->getObjId()) {
if (_current + 1 < _gumps.size()) {
_gumps[_current]->HideGump();
++_current;
_gumps[_current]->UnhideGump();
_gumps[_current]->MakeFocus();
if (_current + 1 == _gumps.size())
_nextButton->HideGump();
_prevButton->UnhideGump();
}
} else if (cid == _prevButton->getObjId()) {
if (_current > 0) {
_gumps[_current]->HideGump();
--_current;
_gumps[_current]->UnhideGump();
_gumps[_current]->MakeFocus();
if (_current == 0)
_prevButton->HideGump();
_nextButton->UnhideGump();
}
}
}
}
void PagedGump::addPage(Gump *g) {
assert(g->GetParent() == this);
g->setRelativePosition(TOP_CENTER, 0, 3 + _topOff);
g->HideGump();
_gumps.push_back(g);
_current = 0;
_gumps[_current]->UnhideGump();
if (_focusChild != _gumps[_current])
_gumps[_current]->MakeFocus();
if (_current + 1 == _gumps.size())
_nextButton->HideGump();
else
_nextButton->UnhideGump();
}
void PagedGump::showPage(uint index) {
if (index >= _gumps.size())
return;
_gumps[_current]->HideGump();
_current = index;
_gumps[_current]->UnhideGump();
_gumps[_current]->MakeFocus();
if (_current + 1 == _gumps.size())
_nextButton->HideGump();
else
_nextButton->UnhideGump();
if (_current == 0)
_prevButton->HideGump();
else
_prevButton->UnhideGump();
}
bool PagedGump::loadData(Common::ReadStream *rs) {
warning("Trying to load ModalGump");
return false;
}
void PagedGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,75 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_PAGEDGUMP_H
#define ULTIMA8_GUMPS_PAGEDGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* Base class for gumps which have multiple pages (books, save/load game)
*/
class PagedGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
PagedGump(int left, int right, int top, int shape);
~PagedGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
void Close(bool no_del = false) override;
// Paint the Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
void onMouseDouble(int button, int32 mx, int32 my) override;
bool OnKeyDown(int key, int mod) override;
void ChildNotify(Gump *child, uint32 message) override;
//! add a page. Note: g already has to be a child gump.
void addPage(Gump *g);
void showPage(uint index);
void enableButtons(bool enabled) {
_buttonsEnabled = enabled;
}
bool loadData(Common::ReadStream *rs);
void saveData(Common::WriteStream *ws) override;
protected:
int _leftOff, _rightOff, _topOff, _gumpShape;
Common::Array<Gump *> _gumps;
Gump *_nextButton;
Gump *_prevButton;
uint _current;
bool _buttonsEnabled;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,450 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/paperdoll_gump.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/world/actors/actor.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/main_shape_archive.h"
#include "ultima/ultima8/gfx/fonts/font.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/gumps/mini_stats_gump.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(PaperdollGump)
// lots of CONSTANTS...
const struct equipcoords_struct {
int x, y;
} equipcoords[] = {
{ 0, 0 },
{ 24, 60 }, // shield
{ 36, 50 }, // arm
{ 40, 26 }, // head
{ 40, 63 }, // body
{ 40, 92 }, // legs
{ 16, 18 } // weapon
};
const struct statcords_struct {
int xd, x, y;
} statcoords[] = {
{ 90, 130, 24 },
{ 90, 130, 33 },
{ 90, 130, 42 },
{ 90, 130, 51 },
{ 90, 130, 60 },
{ 90, 130, 69 },
{ 90, 130, 78 }
};
static const int statdescwidth = 29;
static const int statwidth = 15;
static const int statheight = 8;
static const int statfont = 7;
static const int statdescfont = 0;
static const int statbuttonshape = 38;
static const int statbuttonx = 81;
static const int statbuttony = 84;
PaperdollGump::PaperdollGump() : ContainerGump(), _statButtonId(0),
_draggingArmourClass(0), _draggingWeight(0),
_backpackRect(49, 25, 59, 50) {
Common::fill(_cachedText, _cachedText + 14, (RenderedText *)nullptr);
Common::fill(_cachedVal, _cachedVal + 7, 0);
}
PaperdollGump::PaperdollGump(const Shape *shape, uint32 frameNum, uint16 owner,
uint32 Flags, int32 layer)
: ContainerGump(shape, frameNum, owner, Flags, layer),
_statButtonId(0), _draggingArmourClass(0), _draggingWeight(0),
_backpackRect(49, 25, 59, 50) {
_statButtonId = 0;
Common::fill(_cachedText, _cachedText + 14, (RenderedText *)nullptr);
Common::fill(_cachedVal, _cachedVal + 7, 0);
}
PaperdollGump::~PaperdollGump() {
for (int i = 0; i < 14; ++i) { // ! constant
delete _cachedText[i];
}
}
void PaperdollGump::InitGump(Gump *newparent, bool take_focus) {
ContainerGump::InitGump(newparent, take_focus);
FrameID button_up(GameData::GUMPS, statbuttonshape, 0);
FrameID button_down(GameData::GUMPS, statbuttonshape, 1);
Gump *widget = new ButtonWidget(statbuttonx, statbuttony,
button_up, button_down);
_statButtonId = widget->getObjId();
widget->InitGump(this);
}
void PaperdollGump::Close(bool no_del) {
// NOTE: this does _not_ call its direct parent's Close function
// because we do not want to close the Gumps of our contents.
// Make every item leave the fast area
Container *c = getContainer(_owner);
if (!c) return; // Container gone!?
for (auto *item : c->_contents) {
item->leaveFastArea(); // Can destroy the item
}
Item *o = getItem(_owner);
if (o)
o->clearGump(); //!! is this the appropriate place?
ItemRelativeGump::Close(no_del);
}
void PaperdollGump::PaintStat(RenderSurface *surf, unsigned int n,
Std::string text, int val) {
assert(n < 7); // constant!
Font *font, *descfont;
font = FontManager::get_instance()->getGameFont(statfont);
descfont = FontManager::get_instance()->getGameFont(statdescfont);
char buf[16]; // enough for uint32
unsigned int remaining;
if (!_cachedText[2 * n])
_cachedText[2 * n] = descfont->renderText(text, remaining,
statdescwidth, statheight,
Font::TEXT_RIGHT);
_cachedText[2 * n]->draw(surf, statcoords[n].xd, statcoords[n].y);
if (!_cachedText[2 * n + 1] || _cachedVal[n] != val) {
delete _cachedText[2 * n + 1];
Common::sprintf_s(buf, "%d", val);
_cachedText[2 * n + 1] = font->renderText(buf, remaining,
statwidth, statheight,
Font::TEXT_RIGHT);
_cachedVal[n] = val;
}
_cachedText[2 * n + 1]->draw(surf, statcoords[n].x, statcoords[n].y);
}
void PaperdollGump::PaintStats(RenderSurface *surf, int32 lerp_factor) {
Actor *a = getActor(_owner);
assert(a);
int armour = a->getArmourClass();
int weight = a->getTotalWeight();
if (_displayDragging) {
armour += _draggingArmourClass;
weight += _draggingWeight;
}
PaintStat(surf, 0, _TL_("STR"), a->getStr());
PaintStat(surf, 1, _TL_("INT"), a->getInt());
PaintStat(surf, 2, _TL_("DEX"), a->getDex());
PaintStat(surf, 3, _TL_("ARMR"), armour);
PaintStat(surf, 4, _TL_("HITS"), a->getHP());
PaintStat(surf, 5, _TL_("MANA"), a->getMana());
PaintStat(surf, 6, _TL_("WGHT"), weight / 10);
}
void PaperdollGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
// paint self
ItemRelativeGump::PaintThis(surf, lerp_factor, scaled);
Actor *a = getActor(_owner);
if (!a) {
// Actor gone!?
Close();
return;
}
PaintStats(surf, lerp_factor);
for (int i = 6; i >= 1; --i) { // constants
Item *item = getItem(a->getEquip(i));
if (!item) continue;
int32 itemx, itemy;
uint32 frame = item->getFrame() + 1;
itemx = equipcoords[i].x;
itemy = equipcoords[i].y;
itemx += _itemArea.left;
itemy += _itemArea.top;
const Shape *s = item->getShapeObject();
assert(s);
surf->Paint(s, frame, itemx, itemy);
}
if (_displayDragging) {
int32 itemx, itemy;
itemx = _draggingX + _itemArea.left;
itemy = _draggingY + _itemArea.top;
Shape *s = GameData::get_instance()->getMainShapes()->
getShape(_draggingShape);
assert(s);
surf->PaintInvisible(s, _draggingFrame, itemx, itemy, false, (_draggingFlags & Item::FLG_FLIPPED) != 0);
}
}
// Find object (if any) at (mx,my)
// (mx,my) are relative to parent
uint16 PaperdollGump::TraceObjId(int32 mx, int32 my) {
uint16 objId_ = Gump::TraceObjId(mx, my);
if (objId_ && objId_ != 65535) return objId_;
ParentToGump(mx, my);
Actor *a = getActor(_owner);
if (!a)
return 0; // Container gone!?
for (int i = 1; i <= 6; ++i) {
Item *item = getItem(a->getEquip(i));
if (!item) continue;
int32 itemx, itemy;
itemx = equipcoords[i].x;
itemy = equipcoords[i].y;
itemx += _itemArea.left;
itemy += _itemArea.top;
const Shape *s = item->getShapeObject();
assert(s);
const ShapeFrame *frame = s->getFrame(item->getFrame() + 1);
if (frame->hasPoint(mx - itemx, my - itemy)) {
// found it
return item->getObjId();
}
}
// try backpack
if (_backpackRect.contains(mx - _itemArea.left, my - _itemArea.top)) {
ObjId bp = a->getEquip(ShapeInfo::SE_BACKPACK);
if (bp)
return bp;
}
// didn't find anything, so return self
return getObjId();
}
// get item coords relative to self
bool PaperdollGump::GetLocationOfItem(uint16 itemid, int32 &gx, int32 &gy,
int32 lerp_factor) {
Item *item = getItem(itemid);
if (!item)
return false; // item gone - shouldn't happen?
Item *parent = item->getParentAsContainer();
if (!parent || parent->getObjId() != _owner)
return false;
//!!! need to use lerp_factor
if (item->getShape() == 529) { //!! constant
gx = _backpackRect.left;
gy = _backpackRect.top;
} else {
int equiptype = item->getZ();
assert(equiptype >= 0 && equiptype <= 6); //!! constants
gx = equipcoords[equiptype].x;
gy = equipcoords[equiptype].y;
}
gx += _itemArea.left;
gy += _itemArea.top;
return true;
}
bool PaperdollGump::StartDraggingItem(Item *item, int mx, int my) {
// can't drag backpack
if (item->getShape() == 529) { //!! constant
return false;
}
bool ret = ContainerGump::StartDraggingItem(item, mx, my);
// set dragging offset to center of item
const Shape *s = item->getShapeObject();
assert(s);
const ShapeFrame *frame = s->getFrame(item->getFrame());
assert(frame);
Mouse::get_instance()->setDraggingOffset(frame->_width / 2 - frame->_xoff,
frame->_height / 2 - frame->_yoff);
// Remove equipment and clear owner on drag start for better drag feedback
// NOTE: This original game appears to equip/unequip the item during drag instead of on drop
if (_owner == item->getParent() && item->hasFlags(Item::FLG_EQUIPPED)) {
Actor *a = getActor(_owner);
if (a && a->removeItem(item)) {
item->setParent(0);
}
}
return ret;
}
bool PaperdollGump::DraggingItem(Item *item, int mx, int my) {
if (!_itemArea.contains(mx, my)) {
_displayDragging = false;
return false;
}
Actor *a = getActor(_owner);
assert(a);
bool over_backpack = false;
Container *backpack = getContainer(a->getEquip(7)); // constant!
if (backpack && _backpackRect.contains(mx - _itemArea.left, my - _itemArea.top)) {
over_backpack = true;
}
_displayDragging = true;
_draggingShape = item->getShape();
_draggingFrame = item->getFrame();
_draggingFlags = item->getFlags();
_draggingArmourClass = 0;
_draggingWeight = 0;
Container *root = item->getRootContainer();
if (!root || root->getObjId() != _owner)
_draggingWeight = item->getWeight();
const ShapeInfo *si = item->getShapeInfo();
int equiptype = si->_equipType;
// determine target location and set dragging_x/y
if (!over_backpack && equiptype) {
// check if item will fit (weight/volume/etc...)
if (!a->CanAddItem(item, true)) {
_displayDragging = false;
return false;
}
if (si->_armourInfo) {
_draggingArmourClass += si->_armourInfo[_draggingFrame]._armourClass;
}
if (si->_weaponInfo) {
_draggingArmourClass += si->_weaponInfo->_armourBonus;
}
_draggingFrame++;
_draggingX = equipcoords[equiptype].x;
_draggingY = equipcoords[equiptype].y;
} else {
// drop in backpack
if (backpack && !backpack->CanAddItem(item, true)) {
_displayDragging = false;
return false;
}
_draggingX = _backpackRect.left + _backpackRect.width() / 2;
_draggingY = _backpackRect.top + _backpackRect.height() / 2;
}
return true;
}
void PaperdollGump::DropItem(Item *item, int mx, int my) {
_displayDragging = false;
_draggingArmourClass = 0;
_draggingWeight = 0;
Actor *a = getActor(_owner);
assert(a);
bool over_backpack = false;
Container *backpack = getContainer(a->getEquip(7)); // constant!
if (backpack && _backpackRect.contains(mx - _itemArea.left, my - _itemArea.top)) {
over_backpack = true;
}
int equiptype = item->getShapeInfo()->_equipType;
if (!over_backpack && equiptype) {
item->moveToContainer(a);
} else {
item->moveToContainer(backpack);
item->randomGumpLocation();
}
}
void PaperdollGump::ChildNotify(Gump *child, uint32 message) {
if (child->getObjId() == _statButtonId &&
(message == ButtonWidget::BUTTON_CLICK || message == ButtonWidget::BUTTON_DOUBLE)) {
// check if there already is an open MiniStatsGump
Gump *desktop = Ultima8Engine::get_instance()->getDesktopGump();
Gump *statsgump = desktop->FindGump<MiniStatsGump>();
if (!statsgump) {
Gump *gump = new MiniStatsGump(0, 0);
gump->InitGump(0);
gump->setRelativePosition(BOTTOM_RIGHT, -5, -5);
} else {
// check if it is off-screen. If so, move it back
Common::Rect32 rect = desktop->getDims();
Common::Rect32 sr = statsgump->getDims();
sr.grow(-2);
statsgump->GumpRectToScreenSpace(sr);
if (!sr.intersects(rect))
statsgump->setRelativePosition(BOTTOM_RIGHT, -5, -5);
}
}
}
void PaperdollGump::saveData(Common::WriteStream *ws) {
ContainerGump::saveData(ws);
ws->writeUint16LE(_statButtonId);
}
bool PaperdollGump::loadData(Common::ReadStream *rs, uint32 version) {
if (!ContainerGump::loadData(rs, version)) return false;
_statButtonId = rs->readUint16LE();
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,95 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_PAPERDOLLGUMP_H
#define ULTIMA8_GUMPS_PAPERDOLLGUMP_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/container_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class RenderedText;
/**
* The gump activated by 'Z', which shows the avatar with current armor, and stats
*/
class PaperdollGump : public ContainerGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
PaperdollGump();
PaperdollGump(const Shape *shape, uint32 frameNum, uint16 owner,
uint32 flags = FLAG_DRAGGABLE, int32 layer = LAYER_NORMAL);
~PaperdollGump() override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
// Close the gump
void Close(bool no_del = false) override;
// Paint this Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
void ChildNotify(Gump *child, uint32 message) override;
// Trace a click, and return ObjId
uint16 TraceObjId(int32 mx, int32 my) override;
// Get the location of an item in the gump (coords relative to this).
// Returns false on failure.
bool GetLocationOfItem(uint16 itemid, int32 &gx, int32 &gy,
int32 lerp_factor = 256) override;
bool StartDraggingItem(Item *item, int mx, int my) override;
bool DraggingItem(Item *item, int mx, int my) override;
void DropItem(Item *item, int mx, int my) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
protected:
//! Paint the stats
void PaintStats(RenderSurface *, int32 lerp_factor);
//! Paint a single stat
void PaintStat(RenderSurface *surf, unsigned int n,
Std::string text, int val);
RenderedText *_cachedText[14]; // constant!!
int _cachedVal[7]; // constant!!
uint16 _statButtonId;
uint32 _draggingArmourClass;
uint32 _draggingWeight;
private:
const Common::Rect32 _backpackRect;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,206 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/gumps/quit_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(QuitGump)
static const int u8GumpShape = 17;
static const int u8AskShapeId = 18;
static const int u8YesShapeId = 47;
static const int u8NoShapeId = 50;
static const int remGumpShape = 21;
static const int remAskShapeId = 0;
static const int remYesShapeId = 19;
static const int remNoShapeId = 20;
static const int remQuitSound = 0x109;
static const int regGumpShape = 21;
static const int regAskShapeId = 0;
static const int regYesShapeId = 19;
static const int regNoShapeId = 20;
static const int regQuitSound = 0; // TODO: Work out what sound id
QuitGump::QuitGump(): ModalGump(0, 0, 5, 5), _yesWidget(0), _noWidget(0) {
Mouse *mouse = Mouse::get_instance();
mouse->pushMouseCursor(Mouse::MOUSE_HAND);
if (GAME_IS_U8) {
_gumpShape = u8GumpShape;
_askShape = u8AskShapeId;
_yesShape = u8YesShapeId;
_noShape = u8NoShapeId;
_buttonXOff = 16;
_buttonYOff = 38;
_playSound = 0;
} else if (GAME_IS_REMORSE) {
_gumpShape = remGumpShape;
_askShape = remAskShapeId;
_yesShape = remYesShapeId;
_noShape = remNoShapeId;
_buttonXOff = 55;
_buttonYOff = 47;
_playSound = remQuitSound;
} else if (GAME_IS_REGRET) {
_gumpShape = regGumpShape;
_askShape = regAskShapeId;
_yesShape = regYesShapeId;
_noShape = regNoShapeId;
// TODO: These are pretty approximate, need adjusting.
_buttonXOff = 50;
_buttonYOff = 70;
_playSound = regQuitSound;
} else {
error("unsupported game type");
}
}
QuitGump::~QuitGump() {
Mouse::get_instance()->popMouseCursor();
}
void QuitGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
_shape = GameData::get_instance()->getGumps()->getShape(_gumpShape);
UpdateDimsFromShape();
if (_askShape != 0) {
FrameID askshape(GameData::GUMPS, _askShape, 0);
askshape = _TL_SHP_(askshape);
if (askshape._shapeNum == 0) {
// In JP U8, the ask gump is replaced with text
// confirming quit
Std::string askstr = _TL_("Quit the game?");
Gump *widget = new TextWidget(0, 0, askstr, true, 6); // CONSTANT!
widget->InitGump(this, false);
widget->setRelativePosition(TOP_CENTER, 0, 13);
} else {
const Shape *askShape = GameData::get_instance()->getShape(askshape);
const ShapeFrame *sf = askShape->getFrame(askshape._frameNum);
assert(sf);
Gump *ask = new Gump(0, 0, sf->_width, sf->_height);
ask->SetShape(askShape, askshape._frameNum);
ask->InitGump(this);
ask->setRelativePosition(TOP_CENTER, 0, 5);
}
}
FrameID yesbutton_up(GameData::GUMPS, _yesShape, 0);
FrameID yesbutton_down(GameData::GUMPS, _yesShape, 1);
yesbutton_up = _TL_SHP_(yesbutton_up);
yesbutton_down = _TL_SHP_(yesbutton_down);
Gump *widget;
widget = new ButtonWidget(0, 0, yesbutton_up, yesbutton_down);
widget->InitGump(this);
widget->setRelativePosition(TOP_LEFT, _buttonXOff, _buttonYOff);
_yesWidget = widget->getObjId();
FrameID nobutton_up(GameData::GUMPS, _noShape, 0);
FrameID nobutton_down(GameData::GUMPS, _noShape, 1);
nobutton_up = _TL_SHP_(nobutton_up);
nobutton_down = _TL_SHP_(nobutton_down);
widget = new ButtonWidget(0, 0, nobutton_up, nobutton_down);
widget->InitGump(this);
widget->setRelativePosition(TOP_RIGHT, -(int)_buttonXOff, _buttonYOff);
_noWidget = widget->getObjId();
if (_playSound) {
AudioProcess *audioproc = AudioProcess::get_instance();
audioproc->playSFX(_playSound, 0x10, _objId, 1);
}
}
void QuitGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Gump::PaintThis(surf, lerp_factor, scaled);
}
bool QuitGump::OnKeyDown(int key, int mod) {
switch (key) {
case Common::KEYCODE_ESCAPE: {
Close();
}
break;
default:
break;
}
return true;
}
void QuitGump::ChildNotify(Gump *child, uint32 message) {
ObjId cid = child->getObjId();
if (message == ButtonWidget::BUTTON_CLICK || message == ButtonWidget::BUTTON_DOUBLE) {
if (cid == _yesWidget) {
Ultima8Engine::get_instance()->quitGame();
} else if (cid == _noWidget) {
Close();
}
}
}
bool QuitGump::OnTextInput(int unicode) {
if (!(unicode & 0xFF80)) {
char c = unicode & 0x7F;
if (_TL_("Yy").find(c) != Std::string::npos) {
Ultima8Engine::get_instance()->quitGame();
} else if (_TL_("Nn").find(c) != Std::string::npos) {
Close();
}
}
return true;
}
//static
void QuitGump::verifyQuit() {
ModalGump *gump = new QuitGump();
gump->InitGump(0);
gump->setRelativePosition(CENTER);
}
bool QuitGump::loadData(Common::ReadStream *rs) {
warning("Trying to load ModalGump");
return true;
}
void QuitGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,69 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_QUITGUMP_H
#define ULTIMA8_GUMPS_QUITGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* The "are you sure you want to quit?" gump
*/
class QuitGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
QuitGump();
~QuitGump() override;
void InitGump(Gump *newparent, bool take_focus = true) override;
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 void verifyQuit();
bool loadData(Common::ReadStream *rs);
void saveData(Common::WriteStream *ws) override;
protected:
ObjId _yesWidget, _noWidget;
uint32 _gumpShape; //! shape number for the dialog
uint32 _yesShape; //! shape number for "yes" button
uint32 _noShape; //! shape number for "no" button
uint32 _askShape; //! shape number for "are you sure?"
uint32 _buttonXOff; //! x offset from either edge of yes/no buttons
uint32 _buttonYOff; //! y offset from bottom of yes/no buttons
uint32 _playSound; //! sound to play on open
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,128 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/readable_gump.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/usecode/uc_machine.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(ReadableGump)
const int jpsub_font = 6;
ReadableGump::ReadableGump()
: ModalGump(), _shapeNum(0), _fontNum(0) {
}
ReadableGump::ReadableGump(ObjId owner, uint16 shape, int font, const Std::string &msg) :
ModalGump(0, 0, 100, 100, owner), _shapeNum(shape), _fontNum(font), _text(msg) {
}
ReadableGump::~ReadableGump(void) {
}
void ReadableGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
const Shape *shape = GameData::get_instance()->getGumps()->getShape(_shapeNum);
SetShape(shape, 0);
UpdateDimsFromShape();
if (Ultima8Engine::get_instance()->getGameInfo()->_language ==
GameInfo::GAMELANG_JAPANESE) {
// Japanese subtitles
Std::string::size_type pos;
pos = _text.find('%');
if (pos != Std::string::npos) {
Std::string jpsub = _text.substr(pos + 1);
_text = _text.substr(0, pos);
Gump *subwidget = new TextWidget(0, 0, jpsub, true, jpsub_font, 0, 0, Font::TEXT_CENTER);
subwidget->InitGump(this);
subwidget->setRelativePosition(BOTTOM_CENTER, 0, -8);
}
}
Gump *widget = new TextWidget(0, 0, _text, true, _fontNum, _dims.width() - 16, 0, Font::TEXT_CENTER);
widget->InitGump(this);
widget->setRelativePosition(CENTER);
}
Gump *ReadableGump::onMouseDown(int button, int32 mx, int32 my) {
Close();
return this;
}
bool ReadableGump::OnKeyDown(int key, int mod) {
Close();
return true;
}
uint32 ReadableGump::I_readGrave(const uint8 *args, unsigned int /*argsize*/) {
ARG_ITEM_FROM_PTR(item);
ARG_UINT16(shape);
ARG_STRING(str);
assert(item);
Gump *gump = new ReadableGump(item->getObjId(), shape, 11, str);
gump->InitGump(0);
gump->setRelativePosition(CENTER);
return gump->GetNotifyProcess()->getPid();
}
uint32 ReadableGump::I_readPlaque(const uint8 *args, unsigned int /*argsize*/) {
ARG_ITEM_FROM_PTR(item);
ARG_UINT16(shape);
ARG_STRING(str);
assert(item);
Gump *gump = new ReadableGump(item->getObjId(), shape, 10, str);
gump->InitGump(0);
gump->setRelativePosition(CENTER);
return gump->GetNotifyProcess()->getPid();
}
void ReadableGump::saveData(Common::WriteStream *ws) {
warning("Trying to load ModalGump");
}
bool ReadableGump::loadData(Common::ReadStream *rs, uint32 version) {
warning("Trying to load ModalGump");
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,64 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_READABLEGUMP_H
#define ULTIMA8_GUMPS_READABLEGUMP_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* The gump for popping up text in the game you can read (plaques and gravestones)
*/
class ReadableGump : public ModalGump {
uint16 _shapeNum;
int _fontNum;
Std::string _text;
public:
ENABLE_RUNTIME_CLASSTYPE()
ReadableGump();
ReadableGump(ObjId owner, uint16 shape, int font, const Std::string &msg);
~ReadableGump() override;
// Close on mouse click on key press
Gump *onMouseDown(int button, int32 mx, int32 my) override;
bool OnKeyDown(int key, int mod) override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
INTRINSIC(I_readGrave);
INTRINSIC(I_readPlaque);
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,154 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/resizable_gump.h"
#include "ultima/ultima8/kernel/mouse.h"
namespace Ultima {
namespace Ultima8 {
#define RESIZE_BORDER 5
ResizableGump::ResizableGump(int x, int y, int width, int height)
: Gump(x, y, width, height, 0, FLAG_DRAGGABLE, LAYER_NORMAL),
_dragPosition(Gump::CENTER), _mousePosition(Gump::CENTER), _minWidth(20), _minHeight(20) {
}
ResizableGump::ResizableGump() : Gump(),
_dragPosition(Gump::CENTER), _mousePosition(Gump::CENTER), _minWidth(20), _minHeight(20) {
}
ResizableGump::~ResizableGump() {
}
Gump *ResizableGump::onMouseMotion(int32 mx, int32 my) {
_mousePosition = getPosition(mx, my);
return Gump::onMouseMotion(mx, my);
}
void ResizableGump::onMouseLeft() {
_mousePosition = Gump::CENTER;
}
bool ResizableGump::onDragStart(int32 mx, int32 my) {
if (Gump::onDragStart(mx, my)) {
_dragPosition = getPosition(mx, my);
return true;
}
return false;
}
void ResizableGump::onDragStop(int32 mx, int32 my) {
_dragPosition = Gump::CENTER;
}
void ResizableGump::onDrag(int32 mx, int32 my) {
int32 x = _x;
int32 y = _y;
int32 w = _dims.width();
int32 h = _dims.height();
int32 dx, dy;
Mouse::get_instance()->getDraggingOffset(dx, dy);
int32 px = mx, py = my;
ParentToGump(px, py);
switch (_dragPosition) {
case Gump::CENTER:
x = mx - dx;
y = my - dy;
break;
case Gump::TOP_LEFT:
w -= px - dx;
h -= py - dy;
x = mx - dx;
y = my - dy;
break;
case Gump::TOP_RIGHT:
w = px;
h -= py - dy;
y = my - dy;
break;
case Gump::BOTTOM_LEFT:
w -= px - dx;
h = py;
x = mx - dx;
break;
case Gump::BOTTOM_RIGHT:
w = px;
h = py;
break;
case Gump::TOP_CENTER:
h -= py - dy;
y = my - dy;
break;
case Gump::BOTTOM_CENTER:
h = py;
break;
case Gump::LEFT_CENTER:
w -= px - dx;
x = mx - dx;
break;
case Gump::RIGHT_CENTER:
w = px;
break;
default:
break;
}
if (w >= _minWidth) {
_dims.setWidth(w);
_x = x;
}
if (h >= _minHeight) {
_dims.setHeight(h);
_y = y;
}
}
Gump::Position ResizableGump::getPosition(int32 mx, int32 my) {
Gump::Position position = Gump::CENTER;
ParentToGump(mx, my);
if (mx < _dims.left + RESIZE_BORDER && my < _dims.top + RESIZE_BORDER) {
position = Gump::TOP_LEFT;
} else if (mx >= _dims.right - RESIZE_BORDER && my < _dims.top + RESIZE_BORDER) {
position = Gump::TOP_RIGHT;
} else if (mx < _dims.left + RESIZE_BORDER && my >= _dims.bottom - RESIZE_BORDER) {
position = Gump::BOTTOM_LEFT;
} else if (mx >= _dims.right - RESIZE_BORDER && my >= _dims.bottom - RESIZE_BORDER) {
position = Gump::BOTTOM_RIGHT;
} else if (my < _dims.top + RESIZE_BORDER) {
position = Gump::TOP_CENTER;
} else if (my >= _dims.bottom - RESIZE_BORDER) {
position = Gump::BOTTOM_CENTER;
} else if (mx < _dims.left + RESIZE_BORDER) {
position = Gump::LEFT_CENTER;
} else if (mx >= _dims.right - RESIZE_BORDER) {
position = Gump::RIGHT_CENTER;
}
return position;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,64 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_RESIZABLEGUMP_H
#define ULTIMA8_GUMPS_RESIZABLEGUMP_H
#include "ultima/ultima8/gumps/gump.h"
namespace Ultima {
namespace Ultima8 {
/**
* Base class for gumps that can doesn't have a static size.
* An example of such would be the Console and the GameMap gumps
*/
class ResizableGump : public Gump {
protected:
Gump::Position _dragPosition, _mousePosition;
int32 _minWidth;
int32 _minHeight;
public:
ResizableGump();
ResizableGump(int x, int y, int width, int height);
~ResizableGump() override;
void setMinSize(int minWidth, int minHeight) {
_minWidth = minWidth;
_minHeight = minHeight;
}
Gump *onMouseMotion(int32 mx, int32 my) override;
void onMouseLeft() override;
bool onDragStart(int32 mx, int32 my) override;
void onDragStop(int32 mx, int32 my) override;
void onDrag(int32 mx, int32 my) override;
private:
Gump::Position getPosition(int32 mx, int32 my);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,122 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/keyboard.h"
#include "ultima/ultima8/gumps/scroll_gump.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/usecode/uc_machine.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(ScrollGump)
// TODO: Remove all the hacks
ScrollGump::ScrollGump()
: ModalGump(), _textWidget(0) {
}
ScrollGump::ScrollGump(ObjId owner, const Std::string &msg) :
ModalGump(0, 0, 100, 100, owner), _text(msg), _textWidget(0) {
}
ScrollGump::~ScrollGump(void) {
}
void ScrollGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
// Create the TextWidget
Gump *widget = new TextWidget(22, 29, _text, true, 9, 204, 115, Font::TEXT_LEFT, true); //!! constants
widget->InitGump(this);
_textWidget = widget->getObjId();
_text.clear(); // no longer need this
const Shape *shape = GameData::get_instance()->getGumps()->getShape(19);
SetShape(shape, 0);
UpdateDimsFromShape();
}
void ScrollGump::NextText() {
TextWidget *widget = dynamic_cast<TextWidget *>(getGump(_textWidget));
assert(widget);
if (!widget->setupNextText()) {
Close();
}
}
void ScrollGump::onMouseClick(int button, int32 mx, int32 my) {
// Scroll to next _text, if possible
NextText();
}
void ScrollGump::onMouseDouble(int button, int32 mx, int32 my) {
Close();
}
bool ScrollGump::OnKeyDown(int key, int mod) {
switch (key) {
case Common::KEYCODE_ESCAPE:
Close();
break;
case Common::KEYCODE_SPACE:
NextText();
break;
default:
break;
}
return true;
}
uint32 ScrollGump::I_readScroll(const uint8 *args, unsigned int /*argsize*/) {
ARG_ITEM_FROM_PTR(item);
ARG_STRING(str);
assert(item);
Gump *gump = new ScrollGump(item->getObjId(), str);
gump->InitGump(0);
gump->setRelativePosition(CENTER);
return gump->GetNotifyProcess()->getPid();
}
void ScrollGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
bool ScrollGump::loadData(Common::ReadStream *rs, uint32 version) {
warning("Trying to load ModalGump");
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,70 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_SCROLLGUMP_H
#define ULTIMA8_GUMPS_SCROLLGUMP_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* A paper scroll (with a spell, etc)
*/
class ScrollGump : public ModalGump {
Std::string _text;
ObjId _textWidget;
public:
ENABLE_RUNTIME_CLASSTYPE()
ScrollGump();
ScrollGump(ObjId owner, const Std::string &msg);
~ScrollGump() override;
// Go to the next page on mouse click
void onMouseClick(int button, int32 mx, int32 my) override;
// Close on double click
void onMouseDouble(int button, int32 mx, int32 my) override;
bool OnKeyDown(int key, int mod) override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
INTRINSIC(I_readScroll);
protected:
void NextText();
public:
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,411 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/file.h"
#include "ultima/ultima8/gumps/shape_viewer_gump.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/gfx/shape_info.h"
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/gfx/fonts/font.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/fonts/font_shape_archive.h"
#include "ultima/ultima8/gfx/main_shape_archive.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/mouse_shape_archive.h"
#include "ultima/ultima8/gfx/texture.h"
#include "ultima/ultima8/convert/u8/convert_shape_u8.h"
#include "ultima/ultima8/gfx/palette.h"
#include "ultima/ultima8/gfx/palette_manager.h"
#include "ultima/ultima8/usecode/usecode.h"
#include "ultima/ultima8/metaengine.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(ShapeViewerGump)
static const uint32 background_colors[] = {
TEX32_PACK_RGB(0x10, 0x10, 0x10),
TEX32_PACK_RGB(0x90, 0x90, 0x90)
};
static const uint32 grid_colors[] = {
TEX32_PACK_RGB(0x20, 0x20, 0x20),
TEX32_PACK_RGB(0xA0, 0xA0, 0xA0)
};
static const uint32 axis_colors[] = {
TEX32_PACK_RGB(0x10, 0x30, 0x10),
TEX32_PACK_RGB(0x90, 0xB0, 0x90)
};
ShapeViewerGump::ShapeViewerGump()
: ModalGump(), _curArchive(0), _curShape(0), _curFrame(0),
_background(0), _fontNo(0), _showGrid(false), _mirrored(false),
_shapeW(0), _shapeH(0), _shapeX(0), _shapeY(0) {
}
ShapeViewerGump::ShapeViewerGump(int x, int y, int width, int height,
Common::Array<ShapeArchiveEntry> &archives,
uint32 flags, int32 layer)
: ModalGump(x, y, width, height, 0, flags, layer), _archives(archives),
_curArchive(0), _curShape(0), _curFrame(0),
_background(0), _fontNo(0), _showGrid(false), _mirrored(false),
_shapeW(0), _shapeH(0), _shapeX(0), _shapeY(0) {
if (GAME_IS_CRUSADER) {
// Default to a decent font on Crusader
_fontNo = 6;
}
}
ShapeViewerGump::~ShapeViewerGump() {
for (auto &entry : _archives) {
if (entry._disposeAfterUse == DisposeAfterUse::YES) {
delete entry._archive;
}
}
}
void ShapeViewerGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
}
void ShapeViewerGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool /*scaled*/) {
if (_archives.empty()) {
Close();
return;
}
uint32 color = background_colors[_background];
surf->fill32(color, _dims);
int32 posx = (_dims.width() - _shapeW) / 2 + _shapeX;
int32 posy = (_dims.height() - _shapeH) / 2 + _shapeY - 25;
if (_showGrid) {
const int step = 16;
color = grid_colors[_background];
for (int i = step; i < _dims.width(); i += step) {
int32 x = posx + i;
if (x < _dims.right)
surf->drawLine32(color, x, _dims.top, x, _dims.bottom - 1);
x = posx - i;
if (x > _dims.left)
surf->drawLine32(color, x, _dims.top, x, _dims.bottom - 1);
}
for (int i = step; i < _dims.height(); i += step) {
int32 y = posy + i;
if (y < _dims.bottom)
surf->drawLine32(color, _dims.left, y, _dims.right - 1, y);
y = posy - i;
if (y > _dims.top)
surf->drawLine32(color, _dims.left, y, _dims.right - 1, y);
}
color = axis_colors[_background];
surf->drawLine32(color, posx, _dims.top, posx, _dims.bottom - 1);
surf->drawLine32(color, _dims.left, posy, _dims.right - 1, posy);
}
ShapeArchive *archive = _archives[_curArchive]._archive;
const Shape *shape = archive->getShape(_curShape);
if (shape && _curFrame < shape->frameCount()) {
surf->Paint(shape, _curFrame, posx, posy, _mirrored);
}
RenderedText *rendtext;
Font *font = FontManager::get_instance()->getGameFont(_fontNo, true);
if (!font)
return;
unsigned int remaining;
{
// Basic shape/frame information
char buf1[50];
char buf2[200];
if (!shape) {
Common::sprintf_s(buf1, "NULL");
} else {
Common::sprintf_s(buf1, "Frame %d of %d", _curFrame+1, shape->frameCount());
}
Common::sprintf_s(buf2, "%s: Shape %d, %s %s", _archives[_curArchive]._name.c_str(),
_curShape, buf1, _mirrored ? "(Mirrored)" : "");
rendtext = font->renderText(buf2, remaining);
rendtext->draw(surf, 8, 10);
delete rendtext;
}
if (!_mirrored) {
// Dump the pixel val under the mouse cursor:
int32 mx = 0;
int32 my = 0;
char buf2[200];
Mouse::get_instance()->getMouseCoords(mx, my);
ScreenSpaceToGump(mx, my);
int32 relx = mx - (posx - _shapeX);
int32 rely = my - (posy - _shapeY);
if (shape && relx >= 0 && rely >= 0 && relx < _shapeW && rely < _shapeH) {
// get color
relx -= _shapeX;
rely -= _shapeY;
const ShapeFrame *frame = shape->getFrame(_curFrame);
if (frame && frame->hasPoint(relx, rely)) {
uint8 rawpx = frame->getPixel(relx, rely);
uint8 px_r, px_g, px_b;
shape->getPalette()->get(rawpx, px_r, px_g, px_b);
Common::sprintf_s(buf2, "px: (%d, %d)(%d, %d): %d (%d, %d, %d)", relx, rely, frame->_xoff, frame->_yoff, rawpx, px_r, px_g, px_b);
rendtext = font->renderText(buf2, remaining);
rendtext->draw(surf, 8, 25);
delete rendtext;
}
}
}
{
// Additional shapeinfo (only in main shapes archive)
MainShapeArchive *mainshapes = dynamic_cast<MainShapeArchive *>(archive);
if (!mainshapes || !shape) return;
char buf3[128];
char buf4[128];
char buf5[128];
char buf6[512];
const ShapeInfo *info = mainshapes->getShapeInfo(_curShape);
if (info) {
Common::sprintf_s(buf3, "x: %d, y: %d, z: %d\n flags: 0x%04X, family: %d",
info->_x, info->_y, info->_z, info->_flags, info->_family);
Common::sprintf_s(buf4, "equip type: %d, weight: %d, vol: %d",
info->_equipType, info->_weight, info->_volume);
Common::sprintf_s(buf5, "anim: type: %d, data: %d, speed: %d",
info->_animType, info->_animData, info->_animSpeed);
Common::sprintf_s(buf6, "ShapeInfo: %s\n%s\n%s\nUsecode: %s",
buf3, buf4, buf5, GameData::get_instance()->getMainUsecode()->get_class_name(_curShape));
rendtext = font->renderText(buf6, remaining);
int x, y;
rendtext->getSize(x, y);
rendtext->draw(surf, 8, _dims.height() - y);
delete rendtext;
}
}
}
bool ShapeViewerGump::OnKeyDown(int key, int mod) {
ShapeArchive *archive = _archives[_curArchive]._archive;
bool shapechanged = false;
unsigned int delta = 1;
if (mod & Common::KBD_SHIFT) delta = 10;
switch (key) {
case Common::KEYCODE_UP:
case Common::KEYCODE_k:
if (delta >= archive->getCount())
delta = 1;
if (_curShape < delta)
_curShape = archive->getCount() + _curShape - delta;
else
_curShape -= delta;
shapechanged = true;
_curFrame = 0;
break;
case Common::KEYCODE_DOWN:
case Common::KEYCODE_j:
if (delta >= archive->getCount())
delta = 1;
if (_curShape + delta >= archive->getCount())
_curShape = _curShape + delta - archive->getCount();
else
_curShape += delta;
_curFrame = 0;
shapechanged = true;
break;
case Common::KEYCODE_LEFT:
case Common::KEYCODE_h: {
const Shape *shape = archive->getShape(_curShape);
if (shape && shape->frameCount()) {
if (delta >= shape->frameCount()) delta = 1;
if (_curFrame < delta)
_curFrame = shape->frameCount() + _curFrame - delta;
else
_curFrame -= delta;
}
}
break;
case Common::KEYCODE_RIGHT:
case Common::KEYCODE_l: {
const Shape *shape = archive->getShape(_curShape);
if (shape && shape->frameCount()) {
if (delta >= shape->frameCount()) delta = 1;
if (_curFrame + delta >= shape->frameCount())
_curFrame = _curFrame + delta - shape->frameCount();
else
_curFrame += delta;
}
}
break;
case Common::KEYCODE_COMMA:
case Common::KEYCODE_PAGEUP: {
if (_curArchive == 0)
_curArchive = _archives.size() - 1;
else
_curArchive--;
archive = _archives[_curArchive]._archive;
shapechanged = true;
_curShape = 0;
_curFrame = 0;
}
break;
case Common::KEYCODE_PERIOD:
case Common::KEYCODE_PAGEDOWN: {
if (_curArchive + 1 == _archives.size())
_curArchive = 0;
else
_curArchive++;
archive = _archives[_curArchive]._archive;
shapechanged = true;
_curShape = 0;
_curFrame = 0;
}
break;
case Common::KEYCODE_f: {
_fontNo++;
if (_fontNo >= GameData::get_instance()->getFonts()->getCount() ||
_fontNo > 17) {
_fontNo = 0;
}
}
break;
case Common::KEYCODE_m: {
_mirrored = !_mirrored;
}
break;
case Common::KEYCODE_g: {
_showGrid = !_showGrid;
}
break;
case Common::KEYCODE_b: {
_background = _background ? 0 : 1;
} break;
case Common::KEYCODE_ESCAPE: {
Close();
}
break;
default:
break;
}
if (shapechanged) {
const Shape *shape = archive->getShape(_curShape);
if (shape)
shape->getTotalDimensions(_shapeW, _shapeH, _shapeX, _shapeY);
}
return true;
}
//static
void ShapeViewerGump::U8ShapeViewer() {
GameData *gamedata = GameData::get_instance();
Common::Array<ShapeArchiveEntry> archives;
archives.push_back(ShapeArchiveEntry("shapes", gamedata->getMainShapes()));
archives.push_back(ShapeArchiveEntry("gumps", gamedata->getGumps()));
archives.push_back(ShapeArchiveEntry("fonts", gamedata->getFonts()));
ShapeArchive *mouseShapes = new MouseShapeArchive(gamedata->getMouse(), GameData::OTHER);
archives.push_back(ShapeArchiveEntry("mouse", mouseShapes, DisposeAfterUse::YES));
auto *eintro = new Common::File();
if (eintro->open("static/eintro.skf")) {
ShapeArchive *eintroshapes = new ShapeArchive(eintro, GameData::OTHER,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game),
&U8SKFShapeFormat);
archives.push_back(ShapeArchiveEntry("eintro", eintroshapes, DisposeAfterUse::YES));
} else {
delete eintro;
}
auto *endgame = new Common::File();
if (endgame->open("static/endgame.skf")) {
ShapeArchive *endgameshapes = new ShapeArchive(endgame, GameData::OTHER,
PaletteManager::get_instance()->getPalette(PaletteManager::Pal_Game),
&U8SKFShapeFormat);
archives.push_back(ShapeArchiveEntry("endgame", endgameshapes, DisposeAfterUse::YES));
} else {
delete endgame;
}
Gump *desktopGump = Ultima8Engine::get_instance()->getDesktopGump();
Common::Rect32 res = desktopGump->getDims();
int xoff, yoff, width, height;
if (res.height() > 240) {
width = (res.width() * 4) / 5;
height = (res.height() * 5) / 6;
xoff = res.width() / 10;
yoff = res.height() / 12;
} else {
width = (res.width() * 9) / 10;
height = (res.height() * 11) / 12;
xoff = res.width() / 20;
yoff = res.height() / 24;
}
ModalGump *gump = new ShapeViewerGump(xoff, yoff, width, height, archives);
gump->InitGump(0);
}
bool ShapeViewerGump::loadData(Common::ReadStream *rs) {
warning("Trying to load ModalGump");
return false;
}
void ShapeViewerGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,89 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_SHAPEVIEWERGUMP_H
#define ULTIMA8_GUMPS_SHAPEVIEWERGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/shared/std/containers.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class ShapeArchive;
/**
* A tool for viewing the shapes in the game, for debugging purposes.
*/
class ShapeViewerGump : public ModalGump {
public:
struct ShapeArchiveEntry {
Common::String _name;
ShapeArchive *_archive;
DisposeAfterUse::Flag _disposeAfterUse;
ShapeArchiveEntry(const char *name, ShapeArchive *archive, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::NO)
: _name(name), _archive(archive), _disposeAfterUse(disposeAfterUse) {}
};
ENABLE_RUNTIME_CLASSTYPE()
ShapeViewerGump();
ShapeViewerGump(int x, int y, int width, int height,
Common::Array<ShapeArchiveEntry> &archives,
uint32 flags = FLAG_PREVENT_SAVE, int32 layer = LAYER_MODAL);
~ShapeViewerGump() override;
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
bool OnKeyDown(int key, int mod) override;
// Init the gump, call after construction
void InitGump(Gump *newparent, bool take_focus = true) override;
static void U8ShapeViewer();
bool loadData(Common::ReadStream *rs);
void saveData(Common::WriteStream *ws) override;
protected:
Common::Array<ShapeArchiveEntry> _archives;
unsigned int _curArchive;
uint32 _curShape;
uint32 _curFrame;
uint32 _background;
//! The font used in the shape viewer
uint32 _fontNo;
bool _showGrid;
bool _mirrored;
int32 _shapeW, _shapeH, _shapeX, _shapeY;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,232 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/slider_gump.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gumps/widgets/sliding_widget.h"
#include "ultima/ultima8/gfx/fonts/font.h"
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/usecode/uc_process.h"
#include "ultima/ultima8/kernel/kernel.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/ultima8.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(SliderGump)
SliderGump::SliderGump() : ModalGump(), _renderedText(nullptr), _min(0), _max(0),
_delta(0), _value(0), _usecodeNotifyPID(0), _renderedValue(-1) {
}
SliderGump::SliderGump(int x, int y, int16 min, int16 max,
int16 value, int16 delta)
: ModalGump(x, y, 5, 5), _min(min), _max(max), _delta(delta), _value(value),
_usecodeNotifyPID(0), _renderedText(nullptr), _renderedValue(-1) {
}
SliderGump::~SliderGump() {
}
/*
41:0 = slider gump
42:0,1 = ok button
43:0,1 = left button
44:0,1 = right button
45:0 = slider
*/
static const int gumpshape = 41;
static const int okshape = 42;
static const int leftshape = 43;
static const int rightshape = 44;
static const int slidershape = 45;
static const int sliderframe = 0;
static const int slidery = 17;
static const int sliderminx = 55;
static const int slidermaxx = 140;
static const int labelx = 18;
static const int labely = 26;
static const int labelfont = 0;
static const int OK_INDEX = 1;
static const int LEFT_INDEX = 2;
static const int RIGHT_INDEX = 3;
static const int SLIDER_INDEX = 4;
void SliderGump::setSliderPos() {
SlidingWidget *slider = dynamic_cast<SlidingWidget *>(Gump::FindGump<SlidingWidget>());
assert(slider);
slider->setValueForRange(_value, _min, _max);
}
void SliderGump::drawText(RenderSurface *surf) {
if (!_renderedText || _value != _renderedValue) {
Font *font;
font = FontManager::get_instance()->getGameFont(labelfont);
char buf[10]; // more than enough for a int16
Common::sprintf_s(buf, "%d", _value);
unsigned int remaining;
delete _renderedText;
_renderedText = font->renderText(buf, remaining);
_renderedValue = _value;
}
_renderedText->draw(surf, labelx, labely);
}
void SliderGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Gump::PaintThis(surf, lerp_factor, scaled);
drawText(surf);
}
void SliderGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
_shape = GameData::get_instance()->getGumps()->getShape(gumpshape);
UpdateDimsFromShape();
// Create the SlidingWidget
FrameID frame(GameData::GUMPS, slidershape, sliderframe);
SlidingWidget *slider = new SlidingWidget(sliderminx, slidery, frame, Common::Rect32(sliderminx, slidery, slidermaxx, slidery));
slider->SetIndex(SLIDER_INDEX);
slider->InitGump(this);
slider->setValueForRange(_value, _min, _max);
FrameID button_up(GameData::GUMPS, okshape, 0);
FrameID button_down(GameData::GUMPS, okshape, 1);
Gump *widget = new ButtonWidget(158, 17, button_up, button_down);
widget->SetIndex(OK_INDEX);
widget->InitGump(this);
FrameID buttonleft_up(GameData::GUMPS, leftshape, 0);
FrameID buttonleft_down(GameData::GUMPS, leftshape, 0);
widget = new ButtonWidget(36, 17, buttonleft_up, buttonleft_down);
widget->SetIndex(LEFT_INDEX);
widget->InitGump(this);
FrameID buttonright_up(GameData::GUMPS, rightshape, 0);
FrameID buttonright_down(GameData::GUMPS, rightshape, 0);
widget = new ButtonWidget(141, 17, buttonright_up, buttonright_down);
widget->SetIndex(RIGHT_INDEX);
widget->InitGump(this);
}
void SliderGump::ChildNotify(Gump *child, uint32 message) {
switch (child->GetIndex()) {
case OK_INDEX:
if (message == ButtonWidget::BUTTON_CLICK || message == ButtonWidget::BUTTON_DOUBLE)
Close();
break;
case LEFT_INDEX:
if (message == ButtonWidget::BUTTON_UP) {
_value -= _delta;
if (_value < _min)
_value = _min;
setSliderPos();
}
break;
case RIGHT_INDEX:
if (message == ButtonWidget::BUTTON_UP) {
_value += _delta;
if (_value > _max)
_value = _max;
setSliderPos();
}
break;
case SLIDER_INDEX:
if (message == SlidingWidget::DRAGGING) {
SlidingWidget *slider = dynamic_cast<SlidingWidget *>(child);
assert(slider);
_value = slider->getValueForRange(_min, _max);
_value = _min + _delta * (static_cast<int16>(_value / _delta));
// Set value to force slider to snap to increment
slider->setValueForRange(_value, _min, _max);
}
break;
}
}
void SliderGump::Close(bool no_del) {
_processResult = _value;
if (_usecodeNotifyPID) {
UCProcess *ucp = dynamic_cast<UCProcess *>(Kernel::get_instance()->getProcess(_usecodeNotifyPID));
assert(ucp);
ucp->setReturnValue(_value);
ucp->wakeUp(_value);
}
ModalGump::Close(no_del);
}
bool SliderGump::OnKeyDown(int key, int mod) {
switch (key) {
case Common::KEYCODE_LEFT:
_value -= _delta;
if (_value < _min) _value = _min;
setSliderPos();
break;
case Common::KEYCODE_RIGHT:
_value += _delta;
if (_value > _max) _value = _max;
setSliderPos();
break;
case Common::KEYCODE_RETURN:
Close();
break;
default:
break;
}
return true;
}
void SliderGump::setUsecodeNotify(UCProcess *ucp) {
assert(ucp);
_usecodeNotifyPID = ucp->getPid();
}
void SliderGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
bool SliderGump::loadData(Common::ReadStream *rs, uint32 version) {
warning("Trying to load ModalGump");
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,76 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_SLIDERGUMP_H
#define ULTIMA8_GUMPS_SLIDERGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class UCProcess;
class RenderedText;
/**
* A slider that lets you choose how many things to move (eg, when moving stacked items in the backpack)
*/
class SliderGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
SliderGump();
SliderGump(int x, int y, int16 min, int16 max,
int16 value, int16 delta = 1);
~SliderGump() override;
void InitGump(Gump *newparent, bool take_focus = true) override;
void PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) override;
void Close(bool no_del = false) override;
void ChildNotify(Gump *child, uint32 message) override;
void setUsecodeNotify(UCProcess *ucp);
bool OnKeyDown(int key, int mod) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
protected:
int16 _min;
int16 _max;
int16 _delta;
int16 _value;
uint16 _usecodeNotifyPID;
int16 _renderedValue;
RenderedText *_renderedText;
void setSliderPos();
void drawText(RenderSurface *surf);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,129 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima.h"
#include "ultima/ultima8/gumps/target_gump.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gumps/gump_notify_process.h"
#include "ultima/ultima8/world/item.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(TargetGump)
TargetGump::TargetGump() : ModalGump(), _targetTracing(false) {
}
// Skip pause as usecode processes need to complete & matches original game
TargetGump::TargetGump(int x, int y)
: ModalGump(x, y, 0, 0, 0, FLAG_DONT_SAVE | FLAG_PREVENT_SAVE, LAYER_MODAL, false),
_targetTracing(false) {
}
TargetGump::~TargetGump() {
}
void TargetGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
// we're invisible
}
void TargetGump::InitGump(Gump *newparent, bool take_focus) {
ModalGump::InitGump(newparent, take_focus);
// we need a notifier process
CreateNotifier();
Mouse *mouse = Mouse::get_instance();
mouse->pushMouseCursor(Mouse::MOUSE_TARGET);
}
void TargetGump::Close(bool no_del) {
Mouse *mouse = Mouse::get_instance();
mouse->popMouseCursor();
ModalGump::Close(no_del);
}
bool TargetGump::PointOnGump(int mx, int my) {
// HACK alert: if we're currently tracing from TargetGump::onMouseUp,
// then we do NOT want to intercept the trace
if (_targetTracing) return false;
return ModalGump::PointOnGump(mx, my);
}
void TargetGump::onMouseUp(int button, int32 mx, int32 my) {
if (button == Mouse::BUTTON_LEFT) {
_targetTracing = true;
_parent->GumpToScreenSpace(mx, my);
Gump *desktopgump = _parent;
ObjId objId = desktopgump->TraceObjId(mx, my);
Item *item = getItem(objId);
if (item) {
// done
debugC(kDebugObject, "Target result: %s", item->dumpInfo().c_str());
_processResult = objId;
Close();
}
_targetTracing = false;
}
}
bool TargetGump::OnKeyDown(int key, int mod) {
switch (key) {
case Common::KEYCODE_ESCAPE: {
Close();
} break;
default:
break;
}
return true;
}
uint32 TargetGump::I_target(const uint8 * /*args*/, unsigned int /*argsize*/) {
TargetGump *targetgump = new TargetGump(0, 0);
targetgump->InitGump(0);
return targetgump->GetNotifyProcess()->getPid();
}
void TargetGump::saveData(Common::WriteStream *ws) {
warning("Trying to save ModalGump");
}
bool TargetGump::loadData(Common::ReadStream *rs, uint32 versin) {
warning("Trying to load ModalGump");
return false;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,62 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_TARGETGUMP_H
#define ULTIMA8_GUMPS_TARGETGUMP_H
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/usecode/intrinsics.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class UCProcess;
class TargetGump : public ModalGump {
public:
ENABLE_RUNTIME_CLASSTYPE()
TargetGump();
TargetGump(int x, int y);
~TargetGump() override;
bool PointOnGump(int mx, int my) override;
void InitGump(Gump *newparent, bool take_focus = true) override;
void Close(bool no_del = false) override;
void PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) override;
void onMouseUp(int button, int32 mx, int32 my) override;
bool OnKeyDown(int key, int mod) override;
INTRINSIC(I_target);
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
private:
bool _targetTracing;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,55 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/translucent_gump.h"
#include "ultima/ultima8/gfx/render_surface.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(TranslucentGump)
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) {
}
TranslucentGump::~TranslucentGump() {
}
void TranslucentGump::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
if (_shape) {
surf->PaintTranslucent(_shape, _frameNum, 0, 0);
}
}
void TranslucentGump::saveData(Common::WriteStream *ws) {
Gump::saveData(ws);
}
bool TranslucentGump::loadData(Common::ReadStream *rs, uint32 version) {
return Gump::loadData(rs, version);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,53 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_TRANSLUCENTGUMP_H
#define ULTIMA8_GUMPS_TRANSLUCENTGUMP_H
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
/**
* A regular gump that paints itself translucent
*/
class TranslucentGump : public Gump {
public:
ENABLE_RUNTIME_CLASSTYPE()
TranslucentGump();
TranslucentGump(int x, int y, int width, int height, uint16 owner = 0,
uint32 flags = 0, int32 layer = LAYER_NORMAL);
~TranslucentGump() override;
// Paint this Gump
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,342 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/u8_save_gump.h"
#include "ultima/ultima8/gumps/widgets/edit_widget.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/filesys/savegame.h"
#include "ultima/ultima8/gumps/paged_gump.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "common/config-manager.h"
#include "common/savefile.h"
#include "common/translation.h"
#include "gui/message.h"
namespace Ultima {
namespace Ultima8 {
static const int entryfont = 4;
DEFINE_RUNTIME_CLASSTYPE_CODE(U8SaveGump)
U8SaveGump::U8SaveGump(bool saveMode, int page)
: Gump(0, 0, 5, 5), _save(saveMode), _page(page) {
}
U8SaveGump::~U8SaveGump() {
}
// gumps: 36/0-11: number 1-12
// 46/0: "Entry"
void U8SaveGump::InitGump(Gump *newparent, bool take_focus) {
Gump::InitGump(newparent, take_focus);
_dims.setWidth(220);
_dims.setHeight(170);
FrameID entry_id(GameData::GUMPS, 46, 0);
entry_id = _TL_SHP_(entry_id);
const Shape *entryShape = GameData::get_instance()->getShape(entry_id);
const ShapeFrame *sf = entryShape->getFrame(entry_id._frameNum);
int entrywidth = sf->_width;
int entryheight = sf->_height;
if (_save)
_editWidgets.resize(6); // constant!
loadDescriptions();
for (int i = 0; i < 6; ++i) {
int index = _page * 6 + i;
int xbase = 3;
int yi = i;
if (i >= 3) {
xbase += _dims.width() / 2 + 9;
yi -= 3;
}
Gump *gump = new Gump(xbase, 3 + 40 * yi, 1, 1);
gump->SetShape(entry_id, true);
gump->InitGump(this, false);
int x = xbase + 2 + entrywidth;
if (index >= 9) { // index 9 is labelled "10"
FrameID entrynum1_id(GameData::GUMPS, 36, (index + 1) / 10 - 1);
entrynum1_id = _TL_SHP_(entrynum1_id);
entryShape = GameData::get_instance()->getShape(entrynum1_id);
sf = entryShape->getFrame(entrynum1_id._frameNum);
x += 1 + sf->_width;
gump = new Gump(xbase + 2 + entrywidth, 3 + 40 * yi, 1, 1);
gump->SetShape(entrynum1_id, true);
gump->InitGump(this, false);
}
FrameID entrynum_id(GameData::GUMPS, 36, index % 10);
entrynum_id = _TL_SHP_(entrynum_id);
gump = new Gump(x, 3 + 40 * yi, 1, 1);
gump->SetShape(entrynum_id, true);
if (index % 10 == 9) {
// HACK: There is no frame for '0', so we re-use part of the
// frame for '10', cutting off the first 6 pixels.
Common::Rect32 rect = gump->getDims();
rect.translate(6, 0);
gump->setDims(rect);
}
gump->InitGump(this, false);
if (index == 0) {
// special case for 'The Beginning...' _save
Gump *widget = new TextWidget(xbase, entryheight + 4 + 40 * yi,
_TL_("The Beginning..."),
true, entryfont, 95);
widget->InitGump(this, false);
} else {
if (_save) {
EditWidget *ew = new EditWidget(xbase, entryheight + 4 + 40 * yi,
_descriptions[i],
true, entryfont,
95, 38 - entryheight, 0, true);
ew->SetIndex(i + 1);
ew->InitGump(this, false);
_editWidgets[i] = ew;
} else {
// load
Gump *widget = new TextWidget(xbase, entryheight + 4 + 40 * yi,
_descriptions[i], true, entryfont,
95, 38 - entryheight);
widget->InitGump(this, false);
}
}
}
// remove focus from children (just in case)
if (_focusChild) _focusChild->OnFocus(false);
_focusChild = 0;
}
void U8SaveGump::Close(bool no_del) {
Gump::Close(no_del);
}
void U8SaveGump::OnFocus(bool gain) {
if (gain) {
if (_save)
Mouse::get_instance()->setMouseCursor(Mouse::MOUSE_QUILL);
else
Mouse::get_instance()->setMouseCursor(Mouse::MOUSE_MAGGLASS);
}
}
Gump *U8SaveGump::onMouseDown(int button, int32 mx, int32 my) {
// take all clicks
return this;
}
void U8SaveGump::onMouseClick(int button, int32 mx, int32 my) {
if (button != Mouse::BUTTON_LEFT) return;
ParentToGump(mx, my);
int x;
if (mx >= 3 && mx <= 100)
x = 0;
else if (mx >= _dims.width() / 2 + 10)
x = 1;
else
return;
int y;
if (my >= 3 && my <= 40)
y = 0;
else if (my >= 43 && my <= 80)
y = 1;
else if (my >= 83 && my <= 120)
y = 2;
else
return;
int i = 3 * x + y;
int index = 6 * _page + i + 1;
if (_save && !_focusChild && _editWidgets[i]) {
_editWidgets[i]->MakeFocus();
PagedGump *p = dynamic_cast<PagedGump *>(_parent);
if (p) p->enableButtons(false);
}
if (!_save) {
// If our parent has a notifiy process, we'll put our result in it and won't actually load the game
GumpNotifyProcess *p = _parent ? _parent->GetNotifyProcess() : nullptr;
if (p) {
// Do nothing in this case
if (index != 1 && _descriptions[i].empty()) return;
_parent->SetResult(index);
_parent->Close(); // close PagedGump (and us)
return;
}
loadgame(index); // 'this' will be deleted here!
}
}
void U8SaveGump::onMouseDouble(int button, int32 mx, int32 my) {
onMouseClick(button, mx, my);
}
void U8SaveGump::ChildNotify(Gump *child, uint32 message) {
EditWidget *widget = dynamic_cast<EditWidget *>(child);
if (widget && message == EditWidget::EDIT_ENTER) {
// save
assert(_save);
Std::string name = widget->getText();
if (name.empty()) return;
// Note: this might close us, so we should return right after.
savegame(widget->GetIndex() + 6 * _page, name);
return;
}
if (widget && message == EditWidget::EDIT_ESCAPE) {
// cancel edit
assert(_save);
// remove focus
if (_focusChild) _focusChild->OnFocus(false);
_focusChild = 0;
PagedGump *p = dynamic_cast<PagedGump *>(_parent);
if (p) p->enableButtons(true);
widget->setText(_descriptions[widget->GetIndex() - 1]);
return;
}
}
bool U8SaveGump::OnKeyDown(int key, int mod) {
if (Gump::OnKeyDown(key, mod)) return true;
return false;
}
bool U8SaveGump::loadgame(int saveIndex) {
if (saveIndex == 1) {
return Ultima8Engine::get_instance()->newGame();
}
Common::Error loadError = Ultima8Engine::get_instance()->loadGameState(saveIndex);
if (loadError.getCode() != Common::kNoError) {
GUI::MessageDialog errorDialog(loadError.getDesc());
errorDialog.runModal();
return false;
}
return true;
}
bool U8SaveGump::savegame(int saveIndex, const Std::string &name) {
if (name.empty())
return false;
// We are saving, close parent (and ourselves) first so it doesn't
// block the save or appear in the screenshot
_parent->Close();
if (!Ultima8Engine::get_instance()->canSaveGameStateCurrently())
return false;
return Ultima8Engine::get_instance()->saveGameState(saveIndex, name).getCode() == Common::kNoError;
}
void U8SaveGump::loadDescriptions() {
_descriptions.resize( 6);
for (int i = 0; i < 6; ++i) {
int saveIndex = 6 * _page + i + 1;
Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(
Ultima8Engine::get_instance()->getSaveStateName(saveIndex));
if (!saveFile)
continue;
const SavegameReader *sg = new SavegameReader(saveFile, true);
_descriptions[i] = sg->getDescription();
delete sg;
}
}
//static
Gump *U8SaveGump::showLoadSaveGump(Gump *parent, bool save) {
if (!ConfMan.getBool("originalsaveload")) {
if (save)
Ultima8Engine::get_instance()->saveGameDialog();
else
Ultima8Engine::get_instance()->loadGameDialog();
return nullptr;
}
if (save && !Ultima8Engine::get_instance()->canSaveGameStateCurrently()) {
return nullptr;
}
PagedGump *gump = new PagedGump(34, -38, 3, 35);
gump->InitGump(parent);
for (int page = 0; page < 16; ++page) {
U8SaveGump *s = new U8SaveGump(save, page);
s->InitGump(gump, false);
gump->addPage(s);
}
int lastSave = ConfMan.hasKey("lastSave") ? ConfMan.getInt("lastSave") : -1;
if (lastSave > 0) {
gump->showPage((lastSave - 1) / 6);
}
gump->setRelativePosition(CENTER);
return gump;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,72 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_U8SAVEGUMP_H
#define ULTIMA8_GUMPS_U8SAVEGUMP_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class EditWidget;
/**
* U8-style load/save gump (a single number + editable widget entry in the paged save/load gump)
*/
class U8SaveGump : public Gump {
public:
ENABLE_RUNTIME_CLASSTYPE()
U8SaveGump(bool save, int page);
~U8SaveGump() override;
void InitGump(Gump *newparent, bool take_focus = true) override;
void Close(bool no_del = false) override;
Gump *onMouseDown(int button, int32 mx, int32 my) override;
void onMouseClick(int button, int32 mx, int32 my) override;
void onMouseDouble(int button, int32 mx, int32 my) override;
bool OnKeyDown(int key, int mod) override;
void ChildNotify(Gump *child, uint32 message) override;
void OnFocus(bool gain) override;
static Gump *showLoadSaveGump(Gump *parent, bool save);
protected:
bool _save;
int _page;
Std::vector<EditWidget *> _editWidgets;
Std::vector<Std::string> _descriptions;
void loadDescriptions();
bool loadgame(int saveIndex);
bool savegame(int saveIndex, const Std::string &name);
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,78 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/weasel_dat.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;
if (entry._shapeNo)
_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 (const auto &item : _items) {
if (item._type == type)
count++;
}
return count;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,72 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#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

View File

@@ -0,0 +1,565 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/weasel_gump.h"
#include "ultima/ultima8/gumps/weasel_dat.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/gump_shape_archive.h"
#include "ultima/ultima8/gfx/main_shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/gfx/shape_frame.h"
#include "ultima/ultima8/ultima8.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/gumps/movie_gump.h"
#include "ultima/ultima8/world/actors/main_actor.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/world/get_object.h"
#include "ultima/ultima8/world/item_factory.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 *const FIRST_INTRO_MOVIE = "17A";
static const char *const INTRO_MOVIES[] = {"18A", "18B", "18C"};
static const char *const BUYMORE_MOVIES[] = {"21A", "21B"};
static const char *const CONFIRM_BUY_MOVIES[] = {"21A", "21B"};
static const char *const CANCELLED_PURCHASE_MOVIES[] = {"19C", "19D"};
static const char *const COMPLETED_PURCHASE_MOVIES[] = {"21C", "21D"};
static const char *const INSUFFICIENT_FUND_MOVIES[] = {"20C", "20D"};
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 *const *movies, int nmovies) {
Common::RandomSource &rs = Ultima8Engine::get_instance()->getRandomSource();
int offset = rs.getRandomNumber(nmovies - 1);
return movies[offset];
}
}
bool WeaselGump::_playedIntroMovie = false;
WeaselGump::WeaselGump(uint16 level)
: ModalGump(0, 0, 640, 480), _credits(0), _level(level),
_state(kWeaselStart), _curItem(0), _ammoMode(false), _curItemCost(1),
_curItemShape(0), _ui(nullptr), _movie(nullptr), _weaselDat(nullptr) {
Mouse *mouse = Mouse::get_instance();
mouse->pushMouseCursor(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();
const Shape *top = shapeArchive->getShape(WEASEL_SHAPE_TOP);
const Shape *midhi = shapeArchive->getShape(WEASEL_SHAPE_TOP + 1);
const Shape *midlo = shapeArchive->getShape(WEASEL_SHAPE_TOP + 2);
const 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(Common::Rect32(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];
const 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();
_weaselDat = GameData::get_instance()->getWeaselDat(_level);
if (!_weaselDat || _weaselDat->getNumItems() == 0)
Close();
}
Gump *WeaselGump::playMovie(const Std::string &filename) {
MovieGump *gump = MovieGump::CruMovieViewer(filename, 600, 450, nullptr, this, 0);
if (!gump) {
warning("Couldn't load flic %s", filename.c_str());
return nullptr;
}
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 == 2 && !_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 more keyboard input
switch (key) {
case Common::KEYCODE_LEFT:
if (_state == kWeaselBrowsing)
prevItem();
break;
case Common::KEYCODE_RIGHT:
if (_state == kWeaselBrowsing)
nextItem();
break;
}
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;
updateForAmmoMode();
break;
case kBtnAmmo:
_ammoMode = true;
updateForAmmoMode();
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::updateForAmmoMode() {
Gump *ammobtn = _ui->FindGump(&FindByIndex<kBtnAmmo>);
Gump *wpnbtn = _ui->FindGump(&FindByIndex<kBtnWeapons>);
assert(ammobtn && wpnbtn);
ammobtn->SetVisibility(!_ammoMode);
wpnbtn->SetVisibility(_ammoMode);
_curItem = 0;
_weaselDat = GameData::get_instance()->getWeaselDat(_ammoMode ? 1 : _level);
if (!_weaselDat || _weaselDat->getNumItems() == 0)
Close();
updateItemDisplay();
}
void WeaselGump::prevItem() {
_curItem--;
if (_curItem < 0)
_curItem = _weaselDat->getNumItems() - 1;
updateItemDisplay();
}
void WeaselGump::nextItem() {
_curItem++;
if (_curItem >= _weaselDat->getNumItems())
_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 (const auto &purchase : _purchases) {
Item *newitem = ItemFactory::createItem(purchase, 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(&FindByIndex<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();
updateForAmmoMode();
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(&FindByIndex<kBtnYes>);
Gump *nobtn = _ui->FindGump(&FindByIndex<kBtnNo>);
Gump *qtxt = _ui->FindGump(&FindByIndex<kTxtQuestion>);
Gump *buybtn = _ui->FindGump(&FindByIndex<kBtnBuy>);
Gump *wpnbtn = _ui->FindGump(&FindByIndex<kBtnWeapons>);
Gump *ammobtn = _ui->FindGump(&FindByIndex<kBtnAmmo>);
Gump *exitbtn = _ui->FindGump(&FindByIndex<kBtnExit>);
Gump *blankbtn = _ui->FindGump(&FindByIndex<kBtnBlank>);
Gump *leftbtn = _ui->FindGump(&FindByIndex<kBtnLeft>);
Gump *rightbtn = _ui->FindGump(&FindByIndex<kBtnRight>);
Gump *credtxt = _ui->FindGump(&FindByIndex<kTxtCredits>);
Gump *nametxt = _ui->FindGump(&FindByIndex<kTxtItemName>);
Gump *costtxt = _ui->FindGump(&FindByIndex<kTxtItemCost>);
Gump *purchtxt = _ui->FindGump(&FindByIndex<kTxtItemPurch>);
Gump *ownedtxt = _ui->FindGump(&FindByIndex<kTxtItemOwned>);
Gump *icon = _ui->FindGump(&FindByIndex<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 (const auto &purchase : _purchases) {
if (purchase == shape)
count++;
}
return count;
}
void WeaselGump::updateItemDisplay() {
const Std::vector<WeaselDat::WeaselEntry> &items = _weaselDat->getItems();
// should always have the item..
assert(_curItem < (int)items.size());
_curItemCost = items[_curItem]._cost;
_curItemShape = items[_curItem]._shapeNo;
const ShapeInfo *shapeinfo = GameData::get_instance()->getMainShapes()->getShapeInfo(_curItemShape);
if (!shapeinfo || !shapeinfo->_weaponInfo) {
warning("Weasel: no info for shape %d", _curItemShape);
return;
}
const Shape *shape = GameData::get_instance()->getGumps()->getShape(shapeinfo->_weaponInfo->_displayGumpShape);
_closeIfExists(_ui->FindGump(&FindByIndex<kTxtCredits>));
_closeIfExists(_ui->FindGump(&FindByIndex<kTxtItemName>));
_closeIfExists(_ui->FindGump(&FindByIndex<kTxtItemCost>));
_closeIfExists(_ui->FindGump(&FindByIndex<kTxtItemPurch>));
_closeIfExists(_ui->FindGump(&FindByIndex<kTxtItemOwned>));
_closeIfExists(_ui->FindGump(&FindByIndex<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(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

View File

@@ -0,0 +1,141 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_REMORSEMENUGUMP_H
#define ULTIMA8_GUMPS_REMORSEMENUGUMP_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/modal_gump.h"
#include "ultima/ultima8/misc/classtype.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 updateForAmmoMode();
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;
/**
* Whether the first intro movie has been played. Remember this between
* displays of the gump, but don't save.
*
* It only ever shows on level 1 so there is only a single way it can be re-shown
* (save during the first time at the base and reload after restarting the game).
*
* TODO: test if original does this to be perfectly faithful
*/
static bool _playedIntroMovie;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,256 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/widgets/button_widget.h"
#include "ultima/ultima8/gumps/widgets/text_widget.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/gfx/shape_archive.h"
#include "ultima/ultima8/gfx/shape.h"
#include "ultima/ultima8/kernel/mouse.h"
#include "ultima/ultima8/world/get_object.h"
namespace Ultima {
namespace Ultima8 {
// p_dynamic_class stuff
DEFINE_RUNTIME_CLASSTYPE_CODE(ButtonWidget)
ButtonWidget::ButtonWidget() : Gump(), _shapeUp(nullptr), _shapeDown(nullptr),
_mouseOver(false), _origW(0), _origH(0), _frameNumUp(0),
_frameNumDown(0), _mouseOverBlendCol(0), _textWidget(0) {
}
ButtonWidget::ButtonWidget(int x, int y, Std::string txt, bool gamefont,
int font, uint32 mouseOverBlendCol,
int w, int h, int32 layer) :
Gump(x, y, w, h, 0, 0, layer), _shapeUp(nullptr), _shapeDown(nullptr),
_mouseOver(false), _origW(w), _origH(h), _frameNumUp(0), _frameNumDown(0) {
TextWidget *widget = new TextWidget(0, 0, txt, gamefont, font, w, h);
// FIXME: Do we ever free this widget?
_textWidget = widget->getObjId();
_mouseOverBlendCol = mouseOverBlendCol;
_mouseOver = (_mouseOverBlendCol != 0);
}
ButtonWidget::ButtonWidget(int x, int y, FrameID frame_up, FrameID frame_down,
bool mouseOver, int32 layer)
: Gump(x, y, 5, 5, 0, 0, layer), _textWidget(0), _mouseOver(mouseOver),
_origW(0), _origH(0), _mouseOverBlendCol(0) {
_shapeUp = GameData::get_instance()->getShape(frame_up);
_shapeDown = GameData::get_instance()->getShape(frame_down);
_frameNumUp = frame_up._frameNum;
_frameNumDown = frame_down._frameNum;
}
ButtonWidget::~ButtonWidget(void) {
}
void ButtonWidget::InitGump(Gump *newparent, bool take_focus) {
Gump::InitGump(newparent, take_focus);
if (_textWidget != 0) {
Gump *widget = getGump(_textWidget);
assert(widget);
widget->InitGump(this);
_dims = widget->getDims(); // transfer child dimension to self
widget->Move(0, _dims.top); // move it to the correct height
} else {
assert(_shapeUp != nullptr);
assert(_shapeDown != nullptr);
SetShape(_shapeUp, _frameNumUp);
UpdateDimsFromShape();
}
}
int ButtonWidget::getVlead() {
if (_textWidget != 0) {
Gump *widget = getGump(_textWidget);
TextWidget *txtWidget = dynamic_cast<TextWidget *>(widget);
assert(txtWidget);
return txtWidget->getVlead();
} else {
return 0;
}
}
bool ButtonWidget::PointOnGump(int mx, int my) {
// CHECKME: this makes the ButtonWidget accept any point in its rectangle,
// effectively ignoring shape transparency. For some buttons (like those
// in U8's diary), this is desirable. For others it may not be, so this
// behaviour may need to be changed. (bool ignoreShapeTransparency or
// something)
int32 gx = mx, gy = my;
ParentToGump(gx, gy);
return _dims.contains(gx, gy);
}
Gump *ButtonWidget::onMouseDown(int button, int32 mx, int32 my) {
Gump *ret = Gump::onMouseDown(button, mx, my);
if (ret)
return ret;
if (button == Mouse::BUTTON_LEFT) {
// CHECKME: change dimensions or not?
if (!_mouseOver) {
_shape = _shapeDown;
_frameNum = _frameNumDown;
}
return this;
}
return nullptr;
}
uint16 ButtonWidget::TraceObjId(int32 mx, int32 my) {
if (PointOnGump(mx, my))
return getObjId();
else
return 0;
}
void ButtonWidget::onMouseUp(int button, int32 mx, int32 my) {
if (button == Mouse::BUTTON_LEFT) {
if (!_mouseOver) {
_shape = _shapeUp;
_frameNum = _frameNumUp;
}
if (PointOnGump(mx, my))
_parent->ChildNotify(this, BUTTON_UP);
}
}
void ButtonWidget::onMouseClick(int button, int32 mx, int32 my) {
if (PointOnGump(mx, my))
_parent->ChildNotify(this, BUTTON_CLICK);
}
void ButtonWidget::onMouseDouble(int button, int32 mx, int32 my) {
if (PointOnGump(mx, my))
_parent->ChildNotify(this, BUTTON_DOUBLE);
}
void ButtonWidget::onMouseOver() {
if (_mouseOver) {
if (_textWidget) {
Gump *widget = getGump(_textWidget);
TextWidget *txtWidget = dynamic_cast<TextWidget *>(widget);
assert(txtWidget);
txtWidget->setBlendColour(_mouseOverBlendCol);
} else {
_shape = _shapeDown;
_frameNum = _frameNumDown;
}
}
}
void ButtonWidget::onMouseLeft() {
if (_mouseOver) {
if (_textWidget) {
Gump *widget = getGump(_textWidget);
TextWidget *txtWidget = dynamic_cast<TextWidget *>(widget);
assert(txtWidget);
txtWidget->setBlendColour(0);
} else {
_shape = _shapeUp;
_frameNum = _frameNumUp;
}
}
}
void ButtonWidget::saveData(Common::WriteStream *ws) {
// HACK ALERT
int w = 0, h = 0;
if (_textWidget != 0) {
w = _dims.width();
h = _dims.height();
_dims.setWidth(_origW);
_dims.setHeight(_origH);
}
Gump::saveData(ws);
// HACK ALERT
if (_textWidget != 0) {
_dims.setWidth(w);
_dims.setHeight(h);
}
uint16 flex = 0;
uint32 shapenum = 0;
if (_shapeUp) {
_shapeUp->getShapeId(flex, shapenum);
}
ws->writeUint16LE(flex);
ws->writeUint32LE(shapenum);
ws->writeUint32LE(_frameNumUp);
flex = 0;
shapenum = 0;
if (_shapeDown) {
_shapeDown->getShapeId(flex, shapenum);
}
ws->writeUint16LE(flex);
ws->writeUint32LE(shapenum);
ws->writeUint32LE(_frameNumDown);
ws->writeUint16LE(_textWidget);
ws->writeUint32LE(_mouseOverBlendCol);
uint8 m = (_mouseOver ? 1 : 0);
ws->writeByte(m);
}
bool ButtonWidget::loadData(Common::ReadStream *rs, uint32 version) {
if (!Gump::loadData(rs, version)) return false;
_shapeUp = nullptr;
ShapeArchive *flex = GameData::get_instance()->getShapeFlex(rs->readUint16LE());
uint32 shapenum = rs->readUint32LE();
if (flex) {
_shapeUp = flex->getShape(shapenum);
}
_frameNumUp = rs->readUint32LE();
_shapeDown = nullptr;
flex = GameData::get_instance()->getShapeFlex(rs->readUint16LE());
shapenum = rs->readUint32LE();
if (flex) {
_shapeDown = flex->getShape(shapenum);
}
_frameNumDown = rs->readUint32LE();
_textWidget = rs->readUint16LE();
_mouseOverBlendCol = rs->readUint32LE();
_mouseOver = (rs->readByte() != 0);
// HACK ALERT
if (_textWidget != 0) {
Gump *widget = getGump(_textWidget);
_dims = widget->getDims(); // transfer child dimension to self
widget->Move(0, _dims.top); // move it to the correct height
}
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,88 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_WIDGETS_BUTTONWIDGET_H
#define ULTIMA8_GUMPS_WIDGETS_BUTTONWIDGET_H
#include "ultima/shared/std/string.h"
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/gfx/frame_id.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class ButtonWidget : public Gump {
public:
// p_dynamic_class stuff
ENABLE_RUNTIME_CLASSTYPE()
ButtonWidget();
ButtonWidget(int x, int y, Std::string txt, bool gamefont, int font,
uint32 mouseOverBlendCol = 0, int width = 0, int height = 0,
int32 layer = LAYER_NORMAL);
ButtonWidget(int x, int y, FrameID frame_up, FrameID frame_down,
bool mouseOver = false, int32 layer = LAYER_NORMAL);
~ButtonWidget() override;
void InitGump(Gump *newparent, bool take_focus = true) override;
uint16 TraceObjId(int32 mx, int32 my) override;
bool PointOnGump(int mx, int my) override;
Gump *onMouseDown(int button, int32 mx, int32 my) override;
void onMouseUp(int button, int32 mx, int32 my) override;
void onMouseClick(int button, int32 mx, int32 my) override;
void onMouseDouble(int button, int32 mx, int32 my) override;
void onMouseOver() override;
void onMouseLeft() override;
//! return the textwidget's vlead, or 0 for an image button
int getVlead();
//void SetShapeDown(Shape *_shape, uint32 frameNum);
//void SetShapeUp(Shape *_shape, uint32 frameNum);
enum Message {
BUTTON_CLICK = 0,
BUTTON_UP = 1,
BUTTON_DOUBLE = 2
};
protected:
Shape *_shapeUp;
uint32 _frameNumUp;
Shape *_shapeDown;
uint32 _frameNumDown;
uint16 _textWidget;
uint32 _mouseOverBlendCol;
bool _mouseOver;
int _origW, _origH;
public:
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,273 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/widgets/edit_widget.h"
#include "ultima/ultima8/gfx/fonts/rendered_text.h"
#include "ultima/ultima8/gfx/render_surface.h"
#include "ultima/ultima8/gfx/fonts/font_manager.h"
#include "common/system.h"
#include "common/events.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(EditWidget)
EditWidget::EditWidget(int x, int y, Std::string txt, bool gamefont, int font,
int w, int h, unsigned int maxlength, bool multiline)
: Gump(x, y, w, h), _text(txt), _gameFont(gamefont), _fontNum(font),
_maxLength(maxlength), _multiLine(multiline),
_cursorChanged(0), _cursorVisible(true), _cachedText(nullptr) {
_cursor = _text.size();
}
EditWidget::~EditWidget(void) {
delete _cachedText;
}
// Init the gump, call after construction
void EditWidget::InitGump(Gump *newparent, bool take_focus) {
Gump::InitGump(newparent, take_focus);
Font *font = getFont();
// Y offset is always baseline
_dims.moveTo(0, -font->getBaseline());
if (_gameFont && getFont()->isHighRes()) {
Common::Rect32 rect(_dims);
ScreenSpaceToGumpRect(rect, ROUND_OUTSIDE);
_dims.moveTo(0, rect.top);
}
}
Font *EditWidget::getFont() const {
if (_gameFont)
return FontManager::get_instance()->getGameFont(_fontNum, true);
else
return FontManager::get_instance()->getTTFont(_fontNum);
}
void EditWidget::setText(const Std::string &t) {
_text = t;
_cursor = _text.size();
delete _cachedText;
_cachedText = nullptr;
}
void EditWidget::ensureCursorVisible() {
_cursorVisible = true;
_cursorChanged = g_system->getMillis();
}
bool EditWidget::textFits(Std::string &t) {
Font *font = getFont();
unsigned int remaining;
int32 width, height;
int32 max_width = _multiLine ? _dims.width() : 0;
int32 max_height = _dims.height();
if (_gameFont && font->isHighRes()) {
Common::Rect32 rect(0, 0, max_width, max_height);
GumpRectToScreenSpace(rect, ROUND_INSIDE);
max_width = rect.width();
max_height = rect.height();
}
font->getTextSize(t, width, height, remaining,
max_width, max_height,
Font::TEXT_LEFT, false);
if (_gameFont && font->isHighRes()) {
Common::Rect32 rect(0, 0, width, height);
ScreenSpaceToGumpRect(rect, ROUND_OUTSIDE);
width = rect.width();
height = rect.height();
}
if (_multiLine)
return (remaining >= t.size());
else
return (width <= _dims.width());
}
void EditWidget::renderText() {
bool cv = _cursorVisible;
if (!IsFocus()) {
cv = false;
} else {
uint32 now = g_system->getMillis();
if (now > _cursorChanged + 750) {
cv = !_cursorVisible;
_cursorChanged = now;
}
}
if (cv != _cursorVisible) {
delete _cachedText;
_cachedText = nullptr;
_cursorVisible = cv;
}
if (!_cachedText) {
Font *font = getFont();
int32 max_width = _multiLine ? _dims.width() : 0;
int32 max_height = _dims.height();
if (_gameFont && font->isHighRes()) {
Common::Rect32 rect(0, 0, max_width, max_height);
GumpRectToScreenSpace(rect, ROUND_INSIDE);
max_width = rect.width();
max_height = rect.height();
}
unsigned int remaining;
_cachedText = font->renderText(_text, remaining,
max_width, max_height,
Font::TEXT_LEFT,
false, false,
cv ? _cursor : Std::string::npos);
// Trim text to fit
if (remaining < _text.size()) {
_text.erase(remaining);
_cursor = _text.size();
}
}
}
// Overloadable method to Paint just this Gump (RenderSurface is relative to this)
void EditWidget::PaintThis(RenderSurface *surf, int32 lerp_factor, bool scaled) {
Gump::PaintThis(surf, lerp_factor, scaled);
renderText();
if (scaled && _gameFont && getFont()->isHighRes()) {
return;
}
_cachedText->draw(surf, 0, 0);
}
// Overloadable method to Paint just this gumps unscaled components that require compositing (RenderSurface is relative to parent).
void EditWidget::PaintComposited(RenderSurface *surf, int32 lerp_factor, int32 sx, int32 sy) {
Font *font = getFont();
if (!_gameFont || !font->isHighRes()) return;
int32 x = 0, y = 0;
GumpToScreenSpace(x, y, ROUND_BOTTOMRIGHT);
_cachedText->draw(surf, x, y, true);
Common::Rect32 rect(_dims);
GumpRectToScreenSpace(rect, ROUND_OUTSIDE);
}
// don't handle any mouse motion events, so let parent handle them for us.
Gump *EditWidget::onMouseMotion(int32 mx, int32 my) {
return nullptr;
}
bool EditWidget::OnKeyDown(int key, int mod) {
switch (key) {
case Common::KEYCODE_RETURN:
case Common::KEYCODE_KP_ENTER:
_parent->ChildNotify(this, EDIT_ENTER);
break;
case Common::KEYCODE_ESCAPE:
_parent->ChildNotify(this, EDIT_ESCAPE);
break;
case Common::KEYCODE_BACKSPACE:
if (_cursor > 0) {
_text.erase(--_cursor, 1);
delete _cachedText;
_cachedText = nullptr;
ensureCursorVisible();
}
break;
case Common::KEYCODE_DELETE:
if (_cursor != _text.size()) {
_text.erase(_cursor, 1);
delete _cachedText;
_cachedText = nullptr;
}
break;
case Common::KEYCODE_LEFT:
if (_cursor > 0) {
_cursor--;
delete _cachedText;
_cachedText = nullptr;
ensureCursorVisible();
}
break;
case Common::KEYCODE_RIGHT:
if (_cursor < _text.size()) {
_cursor++;
delete _cachedText;
_cachedText = nullptr;
ensureCursorVisible();
}
break;
default:
break;
}
return true;
}
bool EditWidget::OnKeyUp(int key) {
return true;
}
bool EditWidget::OnTextInput(int unicode) {
if (_maxLength > 0 && _text.size() >= _maxLength)
return true;
char c = 0;
if (unicode >= 0 && unicode < 256)
c = reverse_encoding[unicode];
if (!c) return true;
Std::string newtext = _text;
newtext.insertChar(c, _cursor);
if (textFits(newtext)) {
_text = newtext;
_cursor++;
delete _cachedText;
_cachedText = nullptr;
}
return true;
}
void EditWidget::OnFocus(bool gain) {
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, gain);
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,95 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_WIDGETS_EDITWIDGET_H
#define ULTIMA8_GUMPS_WIDGETS_EDITWIDGET_H
//
// EditWidget. Widget for text input (single or multi-line)
//
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/gfx/fonts/font.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class RenderedText;
class EditWidget : public Gump {
public:
ENABLE_RUNTIME_CLASSTYPE()
EditWidget(int x, int y, Std::string txt, bool gamefont, int fontnum,
int width, int height, unsigned int maxlength = 0,
bool multiline = false);
~EditWidget() override;
void InitGump(Gump *newparent, bool take_focus = true) override;
void PaintThis(RenderSurface *, int32 lerp_factor, bool scaled) override;
void PaintComposited(RenderSurface *surf, int32 lerp_factor, int32 sx, int32 sy) override;
Gump *onMouseMotion(int32 mx, int32 my) override;
bool OnKeyDown(int key, int mod) override;
bool OnKeyUp(int key) override;
bool OnTextInput(int unicode) override;
void OnFocus(bool gain) override;
//! get the current text
Std::string getText() const {
return _text;
}
void setText(const Std::string &t);
enum Message {
EDIT_ENTER = 16,
EDIT_ESCAPE = 17
};
protected:
Std::string _text;
Std::string::size_type _cursor;
bool _gameFont;
int _fontNum;
unsigned int _maxLength;
bool _multiLine;
uint32 _cursorChanged;
bool _cursorVisible;
void ensureCursorVisible();
bool textFits(Std::string &t);
void renderText();
Font *getFont() const;
RenderedText *_cachedText;
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,107 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/ultima8/gumps/widgets/sliding_widget.h"
namespace Ultima {
namespace Ultima8 {
DEFINE_RUNTIME_CLASSTYPE_CODE(SlidingWidget)
SlidingWidget::SlidingWidget()
: Gump(), _dragBounds() {
}
SlidingWidget::SlidingWidget(int x, int y, FrameID frame, const Common::Rect32 &dragBounds)
: Gump(x, y, 5, 5, 0, FLAG_DRAGGABLE), _dragBounds(dragBounds) {
SetShape(frame, true);
if (_dragBounds.width() < _dims.width())
_dragBounds.setWidth(_dims.width());
if (_dragBounds.height() < _dims.height())
_dragBounds.setHeight(_dims.height());
}
SlidingWidget::~SlidingWidget() {
}
int SlidingWidget::getValueForRange(int min, int max) {
int val = min;
if (_dragBounds.isValidRect()) {
val = min + (_x - _dragBounds.left) * (max - min) / (_dragBounds.width() - _dims.width());
if (val < min)
val = min;
if (val > max)
val = max;
}
return val;
}
void SlidingWidget::setValueForRange(int value, int min, int max) {
assert(_dragBounds.isValidRect());
_x = _dragBounds.left + (value - min) * (_dragBounds.width() - _dims.width()) / (max - min);
}
void SlidingWidget::InitGump(Gump *newparent, bool take_focus) {
Gump::InitGump(newparent, take_focus);
UpdateDimsFromShape();
}
uint16 SlidingWidget::TraceObjId(int32 mx, int32 my) {
if (PointOnGump(mx, my))
return getObjId();
else
return 0;
}
void SlidingWidget::Move(int32 x, int32 y) {
if (_dragBounds.isValidRect()) {
if (x < _dragBounds.left)
x = _dragBounds.left;
if (x > _dragBounds.right - _dims.width())
x = _dragBounds.right - _dims.width();
if (y < _dragBounds.top)
y = _dragBounds.top;
if (y > _dragBounds.bottom - _dims.height())
y = _dragBounds.bottom - _dims.height();
}
_x = x;
_y = y;
}
void SlidingWidget::onDrag(int32 mx, int32 my) {
Gump::onDrag(mx, my);
_parent->ChildNotify(this, DRAGGING);
}
void SlidingWidget::saveData(Common::WriteStream *ws) {
Gump::saveData(ws);
}
bool SlidingWidget::loadData(Common::ReadStream *rs, uint32 version) {
if (!Gump::loadData(rs, version)) return false;
return true;
}
} // End of namespace Ultima8
} // End of namespace Ultima

View File

@@ -0,0 +1,62 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef ULTIMA8_GUMPS_WIDGETS_SLIDINGWIDGET_H
#define ULTIMA8_GUMPS_WIDGETS_SLIDINGWIDGET_H
#include "ultima/ultima8/gumps/gump.h"
#include "ultima/ultima8/misc/classtype.h"
namespace Ultima {
namespace Ultima8 {
class SlidingWidget : public Gump {
protected:
Common::Rect32 _dragBounds;
public:
ENABLE_RUNTIME_CLASSTYPE()
SlidingWidget();
SlidingWidget(int x, int y, FrameID frame, const Common::Rect32 &dragBounds);
~SlidingWidget() override;
int getValueForRange(int min, int max);
void setValueForRange(int value, int min, int max);
void InitGump(Gump *newparent, bool take_focus = true) override;
uint16 TraceObjId(int32 mx, int32 my) override;
void Move(int32 x, int32 y) override;
void onDrag(int32 mx, int32 my) override;
bool loadData(Common::ReadStream *rs, uint32 version);
void saveData(Common::WriteStream *ws) override;
enum Message {
DRAGGING = 0
};
};
} // End of namespace Ultima8
} // End of namespace Ultima
#endif

Some files were not shown because too many files have changed in this diff Show More