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/gfx/driver.h"
#include "engines/stark/resources/item.h"
#include "engines/stark/resources/anim.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/knowledgeset.h"
#include "engines/stark/resources/level.h"
#include "engines/stark/resources/pattable.h"
#include "engines/stark/services/services.h" #include "engines/stark/services/services.h"
#include "engines/stark/services/userinterface.h"
#include "engines/stark/services/staticprovider.h" #include "engines/stark/services/staticprovider.h"
#include "engines/stark/services/global.h" #include "engines/stark/services/global.h"
@ -39,81 +41,107 @@
namespace Stark { namespace Stark {
ActionMenu::ActionMenu(Gfx::Driver *gfx) : _gfx(gfx) { ActionMenu::ActionMenu(Gfx::Driver *gfx, Cursor *cursor) :
Global *global = StarkServices::instance().global; Window(gfx, cursor) {
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)));
StaticProvider *staticProvider = StarkServices::instance().staticProvider; StaticProvider *staticProvider = StarkServices::instance().staticProvider;
// TODO: Shouldn't use a function called getCursorImage for this, also unhardcode // TODO: Shouldn't use a function called getCursorImage for this, also unhardcode
_background = staticProvider->getCursorImage(5); _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() { ActionMenu::~ActionMenu() {
_renderEntries.clear();
} }
void ActionMenu::render(Common::Point pos) { void ActionMenu::open(Resources::Item *item, const Common::Point &itemClickPos) {
_gfx->setScreenViewport(true); // Drawn unscaled UserInterface *ui = StarkServices::instance().userInterface;
_renderEntries.clear();
if (_handEnabled) { _visible = true;
_renderEntries.push_back(_hand->getRenderEntry(pos));
}
if (_eyeEnabled) {
_renderEntries.push_back(_eye->getRenderEntry(pos));
}
if (_mouthEnabled) {
_renderEntries.push_back(_mouth->getRenderEntry(pos));
}
Scene *scene = StarkServices::instance().scene; Common::Point screenMousePos = getScreenMousePosition();
_background->render(pos); _position = Common::Rect::center(screenMousePos.x, screenMousePos.y, 160, 111);
scene->render(_renderEntries);
_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;
}
}
}
}
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() { void ActionMenu::clearActions() {
_handEnabled = _mouthEnabled = _eyeEnabled = false; for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
_buttons[i].enabled = false;
}
} }
void ActionMenu::enableAction(ActionMenuType action) { void ActionMenu::enableAction(ActionMenuType action) {
switch (action) { _buttons[action].enabled = true;
case kActionHand: }
_handEnabled = true;
break; void ActionMenu::onMouseMove(const Common::Point &pos) {
case kActionMouth: bool hoveringAction = false;
_mouthEnabled = true; for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
break; if (_buttons[i].enabled && _buttons[i].rect.contains(pos)) {
case kActionEye: hoveringAction = true;
_eyeEnabled = true; }
break; }
default:
error("Invalid action type in ActionMenu::enableAction"); if (hoveringAction) {
setCursor(Cursor::kActive);
} else {
setCursor(Cursor::kDefault);
} }
} }
int ActionMenu::isThisYourButton(Resources::Object *object) { void ActionMenu::onClick(const Common::Point &pos) {
Resources::Item *item = object->findParent<Resources::Item>(); UserInterface *ui = StarkServices::instance().userInterface;
if (item == _mouth) {
return kActionMouth; for (uint i = 0; i < ARRAYSIZE(_buttons); i++) {
} else if (item == _eye) { if (_buttons[i].enabled && _buttons[i].rect.contains(pos)) {
return kActionEye; ui->itemDoActionAt(_item, _buttons[i].action, _itemClickPos);
} else if (item == _hand) { close();
return kActionHand; }
} else {
return -1;
} }
} }
} // End of namespace Stark } // End of namespace Stark

View file

