STARK: Implement the exploding image animation

Used when getting an inventory item
This commit is contained in:
Bastien Bouclet 2018-02-25 11:53:04 +01:00
parent 1f9df9edb8
commit 375c89fd54
12 changed files with 338 additions and 20 deletions

View file

@ -98,6 +98,7 @@ MODULE_OBJS := \
ui/world/gamewindow.o \
ui/world/inventorywindow.o \
visual/actor.o \
visual/explodingimage.o \
visual/image.o \
visual/prop.o \
visual/smacker.o \

View file

@ -40,7 +40,7 @@ KnowledgeSet::KnowledgeSet(Object *parent, byte subType, uint16 index, const Com
void KnowledgeSet::printData() {
}
Gfx::RenderEntryArray KnowledgeSet::getInventoryRenderEntries() {
Gfx::RenderEntryArray KnowledgeSet::getInventoryRenderEntries() const {
Common::Array<Resources::Item *> inventoryItems = listChildren<Resources::Item>(Resources::Item::kItemInventory);
// First add the inventory items from old saves which don't have an order
@ -97,5 +97,12 @@ void KnowledgeSet::saveLoad(ResourceSerializer *serializer) {
}
}
Visual *KnowledgeSet::getInventoryItemVisual(uint16 itemIndex) {
InventoryItem *item = findChildWithIndex<InventoryItem>(itemIndex, Item::kItemInventory);
assert(item);
return item->getCursorVisual();
}
} // End of namespace Resources
} // End of namespace Stark

View file

@ -66,7 +66,10 @@ public:
void removeItem(InventoryItem *item);
/** Get the render entries for the inventory items, in the order they were obtained */
Gfx::RenderEntryArray getInventoryRenderEntries();
Gfx::RenderEntryArray getInventoryRenderEntries() const;
/** Get a cursor style visual for an inventory item */
Visual *getInventoryItemVisual(uint16 itemIndex);
protected:
void printData() override;

View file

@ -20,10 +20,10 @@
*
*/
#include <engines/stark/resources/animscript.h>
#include "engines/stark/services/staticprovider.h"
#include "engines/stark/resources/anim.h"
#include "engines/stark/resources/animscript.h"
#include "engines/stark/resources/container.h"
#include "engines/stark/resources/image.h"
#include "engines/stark/resources/item.h"

View file

