mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-19 00:15:30 +00:00
MYST3: Add preliminary hotspot subtitle support
This commit is contained in:
parent
377cfbee8b
commit
46842b0dc0
@ -108,7 +108,8 @@ Cursor::~Cursor() {
|
||||
}
|
||||
|
||||
void Cursor::changeCursor(uint32 index) {
|
||||
assert(index <= 12);
|
||||
if (index > 12)
|
||||
return;
|
||||
|
||||
_currentCursorID = index;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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)) {
|
||||
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) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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++) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
138
engines/myst3/subtitles.cpp
Normal 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
60
engines/myst3/subtitles.h
Normal 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_ */
|
Loading…
x
Reference in New Issue
Block a user