@ -25,41 +25,55 @@
#include "engines/stark/gfx/renderentry.h" #include "engines/stark/gfx/renderentry.h"
#include "engines/stark/ui/window.h"
namespace Stark { namespace Stark {
class Cursor;
class VisualImageXMG; class VisualImageXMG;
namespace Resources { namespace Resources {
class ItemSub2; class Item;
class Object;
} }
class ActionMenu { class ActionMenu : public Window {
Resources::ItemSub2 *_eye;
Resources::ItemSub2 *_hand;
Resources::ItemSub2 *_mouth;
VisualImageXMG *_background;
bool _eyeEnabled;
bool _handEnabled;
bool _mouthEnabled;
Gfx::RenderEntryArray _renderEntries;
Gfx::Driver *_gfx;
public: public:
enum ActionMenuType { ActionMenu(Gfx::Driver *gfx, Cursor *cursor);
kActionHand,
kActionMouth,
kActionEye
};
ActionMenu(Gfx::Driver *gfx);
~ActionMenu(); ~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 clearActions();
void enableAction(ActionMenuType action); void enableAction(ActionMenuType action);
Gfx::RenderEntryArray getRenderEntries() { return _renderEntries; }
int isThisYourButton(Resources::Object *object);
}; };
} // End of namespace Stark } // End of namespace Stark
#endif #endif

View file

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

View file

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

View file

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

View file

@ -44,8 +44,10 @@ public:
virtual void init() = 0; virtual void init() = 0;
virtual void setGameViewport() = 0; virtual void setGameViewport() = 0; // deprecated
virtual void setScreenViewport(bool noScaling) = 0; virtual void setScreenViewport(bool noScaling) = 0; // deprecated
virtual void setViewport(Common::Rect rect, bool noScaling) = 0;
/** Get the screen viewport in actual resolution */ /** Get the screen viewport in actual resolution */
Common::Rect getScreenViewport() { return _screenViewport; } 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()); 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 { Math::Vector2d OpenGLSDriver::scaled(float x, float y) const {
return Math::Vector2d(x / (float) _unscaledViewport.width(), y / (float) _unscaledViewport.height()); return Math::Vector2d(x / (float) _unscaledViewport.width(), y / (float) _unscaledViewport.height());
} }

View file

