MYST3: Add preliminary hotspot subtitle support

This commit is contained in:
Bastien Bouclet 2012-02-11 14:40:16 +01:00
parent 377cfbee8b
commit 46842b0dc0
13 changed files with 302 additions and 20 deletions

View File

@ -108,7 +108,8 @@ Cursor::~Cursor() {
}
void Cursor::changeCursor(uint32 index) {
assert(index <= 12);
if (index > 12)
return;
_currentCursorID = index;
}

View File

@ -25,7 +25,7 @@
namespace Myst3 {
bool HotSpot::isPointInRectsCube(const Common::Point &p) {
int32 HotSpot::isPointInRectsCube(const Common::Point &p) {
for (uint j = 0; j < rects.size(); j++) {
Common::Rect rect = Common::Rect(
rects[j].centerHeading - rects[j].width / 2,
@ -39,10 +39,10 @@ bool HotSpot::isPointInRectsCube(const Common::Point &p) {
lookAt.x += 360;
if (rect.contains(lookAt))
return true;
return j;
}
return false;
return -1;
}
int32 HotSpot::isPointInRectsFrame(GameState *state, const Common::Point &p) {
@ -73,7 +73,7 @@ bool HotSpot::isEnabled(GameState *state, uint16 var) {
return false;
if (var == 0)
return cursor < 12;
return cursor <= 13;
else
return cursor == var;
}

View File

@ -54,7 +54,7 @@ public:
int16 cursor;
Common::Array<Opcode> script;
bool isPointInRectsCube(const Common::Point &p);
int32 isPointInRectsCube(const Common::Point &p);
int32 isPointInRectsFrame(GameState *state, const Common::Point &p);
bool isEnabled(GameState *state, uint16 var = 0);
};

View File

@ -21,7 +21,8 @@ MODULE_OBJS := \
scene.o \
script.o \
sound.o \
state.o
state.o \
subtitles.o
# This module can be built as a plugin
ifeq ($(ENABLE_MYST3), DYNAMIC_PLUGIN)

View File

@ -291,6 +291,9 @@ void Myst3Engine::closeArchives() {
}
HotSpot *Myst3Engine::getHoveredHotspot(NodePtr nodeData, uint16 var) {
_state->setHotspotHovered(false);
_state->setHotspotActiveRect(0);
if (_state->getViewType() == kCube) {
float pitch, heading;
_cursor->getDirection(pitch, heading);
@ -298,8 +301,13 @@ HotSpot *Myst3Engine::getHoveredHotspot(NodePtr nodeData, uint16 var) {
Common::Point mouse = Common::Point((int16)heading, (int16)pitch);
for (uint j = 0; j < nodeData->hotspots.size(); j++) {
if (nodeData->hotspots[j].isPointInRectsCube(mouse)
int32 hitRect = nodeData->hotspots[j].isPointInRectsCube(mouse);
if (hitRect >= 0
&& nodeData->hotspots[j].isEnabled(_state, var)) {
if (nodeData->hotspots[j].rects.size() > 1) {
_state->setHotspotHovered(true);
_state->setHotspotActiveRect(hitRect);
}
return &nodeData->hotspots[j];
}
}
@ -323,7 +331,10 @@ HotSpot *Myst3Engine::getHoveredHotspot(NodePtr nodeData, uint16 var) {
int32 hitRect = nodeData->hotspots[j].isPointInRectsFrame(_state, scaledMouse);
if (hitRect >= 0
&& nodeData->hotspots[j].isEnabled(_state, var)) {
_state->setHotspotActiveRect(hitRect);
if (nodeData->hotspots[j].rects.size() > 1) {
_state->setHotspotHovered(true);
_state->setHotspotActiveRect(hitRect);
}
return &nodeData->hotspots[j];
}
}
@ -340,7 +351,7 @@ void Myst3Engine::updateCursor() {
if (hovered) {
_cursor->changeCursor(hovered->cursor);
} else if (hoveredInventory > 0 && _state->getViewType() != kMenu) {
} else if (isInventoryVisible() && hoveredInventory > 0) {
_cursor->changeCursor(1);
} else {
_cursor->changeCursor(8);
@ -364,7 +375,7 @@ void Myst3Engine::processInput(bool lookOnly) {
if (lookOnly) continue;
uint16 hoveredInventory = _inventory->hoveredItem();
if (hoveredInventory > 0 && _state->getViewType() != kMenu) {
if (isInventoryVisible() && hoveredInventory > 0) {
_inventory->useItem(hoveredInventory);
continue;
}
@ -492,9 +503,11 @@ void Myst3Engine::drawFrame() {
_scene->drawSunspotFlare(flare);
_scene->drawBlackBorders();
_inventory->draw();
}
if (isInventoryVisible())
_inventory->draw();
// Draw overlay 2D movies
for (uint i = 0; i < _movies.size(); i++) {
_movies[i]->drawOverlay();
@ -504,6 +517,11 @@ void Myst3Engine::drawFrame() {
_drawables[i]->drawOverlay();
}
// Draw spot subtitles
if (_node) {
_node->drawOverlay();
}
if (_cursor->isVisible())
_cursor->draw();
@ -512,6 +530,16 @@ void Myst3Engine::drawFrame() {
_state->updateFrameCounters();
}
bool Myst3Engine::isInventoryVisible() {
if (_state->getViewType() == kMenu)
return false;
if (_node && _node->hasSubtitlesToDraw())
return false;
return true;
}
void Myst3Engine::goToNode(uint16 nodeID, uint transition) {
uint16 node = _state->getLocationNextNode();
if (node == 0)
@ -864,13 +892,23 @@ void Myst3Engine::setMovieLooping(uint16 id, bool loop) {
}
void Myst3Engine::addSpotItem(uint16 id, uint16 condition, bool fade) {
assert(_node);
_node->loadSpotItem(id, condition, fade);
}
void Myst3Engine::addMenuSpotItem(uint16 id, uint16 condition, const Common::Rect &rect) {
assert(_node);
_node->loadMenuSpotItem(id, condition, rect);
}
void Myst3Engine::loadNodeSubtitles(uint32 id) {
assert(_node);
_node->loadSubtitles(id);
}
const DirectorySubEntry *Myst3Engine::getFileDescription(const char* room, uint32 index, uint16 face, DirectorySubEntry::ResourceType type) {
char currentRoom[8];
if (!room) {

View File

@ -139,6 +139,7 @@ public:
void addSpotItem(uint16 id, uint16 condition, bool fade);
void addMenuSpotItem(uint16 id, uint16 condition, const Common::Rect &rect);
void loadNodeSubtitles(uint32 id);
void addSunSpot(uint16 pitch, uint16 heading, uint16 intensity,
uint16 color, uint16 var, bool varControlledIntensity, uint16 radius);
@ -190,6 +191,8 @@ private:
void openArchives();
void closeArchives();
bool isInventoryVisible();
friend class Console;
};

View File

@ -24,6 +24,7 @@
#include "engines/myst3/menu.h"
#include "engines/myst3/myst3.h"
#include "engines/myst3/state.h"
#include "engines/myst3/subtitles.h"
#include "common/debug.h"
#include "common/rect.h"
@ -76,7 +77,8 @@ Face::~Face() {
}
Node::Node(Myst3Engine *vm, uint16 id) :
_vm(vm) {
_vm(vm),
_subtitles(0) {
for (uint i = 0; i < 6; i++)
_faces[i] = 0;
}
@ -134,6 +136,8 @@ Node::~Node() {
for (int i = 0; i < 6; i++) {
delete _faces[i];
}
delete _subtitles;
}
void Node::loadSpotItem(uint16 id, uint16 condition, bool fade) {
@ -189,6 +193,25 @@ void Node::loadMenuSpotItem(uint16 id, uint16 condition, const Common::Rect &rec
_spotItems.push_back(spotItem);
}
void Node::loadSubtitles(uint32 id) {
_subtitles = new Subtitles(_vm, id);
}
bool Node::hasSubtitlesToDraw() {
if (!_subtitles)
return false;
return _vm->_state->getSpotSubtitle() > 0;
}
void Node::drawOverlay() {
if (hasSubtitlesToDraw()) {
uint subId = _vm->_state->getSpotSubtitle();
_subtitles->setFrame(15 * subId + 1);
_subtitles->drawOverlay();
}
}
void Node::update() {
// First undraw ...
for (uint i = 0; i < _spotItems.size(); i++) {

View File

@ -23,6 +23,8 @@
#ifndef MYST3_ROOM_H
#define MYST3_ROOM_H
#include "engines/myst3/gfx.h"
#include "common/array.h"
#include "common/rect.h"
@ -33,6 +35,7 @@ namespace Myst3 {
class Texture;
class Myst3Engine;
class Subtitles;
class Face {
public:
@ -117,11 +120,12 @@ class SunSpot {
float radius;
};
class Node {
class Node : Drawable {
protected:
Myst3Engine *_vm;
Face *_faces[6];
Common::Array<SpotItem *> _spotItems;
Subtitles *_subtitles;
public:
Node(Myst3Engine *vm, uint16 id);
@ -129,10 +133,14 @@ class Node {
void update();
virtual void draw() = 0;
void drawOverlay();
void loadSpotItem(uint16 id, uint16 condition, bool fade);
void loadMenuSpotItem(uint16 id, uint16 condition, const Common::Rect &rect);
void loadSubtitles(uint32 id);
bool hasSubtitlesToDraw();
void dumpFaceMask(uint16 index, int face);
};

View File

@ -102,6 +102,9 @@ void Puzzles::run(uint16 id, uint16 arg0, uint16 arg1, uint16 arg2) {
case 21:
mainMenu(arg0);
break;
case 23:
_vm->loadNodeSubtitles(arg0);
break;
default:
warning("Puzzle %d is not implemented", id);
}

View File

@ -80,7 +80,7 @@ GameState::GameState(Myst3Engine *vm):
VAR(143, MovieOverrideEndFrame, true)
VAR(144, MovieVolume1, true)
VAR(145, MovieVolume2, true)
VAR(146, MovieUnk146, true)
VAR(146, MovieOverrideSubtitles, false)
VAR(147, MovieUnk147, true)
VAR(148, MovieUnk148, true)
VAR(149, MovieConditionBit, true)
@ -187,6 +187,9 @@ GameState::GameState(Myst3Engine *vm):
VAR(1393, LanguageAudio, false)
VAR(1394, LanguageText, false)
VAR(1396, HotspotHovered, false)
VAR(1397, SpotSubtitle, false)
VAR(1399, DragLeverLimited, true)
VAR(1400, DragLeverLimitMin, true)
VAR(1401, DragLeverLimitMax, true)

View File

@ -106,6 +106,7 @@ public:
DECLARE_VAR(142, MovieStartFrame)
DECLARE_VAR(143, MovieEndFrame)
DECLARE_VAR(146, MovieOverrideSubtitles)
DECLARE_VAR(149, MovieConditionBit)
DECLARE_VAR(150, MoviePreloadToMemory)
DECLARE_VAR(151, MovieScriptDriven)
@ -195,11 +196,14 @@ public:
DECLARE_VAR(1352, MenuSaveLoadSelectedItem)
DECLARE_VAR(1353, MenuSaveLoadCurrentPage)
DECLARE_VAR(1374, OverallVolume);
DECLARE_VAR(1377, MusicVolume);
DECLARE_VAR(1380, MusicFrequency);
DECLARE_VAR(1393, LanguageAudio);
DECLARE_VAR(1394, LanguageText);
DECLARE_VAR(1374, OverallVolume)
DECLARE_VAR(1377, MusicVolume)
DECLARE_VAR(1380, MusicFrequency)
DECLARE_VAR(1393, LanguageAudio)
DECLARE_VAR(1394, LanguageText)
DECLARE_VAR(1396, HotspotHovered)
DECLARE_VAR(1397, SpotSubtitle)
DECLARE_VAR(1399, DragLeverLimited)
DECLARE_VAR(1400, DragLeverLimitMin)

138
engines/myst3/subtitles.cpp Normal file
View File

@ -0,0 +1,138 @@
/* 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/myst3/subtitles.h"
#include "engines/myst3/myst3.h"
#include "engines/myst3/state.h"
#include "graphics/fontman.h"
#include "graphics/font.h"
namespace Myst3 {
Subtitles::Subtitles(Myst3Engine *vm, uint32 id) :
_vm(vm),
_id(id),
_surface(0),
_texture(0),
_frame(-1) {
// Subtitles may be overridden using a variable
const DirectorySubEntry *desc;
if (_vm->_state->getMovieOverrideSubtitles()) {
id = _vm->_state->getMovieOverrideSubtitles();
_vm->_state->setMovieOverrideSubtitles(0);
desc = _vm->getFileDescription("IMGR", 100000 + id, 0, DirectorySubEntry::kText);
} else {
desc = _vm->getFileDescription(0, 100000 + id, 0, DirectorySubEntry::kText);
}
if (!desc)
error("Subtitles %d don't exist", 100000 + id);
Common::MemoryReadStream *crypted = desc->getData();
// Read the frames and associated text offsets
while (true) {
Phrase s;
s.frame = crypted->readUint32LE();
s.offset = crypted->readUint32LE();
if (!s.frame)
break;
_phrases.push_back(s);
}
// Read and decrypt the frames subtitles
for (uint i = 0; i < _phrases.size(); i++) {
crypted->seek(_phrases[i].offset);
uint8 key = 35;
char c = 0;
do {
c = crypted->readByte() ^ key++;
_phrases[i].string += c;
} while (c);
}
delete crypted;
// Create a surface to draw the subtitles on
_surface = new Graphics::Surface();
_surface->create(Renderer::kOriginalWidth, Renderer::kBottomBorderHeight, Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));
_texture = _vm->_gfx->createTexture(_surface);
}
Subtitles::~Subtitles() {
if (_surface) {
_surface->free();
delete _surface;
}
if (_texture) {
_vm->_gfx->freeTexture(_texture);
}
}
void Subtitles::setFrame(int32 frame) {
const Phrase *phrase = 0;
for (uint i = 0; i < _phrases.size(); i++) {
if (_phrases[i].frame > frame)
break;
phrase = &_phrases[i];
}
if (phrase == 0
|| phrase->frame == _frame)
return;
_frame = phrase->frame;
// TODO: Use the TTF font provided by the game
// TODO: Use the positioning information provided by the game
const Graphics::Font *font = FontMan.getFontByUsage(Graphics::FontManager::kLocalizedFont);
if (!font)
error("No available font");
// Draw the new text
memset(_surface->pixels, 0, _surface->pitch * _surface->h);
font->drawString(_surface, phrase->string, 0, _surface->h / 2, _surface->w, 0xFFFFFFFF, Graphics::kTextAlignCenter);
// Update the texture
_texture->update(_surface);
}
void Subtitles::drawOverlay() {
Common::Rect textureRect = Common::Rect(_texture->width, _texture->height);
Common::Rect bottomBorder = textureRect;
bottomBorder.translate(0, Renderer::kTopBorderHeight + Renderer::kFrameHeight);
_vm->_gfx->drawTexturedRect2D(bottomBorder, textureRect, _texture);
}
} /* namespace Myst3 */

60
engines/myst3/subtitles.h Normal file
View File

@ -0,0 +1,60 @@
/* 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 SUBTITLES_H_
#define SUBTITLES_H_
#include "engines/myst3/gfx.h"
#include "common/array.h"
namespace Myst3 {
class Myst3Engine;
class Subtitles : public Drawable {
public:
Subtitles(Myst3Engine *vm, uint32 id);
virtual ~Subtitles();
void setFrame(int32 frame);
void drawOverlay();
private:
struct Phrase {
uint32 offset;
int32 frame;
Common::String string;
};
Myst3Engine *_vm;
uint32 _id;
Common::Array<Phrase> _phrases;
int32 _frame;
Graphics::Surface *_surface;
Texture *_texture;
};
} /* namespace Myst3 */
#endif /* SUBTITLES_H_ */