mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-08 11:57:25 +00:00
909 lines
27 KiB
C++
909 lines
27 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "math/matrix3.h"
|
|
#include "common/algorithm.h"
|
|
#include "common/config-manager.h"
|
|
#include "twp/twp.h"
|
|
#include "twp/detection.h"
|
|
#include "twp/lighting.h"
|
|
#include "twp/object.h"
|
|
#include "twp/resmanager.h"
|
|
#include "twp/room.h"
|
|
#include "twp/scenegraph.h"
|
|
|
|
namespace Twp {
|
|
|
|
#define DEFAULT_FPS 10.f
|
|
|
|
#define NUMOBJECTSBYROW 4
|
|
#define MARGIN 8.f
|
|
#define MARGINBOTTOM 10.f
|
|
#define BACKOFFSET 7.f
|
|
#define ARROWWIDTH 56.f
|
|
#define ARROWHEIGHT 86.f
|
|
#define BACKWIDTH 137.f
|
|
#define BACKHEIGHT 75.f
|
|
|
|
class ShakeInventory : public Motor {
|
|
public:
|
|
ShakeInventory(Math::Vector2d &shakeOffset, float amount) : _shakeOffset(shakeOffset), _amount(amount) {}
|
|
|
|
protected:
|
|
void onUpdate(float elapsed) override {
|
|
_shakeTime += 40.f * elapsed;
|
|
_elapsed += elapsed;
|
|
_shakeOffset = Math::Vector2d(_amount * cos(_shakeTime + 0.3f), _amount * sin(_shakeTime));
|
|
}
|
|
|
|
private:
|
|
Math::Vector2d &_shakeOffset;
|
|
const float _amount;
|
|
float _shakeTime = 0.f;
|
|
float _elapsed = 0.f;
|
|
};
|
|
|
|
static float _getFps(float fps, float animFps) {
|
|
if (fps != 0.f)
|
|
return fps;
|
|
return (animFps < 1e-3) ? DEFAULT_FPS : animFps;
|
|
}
|
|
|
|
Node::Node(const Common::String &name, const Math::Vector2d &scale, const Color &color)
|
|
: _name(name),
|
|
_color(color),
|
|
_computedColor(color),
|
|
_scale(scale) {
|
|
}
|
|
|
|
Node::~Node() {
|
|
remove();
|
|
if (_children.empty())
|
|
return;
|
|
|
|
for (size_t i = 0; i < _children.size(); i++) {
|
|
_children[i]->_parent = nullptr;
|
|
}
|
|
}
|
|
|
|
void Node::addChild(Node *child) {
|
|
if (child->_parent == this)
|
|
return;
|
|
if (child->_parent) {
|
|
child->_pos -= getAbsPos();
|
|
child->remove();
|
|
}
|
|
_children.push_back(child);
|
|
child->_parent = this;
|
|
child->updateColor();
|
|
child->updateAlpha();
|
|
}
|
|
|
|
const Node *Node::getRoot() const {
|
|
const Node *result = this;
|
|
while (result->_parent != NULL) {
|
|
result = result->_parent;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int Node::find(Node *other) {
|
|
for (size_t i = 0; i < _children.size(); i++) {
|
|
if (_children[i] == other) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Node::removeChild(Node *child) {
|
|
int i = find(child);
|
|
if (i != -1) {
|
|
_children.remove_at(i);
|
|
}
|
|
child->_parent = nullptr;
|
|
}
|
|
|
|
void Node::clear() {
|
|
if (_children.empty())
|
|
return;
|
|
|
|
Common::Array<Node *> children(_children);
|
|
for (size_t i = 0; i < children.size(); i++) {
|
|
children[i]->remove();
|
|
}
|
|
_children.clear();
|
|
}
|
|
|
|
void Node::remove() {
|
|
if (_parent)
|
|
_parent->removeChild(this);
|
|
}
|
|
|
|
void Node::setColor(const Color &c) {
|
|
_color.rgba.r = c.rgba.r;
|
|
_color.rgba.g = c.rgba.g;
|
|
_color.rgba.b = c.rgba.b;
|
|
_computedColor.rgba.r = c.rgba.r;
|
|
_computedColor.rgba.g = c.rgba.g;
|
|
_computedColor.rgba.b = c.rgba.b;
|
|
updateColor();
|
|
}
|
|
|
|
void Node::setAlpha(float alpha) {
|
|
_color.rgba.a = alpha;
|
|
_computedColor.rgba.a = alpha;
|
|
updateAlpha();
|
|
}
|
|
|
|
void Node::updateColor() {
|
|
Color parentColor = !_parent ? Color(1.f, 1.f, 1.f) : _parent->_computedColor;
|
|
updateColor(parentColor);
|
|
}
|
|
|
|
void Node::updateAlpha() {
|
|
float parentOpacity = !_parent ? 1.f : _parent->_computedColor.rgba.a;
|
|
updateAlpha(parentOpacity);
|
|
}
|
|
|
|
void Node::updateColor(const Color &parentColor) {
|
|
_computedColor.rgba.r = _color.rgba.r * parentColor.rgba.r;
|
|
_computedColor.rgba.g = _color.rgba.g * parentColor.rgba.g;
|
|
_computedColor.rgba.b = _color.rgba.b * parentColor.rgba.b;
|
|
onColorUpdated(_computedColor);
|
|
for (size_t i = 0; i < _children.size(); i++) {
|
|
Node *child = _children[i];
|
|
child->updateColor(_computedColor);
|
|
}
|
|
}
|
|
|
|
void Node::updateAlpha(float parentAlpha) {
|
|
_computedColor.rgba.a = _color.rgba.a * parentAlpha;
|
|
onColorUpdated(_computedColor);
|
|
for (size_t i = 0; i < _children.size(); i++) {
|
|
Node *child = _children[i];
|
|
child->updateAlpha(_computedColor.rgba.a);
|
|
}
|
|
}
|
|
|
|
void Node::setAnchor(const Math::Vector2d &anchor) {
|
|
if (_anchor != anchor) {
|
|
_anchorNorm = anchor / _size;
|
|
_anchor = anchor;
|
|
}
|
|
}
|
|
|
|
void Node::setAnchorNorm(const Math::Vector2d &anchorNorm) {
|
|
if (_anchorNorm != anchorNorm) {
|
|
_anchorNorm = anchorNorm;
|
|
_anchor = _size * _anchorNorm;
|
|
}
|
|
}
|
|
|
|
void Node::setSize(const Math::Vector2d &size) {
|
|
if (_size != size) {
|
|
_size = size;
|
|
_anchor = size * _anchorNorm;
|
|
}
|
|
}
|
|
|
|
// this structure is used to have a stable sort
|
|
typedef struct {
|
|
size_t index;
|
|
Node *node;
|
|
} NodeSort;
|
|
|
|
static int cmpNodes(const NodeSort &x, const NodeSort &y) {
|
|
if (y.node->getZSort() == x.node->getZSort()) {
|
|
return x.index < y.index;
|
|
}
|
|
return x.node->getZSort() > y.node->getZSort();
|
|
}
|
|
|
|
void Node::onDrawChildren(const Math::Matrix4 &trsf) {
|
|
// use this "stable sort" until there is something better available
|
|
Common::Array<NodeSort> children;
|
|
for (size_t i = 0; i < _children.size(); i++) {
|
|
children.push_back({i, _children[i]});
|
|
}
|
|
Common::sort(children.begin(), children.end(), cmpNodes);
|
|
_children.clear();
|
|
_children.reserve(children.size());
|
|
for (size_t i = 0; i < children.size(); i++) {
|
|
_children.push_back(children[i].node);
|
|
}
|
|
for (auto &child : _children) {
|
|
child->draw(trsf);
|
|
}
|
|
}
|
|
|
|
void Node::draw(const Math::Matrix4 &parent) {
|
|
if (_visible) {
|
|
Math::Matrix4 trsf = getTrsf(parent);
|
|
Math::Matrix4 myTrsf(trsf);
|
|
myTrsf.translate(Math::Vector3d(-_anchor.getX(), _anchor.getY(), 0.f));
|
|
drawCore(myTrsf);
|
|
onDrawChildren(trsf);
|
|
}
|
|
}
|
|
|
|
Math::Vector2d Node::getAbsPos() const {
|
|
return !_parent ? _pos : _parent->getAbsPos() + _pos;
|
|
}
|
|
|
|
Math::Matrix4 Node::getTrsf(const Math::Matrix4 &parentTrsf) {
|
|
return parentTrsf * getLocalTrsf();
|
|
}
|
|
|
|
Math::Matrix4 Node::getLocalTrsf() {
|
|
Math::Vector2d p = _pos + _offset + _shakeOffset;
|
|
Math::Matrix4 m1;
|
|
m1.translate(Math::Vector3d(p.getX(), p.getY(), 0.f));
|
|
Math::Matrix3 mRot;
|
|
mRot.buildAroundZ(Math::Angle(-_rotation + _rotationOffset));
|
|
Math::Matrix4 m2;
|
|
m2.setRotation(mRot);
|
|
scale(m2, getScale());
|
|
Math::Matrix4 m3;
|
|
m3.translate(Math::Vector3d(_renderOffset.getX(), _renderOffset.getY(), 0.f));
|
|
return m1 * m2 * m3;
|
|
}
|
|
|
|
Rectf Node::getRect() const {
|
|
Math::Vector2d size = _size * getScale();
|
|
return Rectf::fromPosAndSize(getAbsPos(), Math::Vector2d(-size.getX(), size.getY()) * _anchorNorm * _size);
|
|
}
|
|
|
|
ParallaxNode::ParallaxNode(const Math::Vector2d ¶llax, const Common::String &sheet, const Common::StringArray &frames)
|
|
: Node("parallax"),
|
|
_parallax(parallax),
|
|
_sheet(sheet),
|
|
_frames(frames) {
|
|
}
|
|
|
|
ParallaxNode::~ParallaxNode() {}
|
|
|
|
Math::Matrix4 ParallaxNode::getTrsf(const Math::Matrix4 &parentTrsf) {
|
|
Gfx &gfx = g_twp->getGfx();
|
|
Math::Matrix4 trsf = Node::getTrsf(parentTrsf);
|
|
Math::Vector2d camPos = gfx.cameraPos();
|
|
Math::Vector2d p = Math::Vector2d(-camPos.getX() * _parallax.getX(), -camPos.getY() * _parallax.getY());
|
|
trsf.translate(Math::Vector3d(p.getX(), p.getY(), 0.0f));
|
|
return trsf;
|
|
}
|
|
|
|
void ParallaxNode::onDrawChildren(const Math::Matrix4 &trsf) {
|
|
Node::onDrawChildren(trsf);
|
|
}
|
|
|
|
void ParallaxNode::drawCore(const Math::Matrix4 &trsf) {
|
|
Gfx &gfx = g_twp->getGfx();
|
|
SpriteSheet *sheet = g_twp->_resManager->spriteSheet(_sheet);
|
|
Texture *texture = g_twp->_resManager->texture(sheet->meta.image);
|
|
|
|
// enable debug lighting ?
|
|
if (_zOrder == 0 && g_twp->_lighting->_debug) {
|
|
g_twp->getGfx().use(g_twp->_lighting.get());
|
|
} else {
|
|
g_twp->getGfx().use(nullptr);
|
|
}
|
|
|
|
Math::Matrix4 t = trsf;
|
|
float x = 0.f;
|
|
for (size_t i = 0; i < _frames.size(); i++) {
|
|
const SpriteSheetFrame &frame = sheet->getFrame(_frames[i]);
|
|
g_twp->_lighting->setSpriteOffset({0.f, static_cast<float>(-frame.frame.height())});
|
|
g_twp->_lighting->setSpriteSheetFrame(frame, *texture, false);
|
|
|
|
Math::Matrix4 myTrsf = t;
|
|
myTrsf.translate(Math::Vector3d(x + frame.spriteSourceSize.left, frame.sourceSize.getY() - frame.spriteSourceSize.height() - frame.spriteSourceSize.top, 0.0f));
|
|
gfx.drawSprite(frame.frame, *texture, getColor(), myTrsf);
|
|
t = trsf;
|
|
x += frame.frame.width();
|
|
}
|
|
|
|
g_twp->getGfx().use(nullptr);
|
|
}
|
|
|
|
Anim::Anim(Object *obj)
|
|
: Node("anim") {
|
|
_obj = obj;
|
|
_zOrder = 1000;
|
|
}
|
|
|
|
void Anim::clearFrames() {
|
|
_frames.clear();
|
|
}
|
|
|
|
void Anim::setAnim(const ObjectAnimation *anim, float fps, bool loop, bool instant) {
|
|
_anim = anim;
|
|
_disabled = false;
|
|
setName(anim->name);
|
|
_sheet = anim->sheet;
|
|
_frames = anim->frames;
|
|
_frameIndex = instant && _frames.size() > 0 ? _frames.size() - 1 : 0;
|
|
_frameDuration = 1.0 / _getFps(fps, anim->fps);
|
|
_loop = loop || anim->loop;
|
|
_instant = instant;
|
|
if (_obj)
|
|
setVisible(Twp::find(_obj->_hiddenLayers, _anim->name) == (size_t)-1);
|
|
|
|
clear();
|
|
for (size_t i = 0; i < _anim->layers.size(); i++) {
|
|
const ObjectAnimation &layer = _anim->layers[i];
|
|
Common::SharedPtr<Anim> node(new Anim(_obj));
|
|
_anims.push_back(node);
|
|
node->setAnim(&layer, fps, loop, instant);
|
|
addChild(node.get());
|
|
}
|
|
}
|
|
|
|
void Anim::trigSound() {
|
|
if (_anim && (_anim->triggers.size() > 0) && (_frameIndex < _anim->triggers.size())) {
|
|
const Common::String &trigger = _anim->triggers[_frameIndex];
|
|
if ((trigger.size() > 0) && trigger != "null") {
|
|
_obj->trig(trigger);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Anim::update(float elapsed) {
|
|
if (_anim)
|
|
setVisible(Twp::find(_obj->_hiddenLayers, _anim->name) == (size_t)-1);
|
|
if (_instant)
|
|
disable();
|
|
else if (_frames.size() != 0) {
|
|
_elapsed += elapsed;
|
|
if (_elapsed > _frameDuration) {
|
|
_elapsed = 0;
|
|
if (_frameIndex < _frames.size() - 1) {
|
|
_frameIndex++;
|
|
trigSound();
|
|
} else if (_loop) {
|
|
_frameIndex = 0;
|
|
trigSound();
|
|
} else {
|
|
disable();
|
|
}
|
|
}
|
|
if (_anim && _anim->offsets.size() > 0) {
|
|
Math::Vector2d off = _frameIndex < _anim->offsets.size() ? _anim->offsets[_frameIndex] : Math::Vector2d();
|
|
if (_obj->getFacing() == Facing::FACE_LEFT) {
|
|
off.setX(-off.getX());
|
|
}
|
|
_offset = Common::move(off);
|
|
}
|
|
} else if (_children.size() != 0) {
|
|
bool disabled = true;
|
|
for (size_t i = 0; i < _children.size(); i++) {
|
|
Anim *layer = static_cast<Anim *>(_children[i]);
|
|
layer->update(elapsed);
|
|
disabled = disabled && layer->_disabled;
|
|
}
|
|
if (disabled) {
|
|
disable();
|
|
}
|
|
} else {
|
|
disable();
|
|
}
|
|
}
|
|
|
|
void Anim::drawCore(const Math::Matrix4 &t) {
|
|
Math::Matrix4 trsf(t);
|
|
if (_frameIndex < _frames.size()) {
|
|
const Common::String &frame = _frames[_frameIndex];
|
|
if (frame == "null")
|
|
return;
|
|
|
|
bool flipX = _obj->getFacing() == Facing::FACE_LEFT;
|
|
if (_sheet.size() == 0) {
|
|
_sheet = _obj->_sheet;
|
|
if (_sheet.size() == 0 && _obj->_room) {
|
|
_sheet = _obj->_room->_sheet;
|
|
}
|
|
}
|
|
if (_sheet == "raw") {
|
|
Texture *texture = g_twp->_resManager->texture(frame);
|
|
Math::Vector3d pos(-texture->width / 2.f, -texture->height / 2.f, 0.f);
|
|
trsf.translate(pos);
|
|
g_twp->getGfx().drawSprite(Common::Rect(texture->width, texture->height), *texture, getComputedColor(), trsf, flipX);
|
|
} else {
|
|
const SpriteSheet *sheet = g_twp->_resManager->spriteSheet(_sheet);
|
|
const SpriteSheetFrame *sf = sheet->frame(frame);
|
|
if (!sf)
|
|
return;
|
|
Texture *texture = g_twp->_resManager->texture(sheet->meta.image);
|
|
if (_obj->_lit) {
|
|
g_twp->getGfx().use(g_twp->_lighting.get());
|
|
Math::Vector2d p = getAbsPos() + _obj->_node->getRenderOffset();
|
|
const float left = flipX ? (-1.f + sf->sourceSize.getX()) / 2.f - sf->spriteSourceSize.left : sf->spriteSourceSize.left - sf->sourceSize.getX() / 2.f;
|
|
const float top = -sf->sourceSize.getY() / 2.f + sf->spriteSourceSize.top;
|
|
|
|
g_twp->_lighting->setSpriteOffset({p.getX() + left, -p.getY() + top});
|
|
g_twp->_lighting->setSpriteSheetFrame(*sf, *texture, flipX);
|
|
} else {
|
|
g_twp->getGfx().use(nullptr);
|
|
}
|
|
const float x = flipX ? (1.f - sf->sourceSize.getX()) / 2.f + sf->frame.width() + sf->spriteSourceSize.left : sf->sourceSize.getX() / 2.f - sf->spriteSourceSize.left;
|
|
const float y = sf->sourceSize.getY() / 2.f - sf->spriteSourceSize.height() - sf->spriteSourceSize.top;
|
|
Math::Vector3d pos(int(-x), int(y), 0.f);
|
|
trsf.translate(pos);
|
|
g_twp->getGfx().drawSprite(sf->frame, *texture, getComputedColor(), trsf, flipX);
|
|
g_twp->getGfx().use(nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
ActorNode::ActorNode(Common::SharedPtr<Object> obj)
|
|
: Node(obj->_key), _object(obj) {
|
|
}
|
|
|
|
int ActorNode::getZSort() const { return getPos().getY(); }
|
|
|
|
Math::Vector2d ActorNode::getScale() const {
|
|
float y = _object->_room->getScaling(_object->_node->getPos().getY());
|
|
return Math::Vector2d(y, y);
|
|
}
|
|
|
|
TextNode::TextNode() : Node("text") {
|
|
}
|
|
|
|
TextNode::~TextNode() {}
|
|
|
|
void TextNode::setText(const Text &text) {
|
|
_text = text;
|
|
updateBounds();
|
|
}
|
|
|
|
void TextNode::updateBounds() {
|
|
setSize(_text.getBounds());
|
|
}
|
|
|
|
Rectf TextNode::getRect() const {
|
|
Math::Vector2d size = _size * getScale();
|
|
return Rectf::fromPosAndSize(getAbsPos() + Math::Vector2d(0, -size.getY()) + Math::Vector2d(-size.getX(), size.getY()) * _anchorNorm, size);
|
|
}
|
|
|
|
void TextNode::onColorUpdated(const Color &color) {
|
|
_text.setColor(color);
|
|
}
|
|
|
|
void TextNode::drawCore(const Math::Matrix4 &trsf) {
|
|
_text.draw(g_twp->getGfx(), trsf);
|
|
}
|
|
|
|
Scene::Scene() : Node("Scene") {
|
|
_zOrder = -100;
|
|
}
|
|
Scene::~Scene() {}
|
|
|
|
InputState::InputState() : Node("InputState") {
|
|
_zOrder = -100;
|
|
}
|
|
|
|
InputState::~InputState() {}
|
|
|
|
Common::String InputState::getCursorName() const {
|
|
switch (_cursorShape) {
|
|
case CursorShape::Left:
|
|
return "cursor_left";
|
|
case CursorShape::Right:
|
|
return "cursor_right";
|
|
case CursorShape::Front:
|
|
return "cursor_front";
|
|
case CursorShape::Back:
|
|
return "cursor_back";
|
|
case CursorShape::Pause:
|
|
return "cursor_pause";
|
|
case CursorShape::Normal:
|
|
return "cursor";
|
|
}
|
|
return "cursor";
|
|
}
|
|
|
|
void InputState::drawCore(const Math::Matrix4 &t) {
|
|
Math::Matrix4 trsf(t);
|
|
Common::String cursorName = getCursorName();
|
|
// draw cursor
|
|
SpriteSheet *gameSheet = g_twp->_resManager->spriteSheet("GameSheet");
|
|
Texture *texture = g_twp->_resManager->texture(gameSheet->meta.image);
|
|
if (ConfMan.getBool("hudSentence") && _hotspot) {
|
|
cursorName = "hotspot_" + cursorName;
|
|
}
|
|
const SpriteSheetFrame &sf = gameSheet->getFrame(cursorName);
|
|
Math::Vector3d pos(sf.spriteSourceSize.left - sf.sourceSize.getX() / 2.f, -sf.spriteSourceSize.height() - sf.spriteSourceSize.top + sf.sourceSize.getY() / 2.f, 0.f);
|
|
trsf.translate(pos * 2.f);
|
|
scale(trsf, Math::Vector2d(2.f, 2.f));
|
|
g_twp->getGfx().drawSprite(sf.frame, *texture, getComputedColor(), trsf);
|
|
}
|
|
|
|
InputStateFlag InputState::getState() const {
|
|
int tmp = 0;
|
|
tmp |= (_inputActive ? UI_INPUT_ON : UI_INPUT_OFF);
|
|
tmp |= (_inputVerbsActive ? UI_VERBS_ON : UI_VERBS_OFF);
|
|
tmp |= (_showCursor ? UI_CURSOR_ON : UI_CURSOR_OFF);
|
|
tmp |= (_inputHUD ? UI_HUDOBJECTS_ON : UI_HUDOBJECTS_OFF);
|
|
return (InputStateFlag)tmp;
|
|
}
|
|
|
|
void InputState::setState(InputStateFlag state) {
|
|
if ((UI_INPUT_ON & state) == UI_INPUT_ON)
|
|
_inputActive = true;
|
|
if ((UI_INPUT_OFF & state) == UI_INPUT_OFF)
|
|
_inputActive = false;
|
|
if ((UI_VERBS_ON & state) == UI_VERBS_ON)
|
|
_inputVerbsActive = true;
|
|
if ((UI_VERBS_OFF & state) == UI_VERBS_OFF)
|
|
_inputVerbsActive = false;
|
|
if ((UI_CURSOR_ON & state) == UI_CURSOR_ON) {
|
|
_showCursor = true;
|
|
_visible = true;
|
|
}
|
|
if ((UI_CURSOR_OFF & state) == UI_CURSOR_OFF) {
|
|
_showCursor = false;
|
|
_visible = false;
|
|
}
|
|
if ((UI_HUDOBJECTS_ON & state) == UI_HUDOBJECTS_ON)
|
|
_inputHUD = true;
|
|
if ((UI_HUDOBJECTS_OFF & state) == UI_HUDOBJECTS_OFF)
|
|
_inputHUD = false;
|
|
}
|
|
|
|
void InputState::setCursorShape(CursorShape shape) {
|
|
_cursorShape = shape;
|
|
}
|
|
|
|
OverlayNode::OverlayNode() : Node("overlay") {
|
|
_ovlColor = Color(0.f, 0.f, 0.f, 0.f); // transparent
|
|
_zOrder = INT_MIN;
|
|
}
|
|
|
|
void OverlayNode::drawCore(const Math::Matrix4 &trsf) {
|
|
Math::Vector2d size = g_twp->getGfx().camera();
|
|
g_twp->getGfx().drawQuad(size, _ovlColor);
|
|
}
|
|
|
|
static bool hasUpArrow(Common::SharedPtr<Object> actor) {
|
|
return actor->_inventoryOffset != 0;
|
|
}
|
|
|
|
static bool hasDownArrow(Common::SharedPtr<Object> actor) {
|
|
return actor->_inventory.size() > (size_t)(actor->_inventoryOffset * NUMOBJECTSBYROW + NUMOBJECTS);
|
|
}
|
|
|
|
Inventory::Inventory() : Node("Inventory") {
|
|
for (int i = 0; i < NUMOBJECTS; i++) {
|
|
float x = SCREEN_WIDTH / 2.f + ARROWWIDTH + MARGIN + ((i % NUMOBJECTSBYROW) * (BACKWIDTH + BACKOFFSET));
|
|
float y = MARGINBOTTOM + BACKHEIGHT + BACKOFFSET - ((float)(i / NUMOBJECTSBYROW) * (BACKHEIGHT + BACKOFFSET));
|
|
_itemRects[i] = Common::Rect(x, y, x + BACKWIDTH, y + BACKHEIGHT);
|
|
}
|
|
_arrowUpRect = Common::Rect(SCREEN_WIDTH / 2.f, ARROWHEIGHT + MARGINBOTTOM + BACKOFFSET, SCREEN_WIDTH / 2.f + ARROWWIDTH, ARROWHEIGHT + MARGINBOTTOM + BACKOFFSET + ARROWHEIGHT);
|
|
_arrowDnRect = Common::Rect(SCREEN_WIDTH / 2.f, MARGINBOTTOM, SCREEN_WIDTH / 2.f + ARROWWIDTH, MARGINBOTTOM + ARROWHEIGHT);
|
|
for (int i = 0; i < NUMOBJECTS; i++) {
|
|
_shakeTime[i] = 0.f;
|
|
_inventoryOver[i] = false;
|
|
}
|
|
}
|
|
|
|
Math::Vector2d Inventory::getPos(Common::SharedPtr<Object> inv) const {
|
|
if (_actor) {
|
|
int i = Twp::find(_actor->_inventory, inv) - _actor->_inventoryOffset * NUMOBJECTSBYROW;
|
|
return Math::Vector2d(_itemRects[i].left + _itemRects[i].width() / 2.f, _itemRects[i].bottom + _itemRects[i].height() / 2.f);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
Math::Vector2d Inventory::getPos(int i) const {
|
|
assert((i >= 0) && (i < 8));
|
|
return Math::Vector2d(_itemRects[i].left + _itemRects[i].width() / 2.f, _itemRects[i].top + _itemRects[i].height() / 2.f);
|
|
}
|
|
|
|
int Inventory::getOverIndex() const {
|
|
for (int i = 0; i < NUMOBJECTS; i++) {
|
|
if (_inventoryOver[i])
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Inventory::drawSprite(const SpriteSheetFrame &sf, Texture *texture, const Color &color, const Math::Matrix4 &t) {
|
|
Math::Matrix4 trsf(t);
|
|
Math::Vector3d pos(sf.spriteSourceSize.left - sf.sourceSize.getX() / 2.f, -sf.spriteSourceSize.height() - sf.spriteSourceSize.top + sf.sourceSize.getY() / 2.f, 0.f);
|
|
trsf.translate(pos);
|
|
g_twp->getGfx().drawSprite(sf.frame, *texture, color, trsf);
|
|
}
|
|
|
|
void Inventory::drawArrows(const Math::Matrix4 &trsf) {
|
|
bool isRetro = ConfMan.getBool("retroVerbs");
|
|
SpriteSheet *gameSheet = g_twp->_resManager->spriteSheet("GameSheet");
|
|
Texture *texture = g_twp->_resManager->texture(gameSheet->meta.image);
|
|
const SpriteSheetFrame *arrowUp = &gameSheet->getFrame(isRetro ? "scroll_up_retro" : "scroll_up");
|
|
const SpriteSheetFrame *arrowDn = &gameSheet->getFrame(isRetro ? "scroll_down_retro" : "scroll_down");
|
|
float alphaUp = hasUpArrow(_actor) ? 1.f : 0.f;
|
|
float alphaDn = hasDownArrow(_actor) ? 1.f : 0.f;
|
|
Math::Matrix4 tUp(trsf);
|
|
tUp.translate(Math::Vector3d(SCREEN_WIDTH / 2.f + ARROWWIDTH / 2.f + MARGIN, 1.5f * ARROWHEIGHT + BACKOFFSET, 0.f));
|
|
Math::Matrix4 tDn(trsf);
|
|
tDn.translate(Math::Vector3d(SCREEN_WIDTH / 2.f + ARROWWIDTH / 2.f + MARGIN, 0.5f * ARROWHEIGHT, 0.f));
|
|
|
|
drawSprite(*arrowUp, texture, Color::withAlpha(_verbNormal, alphaUp * getAlpha()), tUp);
|
|
drawSprite(*arrowDn, texture, Color::withAlpha(_verbNormal, alphaDn * getAlpha()), tDn);
|
|
}
|
|
|
|
void Inventory::drawBack(const Math::Matrix4 &trsf) {
|
|
SpriteSheet *gameSheet = g_twp->_resManager->spriteSheet("GameSheet");
|
|
Texture *texture = g_twp->_resManager->texture(gameSheet->meta.image);
|
|
const SpriteSheetFrame *back = &gameSheet->getFrame("inventory_background");
|
|
|
|
float startOffsetX = SCREEN_WIDTH / 2.f + ARROWWIDTH + MARGIN + back->sourceSize.getX() / 2.f;
|
|
float offsetX = startOffsetX;
|
|
float offsetY = 3.f * back->sourceSize.getY() / 2.f + MARGINBOTTOM + BACKOFFSET;
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
Math::Matrix4 t(trsf);
|
|
t.translate(Math::Vector3d(offsetX, offsetY, 0.f));
|
|
drawSprite(*back, texture, Color::withAlpha(_backColor, getAlpha()), t);
|
|
offsetX += back->sourceSize.getX() + BACKOFFSET;
|
|
}
|
|
|
|
offsetX = startOffsetX;
|
|
offsetY = back->sourceSize.getY() / 2.f + MARGINBOTTOM;
|
|
for (int i = 0; i < 4; i++) {
|
|
Math::Matrix4 t(trsf);
|
|
t.translate(Math::Vector3d(offsetX, offsetY, 0.f));
|
|
drawSprite(*back, texture, Color::withAlpha(_backColor, getAlpha()), t);
|
|
offsetX += back->sourceSize.getX() + BACKOFFSET;
|
|
}
|
|
}
|
|
|
|
void Inventory::drawItems(const Math::Matrix4 &trsf) {
|
|
float startOffsetX = SCREEN_WIDTH / 2.f + ARROWWIDTH + MARGIN + BACKWIDTH / 2.f;
|
|
float startOffsetY = MARGINBOTTOM + 1.5f * BACKHEIGHT + BACKOFFSET;
|
|
SpriteSheet *itemsSheet = g_twp->_resManager->spriteSheet("InventoryItems");
|
|
Texture *texture = g_twp->_resManager->texture(itemsSheet->meta.image);
|
|
int count = MIN(NUMOBJECTS, (int)(_actor->_inventory.size() - _actor->_inventoryOffset * NUMOBJECTSBYROW));
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
Common::SharedPtr<Object> obj = _actor->_inventory[_actor->_inventoryOffset * NUMOBJECTSBYROW + i];
|
|
Common::String icon = obj->getIcon();
|
|
if (itemsSheet->_frameTable.contains(icon)) {
|
|
SpriteSheetFrame *itemFrame = &itemsSheet->_frameTable[icon];
|
|
Math::Vector2d pos(startOffsetX + ((float)(i % NUMOBJECTSBYROW) * (BACKWIDTH + BACKOFFSET)), startOffsetY - ((float)(i / NUMOBJECTSBYROW) * (BACKHEIGHT + BACKOFFSET)));
|
|
Math::Matrix4 t(trsf);
|
|
t.translate(Math::Vector3d(pos.getX(), pos.getY(), 0.f));
|
|
if (obj->_jiggle) {
|
|
Math::Matrix3 rot;
|
|
rot.buildAroundZ(18.f * sin(_jiggleTime));
|
|
t.setRotation(rot);
|
|
}
|
|
float s = obj->getScale();
|
|
Twp::scale(t, Math::Vector2d(s, s));
|
|
Math::Vector2d shakeOffset = _shakeOffset[i];
|
|
t.translate(Math::Vector3d(shakeOffset.getX(), shakeOffset.getY(), 0.f));
|
|
drawSprite(*itemFrame, texture, Color::withAlpha(Color(), getAlpha()), t);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Inventory::drawCore(const Math::Matrix4 &trsf) {
|
|
if (_actor) {
|
|
drawArrows(trsf);
|
|
drawBack(trsf);
|
|
drawItems(trsf);
|
|
}
|
|
}
|
|
|
|
void Inventory::update(float elapsed, Common::SharedPtr<Object> actor, const Color &backColor, const Color &verbNormal) {
|
|
_jiggleTime += 10.f * elapsed;
|
|
_fadeTime += elapsed;
|
|
|
|
if (_fadeTime > 2.f) {
|
|
_fadeTime = 2.f;
|
|
}
|
|
|
|
if (_fadeIn) {
|
|
float alpha = MIN(_fadeTime, 2.0f) / 2.0f;
|
|
setAlpha(alpha);
|
|
}
|
|
|
|
// udate colors
|
|
_actor = actor;
|
|
_backColor = backColor;
|
|
_verbNormal = verbNormal;
|
|
|
|
_obj = nullptr;
|
|
if (_actor) {
|
|
Math::Vector2d scrPos = g_twp->winToScreen(g_twp->_cursor.pos);
|
|
_over = scrPos.getY() < 180.f && scrPos.getX() > 704.f;
|
|
|
|
// update mouse click
|
|
bool down = g_twp->_cursor.leftDown;
|
|
if (!_down && down) {
|
|
_down = true;
|
|
if (_arrowUpRect.contains(scrPos.getX(), scrPos.getY())) {
|
|
_actor->inventoryScrollUp();
|
|
} else if (_arrowDnRect.contains(scrPos.getX(), scrPos.getY())) {
|
|
_actor->inventoryScrollDown();
|
|
}
|
|
} else if (!down) {
|
|
_down = false;
|
|
}
|
|
|
|
for (int i = 0; i < NUMOBJECTS; i++) {
|
|
const Common::Rect &item = _itemRects[i];
|
|
if (item.contains(scrPos.getX(), scrPos.getY())) {
|
|
size_t index = _actor->_inventoryOffset * NUMOBJECTSBYROW + i;
|
|
if (index < _actor->_inventory.size()) {
|
|
_obj = _actor->_inventory[index];
|
|
|
|
if (!_inventoryOver[i] && (_shakeTime[i] < 0.1f)) {
|
|
_shakeTime[i] = 0.25f;
|
|
_shake[i] = Common::ScopedPtr<Motor>(new ShakeInventory(_shakeOffset[i], 0.4f));
|
|
_inventoryOver[i] = true;
|
|
}
|
|
} else {
|
|
_inventoryOver[i] = true;
|
|
}
|
|
} else {
|
|
_inventoryOver[i] = false;
|
|
}
|
|
|
|
// shake choice when cursor is over
|
|
if ((_shakeTime[i] > 0.0f) && _shake[i]) {
|
|
_shake[i]->update(elapsed);
|
|
_shakeTime[i] -= elapsed;
|
|
if (_shakeTime[i] < 0.f) {
|
|
_shakeTime[i] = 0.f;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < _actor->_inventory.size(); i++) {
|
|
Common::SharedPtr<Object> obj = _actor->_inventory[i];
|
|
obj->update(elapsed);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Inventory::setVisible(bool visible) {
|
|
if (_fadeIn != visible) {
|
|
_fadeIn = visible;
|
|
Node::setVisible(visible);
|
|
_fadeTime = 0;
|
|
}
|
|
}
|
|
|
|
SentenceNode::SentenceNode() : Node("Sentence") {
|
|
_zOrder = -100;
|
|
}
|
|
|
|
SentenceNode::~SentenceNode() {
|
|
}
|
|
|
|
void SentenceNode::setText(const Common::String &text) {
|
|
_text = text;
|
|
}
|
|
|
|
void SentenceNode::drawCore(const Math::Matrix4 &trsf) {
|
|
Text text("sayline", _text);
|
|
float x, y;
|
|
if (ConfMan.getBool("hudSentence")) {
|
|
x = (SCREEN_WIDTH - text.getBounds().getX()) / 2.f;
|
|
y = 152.f;
|
|
} else {
|
|
x = MAX(_pos.getX() - text.getBounds().getX() / 2.f, MARGIN);
|
|
x = MIN(x, SCREEN_WIDTH - text.getBounds().getX() - MARGIN);
|
|
y = _pos.getY() + 16.f;
|
|
if (y >= (SCREEN_HEIGHT - 32.f))
|
|
y = _pos.getY() - 92.f;
|
|
}
|
|
Math::Matrix4 t;
|
|
t.translate(Math::Vector3d(x, y, 0.f));
|
|
text.draw(g_twp->getGfx(), t);
|
|
}
|
|
|
|
SpriteNode::SpriteNode() : Node("Sprite") {}
|
|
SpriteNode::~SpriteNode() {}
|
|
|
|
void SpriteNode::setSprite(const Common::String &sheet, const Common::String &frame) {
|
|
_sheet = sheet;
|
|
_frame = frame;
|
|
}
|
|
|
|
void SpriteNode::drawCore(const Math::Matrix4 &trsf) {
|
|
SpriteSheet *sheet = g_twp->_resManager->spriteSheet(_sheet);
|
|
const SpriteSheetFrame *frame = &sheet->getFrame(_frame);
|
|
|
|
Common::Rect rect = frame->frame;
|
|
setSize(Math::Vector2d(frame->frame.width(), frame->frame.height()));
|
|
float x = frame->sourceSize.getX() / 2.f - frame->spriteSourceSize.left;
|
|
float y = (frame->sourceSize.getY() + 1.f) / 2.f - frame->spriteSourceSize.height() - frame->spriteSourceSize.top;
|
|
Math::Vector2d anchor((int)(x), (int)(y));
|
|
setAnchor(anchor);
|
|
|
|
Texture *texture = g_twp->_resManager->texture(sheet->meta.image);
|
|
g_twp->getGfx().drawSprite(rect, *texture, getComputedColor(), trsf);
|
|
}
|
|
|
|
NoOverrideNode::NoOverrideNode() : Node("NoOverride") {
|
|
_zOrder = -1000;
|
|
_elapsed = 42.f;
|
|
|
|
_icon.setSprite("GameSheet", "icon_no");
|
|
_icon.setScale(Math::Vector2d(2.f, 2.f));
|
|
_icon.setPos(Math::Vector2d(32.f, SCREEN_HEIGHT - 32.f));
|
|
addChild(&_icon);
|
|
}
|
|
|
|
NoOverrideNode::~NoOverrideNode() {
|
|
}
|
|
|
|
void NoOverrideNode::reset() {
|
|
_elapsed = 0.f;
|
|
setVisible(true);
|
|
}
|
|
|
|
bool NoOverrideNode::update(float elapsed) {
|
|
if (_elapsed > 2.f) {
|
|
setVisible(false);
|
|
return false;
|
|
}
|
|
_elapsed += elapsed;
|
|
setAlpha(CLIP((2.f - _elapsed) / 2.f, 0.f, 1.f));
|
|
debugC(kDebugGame, "no override: %.2f, %.2f", _elapsed, getAlpha());
|
|
return true;
|
|
}
|
|
|
|
HotspotMarkerNode::HotspotMarkerNode() : Node("HotspotMarker") {
|
|
_zOrder = -1000;
|
|
_visible = false;
|
|
}
|
|
|
|
HotspotMarkerNode::~HotspotMarkerNode() {}
|
|
|
|
void HotspotMarkerNode::drawSprite(const SpriteSheetFrame &sf, Texture *texture, const Color &color, const Math::Matrix4 &t) {
|
|
Math::Matrix4 trsf(t);
|
|
Math::Vector3d pos(sf.spriteSourceSize.left - sf.sourceSize.getX() / 2.f, -sf.spriteSourceSize.height() - sf.spriteSourceSize.top + sf.sourceSize.getY() / 2.f, 0.f);
|
|
trsf.translate(pos);
|
|
g_twp->getGfx().drawSprite(sf.frame, *texture, color, trsf);
|
|
}
|
|
|
|
void HotspotMarkerNode::drawCore(const Math::Matrix4 &trsf) {
|
|
SpriteSheet *gameSheet = g_twp->_resManager->spriteSheet("GameSheet");
|
|
Texture *texture = g_twp->_resManager->texture(gameSheet->meta.image);
|
|
const SpriteSheetFrame *frame = &gameSheet->getFrame("hotspot_marker");
|
|
Color color = Color::create(255, 165, 0);
|
|
for (size_t i = 0; i < g_twp->_room->_layers.size(); i++) {
|
|
Common::SharedPtr<Layer> layer = g_twp->_room->_layers[i];
|
|
for (size_t j = 0; j < layer->_objects.size(); j++) {
|
|
Common::SharedPtr<Object> obj = layer->_objects[j];
|
|
if (g_twp->_resManager->isObject(obj->getId()) && (obj->_objType == otNone) && obj->isTouchable()) {
|
|
Math::Vector2d pos = g_twp->roomToScreen(obj->_node->getAbsPos());
|
|
Math::Matrix4 t;
|
|
t.translate(Math::Vector3d(pos.getX(), pos.getY(), 0.f));
|
|
drawSprite(*frame, texture, color, t);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace Twp
|