@ -49,6 +49,7 @@ public:
void setGameViewport() override; void setGameViewport() override;
void setScreenViewport(bool noScaling) 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; 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/smacker.h"
#include "engines/stark/visual/visual.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 Stark {
namespace Gfx { namespace Gfx {
RenderEntry::RenderEntry(Resources::Object *owner, const Common::String &name) : RenderEntry::RenderEntry(Resources::Item *owner, const Common::String &name) :
_visual(nullptr), _visual(nullptr),
_name(name), _name(name),
_owner(owner), _owner(owner),
@ -87,51 +82,5 @@ bool RenderEntry::compare(const RenderEntry *x, const RenderEntry *y) {
return x->_sortKey < y->_sortKey; 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 Gfx
} // End of namespace Stark } // End of namespace Stark

View file

@ -34,7 +34,7 @@ namespace Stark {
class Visual; class Visual;
namespace Resources { namespace Resources {
class Object; class Item;
} }
namespace Gfx { namespace Gfx {
@ -43,7 +43,7 @@ class Driver;
class RenderEntry { class RenderEntry {
public: public:
RenderEntry(Resources::Object *owner, const Common::String &name); RenderEntry(Resources::Item *owner, const Common::String &name);
virtual ~RenderEntry() {}; virtual ~RenderEntry() {};
void render(Driver *gfx); void render(Driver *gfx);
@ -53,17 +53,15 @@ public:
void setPosition3D(const Math::Vector3d &position, float direction); void setPosition3D(const Math::Vector3d &position, float direction);
void setSortKey(float sortKey); 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 */ /** Gets the owner-object */
Resources::Object *getOwner() { return _owner; } Resources::Item *getOwner() const { return _owner; }
/** Compare two render entries by their sort keys */ /** Compare two render entries by their sort keys */
static bool compare(const RenderEntry *x, const RenderEntry *y); static bool compare(const RenderEntry *x, const RenderEntry *y);
protected: protected:
Common::String _name; Common::String _name;
Resources::Object *_owner; Resources::Item *_owner;
Visual *_visual; Visual *_visual;
Common::Point _position; Common::Point _position;

View file

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

View file

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

View file

@ -60,6 +60,23 @@ public:
kAnimSkeleton = 4 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 */ /** Anim factory */
static Object *construct(Object *parent, byte subType, uint16 index, const Common::String &name); 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 */ /** Check is the animation is being used by an item */
bool isInUse(); bool isInUse();
/** Obtain the purpose of this anim */
uint32 getUsage() const;
virtual bool containsPoint(Common::Point point) { return false; } virtual bool containsPoint(Common::Point point) { return false; }
virtual int indexForPoint(Common::Point point) { return -1; } virtual int indexForPoint(Common::Point point) { return -1; }
protected: protected:
virtual void printData() override; virtual void printData() override;
uint32 _field_30; uint32 _usage;
uint32 _currentFrame; uint32 _currentFrame;
uint32 _numFrames; uint32 _numFrames;
int32 _refCount; int32 _refCount;

View file

@ -38,7 +38,7 @@ AnimHierarchy::~AnimHierarchy() {
AnimHierarchy::AnimHierarchy(Object *parent, byte subType, uint16 index, const Common::String &name) : AnimHierarchy::AnimHierarchy(Object *parent, byte subType, uint16 index, const Common::String &name) :
Object(parent, subType, index, name), Object(parent, subType, index, name),
_animIndex(0), _animUsage(0),
_currentAnim(nullptr), _currentAnim(nullptr),
_animHierarchy(nullptr), _animHierarchy(nullptr),
_field_5C(0) { _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); unselectItemAnim(item);
_animIndex = index; _animUsage = usage;
selectItemAnim(item); selectItemAnim(item);
} }
@ -93,7 +93,7 @@ void AnimHierarchy::unselectItemAnim(ItemVisual *item) {
void AnimHierarchy::selectItemAnim(ItemVisual *item) { void AnimHierarchy::selectItemAnim(ItemVisual *item) {
// Search for an animation with the appropriate index // Search for an animation with the appropriate index
for (uint i = 0; i < _animations.size(); i++) { for (uint i = 0; i < _animations.size(); i++) {
if (_animations[i]->getIndex() == _animIndex) { if (_animations[i]->getUsage() == _animUsage) {
_currentAnim = _animations[i]; _currentAnim = _animations[i];
break; break;
} }
@ -125,6 +125,22 @@ TextureSet *AnimHierarchy::findTextureSet(uint32 textureType) {
return findChildWithSubtype<TextureSet>(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() { void AnimHierarchy::printData() {
for (uint i = 0; i < _animationReferences.size(); i++) { for (uint i = 0; i < _animationReferences.size(); i++) {
debug("anim %d: %s", i, _animationReferences[i].describe().c_str()); debug("anim %d: %s", i, _animationReferences[i].describe().c_str());

View file

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

View file

@ -26,12 +26,15 @@
#include "engines/stark/formats/xrc.h" #include "engines/stark/formats/xrc.h"
#include "engines/stark/gfx/renderentry.h" #include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/anim.h" #include "engines/stark/resources/anim.h"
#include "engines/stark/resources/animhierarchy.h" #include "engines/stark/resources/animhierarchy.h"
#include "engines/stark/resources/bonesmesh.h" #include "engines/stark/resources/bonesmesh.h"
#include "engines/stark/resources/bookmark.h" #include "engines/stark/resources/bookmark.h"
#include "engines/stark/resources/floor.h" #include "engines/stark/resources/floor.h"
#include "engines/stark/resources/pattable.h"
#include "engines/stark/resources/textureset.h" #include "engines/stark/resources/textureset.h"
#include "engines/stark/services/global.h" #include "engines/stark/services/global.h"
#include "engines/stark/services/services.h" #include "engines/stark/services/services.h"
#include "engines/stark/services/stateprovider.h" #include "engines/stark/services/stateprovider.h"
@ -44,7 +47,7 @@ Object *Item::construct(Object *parent, byte subType, uint16 index, const Common
case kItemSub1: case kItemSub1:
return new ItemSub1(parent, subType, index, name); return new ItemSub1(parent, subType, index, name);
case kItemSub2: case kItemSub2:
return new ItemSub2(parent, subType, index, name); // TODO return new ItemSub2(parent, subType, index, name);
case kItemSub3: case kItemSub3:
return new ItemSub3(parent, subType, index, name); return new ItemSub3(parent, subType, index, name);
case kItemSub5: case kItemSub5:
@ -91,6 +94,10 @@ Item *Item::getSceneInstance() {
return this; return this;
} }
bool Item::isClickable() const {
return false;
}
void Item::printData() { void Item::printData() {
debug("enabled: %d", _enabled); debug("enabled: %d", _enabled);
debug("field_38: %d", _field_38); debug("field_38: %d", _field_38);
@ -103,6 +110,19 @@ void Item::saveLoad(ResourceSerializer *serializer) {
serializer->syncAsSint32LE(_enabled); 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() { ItemVisual::~ItemVisual() {
delete _renderEntry; delete _renderEntry;
} }
@ -111,15 +131,15 @@ ItemVisual::ItemVisual(Object *parent, byte subType, uint16 index, const Common:
Item(parent, subType, index, name), Item(parent, subType, index, name),
_renderEntry(nullptr), _renderEntry(nullptr),
_animHierarchy(nullptr), _animHierarchy(nullptr),
_currentAnimIndex(-1), _currentAnimKind(-1),
_field_44(1) { _clickable(true) {
_renderEntry = new Gfx::RenderEntry(this, getName()); _renderEntry = new Gfx::RenderEntry(this, getName());
} }
void ItemVisual::readData(Formats::XRCReadStream *stream) { void ItemVisual::readData(Formats::XRCReadStream *stream) {
Item::readData(stream); Item::readData(stream);
_field_44 = stream->readUint32LE(); _clickable = stream->readBool();
} }
void ItemVisual::onAllLoaded() { void ItemVisual::onAllLoaded() {
@ -128,7 +148,7 @@ void ItemVisual::onAllLoaded() {
_animHierarchy = findChild<AnimHierarchy>(false); _animHierarchy = findChild<AnimHierarchy>(false);
if (_subType != kItemSub10) { if (_subType != kItemSub10) {
setAnim(1); setAnimKind(Anim::kActionUsagePassive);
} }
if (!_enabled) { if (!_enabled) {
@ -146,19 +166,23 @@ void ItemVisual::setEnabled(bool enabled) {
} }
} }
void ItemVisual::setAnim(int32 index) { bool ItemVisual::isClickable() const {
bool animNeedsUpdate = index != _currentAnimIndex; return _clickable;
}
_currentAnimIndex = index; void ItemVisual::setAnimKind(int32 usage) {
bool animNeedsUpdate = usage != _currentAnimKind;
_currentAnimKind = usage;
if (animNeedsUpdate && _animHierarchy) { if (animNeedsUpdate && _animHierarchy) {
_animHierarchy->setItemAnim(this, index); _animHierarchy->setItemAnim(this, usage);
} }
} }
void ItemVisual::printData() { void ItemVisual::printData() {
Item::printData(); Item::printData();
debug("field_44: %d", _field_44); debug("clickable: %d", _clickable);
} }
Anim *ItemVisual::getAnim() { Anim *ItemVisual::getAnim() {
@ -175,37 +199,6 @@ Visual *ItemVisual::getVisual() {
return anim->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() { 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() { ItemSub3::~ItemSub3() {
} }
@ -401,7 +424,7 @@ float ItemSub5610::getSortKey() const {
Floor *floor = global->getCurrent()->getFloor(); Floor *floor = global->getCurrent()->getFloor();
if (_floorFaceIndex == -1) { 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); return floor->getDistanceFromCamera(0);
} }
@ -436,6 +459,15 @@ Gfx::RenderEntry *ItemSub56::getRenderEntry(const Common::Point &positionOffset)
return _renderEntry; 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() { void ItemSub56::printData() {
ItemSub5610::printData(); ItemSub5610::printData();
@ -470,6 +502,15 @@ Gfx::RenderEntry *ItemSub78::getRenderEntry(const Common::Point &positionOffset)
return _renderEntry; 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() { void ItemSub78::printData() {
ItemVisual::printData(); ItemVisual::printData();
@ -525,7 +566,7 @@ void ItemSub10::onEnterLocation() {
_animHierarchy = _referencedItem->findStockAnimHierarchy(); _animHierarchy = _referencedItem->findStockAnimHierarchy();
} }
setAnim(1); setAnimKind(Anim::kActorUsageIdle);
} }
BonesMesh *ItemSub10::findBonesMesh() { BonesMesh *ItemSub10::findBonesMesh() {

View file

@ -79,6 +79,7 @@ public:
// Resource API // Resource API
virtual void readData(Formats::XRCReadStream *stream) override; virtual void readData(Formats::XRCReadStream *stream) override;
void saveLoad(ResourceSerializer *serializer) override;
/** Is the item present in the scene */ /** Is the item present in the scene */
bool isEnabled() const; bool isEnabled() const;
@ -92,8 +93,12 @@ public:
/** Obtain the concrete instance of an item template */ /** Obtain the concrete instance of an item template */
virtual Item *getSceneInstance(); virtual Item *getSceneInstance();
void saveLoad(ResourceSerializer *serializer) override; /** If this is false, the item is click through */
virtual bool containsPoint(Common::Point point) { return false; }; virtual bool isClickable() const;
bool doAction(uint32 action, uint32 hotspotIndex);
bool containsPoint(Common::Point point);
virtual int indexForPoint(Common::Point point) { return -1; }; virtual int indexForPoint(Common::Point point) { return -1; };
protected: protected:
void printData() override; void printData() override;
@ -118,12 +123,11 @@ public:
// Item API // Item API
void setEnabled(bool enabled) override; void setEnabled(bool enabled) override;
bool isClickable() const override;
/** Define the current animation index for the item */ /** Define the current animation kind for the item */
virtual void setAnim(int32 index); // TODO: Just virtual to allow hack in ItemSub2 void setAnimKind(int32 usage);
bool containsPoint(Common::Point point) override;
int indexForPoint(Common::Point point) override;
protected: protected:
// Resource API // Resource API
void printData() override; void printData() override;
@ -134,20 +138,8 @@ protected:
Gfx::RenderEntry *_renderEntry; Gfx::RenderEntry *_renderEntry;
AnimHierarchy *_animHierarchy; AnimHierarchy *_animHierarchy;
int32 _currentAnimIndex; int32 _currentAnimKind;
uint32 _field_44; bool _clickable;
};
// 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);
}; };
/** /**
@ -206,6 +198,23 @@ public:
protected: 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 * A level item template
* *
@ -279,6 +288,7 @@ public:
// Item API // Item API
Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override; Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override;
int indexForPoint(Common::Point point) override;
protected: protected:
void printData() override; void printData() override;
@ -333,6 +343,7 @@ public:
// Item API // Item API
Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override; Gfx::RenderEntry *getRenderEntry(const Common::Point &positionOffset) override;
int indexForPoint(Common::Point point) override;
protected: protected:
void printData() override; void printData() override;

View file

@ -23,6 +23,7 @@
#include "engines/stark/resources/knowledgeset.h" #include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/formats/xrc.h" #include "engines/stark/formats/xrc.h"
#include "engines/stark/resources/item.h"
namespace Stark { namespace Stark {
namespace Resources { namespace Resources {
@ -38,5 +39,22 @@ KnowledgeSet::KnowledgeSet(Object *parent, byte subType, uint16 index, const Com
void KnowledgeSet::printData() { 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 Resources
} // End of namespace Stark } // End of namespace Stark

View file

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

View file

@ -59,18 +59,21 @@ void PATTable::printData() {
debug("field_2C: %d", _field_2C); debug("field_2C: %d", _field_2C);
} }
int PATTable::getNumActions() const { ActionArray PATTable::listPossibleActions() const {
int count = 0; ActionArray actions;
for (int i = 0; i < _entries.size(); i++) {
for (uint i = 0; i < _entries.size(); i++) {
if (_entries[i]._scriptIndex != -1) { if (_entries[i]._scriptIndex != -1) {
count++; // TODO: More rules
actions.push_back(_entries[i]._actionType);
} }
} }
return count;
return actions;
} }
bool PATTable::canPerformAction(ActionType action) const { bool PATTable::canPerformAction(uint32 action) const {
for (int i = 0; i < _entries.size(); i++) { for (uint i = 0; i < _entries.size(); i++) {
if (_entries[i]._actionType == action && _entries[i]._scriptIndex != -1) { if (_entries[i]._actionType == action && _entries[i]._scriptIndex != -1) {
return true; return true;
} }
@ -78,22 +81,14 @@ bool PATTable::canPerformAction(ActionType action) const {
return false; return false;
} }
Script *PATTable::getScriptForAction(int action) { bool PATTable::runScriptForAction(uint32 action) {
switch (action) { for (uint i = 0; i < _entries.size(); i++) {
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++) {
if (_entries[i]._actionType == action) { if (_entries[i]._actionType == action) {
Script *script = findChildWithIndex<Script>(_entries[i]._scriptIndex); Script *script = findChildWithIndex<Script>(_entries[i]._scriptIndex);
return script; script->execute(Resources::Script::kCallModePlayerAction);
} }
} }
return nullptr; return nullptr;
} }

View file

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

View file

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

View file

@ -36,7 +36,8 @@ Global::Global() :
_level(nullptr), _level(nullptr),
_current(nullptr), _current(nullptr),
_debug(false), _debug(false),
_fastForward(false) { _fastForward(false),
_inventory(nullptr) {
} }
int32 Global::getCurrentChapter() { int32 Global::getCurrentChapter() {
@ -52,8 +53,7 @@ void Global::setCurrentChapter(int32 value) {
} }
void Global::printInventory(bool printAll) { 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(); Common::Array<Resources::Item*>::iterator it = inventoryItems.begin();
for (int i = 0; it != inventoryItems.end(); ++it, i++) { for (int i = 0; it != inventoryItems.end(); ++it, i++) {
if (printAll || (*it)->isEnabled()) { if (printAll || (*it)->isEnabled()) {
@ -63,25 +63,8 @@ void Global::printInventory(bool printAll) {
} }
void Global::enableInventoryItem(int32 num) { 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); 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 } // End of namespace Stark

View file

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

View file

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

View file

@ -25,11 +25,10 @@
#include "engines/stark/gfx/driver.h" #include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/texture.h" #include "engines/stark/gfx/texture.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/level.h" #include "engines/stark/resources/level.h"
#include "engines/stark/resources/location.h" #include "engines/stark/resources/location.h"
#include "engines/stark/resources/speech.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/floor.h"
#include "engines/stark/resources/object.h" #include "engines/stark/resources/object.h"
@ -37,26 +36,23 @@
#include "engines/stark/resources/script.h" #include "engines/stark/resources/script.h"
#include "engines/stark/resources/item.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/cursor.h"
#include "engines/stark/scene.h" #include "engines/stark/scene.h"
#include "engines/stark/ui.h" #include "engines/stark/ui.h"
namespace Stark { namespace Stark {
UserInterface::UserInterface(Gfx::Driver *driver, const Cursor *cursor) { UserInterface::UserInterface() {
_gfx = driver;
_cursor = cursor;
_actionMenuActive = false;
} }
UserInterface::~UserInterface() { UserInterface::~UserInterface() {
} }
void UserInterface::init() {
_actionMenu = new ActionMenu(_gfx);
}
void UserInterface::skipCurrentSpeeches() { void UserInterface::skipCurrentSpeeches() {
Global *global = StarkServices::instance().global; Global *global = StarkServices::instance().global;
Current *current = global->getCurrent(); 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) { bool UserInterface::itemDoActionAt(Resources::Item *item, uint32 action, const Common::Point &position) {
_actionMenuActive = true; int32 hotspotIndex = item->indexForPoint(position);
_actionMenuPos = pos; return item->doAction(action, hotspotIndex);
_actionMenu->clearActions();
int possible = getActionsPossibleForObject(activeObject);
if (possible & kActionLookPossible) {
_actionMenu->enableAction(ActionMenu::kActionEye);
}
if (possible & kActionUsePossible) {
_actionMenu->enableAction(ActionMenu::kActionHand);
}
if (possible & kActionTalkPossible) {
_actionMenu->enableAction(ActionMenu::kActionMouth);
}
} }
Gfx::RenderEntry *UserInterface::getEntryAtPosition(Common::Point pos, Gfx::RenderEntryArray entries) { Resources::Item *UserInterface::getItemAtPosition(Common::Point pos, Gfx::RenderEntryArray entries) {
Gfx::RenderEntryArray::iterator element = entries.begin(); // Render entries are sorted from the farthest to the camera to the nearest
Gfx::RenderEntry *objectAtPos = nullptr; // Loop in reverse order
// We need this scaled. (Optionally, if we want to scale the cursor, we can move the scaling to the setMousePosition-function) for (int i = entries.size() - 1; i >= 0; i--) {
while (element != entries.end()) { Resources::Item *item = entries[i]->getOwner();
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, if (item->isClickable() && item->containsPoint(pos)) {
// any code that needs the item will thus have to traverse back up. return item;
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; return nullptr;
} }
Common::String UserInterface::getMouseHintForObject(Resources::Object *object) { Common::String UserInterface::getMouseHintForItem(Resources::Item *item) {
if (object) { if (item) {
Resources::Item *item = object->findParent<Resources::Item>(); //TODO: Items with multiple hotspots
if (item) { return item->getName();
if (item->listChildrenRecursive<Resources::PATTable>().size() > 1) { }
// Use the PAT Table name if more than one defined in item.
return object->getName(); return "";
} else { }
return item->getName();
} Resources::ActionArray UserInterface::getActionsPossibleForObject(Resources::Item *item, const Common::Point &pos) {
} else { if (item == nullptr) {
return object->getName(); return Resources::ActionArray();
}
int index = item->indexForPoint(pos);
if (index < 0) {
error("Position is outside of item '%s'", item->getName().c_str());
}
Resources::PATTable *table = item->findChildWithIndex<Resources::PATTable>(index);
return table->listPossibleActions();
}
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]);
} }
} else {
return "";
} }
return stockActions;
} }
int UserInterface::getActionsPossibleForObject(Resources::Object *object) { bool UserInterface::isInventoryObject(Resources::Item *item) {
if (object == nullptr) { if (item->getIndex() < 4 || item->getSubType() != Resources::Item::kItemSub2) {
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;
}
bool UserInterface::isInventoryObject(Resources::Object *object) {
if (object->getType() != Resources::Type::kPATTable) {
error("isInventoryObject requires a PATTable");
}
Resources::PATTable *table = (Resources::PATTable*)object;
Resources::ItemSub2 *inventoryParent = table->findParent<Resources::ItemSub2>();
if (_actionMenu->isThisYourButton(object) != -1 || inventoryParent->getSubType() != Resources::Item::kItemSub2) {
// Do not explicitly add use on action-menu buttons. // Do not explicitly add use on action-menu buttons.
inventoryParent = nullptr; item = nullptr;
} }
return inventoryParent != nullptr; return item != 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;
} }
} // End of namespace Stark } // End of namespace Stark

View file

@ -25,6 +25,8 @@
#include "engines/stark/gfx/renderentry.h" #include "engines/stark/gfx/renderentry.h"
#include "engines/stark/resources/pattable.h"
#include "common/scummsys.h" #include "common/scummsys.h"
#include "common/rect.h" #include "common/rect.h"
@ -32,6 +34,7 @@ namespace Stark {
class ActionMenu; class ActionMenu;
class Cursor; class Cursor;
class VisualImageXMG;
namespace Resources { namespace Resources {
class Item; class Item;
@ -49,11 +52,9 @@ class RenderEntry;
*/ */
class UserInterface { class UserInterface {
public: public:
UserInterface(Gfx::Driver *driver, const Cursor *cursor); UserInterface();
~UserInterface(); ~UserInterface();
void init();
/** Skip currently playing speeches */ /** Skip currently playing speeches */
void skipCurrentSpeeches(); void skipCurrentSpeeches();
@ -63,44 +64,18 @@ public:
/** Make April try to go to the location under the cursor */ /** Make April try to go to the location under the cursor */
void walkTo(const Common::Point &mouse); void walkTo(const Common::Point &mouse);
/** Draw the mouse pointer, and any additional currently active UI */ VisualImageXMG *getActionImage(uint32 itemIndex, bool active);
void render();
bool itemDoActionAt(Resources::Item *item, uint32 action, const Common::Point &position);
Resources::Item *getItemAtPosition(Common::Point, Gfx::RenderEntryArray entries);
/** Update the current state of the user interface */ Common::String getMouseHintForItem(Resources::Item *object);
void update();
Gfx::RenderEntry *getEntryAtPosition(Common::Point, Gfx::RenderEntryArray entries); Resources::ActionArray getActionsPossibleForObject(Resources::Item *item, const Common::Point &pos);
Resources::ActionArray getStockActionsPossibleForObject(Resources::Item *item, const Common::Point &pos);
Resources::Object *getObjectForRenderEntryAtPosition(Common::Point pos, Gfx::RenderEntry *entry);
Common::String getMouseHintForObject(Resources::Object *object); bool isInventoryObject(Resources::Item *item);
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;
}; };
} // End of namespace Stark } // End of namespace Stark

View file

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

View file

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

View file

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

View file

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

View file

@ -25,6 +25,7 @@
#include "engines/stark/gfx/driver.h" #include "engines/stark/gfx/driver.h"
#include "engines/stark/resources/anim.h" #include "engines/stark/resources/anim.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/item.h" #include "engines/stark/resources/item.h"
#include "engines/stark/services/global.h" #include "engines/stark/services/global.h"
@ -33,63 +34,54 @@
#include "engines/stark/visual/image.h" #include "engines/stark/visual/image.h"
#include "engines/stark/scene.h"
namespace Stark { namespace Stark {
InventoryInterface::InventoryInterface() { InventoryInterface::InventoryInterface(Gfx::Driver *gfx, Cursor *cursor) :
Window(gfx, cursor){
StaticProvider *staticProvider = StarkServices::instance().staticProvider; StaticProvider *staticProvider = StarkServices::instance().staticProvider;
_backgroundTexture = staticProvider->getUIItem(StaticProvider::kInventoryBg); _backgroundTexture = staticProvider->getUIItem(StaticProvider::kInventoryBg);
_position = Common::Point(40, 50);
_position = Common::Rect(526, 315);
_position.translate(40, 50);
} }
void InventoryInterface::render() { void InventoryInterface::open() {
Gfx::Driver *gfx = StarkServices::instance().gfx; _visible = true;
gfx->setScreenViewport(false);
_backgroundTexture->getVisual()->get<VisualImageXMG>()->render(_position);
Scene *scene = StarkServices::instance().scene;
scene->render(_renderEntries);
}
void InventoryInterface::update() {
Global *global = StarkServices::instance().global; 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 // TODO: Unhardcode positions
Common::Point pos = _position; Common::Point pos;
int width =_backgroundTexture->getVisual()->get<VisualImageXMG>()->getWidth(); int width =_backgroundTexture->getVisual()->get<VisualImageXMG>()->getWidth();
pos.x += 40; pos.x += 40;
for (;it != _items.end(); ++it) { for (;it != _renderEntries.end(); ++it) {
_renderEntries.push_back((*it)->getRenderEntry(pos)); (*it)->setPosition(pos);
pos.x += 40; (*it)->render(_gfx);
if (pos.x > _position.x + width - 40) {
pos.x = _position.x + 20; pos.x += 36;
pos.y+= 40; if (pos.x > width - 40) {
pos.x = 20;
pos.y+= 36;
} }
} }
} }
bool InventoryInterface::containsPoint(Common::Point point) { void InventoryInterface::onMouseMove(const Common::Point &pos) {
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);
} }
Common::String InventoryInterface::getMouseHintAtPosition(Common::Point point) { void InventoryInterface::onClick(const Common::Point &pos) {
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 "";
} }
} // End of namespace Stark } // End of namespace Stark

View file

@ -25,6 +25,8 @@
#include "engines/stark/gfx/renderentry.h" #include "engines/stark/gfx/renderentry.h"
#include "engines/stark/ui/window.h"
#include "common/scummsys.h" #include "common/scummsys.h"
#include "common/rect.h" #include "common/rect.h"
#include "common/array.h" #include "common/array.h"
@ -43,21 +45,24 @@ namespace Gfx {
class Texture; class Texture;
} }
class InventoryInterface { class InventoryInterface : public Window {
Resources::Anim *_backgroundTexture; Resources::Anim *_backgroundTexture;
Common::Point _position;
Common::Array<Resources::Item*> _items;
Gfx::RenderEntryArray _renderEntries; Gfx::RenderEntryArray _renderEntries;
public: public:
InventoryInterface(); InventoryInterface(Gfx::Driver *gfx, Cursor *cursor);
virtual ~InventoryInterface() {} virtual ~InventoryInterface() {}
void render();
void update(); void open();
bool containsPoint(Common::Point point); void close();
Common::String getMouseHintAtPosition(Common::Point point);
Gfx::RenderEntryArray getRenderEntries() { return _renderEntries; } 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 } // End of namespace Stark
#endif // STARK_DIALOG_INTERFACE_H #endif // STARK_DIALOG_INTERFACE_H

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