@ -26,6 +26,7 @@
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/texture.h"
#include "engines/stark/visual/explodingimage.h"
#include "engines/stark/visual/image.h"
#include "engines/stark/visual/text.h"
@ -38,27 +39,34 @@ Button::Button(const Common::String &text, StaticProvider::UIElement stockElemen
_hintPosition(hintPos),
_align(align),
_mouseText(nullptr),
_renderHint(false) {
_renderHint(false),
_explodingImageAnimation(nullptr) {
}
Button::~Button() {
delete _explodingImageAnimation;
delete _mouseText;
}
void Button::render() {
VisualImageXMG *image = StarkStaticProvider->getUIElement(_stockElement);
image->render(_position, false);
if (_explodingImageAnimation) {
_explodingImageAnimation->render(_position);
}
if (_renderHint) {
Common::Point pos(_hintPosition);
if (_align == kAlignRight) {
pos.x -= _mouseText->getRect().width();
}
_mouseText->render(pos);
_renderHint = false;
}
_renderHint = false;
}
bool Button::containsPoint(Common::Point point) {
bool Button::containsPoint(const Common::Point &point) {
VisualImageXMG *image = StarkStaticProvider->getUIElement(_stockElement);
Common::Rect r;
@ -89,4 +97,17 @@ void Button::goToAnimStatement(int animScriptItemIndex) {
StarkStaticProvider->goToAnimScriptStatement(_stockElement, animScriptItemIndex);
}
void Button::startImageExplosion(VisualImageXMG *image) {
assert(image);
stopImageExplosion();
_explodingImageAnimation = new VisualExplodingImage(StarkGfx);
_explodingImageAnimation->initFromSurface(image->getSurface());
}
void Button::stopImageExplosion() {
delete _explodingImageAnimation;
_explodingImageAnimation = nullptr;
}
} // End of namespace Stark

View file

@ -31,6 +31,7 @@
namespace Stark {
class VisualExplodingImage;
class VisualImageXMG;
class VisualText;
@ -49,7 +50,7 @@ public:
/** Set hint to render for one frame */
void showButtonHint();
void render();
bool containsPoint(Common::Point point);
bool containsPoint(const Common::Point &point);
/** Reset the hint text visual so it is rebuilt with the appropriate texture size */
void resetHintVisual();
@ -57,12 +58,19 @@ public:
/** Move execution of the button's icon anim script to the specified item */
void goToAnimStatement(int animScriptItemIndex);
/** Start overlaying an explosion animation of an image on top of the button */
void startImageExplosion(VisualImageXMG *image);
/** Remove the currently playing exploding image animation, if any */
void stopImageExplosion();
private:
StaticProvider::UIElement _stockElement;
Common::Point _position;
Common::Point _hintPosition;
Common::String _text;
VisualText *_mouseText;
VisualExplodingImage *_explodingImageAnimation;
const HintAlign _align;
bool _renderHint;
};

View file

@ -27,6 +27,7 @@
#include "engines/stark/gfx/driver.h"
#include "engines/stark/resources/knowledgeset.h"
#include "engines/stark/resources/sound.h"
#include "engines/stark/services/global.h"
@ -69,6 +70,7 @@ void TopMenu::onRender() {
_forceVisibleTimeRemaining -= StarkGlobal->getMillisecondsPerGameloop();
if (_forceVisibleTimeRemaining <= 0) {
_inventoryButton->stopImageExplosion();
_inventoryButton->goToAnimStatement(12);
}
}
@ -134,6 +136,9 @@ void TopMenu::onScreenChanged() {
void TopMenu::notifyInventoryItemEnabled(uint16 itemIndex) {
_forceVisibleTimeRemaining = 128 * 33; // 128 frames at 30 fps
_inventoryButton->goToAnimStatement(2);
Visual *inventoryItemImage = StarkGlobal->getInventory()->getInventoryItemVisual(itemIndex);
_inventoryButton->startImageExplosion(inventoryItemImage->get<VisualImageXMG>());
_inventoryNewItemSound->stop();
_inventoryNewItemSound->play();
}

View file

@ -0,0 +1,172 @@
/* 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/visual/explodingimage.h"
#include "common/random.h"
#include "graphics/surface.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/surfacerenderer.h"
#include "engines/stark/gfx/texture.h"
#include "engines/stark/services/global.h"
#include "engines/stark/services/services.h"
namespace Stark {
VisualExplodingImage::VisualExplodingImage(Gfx::Driver *gfx) :
Visual(TYPE),
_gfx(gfx),
_texture(nullptr),
_surface(nullptr) {
_surfaceRenderer = _gfx->createSurfaceRenderer();
}
VisualExplodingImage::~VisualExplodingImage() {
if (_surface) {
_surface->free();
}
delete _surface;
delete _texture;
delete _surfaceRenderer;
}
void VisualExplodingImage::initFromSurface(const Graphics::Surface *surface) {
assert(!_surface && !_texture);
// Decode the XMG
_surface = new Graphics::Surface();
_surface->copyFrom(*surface);
_texture = _gfx->createTexture(_surface);
// Create an explosion unit for each pixel in the surface
_units.resize(_surface->w * _surface->h);
Common::Point explosionCenter(_surface->w / 2, _surface->h / 2);
Common::Point explosionAmplitude(48, 16);
uint index = 0;
for (uint y = 0; y < _surface->h; y++) {
for (uint x = 0; x < _surface->w; x++, index++) {
_units[index].setPosition(x, y);
_units[index].setExplosionSettings(explosionCenter, explosionAmplitude);
_units[index].setColor(*static_cast<uint32 *>(_surface->getBasePtr(x, y)), _surface->format);
}
}
}
void VisualExplodingImage::render(const Common::Point &position) {
// Fill with transparent color
_surface->fillRect(Common::Rect(_surface->w, _surface->h), 0);
for (uint i = 0; i < _units.size(); i++) {
_units[i].update();
_units[i].draw(_surface);
}
_texture->update(_surface);
_surfaceRenderer->render(_texture, position);
}
VisualExplodingImage::ExplosionUnit::ExplosionUnit() :
_stillImageTimeRemaining(33 * 33),
_explosionFastAccelerationTimeRemaining(25 * 33) {
}
void VisualExplodingImage::ExplosionUnit::setPosition(int x, int y) {
_position = Math::Vector2d(x, y);
}
void VisualExplodingImage::ExplosionUnit::setExplosionSettings(const Common::Point &center, const Common::Point &amplitude) {
_center = Math::Vector2d(center.x, center.y);
_speed.setX(cos(StarkRandomSource->getRandomNumber(M_PI * 100)) * (float)amplitude.x);
_speed.setY(sin(StarkRandomSource->getRandomNumber(M_PI * 100)) * (float)amplitude.y);
// WTF, ensuring all fragments go in the same direction?
float magnitude = _position.getDistanceTo(_speed);
_speed -= _position;
_speed = _speed / _speed.getMagnitude() * -magnitude;
}
void VisualExplodingImage::ExplosionUnit::setColor(uint32 color, const Graphics::PixelFormat &format) {
_mainColor = color;
byte a, r, g, b;
format.colorToARGB(color, a, r, g, b);
r >>= 1;
g >>= 1;
b >>= 1;
_darkColor = format.ARGBToColor(a, r, g, b);
}
void VisualExplodingImage::ExplosionUnit::update() {
if (_stillImageTimeRemaining > 0) {
_stillImageTimeRemaining -= StarkGlobal->getMillisecondsPerGameloop();
return;
}
if (_position.getDistanceTo(_center) <= 1.f) {
// Units near the center stay there (to make it look like they enter the chest)
return;
}
Math::Vector2d speed = _speed.getNormalized() * 0.6f;
_position += speed;
// Update the acceleration to units move towards the center
Math::Vector2d acceleration = _center - _position;
if (_explosionFastAccelerationTimeRemaining > 0) {
acceleration *= 3.0f;
_explosionFastAccelerationTimeRemaining -= StarkGlobal->getMillisecondsPerGameloop();
}
_speed += acceleration;
_speed -= speed * 2.5f;
}
void VisualExplodingImage::ExplosionUnit::draw(Graphics::Surface *surface) {
if (_position.getX() <= 1.f || _position.getX() >= surface->w - 1
|| _position.getY() <= 1.f || _position.getY() >= surface->h - 1) {
return; // Ignore units outside of the surface
}
if (_stillImageTimeRemaining <= 0 && _position.getDistanceTo(_center) <= 2.f) {
return; // Ignore units close to the center (to make it look like they enter the chest)
}
uint32 *pixel = static_cast<uint32 *>(surface->getBasePtr(_position.getX(), _position.getY() - 1));
*pixel = _darkColor;
pixel = static_cast<uint32 *>(surface->getBasePtr(_position.getX() - 1, _position.getY()));
*pixel++ = _darkColor;
*pixel++ = _mainColor;
*pixel = _darkColor;
pixel = static_cast<uint32 *>(surface->getBasePtr(_position.getX(), _position.getY() + 1));
*pixel = _darkColor;
}
} // End of namespace Stark

View file

@ -0,0 +1,95 @@
/* 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_VISUAL_EXPLODING_IMAGE_H
#define STARK_VISUAL_EXPLODING_IMAGE_H
#include "engines/stark/visual/visual.h"
#include "common/array.h"
#include "common/rect.h"
#include "graphics/pixelformat.h"
#include "math/vector2d.h"
namespace Graphics {
struct Surface;
}
namespace Stark {
namespace Gfx {
class Driver;
class SurfaceRenderer;
class Texture;
}
/**
* An image with an animated explosion effect
*
* Used by the top bar when picking up an inventory item
*/
class VisualExplodingImage : public Visual {
public:
static const VisualType TYPE = Visual::kExplodingImage;
explicit VisualExplodingImage(Gfx::Driver *gfx);
~VisualExplodingImage() override;
/** Prepare exploding the specified image */
void initFromSurface(const Graphics::Surface *surface);
/** Render the image at the specified position */
void render(const Common::Point &position);
private:
struct ExplosionUnit {
ExplosionUnit();
void setPosition(int x, int y);
void setExplosionSettings(const Common::Point &center, const Common::Point &amplitude);
void setColor(uint32 color, const Graphics::PixelFormat &format);
void update();
void draw(Graphics::Surface *surface);
Math::Vector2d _position;
Math::Vector2d _speed;
Math::Vector2d _center;
int _stillImageTimeRemaining;
int _explosionFastAccelerationTimeRemaining;
uint32 _mainColor;
uint32 _darkColor;
};
Gfx::Driver *_gfx;
Gfx::SurfaceRenderer *_surfaceRenderer;
Gfx::Texture *_texture;
Graphics::Surface *_surface;
Common::Array<ExplosionUnit> _units;
};
} // End of namespace Stark
#endif // STARK_VISUAL_EXPLODING_IMAGE_H

View file

@ -24,13 +24,10 @@
#include "graphics/surface.h"
#include "engines/stark/debug.h"
#include "engines/stark/formats/xmg.h"
#include "engines/stark/gfx/driver.h"
#include "engines/stark/gfx/surfacerenderer.h"
#include "engines/stark/gfx/texture.h"
#include "engines/stark/scene.h"
#include "engines/stark/services/services.h"
namespace Stark {
@ -94,4 +91,9 @@ int VisualImageXMG::getHeight() const {
return _surface->h;
}
const Graphics::Surface *VisualImageXMG::getSurface() const {
assert(_surface);
return _surface;
}
} // End of namespace Stark

View file

@ -73,6 +73,9 @@ public:
/** Get the height in pixels */
int getHeight() const;
/** Get a read only pointer to the surface backing the image */
const Graphics::Surface *getSurface() const;
private:
Gfx::Driver *_gfx;
Gfx::SurfaceRenderer *_surfaceRenderer;

View file

@ -30,16 +30,17 @@ namespace Stark {
class Visual {
public:
enum VisualType {
kImageXMG = 2,
kRendered = 3,
kImageText = 4,
kSmackerStream = 5,
kActor = 6,
kSmackerFMV = 7,
kEffectFish = 8,
kEffectBubbles = 9,
kEffectFirefly = 10,
kEffectSmoke = 11
kImageXMG = 2,
kRendered = 3,
kImageText = 4,
kSmackerStream = 5,
kActor = 6,
kSmackerFMV = 7,
kEffectFish = 8,
kEffectBubbles = 9,
kEffectFirefly = 10,
kEffectSmoke = 11,
kExplodingImage = 100,
};
explicit Visual(VisualType type) : _type(type) {}