STARK: Initiate rework of the user interface code

A base class, Window, will be used to ensure all the UI containers
work in a similar way.
This commit is contained in:
Bastien Bouclet 2015-03-17 21:04:17 +01:00
parent 4f52010182
commit 273a358ad3
36 changed files with 807 additions and 662 deletions

View file

@ -24,12 +24,14 @@
#include "engines/stark/gfx/driver.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/pattable.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/userinterface.h"
#include "engines/stark/services/staticprovider.h"
#include "engines/stark/services/global.h"
@ -39,80 +41,106 @@
namespace Stark {
ActionMenu::ActionMenu(Gfx::Driver *gfx) : _gfx(gfx) {
Global *global = StarkServices::instance().global;
Resources::Object *inventory = global->getLevel()->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kInventory, true);
_hand = inventory->findChildWithIndex<Resources::ItemSub2>(1);
_eye = inventory->findChildWithIndex<Resources::ItemSub2>(2);
_mouth = inventory->findChildWithIndex<Resources::ItemSub2>(3);
// TODO: Should these be hardcoded?
_hand->setPosition(Common::Point(90, -21));
_eye->setPosition(Common::Point(5, 40));
_mouth->setPosition(Common::Point(43, 0));
_renderEntries.push_back(_hand->getRenderEntry(Common::Point(0, 0)));
_renderEntries.push_back(_eye->getRenderEntry(Common::Point(0, 0)));
_renderEntries.push_back(_mouth->getRenderEntry(Common::Point(0, 0)));
ActionMenu::ActionMenu(Gfx::Driver *gfx, Cursor *cursor) :
Window(gfx, cursor) {
StaticProvider *staticProvider = StarkServices::instance().staticProvider;
// TODO: Shouldn't use a function called getCursorImage for this, also unhardcode
_background = staticProvider->getCursorImage(5);
_unscaled = true;
_item = nullptr;
_buttons[kActionHand].action = Resources::PATTable::kActionUse;
_buttons[kActionHand].rect = Common::Rect(90, 15, 126, 63);
_buttons[kActionEye].action = Resources::PATTable::kActionLook;
_buttons[kActionEye].rect = Common::Rect(5, 77, 51, 110);
_buttons[kActionMouth].action = Resources::PATTable::kActionTalk;
_buttons[kActionMouth].rect = Common::Rect(42, 35, 83, 74);
clearActions();
}
ActionMenu::~ActionMenu() {
_renderEntries.clear();
}
void ActionMenu::render(Common::Point pos) {
_gfx->setScreenViewport(true); // Drawn unscaled
_renderEntries.clear();
void ActionMenu::open(Resources::Item *item, const Common::Point &itemClickPos) {
UserInterface *ui = StarkServices::instance().userInterface;
if (_handEnabled) {
_renderEntries.push_back(_hand->getRenderEntry(pos));
_visible = true;
Common::Point screenMousePos = getScreenMousePosition();
_position = Common::Rect::center(screenMousePos.x, screenMousePos.y, 160, 111);
_itemClickPos = itemClickPos;
_item = item;
clearActions();
Resources::ActionArray possible = ui->getActionsPossibleForObject(_item, _itemClickPos);
for (uint i = 0; i < possible.size(); i++) {
for (uint j = 0; j < ARRAYSIZE(_buttons); j++) {
if (_buttons[j].action == possible[i]) {
_buttons[j].enabled = true;
break;
}
}
if (_eyeEnabled) {
_renderEntries.push_back(_eye->getRenderEntry(pos));
}
if (_mouthEnabled) {
_renderEntries.push_back(_mouth->getRenderEntry(pos));
}
Scene *scene = StarkServices::instance().scene;
_background->render(pos);
scene->render(_renderEntries);
void ActionMenu::close() {
_visible = false;
_item = nullptr;
}
void ActionMenu::onRender() {
UserInterface *ui = StarkServices::instance().userInterface;
Common::Point mousePos = getMousePosition();
_background->render(Common::Point(0, 0));
for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
if (_buttons[i].enabled) {
bool active = _buttons[i].rect.contains(mousePos);
VisualImageXMG *visual = ui->getActionImage(_buttons[i].action, active);
visual->render(Common::Point(_buttons[i].rect.left, _buttons[i].rect.top));
}
}
}
void ActionMenu::clearActions() {
_handEnabled = _mouthEnabled = _eyeEnabled = false;
for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
_buttons[i].enabled = false;
}
}
void ActionMenu::enableAction(ActionMenuType action) {
switch (action) {
case kActionHand:
_handEnabled = true;
break;
case kActionMouth:
_mouthEnabled = true;
break;
case kActionEye:
_eyeEnabled = true;
break;
default:
error("Invalid action type in ActionMenu::enableAction");
_buttons[action].enabled = true;
}
void ActionMenu::onMouseMove(const Common::Point &pos) {
bool hoveringAction = false;
for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
if (_buttons[i].enabled && _buttons[i].rect.contains(pos)) {
hoveringAction = true;
}
}
int ActionMenu::isThisYourButton(Resources::Object *object) {
Resources::Item *item = object->findParent<Resources::Item>();
if (item == _mouth) {
return kActionMouth;
} else if (item == _eye) {
return kActionEye;
} else if (item == _hand) {
return kActionHand;
if (hoveringAction) {
setCursor(Cursor::kActive);
} else {
return -1;
setCursor(Cursor::kDefault);
}
}
void ActionMenu::onClick(const Common::Point &pos) {
UserInterface *ui = StarkServices::instance().userInterface;
for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
if (_buttons[i].enabled && _buttons[i].rect.contains(pos)) {
ui->itemDoActionAt(_item, _buttons[i].action, _itemClickPos);
close();
}
}
}

View file

@ -25,38 +25,52 @@
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/ui/window.h"
namespace Stark {
class Cursor;
class VisualImageXMG;
namespace Resources {
class ItemSub2;
class Object;
class Item;
}
class ActionMenu {
Resources::ItemSub2 *_eye;
Resources::ItemSub2 *_hand;
Resources::ItemSub2 *_mouth;
VisualImageXMG *_background;
bool _eyeEnabled;
bool _handEnabled;
bool _mouthEnabled;
Gfx::RenderEntryArray _renderEntries;
Gfx::Driver *_gfx;
class ActionMenu : public Window {
public:
enum ActionMenuType {
kActionHand,
kActionMouth,
kActionEye
};
ActionMenu(Gfx::Driver *gfx);
ActionMenu(Gfx::Driver *gfx, Cursor *cursor);
~ActionMenu();
void render(Common::Point pos);
void open(Resources::Item *item, const Common::Point &itemClickPos);
void close();
protected:
void onMouseMove(const Common::Point &pos) override;
void onClick(const Common::Point &pos) override;
void onRender() override;
private:
enum ActionMenuType {
kActionHand = 0,
kActionEye = 1,
kActionMouth = 2
};
struct ActionButton {
bool enabled;
uint32 action;
Common::Rect rect;
};
ActionButton _buttons[3];
VisualImageXMG *_background;
Common::Point _itemClickPos;
Resources::Item *_item;
void clearActions();
void enableAction(ActionMenuType action);
Gfx::RenderEntryArray getRenderEntries() { return _renderEntries; }
int isThisYourButton(Resources::Object *object);
};

View file

@ -157,7 +157,7 @@ bool Console::Cmd_DumpKnowledge(int argc, const char **argv) {
}
bool Console::Cmd_ChangeKnowledge(int argc, const char **argv) {
int index = 0;
uint index = 0;
char type = 0;
if (argc >= 4) {
@ -211,7 +211,7 @@ bool Console::Cmd_ListScripts(int argc, const char **argv) {
}
bool Console::Cmd_EnableScript(int argc, const char **argv) {
int index = 0;
uint index = 0;
if (argc >= 2) {
index = atoi(argv[1]);
@ -243,7 +243,7 @@ bool Console::Cmd_EnableScript(int argc, const char **argv) {
}
bool Console::Cmd_ForceScript(int argc, const char **argv) {
int index = 0;
uint index = 0;
if (argc >= 2) {
index = atoi(argv[1]);

View file

@ -80,10 +80,14 @@ void Cursor::render() {
}
}
Common::Point Cursor::getMousePosition() const {
// The rest of the engine expects 640x480 coordinates
Common::Point Cursor::getMousePosition(bool unscaled) const {
if (unscaled) {
return _mousePos;
} else {
// Most of the engine expects 640x480 coordinates
return _gfx->scalePoint(_mousePos);
}
}
void Cursor::setMouseHint(const Common::String &hint) {
if (hint != _currentHint) {

View file

@ -54,7 +54,7 @@ public:
/** Update the mouse position */
void setMousePosition(Common::Point pos);
Common::Point getMousePosition() const;
Common::Point getMousePosition(bool unscaled = false) const;
enum CursorType {
kNone = -1,

View file

@ -44,8 +44,10 @@ public:
virtual void init() = 0;
virtual void setGameViewport() = 0;
virtual void setScreenViewport(bool noScaling) = 0;
virtual void setGameViewport() = 0; // deprecated
virtual void setScreenViewport(bool noScaling) = 0; // deprecated
virtual void setViewport(Common::Rect rect, bool noScaling) = 0;
/** Get the screen viewport in actual resolution */
Common::Rect getScreenViewport() { return _screenViewport; }

View file

@ -89,6 +89,27 @@ void OpenGLSDriver::setScreenViewport(bool noScaling) {
glViewport(_viewport.left, _viewport.top, _viewport.width(), _viewport.height());
}
void OpenGLSDriver::setViewport(Common::Rect rect, bool noScaling) {
if (noScaling) {
_viewport = rect;
_unscaledViewport = rect;
} else {
_viewport = Common::Rect(
_screenViewport.width() * rect.width() / kOriginalWidth,
_screenViewport.height() * rect.height() / kOriginalHeight
);
_viewport.translate(
_screenViewport.left + _screenViewport.width() * rect.left / kOriginalWidth,
_screenViewport.top + _screenViewport.height() * rect.top / kOriginalHeight
);
_unscaledViewport = rect;
}
glViewport(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height());
}
Math::Vector2d OpenGLSDriver::scaled(float x, float y) const {
return Math::Vector2d(x / (float) _unscaledViewport.width(), y / (float) _unscaledViewport.height());
}

View file

@ -49,6 +49,7 @@ public:
void setGameViewport() override;
void setScreenViewport(bool noScaling) override;
void setViewport(Common::Rect rect, bool noScaling) override;
void setupCamera(const Math::Matrix4 &projection, const Math::Matrix4 &view) override;

View file

@ -28,15 +28,10 @@
#include "engines/stark/visual/smacker.h"
#include "engines/stark/visual/visual.h"
// TODO: Refactor this logic elsewhere
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/pattable.h"
namespace Stark {
namespace Gfx {
RenderEntry::RenderEntry(Resources::Object *owner, const Common::String &name) :
RenderEntry::RenderEntry(Resources::Item *owner, const Common::String &name) :
_visual(nullptr),
_name(name),
_owner(owner),
@ -87,51 +82,5 @@ bool RenderEntry::compare(const RenderEntry *x, const RenderEntry *y) {
return x->_sortKey < y->_sortKey;
}
bool RenderEntry::containsPoint(Common::Point point) {
return indexForPoint(point) != -1;
}
int RenderEntry::indexForPoint(Common::Point point) {
// TODO: This doesn't consider 3D at all.
// TODO: This is just a quick fix, we still need to calculate the position, after any scaling and 3D transforms.
// TODO: We more or less ignore Y for now, since all we consider is the position-point.
// HACK: Since we lack Subtype2
if (getOwner() && (getOwner()->getType() == Resources::Type::kItem || getOwner()->getType() == Resources::Type::kAnim)) {
point.x -= _position.x;
point.y -= _position.y;
point.y -= Gfx::Driver::kTopBorderHeight; // Adjust for the top part.
if (getOwner()->getType() == Resources::Type::kItem) {
Resources::Item *item = (Resources::Item*)getOwner();
int index = item->indexForPoint(point);
// HACK For subtype 2, we get a PAT-index that is NOT inside the item.
if (getOwner()->getSubType() == Resources::Item::kItemSub2) {
return index;
}
if (index == -1) {
return -1;
} else {
Resources::PATTable *table = item->findChildWithIndex<Resources::PATTable>(index);
// Ignore any uninteractable Items
// this should not be done when handling UI-buttons, as they have 0 actions.
// For now that special case is handled in the Owner == Anim type below,
// but in practice it should be done by way of checking for ItemSub2
if (!table) {
warning("Item %s has no PAT Table", item->getName().c_str());
}
if (!table || table->getNumActions() == 0) {
return -1;
}
}
return index;
// TODO: This is probably not necessary after introducing SubType2, but we keep it for the statics for now.
} else if (getOwner()->getType() == Resources::Type::kAnim) { // HACK Until we get Subtype2
Resources::Anim *anim = (Resources::Anim*)getOwner();
return anim->indexForPoint(point);
}
}
return -1;
}
} // End of namespace Gfx
} // End of namespace Stark

View file

@ -34,7 +34,7 @@ namespace Stark {
class Visual;
namespace Resources {
class Object;
class Item;
}
namespace Gfx {
@ -43,7 +43,7 @@ class Driver;
class RenderEntry {
public:
RenderEntry(Resources::Object *owner, const Common::String &name);
RenderEntry(Resources::Item *owner, const Common::String &name);
virtual ~RenderEntry() {};
void render(Driver *gfx);
@ -53,17 +53,15 @@ public:
void setPosition3D(const Math::Vector3d &position, float direction);
void setSortKey(float sortKey);
/** Checks whether the render entry will draw anything to a specific point */
bool containsPoint(Common::Point point);
int indexForPoint(Common::Point point);
/** Gets the owner-object */
Resources::Object *getOwner() { return _owner; }
Resources::Item *getOwner() const { return _owner; }
/** Compare two render entries by their sort keys */
static bool compare(const RenderEntry *x, const RenderEntry *y);
protected:
Common::String _name;
Resources::Object *_owner;
Resources::Item *_owner;
Visual *_visual;
Common::Point _position;

View file

@ -65,6 +65,7 @@ MODULE_OBJS := \
ui/dialoginterface.o \
ui/inventoryinterface.o \
ui/topmenu.o \
ui/window.o \
visual/actor.o \
visual/image.o \
visual/smacker.o

View file

@ -60,7 +60,7 @@ Anim::~Anim() {
Anim::Anim(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_field_30(0),
_usage(0),
_currentFrame(0),
_numFrames(0),
_refCount(0) {
@ -68,7 +68,7 @@ Anim::Anim(Object *parent, byte subType, uint16 index, const Common::String &nam
}
void Anim::readData(Formats::XRCReadStream *stream) {
_field_30 = stream->readUint32LE();
_usage = stream->readUint32LE();
_numFrames = stream->readUint32LE();
}
@ -79,6 +79,10 @@ Visual *Anim::getVisual() {
return nullptr;
}
uint32 Anim::getUsage() const {
return _usage;
}
void Anim::applyToItem(Item *item) {
_refCount++;
}
@ -91,7 +95,7 @@ bool Anim::isInUse() {
}
void Anim::printData() {
debug("field_30: %d", _field_30);
debug("usage: %d", _usage);
debug("numFrames: %d", _numFrames);
}

View file

@ -60,6 +60,23 @@ public:
kAnimSkeleton = 4
};
enum ActionUsage {
kActionUsagePassive = 1,
kActionUsageActive = 2
};
enum UIUsage {
kUIUsageInventory = 1,
kUIUsageUseCursor = 4
};
enum ActorUsage {
kActorUsageIdle = 1,
kActorUsageWalk = 2,
kActorUsageTalk = 3,
kActorUsageRun = 6
};
/** Anim factory */
static Object *construct(Object *parent, byte subType, uint16 index, const Common::String &name);
@ -84,12 +101,15 @@ public:
/** Check is the animation is being used by an item */
bool isInUse();
/** Obtain the purpose of this anim */
uint32 getUsage() const;
virtual bool containsPoint(Common::Point point) { return false; }
virtual int indexForPoint(Common::Point point) { return -1; }
protected:
virtual void printData() override;
uint32 _field_30;
uint32 _usage;
uint32 _currentFrame;
uint32 _numFrames;
int32 _refCount;

View file

@ -38,7 +38,7 @@ AnimHierarchy::~AnimHierarchy() {
AnimHierarchy::AnimHierarchy(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name),
_animIndex(0),
_animUsage(0),
_currentAnim(nullptr),
_animHierarchy(nullptr),
_field_5C(0) {
@ -76,9 +76,9 @@ void AnimHierarchy::onAllLoaded() {
}
}
void AnimHierarchy::setItemAnim(ItemVisual *item, int32 index) {
void AnimHierarchy::setItemAnim(ItemVisual *item, int32 usage) {
unselectItemAnim(item);
_animIndex = index;
_animUsage = usage;
selectItemAnim(item);
}
@ -93,7 +93,7 @@ void AnimHierarchy::unselectItemAnim(ItemVisual *item) {
void AnimHierarchy::selectItemAnim(ItemVisual *item) {
// Search for an animation with the appropriate index
for (uint i = 0; i < _animations.size(); i++) {
if (_animations[i]->getIndex() == _animIndex) {
if (_animations[i]->getUsage() == _animUsage) {
_currentAnim = _animations[i];
break;
}
@ -125,6 +125,22 @@ TextureSet *AnimHierarchy::findTextureSet(uint32 textureType) {
return findChildWithSubtype<TextureSet>(textureType);
}
Anim *AnimHierarchy::getAnimForUsage(uint32 usage) {
// Search for an animation with the appropriate use
for (uint i = 0; i < _animations.size(); i++) {
if (_animations[i]->getUsage() == usage) {
return _animations[i];
}
}
error("No anim found for use '%d' in '%s'", usage, getName().c_str());
}
Visual *AnimHierarchy::getVisualForUsage(uint32 usage) {
Anim *anim = getAnimForUsage(usage);
return anim->getVisual();
}
void AnimHierarchy::printData() {
for (uint i = 0; i < _animationReferences.size(); i++) {
debug("anim %d: %s", i, _animationReferences[i].describe().c_str());

View file

@ -29,6 +29,9 @@
#include "engines/stark/resourcereference.h"
namespace Stark {
class Visual;
namespace Formats {
class XRCReadStream;
}
@ -57,8 +60,8 @@ public:
void readData(Formats::XRCReadStream *stream) override;
void onAllLoaded() override;
/** Set and apply the current animation for an item */
void setItemAnim(ItemVisual *item, int32 index);
/** Set and apply the current animation kind for an item */
void setItemAnim(ItemVisual *item, int32 usage);
/** Unselect the current animation and remove it from an item */
void unselectItemAnim(ItemVisual *item);
@ -78,7 +81,10 @@ public:
*/
TextureSet *findTextureSet(uint32 textureType);
Visual *getVisualForUsage(uint32 usage);
protected:
Anim *getAnimForUsage(uint32 usage);
void printData() override;
Common::Array<ResourceReference> _animationReferences;
@ -88,7 +94,7 @@ protected:
AnimHierarchy * _animHierarchy;
float _field_5C;
int32 _animIndex;
uint32 _animUsage;
Anim *_currentAnim;
};

View file

@ -26,12 +26,15 @@
#include "engines/stark/formats/xrc.h"
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/animhierarchy.h"
#include "engines/stark/resources/bonesmesh.h"
#include "engines/stark/resources/bookmark.h"
#include "engines/stark/resources/floor.h"
#include "engines/stark/resources/pattable.h"
#include "engines/stark/resources/textureset.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/services/stateprovider.h"
@ -44,7 +47,7 @@ Object *Item::construct(Object *parent, byte subType, uint16 index, const Common
case kItemSub1:
return new ItemSub1(parent, subType, index, name);
case kItemSub2:
return new ItemSub2(parent, subType, index, name); // TODO
return new ItemSub2(parent, subType, index, name);
case kItemSub3:
return new ItemSub3(parent, subType, index, name);
case kItemSub5:
@ -91,6 +94,10 @@ Item *Item::getSceneInstance() {
return this;
}
bool Item::isClickable() const {
return false;
}
void Item::printData() {
debug("enabled: %d", _enabled);
debug("field_38: %d", _field_38);
@ -103,6 +110,19 @@ void Item::saveLoad(ResourceSerializer *serializer) {
serializer->syncAsSint32LE(_enabled);
}
bool Item::doAction(uint32 action, uint32 hotspotIndex) {
PATTable *table = findChildWithIndex<PATTable>(hotspotIndex);
if (table && table->canPerformAction(action)) {
return table->runScriptForAction(action);
}
return false;
}
bool Item::containsPoint(Common::Point point) {
return indexForPoint(point) != -1;
}
ItemVisual::~ItemVisual() {
delete _renderEntry;
}
@ -111,15 +131,15 @@ ItemVisual::ItemVisual(Object *parent, byte subType, uint16 index, const Common:
Item(parent, subType, index, name),
_renderEntry(nullptr),
_animHierarchy(nullptr),
_currentAnimIndex(-1),
_field_44(1) {
_currentAnimKind(-1),
_clickable(true) {
_renderEntry = new Gfx::RenderEntry(this, getName());
}
void ItemVisual::readData(Formats::XRCReadStream *stream) {
Item::readData(stream);
_field_44 = stream->readUint32LE();
_clickable = stream->readBool();
}
void ItemVisual::onAllLoaded() {
@ -128,7 +148,7 @@ void ItemVisual::onAllLoaded() {
_animHierarchy = findChild<AnimHierarchy>(false);
if (_subType != kItemSub10) {
setAnim(1);
setAnimKind(Anim::kActionUsagePassive);
}
if (!_enabled) {
@ -146,19 +166,23 @@ void ItemVisual::setEnabled(bool enabled) {
}
}
void ItemVisual::setAnim(int32 index) {
bool animNeedsUpdate = index != _currentAnimIndex;
bool ItemVisual::isClickable() const {
return _clickable;
}
_currentAnimIndex = index;
void ItemVisual::setAnimKind(int32 usage) {
bool animNeedsUpdate = usage != _currentAnimKind;
_currentAnimKind = usage;
if (animNeedsUpdate && _animHierarchy) {
_animHierarchy->setItemAnim(this, index);
_animHierarchy->setItemAnim(this, usage);
}
}
void ItemVisual::printData() {
Item::printData();
debug("field_44: %d", _field_44);
debug("clickable: %d", _clickable);
}
Anim *ItemVisual::getAnim() {
@ -175,37 +199,6 @@ Visual *ItemVisual::getVisual() {
return anim->getVisual();
}
bool ItemVisual::containsPoint(Common::Point point) {
Anim *anim = getAnim();
if (anim) {
return anim->containsPoint(point);
}
return false;
}
int ItemVisual::indexForPoint(Common::Point point) {
// TODO: This breaks rather weirdly on subtype 6 and 10
Anim *anim = getAnim();
if (anim) {
return anim->indexForPoint(point);
}
return -1;
}
Gfx::RenderEntry *ItemSub2::getRenderEntry(const Common::Point &positionOffset) {
if (_enabled) {
Visual *visual = getVisual();
_renderEntry->setVisual(visual);
_renderEntry->setPosition(_position + positionOffset);
// _renderEntry->setSortKey(getSortKey());
} else {
_renderEntry->setVisual(nullptr);
}
return _renderEntry;
}
ItemSub13::~ItemSub13() {
}
@ -292,6 +285,36 @@ AnimHierarchy *ItemSub1::findStockAnimHierarchy() {
}
}
ItemSub2::~ItemSub2() {
}
ItemSub2::ItemSub2(Object *parent, byte subType, uint16 index, const Common::String &name) :
ItemVisual(parent, subType, index, name) {
}
Gfx::RenderEntry *ItemSub2::getRenderEntry(const Common::Point &positionOffset) {
if (_enabled) {
setAnimKind(Anim::kUIUsageInventory);
Visual *visual = getVisual();
_renderEntry->setVisual(visual);
_renderEntry->setPosition(Common::Point());
} else {
_renderEntry->setVisual(nullptr);
}
return _renderEntry;
}
Visual *ItemSub2::getActionVisual(bool active) {
if (active) {
return _animHierarchy->getVisualForUsage(Anim::kActionUsageActive);
} else {
return _animHierarchy->getVisualForUsage(Anim::kActionUsagePassive);
}
}
ItemSub3::~ItemSub3() {
}
@ -401,7 +424,7 @@ float ItemSub5610::getSortKey() const {
Floor *floor = global->getCurrent()->getFloor();
if (_floorFaceIndex == -1) {
warning("Undefined floor face index for item '%s'", getName().c_str());
// warning("Undefined floor face index for item '%s'", getName().c_str());
return floor->getDistanceFromCamera(0);
}
@ -436,6 +459,15 @@ Gfx::RenderEntry *ItemSub56::getRenderEntry(const Common::Point &positionOffset)
return _renderEntry;
}
int ItemSub56::indexForPoint(Common::Point point) {
// TODO: This breaks rather weirdly on subtype 6 and 10
Anim *anim = getAnim();
if (anim) {
return anim->indexForPoint(point - _position);
}
return -1;
}
void ItemSub56::printData() {
ItemSub5610::printData();
@ -470,6 +502,15 @@ Gfx::RenderEntry *ItemSub78::getRenderEntry(const Common::Point &positionOffset)
return _renderEntry;
}
int ItemSub78::indexForPoint(Common::Point point) {
// TODO: This breaks rather weirdly on subtype 6 and 10
Anim *anim = getAnim();
if (anim) {
return anim->indexForPoint(point - _position);
}
return -1;
}
void ItemSub78::printData() {
ItemVisual::printData();
@ -525,7 +566,7 @@ void ItemSub10::onEnterLocation() {
_animHierarchy = _referencedItem->findStockAnimHierarchy();
}
setAnim(1);
setAnimKind(Anim::kActorUsageIdle);
}
BonesMesh *ItemSub10::findBonesMesh() {

View file

@ -79,6 +79,7 @@ public:
// Resource API
virtual void readData(Formats::XRCReadStream *stream) override;
void saveLoad(ResourceSerializer *serializer) override;
/** Is the item present in the scene */
bool isEnabled() const;
@ -92,8 +93,12 @@ public:
/** Obtain the concrete instance of an item template */
virtual Item *getSceneInstance();
void saveLoad(ResourceSerializer *serializer) override;
virtual bool containsPoint(Common::Point point) { return false; };
/** If this is false, the item is click through */
virtual bool isClickable() const;
bool doAction(uint32 action, uint32 hotspotIndex);
bool containsPoint(Common::Point point);
virtual int indexForPoint(Common::Point point) { return -1; };
protected:
void printData() override;
@ -118,12 +123,11 @@ public:
// Item API
void setEnabled(bool enabled) override;
bool isClickable() const override;
/** Define the current animation index for the item */
virtual void setAnim(int32 index); // TODO: Just virtual to allow hack in ItemSub2
/** Define the current animation kind for the item */
void setAnimKind(int32 usage);
bool containsPoint(Common::Point point) override;
int indexForPoint(Common::Point point) override;
protected:
// Resource API
void printData() override;
@ -134,20 +138,8 @@ protected:
Gfx::RenderEntry *_renderEntry;
AnimHierarchy *_animHierarchy;
int32 _currentAnimIndex;
uint32 _field_44;
};
// Just a stub-class for now, to reduce the amount of hackery necessary elsewhere.
class ItemSub2 : public ItemVisual {
Common::Point _position;
public:
ItemSub2(Object *parent, byte subType, uint16 index, const Common::String &name) : ItemVisual(parent, subType, index, name), _position(0, 0) {};
// This function does not actually need to be virtual in the super class, but
// it's HACKED this way to work around the action-menu stuff for now.
void setAnim(int32 index) override { ItemVisual::setAnim(0); }
void setPosition(Common::Point pos) { _position = pos; }
Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset);
int32 _currentAnimKind;
bool _clickable;
};
/**
@ -206,6 +198,23 @@ public:
protected:
};
/**
* An inventory item
*/
class ItemSub2: public ItemVisual {
public:
ItemSub2(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~ItemSub2();
// Item API
Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override;
/** Obtain an action menu icon */
Visual *getActionVisual(bool active);
protected:
};
/**
* A level item template
*
@ -279,6 +288,7 @@ public:
// Item API
Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override;
int indexForPoint(Common::Point point) override;
protected:
void printData() override;
@ -333,6 +343,7 @@ public:
// Item API
Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override;
int indexForPoint(Common::Point point) override;
protected:
void printData() override;

View file

@ -23,6 +23,7 @@
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/item.h"
namespace Stark {
namespace Resources {
@ -38,5 +39,22 @@ KnowledgeSet::KnowledgeSet(Object *parent, byte subType, uint16 index, const Com
void KnowledgeSet::printData() {
}
Gfx::RenderEntryArray KnowledgeSet::getInventoryRenderEntries() {
// TODO: Keep and persist inventory items order
Common::Array<Resources::Item *> inventoryItems = listChildren<Resources::Item>(Resources::Item::kItemSub2);
Common::Array<Resources::Item *>::iterator it = inventoryItems.begin();
Gfx::RenderEntryArray result;
int i = 0;
for (; it != inventoryItems.end(); ++it, ++i) {
if (i < 4) continue; // HACK: The first 4 elements are UI elements, so skip them for now.
if ((*it)->isEnabled()) {
result.push_back((*it)->getRenderEntry(Common::Point(0, 0)));
}
}
return result;
}
} // End of namespace Resources
} // End of namespace Stark

View file

@ -25,6 +25,7 @@
#include "common/str.h"
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/object.h"
namespace Stark {
@ -35,6 +36,8 @@ class XRCReadStream;
namespace Resources {
class Item;
/**
* A typed collection of Knowledge resources
*/
@ -53,6 +56,8 @@ public:
KnowledgeSet(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~KnowledgeSet();
Gfx::RenderEntryArray getInventoryRenderEntries();
protected:
void printData() override;
};

View file

@ -59,18 +59,21 @@ void PATTable::printData() {
debug("field_2C: %d", _field_2C);
}
int PATTable::getNumActions() const {
int count = 0;
for (int i = 0; i < _entries.size(); i++) {
ActionArray PATTable::listPossibleActions() const {
ActionArray actions;
for (uint i = 0; i < _entries.size(); i++) {
if (_entries[i]._scriptIndex != -1) {
count++;
// TODO: More rules
actions.push_back(_entries[i]._actionType);
}
}
return count;
}
bool PATTable::canPerformAction(ActionType action) const {
for (int i = 0; i < _entries.size(); i++) {
return actions;
}
bool PATTable::canPerformAction(uint32 action) const {
for (uint i = 0; i < _entries.size(); i++) {
if (_entries[i]._actionType == action && _entries[i]._scriptIndex != -1) {
return true;
}
@ -78,22 +81,14 @@ bool PATTable::canPerformAction(ActionType action) const {
return false;
}
Script *PATTable::getScriptForAction(int action) {
switch (action) {
case kActionUse:
case kActionLook:
case kActionTalk:
case kActionExit:
break;
default:
break; // Not one of the 4 basic ones.
}
for (int i = 0; i < _entries.size(); i++) {
bool PATTable::runScriptForAction(uint32 action) {
for (uint i = 0; i < _entries.size(); i++) {
if (_entries[i]._actionType == action) {
Script *script = findChildWithIndex<Script>(_entries[i]._scriptIndex);
return script;
script->execute(Resources::Script::kCallModePlayerAction);
}
}
return nullptr;
}

View file

@ -37,6 +37,8 @@ namespace Resources {
class Script;
typedef Common::Array<uint32> ActionArray;
class PATTable : public Object {
public:
static const Type::ResourceType TYPE = Type::kPATTable;
@ -51,16 +53,17 @@ public:
PATTable(Object *parent, byte subType, uint16 index, const Common::String &name);
virtual ~PATTable();
Script *getScriptForAction(int action);
ActionArray listPossibleActions() const;
int getNumActions() const;
bool canPerformAction(ActionType action) const;
bool runScriptForAction(uint32 action);
bool canPerformAction(uint32 action) const;
// Resource API
void readData(Formats::XRCReadStream *stream) override;
protected:
struct Entry {
int32 _actionType;
uint32 _actionType;
int32 _scriptIndex;
};

View file

@ -47,7 +47,6 @@ FMVPlayer::~FMVPlayer() {
void FMVPlayer::play(const Common::String &name) {
// TODO: Clear existing
ArchiveLoader *archiveLoader = StarkServices::instance().archiveLoader;
Gfx::Driver *gfx = StarkServices::instance().gfx;
Common::SeekableReadStream *stream = archiveLoader->getExternalFile(name, "Global/");
if (!stream) {

View file

@ -36,7 +36,8 @@ Global::Global() :
_level(nullptr),
_current(nullptr),
_debug(false),
_fastForward(false) {
_fastForward(false),
_inventory(nullptr) {
}
int32 Global::getCurrentChapter() {
@ -52,8 +53,7 @@ void Global::setCurrentChapter(int32 value) {
}
void Global::printInventory(bool printAll) {
Resources::KnowledgeSet *inventory = _level->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kInventory);
Common::Array<Resources::Item*> inventoryItems = inventory->listChildren<Resources::Item>(Resources::Item::kItemSub2);
Common::Array<Resources::Item*> inventoryItems = _inventory->listChildren<Resources::Item>(Resources::Item::kItemSub2);
Common::Array<Resources::Item*>::iterator it = inventoryItems.begin();
for (int i = 0; it != inventoryItems.end(); ++it, i++) {
if (printAll || (*it)->isEnabled()) {
@ -63,25 +63,8 @@ void Global::printInventory(bool printAll) {
}
void Global::enableInventoryItem(int32 num) {
Resources::KnowledgeSet *inventory = _level->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kInventory);
Common::Array<Resources::Item*> inventoryItems = inventory->listChildren<Resources::Item>(Resources::Item::kItemSub2);
Common::Array<Resources::Item*> inventoryItems = _inventory->listChildren<Resources::Item>(Resources::Item::kItemSub2);
inventoryItems[num]->setEnabled(true);
}
Common::Array<Resources::Item*> Global::getInventoryContents() {
Resources::KnowledgeSet *inventory = _level->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kInventory);
Common::Array<Resources::Item*> inventoryItems = inventory->listChildren<Resources::Item>(Resources::Item::kItemSub2);
Common::Array<Resources::Item*>::iterator it = inventoryItems.begin();
Common::Array<Resources::Item*> result;
int i = 0;
for (; it != inventoryItems.end(); ++it, ++i) {
if (i < 4) continue; // HACK: The first 4 elements are UI elements, so skip them for now.
if ((*it)->isEnabled()) {
result.push_back(*it);
}
}
return result;
}
} // End of namespace Stark

View file

@ -34,6 +34,7 @@ class Floor;
class Item; // TODO: Should be ItemSub2
class ItemSub1;
class ItemSub10;
class KnowledgeSet;
class Level;
class Location;
class Root;
@ -86,6 +87,7 @@ public:
bool isFastForward() const { return _fastForward; }
uint getMillisecondsPerGameloop() const { return _millisecondsPerGameloop; }
Resources::ItemSub1 *getApril() const { return _april; }
Resources::KnowledgeSet *getInventory() const { return _inventory; }
void setRoot(Resources::Root *root) { _root = root; }
void setLevel(Resources::Level *level) { _level = level; }
@ -94,13 +96,13 @@ public:
void setFastForward(bool fastForward) { _fastForward = fastForward; }
void setMillisecondsPerGameloop(uint millisecondsPerGameloop) { _millisecondsPerGameloop = millisecondsPerGameloop; }
void setApril(Resources::ItemSub1 *april) { _april = april; }
void setInventory(Resources::KnowledgeSet * inventory) { _inventory = inventory; }
/** Retrieve the current chapter number from the global resource tree */
int32 getCurrentChapter();
/** Temporary HACK to allow us to query the inventory */
void printInventory(bool printAll);
Common::Array<Resources::Item*> getInventoryContents(); // TODO: Create a class for the inventory instead
void enableInventoryItem(int32 num);
/** Change the current chapter */
@ -109,7 +111,7 @@ private:
uint _millisecondsPerGameloop;
Resources::Root *_root;
Resources::Level *_level;
/* Inventory *_inventory; */
Resources::KnowledgeSet *_inventory;
Resources::ItemSub1 *_april;
Current *_current;
bool _debug;

View file

@ -26,6 +26,7 @@
#include "engines/stark/resources/camera.h"
#include "engines/stark/resources/floor.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/layer.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h"
@ -72,7 +73,7 @@ void ResourceProvider::initGlobal() {
// Resources lifecycle update
global->onAllLoaded();
//TODO: Retrieve the inventory from the global tree
_global->setInventory(global->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kInventory));
_global->setApril(global->findChildWithSubtype<Resources::ItemSub1>(Resources::Item::kItemSub1));
}
@ -336,6 +337,8 @@ void ResourceProvider::shutdown() {
_global->setLevel(nullptr);
_global->setRoot(nullptr);
_global->setCurrent(nullptr);
_global->setInventory(nullptr);
_global->setApril(nullptr);
_archiveLoader->unloadUnused();
}

View file

@ -25,11 +25,10 @@
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/texture.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h"
#include "engines/stark/resources/speech.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/resources/floor.h"
#include "engines/stark/resources/object.h"
@ -37,26 +36,23 @@
#include "engines/stark/resources/script.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/actionmenu.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
#include "engines/stark/visual/image.h"
#include "engines/stark/cursor.h"
#include "engines/stark/scene.h"
#include "engines/stark/ui.h"
namespace Stark {
UserInterface::UserInterface(Gfx::Driver *driver, const Cursor *cursor) {
_gfx = driver;
_cursor = cursor;
_actionMenuActive = false;
UserInterface::UserInterface() {
}
UserInterface::~UserInterface() {
}
void UserInterface::init() {
_actionMenu = new ActionMenu(_gfx);
}
void UserInterface::skipCurrentSpeeches() {
Global *global = StarkServices::instance().global;
Current *current = global->getCurrent();
@ -113,233 +109,79 @@ void UserInterface::walkTo(const Common::Point &mouse) {
}
}
void UserInterface::update() {
VisualImageXMG *UserInterface::getActionImage(uint32 itemIndex, bool active) {
// Lookup the action's item in the inventory
Global *global = StarkServices::instance().global;
Resources::KnowledgeSet *inventory = global->getLevel()->findChildWithSubtype<Resources::KnowledgeSet>(Resources::KnowledgeSet::kInventory, true);
// Get the visual for the action
Resources::ItemSub2 *action = inventory->findChildWithIndex<Resources::ItemSub2>(itemIndex);
Visual *visual = action->getActionVisual(active);
return visual->get<VisualImageXMG>();
}
void UserInterface::activateActionMenuOn(Common::Point pos, Resources::Object *activeObject) {
_actionMenuActive = true;
_actionMenuPos = pos;
_actionMenu->clearActions();
int possible = getActionsPossibleForObject(activeObject);
if (possible & kActionLookPossible) {
_actionMenu->enableAction(ActionMenu::kActionEye);
bool UserInterface::itemDoActionAt(Resources::Item *item, uint32 action, const Common::Point &position) {
int32 hotspotIndex = item->indexForPoint(position);
return item->doAction(action, hotspotIndex);
}
if (possible & kActionUsePossible) {
_actionMenu->enableAction(ActionMenu::kActionHand);
}
if (possible & kActionTalkPossible) {
_actionMenu->enableAction(ActionMenu::kActionMouth);
Resources::Item *UserInterface::getItemAtPosition(Common::Point pos, Gfx::RenderEntryArray entries) {
// Render entries are sorted from the farthest to the camera to the nearest
// Loop in reverse order
for (int i = entries.size() - 1; i >= 0; i--) {
Resources::Item *item = entries[i]->getOwner();
if (item->isClickable() && item->containsPoint(pos)) {
return item;
}
}
Gfx::RenderEntry *UserInterface::getEntryAtPosition(Common::Point pos, Gfx::RenderEntryArray entries) {
Gfx::RenderEntryArray::iterator element = entries.begin();
Gfx::RenderEntry *objectAtPos = nullptr;
// We need this scaled. (Optionally, if we want to scale the cursor, we can move the scaling to the setMousePosition-function)
while (element != entries.end()) {
if ((*element)->containsPoint(pos)) {
if (!objectAtPos) {
objectAtPos = *element;
// This assumes that lower sort keys are more important than higher sortkeys.
} else if (Gfx::RenderEntry::compare(*element, objectAtPos)) {
objectAtPos = *element;
}
}
++element;
}
return objectAtPos;
}
// To be specific, this returns the PATTable, so that the distinction on index is resolved,
// any code that needs the item will thus have to traverse back up.
Resources::Object *UserInterface::getObjectForRenderEntryAtPosition(Common::Point pos, Gfx::RenderEntry *entry) {
if (entry == nullptr) {
return nullptr;
}
Resources::Object *owner = (Resources::Object*)entry->getOwner();
if (owner->getType() != Resources::Type::kItem) {
// HACK: We don't have ItemSub2 yet.
if (owner->getType() != Resources::Type::kAnim) {
error("Owner of render entry should be an item, was: %s", owner->getType().getName());
} else if (owner->getType() == Resources::Type::kAnim) {
// HACK
owner = owner->findParent<Resources::Item>();
}
}
int index = entry->indexForPoint(pos);
// No table index
if (index == -1) {
return nullptr;
}
Resources::PATTable *table = owner->findChildWithIndex<Resources::PATTable>(index);
if (table) {
return table;
}
return nullptr;
}
Common::String UserInterface::getMouseHintForObject(Resources::Object *object) {
if (object) {
Resources::Item *item = object->findParent<Resources::Item>();
Common::String UserInterface::getMouseHintForItem(Resources::Item *item) {
if (item) {
if (item->listChildrenRecursive<Resources::PATTable>().size() > 1) {
// Use the PAT Table name if more than one defined in item.
return object->getName();
} else {
//TODO: Items with multiple hotspots
return item->getName();
}
} else {
return object->getName();
}
} else {
return "";
}
Resources::ActionArray UserInterface::getActionsPossibleForObject(Resources::Item *item, const Common::Point &pos) {
if (item == nullptr) {
return Resources::ActionArray();
}
int UserInterface::getActionsPossibleForObject(Resources::Object *object) {
if (object == nullptr) {
return kActionNonePossible;
}
if (object->getType() != Resources::Type::kPATTable) {
error("getActionsPossibleForObject requires a PATTable");
}
Resources::PATTable *table = (Resources::PATTable*)object;
int possible = UserInterface::kActionNonePossible;
if (table->canPerformAction(Resources::PATTable::kActionLook)) {
possible |= kActionLookPossible;
}
if (table->canPerformAction(Resources::PATTable::kActionUse) || isInventoryObject(object)) {
possible |= kActionUsePossible;
}
if (table->canPerformAction(Resources::PATTable::kActionTalk)) {
possible |= kActionTalkPossible;
}
if (table->canPerformAction(Resources::PATTable::kActionExit)) {
possible |= kActionExitPossible;
}
return possible;
int index = item->indexForPoint(pos);
if (index < 0) {
error("Position is outside of item '%s'", item->getName().c_str());
}
bool UserInterface::isInventoryObject(Resources::Object *object) {
if (object->getType() != Resources::Type::kPATTable) {
error("isInventoryObject requires a PATTable");
Resources::PATTable *table = item->findChildWithIndex<Resources::PATTable>(index);
return table->listPossibleActions();
}
Resources::PATTable *table = (Resources::PATTable*)object;
Resources::ItemSub2 *inventoryParent = table->findParent<Resources::ItemSub2>();
if (_actionMenu->isThisYourButton(object) != -1 || inventoryParent->getSubType() != Resources::Item::kItemSub2) {
Resources::ActionArray UserInterface::getStockActionsPossibleForObject(Resources::Item *item, const Common::Point &pos) {
Resources::ActionArray actions = getActionsPossibleForObject(item, pos);
Resources::ActionArray stockActions;
for (uint i = 0; i < actions.size(); i++) {
if (actions[i] < 4) {
stockActions.push_back(actions[i]);
}
}
return stockActions;
}
bool UserInterface::isInventoryObject(Resources::Item *item) {
if (item->getIndex() < 4 || item->getSubType() != Resources::Item::kItemSub2) {
// Do not explicitly add use on action-menu buttons.
inventoryParent = nullptr;
item = nullptr;
}
return inventoryParent != nullptr;
}
bool UserInterface::performActionOnObject(Resources::Object *object, Resources::Object *activeObject, int action) {
if (object->getType() != Resources::Type::kPATTable) {
error("performActionOnObject requires a PATTable");
}
// PATTable of object under cursor
Resources::PATTable *table = (Resources::PATTable*)object;
// Possibilites:
// * Click on something that doesn't take an action
// * Click on something that takes exactly 1 action.
// * Click on something that takes more than 1 action (open action menu)
// * Click in the action menu, which has 0 available actions (TODO)
if (action != -1) {
Resources::Script *script = table->getScriptForAction(action);
if (script != nullptr) {
script->execute(Resources::Script::kCallModePlayerAction);
return true;
} else {
warning("Could not perform action %d on %s", action, table->getName().c_str());
}
}
// Assume all inventory objects need action menu.
if (isInventoryObject(object)) {
return false;
}
if (table->getNumActions() == 0) {
if (activeObject) {
// HACK: presumably this can be resolved by adding SubItem2, and hooking up the item to the actionMenu directly.
int menuResult = _actionMenu->isThisYourButton(object);
Resources::Script *script = nullptr;
if (menuResult != -1 && activeObject->getType() == Resources::Type::kPATTable) {
Resources::PATTable *activeObjectTable = (Resources::PATTable *)activeObject;
if (menuResult == ActionMenu::kActionHand) {
if (isInventoryObject(activeObjectTable)) {
StarkServices::instance().ui->notifySelectedInventoryItem(activeObject);
return true;
}
script = activeObjectTable->getScriptForAction(Resources::PATTable::kActionUse);
} else if (menuResult == ActionMenu::kActionEye) {
script = activeObjectTable->getScriptForAction(Resources::PATTable::kActionLook);
} else if (menuResult == ActionMenu::kActionMouth) {
script = activeObjectTable->getScriptForAction(Resources::PATTable::kActionTalk);
}
if (script != nullptr) {
script->execute(Resources::Script::kCallModePlayerAction);
return true;
} else {
warning("No script, did the action menu buttons misalign again?");
// Return true here too to clear the event as handled, even though we had a wrong PAT.
return true;
}
}
}
return true;
} else if (table->getNumActions() == 1) {
if (table->canPerformAction(Resources::PATTable::kActionLook)) {
table->getScriptForAction(Resources::PATTable::kActionLook)->execute(Resources::Script::kCallModePlayerAction);
}
if (table->canPerformAction(Resources::PATTable::kActionUse)) {
table->getScriptForAction(Resources::PATTable::kActionUse)->execute(Resources::Script::kCallModePlayerAction);
}
if (table->canPerformAction(Resources::PATTable::kActionTalk)) {
table->getScriptForAction(Resources::PATTable::kActionTalk)->execute(Resources::Script::kCallModePlayerAction);
}
if (table->canPerformAction(Resources::PATTable::kActionExit)) {
table->getScriptForAction(Resources::PATTable::kActionExit)->execute(Resources::Script::kCallModePlayerAction);
}
return true;
} else {
// This is where we should trigger the Action Menu
return false;
}
}
Gfx::RenderEntryArray UserInterface::getRenderEntries() {
if (_actionMenuActive) {
return _actionMenu->getRenderEntries();
} else {
return Gfx::RenderEntryArray();
}
}
void UserInterface::render() {
// TODO: Move this elsewhere
Common::String debugStr;
Global *global = StarkServices::instance().global;
Current *current = global->getCurrent();
int32 chapter = global->getCurrentChapter();
debugStr += Common::String::format("location: %02x %02x ", current->getLevel()->getIndex(), current->getLocation()->getIndex());
debugStr += current->getLevel()->getName() + ", " + current->getLocation()->getName();
debugStr += Common::String::format(" chapter: %d", chapter);
Gfx::Texture *debugTexture = _gfx->createTextureFromString(debugStr, 0xF0FF0F00);
_gfx->setScreenViewport(false);
_gfx->drawSurface(debugTexture, Common::Point(0,0));
if (_actionMenuActive) {
_actionMenu->render(_actionMenuPos);
}
delete debugTexture;
return item != nullptr;
}
} // End of namespace Stark

View file

@ -25,6 +25,8 @@
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/pattable.h"
#include "common/scummsys.h"
#include "common/rect.h"
@ -32,6 +34,7 @@ namespace Stark {
class ActionMenu;
class Cursor;
class VisualImageXMG;
namespace Resources {
class Item;
@ -49,11 +52,9 @@ class RenderEntry;
*/
class UserInterface {
public:
UserInterface(Gfx::Driver *driver, const Cursor *cursor);
UserInterface();
~UserInterface();
void init();
/** Skip currently playing speeches */
void skipCurrentSpeeches();
@ -63,44 +64,18 @@ public:
/** Make April try to go to the location under the cursor */
void walkTo(const Common::Point &mouse);
/** Draw the mouse pointer, and any additional currently active UI */
void render();
VisualImageXMG *getActionImage(uint32 itemIndex, bool active);
/** Update the current state of the user interface */
void update();
bool itemDoActionAt(Resources::Item *item, uint32 action, const Common::Point &position);
Gfx::RenderEntry *getEntryAtPosition(Common::Point, Gfx::RenderEntryArray entries);
Resources::Item *getItemAtPosition(Common::Point, Gfx::RenderEntryArray entries);
Resources::Object *getObjectForRenderEntryAtPosition(Common::Point pos, Gfx::RenderEntry *entry);
Common::String getMouseHintForItem(Resources::Item *object);
Common::String getMouseHintForObject(Resources::Object *object);
Resources::ActionArray getActionsPossibleForObject(Resources::Item *item, const Common::Point &pos);
Resources::ActionArray getStockActionsPossibleForObject(Resources::Item *item, const Common::Point &pos);
enum ActionFlags {
kActionNonePossible = 0,
kActionUsePossible = 1,
kActionLookPossible = 2,
kActionTalkPossible = 4,
kActionExitPossible = 8
};
int getActionsPossibleForObject(Resources::Object *object);
/** Attempt to run the relevant action on the object, returns true if action menu is needed, false if no action is possible */
bool performActionOnObject(Resources::Object *object, Resources::Object *activeObject, int action = -1);
void activateActionMenuOn(Common::Point pos, Resources::Object *activeObject);
bool isActionMenuOpen() const { return _actionMenuActive; }
void deactivateActionMenu() { _actionMenuActive = false; }
bool isInventoryObject(Resources::Object *object);
Gfx::RenderEntryArray getRenderEntries();
private:
int _indexForCurrentObject;
Resources::Object *_object;
bool _actionMenuActive;
Common::Point _actionMenuPos;
const Cursor *_cursor;
ActionMenu *_actionMenu;
Gfx::Driver *_gfx;
bool isInventoryObject(Resources::Item *item);
};
} // End of namespace Stark

View file

@ -110,7 +110,7 @@ Common::Error StarkEngine::run() {
_scene = new Scene(_gfx);
_dialogPlayer = new DialogPlayer();
_cursor = new Cursor(_gfx);
_userInterface = new UserInterface(_gfx, _cursor);
_userInterface = new UserInterface();
_ui = new UI(_gfx, _cursor);
// Setup the public services
@ -131,7 +131,6 @@ Common::Error StarkEngine::run() {
_staticProvider->init();
_cursor->init();
_dialogPlayer->init();
_userInterface->init();
// Initialize the UI
_ui->init();
@ -199,7 +198,6 @@ void StarkEngine::mainLoop() {
_resourceProvider->performLocationChange();
}
_userInterface->update();
updateDisplayScene();
g_system->delayMillis(50);

View file

@ -25,8 +25,10 @@
#include "engines/stark/ui.h"
#include "engines/stark/actionmenu.h"
#include "engines/stark/cursor.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/object.h"
@ -45,20 +47,20 @@ namespace Stark {
UI::UI(Gfx::Driver *gfx, Cursor *cursor) :
_gfx(gfx),
_cursor(cursor),
_currentObject(nullptr),
_objectUnderCursor(nullptr),
_hasClicked(false),
_inventoryOpen(false),
_topMenu(nullptr),
_dialogInterface(nullptr),
_inventoryInterface(nullptr),
_selectedInventoryItem(-1),
_exitGame(false),
_fmvPlayer(nullptr)
_fmvPlayer(nullptr),
_actionMenu(nullptr)
{
}
UI::~UI() {
delete _actionMenu;
delete _topMenu;
delete _dialogInterface;
delete _inventoryInterface;
@ -68,15 +70,21 @@ UI::~UI() {
void UI::init() {
_topMenu = new TopMenu();
_dialogInterface = new DialogInterface();
_inventoryInterface = new InventoryInterface();
_inventoryInterface = new InventoryInterface(_gfx, _cursor);
_fmvPlayer = new FMVPlayer();
_actionMenu = new ActionMenu(_gfx, _cursor);
}
void UI::update(Gfx::RenderEntryArray renderEntries, bool keepExisting) {
void UI::update(Gfx::RenderEntryArray renderEntries) {
Common::Point pos = _cursor->getMousePosition();
if (_actionMenu->isVisible() && _actionMenu->isMouseInside()) {
_actionMenu->handleMouseMove();
return;
}
// Check for inventory to avoid mouse-overs from the world poking through.
if (_inventoryOpen && _inventoryInterface->containsPoint(pos) && keepExisting == false) {
if (_inventoryInterface->isVisible() && _inventoryInterface->isMouseInside()) {
renderEntries = _inventoryInterface->getRenderEntries();
}
@ -92,21 +100,17 @@ void UI::update(Gfx::RenderEntryArray renderEntries, bool keepExisting) {
return;
}
pos -= Common::Point(0, Gfx::Driver::kTopBorderHeight);
// Check for game world mouse overs
UserInterface *ui = StarkServices::instance().userInterface;
Gfx::RenderEntry *currentEntry = ui->getEntryAtPosition(pos, renderEntries);
Resources::Object *object = ui->getObjectForRenderEntryAtPosition(pos, currentEntry);
// So that we can run update multiple times, without resetting (i.e. after drawing the action menu)
if (!object && keepExisting) {
return;
} else {
// Subsequent runs ignore sort order of items drawn earlier.
_objectUnderCursor = object;
}
Common::String mouseHint = ui->getMouseHintForObject(_objectUnderCursor);
_objectUnderCursor = ui->getItemAtPosition(pos, renderEntries);
Common::String mouseHint = ui->getMouseHintForItem(_objectUnderCursor);
if (_objectUnderCursor) {
int actionsPossible = ui->getActionsPossibleForObject(_objectUnderCursor);
Resources::ActionArray actionsPossible = ui->getActionsPossibleForObject(_objectUnderCursor, pos);
setCursorDependingOnActionsAvailable(actionsPossible);
} else {
// Not an object
@ -115,60 +119,99 @@ void UI::update(Gfx::RenderEntryArray renderEntries, bool keepExisting) {
_cursor->setMouseHint(_selectedInventoryItemText + mouseHint);
}
void UI::setCursorDependingOnActionsAvailable(int actionsAvailable) {
bool moreThanOneActionPossible = false;
switch (actionsAvailable) {
case UserInterface::kActionLookPossible:
_cursor->setCursorType(Cursor::kEye);
break;
case UserInterface::kActionTalkPossible:
_cursor->setCursorType(Cursor::kMouth);
break;
case UserInterface::kActionUsePossible:
_cursor->setCursorType(Cursor::kHand);
break;
case UserInterface::kActionExitPossible:
_cursor->setCursorType(Cursor::kDefault); // TODO
break;
default:
if (actionsAvailable != 0) {
void UI::setCursorDependingOnActionsAvailable(Resources::ActionArray actionsAvailable) {
if (actionsAvailable.empty()) {
_cursor->setCursorType(Cursor::kPassive);
moreThanOneActionPossible = true;
return;
}
uint32 count = 0;
Cursor::CursorType cursorType;
for (uint i = 0; i < actionsAvailable.size(); i++) {
switch (actionsAvailable[i]) {
case Resources::PATTable::kActionLook:
cursorType = Cursor::kEye;
count++;
break;
case Resources::PATTable::kActionTalk:
cursorType = Cursor::kMouth;
count++;
break;
case Resources::PATTable::kActionUse:
cursorType = Cursor::kHand;
count++;
break;
}
if (moreThanOneActionPossible) {
}
if (count == 1) {
_cursor->setCursorType(cursorType);
} else {
_cursor->setCursorType(Cursor::kActive);
}
}
void UI::handleClick() {
UserInterface *ui = StarkServices::instance().userInterface;
if (_objectUnderCursor) {
if (!ui->performActionOnObject(_objectUnderCursor, _currentObject, _selectedInventoryItem)) {
_currentObject = _objectUnderCursor;
ui->activateActionMenuOn(_cursor->getMousePosition(), _currentObject);
// This currently potentially allows for click-through
if (_actionMenu->isVisible()) {
if (_actionMenu->isMouseInside()) {
_actionMenu->handleClick();
} else {
if (ui->isActionMenuOpen()) {
// If the click resulted in a multi-action possibility, then it was outside the action menu.
ui->deactivateActionMenu();
_currentObject = nullptr;
// If we were in the action menu, then retain the selected item.
} else if (_selectedInventoryItem != -1) {
_inventoryInterface->update();
_selectedInventoryItem = -1;
_selectedInventoryItemText = "";
_actionMenu->close();
}
} else if (_objectUnderCursor) {
Common::Point pos = _cursor->getMousePosition();
pos -= Common::Point(0, Gfx::Driver::kTopBorderHeight);
// Possibilites:
// * Click on something that doesn't take an action
// * Click on something that takes exactly 1 action.
// * Click on something that takes more than 1 action (open action menu)
// * Click in the action menu, which has 0 available actions (TODO)
if (_selectedInventoryItem != -1) {
if (!ui->itemDoActionAt(_objectUnderCursor, _selectedInventoryItem, pos)) {
warning("Could not perform action %d on %s", _selectedInventoryItem, _objectUnderCursor->getName().c_str());
}
} else {
Resources::ActionArray actions = ui->getStockActionsPossibleForObject(_objectUnderCursor, pos);
if (actions.size() == 1) {
for (uint i = 0; i < actions.size(); i++) {
if (actions[i] == Resources::PATTable::kActionLook) {
ui->itemDoActionAt(_objectUnderCursor, Resources::PATTable::kActionLook, pos);
break;
} else if (actions[i] == Resources::PATTable::kActionTalk) {
ui->itemDoActionAt(_objectUnderCursor, Resources::PATTable::kActionTalk, pos);
break;
} else if (actions[i] == Resources::PATTable::kActionUse) {
ui->itemDoActionAt(_objectUnderCursor, Resources::PATTable::kActionUse, pos);
break;
}
}
} else if (actions.size() > 1) {
_actionMenu->open(_objectUnderCursor, pos);
}
}
} else {
ui->walkTo(g_system->getEventManager()->getMousePos());
// // Assume all inventory objects need action menu.
// if (isInventoryObject(item)) {
// return false;
// }
// {
// if (_selectedInventoryItem != -1) {
// _inventoryInterface->update();
// _selectedInventoryItem = -1;
// _selectedInventoryItemText = "";
// }
// }
}
// Check this before handling the menu clicks, otherwise it closes again on the same event.
if (_inventoryOpen && !_inventoryInterface->containsPoint(_cursor->getMousePosition())) {
_inventoryOpen = false;
if (_inventoryInterface->isVisible() && !_inventoryInterface->isMouseInside()) {
_inventoryInterface->close();
}
if (_topMenu->containsPoint(_cursor->getMousePosition())) {
_topMenu->handleClick(_cursor->getMousePosition());
@ -192,18 +235,17 @@ void UI::notifyDialogOptions(const Common::StringArray &options) {
}
void UI::notifyShouldOpenInventory() {
_inventoryOpen = true;
// Make the inventory update it's contents.
_inventoryInterface->update();
_inventoryInterface->open();
}
void UI::notifyFMVRequest(const Common::String &name) {
_fmvPlayer->play(name);
}
void UI::notifySelectedInventoryItem(Resources::Object *selectedItem) {
_selectedInventoryItem = selectedItem->findParent<Resources::ItemSub2>()->getIndex();
_selectedInventoryItemText = selectedItem->findParent<Resources::ItemSub2>()->getName() + " -> ";
void UI::notifySelectedInventoryItem(Resources::Item *selectedItem) {
_selectedInventoryItem = selectedItem->getIndex();
_selectedInventoryItemText = selectedItem->getName() + " -> ";
}
bool UI::isPlayingFMV() const {
@ -219,8 +261,7 @@ void UI::render() {
_fmvPlayer->render();
return;
}
UserInterface *ui = StarkServices::instance().userInterface;
update(ui->getRenderEntries(), true);
// Can't handle clicks before this point, since we need to have updated the mouse-over state to include the UI.
if (_hasClicked) {
handleClick();
@ -231,12 +272,10 @@ void UI::render() {
_topMenu->render();
}
if (_inventoryOpen) {
_inventoryInterface->render();
}
_dialogInterface->render();
ui->render();
_actionMenu->render();
}
} // End of namespace Stark

View file

@ -28,16 +28,19 @@
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/pattable.h"
namespace Stark {
namespace Resources {
class Object;
class Item;
}
namespace Gfx {
class Driver;
}
class ActionMenu;
class DialogInterface;
class InventoryInterface;
class TopMenu;
@ -45,31 +48,30 @@ class Cursor;
class FMVPlayer;
class UI {
ActionMenu *_actionMenu;
FMVPlayer *_fmvPlayer;
DialogInterface *_dialogInterface;
InventoryInterface *_inventoryInterface;
TopMenu *_topMenu;
Resources::Object *_currentObject;
Resources::Object *_objectUnderCursor;
Resources::Item *_objectUnderCursor;
int _selectedInventoryItem;
Common::String _selectedInventoryItemText; // Just a temp HACK untill we get cursors sorted.
Gfx::Driver *_gfx;
Cursor *_cursor;
bool _hasClicked;
bool _exitGame;
bool _inventoryOpen;
void handleClick();
void setCursorDependingOnActionsAvailable(int actionsAvailable);
void setCursorDependingOnActionsAvailable(Resources::ActionArray actionsAvailable);
public:
UI(Gfx::Driver *gfx, Cursor *cursor);
virtual ~UI();
void init();
void update(Gfx::RenderEntryArray renderEntries, bool keepExisting = false);
void update(Gfx::RenderEntryArray renderEntries);
void render();
void notifyClick();
void notifySubtitle(const Common::String &subtitle);
void notifyDialogOptions(const Common::StringArray &options);
void notifySelectedInventoryItem(Resources::Object *selectedItem);
void notifySelectedInventoryItem(Resources::Item *selectedItem);
void notifyShouldExit() { _exitGame = true; }
void notifyShouldOpenInventory();
void notifyFMVRequest(const Common::String &name);

View file

@ -40,7 +40,6 @@ Button::Button(const Common::String &text, Resources::Anim *image, Common::Point
}
void Button::render() {
Gfx::Driver *gfx = StarkServices::instance().gfx;
_image->getVisual()->get<VisualImageXMG>()->render(_position);
}

View file

@ -47,7 +47,7 @@ DialogInterface::~DialogInterface() {
}
void DialogInterface::clearOptions() {
for (int i = 0; i < _options.size(); i++) {
for (uint i = 0; i < _options.size(); i++) {
delete _options[i];
}
_options.clear();
@ -55,7 +55,7 @@ void DialogInterface::clearOptions() {
}
void DialogInterface::renderOptions() {
for (int i = 0; i < _options.size(); i++) {
for (uint i = 0; i < _options.size(); i++) {
_options[i]->render();
}
}
@ -91,7 +91,7 @@ void DialogInterface::notifyDialogOptions(const Common::StringArray &options) {
_texture = nullptr;
int pos = 401;
for (int i = 0; i < options.size(); i++) {
for (uint i = 0; i < options.size(); i++) {
ClickText *text = new ClickText(options[i], Common::Point(0, pos));
_options.push_back(text);
pos += text->getHeight();
@ -105,7 +105,7 @@ void DialogInterface::notifyDialogOptions(const Common::StringArray &options) {
bool DialogInterface::containsPoint(Common::Point point) {
if (_hasOptions && _options.size() > 0) {
for (int i = 0; i < _options.size(); i++) {
for (uint i = 0; i < _options.size(); i++) {
if (_options[i]->containsPoint(point)) {
return true;
}
@ -116,7 +116,7 @@ bool DialogInterface::containsPoint(Common::Point point) {
void DialogInterface::handleMouseOver(Common::Point point) {
if (_hasOptions && _options.size() > 0) {
for (int i = 0; i < _options.size(); i++) {
for (uint i = 0; i < _options.size(); i++) {
if (_options[i]->containsPoint(point)) {
_options[i]->handleMouseOver();
} else {
@ -128,7 +128,7 @@ void DialogInterface::handleMouseOver(Common::Point point) {
void DialogInterface::handleClick(Common::Point point) {
if (_hasOptions && _options.size() > 0) {
for (int i = 0; i < _options.size(); i++) {
for (uint i = 0; i < _options.size(); i++) {
if (_options[i]->containsPoint(point)) {
DialogPlayer *dialogPlayer = StarkServices::instance().dialogPlayer;
dialogPlayer->selectOption(i);

View file

@ -25,6 +25,7 @@
#include "engines/stark/gfx/driver.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/services/global.h"
@ -33,63 +34,54 @@
#include "engines/stark/visual/image.h"
#include "engines/stark/scene.h"
namespace Stark {
InventoryInterface::InventoryInterface() {
InventoryInterface::InventoryInterface(Gfx::Driver *gfx, Cursor *cursor) :
Window(gfx, cursor){
StaticProvider *staticProvider = StarkServices::instance().staticProvider;
_backgroundTexture = staticProvider->getUIItem(StaticProvider::kInventoryBg);
_position = Common::Point(40, 50);
_position = Common::Rect(526, 315);
_position.translate(40, 50);
}
void InventoryInterface::render() {
Gfx::Driver *gfx = StarkServices::instance().gfx;
gfx->setScreenViewport(false);
void InventoryInterface::open() {
_visible = true;
_backgroundTexture->getVisual()->get<VisualImageXMG>()->render(_position);
Scene *scene = StarkServices::instance().scene;
scene->render(_renderEntries);
}
void InventoryInterface::update() {
Global *global = StarkServices::instance().global;
_items = global->getInventoryContents();
_renderEntries = global->getInventory()->getInventoryRenderEntries();
}
Common::Array<Resources::Item*>::iterator it = _items.begin();
void InventoryInterface::close() {
_visible = false;
}
void InventoryInterface::onRender() {
_backgroundTexture->getVisual()->get<VisualImageXMG>()->render(Common::Point(0, 0));
Gfx::RenderEntryArray::iterator it = _renderEntries.begin();
// TODO: Unhardcode positions
Common::Point pos = _position;
Common::Point pos;
int width =_backgroundTexture->getVisual()->get<VisualImageXMG>()->getWidth();
pos.x += 40;
for (;it != _items.end(); ++it) {
_renderEntries.push_back((*it)->getRenderEntry(pos));
pos.x += 40;
if (pos.x > _position.x + width - 40) {
pos.x = _position.x + 20;
pos.y+= 40;
for (;it != _renderEntries.end(); ++it) {
(*it)->setPosition(pos);
(*it)->render(_gfx);
pos.x += 36;
if (pos.x > width - 40) {
pos.x = 20;
pos.y+= 36;
}
}
}
bool InventoryInterface::containsPoint(Common::Point point) {
Common::Rect r;
r.left = _position.x;
r.top = _position.y;
r.setWidth(_backgroundTexture->getVisual()->get<VisualImageXMG>()->getWidth());
r.setHeight(_backgroundTexture->getVisual()->get<VisualImageXMG>()->getHeight());
return r.contains(point);
void InventoryInterface::onMouseMove(const Common::Point &pos) {
}
Common::String InventoryInterface::getMouseHintAtPosition(Common::Point point) {
for (int i = 0; i < _renderEntries.size(); i++) {
int index = _renderEntries[i]->indexForPoint(point);
if (index != -1) {
// TODO: Care about index
return _renderEntries[i]->getOwner()->getName();
}
}
return "";
void InventoryInterface::onClick(const Common::Point &pos) {
}
} // End of namespace Stark

View file

@ -25,6 +25,8 @@
#include "engines/stark/gfx/renderentry.h"
#include "engines/stark/ui/window.h"
#include "common/scummsys.h"
#include "common/rect.h"
#include "common/array.h"
@ -43,19 +45,22 @@ namespace Gfx {
class Texture;
}
class InventoryInterface {
class InventoryInterface : public Window {
Resources::Anim *_backgroundTexture;
Common::Point _position;
Common::Array<Resources::Item*> _items;
Gfx::RenderEntryArray _renderEntries;
public:
InventoryInterface();
InventoryInterface(Gfx::Driver *gfx, Cursor *cursor);
virtual ~InventoryInterface() {}
void render();
void update();
bool containsPoint(Common::Point point);
Common::String getMouseHintAtPosition(Common::Point point);
void open();
void close();
Gfx::RenderEntryArray getRenderEntries() { return _renderEntries; }
protected:
void onMouseMove(const Common::Point &pos) override;
void onClick(const Common::Point &pos) override;
void onRender() override;
};
} // End of namespace Stark

View file

@ -0,0 +1,97 @@
/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "engines/stark/ui/window.h"
#include "engines/stark/cursor.h"
#include "engines/stark/gfx/driver.h"
namespace Stark {
Window::Window(Gfx::Driver *gfx, Cursor *cursor) :
_gfx(gfx),
_cursor(cursor),
_unscaled(false),
_visible(false) {
}
Window::~Window() {
}
void Window::render() {
if (!_visible) {
return;
}
_gfx->setViewport(_position, _unscaled);
onRender();
}
bool Window::isMouseInside() const {
if (!_visible) {
return false;
}
Common::Point mousePos = _cursor->getMousePosition(_unscaled);
return _position.contains(mousePos);
}
bool Window::isVisible() const {
return _visible;
}
Common::Point Window::getMousePosition() const {
Common::Point mousePos = _cursor->getMousePosition(_unscaled);
return mousePos - Common::Point(_position.left, _position.top);
}
Common::Point Window::getScreenMousePosition() const {
return _cursor->getMousePosition(_unscaled);
}
void Window::setCursor(Cursor::CursorType type) {
_cursor->setCursorType(type);
}
void Window::handleMouseMove() {
if (!_visible) {
return;
}
if (isMouseInside()) {
onMouseMove(getMousePosition());
}
}
void Window::handleClick() {
if (!_visible) {
return;
}
if (isMouseInside()) {
onClick(getMousePosition());
}
}
} // End of namespace Stark

72
engines/stark/ui/window.h Normal file
View file

@ -0,0 +1,72 @@
/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef STARK_UI_WINDOW_H
#define STARK_UI_WINDOW_H
#include "common/rect.h"
#include "engines/stark/cursor.h"
namespace Stark {
class Cursor;
namespace Gfx {
class Driver;
}
class Window {
public:
Window(Gfx::Driver *gfx, Cursor *cursor);
virtual ~Window();
void handleMouseMove();
void handleClick();
void render();
bool isMouseInside() const;
bool isVisible() const;
protected:
virtual void onMouseMove(const Common::Point &pos) = 0;
virtual void onClick(const Common::Point &pos) = 0;
virtual void onRender() = 0;
void setCursor(Cursor::CursorType type);
Common::Point getMousePosition() const;
Common::Point getScreenMousePosition() const;
Gfx::Driver *_gfx;
Common::Rect _position;
bool _unscaled;
bool _visible;
private:
Cursor *_cursor;
};
} // End of namespace Stark
#endif // STARK_UI_WINDOW_H