Initial import of the work in progress M4 engine
svn-id: r31600
This commit is contained in:
parent
d0590a09ea
commit
7ca439f410
60 changed files with 20007 additions and 0 deletions
202
engines/m4/actor.cpp
Normal file
202
engines/m4/actor.cpp
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/system.h"
|
||||||
|
#include "common/array.h"
|
||||||
|
#include "m4/actor.h"
|
||||||
|
#include "m4/m4_views.h"
|
||||||
|
#include "m4/assets.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define WALKER_BURGER "Wilbur0%i" // wilbur, with a number denoting his current direction
|
||||||
|
|
||||||
|
Actor::Actor(M4Engine *vm) : _vm(vm) {
|
||||||
|
_scaling = 100;
|
||||||
|
_direction = 5;
|
||||||
|
_walkerSprites.resize(10);
|
||||||
|
loadWalkers();
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor::~Actor() {
|
||||||
|
unloadWalkers();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Actor::getWalkerWidth() { return _walkerSprites[kFacingSouth]->getFrame(0)->w; }
|
||||||
|
int Actor::getWalkerHeight() { return _walkerSprites[kFacingSouth]->getFrame(0)->h; }
|
||||||
|
|
||||||
|
void Actor::placeWalkerSpriteAt(int spriteNum, int x, int y) {
|
||||||
|
if (_direction < 1 || _direction > 9) {
|
||||||
|
warning("Direction is %i, fixing", _direction);
|
||||||
|
_direction = 1; // TODO: this is a temporary fix
|
||||||
|
}
|
||||||
|
SpriteInfo info;
|
||||||
|
info.sprite = _walkerSprites[_direction]->getFrame(spriteNum);
|
||||||
|
info.hotX = info.hotY = 0;
|
||||||
|
info.width = info.sprite->w;
|
||||||
|
info.height = info.sprite->h;
|
||||||
|
info.scaleX = info.scaleY = _scaling;
|
||||||
|
info.palette = _walkerSprites[_direction]->getPalette();
|
||||||
|
info.inverseColorTable = _vm->_scene->getInverseColorTable();
|
||||||
|
|
||||||
|
_vm->_scene->drawSprite(x, y, info, Common::Rect(640, 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::loadWalkers() {
|
||||||
|
for (uint8 i = 1; i < 10; i++) {
|
||||||
|
if (i == 6)
|
||||||
|
continue; // walker sprite 6 is unused
|
||||||
|
loadWalkerDirection(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::loadWalkerDirection(uint8 direction) {
|
||||||
|
char name[20];
|
||||||
|
Common::SeekableReadStream *walkerS;
|
||||||
|
|
||||||
|
if (_vm->getGameType() == GType_Burger) {
|
||||||
|
sprintf(name, WALKER_BURGER, direction);
|
||||||
|
} else {
|
||||||
|
//warning("Actor::loadWalkerDirection: unspecified walker type, not loading walker");
|
||||||
|
// TODO: Master Lu walkers
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
walkerS = _vm->res()->get(name);
|
||||||
|
_walkerSprites.insert_at(direction, new SpriteAsset(_vm, walkerS, walkerS->size(), name));
|
||||||
|
_vm->res()->toss(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::unloadWalkers() {
|
||||||
|
for (uint8 i = 9; i > 0; i--) {
|
||||||
|
if (i == 6)
|
||||||
|
continue; // walker sprite 6 is unused
|
||||||
|
SpriteAsset *tempSprite = _walkerSprites[i];
|
||||||
|
_walkerSprites.remove_at(i);
|
||||||
|
if (tempSprite)
|
||||||
|
delete tempSprite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Actor::setWalkerPalette() {
|
||||||
|
_vm->_palette->setPalette(_walkerSprites[kFacingSouthEast]->getPalette(), 0,
|
||||||
|
_walkerSprites[kFacingSouthEast]->getColorCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
Inventory::Inventory(M4Engine *vm) : _vm(vm) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Inventory::~Inventory() {
|
||||||
|
_inventory.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inventory::registerObject(char* name, int32 scene, int32 icon) {
|
||||||
|
InventoryObject *newObject = new InventoryObject();
|
||||||
|
int newObjectIndex = 0;
|
||||||
|
|
||||||
|
// Capitalize registered inventory object names
|
||||||
|
str_upper(name);
|
||||||
|
|
||||||
|
newObject->name = strdup(name);
|
||||||
|
newObject->scene = scene;
|
||||||
|
newObject->icon = icon;
|
||||||
|
|
||||||
|
newObjectIndex = _inventory.size();
|
||||||
|
|
||||||
|
_inventory.push_back(newObject);
|
||||||
|
|
||||||
|
if (scene == BACKPACK)
|
||||||
|
addToBackpack(newObjectIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inventory::moveObject(char* name, int32 scene) {
|
||||||
|
uint i = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < _inventory.size(); i++) {
|
||||||
|
if (!scumm_stricmp(_inventory[i]->name, name)) {
|
||||||
|
if (_inventory[i]->scene == BACKPACK && scene != BACKPACK)
|
||||||
|
removeFromBackpack(i);
|
||||||
|
|
||||||
|
_inventory[i]->scene = scene;
|
||||||
|
|
||||||
|
if (scene == BACKPACK)
|
||||||
|
addToBackpack(i);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inventory::addToBackpack(uint32 objectIndex) {
|
||||||
|
_vm->_interfaceView->inventoryAdd(_inventory[objectIndex]->name, "", _inventory[objectIndex]->icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inventory::removeFromBackpack(uint32 objectIndex) {
|
||||||
|
_vm->_interfaceView->inventoryRemove(_inventory[objectIndex]->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Inventory::isInCurrentScene(char* name) {
|
||||||
|
return (getScene(name) == _vm->_scene->getCurrentScene());
|
||||||
|
}
|
||||||
|
|
||||||
|
int Inventory::getScene(char* name) {
|
||||||
|
uint i = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < _inventory.size(); i++) {
|
||||||
|
if (!scumm_stricmp(_inventory[i]->name, name))
|
||||||
|
return _inventory[i]->scene;
|
||||||
|
}
|
||||||
|
return UNKNOWN_OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Inventory::getIcon(char* name) {
|
||||||
|
uint i = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < _inventory.size(); i++) {
|
||||||
|
if (!scumm_stricmp(_inventory[i]->name, name))
|
||||||
|
return _inventory[i]->icon;
|
||||||
|
}
|
||||||
|
return UNKNOWN_OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Inventory::getIndex(char* name) {
|
||||||
|
uint i = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < _inventory.size(); i++) {
|
||||||
|
if (!scumm_stricmp(_inventory[i]->name, name))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return UNKNOWN_OBJECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inventory::clear() {
|
||||||
|
for (uint i = 0; i < _inventory.size(); i++) {
|
||||||
|
delete _inventory[i]->name;
|
||||||
|
delete _inventory[i];
|
||||||
|
_inventory.remove_at(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
117
engines/m4/actor.h
Normal file
117
engines/m4/actor.h
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_ACTOR_H
|
||||||
|
#define M4_ACTOR_H
|
||||||
|
|
||||||
|
#include "common/array.h"
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/scene.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/assets.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
struct InventoryObject {
|
||||||
|
const char* name;
|
||||||
|
int32 scene;
|
||||||
|
int32 icon;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum inventoryObjectFlags {
|
||||||
|
UNKNOWN_OBJECT = 997,
|
||||||
|
BACKPACK = 998,
|
||||||
|
NOWHERE = 999
|
||||||
|
};
|
||||||
|
|
||||||
|
enum WalkerDirection {
|
||||||
|
kFacingNorth = 1, // has shadow
|
||||||
|
kFacingNorthEast = 2, // has shadow
|
||||||
|
kFacingEast = 3, // has shadow
|
||||||
|
kFacingSouthEast = 4, // has shadow
|
||||||
|
kFacingSouth = 5, // has shadow
|
||||||
|
// 6 is unused
|
||||||
|
kFacingSouthAlt = 7, // no shadow
|
||||||
|
kFacingSouthWest = 8, // no shadow
|
||||||
|
kFacingWest = 9 // no shadow
|
||||||
|
};
|
||||||
|
|
||||||
|
class Actor {
|
||||||
|
public:
|
||||||
|
Actor(M4Engine *vm);
|
||||||
|
~Actor();
|
||||||
|
void placeWalkerSpriteAt(int spriteNum, int x, int y);
|
||||||
|
void setWalkerScaling(int scaling) { _scaling = scaling; }
|
||||||
|
int getWalkerScaling() { return _scaling; }
|
||||||
|
void setWalkerDirection(uint8 direction) { _direction = direction; }
|
||||||
|
uint8 getWalkerDirection() { return _direction; }
|
||||||
|
void setWalkerPalette();
|
||||||
|
int getWalkerWidth();
|
||||||
|
int getWalkerHeight();
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
int _scaling;
|
||||||
|
uint8 _direction;
|
||||||
|
Common::Array<SpriteAsset*> _walkerSprites;
|
||||||
|
|
||||||
|
void loadWalkers();
|
||||||
|
void loadWalkerDirection(uint8 direction);
|
||||||
|
void unloadWalkers();
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: perhaps the inventory and its view could be merged?
|
||||||
|
// TODO: the original game capitalizes all inventory object names
|
||||||
|
// internally, which we do as well, but perhaps we could make sure
|
||||||
|
// that all object names are parsed with the same case and avoid
|
||||||
|
// case-insensitive string comparing through scumm_stricmp, using
|
||||||
|
// the normal strcmp method instead
|
||||||
|
class Inventory {
|
||||||
|
public:
|
||||||
|
Inventory(M4Engine *vm);
|
||||||
|
~Inventory();
|
||||||
|
void clear();
|
||||||
|
void registerObject(char* name, int32 scene, int32 icon);
|
||||||
|
void moveObject(char* name, int32 scene);
|
||||||
|
void giveToPlayer(char* name) { moveObject(name, BACKPACK); }
|
||||||
|
void addToBackpack(uint32 objectIndex);
|
||||||
|
void removeFromBackpack(uint32 objectIndex);
|
||||||
|
bool isInBackpack(char* name) { return (getScene(name) == BACKPACK); }
|
||||||
|
bool isInScene(char* name, int32 scene) { return (getScene(name) == scene); }
|
||||||
|
bool isInCurrentScene(char* name);
|
||||||
|
int getScene(char* name);
|
||||||
|
int getIcon(char* name);
|
||||||
|
int getIndex(char* name);
|
||||||
|
int getTotalItems() { return _inventory.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
Common::Array<InventoryObject *> _inventory;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
210
engines/m4/animation.cpp
Normal file
210
engines/m4/animation.cpp
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/assets.h"
|
||||||
|
#include "m4/animation.h"
|
||||||
|
#include "m4/compression.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
// TODO: this code needs cleanup
|
||||||
|
|
||||||
|
Animation::Animation(M4Engine *vm) {
|
||||||
|
_vm = vm;
|
||||||
|
_playing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animation::loadFullScreen(const char *filename) {
|
||||||
|
_vm->_palette->deleteAllRanges();
|
||||||
|
load(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animation::load(const char *filename) {
|
||||||
|
MadsPack anim(filename, _vm);
|
||||||
|
char buffer[20];
|
||||||
|
|
||||||
|
// Chunk 1: header
|
||||||
|
// header
|
||||||
|
// TODO: there are some unknown fields here, plus we don't read
|
||||||
|
// the entire chunk
|
||||||
|
Common::SeekableReadStream *animStream = anim.getItemStream(0);
|
||||||
|
Common::SeekableReadStream *spriteSeriesStream;
|
||||||
|
//printf("Chunk 0, size %i\n", animStream->size());
|
||||||
|
_seriesCount = animStream->readUint16LE();
|
||||||
|
_frameCount = animStream->readUint16LE();
|
||||||
|
_frameEntryCount = animStream->readUint16LE();
|
||||||
|
|
||||||
|
// Unknown
|
||||||
|
for (int i = 0; i < 43; i++)
|
||||||
|
animStream->readByte();
|
||||||
|
|
||||||
|
_spriteSeriesNames = new Common::String[_seriesCount];
|
||||||
|
printf("%i sprite series\n", _seriesCount);
|
||||||
|
|
||||||
|
// TODO: for now, we only load the first sprite series
|
||||||
|
if (_seriesCount > 1)
|
||||||
|
printf("TODO: Anim has %i sprite series, for now, we only load the first one\n", _seriesCount);
|
||||||
|
_seriesCount = 1; // TODO
|
||||||
|
|
||||||
|
for (int i = 0; i < _seriesCount; i++) {
|
||||||
|
animStream->read(buffer, 13);
|
||||||
|
_spriteSeriesNames[i] = Common::String(buffer);
|
||||||
|
//printf("%03d: %s\n", i, _spriteSeriesNames[i].c_str());
|
||||||
|
|
||||||
|
spriteSeriesStream = _vm->res()->get(_spriteSeriesNames[i].c_str());
|
||||||
|
_spriteSeries = new SpriteAsset(_vm, spriteSeriesStream,
|
||||||
|
spriteSeriesStream->size(), _spriteSeriesNames[i].c_str());
|
||||||
|
_vm->res()->toss(_spriteSeriesNames[i].c_str());
|
||||||
|
|
||||||
|
// Adjust the palette of the sprites in the sprite series
|
||||||
|
// so that they can be displayed on screen correctly
|
||||||
|
RGBList *palData = new RGBList(_spriteSeries->getColorCount(), _spriteSeries->getPalette(), true);
|
||||||
|
_vm->_palette->addRange(palData);
|
||||||
|
|
||||||
|
for (int k = 0; k < _spriteSeries->getCount(); k++) {
|
||||||
|
M4Sprite *spr = _spriteSeries->getFrame(k);
|
||||||
|
spr->translate(palData); // sprite pixel translation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("End pos: %i\n", animStream->pos());
|
||||||
|
|
||||||
|
delete animStream;
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
// Chunk 2: anim info
|
||||||
|
AnimationFrame frame;
|
||||||
|
animStream = anim.getItemStream(1);
|
||||||
|
//printf("Chunk 1, size %i\n", animStream->size());
|
||||||
|
|
||||||
|
_frameEntries = new AnimationFrame[_frameEntryCount];
|
||||||
|
|
||||||
|
for (int i = 0; i < _frameEntryCount; i++) {
|
||||||
|
|
||||||
|
frame.animFrameIndex = animStream->readUint16LE();
|
||||||
|
frame.u = animStream->readByte();
|
||||||
|
frame.seriesIndex = animStream->readByte();
|
||||||
|
frame.seriesFrameIndex = animStream->readUint16LE();
|
||||||
|
frame.x = animStream->readUint16LE();
|
||||||
|
frame.y = animStream->readUint16LE();
|
||||||
|
frame.v = animStream->readByte();
|
||||||
|
frame.w = animStream->readByte();
|
||||||
|
|
||||||
|
_frameEntries[i] = frame;
|
||||||
|
|
||||||
|
/*
|
||||||
|
printf(
|
||||||
|
"animFrameIndex = %4d, "
|
||||||
|
"u = %3d, "
|
||||||
|
"seriesIndex = %3d, "
|
||||||
|
"seriesFrameIndex = %6d, "
|
||||||
|
"x = %3d, "
|
||||||
|
"y = %3d, "
|
||||||
|
"v = %3d, "
|
||||||
|
"w = %3d\n",
|
||||||
|
|
||||||
|
frame.animFrameIndex,
|
||||||
|
frame.u,
|
||||||
|
frame.seriesIndex,
|
||||||
|
frame.seriesFrameIndex,
|
||||||
|
frame.x,
|
||||||
|
frame.y,
|
||||||
|
frame.v,
|
||||||
|
frame.w
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
//printf("End pos: %i\n", animStream->pos());
|
||||||
|
|
||||||
|
delete animStream;
|
||||||
|
|
||||||
|
// Chunk 3: unknown (seems to be sound data?)
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation::~Animation() {
|
||||||
|
//delete[] _spriteSeriesNames;
|
||||||
|
//delete[] _spriteSeries;
|
||||||
|
//delete[] _frameEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animation::start() {
|
||||||
|
_curFrame = 0;
|
||||||
|
_curFrameEntry = 0;
|
||||||
|
//for (int i = 0; i < _seriesCount; i++) {
|
||||||
|
//_spriteSeries[i] = new SpriteSeries((char*)_spriteSeriesNames[i].c_str());
|
||||||
|
//}
|
||||||
|
_playing = true;
|
||||||
|
updateAnim();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Animation::updateAnim() {
|
||||||
|
if (!_playing)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Get the scene background surface
|
||||||
|
M4Surface *bg = _vm->_scene->getBackgroundSurface();
|
||||||
|
|
||||||
|
while (_frameEntries[_curFrameEntry].animFrameIndex == _curFrame) {
|
||||||
|
AnimationFrame *frame = &_frameEntries[_curFrameEntry];
|
||||||
|
int seriesFrameIndex = (frame->seriesFrameIndex & 0x7FFF) - 1;
|
||||||
|
|
||||||
|
// Write the sprite onto the screen
|
||||||
|
M4Sprite *spr = _spriteSeries->getFrame(seriesFrameIndex);
|
||||||
|
|
||||||
|
// FIXME: We assume that the transparent color is the color of the top left pixel
|
||||||
|
byte *transparentColor = (byte *)spr->pixels;
|
||||||
|
|
||||||
|
// FIXME: correct x, y
|
||||||
|
spr->copyTo(bg, frame->x, frame->y, (int)*transparentColor);
|
||||||
|
|
||||||
|
// HACK: wait a bit
|
||||||
|
g_system->delayMillis(100);
|
||||||
|
|
||||||
|
//printf("_curFrameEntry = %d\n", _curFrameEntry);
|
||||||
|
_curFrameEntry++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("_curFrame = %d\n", _curFrame);
|
||||||
|
|
||||||
|
_curFrame++;
|
||||||
|
if (_curFrame >= _frameCount) // anim done
|
||||||
|
stop();
|
||||||
|
|
||||||
|
return _curFrame >= _frameCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Animation::stop() {
|
||||||
|
_playing = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < _seriesCount; i++) {
|
||||||
|
// TODO: cleanup
|
||||||
|
//delete _spriteSeries[i];
|
||||||
|
//_spriteSeries[i] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
69
engines/m4/animation.h
Normal file
69
engines/m4/animation.h
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_ANIMATION_H
|
||||||
|
#define M4_ANIMATION_H
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/assets.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
struct AnimationFrame {
|
||||||
|
uint16 animFrameIndex;
|
||||||
|
byte u;
|
||||||
|
byte seriesIndex;
|
||||||
|
uint16 seriesFrameIndex;
|
||||||
|
uint16 x, y;
|
||||||
|
byte v, w;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Animation {
|
||||||
|
public:
|
||||||
|
Animation(M4Engine *vm);
|
||||||
|
~Animation();
|
||||||
|
|
||||||
|
void load(const char *filename);
|
||||||
|
void loadFullScreen(const char *filename);
|
||||||
|
void start();
|
||||||
|
bool updateAnim();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _playing;
|
||||||
|
M4Engine *_vm;
|
||||||
|
int _seriesCount;
|
||||||
|
int _frameCount;
|
||||||
|
int _frameEntryCount;
|
||||||
|
AnimationFrame *_frameEntries;
|
||||||
|
Common::String *_spriteSeriesNames;
|
||||||
|
SpriteAsset *_spriteSeries;
|
||||||
|
int _curFrame, _curFrameEntry;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
544
engines/m4/assets.cpp
Normal file
544
engines/m4/assets.cpp
Normal file
|
@ -0,0 +1,544 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/assets.h"
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/compression.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
BaseAsset::BaseAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : _vm(vm) {
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseAsset::~BaseAsset() {
|
||||||
|
}
|
||||||
|
|
||||||
|
MachineAsset::MachineAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
|
||||||
|
uint32 stateCount = stream->readUint32LE();
|
||||||
|
for (uint32 curState = 0; curState < stateCount; curState++) {
|
||||||
|
uint32 stateOffset = stream->readUint32LE();
|
||||||
|
_stateTable.push_back(stateOffset);
|
||||||
|
}
|
||||||
|
_codeSize = size - 4 - 4 * stateCount;
|
||||||
|
_code = new byte[_codeSize];
|
||||||
|
stream->read(_code, _codeSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
MachineAsset::~MachineAsset() {
|
||||||
|
delete[] _code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MachineAsset::getCode(byte *&code, uint32 &codeSize) {
|
||||||
|
code = _code;
|
||||||
|
codeSize = _codeSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 MachineAsset::getStateOffset(uint32 state) {
|
||||||
|
assert(state < _stateTable.size());
|
||||||
|
return _stateTable[state];
|
||||||
|
}
|
||||||
|
|
||||||
|
SequenceAsset::SequenceAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
|
||||||
|
_localVarCount = stream->readUint32LE();
|
||||||
|
_codeSize = size - 4;
|
||||||
|
_code = new byte[_codeSize];
|
||||||
|
stream->read(_code, _codeSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
SequenceAsset::~SequenceAsset() {
|
||||||
|
delete[] _code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SequenceAsset::getCode(byte *&code, uint32 &codeSize) {
|
||||||
|
code = _code;
|
||||||
|
codeSize = _codeSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
DataAsset::DataAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) {
|
||||||
|
|
||||||
|
_recCount = stream->readUint32LE();
|
||||||
|
_recSize = stream->readUint32LE();
|
||||||
|
_dataSize = _recCount * _recSize;
|
||||||
|
_data = new long[_dataSize];
|
||||||
|
for (uint32 i = 0; i < _dataSize; i++)
|
||||||
|
_data[i] = (long)stream->readUint32LE();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
DataAsset::~DataAsset() {
|
||||||
|
delete _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
long *DataAsset::getRow(int index) {
|
||||||
|
assert(index < _recCount);
|
||||||
|
return &_data[_recSize * index];
|
||||||
|
}
|
||||||
|
|
||||||
|
SpriteAsset::SpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) : BaseAsset(vm, stream, size, name) {
|
||||||
|
_stream = stream;
|
||||||
|
|
||||||
|
if (_vm->isM4()) {
|
||||||
|
loadM4SpriteAsset(vm, stream, asStream);
|
||||||
|
} else {
|
||||||
|
loadMadsSpriteAsset(vm, stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpriteAsset::loadM4SpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream, bool asStream) {
|
||||||
|
bool isBigEndian = false;
|
||||||
|
uint32 frameOffset;
|
||||||
|
|
||||||
|
uint32 header = _stream->readUint32LE();
|
||||||
|
if (header == HEAD_M4SS) {
|
||||||
|
printf("LE-encoded sprite\n");
|
||||||
|
} else {
|
||||||
|
printf("BE-encoded sprite\n");
|
||||||
|
isBigEndian = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_srcSize = parseSprite(isBigEndian);
|
||||||
|
|
||||||
|
_stream->readUint32LE();
|
||||||
|
_frameRate = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
_pixelSpeed = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
_maxWidth = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
_maxHeight = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
_stream->skip(6 * 4);
|
||||||
|
_frameCount = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
|
||||||
|
printf("SpriteAsset::SpriteAsset() srcSize = %d; frameRate = %04X; pixelSpeed = %04X; maxWidth = %d; maxHeight = %d; frameCount = %d\n", _srcSize, _frameRate, _pixelSpeed, _maxWidth, _maxHeight, _frameCount);
|
||||||
|
|
||||||
|
for (int curFrame = 0; curFrame < _frameCount; curFrame++) {
|
||||||
|
frameOffset = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
_frameOffsets.push_back(frameOffset);
|
||||||
|
}
|
||||||
|
_frameOffsets.push_back(_srcSize - 48 - _frameCount * 4);
|
||||||
|
|
||||||
|
_frameStartOffset = _stream->pos();
|
||||||
|
|
||||||
|
// We don't need to load frames when streaming
|
||||||
|
if (asStream)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int curFrame = 0; curFrame < _frameCount; curFrame++) {
|
||||||
|
frameOffset = _frameStartOffset + _frameOffsets[curFrame];
|
||||||
|
_stream->seek(frameOffset);
|
||||||
|
|
||||||
|
SpriteAssetFrame frame;
|
||||||
|
loadFrameHeader(frame, isBigEndian);
|
||||||
|
|
||||||
|
// Load & unpack RLE data if it's not a streaming animation
|
||||||
|
if (frame.stream != 1) {
|
||||||
|
|
||||||
|
frame.frame = new M4Sprite(stream, frame.x, frame.y, frame.w, frame.h, true, frame.comp);
|
||||||
|
#if 0
|
||||||
|
char fn[512];
|
||||||
|
sprintf(fn, "%04d.raw", curFrame);
|
||||||
|
FILE *h = fopen(fn, "wb");
|
||||||
|
fwrite((byte*)frame.frame->getData(), frame.w * frame.h, 1, h);
|
||||||
|
fclose(h);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
_frames.push_back(frame);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpriteAsset::loadMadsSpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream) {
|
||||||
|
int curFrame = 0;
|
||||||
|
uint32 frameOffset = 0;
|
||||||
|
MadsPack sprite(stream);
|
||||||
|
_frameRate = 0;
|
||||||
|
_pixelSpeed = 0;
|
||||||
|
_maxWidth = 0;
|
||||||
|
_maxHeight = 0;
|
||||||
|
|
||||||
|
Common::SeekableReadStream *spriteStream = sprite.getItemStream(0);
|
||||||
|
for (int i = 0; i < 19; i++) {
|
||||||
|
spriteStream->readUint16LE();
|
||||||
|
}
|
||||||
|
_frameCount = spriteStream->readUint16LE();
|
||||||
|
// we skip the rest of the data
|
||||||
|
delete spriteStream;
|
||||||
|
|
||||||
|
// Get the palette data
|
||||||
|
spriteStream = sprite.getItemStream(2);
|
||||||
|
int numColors = 0;
|
||||||
|
RGB8 *palData = Palette::decodeMadsPalette(spriteStream, &numColors);
|
||||||
|
Common::copy(palData, &palData[numColors], &_palette[0]);
|
||||||
|
if (numColors < 256)
|
||||||
|
Common::set_to((byte *)&_palette[numColors], (byte *)&_palette[256], 0);
|
||||||
|
_colorCount = numColors;
|
||||||
|
delete[] palData;
|
||||||
|
delete spriteStream;
|
||||||
|
|
||||||
|
spriteStream = sprite.getItemStream(1);
|
||||||
|
Common::SeekableReadStream *spriteDataStream = sprite.getItemStream(3);
|
||||||
|
SpriteAssetFrame frame;
|
||||||
|
for (curFrame = 0; curFrame < _frameCount; curFrame++) {
|
||||||
|
frame.comp = 0;
|
||||||
|
frameOffset = spriteStream->readUint32LE();
|
||||||
|
_frameOffsets.push_back(frameOffset);
|
||||||
|
spriteStream->readUint32LE(); // frame size
|
||||||
|
frame.x = spriteStream->readUint16LE();
|
||||||
|
frame.y = spriteStream->readUint16LE();
|
||||||
|
frame.w = spriteStream->readUint16LE();
|
||||||
|
frame.h = spriteStream->readUint16LE();
|
||||||
|
if (curFrame == 0)
|
||||||
|
printf("%i frames, x = %i, y = %i, w = %i, h = %i\n", _frameCount, frame.x, frame.y, frame.w, frame.h);
|
||||||
|
|
||||||
|
frame.frame = new M4Sprite(spriteDataStream, frame.x, frame.y, frame.w, frame.h, false);
|
||||||
|
_frames.push_back(frame);
|
||||||
|
}
|
||||||
|
delete spriteStream;
|
||||||
|
delete spriteDataStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpriteAsset::~SpriteAsset() {
|
||||||
|
for (Common::Array<SpriteAssetFrame>::iterator it = _frames.begin(); it != _frames.end(); it++) {
|
||||||
|
delete (*it).frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 SpriteAsset::parseSprite(bool isBigEndian) {
|
||||||
|
|
||||||
|
uint32 format, chunkType, chunkSize = 0;
|
||||||
|
|
||||||
|
_colorCount = 0;
|
||||||
|
|
||||||
|
format = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
|
||||||
|
chunkType = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
|
||||||
|
if (chunkType == CELS__PAL) {
|
||||||
|
chunkSize = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
uint32 numColors = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
// TODO
|
||||||
|
//if (palette) {
|
||||||
|
// TODO: A sprite set palette specifies the indexes, which need not start at
|
||||||
|
// index 0. For now, I'm simply preloading the currently active palette
|
||||||
|
// before starting to replace existing entries
|
||||||
|
|
||||||
|
_vm->_palette->grabPalette((byte *) _palette, 0, 256);
|
||||||
|
_colorCount = 0;
|
||||||
|
|
||||||
|
for (uint32 i = 0; i < numColors; i++) {
|
||||||
|
uint32 paletteEntry = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
uint index = (paletteEntry >> 24) & 0xFF;
|
||||||
|
|
||||||
|
_palette[index].r = ((paletteEntry >> 16) & 0xFF) << 2;
|
||||||
|
_palette[index].g = ((paletteEntry >> 8) & 0xFF) << 2;
|
||||||
|
_palette[index].b = (paletteEntry & 0xFF) << 2;
|
||||||
|
|
||||||
|
_colorCount = MAX(_colorCount, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
} else {
|
||||||
|
stream.seek(colorCount, )
|
||||||
|
data += colorCount * 4;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
chunkType = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunkType == CELS___SS) {
|
||||||
|
chunkSize = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
} else {
|
||||||
|
warning("SpriteAsset::parseSprite() Expected chunk type %08X, got %08X", CELS___SS, chunkType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunkSize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpriteAsset::loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian) {
|
||||||
|
_stream->readUint32LE();
|
||||||
|
frameHeader.stream = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
frameHeader.x = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
frameHeader.y = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
frameHeader.w = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
frameHeader.h = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
frameHeader.comp = (!isBigEndian) ? _stream->readUint32LE() : _stream->readUint32BE();
|
||||||
|
frameHeader.frame = NULL;
|
||||||
|
_stream->seek(8 * 4, SEEK_CUR);
|
||||||
|
//printf("SpriteAsset::loadFrameHeader() stream = %d; x = %d; y = %d; w = %d; h = %d; comp = %d\n", frameHeader.stream, frameHeader.x, frameHeader.y, frameHeader.w, frameHeader.h, frameHeader.comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
M4Sprite *SpriteAsset::getFrame(int frameIndex) {
|
||||||
|
return _frames[frameIndex].frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpriteAsset::loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY) {
|
||||||
|
uint32 frameOffset = _frameStartOffset + _frameOffsets[frameIndex];
|
||||||
|
_stream->seek(frameOffset);
|
||||||
|
|
||||||
|
SpriteAssetFrame frameHeader;
|
||||||
|
loadFrameHeader(frameHeader);
|
||||||
|
|
||||||
|
if (frameHeader.w > 0 && frameHeader.h > 0) {
|
||||||
|
Common::MemoryReadStream *frameData = _stream->readStream(getFrameSize(frameIndex));
|
||||||
|
if (frameHeader.stream) {
|
||||||
|
frame->loadDeltaRle(frameData, destX - frameHeader.x, destY - frameHeader.y);
|
||||||
|
} else {
|
||||||
|
frame->loadRle(frameData);
|
||||||
|
}
|
||||||
|
delete frameData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RGBList *SpriteAsset::getRgbList() {
|
||||||
|
RGBList *result = new RGBList(_colorCount);
|
||||||
|
Common::copy((byte *)&_palette[0], (byte *)&_palette[_colorCount], (byte *)result->data());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpriteAsset::translate(RGBList *list, bool isTransparent) {
|
||||||
|
for (int frameIndex = 0; frameIndex < _frameCount; ++frameIndex)
|
||||||
|
_frames[frameIndex].frame->translate(list, isTransparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 SpriteAsset::getFrameSize(int index) {
|
||||||
|
/*
|
||||||
|
if (index + 1 == _frameCount) {
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return _frameOffsets[index + 1] - _frameOffsets[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetManager::AssetManager(M4Engine *vm) {
|
||||||
|
|
||||||
|
_vm = vm;
|
||||||
|
|
||||||
|
/* Initialize asset arrays */
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
_MACH[i] = NULL;
|
||||||
|
_SEQU[i] = NULL;
|
||||||
|
_DATA[i] = NULL;
|
||||||
|
_CELS[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetManager::~AssetManager() {
|
||||||
|
// unload all remaining assets
|
||||||
|
clearAssets(kAssetTypeMACH, 0, 255);
|
||||||
|
clearAssets(kAssetTypeSEQU, 0, 255);
|
||||||
|
clearAssets(kAssetTypeCELS, 0, 255);
|
||||||
|
clearAssets(kAssetTypeDATA, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetManager::clearAssets(AssetType assetType, int32 minHash, int32 maxHash) {
|
||||||
|
|
||||||
|
minHash = MAX(0, minHash);
|
||||||
|
maxHash = MIN(maxHash, 255);
|
||||||
|
|
||||||
|
switch (assetType) {
|
||||||
|
case kAssetTypeMACH:
|
||||||
|
for (int i = minHash; i <= maxHash; i++)
|
||||||
|
if (_MACH[i]) {
|
||||||
|
delete _MACH[i];
|
||||||
|
_MACH[i] = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kAssetTypeSEQU:
|
||||||
|
for (int i = minHash; i <= maxHash; i++)
|
||||||
|
if (_SEQU[i]) {
|
||||||
|
delete _SEQU[i];
|
||||||
|
_SEQU[i] = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kAssetTypeDATA:
|
||||||
|
for (int i = minHash; i <= maxHash; i++)
|
||||||
|
if (_DATA[i]) {
|
||||||
|
delete _DATA[i];
|
||||||
|
_DATA[i] = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kAssetTypeCELS:
|
||||||
|
for (int i = minHash; i <= maxHash; i++)
|
||||||
|
if (_CELS[i]) {
|
||||||
|
delete _CELS[i];
|
||||||
|
_CELS[i] = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: no value is returned, returning true for now
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetManager::loadAsset(const char *assetName, RGB8 *palette) {
|
||||||
|
|
||||||
|
printf("AssetManager::loadAsset() %s\n", assetName);
|
||||||
|
|
||||||
|
// TODO, better use MemoryReadStreamEndian?
|
||||||
|
//convertAssetToLE(assetData, assetSize);
|
||||||
|
|
||||||
|
Common::SeekableReadStream *assetS = _vm->res()->get(assetName);
|
||||||
|
|
||||||
|
while (assetS->pos() + 12 < assetS->size()) {
|
||||||
|
uint32 chunkType, chunkSize, chunkHash;
|
||||||
|
|
||||||
|
chunkType = assetS->readUint32LE();
|
||||||
|
chunkSize = assetS->readUint32LE() - 12; // sub 12 for the chunk header
|
||||||
|
chunkHash = assetS->readUint32LE();
|
||||||
|
|
||||||
|
printf("hash = %d\n", chunkHash);
|
||||||
|
|
||||||
|
// Until loading code is complete, so that chunks not fully read are skipped correctly
|
||||||
|
uint32 nextOfs = assetS->pos() + chunkSize;
|
||||||
|
|
||||||
|
switch (chunkType) {
|
||||||
|
case CHUNK_MACH:
|
||||||
|
printf("MACH\n");
|
||||||
|
clearAssets(kAssetTypeMACH, chunkHash, chunkHash);
|
||||||
|
_MACH[chunkHash] = new MachineAsset(_vm, assetS, chunkSize, assetName);
|
||||||
|
break;
|
||||||
|
case CHUNK_SEQU:
|
||||||
|
printf("SEQU\n");
|
||||||
|
clearAssets(kAssetTypeSEQU, chunkHash, chunkHash);
|
||||||
|
_SEQU[chunkHash] = new SequenceAsset(_vm, assetS, chunkSize, assetName);
|
||||||
|
break;
|
||||||
|
case CHUNK_DATA:
|
||||||
|
printf("DATA\n");
|
||||||
|
clearAssets(kAssetTypeDATA, chunkHash, chunkHash);
|
||||||
|
_DATA[chunkHash] = new DataAsset(_vm, assetS, chunkSize, assetName);
|
||||||
|
break;
|
||||||
|
case CHUNK_CELS:
|
||||||
|
printf("CELS\n");
|
||||||
|
clearAssets(kAssetTypeCELS, chunkHash, chunkHash);
|
||||||
|
_CELS[chunkHash] = new SpriteAsset(_vm, assetS, chunkSize, assetName);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("AssetManager::loadAsset() Unknown chunk type %08X\n", chunkType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Until loading code is complete (see above)
|
||||||
|
assetS->seek(nextOfs);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->res()->toss(assetName);
|
||||||
|
|
||||||
|
// FIXME: no value is returned, returning true for now
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 AssetManager::addSpriteAsset(const char *assetName, int32 hash, RGB8 *palette) {
|
||||||
|
|
||||||
|
bool alreadyLoaded = false;
|
||||||
|
|
||||||
|
if (hash < 0) {
|
||||||
|
for (int i = 0; i <= 255; i++) {
|
||||||
|
if (_CELS[i] != NULL) {
|
||||||
|
if (_CELS[i]->getName() == assetName) {
|
||||||
|
alreadyLoaded = true;
|
||||||
|
hash = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hash = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alreadyLoaded = _CELS[hash] != NULL && _CELS[hash]->getName() == assetName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not loaded and no empty slots */
|
||||||
|
if (hash < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!alreadyLoaded) {
|
||||||
|
|
||||||
|
printf("AssetManager::addSpriteAsset() asset %s not loaded, loading into %d\n", assetName, hash);
|
||||||
|
|
||||||
|
clearAssets(kAssetTypeCELS, hash, hash);
|
||||||
|
|
||||||
|
Common::SeekableReadStream *assetS = _vm->res()->get(assetName);
|
||||||
|
_CELS[hash] = new SpriteAsset(_vm, assetS, assetS->size(), assetName);
|
||||||
|
_vm->res()->toss(assetName);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
printf("AssetManager::addSpriteAsset() asset %s already loaded in %d\n", assetName, hash);
|
||||||
|
|
||||||
|
/* TODO/FIXME
|
||||||
|
if (_CELS[hash].palOffset >= 0 && palette)
|
||||||
|
restorePalette(palette, _CELS[hash].data + _CELS[hash].palOffset);
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetManager::restorePalette(RGB8 *palette, byte *data) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetManager::convertAssetToLE(byte *assetData, uint32 assetSize) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MachineAsset *AssetManager::getMachine(int32 hash) {
|
||||||
|
assert(_MACH[hash] != NULL);
|
||||||
|
return _MACH[hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
SequenceAsset *AssetManager::getSequence(int32 hash) {
|
||||||
|
assert(_SEQU[hash] != NULL);
|
||||||
|
return _SEQU[hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
DataAsset *AssetManager::getData(int32 hash) {
|
||||||
|
assert(_DATA[hash] != NULL);
|
||||||
|
return _DATA[hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
SpriteAsset *AssetManager::getSprite(int32 hash) {
|
||||||
|
assert(_CELS[hash] != NULL);
|
||||||
|
return _CELS[hash];
|
||||||
|
}
|
||||||
|
|
||||||
|
M4Sprite *AssetManager::getSpriteFrame(int32 hash, int frameIndex) {
|
||||||
|
assert(_CELS[hash] != NULL);
|
||||||
|
return _CELS[hash]->getFrame(frameIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 AssetManager::getSpriteFrameCount(int32 hash) {
|
||||||
|
assert(_CELS[hash] != NULL);
|
||||||
|
return _CELS[hash]->getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
184
engines/m4/assets.h
Normal file
184
engines/m4/assets.h
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef M4_ASSETS_H
|
||||||
|
#define M4_ASSETS_H
|
||||||
|
|
||||||
|
#include "common/scummsys.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
|
||||||
|
#include "m4/sprite.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
// Sequence chunks
|
||||||
|
#define CHUNK_SCEN MKID_BE('SCEN')
|
||||||
|
#define CHUNK_MACH MKID_BE('MACH')
|
||||||
|
#define CHUNK_SEQU MKID_BE('SEQU')
|
||||||
|
#define CHUNK_DATA MKID_BE('DATA')
|
||||||
|
#define CHUNK_CELS MKID_BE('CELS')
|
||||||
|
|
||||||
|
// Sprite chunks
|
||||||
|
#define HEAD_M4SS MKID_BE('M4SS') //'M4SS'
|
||||||
|
#define CELS__PAL MKID_BE(' PAL') //' PAL'
|
||||||
|
#define CELS___SS MKID_BE(' SS') //' SS'
|
||||||
|
|
||||||
|
class M4Engine;
|
||||||
|
|
||||||
|
class BaseAsset {
|
||||||
|
public:
|
||||||
|
BaseAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
|
||||||
|
~BaseAsset();
|
||||||
|
const Common::String getName() const { return _name; }
|
||||||
|
protected:
|
||||||
|
M4Engine *_vm;
|
||||||
|
Common::String _name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MachineAsset : public BaseAsset {
|
||||||
|
public:
|
||||||
|
MachineAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
|
||||||
|
~MachineAsset();
|
||||||
|
void getCode(byte *&code, uint32 &codeSize);
|
||||||
|
uint32 getStateOffset(uint32 state);
|
||||||
|
protected:
|
||||||
|
Common::Array<uint32> _stateTable;
|
||||||
|
byte *_code;
|
||||||
|
uint32 _codeSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SequenceAsset : public BaseAsset {
|
||||||
|
public:
|
||||||
|
SequenceAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
|
||||||
|
~SequenceAsset();
|
||||||
|
void getCode(byte *&code, uint32 &codeSize);
|
||||||
|
int localVarCount() const { return _localVarCount; }
|
||||||
|
protected:
|
||||||
|
int _localVarCount;
|
||||||
|
byte *_code;
|
||||||
|
uint32 _codeSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DataAsset : public BaseAsset {
|
||||||
|
public:
|
||||||
|
DataAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name);
|
||||||
|
~DataAsset();
|
||||||
|
int getCount() const { return _recCount; }
|
||||||
|
long *getRow(int index);
|
||||||
|
protected:
|
||||||
|
long *_data;
|
||||||
|
uint32 _recSize, _dataSize;
|
||||||
|
int _recCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SpriteAssetFrame {
|
||||||
|
uint32 stream;
|
||||||
|
int x, y, w, h;
|
||||||
|
uint32 comp;
|
||||||
|
M4Sprite *frame;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SpriteAsset : public BaseAsset {
|
||||||
|
public:
|
||||||
|
SpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream = false);
|
||||||
|
~SpriteAsset();
|
||||||
|
void loadM4SpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream, bool asStream);
|
||||||
|
void loadMadsSpriteAsset(M4Engine *vm, Common::SeekableReadStream* stream);
|
||||||
|
int32 getCount() { return _frameCount; }
|
||||||
|
int32 getFrameRate() const { return _frameRate; }
|
||||||
|
int32 getPixelSpeed() const { return _pixelSpeed; }
|
||||||
|
int32 getFrameWidth(int index);
|
||||||
|
int32 getFrameHeight(int index);
|
||||||
|
int32 getMaxFrameWidth() const { return _maxWidth; }
|
||||||
|
int32 getMaxFrameHeight() const { return _maxHeight; }
|
||||||
|
M4Sprite *getFrame(int frameIndex);
|
||||||
|
void loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY);
|
||||||
|
RGB8* getPalette() { return _palette; }
|
||||||
|
int getColorCount() { return _colorCount; }
|
||||||
|
RGBList *getRgbList();
|
||||||
|
void translate(RGBList *list, bool isTransparent = false);
|
||||||
|
int32 getFrameSize(int index);
|
||||||
|
M4Sprite *operator[](int index) { return getFrame(index); }
|
||||||
|
protected:
|
||||||
|
RGB8 _palette[256];
|
||||||
|
uint32 _colorCount;
|
||||||
|
uint32 _srcSize;
|
||||||
|
int32 _frameRate, _pixelSpeed;
|
||||||
|
int _maxWidth, _maxHeight;
|
||||||
|
int _frameCount;
|
||||||
|
Common::Array<uint32> _frameOffsets;
|
||||||
|
Common::Array<SpriteAssetFrame> _frames;
|
||||||
|
uint32 _frameStartOffset;
|
||||||
|
Common::SeekableReadStream *_stream;
|
||||||
|
int32 parseSprite(bool isBigEndian = false);
|
||||||
|
void loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AssetType {
|
||||||
|
kAssetTypeMACH,
|
||||||
|
kAssetTypeSEQU,
|
||||||
|
kAssetTypeDATA,
|
||||||
|
kAssetTypeCELS
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CallbackHandlers {
|
||||||
|
kCallbackTriggerDispatch
|
||||||
|
};
|
||||||
|
|
||||||
|
class AssetManager {
|
||||||
|
public:
|
||||||
|
|
||||||
|
AssetManager(M4Engine *vm);
|
||||||
|
~AssetManager();
|
||||||
|
|
||||||
|
bool clearAssets(AssetType assetType, int32 minHash, int32 maxHash);
|
||||||
|
bool loadAsset(const char *assetName, RGB8 *palette);
|
||||||
|
int32 addSpriteAsset(const char *assetName, int32 hash, RGB8 *palette);
|
||||||
|
|
||||||
|
// TODO: Move to Palette class
|
||||||
|
void restorePalette(RGB8 *palette, byte *data);
|
||||||
|
|
||||||
|
MachineAsset *getMachine(int32 hash);
|
||||||
|
SequenceAsset *getSequence(int32 hash);
|
||||||
|
DataAsset *getData(int32 hash);
|
||||||
|
SpriteAsset *getSprite(int32 hash);
|
||||||
|
M4Sprite *getSpriteFrame(int32 hash, int frameIndex);
|
||||||
|
int32 getSpriteFrameCount(int32 hash);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// TODO: Check if we need _vm
|
||||||
|
M4Engine *_vm;
|
||||||
|
|
||||||
|
MachineAsset *_MACH[256];
|
||||||
|
SequenceAsset *_SEQU[256];
|
||||||
|
DataAsset *_DATA[256];
|
||||||
|
SpriteAsset *_CELS[256];
|
||||||
|
|
||||||
|
void convertAssetToLE(byte *assetData, uint32 assetSize);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
85
engines/m4/burger_data.h
Normal file
85
engines/m4/burger_data.h
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_BURGER_DATA_H
|
||||||
|
#define M4_BURGER_DATA_H
|
||||||
|
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/actor.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
InventoryObject burger_inventory [] = {
|
||||||
|
// name scene icon
|
||||||
|
//-------------------- ----- -----
|
||||||
|
{ "empty jug", 303, 14 },
|
||||||
|
{ "distilled juice", 999, 15 },
|
||||||
|
{ "broken puz dispenser", 999, 16 },
|
||||||
|
{ "puz dispenser", 999, 17 },
|
||||||
|
{ "broken mouse trap", 999, 18 },
|
||||||
|
{ "mouse trap", 999, 19 },
|
||||||
|
{ "kindling", 999, 20 },
|
||||||
|
{ "burning kindling", 999, 21 },
|
||||||
|
{ "lights", 508, 22 },
|
||||||
|
{ "lights on", 508, 23 },
|
||||||
|
{ "bottle", 999, 24 },
|
||||||
|
{ "carrot juice", 999, 25 },
|
||||||
|
{ "soapy water", 999, 26 },
|
||||||
|
{ "iron filings", 999, 27 },
|
||||||
|
{ "waxed hair", 999, 28 },
|
||||||
|
{ "fish", 999, 29 },
|
||||||
|
{ "hook", 999, 30 },
|
||||||
|
{ "keys", 999, 31 },
|
||||||
|
{ "records", 999, 32 },
|
||||||
|
{ "collar", 999, 33 },
|
||||||
|
{ "amp", 999, 34 },
|
||||||
|
{ "rubber gloves", 999, 35 },
|
||||||
|
{ "sock", 504, 36 },
|
||||||
|
{ "jaws of life", 999, 37 },
|
||||||
|
{ "deed", 999, 38 },
|
||||||
|
{ "burger morsel", 999, 39 },
|
||||||
|
{ "whistle", 999, 40 },
|
||||||
|
{ "coin", 999, 41 },
|
||||||
|
{ "matches", 999, 42 },
|
||||||
|
{ "phone cord", 999, 43 },
|
||||||
|
{ "kibble", 602, 44 }, // picked up from tray
|
||||||
|
{ "pantyhose", 999, 45 },
|
||||||
|
{ "fan belt", 999, 46 },
|
||||||
|
{ "spring", 999, 47 },
|
||||||
|
{ "mirror", 999, 48 },
|
||||||
|
{ "grate", 999, 49 },
|
||||||
|
{ "ray gun", 604, 50 }, // given to Wilbur when he enters 604
|
||||||
|
{ "grasshoppers", 999, 51 },
|
||||||
|
{ "rolling pin", 999, 52 },
|
||||||
|
{ "rubber duck", 999, 53 },
|
||||||
|
{ "ladder", 999, 54 },
|
||||||
|
{ "money", 999, 55 },
|
||||||
|
{ "crow bar", 999, 56 },
|
||||||
|
{ "Wilbur", 999, 57 }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
189
engines/m4/compression.cpp
Normal file
189
engines/m4/compression.cpp
Normal file
|
@ -0,0 +1,189 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/compression.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
const char *madsPackString = "MADSPACK";
|
||||||
|
|
||||||
|
bool MadsPack::isCompressed(Common::SeekableReadStream *stream) {
|
||||||
|
// Check whether the passed stream is packed
|
||||||
|
|
||||||
|
char tempBuffer[8];
|
||||||
|
stream->seek(0);
|
||||||
|
if (stream->read(tempBuffer, 8) == 8) {
|
||||||
|
if (!strncmp(tempBuffer, madsPackString, 8))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MadsPack::MadsPack(Common::SeekableReadStream *stream) {
|
||||||
|
initialise(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
MadsPack::MadsPack(const char *resourceName, M4Engine* vm) {
|
||||||
|
Common::SeekableReadStream *stream = vm->_resourceManager->get(resourceName);
|
||||||
|
initialise(stream);
|
||||||
|
vm->_resourceManager->toss(resourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MadsPack::initialise(Common::SeekableReadStream *stream) {
|
||||||
|
if (!MadsPack::isCompressed(stream))
|
||||||
|
error("Attempted to decompress a resource that was not MadsPacked");
|
||||||
|
|
||||||
|
stream->seek(14);
|
||||||
|
_count = stream->readUint16LE();
|
||||||
|
_items = new MadsPackEntry[_count];
|
||||||
|
|
||||||
|
byte *headerData = new byte[0xA0];
|
||||||
|
byte *header = headerData;
|
||||||
|
stream->read(headerData, 0xA0);
|
||||||
|
|
||||||
|
for (int i = 0; i < _count; ++i, header += 10) {
|
||||||
|
// Get header data
|
||||||
|
_items[i].hash = READ_LE_UINT16(header);
|
||||||
|
_items[i].size = READ_LE_UINT32(header + 2);
|
||||||
|
_items[i].compressedSize = READ_LE_UINT32(header + 6);
|
||||||
|
|
||||||
|
_items[i].data = new byte[_items[i].size];
|
||||||
|
if (_items[i].size == _items[i].compressedSize) {
|
||||||
|
// Entry isn't compressed
|
||||||
|
stream->read(_items[i].data, _items[i].size);
|
||||||
|
} else {
|
||||||
|
// Decompress the entry
|
||||||
|
byte *compressedData = new byte[_items[i].compressedSize];
|
||||||
|
stream->read(compressedData, _items[i].compressedSize);
|
||||||
|
|
||||||
|
FabDecompressor fab;
|
||||||
|
fab.decompress(compressedData, _items[i].compressedSize, _items[i].data, _items[i].size);
|
||||||
|
delete[] compressedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] headerData;
|
||||||
|
_dataOffset = stream->pos();
|
||||||
|
}
|
||||||
|
|
||||||
|
MadsPack::~MadsPack() {
|
||||||
|
for (int i = 0; i < _count; ++i)
|
||||||
|
delete[] _items[i].data;
|
||||||
|
delete[] _items;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const char *FabInputExceededError = "FabDecompressor - Passed end of input buffer during decompression";
|
||||||
|
const char *FabOutputExceededError = "FabDecompressor - Decompressed data exceeded specified size";
|
||||||
|
|
||||||
|
void FabDecompressor::decompress(const byte *srcData, int srcSize, byte *destData, int destSize) {
|
||||||
|
byte copyLen, copyOfsShift, copyOfsMask, copyLenMask;
|
||||||
|
unsigned long copyOfs;
|
||||||
|
byte *destP;
|
||||||
|
|
||||||
|
// Validate that the data starts with the FAB header
|
||||||
|
if (strncmp((const char *)srcData, "FAB", 3) != 0)
|
||||||
|
error("FabDecompressor - Invalid compressed data");
|
||||||
|
|
||||||
|
int shiftVal = srcData[3];
|
||||||
|
if ((shiftVal < 10) || (shiftVal > 13))
|
||||||
|
error("FabDecompressor - Invalid shift start");
|
||||||
|
|
||||||
|
copyOfsShift = 16 - shiftVal;
|
||||||
|
copyOfsMask = 0xFF << (shiftVal - 8);
|
||||||
|
copyLenMask = (1 << copyOfsShift) - 1;
|
||||||
|
copyOfs = 0xFFFF0000;
|
||||||
|
destP = destData;
|
||||||
|
|
||||||
|
// Initialise data fields
|
||||||
|
_srcData = srcData;
|
||||||
|
_srcP = _srcData + 6;
|
||||||
|
_srcSize = srcSize;
|
||||||
|
_bitsLeft = 16;
|
||||||
|
_bitBuffer = READ_LE_UINT16(srcData + 4);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (getBit() == 0) {
|
||||||
|
if (getBit() == 0) {
|
||||||
|
copyLen = ((getBit() << 1) | getBit()) + 2;
|
||||||
|
copyOfs = *_srcP++ | 0xFFFFFF00;
|
||||||
|
} else {
|
||||||
|
copyOfs = (((_srcP[1] >> copyOfsShift) | copyOfsMask) << 8) | _srcP[0];
|
||||||
|
copyLen = _srcP[1] & copyLenMask;
|
||||||
|
_srcP += 2;
|
||||||
|
if (copyLen == 0) {
|
||||||
|
copyLen = *_srcP++;
|
||||||
|
if (copyLen == 0)
|
||||||
|
break;
|
||||||
|
else if (copyLen == 1)
|
||||||
|
continue;
|
||||||
|
else
|
||||||
|
copyLen++;
|
||||||
|
} else {
|
||||||
|
copyLen += 2;
|
||||||
|
}
|
||||||
|
copyOfs |= 0xFFFF0000;
|
||||||
|
}
|
||||||
|
while (copyLen-- > 0) {
|
||||||
|
if (destP - destData == destSize)
|
||||||
|
error(FabOutputExceededError);
|
||||||
|
|
||||||
|
*destP = destP[(signed int)copyOfs];
|
||||||
|
destP++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_srcP - srcData == srcSize)
|
||||||
|
error(FabInputExceededError);
|
||||||
|
if (destP - destData == destSize)
|
||||||
|
error(FabOutputExceededError);
|
||||||
|
|
||||||
|
*destP++ = *_srcP++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destP - destData != destSize)
|
||||||
|
error("FabDecompressor - Decompressed data does not match header decompressed size");
|
||||||
|
}
|
||||||
|
|
||||||
|
int FabDecompressor::getBit() {
|
||||||
|
_bitsLeft--;
|
||||||
|
if (_bitsLeft == 0) {
|
||||||
|
if (_srcP - _srcData == _srcSize)
|
||||||
|
error(FabInputExceededError);
|
||||||
|
|
||||||
|
_bitBuffer = (READ_LE_UINT16(_srcP) << 1) | (_bitBuffer & 1);
|
||||||
|
_srcP += 2;
|
||||||
|
_bitsLeft = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bit = _bitBuffer & 1;
|
||||||
|
_bitBuffer >>= 1;
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
82
engines/m4/compression.h
Normal file
82
engines/m4/compression.h
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_COMPRESSION_H
|
||||||
|
#define M4_COMPRESSION_H
|
||||||
|
|
||||||
|
#include "common/scummsys.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
#include "common/endian.h"
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
struct MadsPackEntry {
|
||||||
|
public:
|
||||||
|
uint16 hash;
|
||||||
|
uint32 size;
|
||||||
|
uint32 compressedSize;
|
||||||
|
byte *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MadsPack {
|
||||||
|
private:
|
||||||
|
MadsPackEntry *_items;
|
||||||
|
int _count;
|
||||||
|
int _dataOffset;
|
||||||
|
|
||||||
|
void initialise(Common::SeekableReadStream *stream);
|
||||||
|
public:
|
||||||
|
static bool isCompressed(Common::SeekableReadStream *stream);
|
||||||
|
MadsPack(Common::SeekableReadStream *stream);
|
||||||
|
MadsPack(const char *resourceName, M4Engine* _vm);
|
||||||
|
~MadsPack();
|
||||||
|
|
||||||
|
int getCount() const { return _count; }
|
||||||
|
MadsPackEntry &getItem(int index) const { return _items[index]; }
|
||||||
|
MadsPackEntry &operator[](int index) const { return _items[index]; }
|
||||||
|
Common::MemoryReadStream *getItemStream(int index) {
|
||||||
|
return new Common::MemoryReadStream(_items[index].data, _items[index].size, false);
|
||||||
|
}
|
||||||
|
int getDataOffset() const { return _dataOffset; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class FabDecompressor {
|
||||||
|
private:
|
||||||
|
int _bitsLeft;
|
||||||
|
uint32 _bitBuffer;
|
||||||
|
const byte *_srcData, *_srcP;
|
||||||
|
int _srcSize;
|
||||||
|
|
||||||
|
int getBit();
|
||||||
|
public:
|
||||||
|
void decompress(const byte *srcData, int srcSize, byte *destData, int destSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
287
engines/m4/console.cpp
Normal file
287
engines/m4/console.cpp
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/console.h"
|
||||||
|
#include "m4/scene.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
Console::Console(M4Engine *vm) : GUI::Debugger() {
|
||||||
|
_vm = vm;
|
||||||
|
|
||||||
|
DCmd_Register("scene", WRAP_METHOD(Console, cmdLoadScene));
|
||||||
|
DCmd_Register("start", WRAP_METHOD(Console, cmdStartingScene));
|
||||||
|
DCmd_Register("scene_info", WRAP_METHOD(Console, cmdSceneInfo));
|
||||||
|
DCmd_Register("show_hotspots", WRAP_METHOD(Console, cmdShowHotSpots));
|
||||||
|
DCmd_Register("list_hotspots", WRAP_METHOD(Console, cmdListHotSpots));
|
||||||
|
DCmd_Register("play_sound", WRAP_METHOD(Console, cmdPlaySound));
|
||||||
|
DCmd_Register("play_dsr_sound", WRAP_METHOD(Console, cmdPlayDSRSound));
|
||||||
|
DCmd_Register("show_resources", WRAP_METHOD(Console, cmdShowResources));
|
||||||
|
DCmd_Register("show_codes", WRAP_METHOD(Console, cmdShowCodes));
|
||||||
|
DCmd_Register("dump_file", WRAP_METHOD(Console, cmdDumpFile));
|
||||||
|
DCmd_Register("sprite", WRAP_METHOD(Console, cmdShowSprite));
|
||||||
|
DCmd_Register("start_conv", WRAP_METHOD(Console, cmdStartConversation));
|
||||||
|
DCmd_Register("textview", WRAP_METHOD(Console, cmdShowTextview));
|
||||||
|
DCmd_Register("animview", WRAP_METHOD(Console, cmdShowAnimview));
|
||||||
|
DCmd_Register("anim", WRAP_METHOD(Console, cmdPlayAnimation));
|
||||||
|
}
|
||||||
|
|
||||||
|
Console::~Console() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdLoadScene(int argc, const char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
DebugPrintf("Usage: %s <scene number>\n", argv[0]);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (_vm->isM4())
|
||||||
|
_vm->_kernel->newRoom = atoi(argv[1]);
|
||||||
|
else
|
||||||
|
_vm->_scene->loadScene(atoi(argv[1]));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdStartingScene(int argc, const char **argv) {
|
||||||
|
if (_vm->getGameType() != GType_Riddle) {
|
||||||
|
if (_vm->isM4())
|
||||||
|
_vm->_kernel->newRoom = FIRST_SCENE;
|
||||||
|
else
|
||||||
|
_vm->_scene->loadScene(FIRST_SCENE);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
DebugPrintf("%s: Riddle of Master Lu is not supported", argv[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdShowHotSpots(int argc, const char **argv) {
|
||||||
|
_vm->_scene->showHotSpots();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdSceneInfo(int argc, const char **argv) {
|
||||||
|
DebugPrintf("Current scene is: %i\n", _vm->_scene->getCurrentScene());
|
||||||
|
if (_vm->isM4()) {
|
||||||
|
DebugPrintf("Scene resources:\n");
|
||||||
|
DebugPrintf("artBase: %s\n", _vm->_scene->getSceneResources().artBase);
|
||||||
|
DebugPrintf("pictureBase: %s\n", _vm->_scene->getSceneResources().pictureBase);
|
||||||
|
DebugPrintf("hotspotCount: %i\n", _vm->_scene->getSceneResources().hotspotCount);
|
||||||
|
DebugPrintf("parallaxCount: %i\n", _vm->_scene->getSceneResources().parallaxCount);
|
||||||
|
DebugPrintf("propsCount: %i\n", _vm->_scene->getSceneResources().propsCount);
|
||||||
|
DebugPrintf("frontY: %i\n", _vm->_scene->getSceneResources().frontY);
|
||||||
|
DebugPrintf("backY: %i\n", _vm->_scene->getSceneResources().backY);
|
||||||
|
DebugPrintf("frontScale: %i\n", _vm->_scene->getSceneResources().frontScale);
|
||||||
|
DebugPrintf("backScale: %i\n", _vm->_scene->getSceneResources().backScale);
|
||||||
|
DebugPrintf("depthTable: ");
|
||||||
|
for (uint i = 0; i < 16; i++)
|
||||||
|
DebugPrintf("%i ", _vm->_scene->getSceneResources().depthTable[i]);
|
||||||
|
DebugPrintf("\n");
|
||||||
|
DebugPrintf("railNodeCount: %i\n", _vm->_scene->getSceneResources().railNodeCount);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdListHotSpots(int argc, const char **argv) {
|
||||||
|
DebugPrintf("Scene hotspots\n");
|
||||||
|
_vm->_scene->getSceneResources().hotspots->dump();
|
||||||
|
if (_vm->isM4()) {
|
||||||
|
DebugPrintf("Scene parallax\n");
|
||||||
|
_vm->_scene->getSceneResources().parallax->dump();
|
||||||
|
DebugPrintf("Scene props\n");
|
||||||
|
_vm->_scene->getSceneResources().props->dump();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdPlaySound(int argc, const char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
DebugPrintf("Usage: %s <sound file>\n", argv[0]);
|
||||||
|
} else {
|
||||||
|
_vm->_sound->playSound(argv[1], 255, false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdPlayDSRSound(int argc, const char **argv) {
|
||||||
|
if (argc != 2 && argc != 3) {
|
||||||
|
DebugPrintf("Usage: %s <sound index> <DSR file>\n", argv[0]);
|
||||||
|
DebugPrintf("The DSR file parameter is optional, and specifies which DSR to load\n", argv[0]);
|
||||||
|
} else {
|
||||||
|
if (argc == 3)
|
||||||
|
_vm->_sound->loadDSRFile(argv[2]);
|
||||||
|
_vm->_sound->playDSRSound(atoi(argv[1]), 255, false);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdShowResources(int argc, const char **argv) {
|
||||||
|
_vm->res()->dump();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdShowCodes(int argc, const char **argv) {
|
||||||
|
if (_vm->getGameType() != GType_RexNebular)
|
||||||
|
_vm->_scene->showCodes();
|
||||||
|
else
|
||||||
|
DebugPrintf("Pathfinding codes not done yet for Rex Nebular");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdDumpFile(int argc, const char **argv) {
|
||||||
|
if (argc != 2 && argc != 3) {
|
||||||
|
DebugPrintf("Usage: %s <file> <uncompress>\n", argv[0]);
|
||||||
|
DebugPrintf("If uncompress is 1, the file is uncompressed (for MADS games)\n");
|
||||||
|
} else {
|
||||||
|
if (argc == 2) {
|
||||||
|
_vm->dumpFile(strdup(argv[1]));
|
||||||
|
} else {
|
||||||
|
if (argc == 3 && atoi(argv[2]) == 1)
|
||||||
|
_vm->dumpFile(strdup(argv[1]), true);
|
||||||
|
else
|
||||||
|
_vm->dumpFile(strdup(argv[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdShowSprite(int argc, const char **argv) {
|
||||||
|
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
|
||||||
|
if (view == NULL)
|
||||||
|
DebugPrintf("The scene view isn't currently active\n");
|
||||||
|
else if (argc < 2)
|
||||||
|
DebugPrintf("Usage: %s resource_name\n", argv[0]);
|
||||||
|
else {
|
||||||
|
char resourceName[20];
|
||||||
|
strncpy(resourceName, argv[1], 15);
|
||||||
|
resourceName[15] = '\0';
|
||||||
|
if (!strchr(resourceName, '.'))
|
||||||
|
strcat(resourceName, ".SS");
|
||||||
|
|
||||||
|
_vm->_viewManager->moveToFront(view);
|
||||||
|
Common::SeekableReadStream *data = _vm->res()->get(resourceName);
|
||||||
|
SpriteAsset *asset = new SpriteAsset(_vm, data, data->size(), resourceName);
|
||||||
|
_vm->res()->toss(resourceName);
|
||||||
|
|
||||||
|
RGBList *palData = new RGBList(asset->getColorCount(), asset->getPalette(), true);
|
||||||
|
_vm->_palette->addRange(palData);
|
||||||
|
|
||||||
|
// Get the scene background surface
|
||||||
|
M4Surface *bg = _vm->_scene->getBackgroundSurface();
|
||||||
|
|
||||||
|
// Write the sprite onto the screen
|
||||||
|
int x = 0, y = 0, yMax = 0;
|
||||||
|
for (int index = 0; index < asset->getCount(); index++) {
|
||||||
|
M4Sprite *spr = asset->getFrame(index);
|
||||||
|
spr->translate(palData); // sprite pixel translation
|
||||||
|
|
||||||
|
if ((x + spr->width() >= bg->width()) && (yMax != 0)) {
|
||||||
|
x = 0;
|
||||||
|
y += yMax;
|
||||||
|
yMax = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y >= bg->height())
|
||||||
|
break;
|
||||||
|
|
||||||
|
// FIXME: We assume that the transparent color is the color of the top left pixel
|
||||||
|
byte *transparentColor = (byte *)spr->pixels;
|
||||||
|
|
||||||
|
spr->copyTo(bg, x, y, (int)*transparentColor);
|
||||||
|
|
||||||
|
x += spr->width();
|
||||||
|
yMax = MAX(yMax, spr->height());
|
||||||
|
}
|
||||||
|
|
||||||
|
view->restore(0, 0, view->width(), view->height());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdStartConversation(int argc, const char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
DebugPrintf("Usage: %s <conversation file name>\n", argv[0]);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
_vm->_converse->startConversation(argv[1]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdShowTextview(int argc, const char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
DebugPrintf("Usage: %s <txr resource>\n", argv[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->_viewManager->showTextView(argv[1], false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdShowAnimview(int argc, const char **argv) {
|
||||||
|
if (argc != 2) {
|
||||||
|
DebugPrintf("Usage: %s <res resource>\n", argv[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char resName[80];
|
||||||
|
strcpy(resName, "@");
|
||||||
|
strcat(resName, *argv[1] == '@' ? argv[1] + 1 : argv[1]);
|
||||||
|
|
||||||
|
_vm->_viewManager->showAnimView(resName, false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Console::cmdPlayAnimation(int argc, const char **argv) {
|
||||||
|
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
|
||||||
|
if (view == NULL) {
|
||||||
|
DebugPrintf("The scene view isn't currently active\n");
|
||||||
|
} else if (argc != 2 && argc != 3) {
|
||||||
|
DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]);
|
||||||
|
DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n");
|
||||||
|
} else {
|
||||||
|
char resourceName[20];
|
||||||
|
strncpy(resourceName, argv[1], 15);
|
||||||
|
resourceName[15] = '\0';
|
||||||
|
if (!strchr(resourceName, '.'))
|
||||||
|
strcat(resourceName, ".AA");
|
||||||
|
|
||||||
|
_vm->_viewManager->moveToFront(view);
|
||||||
|
if (argc == 3 && atoi(argv[2]) == 1)
|
||||||
|
_vm->_animation->loadFullScreen(resourceName);
|
||||||
|
else
|
||||||
|
_vm->_animation->load(resourceName);
|
||||||
|
_vm->_animation->start();
|
||||||
|
view->restore(0, 0, view->width(), view->height());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
64
engines/m4/console.h
Normal file
64
engines/m4/console.h
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_CONSOLE_H
|
||||||
|
#define M4_CONSOLE_H
|
||||||
|
|
||||||
|
#include "gui/debugger.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
class M4Engine;
|
||||||
|
|
||||||
|
class Console : public GUI::Debugger {
|
||||||
|
public:
|
||||||
|
Console(M4Engine *vm);
|
||||||
|
virtual ~Console(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool cmdLoadScene(int argc, const char **argv);
|
||||||
|
bool cmdStartingScene(int argc, const char **argv);
|
||||||
|
bool cmdSceneInfo(int argc, const char **argv);
|
||||||
|
bool cmdShowHotSpots(int argc, const char **argv);
|
||||||
|
bool cmdListHotSpots(int argc, const char **argv);
|
||||||
|
bool cmdPlaySound(int argc, const char **argv);
|
||||||
|
bool cmdPlayDSRSound(int argc, const char **argv);
|
||||||
|
bool cmdShowResources(int argc, const char **argv);
|
||||||
|
bool cmdShowCodes(int argc, const char **argv);
|
||||||
|
bool cmdDumpFile(int argc, const char **argv);
|
||||||
|
bool cmdShowSprite(int argc, const char **argv);
|
||||||
|
bool cmdStartConversation(int argc, const char **argv);
|
||||||
|
bool cmdShowTextview(int argc, const char **argv);
|
||||||
|
bool cmdShowAnimview(int argc, const char **argv);
|
||||||
|
bool cmdPlayAnimation(int argc, const char **argv);
|
||||||
|
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
1211
engines/m4/converse.cpp
Normal file
1211
engines/m4/converse.cpp
Normal file
File diff suppressed because it is too large
Load diff
200
engines/m4/converse.h
Normal file
200
engines/m4/converse.h
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_CONVERSE_H
|
||||||
|
#define M4_CONVERSE_H
|
||||||
|
|
||||||
|
#include "common/array.h"
|
||||||
|
#include "common/hashmap.h"
|
||||||
|
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
enum ConversationState {
|
||||||
|
kConversationOptionsShown = 0,
|
||||||
|
kEntryIsActive = 1,
|
||||||
|
kReplyIsActive = 2,
|
||||||
|
kNoConversation = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EntryType {
|
||||||
|
kVariable = 0,
|
||||||
|
kNode = 1,
|
||||||
|
kLinearNode = 2,
|
||||||
|
kEntry = 3,
|
||||||
|
kReply = 4,
|
||||||
|
kWeightedReply = 5
|
||||||
|
};
|
||||||
|
|
||||||
|
// Flags are:
|
||||||
|
// bit 0: if it's 1, the entry is "initial", i.e. not hidden when the dialog starts
|
||||||
|
// bit 1: if it's 1, the entry persists if selected
|
||||||
|
enum EntryFlags {
|
||||||
|
kEntryInitial = 1, // byte 0
|
||||||
|
kEntryPersists = 2 // byte 1
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EntryActionType {
|
||||||
|
kUnknownAction = 0,
|
||||||
|
kGotoEntry = 1,
|
||||||
|
kHideEntry = 2,
|
||||||
|
kUnhideEntry = 3,
|
||||||
|
kDestroyEntry = 4,
|
||||||
|
kAssignValue = 5,
|
||||||
|
kExitConv = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
enum LogicOp {
|
||||||
|
kOpPercent = 405,
|
||||||
|
kOpGreaterOrEqual = 421,
|
||||||
|
kOpLessOrEqual = 420,
|
||||||
|
kOpGreaterThan = 413,
|
||||||
|
kOpLessThan = 412,
|
||||||
|
kOpNotEqual = 422,
|
||||||
|
kOpCondNotEqual = 448,
|
||||||
|
kOpAssign = 407,
|
||||||
|
kOpAnd = 444,
|
||||||
|
kOpOr = 445
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Condition {
|
||||||
|
int32 offset;
|
||||||
|
int32 op;
|
||||||
|
int32 val;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EntryAction {
|
||||||
|
int32 targetOffset; // absolute offset (inside the *.chk file) of the action's target
|
||||||
|
int32 value; // the value set by assignment chunks
|
||||||
|
EntryActionType actionType;
|
||||||
|
EntryType targetType;
|
||||||
|
int32 nodeId;
|
||||||
|
int32 entryId;
|
||||||
|
bool isConditional;
|
||||||
|
Condition condition;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConvEntry {
|
||||||
|
EntryType entryType;
|
||||||
|
int32 id;
|
||||||
|
int32 offset; // absolute offset inside the *.chk file, referenced by other chunks
|
||||||
|
int32 size; // entry action data size (for MADS games)
|
||||||
|
int32 flags;
|
||||||
|
int32 fallthroughMinEntries;
|
||||||
|
int32 fallthroughOffset;
|
||||||
|
int32 weight; // weight for weighted replies
|
||||||
|
int32 totalWeight;
|
||||||
|
uint16 entryCount; // entries inside this node (for MADS games)
|
||||||
|
char voiceFile[10];
|
||||||
|
char text[512];
|
||||||
|
bool autoSelect;
|
||||||
|
bool visible;
|
||||||
|
bool isConditional;
|
||||||
|
Condition condition;
|
||||||
|
Common::Array<EntryAction*>actions;
|
||||||
|
Common::Array<ConvEntry*>entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EntryInfo {
|
||||||
|
EntryType targetType;
|
||||||
|
int32 nodeIndex;
|
||||||
|
int32 entryIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MessageEntry {
|
||||||
|
Common::Array<char*>messageStrings;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ConverseStyle {CONVSTYLE_EARTH, CONVSTYLE_SPACE};
|
||||||
|
|
||||||
|
typedef Common::HashMap<Common::String,EntryInfo,Common::IgnoreCase_Hash,Common::IgnoreCase_EqualTo> OffsetHashMap;
|
||||||
|
typedef Common::HashMap<Common::String,int32,Common::IgnoreCase_Hash,Common::IgnoreCase_EqualTo> ConvVarHashMap;
|
||||||
|
|
||||||
|
class ConversationView: public View {
|
||||||
|
public:
|
||||||
|
ConversationView(M4Engine *vm);
|
||||||
|
~ConversationView();
|
||||||
|
void setNode(int32 nodeIndex);
|
||||||
|
|
||||||
|
void onRefresh(RectList *rects, M4Surface *destSurface);
|
||||||
|
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
|
||||||
|
int32 getCurrentNodeIndex() { return _currentNodeIndex; }
|
||||||
|
void selectEntry(int entryIndex);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateState();
|
||||||
|
void playNextReply();
|
||||||
|
|
||||||
|
int32 _currentNodeIndex;
|
||||||
|
Common::Array<ConvEntry *> _activeItems;
|
||||||
|
int _highlightedIndex;
|
||||||
|
int _xEnd;
|
||||||
|
bool _entriesShown;
|
||||||
|
ConversationState _conversationState;
|
||||||
|
SndHandle *_currentHandle;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Converse {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Converse(M4Engine *vm) : _vm(vm) {}
|
||||||
|
~Converse() {}
|
||||||
|
|
||||||
|
void startConversation(const char *convName, bool showConversebox = true, ConverseStyle style = CONVSTYLE_EARTH );
|
||||||
|
void endConversation();
|
||||||
|
const EntryInfo* getEntryInfo(int32 offset);
|
||||||
|
ConvEntry *getNode(int32 index) { return _convNodes[index]; }
|
||||||
|
|
||||||
|
void setValue(int32 offset, int32 value);
|
||||||
|
const int32 getValue(int32 offset);
|
||||||
|
bool evaluateCondition(int32 leftVal, int32 op, int32 rightVal);
|
||||||
|
bool performAction(EntryAction *action);
|
||||||
|
/*
|
||||||
|
void resume() { play(); }
|
||||||
|
void play();
|
||||||
|
*/
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
Common::Array<ConvEntry*>_convNodes;
|
||||||
|
Common::Array<MessageEntry*>_madsMessageList;
|
||||||
|
Common::Array<char *>_convStrings;
|
||||||
|
bool _playerCommandsAllowed;
|
||||||
|
bool _interfaceWasVisible;
|
||||||
|
ConverseStyle _style;
|
||||||
|
OffsetHashMap _offsetMap;
|
||||||
|
ConvVarHashMap _variables;
|
||||||
|
|
||||||
|
void loadConversation(const char *convName);
|
||||||
|
void loadConversationMads(const char *convName);
|
||||||
|
void readConvEntryActions(Common::SubReadStream *convS, ConvEntry *curEntry);
|
||||||
|
void setEntryInfo(int32 offset, EntryType type, int32 nodeIndex, int32 entryIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
345
engines/m4/detection.cpp
Normal file
345
engines/m4/detection.cpp
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "base/plugins.h"
|
||||||
|
|
||||||
|
#include "common/advancedDetector.h"
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/resource.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
struct M4GameDescription {
|
||||||
|
Common::ADGameDescription desc;
|
||||||
|
|
||||||
|
int gameType;
|
||||||
|
uint32 features;
|
||||||
|
};
|
||||||
|
|
||||||
|
int M4Engine::getGameType() const { return _gameDescription->gameType; }
|
||||||
|
uint32 M4Engine::getFeatures() const { return _gameDescription->features; }
|
||||||
|
Common::Language M4Engine::getLanguage() const { return _gameDescription->desc.language; }
|
||||||
|
Common::Platform M4Engine::getPlatform() const { return _gameDescription->desc.platform; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static const PlainGameDescriptor m4Games[] = {
|
||||||
|
{"m4", "MADS/M4 engine game"},
|
||||||
|
{"riddle", "Riddle of Master Lu: Believe it or Not!"},
|
||||||
|
{"burger", "Orion Burger"},
|
||||||
|
{"rex", "Rex Nebular and the Cosmic Gender Bender"},
|
||||||
|
{"dragon", "DragonSphere"},
|
||||||
|
{"dragoncd", "DragonSphere CD"},
|
||||||
|
{"phantom", "Return of the Phantom"},
|
||||||
|
{"phantomcd", "Return of the Phantom CD"},
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
const char *M4Engine::getGameFile(int fileType) {
|
||||||
|
for (int i = 0; _gameDescription->desc.filesDescriptions[i].fileName; i++) {
|
||||||
|
if (_gameDescription->desc.filesDescriptions[i].fileType == fileType)
|
||||||
|
return _gameDescription->desc.filesDescriptions[i].fileName;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const M4GameDescription gameDescriptions[] = {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"burger",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "burger.has", kFileTypeHash, "10c8064e9c771072122f50737ac97245", 730771},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_Burger,
|
||||||
|
kFeaturesCD
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"burger",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "burger.has", kFileTypeHash, "55be8693a4c36e7efcdca0f0c77758ae", 724191},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::DE_DEU,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_Burger,
|
||||||
|
kFeaturesCD
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"riddle",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "ripley.has", kFileTypeHash, "056d517360c89eb4c297a319f21a7071", 700469},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_Riddle,
|
||||||
|
kFeaturesCD
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"riddle",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "ripley.has", kFileTypeHash, "d073582c9011d44dd0d7e2ede317a86d", 700469},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_Riddle,
|
||||||
|
kFeaturesCD
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"riddle",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "ripley.has", kFileTypeHash, "d9e9f8befec432a047b1047fb2bc7aea", 710997},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::DE_DEU,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_Riddle,
|
||||||
|
kFeaturesCD
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"riddle",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "ripley.has", kFileTypeHash, "3d48c5700785d11e6a5bc832b95be918", 701973},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::FR_FRA,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_Riddle,
|
||||||
|
kFeaturesCD
|
||||||
|
},
|
||||||
|
{ // Demo
|
||||||
|
{
|
||||||
|
"riddle",
|
||||||
|
"Demo",
|
||||||
|
{
|
||||||
|
{ "ripley.has", kFileTypeHash, "3a90dd7052860b6e246ec7e0aaf202f6", 104527},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_DEMO
|
||||||
|
},
|
||||||
|
GType_Riddle,
|
||||||
|
kFeaturesDemo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"rex",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "global.hag", kFileTypeHAG, "0530cbeee109fc79cc24421128dea1ce", 2083078},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_RexNebular,
|
||||||
|
kFeaturesNone
|
||||||
|
},
|
||||||
|
{ // Demo
|
||||||
|
{
|
||||||
|
"rex",
|
||||||
|
"Demo",
|
||||||
|
{
|
||||||
|
{ "global.hag", kFileTypeHAG, "d5a481d14bc1bda66e46965a39badcc7", 220429},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_DEMO
|
||||||
|
},
|
||||||
|
GType_RexNebular,
|
||||||
|
kFeaturesDemo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"dragon",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "global.hag", kFileTypeHAG, "c3a6877665e7f21bf3d2b1e667155562", 2320567},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_DragonSphere,
|
||||||
|
kFeaturesNone
|
||||||
|
},
|
||||||
|
{ // CD version
|
||||||
|
{
|
||||||
|
"dragoncd",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "global.hag", kFileTypeHAG, "c3a6877665e7f21bf3d2b1e667155562", 2320567},
|
||||||
|
{ "speech.hag", kFileTypeHAG, NULL, -1},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_DragonSphere,
|
||||||
|
kFeaturesCD
|
||||||
|
},
|
||||||
|
{ // Demo
|
||||||
|
{
|
||||||
|
"dragon",
|
||||||
|
"Demo",
|
||||||
|
{
|
||||||
|
{ "global.hag", kFileTypeHAG, "541e12d8e9aad0c65d65f150de47582e", 212718},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_DEMO
|
||||||
|
},
|
||||||
|
GType_DragonSphere,
|
||||||
|
kFeaturesDemo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"phantom",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "global.hag", kFileTypeHAG, "bdce9ca93a015f0883d1bc0fabd0cdfa", 812150},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_Phantom,
|
||||||
|
kFeaturesNone
|
||||||
|
},
|
||||||
|
{ // CD version
|
||||||
|
{
|
||||||
|
"phantomcd",
|
||||||
|
"",
|
||||||
|
{
|
||||||
|
{ "global.hag", kFileTypeHAG, "8a51c984eb4c64e8b30a7e6670f6bddb", 101154000},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_NO_FLAGS
|
||||||
|
},
|
||||||
|
GType_Phantom,
|
||||||
|
kFeaturesCD
|
||||||
|
},
|
||||||
|
{ // CD version
|
||||||
|
{
|
||||||
|
"phantom",
|
||||||
|
"Demo",
|
||||||
|
{
|
||||||
|
{ "global.hag", kFileTypeHAG, "e810adbc6fac77ac2fec841a9ec5e20e", 115266},
|
||||||
|
{ NULL, 0, NULL, 0}
|
||||||
|
},
|
||||||
|
Common::EN_ANY,
|
||||||
|
Common::kPlatformPC,
|
||||||
|
Common::ADGF_DEMO
|
||||||
|
},
|
||||||
|
GType_Phantom,
|
||||||
|
kFeaturesDemo
|
||||||
|
},
|
||||||
|
{ { NULL, NULL, { { NULL, 0, NULL, 0 } }, Common::UNK_LANG, Common::kPlatformUnknown, Common::ADGF_NO_FLAGS }, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Common::ADParams detectionParams = {
|
||||||
|
// Pointer to ADGameDescription or its superset structure
|
||||||
|
(const byte *)M4::gameDescriptions,
|
||||||
|
// Size of that superset structure
|
||||||
|
sizeof(M4::M4GameDescription),
|
||||||
|
// Number of bytes to compute MD5 sum for
|
||||||
|
5000,
|
||||||
|
// List of all engine targets
|
||||||
|
m4Games,
|
||||||
|
// Structure for autoupgrading obsolete targets
|
||||||
|
0,
|
||||||
|
// Name of single gameid (optional)
|
||||||
|
"m4",
|
||||||
|
// List of files for file-based fallback detection (optional)
|
||||||
|
0,
|
||||||
|
// Flags
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
class M4MetaEngine : public Common::AdvancedMetaEngine {
|
||||||
|
public:
|
||||||
|
M4MetaEngine() : Common::AdvancedMetaEngine(detectionParams) {}
|
||||||
|
|
||||||
|
virtual const char *getName() const {
|
||||||
|
return "MADS/M4 engine";
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const char *getCopyright() const {
|
||||||
|
return "Riddle of Master Lu & Orion Burger (C) Sanctuary Woods";
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool M4MetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const {
|
||||||
|
const M4::M4GameDescription *gd = (const M4::M4GameDescription *)desc;
|
||||||
|
if (gd) {
|
||||||
|
*engine = new M4::M4Engine(syst, gd);
|
||||||
|
}
|
||||||
|
return gd != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER_PLUGIN(M4, PLUGIN_TYPE_ENGINE, M4MetaEngine);
|
351
engines/m4/events.cpp
Normal file
351
engines/m4/events.cpp
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: There is a 'please_hyperwalk' variable that gets accessed that is meant to be global, but
|
||||||
|
// at the moment it's implemented as a local variable
|
||||||
|
|
||||||
|
#include "graphics/cursorman.h"
|
||||||
|
|
||||||
|
#include "m4/events.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/scene.h"
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
bool please_hyperwalk = false;
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*
|
||||||
|
* Events *
|
||||||
|
* *
|
||||||
|
* Implements an interface to the event system *
|
||||||
|
*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
Events::Events(M4Engine *vm) : _vm(vm) {
|
||||||
|
_mouseState = MSTATE_NO_EVENT;
|
||||||
|
quitFlag = false;
|
||||||
|
_keyCode = 0;
|
||||||
|
_console = new Console(_vm);
|
||||||
|
_mouseButtons = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
M4EventType Events::handleEvents() {
|
||||||
|
static int oldX = -1, oldY = -1;
|
||||||
|
static uint32 dclickTime = 0;
|
||||||
|
|
||||||
|
// Handle event types
|
||||||
|
while (g_system->getEventManager()->pollEvent(_event)) {
|
||||||
|
switch (_event.type) {
|
||||||
|
case Common::EVENT_QUIT:
|
||||||
|
quitFlag = true;
|
||||||
|
break;
|
||||||
|
case Common::EVENT_KEYDOWN:
|
||||||
|
if (_event.kbd.flags == Common::KBD_CTRL) {
|
||||||
|
if (_event.kbd.keycode == Common::KEYCODE_d)
|
||||||
|
_console->attach();
|
||||||
|
_console->onFrame();
|
||||||
|
}
|
||||||
|
_keyCode = (int)_event.kbd.keycode;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case Common::EVENT_LBUTTONDOWN:
|
||||||
|
case Common::EVENT_LBUTTONUP:
|
||||||
|
case Common::EVENT_RBUTTONDOWN:
|
||||||
|
case Common::EVENT_RBUTTONUP:
|
||||||
|
case Common::EVENT_MBUTTONDOWN:
|
||||||
|
case Common::EVENT_MBUTTONUP:
|
||||||
|
case Common::EVENT_MOUSEMOVE:
|
||||||
|
case Common::EVENT_WHEELUP:
|
||||||
|
case Common::EVENT_WHEELDOWN:
|
||||||
|
_vm->_mouse->handleEvent(_event);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_mouseButtons = g_system->getEventManager()->getButtonState();
|
||||||
|
|
||||||
|
// State machine for moving between states
|
||||||
|
switch (_mouseState) {
|
||||||
|
case MSTATE_NO_EVENT:
|
||||||
|
if (_mouseButtons & LEFT_BUTTON_DOWN) {
|
||||||
|
if ((dclickTime != 0) && (g_system->getMillis() < dclickTime)) {
|
||||||
|
_mouseState = MSTATE_DOUBLECLICK_DOWN;
|
||||||
|
dclickTime = 0;
|
||||||
|
return MEVENT_DOUBLECLICK;
|
||||||
|
}
|
||||||
|
dclickTime = 0;
|
||||||
|
_mouseState = MSTATE_LEFT_CLICK_DOWN;
|
||||||
|
return MEVENT_LEFT_CLICK;
|
||||||
|
}
|
||||||
|
if (_mouseButtons & RIGHT_BUTTON_DOWN) {
|
||||||
|
_mouseState = MSTATE_RIGHT_CLICK_DOWN;
|
||||||
|
return MEVENT_RIGHT_CLICK;
|
||||||
|
}
|
||||||
|
if ((_event.mouse.x != oldX) || (_event.mouse.y != oldY)) {
|
||||||
|
oldX = _event.mouse.x; oldY = _event.mouse.y;
|
||||||
|
return MEVENT_MOVE;
|
||||||
|
}
|
||||||
|
return MEVENT_NO_EVENT;
|
||||||
|
|
||||||
|
case MSTATE_LEFT_CLICK_DOWN:
|
||||||
|
if (!(_mouseButtons & LEFT_BUTTON_DOWN)) {
|
||||||
|
dclickTime = g_system->getMillis() + 1000 * 15 / 60;
|
||||||
|
_mouseState = MSTATE_NO_EVENT;
|
||||||
|
return MEVENT_LEFT_RELEASE;
|
||||||
|
}
|
||||||
|
if ((_event.mouse.x != oldX) || (_event.mouse.y != oldY)) {
|
||||||
|
oldX = _event.mouse.x; oldY = _event.mouse.y;
|
||||||
|
return MEVENT_LEFT_DRAG;
|
||||||
|
}
|
||||||
|
return MEVENT_LEFT_HOLD;
|
||||||
|
|
||||||
|
case MSTATE_RIGHT_CLICK_DOWN:
|
||||||
|
if (!(_mouseButtons & RIGHT_BUTTON_DOWN)) {
|
||||||
|
_mouseState = MSTATE_NO_EVENT;
|
||||||
|
please_hyperwalk = true;
|
||||||
|
return MEVENT_RIGHT_RELEASE;
|
||||||
|
}
|
||||||
|
if ((_event.mouse.x != oldX) || (_event.mouse.y != oldY)) {
|
||||||
|
oldX = _event.mouse.x; oldY = _event.mouse.y;
|
||||||
|
return MEVENT_RIGHT_DRAG;
|
||||||
|
}
|
||||||
|
return MEVENT_RIGHT_HOLD;
|
||||||
|
|
||||||
|
case MSTATE_DOUBLECLICK_DOWN:
|
||||||
|
if (!(_mouseButtons & LEFT_BUTTON_DOWN)) {
|
||||||
|
_mouseState = MSTATE_NO_EVENT;
|
||||||
|
return MEVENT_DOUBLECLICK_RELEASE;
|
||||||
|
}
|
||||||
|
if ((_event.mouse.x != oldX) || (_event.mouse.y != oldY)) {
|
||||||
|
oldX = _event.mouse.x; oldY = _event.mouse.y;
|
||||||
|
return MEVENT_DOUBLECLICK_DRAG;
|
||||||
|
}
|
||||||
|
return MEVENT_DOUBLECLICK_HOLD;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return MEVENT_NO_EVENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Events::kbdCheck(uint32 &keyCode) {
|
||||||
|
if (_keyCode == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
keyCode = _keyCode;
|
||||||
|
_keyCode = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*
|
||||||
|
* Mouse *
|
||||||
|
* *
|
||||||
|
* Implements an interface to the mouse *
|
||||||
|
*--------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
Mouse::Mouse(M4Engine *vm) : _vm(vm) {
|
||||||
|
_locked = false;
|
||||||
|
_cursorOn = false;
|
||||||
|
_cursor = NULL;
|
||||||
|
_cursorSprites = NULL;
|
||||||
|
resetMouse();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mouse::~Mouse() {
|
||||||
|
if (_cursorSprites)
|
||||||
|
delete _cursorSprites;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse::init(const char *seriesName, RGB8 *palette) {
|
||||||
|
Common::SeekableReadStream *stream = _vm->res()->get(seriesName);
|
||||||
|
int colorCount = 0;
|
||||||
|
RGB8* cursorPalette;
|
||||||
|
|
||||||
|
_cursorSprites = new SpriteAsset(_vm, stream, stream->size(), seriesName);
|
||||||
|
|
||||||
|
// Remove cursor special pixels and set the mouse cursor hotspot in MADS games
|
||||||
|
if (!_vm->isM4()) {
|
||||||
|
byte *data = NULL;
|
||||||
|
for (int i = 0; i < _cursorSprites->getCount(); i++) {
|
||||||
|
bool hotSpotSet = false;
|
||||||
|
|
||||||
|
for (int x = 0; x < _cursorSprites->getFrame(i)->width(); x++) {
|
||||||
|
for (int y = 0; y < _cursorSprites->getFrame(i)->height(); y++) {
|
||||||
|
data = _cursorSprites->getFrame(i)->getBasePtr(x, y);
|
||||||
|
if (*data == 1) {
|
||||||
|
// It seems that some cursors have more than one hotspot
|
||||||
|
// In such a case, the first hotspot seems to set the x and
|
||||||
|
// the second one the y hotspot offset
|
||||||
|
if (!hotSpotSet) {
|
||||||
|
_cursorSprites->getFrame(i)->xOffset = x;
|
||||||
|
_cursorSprites->getFrame(i)->yOffset = y;
|
||||||
|
hotSpotSet = true;
|
||||||
|
} else {
|
||||||
|
_cursorSprites->getFrame(i)->yOffset = y;
|
||||||
|
}
|
||||||
|
*data = 0;
|
||||||
|
}
|
||||||
|
} // for y
|
||||||
|
} // for x
|
||||||
|
} // for i
|
||||||
|
}
|
||||||
|
|
||||||
|
colorCount = _cursorSprites->getColorCount();
|
||||||
|
cursorPalette = _cursorSprites->getPalette();
|
||||||
|
_vm->_palette->setPalette(cursorPalette, 0, colorCount);
|
||||||
|
|
||||||
|
//printf("Cursor count: %d\n", _cursorSprites->getCount());
|
||||||
|
|
||||||
|
_vm->res()->toss(seriesName);
|
||||||
|
|
||||||
|
_currentCursor = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse::setCursorNum(int cursorIndex) {
|
||||||
|
if ((cursorIndex < 0) || (cursorIndex >= (int)_cursorSprites->getCount()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_lockedCursor = cursorIndex;
|
||||||
|
if (_locked)
|
||||||
|
// Cursor is locked, so don't go ahead with changing cursor
|
||||||
|
return true;
|
||||||
|
|
||||||
|
_currentCursor = _lockedCursor;
|
||||||
|
_cursor = _cursorSprites->getFrame(cursorIndex);
|
||||||
|
|
||||||
|
// Set the cursor to the sprite
|
||||||
|
CursorMan.replaceCursor((const byte *)_cursor->getData(), _cursor->w, _cursor->h, _cursor->xOffset, _cursor->yOffset, 0);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Mouse::cursorCount() {
|
||||||
|
return _cursorSprites->getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse::cursorOn() {
|
||||||
|
_cursorOn = true;
|
||||||
|
CursorMan.showMouse(!inHideArea());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse::cursorOff() {
|
||||||
|
_cursorOn = false;
|
||||||
|
CursorMan.showMouse(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse::lockCursor(int cursorIndex) {
|
||||||
|
_locked = false;
|
||||||
|
setCursorNum(cursorIndex);
|
||||||
|
_locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse::unlockCursor() {
|
||||||
|
_locked = false;
|
||||||
|
setCursorNum(_lockedCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *Mouse::getVerb() {
|
||||||
|
switch (_vm->_mouse->getCursorNum()) {
|
||||||
|
case CURSOR_LOOK:
|
||||||
|
return "LOOK AT";
|
||||||
|
case CURSOR_TAKE:
|
||||||
|
return "TAKE";
|
||||||
|
case CURSOR_USE:
|
||||||
|
return "GEAR";
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse::resetMouse() {
|
||||||
|
_hideRect.left = -1;
|
||||||
|
_hideRect.top = -1;
|
||||||
|
_hideRect.right = -1;
|
||||||
|
_hideRect.bottom = -1;
|
||||||
|
_showRect.left = -1;
|
||||||
|
_showRect.top = -1;
|
||||||
|
_showRect.right = -1;
|
||||||
|
_showRect.bottom = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse::setHideRect(Common::Rect &r) {
|
||||||
|
_hideRect = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse::setShowRect(Common::Rect &r) {
|
||||||
|
_showRect = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Common::Rect *Mouse::getHideRect() {
|
||||||
|
return &_hideRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Common::Rect *Mouse::getShowRect() {
|
||||||
|
if ((_showRect.top == -1) || (_showRect.left == -1)) {
|
||||||
|
// Show rectangle uninitialised - set it to current screen dimensions
|
||||||
|
_showRect.top = 0;
|
||||||
|
_showRect.left = 0;
|
||||||
|
_showRect.right = _vm->_screen->width() - 1;
|
||||||
|
_showRect.bottom = _vm->_screen->height() -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &_showRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse::handleEvent(Common::Event &event) {
|
||||||
|
_currentPos.x = event.mouse.x;
|
||||||
|
_currentPos.y = event.mouse.y;
|
||||||
|
|
||||||
|
// If mouse is turned on, check to see if the position is in the hide rect, or outside the show rect.
|
||||||
|
// If so, handle toggling the visibility of the mouse
|
||||||
|
bool showFlag = !inHideArea();
|
||||||
|
if (_cursorOn && (CursorMan.isVisible() != showFlag)) {
|
||||||
|
CursorMan.showMouse(showFlag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse::inHideArea() {
|
||||||
|
// Returns true if the mouse is inside a specified hide rect, or if a show rect is specified and
|
||||||
|
// the mouse is currently outside it
|
||||||
|
if ((_currentPos.x >= _hideRect.left) && (_currentPos.x <= _hideRect.right) &&
|
||||||
|
(_currentPos.y >= _hideRect.top) && (_currentPos.y <= _hideRect.bottom))
|
||||||
|
// Inside a hide area
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
if ((_showRect.top == -1) && (_showRect.left == -1))
|
||||||
|
// No show rect defined
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Return true if the mouse is outside the show area
|
||||||
|
return (_currentPos.x < _showRect.left) || (_currentPos.x > _showRect.right) ||
|
||||||
|
(_currentPos.y < _showRect.top) || (_currentPos.y > _showRect.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
132
engines/m4/events.h
Normal file
132
engines/m4/events.h
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_EVENTS_H
|
||||||
|
#define M4_EVENTS_H
|
||||||
|
|
||||||
|
#include "common/events.h"
|
||||||
|
#include "common/rect.h"
|
||||||
|
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/assets.h"
|
||||||
|
#include "m4/sprite.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/console.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define LEFT_BUTTON_DOWN 1 << 0
|
||||||
|
#define RIGHT_BUTTON_DOWN 1 << 1
|
||||||
|
|
||||||
|
enum M4EventType {
|
||||||
|
MEVENT_NO_EVENT, MEVENT_MOVE,
|
||||||
|
MEVENT_LEFT_CLICK, MEVENT_LEFT_HOLD, MEVENT_LEFT_DRAG, MEVENT_LEFT_RELEASE,
|
||||||
|
MEVENT_RIGHT_CLICK, MEVENT_RIGHT_HOLD, MEVENT_RIGHT_DRAG, MEVENT_RIGHT_RELEASE,
|
||||||
|
MEVENT_BOTH_CLICK, MEVENT_BOTH_HOLD, MEVENT_BOTH_DRAG, MEVENT_BOTH_RELEASE,
|
||||||
|
MEVENT_DOUBLECLICK, MEVENT_DOUBLECLICK_HOLD, MEVENT_DOUBLECLICK_DRAG, MEVENT_DOUBLECLICK_RELEASE,
|
||||||
|
KEVENT_KEY
|
||||||
|
};
|
||||||
|
|
||||||
|
enum M4MouseState {
|
||||||
|
MSTATE_NO_EVENT, MSTATE_LEFT_CLICK_DOWN, MSTATE_RIGHT_CLICK_DOWN, MSTATE_BOTH_CLICK_DOWN,
|
||||||
|
MSTATE_DOUBLECLICK_DOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
enum M4CommonCursors {
|
||||||
|
CURSOR_ARROW = 0,
|
||||||
|
CURSOR_HOURGLASS = 5,
|
||||||
|
CURSOR_LOOK = 6,
|
||||||
|
CURSOR_TAKE = 8,
|
||||||
|
CURSOR_USE = 9
|
||||||
|
};
|
||||||
|
|
||||||
|
class M4Sprite;
|
||||||
|
class SpriteAsset;
|
||||||
|
|
||||||
|
class Events {
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
Common::Event _event;
|
||||||
|
M4MouseState _mouseState;
|
||||||
|
int _keyCode;
|
||||||
|
int _mouseButtons;
|
||||||
|
Console *_console;
|
||||||
|
public:
|
||||||
|
bool quitFlag;
|
||||||
|
Events(M4Engine *vm);
|
||||||
|
|
||||||
|
Common::Event &event() { return _event; }
|
||||||
|
Common::EventType type() { return _event.type; }
|
||||||
|
|
||||||
|
// M4-centric methods
|
||||||
|
M4EventType handleEvents();
|
||||||
|
bool kbdCheck(uint32 &keyCode);
|
||||||
|
int getMouseButtonsState() { return _mouseButtons; }
|
||||||
|
Console* getConsole() { return _console; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Mouse {
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
int _currentCursor, _lockedCursor;
|
||||||
|
bool _locked;
|
||||||
|
bool _cursorOn;
|
||||||
|
M4Sprite *_cursor;
|
||||||
|
SpriteAsset *_cursorSprites;
|
||||||
|
Common::Rect _hideRect, _showRect;
|
||||||
|
Common::Point _currentPos;
|
||||||
|
|
||||||
|
void handleEvent(Common::Event &event);
|
||||||
|
bool inHideArea();
|
||||||
|
friend class Events;
|
||||||
|
public:
|
||||||
|
Mouse(M4Engine *vm);
|
||||||
|
~Mouse();
|
||||||
|
|
||||||
|
bool init(const char *seriesName, RGB8 *palette);
|
||||||
|
bool setCursorNum(int cursorIndex);
|
||||||
|
int getCursorNum() { return _currentCursor; }
|
||||||
|
int cursorCount();
|
||||||
|
Common::Point currentPos() const { return _currentPos; }
|
||||||
|
M4Sprite *cursor() { return _cursor; }
|
||||||
|
void cursorOn();
|
||||||
|
void cursorOff();
|
||||||
|
bool getCursorOn() { return _cursorOn; }
|
||||||
|
void lockCursor(int cursorIndex);
|
||||||
|
void unlockCursor();
|
||||||
|
|
||||||
|
const char *getVerb();
|
||||||
|
|
||||||
|
void resetMouse();
|
||||||
|
void setHideRect(Common::Rect &r);
|
||||||
|
void setShowRect(Common::Rect &r);
|
||||||
|
const Common::Rect *getHideRect();
|
||||||
|
const Common::Rect *getShowRect();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
267
engines/m4/font.cpp
Normal file
267
engines/m4/font.cpp
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/font.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/compression.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
Font::Font(M4Engine *vm) : _vm(vm) {
|
||||||
|
_sysFont = true;
|
||||||
|
_filename = NULL;
|
||||||
|
//TODO: System font
|
||||||
|
_fontColors[0] = _vm->_palette->BLACK;
|
||||||
|
_fontColors[1] = _vm->_palette->WHITE;
|
||||||
|
_fontColors[2] = _vm->_palette->BLACK;
|
||||||
|
_fontColors[3] = _vm->_palette->DARK_GRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::setFont(const char *filename) {
|
||||||
|
if ((_filename != NULL) && (strcmp(filename, _filename) == 0))
|
||||||
|
// Already using specified font, so don't bother reloading
|
||||||
|
return;
|
||||||
|
|
||||||
|
_sysFont = false;
|
||||||
|
_filename = filename;
|
||||||
|
|
||||||
|
if (_vm->isM4())
|
||||||
|
setFontM4(filename);
|
||||||
|
else
|
||||||
|
setFontMads(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::setFontM4(const char *filename) {
|
||||||
|
Common::SeekableReadStream *fontFile = _vm->res()->openFile(filename);
|
||||||
|
|
||||||
|
if (fontFile->readUint32LE() != MKID_BE('FONT')) {
|
||||||
|
printf("Font::Font: FONT tag expected\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_maxHeight = fontFile->readByte();
|
||||||
|
_maxWidth = fontFile->readByte();
|
||||||
|
uint32 fontSize = fontFile->readUint32LE();
|
||||||
|
|
||||||
|
//printf("Font::Font: _maxWidth = %d, _maxHeight = %d, fontSize = %d\n", _maxWidth, _maxHeight, fontSize);
|
||||||
|
|
||||||
|
if (fontFile->readUint32LE() != MKID_BE('WIDT')) {
|
||||||
|
printf("Font::Font: WIDT tag expected\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_charWidths = new uint8[256];
|
||||||
|
fontFile->read(_charWidths, 256);
|
||||||
|
|
||||||
|
if (fontFile->readUint32LE() != MKID_BE('OFFS')) {
|
||||||
|
printf("Font::Font: OFFS tag expected\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_charOffs = new uint16[256];
|
||||||
|
|
||||||
|
for (int i = 0; i < 256; i++)
|
||||||
|
_charOffs[i] = fontFile->readUint16LE();
|
||||||
|
|
||||||
|
if (fontFile->readUint32LE() != MKID_BE('PIXS')) {
|
||||||
|
printf("Font::Font: PIXS tag expected\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_charData = new uint8[fontSize];
|
||||||
|
fontFile->read(_charData, fontSize);
|
||||||
|
|
||||||
|
_vm->res()->toss(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::setFontMads(const char *filename) {
|
||||||
|
MadsPack fontData(filename, _vm);
|
||||||
|
Common::SeekableReadStream *fontFile = fontData.getItemStream(0);
|
||||||
|
|
||||||
|
_maxHeight = fontFile->readByte();
|
||||||
|
_maxWidth = fontFile->readByte();
|
||||||
|
|
||||||
|
_charWidths = new uint8[128];
|
||||||
|
// Char data is shifted by 1
|
||||||
|
_charWidths[0] = 0;
|
||||||
|
fontFile->read(_charWidths + 1, 127);
|
||||||
|
fontFile->readByte(); // remainder
|
||||||
|
|
||||||
|
_charOffs = new uint16[128];
|
||||||
|
|
||||||
|
uint32 startOffs = 2 + 128 + 256;
|
||||||
|
uint32 fontSize = fontFile->size() - startOffs;
|
||||||
|
|
||||||
|
// Char data is shifted by 1
|
||||||
|
_charOffs[0] = 0;
|
||||||
|
for (int i = 1; i < 128; i++)
|
||||||
|
_charOffs[i] = fontFile->readUint16LE() - startOffs;
|
||||||
|
fontFile->readUint16LE(); // remainder
|
||||||
|
|
||||||
|
_charData = new uint8[fontSize];
|
||||||
|
fontFile->read(_charData, fontSize);
|
||||||
|
|
||||||
|
delete fontFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
Font::~Font() {
|
||||||
|
if (!_sysFont) {
|
||||||
|
delete[] _charWidths;
|
||||||
|
delete[] _charOffs;
|
||||||
|
delete[] _charData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::setColor(uint8 color) {
|
||||||
|
if (_sysFont)
|
||||||
|
_fontColors[1] = color;
|
||||||
|
else
|
||||||
|
_fontColors[3] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Font::setColors(uint8 alt1, uint8 alt2, uint8 foreground) {
|
||||||
|
if (_sysFont)
|
||||||
|
_fontColors[1] = foreground;
|
||||||
|
else {
|
||||||
|
_fontColors[1] = alt1;
|
||||||
|
_fontColors[2] = alt2;
|
||||||
|
_fontColors[3] = foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 Font::write(M4Surface *surface, const char *text, int x, int y, int width, int spaceWidth, uint8 colors[]) {
|
||||||
|
|
||||||
|
/*TODO
|
||||||
|
if (custom_ascii_converter) { // if there is a function to convert the extended ASCII characters
|
||||||
|
custom_ascii_converter(out_string); // call it with the string
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (width > 0)
|
||||||
|
width = MIN(surface->width(), x + width);
|
||||||
|
else
|
||||||
|
width = surface->width();
|
||||||
|
|
||||||
|
x++;
|
||||||
|
y++;
|
||||||
|
|
||||||
|
int skipY = 0;
|
||||||
|
if (y < 0) {
|
||||||
|
skipY = -y;
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int height = MAX(0, _maxHeight - skipY);
|
||||||
|
if (height == 0)
|
||||||
|
return x;
|
||||||
|
|
||||||
|
int bottom = y + height - 1;
|
||||||
|
if (bottom > surface->height() - 1) {
|
||||||
|
height -= MIN(height, bottom - (surface->height() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (height <= 0)
|
||||||
|
return x;
|
||||||
|
|
||||||
|
uint8 *destPtr = (uint8*)surface->getBasePtr(x, y);
|
||||||
|
uint8 *oldDestPtr = destPtr;
|
||||||
|
|
||||||
|
int xPos = x;
|
||||||
|
|
||||||
|
while (*text) {
|
||||||
|
|
||||||
|
char theChar = (*text++) & 0x7F;
|
||||||
|
int charWidth = _charWidths[theChar];
|
||||||
|
|
||||||
|
if (charWidth > 0) {
|
||||||
|
|
||||||
|
if (xPos + charWidth >= width)
|
||||||
|
return xPos;
|
||||||
|
|
||||||
|
uint8 *charData = &_charData[_charOffs[theChar]];
|
||||||
|
int bpp = charWidth / 4 + 1;
|
||||||
|
|
||||||
|
if (!_vm->isM4()) {
|
||||||
|
if (charWidth > 12)
|
||||||
|
bpp = 4;
|
||||||
|
else if (charWidth > 8)
|
||||||
|
bpp = 3;
|
||||||
|
else if (charWidth > 4)
|
||||||
|
bpp = 2;
|
||||||
|
else
|
||||||
|
bpp = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipY != 0)
|
||||||
|
charData += bpp * skipY;
|
||||||
|
|
||||||
|
for (int i = 0; i < height; i++) {
|
||||||
|
for (int j = 0; j < bpp; j++) {
|
||||||
|
if (*charData & 0xc0)
|
||||||
|
*destPtr = colors[(*charData & 0xc0) >> 6];
|
||||||
|
destPtr++;
|
||||||
|
if (*charData & 0x30)
|
||||||
|
*destPtr = colors[(*charData & 0x30) >> 4];
|
||||||
|
destPtr++;
|
||||||
|
if (*charData & 0x0C)
|
||||||
|
*destPtr = colors[(*charData & 0x0C) >> 2];
|
||||||
|
destPtr++;
|
||||||
|
if (*charData & 0x03)
|
||||||
|
*destPtr = colors[*charData & 0x03];
|
||||||
|
destPtr++;
|
||||||
|
charData++;
|
||||||
|
}
|
||||||
|
|
||||||
|
destPtr += surface->width() - bpp * 4;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
destPtr = oldDestPtr + charWidth + spaceWidth;
|
||||||
|
oldDestPtr = destPtr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
xPos += charWidth + spaceWidth;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
surface->freeData();
|
||||||
|
return xPos;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 Font::getWidth(char *text, int spaceWidth) {
|
||||||
|
/*
|
||||||
|
if (custom_ascii_converter) { // if there is a function to convert the extended ASCII characters
|
||||||
|
custom_ascii_converter(out_string); // call it with the string
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
int width = 0;
|
||||||
|
while (*text)
|
||||||
|
width += _charWidths[*text++ & 0x7F] + spaceWidth;
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
91
engines/m4/font.h
Normal file
91
engines/m4/font.h
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_FONT_H
|
||||||
|
#define M4_FONT_H
|
||||||
|
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "common/endian.h"
|
||||||
|
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO:
|
||||||
|
- make a FontSystem class that creates/manages the fonts
|
||||||
|
(similar to FileSystem)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define FONT_MENU "fontmenu.fnt"
|
||||||
|
#define FONT_INTERFACE "fontintr.fnt"
|
||||||
|
#define FONT_TINY "small.fnt"
|
||||||
|
#define FONT_SMALL "small.fnt"
|
||||||
|
#define FONT_MEDIUM "medium.fnt"
|
||||||
|
#define FONT_LINE "fontline.fnt"
|
||||||
|
#define FONT_CONVERSATION "fontconv.fnt"
|
||||||
|
#define FONT_4X6 "4x6pp.fnt"
|
||||||
|
#define FONT_5X6 "5x6pp.fnt"
|
||||||
|
|
||||||
|
#define FONT_CONVERSATION_MADS "fontconv.ff"
|
||||||
|
#define FONT_INTERFACE_MADS "fontintr.ff"
|
||||||
|
#define FONT_MAIN_MADS "fontmain.ff"
|
||||||
|
#define FONT_MENU_MADS "fontmenu.ff" // Not in Rex (uses bitmap files for menu strings)
|
||||||
|
#define FONT_MISC_MADS "fontmisc.ff"
|
||||||
|
#define FONT_TELE_MADS "fonttele.ff" // Not in Phantom
|
||||||
|
#define FONT_PHAN_MADS "fontphan.ff" // Phantom only
|
||||||
|
|
||||||
|
class Font {
|
||||||
|
public:
|
||||||
|
Font(M4Engine *vm);
|
||||||
|
~Font();
|
||||||
|
void setFont(const char *filename);
|
||||||
|
void setColor(uint8 color);
|
||||||
|
void setColors(uint8 alt1, uint8 alt2, uint8 foreground);
|
||||||
|
|
||||||
|
int32 getWidth(char *text, int spaceWidth = -1);
|
||||||
|
int32 getHeight() const { return _maxHeight; }
|
||||||
|
int32 write(M4Surface *surface, const char *text, int x, int y, int width, int spaceWidth, uint8 colors[]);
|
||||||
|
int32 writeString(M4Surface *surface, const char *text, int x, int y, int width = 0, int spaceWidth = -1) {
|
||||||
|
return write(surface, text, x, y, width, spaceWidth, _fontColors);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setFontM4(const char *filename);
|
||||||
|
void setFontMads(const char *filename);
|
||||||
|
|
||||||
|
M4Engine *_vm;
|
||||||
|
uint8 _maxWidth, _maxHeight;
|
||||||
|
uint8 *_charWidths;
|
||||||
|
uint16 *_charOffs;
|
||||||
|
uint8 *_charData;
|
||||||
|
bool _sysFont;
|
||||||
|
const char *_filename;
|
||||||
|
uint8 _fontColors[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
447
engines/m4/globals.cpp
Normal file
447
engines/m4/globals.cpp
Normal file
|
@ -0,0 +1,447 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/gui.h"
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
#include "m4/script.h"
|
||||||
|
#include "m4/m4_views.h"
|
||||||
|
#include "m4/compression.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
Kernel::Kernel(M4Engine *vm) : _vm(vm) {
|
||||||
|
daemonTriggerAvailable = true;
|
||||||
|
firstFadeColorIndex = 0;
|
||||||
|
paused = false;
|
||||||
|
betweenRooms = false;
|
||||||
|
currentSection = 0;
|
||||||
|
newSection = 0;
|
||||||
|
previousSection = 0;
|
||||||
|
currentRoom = 0;
|
||||||
|
newRoom = 0;
|
||||||
|
previousRoom = 0;
|
||||||
|
trigger = 0;
|
||||||
|
triggerMode = KT_DAEMON;
|
||||||
|
|
||||||
|
_globalDaemonFn = NULL;
|
||||||
|
_globalParserFn = NULL;
|
||||||
|
|
||||||
|
_sectionInitFn = NULL;
|
||||||
|
_sectionDaemonFn = NULL;
|
||||||
|
_sectionParserFn = NULL;
|
||||||
|
|
||||||
|
_roomInitFn = NULL;
|
||||||
|
_roomDaemonFn = NULL;
|
||||||
|
_roomPreParserFn = NULL;
|
||||||
|
_roomParserFn = NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 Kernel::createTrigger(int32 triggerNum) {
|
||||||
|
if (triggerNum < 0)
|
||||||
|
return triggerNum;
|
||||||
|
else
|
||||||
|
return triggerNum | (currentRoom << 16) | (triggerMode << 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Kernel::sendTrigger(int32 triggerNum) {
|
||||||
|
return handleTrigger(createTrigger(triggerNum));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Kernel::handleTrigger(int32 triggerNum) {
|
||||||
|
|
||||||
|
printf("betweenRooms = %d; triggerNum = %08X\n", betweenRooms, triggerNum);
|
||||||
|
|
||||||
|
if (betweenRooms)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (triggerNum < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
KernelTriggerType saveTriggerMode = triggerMode;
|
||||||
|
int32 saveTrigger = trigger;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
int room = (triggerNum >> 16) & 0xFFF;
|
||||||
|
|
||||||
|
printf("room = %d; currentRoom = %d\n", room, currentRoom); fflush(stdout);
|
||||||
|
|
||||||
|
if (room != currentRoom) {
|
||||||
|
printf("Kernel::handleTrigger() Trigger from another room\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
trigger = triggerNum & 0xFFFF;
|
||||||
|
KernelTriggerType mode = (KernelTriggerType)(triggerNum >> 28);
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
|
||||||
|
case KT_PREPARSE:
|
||||||
|
if (trigger < 32000) {
|
||||||
|
triggerMode = KT_PREPARSE;
|
||||||
|
roomPreParser();
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KT_PARSE:
|
||||||
|
if (trigger < 32000) {
|
||||||
|
triggerMode = KT_PARSE;
|
||||||
|
// TODO player.commandReady = TRUE;
|
||||||
|
roomParser();
|
||||||
|
/* TODO
|
||||||
|
if (player.commandReady)
|
||||||
|
globalParser();
|
||||||
|
*/
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KT_DAEMON:
|
||||||
|
printf("KT_DAEMON\n");
|
||||||
|
fflush(stdout);
|
||||||
|
triggerMode = KT_DAEMON;
|
||||||
|
daemonTriggerAvailable = false;
|
||||||
|
roomDaemon();
|
||||||
|
if (daemonTriggerAvailable) {
|
||||||
|
daemonTriggerAvailable = false;
|
||||||
|
sectionDaemon();
|
||||||
|
}
|
||||||
|
if (daemonTriggerAvailable) {
|
||||||
|
daemonTriggerAvailable = false;
|
||||||
|
globalDaemon();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
printf("Kernel::handleTrigger() Unknown trigger mode %d\n", mode);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerMode = saveTriggerMode;
|
||||||
|
trigger = saveTrigger;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::loadGlobalScriptFunctions() {
|
||||||
|
_globalDaemonFn = _vm->_script->loadFunction("global_daemon");
|
||||||
|
_globalParserFn = _vm->_script->loadFunction("global_parser");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::loadSectionScriptFunctions() {
|
||||||
|
char tempFnName[128];
|
||||||
|
snprintf(tempFnName, 128, "section_init_%d", currentSection);
|
||||||
|
_sectionInitFn = _vm->_script->loadFunction(tempFnName);
|
||||||
|
snprintf(tempFnName, 128, "section_daemon_%d", currentSection);
|
||||||
|
_sectionDaemonFn = _vm->_script->loadFunction(tempFnName);
|
||||||
|
snprintf(tempFnName, 128, "section_parser_%d", currentSection);
|
||||||
|
_sectionParserFn = _vm->_script->loadFunction(tempFnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::loadRoomScriptFunctions() {
|
||||||
|
char tempFnName[128];
|
||||||
|
snprintf(tempFnName, 128, "room_init_%d", currentRoom);
|
||||||
|
_roomInitFn = _vm->_script->loadFunction(tempFnName);
|
||||||
|
snprintf(tempFnName, 128, "room_daemon_%d", currentRoom);
|
||||||
|
_roomDaemonFn = _vm->_script->loadFunction(tempFnName);
|
||||||
|
snprintf(tempFnName, 128, "room_pre_parser_%d", currentRoom);
|
||||||
|
_roomPreParserFn = _vm->_script->loadFunction(tempFnName);
|
||||||
|
snprintf(tempFnName, 128, "room_parser_%d", currentRoom);
|
||||||
|
_roomParserFn = _vm->_script->loadFunction(tempFnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::globalDaemon() {
|
||||||
|
if (_globalDaemonFn)
|
||||||
|
_vm->_script->runFunction(_globalDaemonFn);
|
||||||
|
else {
|
||||||
|
printf("Kernel::globalDaemon() _globalDaemonFn is NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::globalParser() {
|
||||||
|
if (_globalParserFn)
|
||||||
|
_vm->_script->runFunction(_globalParserFn);
|
||||||
|
else {
|
||||||
|
printf("Kernel::globalParser() _globalParserFn is NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::sectionInit() {
|
||||||
|
if (_sectionInitFn)
|
||||||
|
_vm->_script->runFunction(_sectionInitFn);
|
||||||
|
else {
|
||||||
|
printf("Kernel::sectionInit() _sectionInitFn is NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::sectionDaemon() {
|
||||||
|
if (_sectionDaemonFn)
|
||||||
|
_vm->_script->runFunction(_sectionDaemonFn);
|
||||||
|
else {
|
||||||
|
printf("Kernel::sectionDaemon() _sectionDaemonFn is NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::sectionParser() {
|
||||||
|
if (_sectionParserFn)
|
||||||
|
_vm->_script->runFunction(_sectionParserFn);
|
||||||
|
else {
|
||||||
|
printf("Kernel::sectionParser() _sectionParserFn is NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::roomInit() {
|
||||||
|
if (_roomInitFn)
|
||||||
|
_vm->_script->runFunction(_roomInitFn);
|
||||||
|
else {
|
||||||
|
printf("Kernel::roomInit() _roomInitFn is NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::roomDaemon() {
|
||||||
|
if (_roomDaemonFn)
|
||||||
|
_vm->_script->runFunction(_roomDaemonFn);
|
||||||
|
else {
|
||||||
|
printf("Kernel::roomDaemon() _roomDaemonFn is NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::roomPreParser() {
|
||||||
|
if (_roomPreParserFn)
|
||||||
|
_vm->_script->runFunction(_roomPreParserFn);
|
||||||
|
else {
|
||||||
|
printf("Kernel::roomPreParser() _roomPreParserFn is NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::roomParser() {
|
||||||
|
if (_roomParserFn)
|
||||||
|
_vm->_script->runFunction(_roomParserFn);
|
||||||
|
else {
|
||||||
|
printf("Kernel::roomParser() _roomParserFn is NULL\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::pauseGame(bool value) {
|
||||||
|
paused = value;
|
||||||
|
|
||||||
|
if (paused) pauseEngines();
|
||||||
|
else unpauseEngines();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::pauseEngines() {
|
||||||
|
// TODO: A proper implementation of game pausing. At the moment I'm using a hard-coded
|
||||||
|
// check in events.cpp on Kernel::paused to prevent any events going to the scene
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kernel::unpauseEngines() {
|
||||||
|
// TODO: A proper implementation of game unpausing
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Globals::Globals(M4Engine *vm): _vm(vm) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Globals::~Globals() {
|
||||||
|
for(uint32 i = 0; i < _madsVocab.size(); i++)
|
||||||
|
free(_madsVocab[i]);
|
||||||
|
_madsVocab.clear();
|
||||||
|
|
||||||
|
for(uint32 i = 0; i < _madsQuotes.size(); i++)
|
||||||
|
free(_madsQuotes[i]);
|
||||||
|
_madsQuotes.clear();
|
||||||
|
|
||||||
|
_madsMessages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Globals::isInterfaceVisible() {
|
||||||
|
return _vm->_interfaceView->isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Globals::loadMadsVocab() {
|
||||||
|
Common::SeekableReadStream *vocabS = _vm->res()->get("vocab.dat");
|
||||||
|
int curPos = 0;
|
||||||
|
|
||||||
|
char buffer[30];
|
||||||
|
strcpy(buffer, "");
|
||||||
|
|
||||||
|
while(!vocabS->eos()) {
|
||||||
|
buffer[curPos++] = vocabS->readByte();
|
||||||
|
if (buffer[curPos - 1] == '\0') {
|
||||||
|
// end of string, add it to the strings list
|
||||||
|
_madsVocab.push_back(strdup(buffer));
|
||||||
|
curPos = 0;
|
||||||
|
strcpy(buffer, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->res()->toss("vocab.dat");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Globals::loadMadsQuotes() {
|
||||||
|
Common::SeekableReadStream *quoteS = _vm->res()->get("quotes.dat");
|
||||||
|
int curPos = 0;
|
||||||
|
|
||||||
|
char buffer[128];
|
||||||
|
strcpy(buffer, "");
|
||||||
|
|
||||||
|
while(!quoteS->eos()) {
|
||||||
|
buffer[curPos++] = quoteS->readByte();
|
||||||
|
if (buffer[curPos - 1] == '\0') {
|
||||||
|
// end of string, add it to the strings list
|
||||||
|
_madsQuotes.push_back(strdup(buffer));
|
||||||
|
curPos = 0;
|
||||||
|
strcpy(buffer, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->res()->toss("quotes.dat");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Globals::loadMadsMessagesInfo() {
|
||||||
|
Common::SeekableReadStream *messageS = _vm->res()->get("messages.dat");
|
||||||
|
|
||||||
|
int16 count = messageS->readUint16LE();
|
||||||
|
//printf("%i messages\n", count);
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
MessageItem *curMessage = new MessageItem();
|
||||||
|
curMessage->id = messageS->readUint32LE();
|
||||||
|
curMessage->offset = messageS->readUint32LE();
|
||||||
|
curMessage->uncompSize = messageS->readUint16LE();
|
||||||
|
|
||||||
|
if (i > 0)
|
||||||
|
_madsMessages[i - 1]->compSize = curMessage->offset - _madsMessages[i - 1]->offset;
|
||||||
|
|
||||||
|
if (i == count - 1)
|
||||||
|
curMessage->compSize = messageS->size() - curMessage->offset;
|
||||||
|
|
||||||
|
//printf("id: %i, offset: %i, uncomp size: %i\n", curMessage->id, curMessage->offset, curMessage->uncompSize);
|
||||||
|
_madsMessages.push_back(curMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->res()->toss("messages.dat");
|
||||||
|
}
|
||||||
|
|
||||||
|
char* Globals::loadMessage(uint32 index) {
|
||||||
|
if (index > _madsMessages.size() - 1) {
|
||||||
|
warning("Invalid message index: %i", index);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
FabDecompressor fab;
|
||||||
|
byte *compData = new byte[_madsMessages[index]->compSize];
|
||||||
|
byte *buffer = new byte[_madsMessages[index]->uncompSize];
|
||||||
|
|
||||||
|
Common::SeekableReadStream *messageS = _vm->res()->get("messages.dat");
|
||||||
|
messageS->seek(_madsMessages[index]->offset, SEEK_SET);
|
||||||
|
messageS->read(compData, _madsMessages[index]->compSize);
|
||||||
|
fab.decompress(compData, _madsMessages[index]->compSize, buffer, _madsMessages[index]->uncompSize);
|
||||||
|
|
||||||
|
for (int i = 0; i < _madsMessages[index]->uncompSize - 1; i++)
|
||||||
|
if (buffer[i] == '\0') buffer[i] = '\n';
|
||||||
|
|
||||||
|
_vm->res()->toss("messages.dat");
|
||||||
|
|
||||||
|
return (char*)buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Player::Player(M4Engine *vm) : _vm(vm) {
|
||||||
|
commandsAllowed = true;
|
||||||
|
needToWalk = false;
|
||||||
|
readyToWalk = false;
|
||||||
|
waitingForWalk = false;
|
||||||
|
commandReady = false;
|
||||||
|
strcpy(verb, "");
|
||||||
|
strcpy(noun, "");
|
||||||
|
strcpy(prep, "");
|
||||||
|
strcpy(object, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setCommandsAllowed(bool value) {
|
||||||
|
setCommandsAllowedFlag = true;
|
||||||
|
commandsAllowed = value;
|
||||||
|
if (value) {
|
||||||
|
// Player commands are enabled again
|
||||||
|
_vm->_mouse->lockCursor(CURSOR_ARROW);
|
||||||
|
//_vm->_interfaceView->cancelSentence();
|
||||||
|
} else {
|
||||||
|
// Player commands are disabled, so show hourglass cursor
|
||||||
|
_vm->_mouse->lockCursor(CURSOR_HOURGLASS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::said(const char *word1, const char *word2, const char *word3) {
|
||||||
|
const char *words[3];
|
||||||
|
words[0] = word1;
|
||||||
|
words[1] = word2;
|
||||||
|
words[2] = word2;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (words[i])
|
||||||
|
if ((scumm_stricmp(noun, words[i])) &&
|
||||||
|
(scumm_stricmp(object, words[i])) &&
|
||||||
|
(scumm_stricmp(verb, words[i])))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Player::saidAny(const char *word1, const char *word2, const char *word3,
|
||||||
|
const char *word4, const char *word5, const char *word6, const char *word7,
|
||||||
|
const char *word8, const char *word9, const char *word10) {
|
||||||
|
const char *words[10];
|
||||||
|
words[0] = word1;
|
||||||
|
words[1] = word2;
|
||||||
|
words[2] = word3;
|
||||||
|
words[3] = word4;
|
||||||
|
words[4] = word5;
|
||||||
|
words[5] = word6;
|
||||||
|
words[6] = word7;
|
||||||
|
words[7] = word8;
|
||||||
|
words[8] = word9;
|
||||||
|
words[9] = word10;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (words[i]) {
|
||||||
|
if (!scumm_stricmp(noun, words[i]))
|
||||||
|
return true;
|
||||||
|
if (!scumm_stricmp(object, words[i]))
|
||||||
|
return true;
|
||||||
|
if (!scumm_stricmp(verb, words[i]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // End of namespace M4
|
221
engines/m4/globals.h
Normal file
221
engines/m4/globals.h
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_GLOBALS_H
|
||||||
|
#define M4_GLOBALS_H
|
||||||
|
|
||||||
|
#include "common/array.h"
|
||||||
|
#include "common/rect.h"
|
||||||
|
#include "common/scummsys.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
class M4Engine;
|
||||||
|
class ScriptInterpreter;
|
||||||
|
class ScriptFunction;
|
||||||
|
|
||||||
|
// Globals
|
||||||
|
enum WoodScriptGlobals {
|
||||||
|
kGlobTime = 0,
|
||||||
|
kGlobTimeDelta = 1,
|
||||||
|
kGlobMinY = 2,
|
||||||
|
kGlobMaxY = 3,
|
||||||
|
kGlobMinScale = 4,
|
||||||
|
kGlobMaxScale = 5,
|
||||||
|
kGlobScaler = 6,
|
||||||
|
kGlobTemp1 = 7,
|
||||||
|
kGlobTemp2 = 8,
|
||||||
|
kGlobTemp3 = 9,
|
||||||
|
kGlobTemp4 = 10,
|
||||||
|
kGlobTemp5 = 11,
|
||||||
|
kGlobTemp6 = 12,
|
||||||
|
kGlobTemp7 = 13,
|
||||||
|
kGlobTemp8 = 14,
|
||||||
|
kGlobTemp9 = 15,
|
||||||
|
kGlobTemp10 = 16,
|
||||||
|
kGlobTemp11 = 17,
|
||||||
|
kGlobTemp12 = 18,
|
||||||
|
kGlobTemp13 = 19,
|
||||||
|
kGlobTemp14 = 20,
|
||||||
|
kGlobTemp15 = 21,
|
||||||
|
kGlobTemp16 = 22,
|
||||||
|
kGlobTemp17 = 23,
|
||||||
|
kGlobTemp18 = 24,
|
||||||
|
kGlobTemp19 = 25,
|
||||||
|
kGlobTemp20 = 26,
|
||||||
|
kGlobTemp21 = 27,
|
||||||
|
kGlobTemp22 = 28,
|
||||||
|
kGlobTemp23 = 29,
|
||||||
|
kGlobTemp24 = 30,
|
||||||
|
kGlobTemp25 = 31,
|
||||||
|
kGlobTemp26 = 32,
|
||||||
|
kGlobTemp27 = 33,
|
||||||
|
kGlobTemp28 = 34,
|
||||||
|
kGlobTemp29 = 35,
|
||||||
|
kGlobTemp30 = 36,
|
||||||
|
kGlobTemp31 = 37,
|
||||||
|
kGlobTemp32 = 38
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32 SERIES_FORWARD = 0;
|
||||||
|
const uint32 SERIES_PINGPONG = 1;
|
||||||
|
const uint32 SERIES_BACKWARD = 2;
|
||||||
|
const uint32 SERIES_RANDOM = 4;
|
||||||
|
const uint32 SERIES_NO_TOSS = 8;
|
||||||
|
const uint32 SERIES_STICK = 16;
|
||||||
|
const uint32 SERIES_LOOP_TRIGGER = 32;
|
||||||
|
const uint32 SERIES_LOAD_PALETTE = 64;
|
||||||
|
const uint32 SERIES_HORZ_FLIP =128;
|
||||||
|
|
||||||
|
enum KernelTriggerType {
|
||||||
|
KT_PARSE = 1,
|
||||||
|
KT_DAEMON,
|
||||||
|
KT_PREPARSE
|
||||||
|
};
|
||||||
|
|
||||||
|
class Kernel {
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
ScriptFunction *_globalDaemonFn, *_globalParserFn;
|
||||||
|
ScriptFunction *_sectionInitFn, *_sectionDaemonFn, *_sectionParserFn;
|
||||||
|
ScriptFunction *_roomInitFn, *_roomDaemonFn, *_roomPreParserFn, *_roomParserFn;
|
||||||
|
void pauseEngines();
|
||||||
|
void unpauseEngines();
|
||||||
|
public:
|
||||||
|
Kernel(M4Engine *vm);
|
||||||
|
|
||||||
|
// TODO: Move to some palette/fading class
|
||||||
|
int fadeUpDuration, firstFadeColorIndex;
|
||||||
|
int minPalEntry, maxPalEntry;
|
||||||
|
|
||||||
|
bool paused;
|
||||||
|
//machine* myWalker;
|
||||||
|
bool repeatedlyCallDeamon;
|
||||||
|
bool daemonTriggerAvailable;
|
||||||
|
bool betweenRooms;
|
||||||
|
int currentSection, newSection, previousSection;
|
||||||
|
int currentRoom, newRoom, previousRoom;
|
||||||
|
|
||||||
|
int32 trigger;
|
||||||
|
KernelTriggerType triggerMode;
|
||||||
|
|
||||||
|
int32 createTrigger(int32 triggerNum);
|
||||||
|
bool sendTrigger(int32 triggerNum);
|
||||||
|
bool handleTrigger(int32 triggerNum);
|
||||||
|
|
||||||
|
void loadGlobalScriptFunctions();
|
||||||
|
void loadSectionScriptFunctions();
|
||||||
|
void loadRoomScriptFunctions();
|
||||||
|
|
||||||
|
void globalDaemon();
|
||||||
|
void globalParser();
|
||||||
|
|
||||||
|
void sectionInit();
|
||||||
|
void sectionDaemon();
|
||||||
|
void sectionParser();
|
||||||
|
|
||||||
|
void roomInit();
|
||||||
|
void roomDaemon();
|
||||||
|
void roomPreParser();
|
||||||
|
void roomParser();
|
||||||
|
|
||||||
|
void pauseGame(bool value);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TOTAL_NUM_VARIABLES 256
|
||||||
|
|
||||||
|
class Globals {
|
||||||
|
private:
|
||||||
|
struct MessageItem {
|
||||||
|
uint32 id;
|
||||||
|
uint32 offset;
|
||||||
|
uint16 uncompSize;
|
||||||
|
uint16 compSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
M4Engine *_vm;
|
||||||
|
Common::Array<char* > _madsVocab;
|
||||||
|
Common::Array<char* > _madsQuotes;
|
||||||
|
Common::Array<MessageItem* > _madsMessages;
|
||||||
|
public:
|
||||||
|
Globals(M4Engine *vm);
|
||||||
|
~Globals();
|
||||||
|
bool isInterfaceVisible();
|
||||||
|
|
||||||
|
// M4 variables
|
||||||
|
bool invSuppressClickSound;
|
||||||
|
|
||||||
|
void loadMadsVocab();
|
||||||
|
uint32 getVocabSize() { return _madsVocab.size(); }
|
||||||
|
char* getVocab(uint32 index) { return _madsVocab[index]; }
|
||||||
|
|
||||||
|
void loadMadsQuotes();
|
||||||
|
uint32 getQuotesSize() { return _madsQuotes.size(); }
|
||||||
|
char* getQuote(uint32 index) { return _madsQuotes[index]; }
|
||||||
|
|
||||||
|
void loadMadsMessagesInfo();
|
||||||
|
uint32 getMessagesSize() { return _madsMessages.size(); }
|
||||||
|
char* loadMessage(uint32 index);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PLAYER_FIELD_LENGTH 40
|
||||||
|
|
||||||
|
class Player {
|
||||||
|
public:
|
||||||
|
Player(M4Engine *vm);
|
||||||
|
void setCommandsAllowed(bool value);
|
||||||
|
|
||||||
|
// Variables
|
||||||
|
Common::Point position; // Player's current position
|
||||||
|
int facing; // Facing direction
|
||||||
|
|
||||||
|
char verb[PLAYER_FIELD_LENGTH]; // Action strings
|
||||||
|
char noun[PLAYER_FIELD_LENGTH];
|
||||||
|
char prep[PLAYER_FIELD_LENGTH];
|
||||||
|
char object[PLAYER_FIELD_LENGTH];
|
||||||
|
Common::String assetName, shadowName;
|
||||||
|
int walkerType, shadowType;
|
||||||
|
bool needToWalk, readyToWalk, waitingForWalk;
|
||||||
|
bool commandsAllowed;
|
||||||
|
bool commandReady;
|
||||||
|
bool visible;
|
||||||
|
bool beenInRoomBefore;
|
||||||
|
bool walkerInCurrentRoom;
|
||||||
|
int32 walkerTriggerNum;
|
||||||
|
int walkFacing;
|
||||||
|
bool setCommandsAllowedFlag;
|
||||||
|
|
||||||
|
bool said(const char *word1, const char *word2 = NULL, const char *word3 = NULL);
|
||||||
|
bool saidAny(const char *word1, const char *word2, const char *word3,
|
||||||
|
const char *word4, const char *word5, const char *word6, const char *word7,
|
||||||
|
const char *word8, const char *word9, const char *word10);
|
||||||
|
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
1074
engines/m4/graphics.cpp
Normal file
1074
engines/m4/graphics.cpp
Normal file
File diff suppressed because it is too large
Load diff
222
engines/m4/graphics.h
Normal file
222
engines/m4/graphics.h
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_GRAPHICS_H
|
||||||
|
#define M4_GRAPHICS_H
|
||||||
|
|
||||||
|
#include "common/rect.h"
|
||||||
|
#include "common/system.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
#include "graphics/surface.h"
|
||||||
|
|
||||||
|
#include "m4/globals.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
struct BGR8 {
|
||||||
|
uint8 b, g, r;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RGB8 {
|
||||||
|
uint8 r, g, b, u;
|
||||||
|
};
|
||||||
|
|
||||||
|
//later use ScummVM's Rect?
|
||||||
|
struct M4Rect {
|
||||||
|
int32 x1, y1, x2, y2;
|
||||||
|
};
|
||||||
|
|
||||||
|
class M4Surface;
|
||||||
|
|
||||||
|
// RGBList
|
||||||
|
// Implements a list of RGB entries
|
||||||
|
|
||||||
|
class RGBList {
|
||||||
|
private:
|
||||||
|
int _size;
|
||||||
|
RGB8 *_data;
|
||||||
|
byte *_palIndexes;
|
||||||
|
bool _freeData;
|
||||||
|
public:
|
||||||
|
RGBList(int numEntries = 256, RGB8 *srcData = NULL, bool freeData = true);
|
||||||
|
~RGBList();
|
||||||
|
|
||||||
|
RGB8 *data() { return _data; }
|
||||||
|
byte *palIndexes() { return _palIndexes; }
|
||||||
|
int size() { return _size; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// M4Surface
|
||||||
|
// Class representing either a buffered surface or the physical screen.
|
||||||
|
|
||||||
|
class M4Sprite;
|
||||||
|
|
||||||
|
struct SpriteInfo {
|
||||||
|
M4Sprite *sprite;
|
||||||
|
int hotX, hotY;
|
||||||
|
int width, height;
|
||||||
|
int scaleX, scaleY;
|
||||||
|
uint8 encoding;
|
||||||
|
byte *inverseColorTable;
|
||||||
|
RGB8 *palette;
|
||||||
|
};
|
||||||
|
|
||||||
|
class M4Surface : public Graphics::Surface {
|
||||||
|
private:
|
||||||
|
byte _color;
|
||||||
|
bool _isScreen;
|
||||||
|
|
||||||
|
void rexLoadBackground(Common::SeekableReadStream *source, RGBList **palData = NULL);
|
||||||
|
void madsLoadBackground(int roomNumber, RGBList **palData = NULL);
|
||||||
|
void m4LoadBackground(Common::SeekableReadStream *source);
|
||||||
|
public:
|
||||||
|
M4Surface(bool isScreen = false) {
|
||||||
|
create(g_system->getWidth(), g_system->getHeight(), 1);
|
||||||
|
_isScreen = isScreen;
|
||||||
|
}
|
||||||
|
M4Surface(int Width, int Height) { create(Width, Height, 1); _isScreen = false; }
|
||||||
|
|
||||||
|
// loads a .COD file into the M4Surface
|
||||||
|
// TODO: maybe move this to the rail system? check where it makes sense
|
||||||
|
// The sprite drawing needs this, too, so should be more global.
|
||||||
|
void loadCodesM4(Common::SeekableReadStream *source);
|
||||||
|
void loadCodesMads(Common::SeekableReadStream *source);
|
||||||
|
|
||||||
|
// loads the specified background
|
||||||
|
void loadBackground(int sceneNumber, RGBList **palData = NULL);
|
||||||
|
void loadBackgroundRiddle(const char *sceneName);
|
||||||
|
void madsloadInterface(int index, RGBList **palData);
|
||||||
|
|
||||||
|
void setColor(byte value) { _color = value; }
|
||||||
|
byte getColor() { return _color; }
|
||||||
|
void vLine(int x, int y1, int y2);
|
||||||
|
void hLine(int x1, int x2, int y);
|
||||||
|
void vLineXor(int x, int y1, int y2);
|
||||||
|
void hLineXor(int x1, int x2, int y);
|
||||||
|
void line(int x1, int y1, int x2, int y2, byte color);
|
||||||
|
void frameRect(int x1, int y1, int x2, int y2);
|
||||||
|
void fillRect(int x1, int y1, int x2, int y2);
|
||||||
|
|
||||||
|
void drawSprite(int x, int y, SpriteInfo &info, const Common::Rect &clipRect);
|
||||||
|
|
||||||
|
// Surface methods
|
||||||
|
int width() { return w; }
|
||||||
|
int height() { return h; }
|
||||||
|
void setSize(int sizeX, int sizeY) { create(sizeX, sizeY, 1); }
|
||||||
|
byte *getData();
|
||||||
|
byte *getBasePtr(int x, int y);
|
||||||
|
void freeData();
|
||||||
|
void empty();
|
||||||
|
void frameRect(const Common::Rect &r, uint8 color);
|
||||||
|
void fillRect(const Common::Rect &r, uint8 color);
|
||||||
|
void copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY,
|
||||||
|
int transparentColor = -1);
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
if (_isScreen) {
|
||||||
|
g_system->copyRectToScreen((const byte *)pixels, pitch, 0, 0, w, h);
|
||||||
|
g_system->updateScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyTo methods
|
||||||
|
void copyTo(M4Surface *dest, int transparentColor = -1) {
|
||||||
|
dest->copyFrom(this, Common::Rect(width(), height()), 0, 0, transparentColor);
|
||||||
|
}
|
||||||
|
void copyTo(M4Surface *dest, int x, int y, int transparentColor = -1) {
|
||||||
|
dest->copyFrom(this, Common::Rect(width(), height()), x, y, transparentColor);
|
||||||
|
}
|
||||||
|
void copyTo(M4Surface *dest, const Common::Rect &srcBounds, int destX, int destY,
|
||||||
|
int transparentColor = -1) {
|
||||||
|
dest->copyFrom(this, srcBounds, destX, destY, transparentColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void translate(RGBList *list, bool isTransparent = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FadeType {FT_TO_GREY, FT_TO_COLOR, FT_TO_BLOCK};
|
||||||
|
|
||||||
|
class Palette {
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
bool _colorsChanged;
|
||||||
|
bool _fading_in_progress;
|
||||||
|
byte _originalPalette[256 * 4];
|
||||||
|
byte _fadedPalette[256 * 4];
|
||||||
|
int _usageCount[256];
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
public:
|
||||||
|
Palette(M4Engine *vm);
|
||||||
|
|
||||||
|
void setPalette(const byte *colors, uint start, uint num);
|
||||||
|
void setPalette(const RGB8 *colors, uint start, uint num);
|
||||||
|
void grabPalette(byte *colors, uint start, uint num);
|
||||||
|
void grabPalette(RGB8 *colors, uint start, uint num) {
|
||||||
|
grabPalette((byte *)colors, start, num);
|
||||||
|
}
|
||||||
|
uint8 palIndexFromRgb(byte r, byte g, byte b, RGB8 *paletteData = NULL);
|
||||||
|
|
||||||
|
void fadeToGreen(int numSteps, uint delayAmount);
|
||||||
|
void fadeFromGreen(int numSteps, uint delayAmount, bool fadeToBlack);
|
||||||
|
void fadeIn(int numSteps, uint delayAmount, RGB8 *destPalette, int numColors);
|
||||||
|
void fadeIn(int numSteps, uint delayAmount, RGBList *destPalette);
|
||||||
|
static RGB8 *decodeMadsPalette(Common::SeekableReadStream *palStream, int *numColors);
|
||||||
|
int setMadsPalette(Common::SeekableReadStream *palStream, int indexStart = 0);
|
||||||
|
void setMadsSystemPalette();
|
||||||
|
|
||||||
|
// Methods used for reference counting color usage
|
||||||
|
void resetColorCounts();
|
||||||
|
void blockRange(int startIndex, int size);
|
||||||
|
void addRange(RGBList *list);
|
||||||
|
void deleteRange(RGBList *list);
|
||||||
|
void deleteAllRanges();
|
||||||
|
|
||||||
|
// Color indexes
|
||||||
|
uint8 BLACK;
|
||||||
|
uint8 BLUE;
|
||||||
|
uint8 GREEN;
|
||||||
|
uint8 CYAN;
|
||||||
|
uint8 RED;
|
||||||
|
uint8 VIOLET;
|
||||||
|
uint8 BROWN;
|
||||||
|
uint8 LIGHT_GRAY;
|
||||||
|
uint8 DARK_GRAY;
|
||||||
|
uint8 LIGHT_BLUE;
|
||||||
|
uint8 LIGHT_GREEN;
|
||||||
|
uint8 LIGHT_CYAN;
|
||||||
|
uint8 LIGHT_RED;
|
||||||
|
uint8 PINK;
|
||||||
|
uint8 YELLOW;
|
||||||
|
uint8 WHITE;
|
||||||
|
};
|
||||||
|
|
||||||
|
void decompressRle(byte *rleData, int rleSize, byte *celData, int w, int h);
|
||||||
|
void decompressRle(Common::SeekableReadStream &rleData, byte *celData, int w, int h);
|
||||||
|
int scaleValue(int value, int scale, int err);
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
1217
engines/m4/gui.cpp
Normal file
1217
engines/m4/gui.cpp
Normal file
File diff suppressed because it is too large
Load diff
446
engines/m4/gui.h
Normal file
446
engines/m4/gui.h
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_GUI_H
|
||||||
|
#define M4_GUI_H
|
||||||
|
|
||||||
|
#include "common/list.h"
|
||||||
|
#include "common/rect.h"
|
||||||
|
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
#include "m4/events.h"
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/saveload.h"
|
||||||
|
#include "m4/sprite.h"
|
||||||
|
#include "m4/assets.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
class GUIObject;
|
||||||
|
class MenuObject;
|
||||||
|
class GUIObjectButton;
|
||||||
|
class SpriteAsset;
|
||||||
|
class View;
|
||||||
|
|
||||||
|
enum MenuType {GAME_MENU, OPTIONS_MENU, SAVE_MENU, LOAD_MENU, MAIN_MENU};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
BTNID_QUIT = 1,
|
||||||
|
BTNID_OPTIONS = 2,
|
||||||
|
BTNID_RESUME = 3,
|
||||||
|
BTNID_SAVE = 4,
|
||||||
|
BTNID_LOAD = 5,
|
||||||
|
BTNID_MAIN = 6,
|
||||||
|
|
||||||
|
OPTIONID_DONE = 1,
|
||||||
|
OPTIONID_CANCEL = 2,
|
||||||
|
OPTIONID_HSLIDER_DIGI = 3,
|
||||||
|
OPTIONID_HSLIDER_MIDI = 4,
|
||||||
|
|
||||||
|
SAVELOADID_SAVE = 1,
|
||||||
|
SAVELOADID_CANCELSAVE = 2,
|
||||||
|
SAVELOADID_LOAD = 3,
|
||||||
|
SAVELOADID_CANCELLOAD = 4,
|
||||||
|
|
||||||
|
SLTAG_SAVELOAD = 100,
|
||||||
|
SLTAG_SAVELOAD_LABEL = 101,
|
||||||
|
SLTAG_CANCEL = 102,
|
||||||
|
SLTAG_VSLIDER = 103,
|
||||||
|
SLTAG_THUMBNAIL = 104,
|
||||||
|
|
||||||
|
SLTAG_SLOTS_START = 1001,
|
||||||
|
SLTAG_TEXTFIELD = 2000
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ObjectType {
|
||||||
|
OBJTYPE_BUTTON,
|
||||||
|
|
||||||
|
OBJTYPE_SLIDER,
|
||||||
|
OBJTYPE_OM_SWITCH_ON,
|
||||||
|
OBJTYPE_OM_SWITCH_OFF,
|
||||||
|
OBJTYPE_OM_DONE,
|
||||||
|
OBJTYPE_OM_CANCEL,
|
||||||
|
|
||||||
|
OBJTYPE_SL_SAVE,
|
||||||
|
OBJTYPE_SL_LOAD,
|
||||||
|
OBJTYPE_SL_CANCEL,
|
||||||
|
OBJTYPE_SL_TEXT
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GameMenuSpriteType {
|
||||||
|
GM_DIALOG_BOX,
|
||||||
|
|
||||||
|
GM_BUTTON_GREYED,
|
||||||
|
GM_BUTTON_NORMAL,
|
||||||
|
GM_BUTTON_MOUSEOVER,
|
||||||
|
GM_BUTTON_PRESSED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OptionMenuSpriteType {
|
||||||
|
OM_DIALOG_BOX,
|
||||||
|
|
||||||
|
OM_SLIDER_BTN_NORMAL,
|
||||||
|
OM_SLIDER_BTN_MOUSEOVER,
|
||||||
|
OM_SLIDER_BTN_PRESSED,
|
||||||
|
OM_SLIDER_BAR,
|
||||||
|
OM_DONE_BTN_GREYED,
|
||||||
|
OM_DONE_BTN_NORMAL,
|
||||||
|
OM_DONE_BTN_MOUSEOVER,
|
||||||
|
OM_DONE_BTN_PRESSED,
|
||||||
|
OM_CANCEL_BTN_NORMAL,
|
||||||
|
OM_CANCEL_BTN_MOUSEOVER,
|
||||||
|
OM_CANCEL_BTN_PRESSED
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum SaveLoadSpriteType {
|
||||||
|
SL_DIALOG_BOX,
|
||||||
|
SL_EMPTY_THUMBNAIL,
|
||||||
|
|
||||||
|
SL_SAVE_BTN_GREYED,
|
||||||
|
SL_SAVE_BTN_NORMAL,
|
||||||
|
SL_SAVE_BTN_MOUSEOVER,
|
||||||
|
SL_SAVE_BTN_PRESSED,
|
||||||
|
|
||||||
|
SL_LOAD_BTN_GREYED,
|
||||||
|
SL_LOAD_BTN_NORMAL,
|
||||||
|
SL_LOAD_BTN_MOUSEOVER,
|
||||||
|
SL_LOAD_BTN_PRESSED,
|
||||||
|
|
||||||
|
SL_CANCEL_BTN_NORMAL,
|
||||||
|
SL_CANCEL_BTN_MOUSEOVER,
|
||||||
|
SL_CANCEL_BTN_PRESSED,
|
||||||
|
|
||||||
|
SL_UP_BTN_GREYED,
|
||||||
|
SL_UP_BTN_NORMAL,
|
||||||
|
SL_UP_BTN_MOUSEOVER,
|
||||||
|
SL_UP_BTN_PRESSED,
|
||||||
|
|
||||||
|
SL_DOWN_BTN_GREYED,
|
||||||
|
SL_DOWN_BTN_NORMAL,
|
||||||
|
SL_DOWN_BTN_MOUSEOVER,
|
||||||
|
SL_DOWN_BTN_PRESSED,
|
||||||
|
|
||||||
|
SL_SAVE_LABEL,
|
||||||
|
SL_LOAD_LABEL,
|
||||||
|
|
||||||
|
SL_SLIDER_BTN_NORMAL,
|
||||||
|
SL_SLIDER_BTN_MOUSEOVER,
|
||||||
|
SL_SLIDER_BTN_PRESSED,
|
||||||
|
|
||||||
|
SL_LINE_NORMAL,
|
||||||
|
SL_LINE_MOUSEOVER,
|
||||||
|
SL_LINE_PRESSED,
|
||||||
|
|
||||||
|
SL_SCROLLBAR
|
||||||
|
};
|
||||||
|
|
||||||
|
enum TextColors {
|
||||||
|
TEXT_COLOR_NORMAL = 1,
|
||||||
|
TEXT_COLOR_GREYED = 1,
|
||||||
|
TEXT_COLOR_MOUSEOVER = 2,
|
||||||
|
TEXT_COLOR_PRESSED = 2,
|
||||||
|
|
||||||
|
TEXT_COLOR_GREYED_HILIGHT = 236,
|
||||||
|
TEXT_COLOR_GREYED_FOREGROUND = 131,
|
||||||
|
TEXT_COLOR_GREYED_SHADOW = 186,
|
||||||
|
|
||||||
|
TEXT_COLOR_NORMAL_HILIGHT = 129,
|
||||||
|
TEXT_COLOR_NORMAL_FOREGROUND = 130,
|
||||||
|
TEXT_COLOR_NORMAL_SHADOW = 236,
|
||||||
|
|
||||||
|
TEXT_COLOR_MOUSEOVER_HILIGHT = 129,
|
||||||
|
TEXT_COLOR_MOUSEOVER_FOREGROUND = 130,
|
||||||
|
TEXT_COLOR_MOUSEOVER_SHADOW = 236,
|
||||||
|
|
||||||
|
TEXT_COLOR_PRESSED_HILIGHT = 236,
|
||||||
|
TEXT_COLOR_PRESSED_FOREGROUND = 130,
|
||||||
|
TEXT_COLOR_PRESSED_SHADOW = 129
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MENU_SS_FIELD_NORMAL 5
|
||||||
|
#define MENU_SS_FIELD_MOUSEOVER 6
|
||||||
|
|
||||||
|
#define MENU_SS_H_SLIDER_NORMAL 5
|
||||||
|
#define MENU_SS_H_SLIDER_MOUSE_OVER 6
|
||||||
|
#define MENU_SS_H_SLIDER_PRESSED 7
|
||||||
|
|
||||||
|
#define MENU_SS_SWITCH_ON_NORMAL 8
|
||||||
|
#define MENU_SS_SWITCH_ON_MOUSEOVER 9
|
||||||
|
#define MENU_SS_SWITCH_ON_PRESSED 13
|
||||||
|
|
||||||
|
#define MENU_SS_SWITCH_OFF_PRESSED 10
|
||||||
|
#define MENU_SS_SWITCH_OFF_NORMAL 11
|
||||||
|
#define MENU_SS_SWITCH_OFF_MOUSEOVER 12
|
||||||
|
|
||||||
|
#define MENU_GUI "gui menu"
|
||||||
|
#define MENU_GAME "gamemenu"
|
||||||
|
#define MENU_OPTIONS "opmenu"
|
||||||
|
#define MENU_ERROR "errmenu"
|
||||||
|
#define MENU_SAVELOAD "slmenu"
|
||||||
|
#define MENU_BURGMAIN "903menu"
|
||||||
|
#define MENU_BURGDEMOMAIN "901menu"
|
||||||
|
|
||||||
|
#define SL_NUM_VISIBLE_SLOTS 8
|
||||||
|
#define SL_THUMBNAIL_WIDTH 215
|
||||||
|
#define SL_THUMBNAIL_HEIGHT 162
|
||||||
|
|
||||||
|
enum MenuObjectState {OS_GREYED = 0, OS_NORMAL = 1, OS_MOUSEOVER = 2, OS_PRESSED = 3};
|
||||||
|
|
||||||
|
class DialogView: public View {
|
||||||
|
public:
|
||||||
|
DialogView(M4Engine *Vm, const Common::Rect &viewBounds, bool transparent = false):
|
||||||
|
View(Vm, viewBounds, transparent) {};
|
||||||
|
DialogView(M4Engine *Vm, int x = 0, int y = 0, bool transparent = false):
|
||||||
|
View(Vm, x, y, transparent) {};
|
||||||
|
|
||||||
|
M4Engine *vm() { return _vm; }
|
||||||
|
virtual SpriteAsset *sprites() = 0;
|
||||||
|
virtual MenuType getMenuType() = 0;
|
||||||
|
virtual MenuObject *getItem(int objectId) { return NULL; }
|
||||||
|
virtual void refresh(const Common::Rect &areaRect) {}
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
|
int _topSaveSlotIndex, _selectedSlot;
|
||||||
|
int _highlightedSlot;
|
||||||
|
bool _deleteSaveDesc;
|
||||||
|
M4Surface *_savegameThumbnail;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GUIObject {
|
||||||
|
protected:
|
||||||
|
View *_parent;
|
||||||
|
Common::Rect _bounds;
|
||||||
|
M4Surface *_background;
|
||||||
|
public:
|
||||||
|
GUIObject(View *owner, const Common::Rect &bounds);
|
||||||
|
virtual ~GUIObject() {}
|
||||||
|
|
||||||
|
bool isInside(int x, int y) { return _bounds.contains(x, y); }
|
||||||
|
Common::Rect getBounds() const { return _bounds; }
|
||||||
|
|
||||||
|
virtual void onRefresh() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuObject: public GUIObject {
|
||||||
|
public:
|
||||||
|
typedef void (*Callback)(DialogView *view, MenuObject *item);
|
||||||
|
protected:
|
||||||
|
MenuObject::Callback _callback;
|
||||||
|
ObjectType _objectType;
|
||||||
|
MenuObjectState _objectState;
|
||||||
|
bool _transparent;
|
||||||
|
int _objectId;
|
||||||
|
public:
|
||||||
|
MenuObject(DialogView *owner, int objectId, int xs, int ys, int width, int height,
|
||||||
|
bool greyed = false, bool transparent = false);
|
||||||
|
~MenuObject();
|
||||||
|
|
||||||
|
DialogView *parent() { return (DialogView *)_parent; }
|
||||||
|
MenuObjectState getState() { return _objectState; }
|
||||||
|
virtual void setState(MenuObjectState state) {
|
||||||
|
_objectState = state;
|
||||||
|
onRefresh();
|
||||||
|
}
|
||||||
|
int getObjectId() { return _objectId; }
|
||||||
|
|
||||||
|
void onExecute();
|
||||||
|
virtual bool onEvent(M4EventType event, int param, int x, int y, MenuObject *¤tItem) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuButton: public MenuObject {
|
||||||
|
public:
|
||||||
|
MenuButton(DialogView *owner, int buttonId, int xs, int ys, int width, int height,
|
||||||
|
Callback callbackFn = NULL, bool greyed = false, bool transparent = false,
|
||||||
|
ObjectType buttonType = OBJTYPE_BUTTON);
|
||||||
|
|
||||||
|
void onRefresh();
|
||||||
|
bool onEvent(M4EventType event, int param, int x, int y, MenuObject *¤tItem);
|
||||||
|
bool getToggled() { return _objectType == OBJTYPE_OM_SWITCH_ON; }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MenuHorizSliderState {HSLIDER_THUMB_NORMAL = 0, HSLIDER_THUMB_MOUSEOVER = 1, HSLIDER_THUMB_PRESSED = 2};
|
||||||
|
#define SLIDER_BAR_COLOR 129
|
||||||
|
|
||||||
|
class MenuHorizSlider: public MenuObject {
|
||||||
|
protected:
|
||||||
|
MenuHorizSliderState _sliderState;
|
||||||
|
Common::Point _thumbSize;
|
||||||
|
int _maxThumbX;
|
||||||
|
int _percent;
|
||||||
|
int _thumbX;
|
||||||
|
public:
|
||||||
|
MenuHorizSlider(DialogView *owner, int sliderId, int xs, int ys, int width, int height,
|
||||||
|
int initialPercentage, Callback callbackFn = NULL, bool transparent = false);
|
||||||
|
|
||||||
|
void onRefresh();
|
||||||
|
bool onEvent(M4EventType event, int param, int x, int y, MenuObject *¤tItem);
|
||||||
|
int percent() { return _percent; }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MenuVertSliderState {
|
||||||
|
VSLIDER_NONE = 0x0000,
|
||||||
|
VSLIDER_UP = 0x0010,
|
||||||
|
VSLIDER_PAGE_UP = 0x0020,
|
||||||
|
VSLIDER_THUMBNAIL = 0x0030,
|
||||||
|
VSLIDER_PAGE_DOWN = 0x0040,
|
||||||
|
VSLIDER_DOWN = 0x0050
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuVertSlider: public MenuObject {
|
||||||
|
protected:
|
||||||
|
MenuVertSliderState _sliderState;
|
||||||
|
Common::Point _thumbSize;
|
||||||
|
int _percent;
|
||||||
|
int _thumbY;
|
||||||
|
int _minThumbY;
|
||||||
|
int _maxThumbY;
|
||||||
|
|
||||||
|
MenuVertSliderState getSliderArea(int y);
|
||||||
|
void updateThumbnails() {}
|
||||||
|
public:
|
||||||
|
MenuVertSlider(DialogView *owner, int sliderId, int xs, int ys, int width, int height,
|
||||||
|
int initialPercentage, Callback callbackFn = NULL, bool transparent = false);
|
||||||
|
|
||||||
|
void onRefresh();
|
||||||
|
bool onEvent(M4EventType event, int param, int x, int y, MenuObject *¤tItem);
|
||||||
|
MenuVertSliderState sliderState() { return _sliderState; }
|
||||||
|
int percent() { return _percent; }
|
||||||
|
void setPercentage(int value);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuMessage: public MenuObject {
|
||||||
|
public:
|
||||||
|
MenuMessage(DialogView *owner, int objectId, int x, int y, int w, int h, bool transparent = false);
|
||||||
|
|
||||||
|
void onRefresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuImage: public MenuObject {
|
||||||
|
private:
|
||||||
|
M4Surface *_sprite;
|
||||||
|
public:
|
||||||
|
MenuImage(DialogView *owner, int objectId, int xs, int ys, int width, int height,
|
||||||
|
M4Surface *image, bool transparent = false);
|
||||||
|
|
||||||
|
void onRefresh();
|
||||||
|
const M4Surface *sprite() { return _sprite; }
|
||||||
|
void setSprite(M4Surface *v) {
|
||||||
|
_sprite = v;
|
||||||
|
onRefresh();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuSaveLoadText: public MenuButton {
|
||||||
|
private:
|
||||||
|
bool _loadFlag;
|
||||||
|
const char *_displayText;
|
||||||
|
int _displayValue;
|
||||||
|
int _index;
|
||||||
|
bool _visible;
|
||||||
|
public:
|
||||||
|
MenuSaveLoadText(DialogView *owner, int textId, int xs, int ys, int width, int height,
|
||||||
|
Callback callbackFn = NULL, bool greyed = false, bool transparent = false,
|
||||||
|
bool loadFlag = false, const char *displayText = NULL, int displayValue = 0);
|
||||||
|
|
||||||
|
void onRefresh();
|
||||||
|
bool onEvent(M4EventType event, int param, int x, int y, MenuObject *¤tItem);
|
||||||
|
void setDisplay(int value, const char *text) { _displayValue = value; _displayText = text; }
|
||||||
|
int getIndex() { return _index; }
|
||||||
|
const char *getText() { return _displayText; }
|
||||||
|
bool getVisible() const { return _visible; }
|
||||||
|
void setVisible(bool value);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MenuTextField: public MenuObject {
|
||||||
|
private:
|
||||||
|
int _displayValue;
|
||||||
|
char _displayText[MAX_SAVEGAME_NAME];
|
||||||
|
int _pixelWidth;
|
||||||
|
char *_promptEnd;
|
||||||
|
char *_cursor;
|
||||||
|
public:
|
||||||
|
MenuTextField(DialogView *owner, int fieldId, int xs, int ys, int width, int height,
|
||||||
|
bool greyed, Callback callbackFn = NULL, const char *displayText = NULL,
|
||||||
|
int displayValue = 0, bool transparent = true);
|
||||||
|
|
||||||
|
void onRefresh();
|
||||||
|
bool onEvent(M4EventType event, int param, int x, int y, MenuObject *¤tItem);
|
||||||
|
|
||||||
|
const char *getText() { return _displayText; }
|
||||||
|
int getDisplayValue() { return _displayValue; }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class GUIRect: public GUIObject {
|
||||||
|
private:
|
||||||
|
int _tag;
|
||||||
|
public:
|
||||||
|
GUIRect(View *owner, const Common::Rect &bounds, int tag): GUIObject(owner, bounds) { _tag = tag; };
|
||||||
|
|
||||||
|
virtual bool onEvent(M4EventType eventType, int param, int x, int y, GUIObject *¤tItem) { return false; }
|
||||||
|
int getTag() const { return _tag; }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum GUIButtonState {BUTTON_NORMAL, BUTTON_MOUSEOVER, BUTTON_PRESSED};
|
||||||
|
|
||||||
|
class GUIButton: public GUIRect {
|
||||||
|
protected:
|
||||||
|
M4Surface *_normalSprite, *_mouseOverSprite, *_pressedSprite;
|
||||||
|
GUIButtonState _buttonState;
|
||||||
|
bool _visible;
|
||||||
|
bool _tracking;
|
||||||
|
public:
|
||||||
|
GUIButton(View *owner, const Common::Rect &bounds, int tag,
|
||||||
|
M4Surface *normalSprite, M4Surface *mouseOverSprite, M4Surface *pressedSprite);
|
||||||
|
|
||||||
|
void onRefresh();
|
||||||
|
bool onEvent(M4EventType eventType, int param, int x, int y, GUIObject *¤tItem);
|
||||||
|
GUIButtonState getState() const { return _buttonState; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class GUITextField: public GUIRect {
|
||||||
|
private:
|
||||||
|
Common::String _text;
|
||||||
|
public:
|
||||||
|
GUITextField(View *owner, const Common::Rect &bounds);
|
||||||
|
void setText(const char *value) {
|
||||||
|
_text = value;
|
||||||
|
onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRefresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Dialogs {
|
||||||
|
public:
|
||||||
|
void keyMouseCollision() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
293
engines/m4/hotspot.cpp
Normal file
293
engines/m4/hotspot.cpp
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/events.h"
|
||||||
|
#include "m4/hotspot.h"
|
||||||
|
#include "gui/debugger.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
/*
|
||||||
|
HotSpot
|
||||||
|
*/
|
||||||
|
|
||||||
|
HotSpot::HotSpot(int x1, int y1, int x2, int y2) :
|
||||||
|
_vocab(NULL), _verb(NULL), _prep(NULL), _sprite(NULL) {
|
||||||
|
|
||||||
|
_rect.left = x1;
|
||||||
|
_rect.top = y1;
|
||||||
|
_rect.right = x2 + 1;
|
||||||
|
_rect.bottom = y2 + 1;
|
||||||
|
_active = true;
|
||||||
|
|
||||||
|
_syntax = 0;
|
||||||
|
_cursor = 0;
|
||||||
|
_facing = 5;
|
||||||
|
_feetX = 32767;
|
||||||
|
_feetY = 32767;
|
||||||
|
}
|
||||||
|
|
||||||
|
HotSpot::~HotSpot() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpot::setRect(int x1, int y1, int x2, int y2) {
|
||||||
|
_rect.left = x1;
|
||||||
|
_rect.top = y1;
|
||||||
|
_rect.right = x2 + 1;
|
||||||
|
_rect.bottom = y2 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpot::setFeet(int x, int y) {
|
||||||
|
_feetX = x;
|
||||||
|
_feetY = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpot::setVocab(const char *value) {
|
||||||
|
free(_vocab);
|
||||||
|
_vocab = strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpot::setVerb(const char *value) {
|
||||||
|
free(_verb);
|
||||||
|
_verb = strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpot::setPrep(const char *value) {
|
||||||
|
free(_prep);
|
||||||
|
_prep = strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpot::setSprite(const char *value) {
|
||||||
|
free(_sprite);
|
||||||
|
_sprite = strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Rect HotSpot::getRect() const {
|
||||||
|
Common::Rect tempRect;
|
||||||
|
tempRect.left = _rect.left;
|
||||||
|
tempRect.top = _rect.top;
|
||||||
|
tempRect.right = _rect.right - 1;
|
||||||
|
tempRect.bottom = _rect.bottom - 1;
|
||||||
|
|
||||||
|
return tempRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
HotSpotList
|
||||||
|
*/
|
||||||
|
|
||||||
|
HotSpotList::HotSpotList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
HotSpotList::~HotSpotList() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
int HotSpotList::add(HotSpot *hotspot, bool head) {
|
||||||
|
if (head || _hotspots.size() == 0) {
|
||||||
|
_hotspots.insert_at(0, hotspot);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
int32 area = hotspot->area();
|
||||||
|
int index = _hotspots.size();
|
||||||
|
for (uint i = 0; i < _hotspots.size(); i++) {
|
||||||
|
if (area < _hotspots[i]->area()) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_hotspots.insert_at(index, hotspot);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpotList::remove(HotSpot *hotspot) {
|
||||||
|
unlink(hotspot);
|
||||||
|
delete hotspot; //TODO: check this?
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpotList::unlink(HotSpot *hotspot) {
|
||||||
|
uint index = 0;
|
||||||
|
while (index < _hotspots.size()) {
|
||||||
|
if (_hotspots[index] == hotspot) {
|
||||||
|
_hotspots.remove_at(index);
|
||||||
|
} else {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpotList::clear() {
|
||||||
|
for (uint i = 0; i < _hotspots.size(); i++)
|
||||||
|
delete _hotspots[i];
|
||||||
|
_hotspots.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
HotSpot *HotSpotList::findByXY(int x, int y) {
|
||||||
|
for (uint i = 0; i < _hotspots.size(); i++) {
|
||||||
|
if (_hotspots[i]->getActive() && _hotspots[i]->pointInside(x, y)) {
|
||||||
|
return _hotspots[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpotList::setActive(const char *name, bool active) {
|
||||||
|
for (uint i = 0; i < _hotspots.size(); i++) {
|
||||||
|
if (!scumm_stricmp(_hotspots[i]->_vocab, name)) {
|
||||||
|
_hotspots[i]->setActive(active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpotList::setActiveXY(const char *name, int x, int y, bool active) {
|
||||||
|
for (uint i = 0; i < _hotspots.size(); i++) {
|
||||||
|
if (_hotspots[i]->pointInside(x, y) && !scumm_stricmp(_hotspots[i]->_vocab, name)) {
|
||||||
|
_hotspots[i]->setActive(active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpotList::dump() {
|
||||||
|
_vm->_events->getConsole()->DebugPrintf("%d hotspots in the list\n", _hotspots.size());
|
||||||
|
|
||||||
|
for (uint index = 0; index < _hotspots.size(); index++) {
|
||||||
|
_vm->_events->getConsole()->DebugPrintf("(%d): %p x1 = %d; y1 = %d; x2 = %d; y2 = %d\n",
|
||||||
|
index, (void *)_hotspots[index],
|
||||||
|
_hotspots[index]->_rect.left, _hotspots[index]->_rect.top,
|
||||||
|
_hotspots[index]->_rect.right - 1, _hotspots[index]->_rect.bottom - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpotList::loadHotSpotsM4(Common::SeekableReadStream* hotspotStream, int hotspotCount) {
|
||||||
|
uint32 x1, y1, x2, y2;
|
||||||
|
char buffer[256];
|
||||||
|
uint32 strLength = 0;
|
||||||
|
uint32 index = 0;
|
||||||
|
HotSpot *currentHotSpot;
|
||||||
|
uint32 feetX, feetY;
|
||||||
|
|
||||||
|
for (int i = 0; i < hotspotCount; i++) {
|
||||||
|
x1 = hotspotStream->readUint32LE();
|
||||||
|
y1 = hotspotStream->readUint32LE();
|
||||||
|
x2 = hotspotStream->readUint32LE();
|
||||||
|
y2 = hotspotStream->readUint32LE();
|
||||||
|
index = add(new HotSpot(x1, y1, x2, y2), i == 0);
|
||||||
|
currentHotSpot = get(index);
|
||||||
|
feetX = hotspotStream->readUint32LE();
|
||||||
|
feetY = hotspotStream->readUint32LE();
|
||||||
|
currentHotSpot->setFeet(feetX, feetY);
|
||||||
|
currentHotSpot->setFacing((uint8)hotspotStream->readByte());
|
||||||
|
currentHotSpot->setActive(hotspotStream->readByte() != 0);
|
||||||
|
currentHotSpot->setCursor((uint8)hotspotStream->readByte());
|
||||||
|
hotspotStream->readByte(); // syntax (unused)
|
||||||
|
hotspotStream->readUint32LE(); // vocabID
|
||||||
|
hotspotStream->readUint32LE(); // verbID
|
||||||
|
strLength = hotspotStream->readUint32LE(); // vocabLength
|
||||||
|
hotspotStream->read(buffer, strLength); // vocab (the hotspot's name)
|
||||||
|
// Capitalize the hotspot's name here
|
||||||
|
str_upper(buffer);
|
||||||
|
currentHotSpot->setVocab(buffer);
|
||||||
|
// Verbs are used internally by the game scripts in Orion Burger
|
||||||
|
strLength = hotspotStream->readUint32LE(); // verbLength
|
||||||
|
hotspotStream->read(buffer, strLength); // verb
|
||||||
|
// Capitalize the hotspot's verb here
|
||||||
|
str_upper(buffer);
|
||||||
|
currentHotSpot->setVerb(buffer);
|
||||||
|
strLength = hotspotStream->readUint32LE(); // prepLength
|
||||||
|
hotspotStream->read(buffer, strLength); // prep
|
||||||
|
str_upper(buffer);
|
||||||
|
|
||||||
|
/* Hotspot names for non-English versions are stored in prep.
|
||||||
|
Prep can be set two ways: For English versions, copy the
|
||||||
|
text from vocab. For non-English versions, use the prep text
|
||||||
|
from the room file.
|
||||||
|
*/
|
||||||
|
if (strlen(buffer) > 0 && strcmp(buffer, "--") != 0 && strcmp(buffer, "ON") != 0)
|
||||||
|
currentHotSpot->setPrep(buffer);
|
||||||
|
else
|
||||||
|
currentHotSpot->setPrep(currentHotSpot->getVocab());
|
||||||
|
|
||||||
|
// The following values are not used at all by Orion Burger
|
||||||
|
strLength = hotspotStream->readUint32LE(); // spriteLength
|
||||||
|
hotspotStream->read(buffer, strLength); // sprite
|
||||||
|
hotspotStream->readUint16LE(); // sprite hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotSpotList::loadHotSpotsMads(Common::SeekableReadStream* hotspotStream, int hotspotCount) {
|
||||||
|
uint16 x1, y1, x2, y2;
|
||||||
|
HotSpot *currentHotSpot;
|
||||||
|
uint16 feetX, feetY;
|
||||||
|
uint16 index = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < hotspotCount; i++) {
|
||||||
|
x1 = hotspotStream->readUint16LE();
|
||||||
|
y1 = hotspotStream->readUint16LE();
|
||||||
|
x2 = hotspotStream->readUint16LE();
|
||||||
|
y2 = hotspotStream->readUint16LE();
|
||||||
|
index = add(new HotSpot(x1, y1, x2, y2), i == 0);
|
||||||
|
currentHotSpot = get(index);
|
||||||
|
//printf("x1, y1, x2, y2: %i %i %i %i\n", x1, y1, x2, y2);
|
||||||
|
feetX = hotspotStream->readUint16LE();
|
||||||
|
feetY = hotspotStream->readUint16LE();
|
||||||
|
currentHotSpot->setFeet(feetX, feetY);
|
||||||
|
currentHotSpot->setFacing((uint8)hotspotStream->readByte());
|
||||||
|
index = hotspotStream->readByte(); // unknown (initial facing?)
|
||||||
|
|
||||||
|
hotspotStream->readByte(); // unused (always 255)
|
||||||
|
|
||||||
|
index = hotspotStream->readByte(); // cursor
|
||||||
|
if (index == 0)
|
||||||
|
currentHotSpot->setCursor(0);
|
||||||
|
else
|
||||||
|
currentHotSpot->setCursor(index - 1);
|
||||||
|
|
||||||
|
// Rex Nebular doesn't have this field
|
||||||
|
if (_vm->getGameType() != GType_RexNebular) {
|
||||||
|
// This looks to be some sort of bitmask. Perhaps it signifies
|
||||||
|
// the valid verbs for this hotspot
|
||||||
|
index = hotspotStream->readUint16LE(); // unknown
|
||||||
|
//printf("%i ", index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = hotspotStream->readUint16LE(); // noun index
|
||||||
|
currentHotSpot->setVocabID(index);
|
||||||
|
currentHotSpot->setVocab(_vm->_globals->getVocab(index - 1));
|
||||||
|
index = hotspotStream->readUint16LE(); // verb index (default left click verb)
|
||||||
|
currentHotSpot->setVerbID(index);
|
||||||
|
if (index != 0) {
|
||||||
|
currentHotSpot->setVerb(_vm->_globals->getVocab(index - 1));
|
||||||
|
} else {
|
||||||
|
currentHotSpot->setVerb("");
|
||||||
|
}
|
||||||
|
//printf("%s ", currentHotSpot->getVerb());
|
||||||
|
//printf("%s ", currentHotSpot->getVocab());
|
||||||
|
//printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
120
engines/m4/hotspot.h
Normal file
120
engines/m4/hotspot.h
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_HOTSPOT_H
|
||||||
|
#define M4_HOTSPOT_H
|
||||||
|
|
||||||
|
#include "common/array.h"
|
||||||
|
#include "common/rect.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO:
|
||||||
|
- check if hotspot_duplicate is needed
|
||||||
|
NOTES:
|
||||||
|
- hotspot_add_dynamic unused in Orion Burger
|
||||||
|
*/
|
||||||
|
|
||||||
|
//???: should String be used instead of char* here?
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
class HotSpot {
|
||||||
|
friend class HotSpotList;//just for debugging, to be removed later
|
||||||
|
public:
|
||||||
|
|
||||||
|
HotSpot(int x1 = 0, int y1 = 0, int x2 = 0, int y2 = 0);
|
||||||
|
~HotSpot();
|
||||||
|
|
||||||
|
void setVocab(const char *value);
|
||||||
|
void setVocabID(int32 value) { _vocabID = value; }
|
||||||
|
void setVerb(const char *value);
|
||||||
|
void setVerbID(int32 value) { _verbID = value; }
|
||||||
|
void setPrep(const char *value);
|
||||||
|
void setSprite(const char *value);
|
||||||
|
void setActive(bool value) { _active = value; }
|
||||||
|
void setCursor(uint8 value) { _cursor = value; }
|
||||||
|
void setRect(int x1, int y1, int x2, int y2);
|
||||||
|
void setFeet(int x, int y);
|
||||||
|
void setFacing(uint8 facing) { _facing = facing; }
|
||||||
|
char *getVocab() const { return _vocab; }
|
||||||
|
int32 getVocabID() { return _vocabID; }
|
||||||
|
char *getVerb() const { return _verb; }
|
||||||
|
int32 getVerbID() { return _verbID; }
|
||||||
|
char *getPrep() const { return _prep; }
|
||||||
|
char *getSprite() const { return _sprite; }
|
||||||
|
uint8 getCursor() const { return _cursor; }
|
||||||
|
bool getActive() const { return _active; }
|
||||||
|
uint8 getFacing() const { return _facing; }
|
||||||
|
int getFeetX() { return _feetX; }
|
||||||
|
int getFeetY() { return _feetY; }
|
||||||
|
Common::Rect getRect() const;
|
||||||
|
|
||||||
|
int32 area() const { return (_rect.width() - 1) * (_rect.height() - 1); }
|
||||||
|
bool pointInside(int x, int y) { return _rect.contains(x, y); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
char *_vocab, *_verb, *_prep, *_sprite;
|
||||||
|
Common::Rect _rect;
|
||||||
|
bool _active;
|
||||||
|
int _feetX, _feetY;
|
||||||
|
uint8 _facing, _cursor;
|
||||||
|
|
||||||
|
// Unused in Orion Burger, used in MADS games
|
||||||
|
uint8 _syntax;
|
||||||
|
int32 _vocabID, _verbID;
|
||||||
|
//TODO: check if this is actually needed by the game
|
||||||
|
int16 _hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HotSpotList {
|
||||||
|
public:
|
||||||
|
HotSpotList();
|
||||||
|
~HotSpotList();
|
||||||
|
|
||||||
|
int add(HotSpot *hotspot, bool head = false);
|
||||||
|
HotSpot *get(int index) { return _hotspots[index]; }
|
||||||
|
void remove(HotSpot *hotspot);
|
||||||
|
void unlink(HotSpot *hotspot);
|
||||||
|
void clear();
|
||||||
|
HotSpot *findByXY(int x, int y);
|
||||||
|
void setActive(const char *name, bool active);
|
||||||
|
void setActiveXY(const char *name, int x, int y, bool active);
|
||||||
|
|
||||||
|
void dump();
|
||||||
|
|
||||||
|
void loadHotSpotsM4(Common::SeekableReadStream* hotspotStream, int hotspotCount);
|
||||||
|
void loadHotSpotsMads(Common::SeekableReadStream* hotspotStream, int hotspotCount);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef Common::Array<HotSpot*> HotSpotArray;
|
||||||
|
HotSpotArray _hotspots;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
555
engines/m4/m4.cpp
Normal file
555
engines/m4/m4.cpp
Normal file
|
@ -0,0 +1,555 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define SCRIPT_TEST
|
||||||
|
//#define INTRO_TEST
|
||||||
|
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/burger_data.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/resource.h"
|
||||||
|
#include "m4/sprite.h"
|
||||||
|
#include "m4/hotspot.h"
|
||||||
|
#include "m4/font.h"
|
||||||
|
#include "m4/rails.h"
|
||||||
|
#include "m4/midi.h"
|
||||||
|
#include "m4/events.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
#include "m4/gui.h"
|
||||||
|
#include "m4/woodscript.h"
|
||||||
|
#include "m4/actor.h"
|
||||||
|
#include "m4/sound.h"
|
||||||
|
#include "m4/rails.h"
|
||||||
|
#include "m4/script.h"
|
||||||
|
#include "m4/compression.h"
|
||||||
|
#include "m4/animation.h"
|
||||||
|
#include "m4/m4_menus.h"
|
||||||
|
#include "m4/m4_views.h"
|
||||||
|
#include "m4/mads_anim.h"
|
||||||
|
#include "m4/mads_menus.h"
|
||||||
|
|
||||||
|
#include "common/file.h"
|
||||||
|
#include "common/events.h"
|
||||||
|
#include "common/endian.h"
|
||||||
|
#include "common/system.h"
|
||||||
|
#include "common/config-manager.h"
|
||||||
|
#include "engines/engine.h"
|
||||||
|
#include "graphics/surface.h"
|
||||||
|
#include "sound/mididrv.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
// FIXME: remove global
|
||||||
|
M4Engine *_vm;
|
||||||
|
|
||||||
|
void escapeHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
|
||||||
|
// For now, simply exit the game
|
||||||
|
vm->_events->quitFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary hotkey handler for use in testing the TextviewView class
|
||||||
|
|
||||||
|
void textviewHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
|
||||||
|
// Deactivate the scene if it's currently active
|
||||||
|
View *sceneView = vm->_viewManager->getView(VIEWID_SCENE);
|
||||||
|
if (sceneView != NULL)
|
||||||
|
vm->_viewManager->deleteView(sceneView);
|
||||||
|
|
||||||
|
// Activate the textview view
|
||||||
|
vm->_font->setFont(FONT_CONVERSATION_MADS);
|
||||||
|
TextviewView *textView = new TextviewView(vm);
|
||||||
|
vm->_viewManager->addView(textView);
|
||||||
|
textView->setScript("quotes", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveGameHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
|
||||||
|
// TODO: See CreateF2SaveMenu - save menu should only be activated when
|
||||||
|
// certain conditions are met, such as player_commands_allowed, and isInterfaceVisible
|
||||||
|
vm->loadMenu(SAVE_MENU, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadGameHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
|
||||||
|
// TODO: See CreateF3LoadMenu - save menu should only be activated when
|
||||||
|
// certain conditions are met, such as player_commands_allowed, and isInterfaceVisible
|
||||||
|
vm->loadMenu(LOAD_MENU, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gameMenuHotkeyHandler(M4Engine *vm, View *view, uint32 key) {
|
||||||
|
vm->loadMenu(GAME_MENU);
|
||||||
|
}
|
||||||
|
|
||||||
|
M4Engine::M4Engine(OSystem *syst, const M4GameDescription *gameDesc) :
|
||||||
|
Engine(syst), _gameDescription(gameDesc) {
|
||||||
|
|
||||||
|
// FIXME
|
||||||
|
_vm = this;
|
||||||
|
|
||||||
|
Common::File::addDefaultDirectory(_gameDataPath);
|
||||||
|
Common::File::addDefaultDirectory("goodstuf");
|
||||||
|
Common::File::addDefaultDirectory("resource");
|
||||||
|
|
||||||
|
Common::addSpecialDebugLevel(kDebugScript, "script", "Script debug level");
|
||||||
|
Common::addSpecialDebugLevel(kDebugConversations, "conversations", "Conversations debugging");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
M4Engine::~M4Engine() {
|
||||||
|
delete _globals;
|
||||||
|
delete _midi;
|
||||||
|
delete _saveLoad;
|
||||||
|
delete _kernel;
|
||||||
|
delete _player;
|
||||||
|
delete _mouse;
|
||||||
|
delete _events;
|
||||||
|
delete _font;
|
||||||
|
delete _actor;
|
||||||
|
// delete _scene; // deleted by the viewmanager
|
||||||
|
delete _dialogs;
|
||||||
|
delete _screen;
|
||||||
|
delete _inventory;
|
||||||
|
delete _viewManager;
|
||||||
|
delete _rails;
|
||||||
|
delete _converse;
|
||||||
|
delete _script;
|
||||||
|
delete _ws;
|
||||||
|
delete _random;
|
||||||
|
delete _animation;
|
||||||
|
delete _palette;
|
||||||
|
delete _resourceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
int M4Engine::init() {
|
||||||
|
// Initialize backend
|
||||||
|
_system->beginGFXTransaction();
|
||||||
|
initCommonGFX(isM4());
|
||||||
|
if (isM4())
|
||||||
|
_system->initSize(640, 480);
|
||||||
|
else
|
||||||
|
_system->initSize(320, 200);
|
||||||
|
_system->endGFXTransaction();
|
||||||
|
|
||||||
|
_screen = new M4Surface(true); // Special form for creating screen reference
|
||||||
|
|
||||||
|
int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
|
||||||
|
bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
|
||||||
|
|
||||||
|
MidiDriver *driver = MidiDriver::createMidi(midiDriver);
|
||||||
|
if (native_mt32)
|
||||||
|
driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
|
||||||
|
|
||||||
|
_midi = new MidiPlayer(this, driver);
|
||||||
|
_midi->setGM(true);
|
||||||
|
_midi->setNativeMT32(native_mt32);
|
||||||
|
|
||||||
|
_globals = new Globals(this);
|
||||||
|
if (isM4())
|
||||||
|
_resourceManager = new M4ResourceManager(this);
|
||||||
|
else
|
||||||
|
_resourceManager = new MADSResourceManager(this);
|
||||||
|
_saveLoad = new SaveLoad(this);
|
||||||
|
_palette = new Palette(this);
|
||||||
|
_mouse = new Mouse(this);
|
||||||
|
_events = new Events(this);
|
||||||
|
_kernel = new Kernel(this);
|
||||||
|
_player = new Player(this);
|
||||||
|
_font = new Font(this);
|
||||||
|
if (getGameType() == GType_Burger) {
|
||||||
|
_actor = new Actor(this);
|
||||||
|
_interfaceView = new GameInterfaceView(this);
|
||||||
|
_conversationView = new ConversationView(this);
|
||||||
|
} else {
|
||||||
|
_actor = NULL;
|
||||||
|
}
|
||||||
|
_rails = new Rails(); // needs to be initialized before _scene
|
||||||
|
_scene = new Scene(this);
|
||||||
|
_dialogs = new Dialogs();
|
||||||
|
_viewManager = new ViewManager(this);
|
||||||
|
_inventory = new Inventory(this);
|
||||||
|
_sound = new Sound(this, _mixer, 255);
|
||||||
|
_converse = new Converse(this);
|
||||||
|
_script = new ScriptInterpreter(this);
|
||||||
|
_ws = new WoodScript(this);
|
||||||
|
_animation = new Animation(this);
|
||||||
|
//_callbacks = new Callbacks(this);
|
||||||
|
_random = new Common::RandomSource();
|
||||||
|
g_system->getEventManager()->registerRandomSource(*_random, "m4");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void M4Engine::eventHandler() {
|
||||||
|
M4EventType event;
|
||||||
|
uint32 keycode = 0;
|
||||||
|
|
||||||
|
if ((event = _events->handleEvents()) != MEVENT_NO_EVENT) {
|
||||||
|
if (_viewManager->viewCount() > 0)
|
||||||
|
_viewManager->handleMouseEvents(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_events->kbdCheck(keycode))
|
||||||
|
_viewManager->handleKeyboardEvents(keycode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool M4Engine::delay(int duration, bool keyAborts, bool clickAborts) {
|
||||||
|
uint32 endTime = g_system->getMillis() + duration;
|
||||||
|
M4EventType event;
|
||||||
|
uint32 keycode = 0;
|
||||||
|
|
||||||
|
while (!_events->quitFlag && (g_system->getMillis() < endTime)) {
|
||||||
|
event = _events->handleEvents();
|
||||||
|
if (clickAborts && (event == MEVENT_LEFT_RELEASE) || (event == MEVENT_RIGHT_RELEASE))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (_events->kbdCheck(keycode)) {
|
||||||
|
if (keyAborts)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_system->delayMillis(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void M4Engine::loadMenu(MenuType menuType, bool loadSaveFromHotkey, bool calledFromMainMenu) {
|
||||||
|
if (isM4() && (menuType != MAIN_MENU)) {
|
||||||
|
bool menuActive = _viewManager->getView(VIEWID_MENU) != NULL;
|
||||||
|
|
||||||
|
if (!menuActive)
|
||||||
|
_palette->fadeToGreen(M4_DIALOG_FADE_STEPS, M4_DIALOG_FADE_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
View *view;
|
||||||
|
|
||||||
|
switch (menuType) {
|
||||||
|
case MAIN_MENU:
|
||||||
|
if (getGameType() == GType_RexNebular)
|
||||||
|
view = new RexMainMenuView(this);
|
||||||
|
else if (getGameType() == GType_DragonSphere)
|
||||||
|
view = new DragonMainMenuView(this);
|
||||||
|
else
|
||||||
|
view = new MadsMainMenuView(this);
|
||||||
|
break;
|
||||||
|
case GAME_MENU:
|
||||||
|
view = new OrionMenuView(this, 200, 100, GAME_MENU, calledFromMainMenu, loadSaveFromHotkey);
|
||||||
|
break;
|
||||||
|
case OPTIONS_MENU:
|
||||||
|
view = new OrionMenuView(this, 172, 160, OPTIONS_MENU, calledFromMainMenu, loadSaveFromHotkey);
|
||||||
|
break;
|
||||||
|
case LOAD_MENU:
|
||||||
|
case SAVE_MENU:
|
||||||
|
view = new OrionMenuView(this, 145, 10, menuType, calledFromMainMenu, loadSaveFromHotkey);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error("Unknown menu type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewManager->addView(view);
|
||||||
|
_viewManager->moveToFront(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
int M4Engine::go() {
|
||||||
|
if (isM4())
|
||||||
|
return goM4();
|
||||||
|
else
|
||||||
|
return goMADS();
|
||||||
|
}
|
||||||
|
|
||||||
|
int M4Engine::goMADS() {
|
||||||
|
_palette->setMadsSystemPalette();
|
||||||
|
|
||||||
|
_mouse->init("cursor.ss", NULL);
|
||||||
|
_mouse->setCursorNum(0);
|
||||||
|
|
||||||
|
// Load MADS data files
|
||||||
|
_globals->loadMadsVocab(); // vocab.dat
|
||||||
|
_globals->loadMadsQuotes(); // quotes.dat
|
||||||
|
_globals->loadMadsMessagesInfo(); // messages.dat
|
||||||
|
// TODO: objects.dat
|
||||||
|
// TODO: hoganus.dat (what is it used for?)
|
||||||
|
|
||||||
|
// Test code to dump all messages to the console
|
||||||
|
//for (int i = 0; i < _globals->getMessagesSize(); i++)
|
||||||
|
//printf("%s\n----------\n", _globals->loadMessage(i));
|
||||||
|
|
||||||
|
if ((getGameType() == GType_RexNebular) || (getGameType() == GType_DragonSphere)) {
|
||||||
|
loadMenu(MAIN_MENU);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (getGameType() == GType_DragonSphere) {
|
||||||
|
_scene->loadScene(FIRST_SCENE);
|
||||||
|
} else if (getGameType() == GType_Phantom) {
|
||||||
|
//_scene->loadScene(FIRST_SCENE);
|
||||||
|
_scene->loadScene(106); // a more interesting scene
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewManager->addView(_scene);
|
||||||
|
|
||||||
|
_font->setFont(FONT_MAIN_MADS);
|
||||||
|
_font->setColors(2, 1, 3);
|
||||||
|
_font->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2);
|
||||||
|
_font->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2);
|
||||||
|
|
||||||
|
if (getGameType() == GType_DragonSphere) {
|
||||||
|
//_scene->showMADSV2TextBox("Test", 10, 10, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
_mouse->cursorOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewManager->systemHotkeys().add(Common::KEYCODE_ESCAPE, &escapeHotkeyHandler);
|
||||||
|
_viewManager->systemHotkeys().add(Common::KEYCODE_KP_MULTIPLY, &textviewHotkeyHandler);
|
||||||
|
|
||||||
|
// Load the general game SFX/voices
|
||||||
|
if (getGameType() == GType_RexNebular) {
|
||||||
|
_sound->loadDSRFile("rex009.dsr");
|
||||||
|
} else if (getGameType() == GType_Phantom) {
|
||||||
|
_sound->loadDSRFile("phan009.dsr");
|
||||||
|
} else if (getGameType() == GType_DragonSphere) {
|
||||||
|
_sound->loadDSRFile("drag009.dsr");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 nextFrame = g_system->getMillis();
|
||||||
|
while (!_events->quitFlag) {
|
||||||
|
eventHandler();
|
||||||
|
|
||||||
|
_animation->updateAnim();
|
||||||
|
|
||||||
|
// Call the updateState method of all views
|
||||||
|
_viewManager->updateState();
|
||||||
|
|
||||||
|
if (g_system->getMillis() >= nextFrame) {
|
||||||
|
|
||||||
|
_viewManager->refreshAll();
|
||||||
|
nextFrame = g_system->getMillis();// + GAME_FRAME_DELAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_system->delayMillis(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int M4Engine::goM4() {
|
||||||
|
|
||||||
|
_script->open("m4.dat");
|
||||||
|
|
||||||
|
#ifdef SCRIPT_TEST
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
ScriptFunction *func = _script->loadFunction("room_parser_142");
|
||||||
|
_script->runFunction(func);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
ScriptFunction *func = _script->loadFunction("room_daemon_951");
|
||||||
|
for (int i = 1; i < 58; i++) {
|
||||||
|
_vm->_kernel->trigger = i;
|
||||||
|
_script->runFunction(func);
|
||||||
|
printf("=================================\n");
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Set up the inventory
|
||||||
|
|
||||||
|
// Set up the game interface view
|
||||||
|
//_interfaceView->inventoryAdd("Money", "", 55); // Sample item
|
||||||
|
|
||||||
|
if (getGameType() == GType_Burger) {
|
||||||
|
for (int i = 0; i < ARRAYSIZE(burger_inventory); i++) {
|
||||||
|
char* itemName = strdup(burger_inventory[i].name);
|
||||||
|
_inventory->registerObject(itemName, burger_inventory[i].scene,
|
||||||
|
burger_inventory[i].icon);
|
||||||
|
_inventory->addToBackpack(i); // debug: this adds ALL objects to the player's backpack
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewManager->addView(_interfaceView);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show intro
|
||||||
|
|
||||||
|
if (getGameType() == GType_Burger)
|
||||||
|
_kernel->newRoom = TITLE_SCENE_BURGER;
|
||||||
|
else
|
||||||
|
_scene->getBackgroundSurface()->loadBackgroundRiddle("main menu");
|
||||||
|
|
||||||
|
_viewManager->addView(_scene);
|
||||||
|
|
||||||
|
// Setup game wide hotkeys. Note that Orion Burger used F2/F3 for Save/Restore,
|
||||||
|
// but for standardisation with most other games, F5/F7 are also mapped
|
||||||
|
|
||||||
|
_viewManager->systemHotkeys().add(Common::KEYCODE_ESCAPE, &escapeHotkeyHandler);
|
||||||
|
_viewManager->systemHotkeys().add(Common::KEYCODE_F2, &saveGameHotkeyHandler);
|
||||||
|
_viewManager->systemHotkeys().add(Common::KEYCODE_F3, &loadGameHotkeyHandler);
|
||||||
|
_viewManager->systemHotkeys().add(Common::KEYCODE_F5, &saveGameHotkeyHandler);
|
||||||
|
_viewManager->systemHotkeys().add(Common::KEYCODE_F7, &loadGameHotkeyHandler);
|
||||||
|
_viewManager->systemHotkeys().add(Common::KEYCODE_F9, &gameMenuHotkeyHandler);
|
||||||
|
|
||||||
|
// Start playing Orion Burger intro music
|
||||||
|
//_midi->playMusic("999intro", 255, false, -1, -1);
|
||||||
|
|
||||||
|
// TODO: start playing intro animations
|
||||||
|
|
||||||
|
// TODO: Master Lu
|
||||||
|
|
||||||
|
// Test for mouse
|
||||||
|
_mouse->init("cursor", NULL);
|
||||||
|
_mouse->setCursorNum(0);
|
||||||
|
_mouse->cursorOn();
|
||||||
|
|
||||||
|
_ws->assets()->loadAsset("SHOW SCRIPT", NULL);
|
||||||
|
_ws->assets()->loadAsset("STREAM SCRIPT", NULL);
|
||||||
|
|
||||||
|
#ifdef INTRO_TEST
|
||||||
|
int curPart = 0;
|
||||||
|
Machine *mach = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_ws->setSurfaceView(_scene);
|
||||||
|
|
||||||
|
uint32 nextFrame = g_system->getMillis();
|
||||||
|
while (!_events->quitFlag) {
|
||||||
|
|
||||||
|
// This should probably be moved to either Scene or Kernel
|
||||||
|
if (_kernel->currentRoom != _kernel->newRoom) {
|
||||||
|
|
||||||
|
_ws->clear();
|
||||||
|
|
||||||
|
_kernel->currentSection = _kernel->newRoom / 100;
|
||||||
|
_kernel->currentRoom = _kernel->newRoom;
|
||||||
|
|
||||||
|
_scene->loadScene(_kernel->currentRoom);
|
||||||
|
|
||||||
|
_ws->setBackgroundSurface(_scene->getBackgroundSurface());
|
||||||
|
_ws->setInverseColorTable(_scene->getInverseColorTable());
|
||||||
|
|
||||||
|
_kernel->loadSectionScriptFunctions();
|
||||||
|
_kernel->loadRoomScriptFunctions();
|
||||||
|
|
||||||
|
_kernel->roomInit();
|
||||||
|
|
||||||
|
#ifdef INTRO_TEST
|
||||||
|
if (_kernel->currentRoom == 951) {
|
||||||
|
curPart = 0;
|
||||||
|
mach = _ws->streamSeries("PLANET X HILLTOP A", 1, 0x1000, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
eventHandler();
|
||||||
|
|
||||||
|
// Call the updateState method of all views
|
||||||
|
_viewManager->updateState();
|
||||||
|
|
||||||
|
// Handle frame updates
|
||||||
|
if (g_system->getMillis() >= nextFrame) {
|
||||||
|
#ifdef INTRO_TEST
|
||||||
|
// Orion Burger intro test (scene 951)
|
||||||
|
// This is ugly and bad, machine is not deleted so there's a huge memory
|
||||||
|
// leak too. But hey, we can see some of the intro!
|
||||||
|
if (mach && mach->getState() == -1) {
|
||||||
|
if (curPart == 0)
|
||||||
|
mach = _ws->streamSeries("Planet X Low Ground Shot", 1, 0x1000, 0);
|
||||||
|
else if (curPart == 1)
|
||||||
|
mach = _ws->streamSeries("Planet X Hilltop B", 1, 0x1000, 0);
|
||||||
|
else if (curPart == 2)
|
||||||
|
mach = _ws->streamSeries("Space Station Panorama A", 1, 0x1000, 0);
|
||||||
|
else if (curPart == 3)
|
||||||
|
mach = _ws->streamSeries("Cargo Transfer Area A", 1, 0x1000, 0);
|
||||||
|
else if (curPart == 4)
|
||||||
|
mach = _ws->streamSeries("VP's Office A", 1, 0x1000, 0);
|
||||||
|
else if (curPart == 5)
|
||||||
|
mach = _ws->streamSeries("Hologram", 1, 0x1000, 0);
|
||||||
|
else if (curPart == 6)
|
||||||
|
mach = _ws->streamSeries("VP's Office B", 1, 0x1000, 0);
|
||||||
|
else if (curPart == 7)
|
||||||
|
mach = _ws->streamSeries("Cargo Transfer Area B", 1, 0x1000, 0);
|
||||||
|
else if (curPart == 8)
|
||||||
|
mach = _ws->streamSeries("Cargo Transfer Controls", 1, 0x1000, 0);
|
||||||
|
else if (curPart == 9)
|
||||||
|
mach = _ws->streamSeries("Space Station Panorama B", 1, 0x1000, 0);
|
||||||
|
// This last scene is from the rolling demo
|
||||||
|
//else if (curPart == 10)
|
||||||
|
// mach = _ws->streamSeries("Call To Action", 1, 0x1000, 0);
|
||||||
|
curPart++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
_ws->update();
|
||||||
|
_viewManager->refreshAll();
|
||||||
|
nextFrame = g_system->getMillis();// + GAME_FRAME_DELAY;
|
||||||
|
|
||||||
|
// TEST STUFF ONLY
|
||||||
|
if (_player->commandReady) {
|
||||||
|
_kernel->roomParser();
|
||||||
|
_player->commandReady = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
g_system->delayMillis(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void M4Engine::dumpFile(const char* filename, bool uncompress) {
|
||||||
|
Common::SeekableReadStream *fileS = res()->get(filename);
|
||||||
|
byte buffer[256];
|
||||||
|
FILE *destFile = fopen(filename, "wb");
|
||||||
|
int bytesRead = 0;
|
||||||
|
printf("Dumping %s, size: %i\n", filename, fileS->size());
|
||||||
|
|
||||||
|
if (!uncompress) {
|
||||||
|
while(!fileS->eos()) {
|
||||||
|
bytesRead = fileS->read(buffer, 256);
|
||||||
|
fwrite(buffer, bytesRead, 1, destFile);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MadsPack packData(fileS);
|
||||||
|
Common::MemoryReadStream *sourceUnc;
|
||||||
|
for (int i = 0; i < packData.getCount(); i++) {
|
||||||
|
sourceUnc = packData.getItemStream(i);
|
||||||
|
printf("Dumping compressed chunk %i of %i, size is %i\n", i + 1, packData.getCount(), sourceUnc->size());
|
||||||
|
while(!sourceUnc->eos()) {
|
||||||
|
bytesRead = sourceUnc->read(buffer, 256);
|
||||||
|
fwrite(buffer, bytesRead, 1, destFile);
|
||||||
|
}
|
||||||
|
delete sourceUnc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(destFile);
|
||||||
|
res()->toss(filename);
|
||||||
|
res()->purge();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
184
engines/m4/m4.h
Normal file
184
engines/m4/m4.h
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_H
|
||||||
|
#define M4_H
|
||||||
|
|
||||||
|
#include "common/scummsys.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
#include "engines/engine.h"
|
||||||
|
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/resource.h"
|
||||||
|
#include "m4/saveload.h"
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
#include "m4/gui.h"
|
||||||
|
#include "m4/events.h"
|
||||||
|
#include "m4/font.h"
|
||||||
|
#include "m4/scene.h"
|
||||||
|
#include "m4/actor.h"
|
||||||
|
#include "m4/sound.h"
|
||||||
|
#include "m4/rails.h"
|
||||||
|
#include "m4/converse.h"
|
||||||
|
#include "m4/animation.h"
|
||||||
|
|
||||||
|
//#define DUMP_SCRIPTS
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
class MidiPlayer;
|
||||||
|
class FileSystem;
|
||||||
|
class ResourceManager;
|
||||||
|
class Mouse;
|
||||||
|
class Events;
|
||||||
|
class Scene;
|
||||||
|
class ViewManager;
|
||||||
|
class View;
|
||||||
|
class Inventory;
|
||||||
|
class GameInterfaceView;
|
||||||
|
class ConversationView;
|
||||||
|
class Actor;
|
||||||
|
class Converse;
|
||||||
|
class ScriptInterpreter;
|
||||||
|
class WoodScript;
|
||||||
|
class Animation;
|
||||||
|
|
||||||
|
enum M4GameType {
|
||||||
|
GType_Riddle = 1,
|
||||||
|
GType_Burger,
|
||||||
|
GType_RexNebular,
|
||||||
|
GType_DragonSphere,
|
||||||
|
GType_Phantom
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Features {
|
||||||
|
kFeaturesNone = 0,
|
||||||
|
kFeaturesCD = 1 << 0,
|
||||||
|
kFeaturesDemo = 1 << 1
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kFileTypeHash,
|
||||||
|
kFileTypeHAG
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kDebugScript = 1 << 0,
|
||||||
|
kDebugConversations = 2 << 0
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MESSAGE_BASIC 1
|
||||||
|
#define MESSAGE_INTERMEDIATE 2
|
||||||
|
#define MESSAGE_DETAILED 3
|
||||||
|
|
||||||
|
struct M4GameDescription;
|
||||||
|
|
||||||
|
#define GAME_FRAME_DELAY 50
|
||||||
|
|
||||||
|
FORCEINLINE void str_lower(char *s) { while (*s) { *s = tolower(*s); s++; } }
|
||||||
|
FORCEINLINE void str_upper(char *s) { while (*s) { *s = toupper(*s); s++; } }
|
||||||
|
|
||||||
|
FORCEINLINE long FixedMul(long a, long b) { return (long)(((float)a * (float)b) / 65536.0); }
|
||||||
|
FORCEINLINE long FixedDiv(long a, long b) { return (long)(((float)a / (float)b) * 65536.0); }
|
||||||
|
|
||||||
|
class M4Engine : public Engine {
|
||||||
|
private:
|
||||||
|
int goMADS();
|
||||||
|
int goM4();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int init();
|
||||||
|
int go();
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
MidiPlayer *_midi;
|
||||||
|
|
||||||
|
public:
|
||||||
|
M4Engine(OSystem *syst, const M4GameDescription *gameDesc);
|
||||||
|
virtual ~M4Engine();
|
||||||
|
|
||||||
|
int getGameType() const;
|
||||||
|
uint32 getFeatures() const;
|
||||||
|
Common::Language getLanguage() const;
|
||||||
|
Common::Platform getPlatform() const;
|
||||||
|
bool isM4() const { return (getGameType() == GType_Riddle) || (getGameType() == GType_Burger); }
|
||||||
|
|
||||||
|
const char *getGameFile(int fileType);
|
||||||
|
Common::EventManager *eventMan() { return _eventMan; }
|
||||||
|
OSystem *system() { return _system; }
|
||||||
|
|
||||||
|
const M4GameDescription *_gameDescription;
|
||||||
|
|
||||||
|
ResourceManager *res() const { return _resourceManager; }
|
||||||
|
MidiPlayer *midi() { return _midi; }
|
||||||
|
Common::SaveFileManager *saveManager() { return _saveFileMan; }
|
||||||
|
void dumpFile(const char* filename, bool uncompress = false);
|
||||||
|
void eventHandler();
|
||||||
|
bool delay(int duration, bool keyAborts = true, bool clickAborts = true);
|
||||||
|
void loadMenu(MenuType menuType, bool loadSaveFromHotkey = false,
|
||||||
|
bool calledFromMainMenu = false);
|
||||||
|
|
||||||
|
// TODO: eventually these have to be removed
|
||||||
|
int32 seed;
|
||||||
|
void imath_seed(int32 seednum) { seed = seednum; }
|
||||||
|
uint32 imath_random() { return(seed = (25173*seed + 13849) & 0xffff); }
|
||||||
|
int32 imath_ranged_rand(int32 a, int32 b) { return (a + (((1 + ABS<int32>(b-a))*imath_random())>>16)); }
|
||||||
|
long imath_ranged_rand16(long a, long b) { return ((a + FixedMul(1+ABS<int32>(b-a),imath_random()))); }
|
||||||
|
//
|
||||||
|
|
||||||
|
ResourceManager *_resourceManager;
|
||||||
|
SaveLoad *_saveLoad;
|
||||||
|
ViewManager *_viewManager;
|
||||||
|
Palette *_palette;
|
||||||
|
Kernel *_kernel;
|
||||||
|
Globals *_globals;
|
||||||
|
Player *_player;
|
||||||
|
Mouse *_mouse;
|
||||||
|
Events *_events;
|
||||||
|
Font *_font;
|
||||||
|
Actor *_actor;
|
||||||
|
Scene *_scene;
|
||||||
|
Dialogs *_dialogs;
|
||||||
|
M4Surface *_screen;
|
||||||
|
Inventory *_inventory;
|
||||||
|
GameInterfaceView *_interfaceView;
|
||||||
|
ConversationView *_conversationView;
|
||||||
|
Sound *_sound;
|
||||||
|
Rails *_rails;
|
||||||
|
Converse *_converse;
|
||||||
|
ScriptInterpreter *_script;
|
||||||
|
WoodScript *_ws;
|
||||||
|
Animation *_animation;
|
||||||
|
Common::RandomSource *_random;
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: remove global
|
||||||
|
extern M4Engine *_vm;
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
727
engines/m4/m4_menus.cpp
Normal file
727
engines/m4/m4_menus.cpp
Normal file
|
@ -0,0 +1,727 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/algorithm.h" // for find()
|
||||||
|
#include "gui/dialog.h"
|
||||||
|
#include "gui/message.h"
|
||||||
|
|
||||||
|
#include "m4/m4_menus.h"
|
||||||
|
#include "m4/m4_views.h"
|
||||||
|
#include "m4/woodscript.h"
|
||||||
|
#include "m4/midi.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
const char *EmptySaveString = "<empty>";
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// Callback methods
|
||||||
|
//
|
||||||
|
// Following is a set of callback methods used to handle the execution
|
||||||
|
// of buttons in the various dialogs
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// General function which simply closes the active menu
|
||||||
|
|
||||||
|
void OrionCallbacks::closeMenuFn(DialogView *view, MenuObject *item) {
|
||||||
|
view->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::closeMenuFn(OrionMenuView *view) {
|
||||||
|
closeMenuFn(view, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Game menu functions */
|
||||||
|
|
||||||
|
void OrionCallbacks::gameOptionsMenuFn(DialogView *view, MenuObject *item) {
|
||||||
|
view->vm()->loadMenu(OPTIONS_MENU);
|
||||||
|
view->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::gameSaveGameFn(DialogView *view, MenuObject *item) {
|
||||||
|
view->vm()->loadMenu(SAVE_MENU);
|
||||||
|
view->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::gameLoadGameFn(DialogView *view, MenuObject *item) {
|
||||||
|
view->vm()->loadMenu(LOAD_MENU);
|
||||||
|
view->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::gameExitFn(DialogView *view, MenuObject *item) {
|
||||||
|
view->vm()->_events->quitFlag = true;
|
||||||
|
view->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Options menu */
|
||||||
|
|
||||||
|
void OrionCallbacks::optionsDigiSliderFn(DialogView *view, MenuObject *item) {
|
||||||
|
// Digi volume slider changed
|
||||||
|
int percent = ((MenuHorizSlider *) item)->percent();
|
||||||
|
|
||||||
|
view->vm()->_sound->setVolume(percent * 255 / 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::optionsMidiSliderFn(DialogView *view, MenuObject *item) {
|
||||||
|
// Midi volume slider changed
|
||||||
|
int percent = ((MenuHorizSlider *) item)->percent();
|
||||||
|
|
||||||
|
view->vm()->midi()->setVolume(percent * 255 / 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::optionsScrollingFn(DialogView *view, MenuObject *item) {
|
||||||
|
// TODO: Change current Digi volume settings here
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::optionsCancelFn(DialogView *view, MenuObject *item) {
|
||||||
|
// TODO: Reset original option settings here
|
||||||
|
OrionMenuView *vw = (OrionMenuView *) view;
|
||||||
|
|
||||||
|
vw->vm()->midi()->setVolume(vw->_originalMidiVolume);
|
||||||
|
|
||||||
|
vw->vm()->loadMenu(GAME_MENU);
|
||||||
|
vw->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::optionsDoneFn(DialogView *view, MenuObject *item) {
|
||||||
|
view->vm()->loadMenu(GAME_MENU);
|
||||||
|
view->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::optionsReturnFn(OrionMenuView *view) {
|
||||||
|
optionsDoneFn(view, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::optionsEscapeFn(OrionMenuView *view) {
|
||||||
|
optionsCancelFn(view, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save/Load dialog functions */
|
||||||
|
|
||||||
|
// Save the current game
|
||||||
|
|
||||||
|
void OrionCallbacks::saveLoadSaveFn(DialogView *view, MenuObject *item) {
|
||||||
|
if (view->_selectedSlot == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
MenuTextField *textItem = (MenuTextField *) view->getItem(SLTAG_TEXTFIELD);
|
||||||
|
if (!textItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
textItem->setState(OS_NORMAL);
|
||||||
|
|
||||||
|
// Save the game
|
||||||
|
bool succeeded = view->vm()->_saveLoad->save(view->_selectedSlot + 1, textItem->getText());
|
||||||
|
|
||||||
|
if (!succeeded) {
|
||||||
|
GUI::MessageDialog dialog("Save game failed!");
|
||||||
|
dialog.runModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the menu
|
||||||
|
closeMenuFn(view, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::saveLoadLoadFn(DialogView *view, MenuObject *item) {
|
||||||
|
// TODO: load the selected save game
|
||||||
|
closeMenuFn(view, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::saveLoadSlotFn(DialogView *view, MenuObject *item) {
|
||||||
|
OrionMenuView *vw = (OrionMenuView *) view;
|
||||||
|
MenuSaveLoadText *button = (MenuSaveLoadText *) item;
|
||||||
|
|
||||||
|
view->_selectedSlot = button->getIndex();
|
||||||
|
view->_deleteSaveDesc = true;
|
||||||
|
|
||||||
|
// Disable all the slots except the selected one
|
||||||
|
for (int index = 0; index < SL_NUM_VISIBLE_SLOTS; ++index) {
|
||||||
|
MenuSaveLoadText *currentItem = (MenuSaveLoadText *) view->getItem(SLTAG_SLOTS_START + index);
|
||||||
|
if (currentItem->getIndex() != button->getIndex()) {
|
||||||
|
currentItem->setState(OS_GREYED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a copy of the slot bounds
|
||||||
|
Common::Rect slotBounds = button->getBounds();
|
||||||
|
|
||||||
|
if (view->getMenuType() == SAVE_MENU) {
|
||||||
|
// Add in a text field for entry of the savegame name
|
||||||
|
vw->items().push_back(new MenuTextField(view, SLTAG_TEXTFIELD,
|
||||||
|
slotBounds.left, slotBounds.top, slotBounds.width(), slotBounds.height(), false,
|
||||||
|
saveLoadSaveFn, (button->getText() == EmptySaveString) ? NULL : button->getText(),
|
||||||
|
button->getIndex() + 1));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
vw->items().push_back(new MenuTextField(view, SLTAG_TEXTFIELD,
|
||||||
|
slotBounds.left, slotBounds.top, slotBounds.width(), slotBounds.height(), true,
|
||||||
|
saveLoadLoadFn, button->getText(), button->getIndex() + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide the existing slot
|
||||||
|
button->setVisible(false);
|
||||||
|
|
||||||
|
// Disable the slider
|
||||||
|
|
||||||
|
MenuVertSlider *slider = (MenuVertSlider *) view->getItem(SLTAG_VSLIDER);
|
||||||
|
slider->setState(OS_GREYED);
|
||||||
|
|
||||||
|
// Enable the save/load button
|
||||||
|
MenuButton *btn = (MenuButton *) view->getItem(SLTAG_SAVELOAD);
|
||||||
|
btn->setState(OS_NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::saveLoadCancelFn(DialogView *view, MenuObject *item) {
|
||||||
|
OrionMenuView *vw = (OrionMenuView *) view;
|
||||||
|
|
||||||
|
if (view->_selectedSlot != -1) {
|
||||||
|
// Pressed cancel with a save selected, so revert back to no selection
|
||||||
|
|
||||||
|
// Re-enable all the other slots
|
||||||
|
|
||||||
|
for (int index = 0; index < SL_NUM_VISIBLE_SLOTS; ++index) {
|
||||||
|
if (index != view->_selectedSlot) {
|
||||||
|
MenuSaveLoadText *currentItem = (MenuSaveLoadText *) view->getItem(SLTAG_SLOTS_START + index);
|
||||||
|
currentItem->setState(OS_NORMAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the previously hidden slot again
|
||||||
|
MenuSaveLoadText *slot = (MenuSaveLoadText *) view->getItem(SLTAG_SLOTS_START + view->_selectedSlot);
|
||||||
|
slot->setVisible(true);
|
||||||
|
slot->setState(OS_NORMAL);
|
||||||
|
|
||||||
|
// Remove the text selection
|
||||||
|
MenuTextField *textField = (MenuTextField *) view->getItem(SLTAG_TEXTFIELD);
|
||||||
|
delete textField;
|
||||||
|
vw->items().remove(textField);
|
||||||
|
|
||||||
|
// Set button enablement
|
||||||
|
MenuButton *btn = (MenuButton *) view->getItem(SLTAG_SAVELOAD);
|
||||||
|
btn->setState(OS_GREYED);
|
||||||
|
btn = (MenuButton *) view->getItem(SLTAG_CANCEL);
|
||||||
|
btn->setState(OS_NORMAL);
|
||||||
|
|
||||||
|
// Re-enable the slider
|
||||||
|
|
||||||
|
MenuVertSlider *slider = (MenuVertSlider *) view->getItem(SLTAG_VSLIDER);
|
||||||
|
slider->setState(OS_NORMAL);
|
||||||
|
|
||||||
|
view->_selectedSlot = -1;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Close the dialog
|
||||||
|
if (vw->_loadSaveFromHotkey)
|
||||||
|
// Since dialog was called from hotkey, return directly to the game
|
||||||
|
closeMenuFn(view, item);
|
||||||
|
else {
|
||||||
|
// Return to the game menu
|
||||||
|
view->vm()->loadMenu(GAME_MENU);
|
||||||
|
view->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::saveLoadSliderFn(DialogView *view, MenuObject *item) {
|
||||||
|
OrionMenuView *vw = (OrionMenuView *) view;
|
||||||
|
MenuVertSlider *slider = (MenuVertSlider *) item;
|
||||||
|
|
||||||
|
if (slider->sliderState() == VSLIDER_THUMBNAIL) {
|
||||||
|
// Callback generated by slider thumb, so set top slot using slider percentage
|
||||||
|
vw->setTopSaveSlot(slider->percent() * 89 / 100);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
int newIndex = view->_topSaveSlotIndex;
|
||||||
|
|
||||||
|
switch (slider->sliderState()) {
|
||||||
|
case VSLIDER_UP:
|
||||||
|
if (newIndex > 0)
|
||||||
|
--newIndex;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VSLIDER_PAGE_UP:
|
||||||
|
if (newIndex > 0)
|
||||||
|
newIndex = MAX(newIndex - 10, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VSLIDER_PAGE_DOWN:
|
||||||
|
if (newIndex < 89)
|
||||||
|
newIndex = MIN(newIndex + 10, 89);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VSLIDER_DOWN:
|
||||||
|
if (newIndex < 89)
|
||||||
|
++newIndex;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIndex != view->_topSaveSlotIndex) {
|
||||||
|
// Set the new top slot
|
||||||
|
vw->setTopSaveSlot(newIndex);
|
||||||
|
|
||||||
|
// Set the new slider position
|
||||||
|
slider->setPercentage(newIndex * 100 / 89);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::saveLoadEscapeFn(OrionMenuView *view) {
|
||||||
|
saveLoadCancelFn(view, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionCallbacks::saveLoadReturnFn(OrionMenuView *view) {
|
||||||
|
MenuTextField *textItem = (MenuTextField *) view->getItem(SLTAG_TEXTFIELD);
|
||||||
|
if (textItem) {
|
||||||
|
if (view->getMenuType() == SAVE_MENU)
|
||||||
|
saveLoadSaveFn(view, NULL);
|
||||||
|
else
|
||||||
|
saveLoadLoadFn(view, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
OrionMenuView::OrionMenuView(M4Engine *Vm, int x, int y, MenuType menuType, bool calledFromMainMenu,
|
||||||
|
bool loadSaveFromHotkey): DialogView(Vm, x, y, true) {
|
||||||
|
_menuType = menuType;
|
||||||
|
_screenType = VIEWID_MENU;
|
||||||
|
_screenFlags.layer = LAYER_MENU;
|
||||||
|
_screenFlags.get = SCREVENT_ALL;
|
||||||
|
_screenFlags.blocks = SCREVENT_ALL;
|
||||||
|
_screenFlags.immovable = true;
|
||||||
|
//_screenFlags.immovable = false; // uncomment to make menu movable
|
||||||
|
_coords.left = x;
|
||||||
|
_coords.top = y;
|
||||||
|
_currentItem = NULL;
|
||||||
|
_escapeHandler = &OrionCallbacks::closeMenuFn;
|
||||||
|
_returnHandler = NULL;
|
||||||
|
_saveNames = NULL;
|
||||||
|
_savegameThumbnail = NULL;
|
||||||
|
_deleteSaveDesc = false;
|
||||||
|
_closeFlag = false;
|
||||||
|
|
||||||
|
_calledFromMainMenu = calledFromMainMenu;
|
||||||
|
_loadSaveFromHotkey = loadSaveFromHotkey;
|
||||||
|
|
||||||
|
_interfaceWasVisible = _vm->_interfaceView->isVisible();
|
||||||
|
if (_interfaceWasVisible)
|
||||||
|
_vm->_interfaceView->hide();
|
||||||
|
|
||||||
|
_vm->_mouse->setCursorNum(CURSOR_ARROW);
|
||||||
|
|
||||||
|
switch (menuType) {
|
||||||
|
case GAME_MENU:
|
||||||
|
loadSprites(MENU_GAME);
|
||||||
|
|
||||||
|
// Add menu contents
|
||||||
|
_menuObjects.push_back(new MenuButton(this, BTNID_MAIN, 45, 53, 24, 24, &OrionCallbacks::closeMenuFn));
|
||||||
|
_menuObjects.push_back(new MenuButton(this, BTNID_OPTIONS, 45, 94, 24, 24, &OrionCallbacks::gameOptionsMenuFn));
|
||||||
|
_menuObjects.push_back(new MenuButton(this, BTNID_RESUME, 45, 135, 24, 24, &OrionCallbacks::closeMenuFn));
|
||||||
|
_menuObjects.push_back(new MenuButton(this, BTNID_QUIT, 141, 135, 24, 24, &OrionCallbacks::gameExitFn));
|
||||||
|
_menuObjects.push_back(new MenuButton(this, BTNID_SAVE, 141, 53, 24, 24, &OrionCallbacks::gameSaveGameFn, _calledFromMainMenu));
|
||||||
|
_menuObjects.push_back(new MenuButton(this, BTNID_LOAD, 141, 94, 24, 24, &OrionCallbacks::gameLoadGameFn,
|
||||||
|
!_vm->_saveLoad->hasSaves()));
|
||||||
|
|
||||||
|
_escapeHandler = &OrionCallbacks::closeMenuFn;
|
||||||
|
_returnHandler = &OrionCallbacks::closeMenuFn;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPTIONS_MENU:
|
||||||
|
loadSprites(MENU_OPTIONS);
|
||||||
|
|
||||||
|
// Store the original settings in case user aborts dialog
|
||||||
|
_originalMidiVolume = _vm->midi()->getVolume();
|
||||||
|
|
||||||
|
// Add menu contents
|
||||||
|
// TODO: Currently the Digi slider isn't hooked up to anything
|
||||||
|
_menuObjects.push_back(new MenuButton(this, OPTIONID_CANCEL, 93, 141, 74, 43,
|
||||||
|
&OrionCallbacks::optionsCancelFn, false, false, OBJTYPE_OM_CANCEL));
|
||||||
|
_menuObjects.push_back(new MenuButton(this, OPTIONID_DONE, 168, 141, 74, 43,
|
||||||
|
&OrionCallbacks::optionsDoneFn, false, false, OBJTYPE_OM_DONE));
|
||||||
|
_menuObjects.push_back(new MenuHorizSlider(this, OPTIONID_HSLIDER_MIDI, 47, 64, 212, 24,
|
||||||
|
_originalMidiVolume * 100 / 255, &OrionCallbacks::optionsMidiSliderFn, true));
|
||||||
|
_menuObjects.push_back(new MenuHorizSlider(this, OPTIONID_HSLIDER_DIGI, 47, 104, 212, 24,
|
||||||
|
0, &OrionCallbacks::optionsDigiSliderFn, true));
|
||||||
|
|
||||||
|
_escapeHandler = &OrionCallbacks::optionsEscapeFn;
|
||||||
|
_returnHandler = &OrionCallbacks::optionsReturnFn;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SAVE_MENU:
|
||||||
|
case LOAD_MENU:
|
||||||
|
loadSprites(MENU_SAVELOAD);
|
||||||
|
|
||||||
|
// Set up the defaults for the window
|
||||||
|
_topSaveSlotIndex = 0;
|
||||||
|
_selectedSlot = -1;
|
||||||
|
_highlightedSlot = -1;
|
||||||
|
_saveNames = _vm->_saveLoad->getSaves();
|
||||||
|
|
||||||
|
// Set up menu elements
|
||||||
|
_menuObjects.push_back(new MenuMessage(this, SLTAG_SAVELOAD_LABEL, 50, 241, 70, 16));
|
||||||
|
_menuObjects.push_back(new MenuButton(this, SLTAG_SAVELOAD, 214, 384, 72, 41,
|
||||||
|
(menuType == SAVE_MENU) ? &OrionCallbacks::saveLoadSaveFn : &OrionCallbacks::saveLoadLoadFn,
|
||||||
|
true, true, (menuType == SAVE_MENU) ? OBJTYPE_SL_SAVE : OBJTYPE_SL_LOAD));
|
||||||
|
_menuObjects.push_back(new MenuButton(this, SLTAG_CANCEL, 139, 384, 74, 43,
|
||||||
|
&OrionCallbacks::saveLoadCancelFn, false, false, OBJTYPE_SL_CANCEL));
|
||||||
|
_menuObjects.push_back(new MenuVertSlider(this, SLTAG_VSLIDER, 291, 255, 23, 127, 0,
|
||||||
|
&OrionCallbacks::saveLoadSliderFn));
|
||||||
|
|
||||||
|
if (_menuType == SAVE_MENU)
|
||||||
|
_savegameThumbnail = createThumbnail();
|
||||||
|
|
||||||
|
_menuObjects.push_back(new MenuImage(this, SLTAG_THUMBNAIL, 66, 28, 215, 162,
|
||||||
|
(_savegameThumbnail == NULL) ? _sprites->getFrame(SL_EMPTY_THUMBNAIL) : _savegameThumbnail));
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
SaveGameIterator slot = _saveNames->begin();
|
||||||
|
for (uint slotIndex = 0; slotIndex < SL_NUM_VISIBLE_SLOTS; ++slotIndex, ++slot) {
|
||||||
|
// Get save slot
|
||||||
|
bool isEmpty = (slotIndex >= _saveNames->size()) || (*slot).empty();
|
||||||
|
|
||||||
|
_menuObjects.push_back(new MenuSaveLoadText(this, SLTAG_SLOTS_START + slotIndex,
|
||||||
|
50, 256 + slotIndex * 15, 238, 15, &OrionCallbacks::saveLoadSlotFn,
|
||||||
|
(menuType == LOAD_MENU) && isEmpty, true, (menuType == LOAD_MENU),
|
||||||
|
isEmpty ? EmptySaveString : slot->c_str(), slotIndex + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_escapeHandler = &OrionCallbacks::saveLoadEscapeFn;
|
||||||
|
_returnHandler = &OrionCallbacks::saveLoadReturnFn;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error("Unknown menu type");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw all the items onto the background surface
|
||||||
|
for (MenuObjectsIterator i = _menuObjects.begin(); i != _menuObjects.end(); ++i)
|
||||||
|
(*i)->onRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
OrionMenuView::~OrionMenuView() {
|
||||||
|
delete _sprites;
|
||||||
|
|
||||||
|
for (MenuObjectList::iterator i = _menuObjects.begin(); i != _menuObjects.end(); ++i)
|
||||||
|
delete *i;
|
||||||
|
_menuObjects.clear();
|
||||||
|
|
||||||
|
if (_saveNames)
|
||||||
|
delete _saveNames;
|
||||||
|
if (_savegameThumbnail)
|
||||||
|
delete _savegameThumbnail;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OrionMenuView::loadSprites(const char *seriesName) {
|
||||||
|
Common::SeekableReadStream *data = _vm->res()->get(seriesName);
|
||||||
|
RGB8 *palette;
|
||||||
|
|
||||||
|
_sprites = new SpriteAsset(_vm, data, data->size(), seriesName);
|
||||||
|
palette = _sprites->getPalette();
|
||||||
|
_vm->_palette->setPalette(palette, 0, _sprites->getColorCount());
|
||||||
|
|
||||||
|
_vm->res()->toss(seriesName);
|
||||||
|
|
||||||
|
// Update the palette
|
||||||
|
//_vm->setPalette((byte *) _menuPalette, 59, 197);
|
||||||
|
|
||||||
|
// The first sprite is the menu background
|
||||||
|
|
||||||
|
M4Sprite *bg = _sprites->getFrame(0);
|
||||||
|
this->setSize(bg->width(), bg->height());
|
||||||
|
_coords.setWidth(bg->width());
|
||||||
|
_coords.setHeight(bg->height());
|
||||||
|
bg->copyTo(this);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a thumbnail based on the current background screen
|
||||||
|
|
||||||
|
M4Surface *OrionMenuView::createThumbnail() {
|
||||||
|
M4Surface srcSurface(_vm->_screen->width(), _vm->_screen->height());
|
||||||
|
M4Surface *result = new M4Surface(_vm->_screen->width() / 3, _vm->_screen->height() / 3);
|
||||||
|
|
||||||
|
// Translate the scene data
|
||||||
|
|
||||||
|
_vm->_scene->onRefresh(NULL, &srcSurface);
|
||||||
|
byte *srcP = (byte *)srcSurface.pixels;
|
||||||
|
byte *destP = (byte *)result->pixels;
|
||||||
|
|
||||||
|
for (int yCtr = 0; yCtr < _vm->_scene->height() / 3; ++yCtr, srcP += g_system->getWidth() * 3) {
|
||||||
|
byte *src0P = srcP;
|
||||||
|
byte *src1P = srcP + _vm->_screen->width();
|
||||||
|
byte *src2P = src1P + _vm->_screen->width();
|
||||||
|
|
||||||
|
for (int xCtr = 0; xCtr < result->width(); ++xCtr) {
|
||||||
|
*destP = (byte)((uint32)((
|
||||||
|
*src0P + *(src0P + 1) + *(src0P + 2) +
|
||||||
|
*src1P + *(src1P + 1) + *(src1P + 2) +
|
||||||
|
*src2P + *(src2P + 1) + *(src2P + 2)) / 9));
|
||||||
|
if (*destP == 0)
|
||||||
|
*destP = 21;
|
||||||
|
|
||||||
|
++destP;
|
||||||
|
src0P += 3;
|
||||||
|
src1P += 3;
|
||||||
|
src2P += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate the game interface view - since it's using standard colors that can't be
|
||||||
|
// averaged, simply take the top left pixel of every 3x3 pixel block
|
||||||
|
|
||||||
|
_vm->_interfaceView->onRefresh(NULL, &srcSurface);
|
||||||
|
destP = (byte *)result->pixels + (_vm->_screen->width() / 3) * (_vm->_interfaceView->bounds().top / 3);
|
||||||
|
|
||||||
|
int yStart = _vm->_interfaceView->bounds().top;
|
||||||
|
int yEnd = MIN(_vm->_screen->height() - 1, (int) _vm->_interfaceView->bounds().bottom - 1);
|
||||||
|
for (int yCtr = yStart; yCtr <= yEnd; yCtr += 3) {
|
||||||
|
srcP = (byte *)srcSurface.pixels + (yCtr * _vm->_screen->width());
|
||||||
|
|
||||||
|
for (int xCtr = 0; xCtr < result->width(); ++xCtr, srcP += 3)
|
||||||
|
*destP++ = *srcP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionMenuView::destroyView() {
|
||||||
|
M4Engine *engine = _vm;
|
||||||
|
bool interfaceVisible = _interfaceWasVisible;
|
||||||
|
engine->_viewManager->deleteView(this);
|
||||||
|
|
||||||
|
// Fade the game back in if no menu views are active (such as if a button was pressed in one menu
|
||||||
|
// to activate another menu dialog)
|
||||||
|
bool fadeIn = engine->_viewManager->getView(VIEWID_MENU) == NULL;
|
||||||
|
|
||||||
|
if (fadeIn) {
|
||||||
|
bool fadeToBlack = engine->_events->quitFlag;
|
||||||
|
engine->_ws->update();
|
||||||
|
engine->_palette->fadeFromGreen(M4_DIALOG_FADE_STEPS, M4_DIALOG_FADE_DELAY, fadeToBlack);
|
||||||
|
|
||||||
|
if (interfaceVisible)
|
||||||
|
engine->_interfaceView->show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OrionMenuView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
|
||||||
|
static Common::Point movingPos(0, 0);
|
||||||
|
static bool movingFlag = false;
|
||||||
|
|
||||||
|
bool handledFlag = false;
|
||||||
|
int localX, localY;
|
||||||
|
MenuObjectsIterator i;
|
||||||
|
|
||||||
|
if (!_screenFlags.visible)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!movingFlag)
|
||||||
|
captureEvents = false;
|
||||||
|
|
||||||
|
// If the escape key is pressed, then pass onto the Escape handler
|
||||||
|
|
||||||
|
if (eventType == KEVENT_KEY) {
|
||||||
|
if ((param == Common::KEYCODE_ESCAPE) && (_escapeHandler != NULL)) {
|
||||||
|
// Execute the Escape handler function
|
||||||
|
_currentItem = NULL;
|
||||||
|
captureEvents = false;
|
||||||
|
_escapeHandler(this);
|
||||||
|
destroyView();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((param == Common::KEYCODE_RETURN) || (param == Common::KEYCODE_KP_ENTER)) &&
|
||||||
|
(_returnHandler != NULL)) {
|
||||||
|
// Execute the Return handler function
|
||||||
|
_currentItem = NULL;
|
||||||
|
captureEvents = false;
|
||||||
|
_returnHandler(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuTextField *textItem = (MenuTextField *) getItem(SLTAG_TEXTFIELD);
|
||||||
|
if (textItem && textItem->onEvent(KEVENT_KEY, param, x, y, _currentItem))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the screen position to a relative position within the menu surface
|
||||||
|
localX = x - _coords.left;
|
||||||
|
localY = y - _coords.top;
|
||||||
|
|
||||||
|
// If there is an active object handling events, pass it on until it releases control
|
||||||
|
|
||||||
|
if (_currentItem) {
|
||||||
|
handledFlag = _currentItem->onEvent(eventType, param, localX, localY, _currentItem);
|
||||||
|
|
||||||
|
if (_closeFlag) {
|
||||||
|
// Dialog has been flagged to be closed
|
||||||
|
captureEvents = false;
|
||||||
|
destroyView();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentItem) {
|
||||||
|
captureEvents =
|
||||||
|
(Common::find(_menuObjects.begin(), _menuObjects.end(), _currentItem) != _menuObjects.end());
|
||||||
|
if (!captureEvents)
|
||||||
|
// The menu object is no longer active, so reset current item
|
||||||
|
_currentItem = NULL;
|
||||||
|
} else {
|
||||||
|
captureEvents = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handledFlag)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType == KEVENT_KEY) {
|
||||||
|
// Handle keypresses by looping through the item list to see if any of them want it
|
||||||
|
|
||||||
|
for (i = _menuObjects.begin(); (i != _menuObjects.end()) && !handledFlag; ++i) {
|
||||||
|
MenuObject *menuObj = *i;
|
||||||
|
MenuObject *dummyItem;
|
||||||
|
handledFlag = menuObj->onEvent(eventType, param, localX, localY, dummyItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
return handledFlag;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Handle mouse events by scanning the item list to see if the cursor is within any
|
||||||
|
|
||||||
|
for (i = _menuObjects.begin(); (i != _menuObjects.end()) && !handledFlag; ++i) {
|
||||||
|
MenuObject *menuObj = *i;
|
||||||
|
|
||||||
|
if (menuObj->isInside(localX, localY)) {
|
||||||
|
// Found an item, so pass it the event
|
||||||
|
menuObj->onEvent(eventType, param, localX, localY, _currentItem);
|
||||||
|
|
||||||
|
if (_closeFlag) {
|
||||||
|
// Dialog has been flagged to be closed
|
||||||
|
captureEvents = false;
|
||||||
|
destroyView();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentItem) {
|
||||||
|
captureEvents =
|
||||||
|
(Common::find(_menuObjects.begin(), _menuObjects.end(), _currentItem) != _menuObjects.end());
|
||||||
|
if (!captureEvents)
|
||||||
|
// The menu object is no longer active, so reset current item
|
||||||
|
_currentItem = NULL;
|
||||||
|
} else {
|
||||||
|
captureEvents = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// None of the items have handled the event, so fall back on menu-wide event handling
|
||||||
|
|
||||||
|
switch (eventType) {
|
||||||
|
case MEVENT_LEFT_CLICK:
|
||||||
|
case MEVENT_DOUBLECLICK:
|
||||||
|
if (!_screenFlags.immovable) {
|
||||||
|
// Move the entire dialog
|
||||||
|
captureEvents = true;
|
||||||
|
movingFlag = true;
|
||||||
|
movingPos.x = x;
|
||||||
|
movingPos.y = y;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MEVENT_LEFT_DRAG:
|
||||||
|
case MEVENT_DOUBLECLICK_DRAG:
|
||||||
|
if (movingFlag) {
|
||||||
|
moveRelative(x - movingPos.x, y - movingPos.y);
|
||||||
|
movingPos.x = x;
|
||||||
|
movingPos.y = y;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MEVENT_LEFT_RELEASE:
|
||||||
|
case MEVENT_DOUBLECLICK_RELEASE:
|
||||||
|
captureEvents = false;
|
||||||
|
movingFlag = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuObject *OrionMenuView::getItem(int objectId) {
|
||||||
|
MenuObjectsIterator i;
|
||||||
|
for (i = _menuObjects.begin(); i != _menuObjects.end(); ++i) {
|
||||||
|
MenuObject *obj = *i;
|
||||||
|
if (obj->getObjectId() == objectId)
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionMenuView::setTopSaveSlot(int slotNumber) {
|
||||||
|
_topSaveSlotIndex = MAX(MIN(slotNumber, 89), 0);
|
||||||
|
|
||||||
|
// Update the details of the load/save slots
|
||||||
|
|
||||||
|
// Get save slot
|
||||||
|
SaveGameIterator slot = _saveNames->begin();
|
||||||
|
for (int i = 0; i < _topSaveSlotIndex; ++i)
|
||||||
|
++slot;
|
||||||
|
|
||||||
|
for (uint index = 0; index < SL_NUM_VISIBLE_SLOTS; ++index, ++slot) {
|
||||||
|
MenuSaveLoadText *item = (MenuSaveLoadText *) getItem(SLTAG_SLOTS_START + index);
|
||||||
|
uint slotIndex = _topSaveSlotIndex + index;
|
||||||
|
|
||||||
|
bool isEmpty = (slotIndex >= _saveNames->size()) || slot->empty();
|
||||||
|
item->setDisplay(slotIndex + 1, isEmpty ? EmptySaveString : slot->c_str());
|
||||||
|
|
||||||
|
item->setState((_menuType == SAVE_MENU) || !isEmpty ? OS_NORMAL : OS_GREYED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OrionMenuView::refresh(const Common::Rect &areaRect) {
|
||||||
|
// Copy the selected portion of the background
|
||||||
|
_sprites->getFrame(0)->copyTo(this, areaRect, areaRect.left, areaRect.top);
|
||||||
|
|
||||||
|
for (MenuObjectsIterator i = _menuObjects.begin(); i != _menuObjects.end(); ++i) {
|
||||||
|
MenuObject *obj = *i;
|
||||||
|
if (obj->getBounds().intersects(areaRect))
|
||||||
|
obj->onRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
106
engines/m4/m4_menus.h
Normal file
106
engines/m4/m4_menus.h
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_M4_MENUS_H
|
||||||
|
#define M4_M4_MENUS_H
|
||||||
|
|
||||||
|
#include "common/list.h"
|
||||||
|
#include "common/ptr.h"
|
||||||
|
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/gui.h"
|
||||||
|
#include "m4/saveload.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define M4_DIALOG_FADE_STEPS 5
|
||||||
|
#define M4_DIALOG_FADE_DELAY 30
|
||||||
|
|
||||||
|
typedef Common::List<MenuObject *> MenuObjectList;
|
||||||
|
|
||||||
|
class OrionMenuView: public DialogView {
|
||||||
|
typedef MenuObjectList::iterator MenuObjectsIterator;
|
||||||
|
private:
|
||||||
|
MenuType _menuType;
|
||||||
|
SpriteAsset *_sprites;
|
||||||
|
MenuObjectList _menuObjects;
|
||||||
|
MenuObject *_currentItem;
|
||||||
|
typedef void (*Callback)(OrionMenuView *view);
|
||||||
|
OrionMenuView::Callback _escapeHandler, _returnHandler;
|
||||||
|
bool _closeFlag;
|
||||||
|
bool _calledFromMainMenu;
|
||||||
|
bool _interfaceWasVisible;
|
||||||
|
int _firstSlotIndex;
|
||||||
|
|
||||||
|
bool loadSprites(const char *seriesName);
|
||||||
|
M4Surface *createThumbnail();
|
||||||
|
void destroyView();
|
||||||
|
public:
|
||||||
|
OrionMenuView(M4Engine *vm, int x, int y, MenuType menuType, bool calledFromMainMenu,
|
||||||
|
bool loadSaveFromHotkey);
|
||||||
|
~OrionMenuView();
|
||||||
|
MenuType getMenuType() { return _menuType; }
|
||||||
|
SpriteAsset *sprites() { return _sprites; }
|
||||||
|
MenuObjectList &items() { return _menuObjects; }
|
||||||
|
MenuObject *getItem(int objectId);
|
||||||
|
void setTopSaveSlot(int slotNumber);
|
||||||
|
void refresh(const Common::Rect &areaRect);
|
||||||
|
void close() { _closeFlag = true; }
|
||||||
|
|
||||||
|
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
|
||||||
|
|
||||||
|
int _originalMidiVolume;
|
||||||
|
SaveGameList *_saveNames;
|
||||||
|
bool _loadSaveFromHotkey;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OrionCallbacks {
|
||||||
|
public:
|
||||||
|
static void closeMenuFn(DialogView *view, MenuObject *item);
|
||||||
|
static void closeMenuFn(OrionMenuView *view);
|
||||||
|
static void gameOptionsMenuFn(DialogView *view, MenuObject *item);
|
||||||
|
static void gameSaveGameFn(DialogView *view, MenuObject *item);
|
||||||
|
static void gameLoadGameFn(DialogView *view, MenuObject *item);
|
||||||
|
static void gameExitFn(DialogView *view, MenuObject *item);
|
||||||
|
static void optionsDigiSliderFn(DialogView *view, MenuObject *item);
|
||||||
|
static void optionsMidiSliderFn(DialogView *view, MenuObject *item);
|
||||||
|
static void optionsScrollingFn(DialogView *view, MenuObject *item);
|
||||||
|
static void optionsCancelFn(DialogView *view, MenuObject *item);
|
||||||
|
static void optionsDoneFn(DialogView *view, MenuObject *item);
|
||||||
|
static void optionsReturnFn(OrionMenuView *view);
|
||||||
|
static void optionsEscapeFn(OrionMenuView *view);
|
||||||
|
static void saveLoadSaveFn(DialogView *view, MenuObject *item);
|
||||||
|
static void saveLoadLoadFn(DialogView *view, MenuObject *item);
|
||||||
|
static void saveLoadSlotFn(DialogView *view, MenuObject *item);
|
||||||
|
static void saveLoadCancelFn(DialogView *view, MenuObject *item);
|
||||||
|
static void saveLoadSliderFn(DialogView *view, MenuObject *item);
|
||||||
|
static void saveLoadEscapeFn(OrionMenuView *view);
|
||||||
|
static void saveLoadReturnFn(OrionMenuView *view);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
345
engines/m4/m4_views.cpp
Normal file
345
engines/m4/m4_views.cpp
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/m4_views.h"
|
||||||
|
#include "m4/events.h"
|
||||||
|
#include "m4/font.h"
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
GUIInventory::GUIInventory(View *owner, M4Engine *vm, const Common::Rect &bounds, int horizCells,
|
||||||
|
int vertCells, int cellWidth, int cellHeight, int tag): GUIRect(owner, bounds, tag) {
|
||||||
|
|
||||||
|
_vm = vm;
|
||||||
|
_cellCount.x = horizCells;
|
||||||
|
_cellCount.y = vertCells;
|
||||||
|
_cellSize.x = cellWidth;
|
||||||
|
_cellSize.y = cellHeight;
|
||||||
|
|
||||||
|
// Validate the cell settings
|
||||||
|
if ((_cellCount.x * _cellSize.x > _bounds.width()) ||
|
||||||
|
(_cellCount.y * _cellSize.y > _bounds.height()))
|
||||||
|
error("Cell settings for inventory display exceeded control size");
|
||||||
|
|
||||||
|
_visible = true;
|
||||||
|
_scrollPosition = 0;
|
||||||
|
_scrollable = false;
|
||||||
|
_highlightedIndex = -1;
|
||||||
|
_selectedIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUIInventory::onRefresh() {
|
||||||
|
_parent->fillRect(_bounds, _vm->_palette->BLACK);
|
||||||
|
//_parent->frameRect(_bounds, _vm->_palette->LIGHT_GRAY);
|
||||||
|
|
||||||
|
if (_visible) {
|
||||||
|
//kernel_trigger_dispatch(kernel_trigger_create(TRIG_INV_CLICK));
|
||||||
|
|
||||||
|
_scrollable = false;
|
||||||
|
|
||||||
|
// Get to the starting inventory position for display
|
||||||
|
ItemsIterator i = _inventoryItems.begin();
|
||||||
|
int index = _scrollPosition;
|
||||||
|
while (index-- > 0) ++i;
|
||||||
|
|
||||||
|
// Loop through displaying entries
|
||||||
|
for (index = 0; (i != _inventoryItems.end()) && (index < _cellCount.x * _cellCount.y); ++index, ++i) {
|
||||||
|
GUIInventoryItem *item = (*i).get();
|
||||||
|
const Common::Point cellPos = getCellPosition(index);
|
||||||
|
/* Common::Rect cellBounds(_bounds.left + cellPos.x + xOffset,
|
||||||
|
_bounds.top + cellPos.y + yOffset,
|
||||||
|
_bounds.left + cellPos.x + xOffset + _cellSize.x,
|
||||||
|
_bounds.top + cellPos.y + _cellSize.y);*/
|
||||||
|
Common::Rect cellBounds(_bounds.left + cellPos.x, _bounds.top + cellPos.y,
|
||||||
|
_bounds.left + cellPos.x + _cellSize.x, _bounds.top + cellPos.y + _cellSize.y);
|
||||||
|
|
||||||
|
Common::Point iconPt(
|
||||||
|
cellBounds.left + (cellBounds.width() - item->icon->width()) / 2,
|
||||||
|
cellBounds.top + (cellBounds.height() - item->icon->height()) / 2);
|
||||||
|
|
||||||
|
item->icon->copyTo(_parent, iconPt.x, iconPt.y, 0);
|
||||||
|
|
||||||
|
if (_highlightedIndex == index)
|
||||||
|
_parent->frameRect(Common::Rect(iconPt.x - 2, iconPt.y - 2,
|
||||||
|
iconPt.x + item->icon->width() + 2, iconPt.y + item->icon->height() + 2),
|
||||||
|
_vm->_palette->LIGHT_GRAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GUIInventory::onEvent(M4EventType eventType, int param, int x, int y, GUIObject *¤tItem) {
|
||||||
|
bool result = false;
|
||||||
|
int overIndex = getInsideIndex(x, y);
|
||||||
|
bool isPressed = (eventType == MEVENT_LEFT_CLICK) || (eventType == MEVENT_LEFT_HOLD) ||
|
||||||
|
(eventType == MEVENT_LEFT_DRAG);
|
||||||
|
ItemsIterator curItem = _inventoryItems.begin();
|
||||||
|
|
||||||
|
if (isPressed) {
|
||||||
|
if (_selectedIndex == -1 && overIndex != -1) {
|
||||||
|
setHighlight(overIndex);
|
||||||
|
_selectedIndex = overIndex;
|
||||||
|
for (int i = 0; i < _scrollPosition + _selectedIndex; i++)
|
||||||
|
++curItem;
|
||||||
|
if (_scrollPosition + _selectedIndex < (int)_inventoryItems.size())
|
||||||
|
_vm->_mouse->setCursorNum(curItem->get()->iconIndex);
|
||||||
|
result = true;
|
||||||
|
} else {
|
||||||
|
// We are over something being tracked
|
||||||
|
if (_selectedIndex == overIndex) {
|
||||||
|
setHighlight(overIndex);
|
||||||
|
result = true;
|
||||||
|
} else {
|
||||||
|
// Otherwise reset highlighting
|
||||||
|
setHighlight(-1);
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No button pressed
|
||||||
|
if (_selectedIndex == overIndex) {
|
||||||
|
result = (_selectedIndex != -1);
|
||||||
|
} else {
|
||||||
|
result = (overIndex + _scrollPosition < (int)_inventoryItems.size());
|
||||||
|
if (result) {
|
||||||
|
for (int i = 0; i < overIndex + _scrollPosition; i++)
|
||||||
|
++curItem;
|
||||||
|
_vm->_interfaceView->setStatusText(curItem->get()->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop tracking anything
|
||||||
|
setHighlight(overIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUIInventory::add(const char *name, const char *verb, M4Surface *icon, int iconIndex) {
|
||||||
|
// First scan through the list to prevent duplicate objects
|
||||||
|
for (ItemsIterator i = _inventoryItems.begin(); i != _inventoryItems.end(); ++i) {
|
||||||
|
if (!strcmp(name, ((*i).get())->name))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_inventoryItems.push_back(InventoryList::value_type(new GUIInventoryItem(name, verb, icon, iconIndex)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GUIInventory::remove(const char *name) {
|
||||||
|
for (ItemsIterator i = _inventoryItems.begin(); i != _inventoryItems.end(); ++i) {
|
||||||
|
if (!strcmp(name, ((*i).get())->name)) {
|
||||||
|
_inventoryItems.erase(i);
|
||||||
|
_scrollPosition = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GUIInventory::getInsideIndex(int x, int y) {
|
||||||
|
if (!_bounds.contains(x, y))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int localX = x - _bounds.left;
|
||||||
|
int localY = y - _bounds.top;
|
||||||
|
return (localX / _cellSize.x) * _cellCount.y + (localY / _cellSize.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *GUIInventory::getSelectedObjectName() {
|
||||||
|
if (_selectedIndex != -1) {
|
||||||
|
ItemsIterator curItem = _inventoryItems.begin();
|
||||||
|
for (int i = 0; i < _selectedIndex; i++)
|
||||||
|
++curItem;
|
||||||
|
return curItem->get()->name;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Common::Point &GUIInventory::getCellPosition(int index) {
|
||||||
|
static Common::Point result;
|
||||||
|
|
||||||
|
if (_cellCount.x > _cellCount.y) {
|
||||||
|
// Horizontal orientation
|
||||||
|
result.x = (index / _cellCount.y) * _cellSize.x;
|
||||||
|
result.y = (index % _cellCount.y) * _cellSize.x;
|
||||||
|
} else {
|
||||||
|
// Vertical orientation
|
||||||
|
result.x = (index / _cellCount.x) * _cellSize.y;
|
||||||
|
result.y = (index % _cellCount.x) * _cellSize.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUIInventory::setHighlight(int index) {
|
||||||
|
if (_highlightedIndex == index)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_highlightedIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUIInventory::setScrollPosition(int value) {
|
||||||
|
if (value < 0)
|
||||||
|
return;
|
||||||
|
else if (value >= (int)_inventoryItems.size() - (_cellCount.x * _cellCount.y))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_scrollPosition = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const char *INTERFACE_SERIES = "999intr";
|
||||||
|
|
||||||
|
#define SPR(x) _sprites->getFrame(x)
|
||||||
|
|
||||||
|
GameInterfaceView::GameInterfaceView(M4Engine *vm):
|
||||||
|
View(vm, Common::Rect(0, vm->_screen->height() - INTERFACE_HEIGHT,
|
||||||
|
vm->_screen->width(), vm->_screen->height())),
|
||||||
|
_statusText(GUITextField(this, Common::Rect(200, 1, 450, 21))),
|
||||||
|
_inventory(GUIInventory(this, vm, Common::Rect(188, 22, 539, 97), 9, 1, 39, 75, 3)) {
|
||||||
|
|
||||||
|
_screenType = VIEWID_INTERFACE;
|
||||||
|
_screenFlags.layer = LAYER_INTERFACE;
|
||||||
|
_screenFlags.visible = false;
|
||||||
|
_screenFlags.get = SCREVENT_MOUSE;
|
||||||
|
_highlightedIndex = -1;
|
||||||
|
_selected = false;
|
||||||
|
|
||||||
|
Common::SeekableReadStream *data = _vm->res()->get(INTERFACE_SERIES);
|
||||||
|
RGB8 *palette;
|
||||||
|
|
||||||
|
_sprites = new SpriteAsset(_vm, data, data->size(), INTERFACE_SERIES);
|
||||||
|
palette = _sprites->getPalette();
|
||||||
|
|
||||||
|
//Palette.setPalette(palette, 0, _sprites->getColorCount());
|
||||||
|
|
||||||
|
_vm->res()->toss(INTERFACE_SERIES);
|
||||||
|
|
||||||
|
// Setup the interface buttons
|
||||||
|
|
||||||
|
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(15, 35, 47, 66), 0, SPR(0), SPR(1), SPR(2)))); // look
|
||||||
|
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(60, 35, 92, 66), 1, SPR(3), SPR(4), SPR(5)))); // take
|
||||||
|
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(105, 35, 137, 66), 2, SPR(6), SPR(7), SPR(8)))); // manipulate
|
||||||
|
|
||||||
|
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(580, 10, 620, 69), 3, SPR(69), SPR(70), SPR(71)))); // abduction
|
||||||
|
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(582, 70, 619, 105), 4, SPR(76), SPR(77), SPR(78)))); // menu
|
||||||
|
|
||||||
|
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(168, 22, 188, 97), 5, SPR(60), SPR(61), SPR(62)))); // Scroll left
|
||||||
|
_buttons.push_back(ButtonList::value_type(new GUIButton(this, Common::Rect(539, 22, 559, 97), 6, SPR(64), SPR(65), SPR(66)))); // Scroll right
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef SPR
|
||||||
|
|
||||||
|
GameInterfaceView::~GameInterfaceView() {
|
||||||
|
delete _sprites;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameInterfaceView::setHighlightedButton(int index) {
|
||||||
|
if (index == _highlightedIndex)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_selected = (index == -1);
|
||||||
|
_highlightedIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameInterfaceView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
|
||||||
|
static bool selectionFlag = false;
|
||||||
|
if (eventType == MEVENT_LEFT_RELEASE)
|
||||||
|
selectionFlag = false;
|
||||||
|
|
||||||
|
captureEvents = isInside(x, y);
|
||||||
|
if (!captureEvents)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int localX = x - _coords.left;
|
||||||
|
int localY = y - _coords.top;
|
||||||
|
GUIObject *currentItem;
|
||||||
|
|
||||||
|
_statusText.onEvent(eventType, param, localX, localY, currentItem);
|
||||||
|
_inventory.onEvent(eventType, param, localX, localY, currentItem);
|
||||||
|
|
||||||
|
if (_vm->_mouse->getCursorNum() != CURSOR_LOOK &&
|
||||||
|
_vm->_mouse->getCursorNum() != CURSOR_TAKE &&
|
||||||
|
_vm->_mouse->getCursorNum() != CURSOR_USE &&
|
||||||
|
_vm->_interfaceView->_inventory.getSelectedIndex() == -1) {
|
||||||
|
if (_vm->_mouse->getCursorNum() != 0)
|
||||||
|
_vm->_mouse->setCursorNum(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ButtonsIterator i = _buttons.begin(); i != _buttons.end(); ++i) {
|
||||||
|
GUIButton *btn = (*i).get();
|
||||||
|
btn->onEvent(eventType, param, localX, localY, currentItem);
|
||||||
|
if ((btn->getState() == BUTTON_PRESSED) && !selectionFlag) {
|
||||||
|
selectionFlag = true;
|
||||||
|
_highlightedIndex = btn->getTag();
|
||||||
|
|
||||||
|
switch (_highlightedIndex) {
|
||||||
|
case 0:
|
||||||
|
_vm->_mouse->setCursorNum(CURSOR_LOOK);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
_vm->_mouse->setCursorNum(CURSOR_TAKE);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
_vm->_mouse->setCursorNum(CURSOR_USE);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
// TODO: Jump to abduction
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
_vm->loadMenu(GAME_MENU);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
_inventory.scrollLeft();
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
_inventory.scrollRight();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) {
|
||||||
|
empty();
|
||||||
|
|
||||||
|
_statusText.onRefresh();
|
||||||
|
_inventory.onRefresh();
|
||||||
|
for (ButtonsIterator i = _buttons.begin(); i != _buttons.end(); ++i)
|
||||||
|
((*i).get())->onRefresh();
|
||||||
|
|
||||||
|
View::onRefresh(rects, destSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // End of namespace M4
|
118
engines/m4/m4_views.h
Normal file
118
engines/m4/m4_views.h
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_M4_VIEWS_H
|
||||||
|
#define M4_M4_VIEWS_H
|
||||||
|
|
||||||
|
#include "m4/gui.h"
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
#include "common/rect.h"
|
||||||
|
#include "common/list.h"
|
||||||
|
#include "common/ptr.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
class GUIInventoryItem {
|
||||||
|
public:
|
||||||
|
const char *name;
|
||||||
|
const char *verb;
|
||||||
|
M4Surface *icon;
|
||||||
|
int iconIndex;
|
||||||
|
|
||||||
|
GUIInventoryItem(const char *_name, const char *_verb, M4Surface *_icon, int _iconIndex) {
|
||||||
|
name = _name; _verb = verb; icon = _icon; iconIndex = _iconIndex;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GUIInventory: public GUIRect {
|
||||||
|
typedef Common::List<Common::SharedPtr<GUIInventoryItem> > InventoryList;
|
||||||
|
typedef InventoryList::iterator ItemsIterator;
|
||||||
|
private:
|
||||||
|
InventoryList _inventoryItems;
|
||||||
|
Common::Point _cellSize;
|
||||||
|
Common::Point _cellCount;
|
||||||
|
bool _visible;
|
||||||
|
bool _scrollable;
|
||||||
|
int _scrollPosition;
|
||||||
|
int _highlightedIndex;
|
||||||
|
int _selectedIndex;
|
||||||
|
M4Engine *_vm;
|
||||||
|
public:
|
||||||
|
GUIInventory(View *owner, M4Engine *vm, const Common::Rect &bounds,
|
||||||
|
int horizCells, int vertCells, int cellWidth, int cellHeight, int tag);
|
||||||
|
|
||||||
|
void onRefresh();
|
||||||
|
bool onEvent(M4EventType eventType, int param, int x, int y, GUIObject *¤tItem);
|
||||||
|
|
||||||
|
void add(const char *name, const char *verb, M4Surface *icon, int iconIndex);
|
||||||
|
bool remove(const char *name);
|
||||||
|
int getInsideIndex(int x, int y);
|
||||||
|
int getSelectedIndex() { return _selectedIndex; }
|
||||||
|
const char *getSelectedObjectName();
|
||||||
|
void clearSelected() {
|
||||||
|
_selectedIndex = -1;
|
||||||
|
setHighlight(-1);
|
||||||
|
}
|
||||||
|
const Common::Point &getCellPosition(int index);
|
||||||
|
void setHighlight(int index);
|
||||||
|
bool needLeftButton() { return _scrollPosition != 0; }
|
||||||
|
bool needRightButton() {
|
||||||
|
return (uint)(_inventoryItems.size() - _scrollPosition) > (uint)(_cellCount.x * _cellCount.y);
|
||||||
|
}
|
||||||
|
void setScrollPosition(int value);
|
||||||
|
void scrollLeft() { setScrollPosition(_scrollPosition - 1); }
|
||||||
|
void scrollRight() { setScrollPosition(_scrollPosition + 1); }
|
||||||
|
void setVisible(bool value) { _visible = value; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class GameInterfaceView: public View {
|
||||||
|
typedef Common::List<Common::SharedPtr<GUIButton> > ButtonList;
|
||||||
|
typedef ButtonList::iterator ButtonsIterator;
|
||||||
|
public:
|
||||||
|
SpriteAsset *_sprites;
|
||||||
|
ButtonList _buttons;
|
||||||
|
GUITextField _statusText;
|
||||||
|
GUIInventory _inventory;
|
||||||
|
int _highlightedIndex;
|
||||||
|
bool _selected;
|
||||||
|
private:
|
||||||
|
void setHighlightedButton(int index);
|
||||||
|
public:
|
||||||
|
GameInterfaceView(M4Engine *vm);
|
||||||
|
~GameInterfaceView();
|
||||||
|
|
||||||
|
void onRefresh(RectList *rects, M4Surface *destSurface);
|
||||||
|
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
|
||||||
|
void setStatusText(const char *text) { _statusText.setText(text); }
|
||||||
|
void cancelSentence() { setStatusText(NULL); }
|
||||||
|
void inventoryAdd(const char *name, const char *verb, int iconIndex) {
|
||||||
|
_inventory.add(name, verb, _sprites->getFrame(iconIndex), iconIndex);
|
||||||
|
}
|
||||||
|
bool inventoryRemove(const char *name) { return _inventory.remove(name); }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
705
engines/m4/mads_anim.cpp
Normal file
705
engines/m4/mads_anim.cpp
Normal file
|
@ -0,0 +1,705 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/mads_anim.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define TEXTVIEW_LINE_SPACING 2
|
||||||
|
#define TEXT_ANIMATION_DELAY 100
|
||||||
|
#define TV_NUM_FADE_STEPS 40
|
||||||
|
#define TV_FADE_DELAY_MILLI 50
|
||||||
|
|
||||||
|
TextviewView::TextviewView(M4Engine *vm):
|
||||||
|
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())),
|
||||||
|
_bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT),
|
||||||
|
_textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->getHeight() +
|
||||||
|
TEXTVIEW_LINE_SPACING) {
|
||||||
|
|
||||||
|
_screenType = VIEWID_TEXTVIEW;
|
||||||
|
_screenFlags.layer = LAYER_BACKGROUND;
|
||||||
|
_screenFlags.visible = true;
|
||||||
|
_screenFlags.get = SCREVENT_ALL;
|
||||||
|
_callback = NULL;
|
||||||
|
_script = NULL;
|
||||||
|
_spareScreen = NULL;
|
||||||
|
_bgCurrent = NULL;
|
||||||
|
_bgSpare = NULL;
|
||||||
|
reset();
|
||||||
|
|
||||||
|
// Set up system palette colors and the two colors for text display
|
||||||
|
_vm->_palette->setMadsSystemPalette();
|
||||||
|
RGB8 palData[3];
|
||||||
|
palData[0].r = palData[0].g = palData[0].b = 0;
|
||||||
|
palData[1].r = 0; palData[1].g = palData[1].b = 252;
|
||||||
|
palData[2].r = 0; palData[2].g = palData[2].b = 180;
|
||||||
|
_vm->_palette->setPalette(&palData[0], 4, 3);
|
||||||
|
_vm->_palette->blockRange(4, 3);
|
||||||
|
|
||||||
|
_vm->_font->setColors(5, 6, 4);
|
||||||
|
|
||||||
|
empty();
|
||||||
|
_bgSurface.empty();
|
||||||
|
_textSurface.empty();
|
||||||
|
|
||||||
|
int y = (height() - MADS_SURFACE_HEIGHT) / 2;
|
||||||
|
setColor(2);
|
||||||
|
hLine(0, width() - 1, y - 2);
|
||||||
|
hLine(0, width() - 1, height() - y + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextviewView::~TextviewView() {
|
||||||
|
if (_script)
|
||||||
|
_vm->res()->toss(_resourceName);
|
||||||
|
if (_spareScreen)
|
||||||
|
delete _spareScreen;
|
||||||
|
if (_bgCurrent)
|
||||||
|
delete _bgCurrent;
|
||||||
|
if (_bgSpare)
|
||||||
|
delete _bgSpare;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextviewView::reset() {
|
||||||
|
_bgSurface.empty();
|
||||||
|
_textSurface.empty();
|
||||||
|
_animating = false;
|
||||||
|
_panX = 0;
|
||||||
|
_panY = 0;
|
||||||
|
_panSpeed = 0;
|
||||||
|
_soundDriverLoaded = false;
|
||||||
|
Common::set_to(&_spareScreens[0], &_spareScreens[10], 0);
|
||||||
|
_scrollCount = 0;
|
||||||
|
_lineY = -1;
|
||||||
|
_scrollTimeout = 0;
|
||||||
|
_panCountdown = 0;
|
||||||
|
_processEvents = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextviewView::setScript(const char *resourceName, TextviewCallback callback) {
|
||||||
|
_callback = callback;
|
||||||
|
if (_script)
|
||||||
|
_vm->res()->toss(_resourceName);
|
||||||
|
if (_spareScreen) {
|
||||||
|
delete _spareScreen;
|
||||||
|
_spareScreen = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
strncpy(_resourceName, resourceName, 15);
|
||||||
|
_resourceName[15] = '\0';
|
||||||
|
if (!strchr(_resourceName, '.'))
|
||||||
|
strcat(_resourceName, ".txr");
|
||||||
|
|
||||||
|
_script = _vm->res()->get(_resourceName);
|
||||||
|
|
||||||
|
processLines();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextviewView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
|
||||||
|
if (!_processEvents)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Wait for the Escape key or a mouse press
|
||||||
|
if (((eventType == KEVENT_KEY) && (param == Common::KEYCODE_ESCAPE)) ||
|
||||||
|
(eventType == MEVENT_LEFT_RELEASE) || (eventType == MEVENT_RIGHT_RELEASE)) {
|
||||||
|
scriptDone();
|
||||||
|
captureEvents = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextviewView::updateState() {
|
||||||
|
if (!_animating)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Only update state if wait period has expired
|
||||||
|
uint32 currTime = g_system->getMillis();
|
||||||
|
|
||||||
|
// If a screen transition is in progress and it's time for another column, handle it
|
||||||
|
if (_spareScreen) {
|
||||||
|
byte *srcP = _spareScreen->getBasePtr(_translationX, 0);
|
||||||
|
byte *destP = _bgSurface.getBasePtr(_translationX, 0);
|
||||||
|
|
||||||
|
for (int y = 0; y < _bgSurface.height(); ++y, srcP += _spareScreen->width(),
|
||||||
|
destP += _bgSurface.width()) {
|
||||||
|
*destP = *srcP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (++_translationX >= _bgSurface.width()) {
|
||||||
|
// Surface transition is complete
|
||||||
|
delete _spareScreen;
|
||||||
|
_spareScreen = NULL;
|
||||||
|
|
||||||
|
_vm->_palette->deleteRange(_bgCurrent);
|
||||||
|
delete _bgCurrent;
|
||||||
|
_bgCurrent = _bgSpare;
|
||||||
|
_bgSpare = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure it's time for an update
|
||||||
|
if (currTime < _scrollTimeout)
|
||||||
|
return;
|
||||||
|
_scrollTimeout = g_system->getMillis() + TEXT_ANIMATION_DELAY;
|
||||||
|
|
||||||
|
// If any panning values are set, pan the background surface
|
||||||
|
if ((_panX != 0) || (_panY != 0)) {
|
||||||
|
if (_panCountdown > 0) {
|
||||||
|
--_panCountdown;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle horizontal panning
|
||||||
|
if (_panX != 0) {
|
||||||
|
byte *lineTemp = new byte[_panX];
|
||||||
|
for (int y = 0; y < _bgSurface.height(); ++y) {
|
||||||
|
byte *pixelsP = _bgSurface.getBasePtr(0, y);
|
||||||
|
|
||||||
|
// Copy the first X pixels into temp buffer, move the rest of the line
|
||||||
|
// to the start of the line, and then move temp buffer pixels to end of line
|
||||||
|
Common::copy(pixelsP, pixelsP + _panX, lineTemp);
|
||||||
|
Common::copy(pixelsP + _panX, pixelsP + _bgSurface.width(), pixelsP);
|
||||||
|
Common::copy(lineTemp, lineTemp + _panX, pixelsP + _bgSurface.width() - _panX);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] lineTemp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle vertical panning
|
||||||
|
if (_panY != 0) {
|
||||||
|
// Store the bottom Y lines into a temp buffer, move the rest of the lines down,
|
||||||
|
// and then copy the stored lines back to the top of the screen
|
||||||
|
byte *linesTemp = new byte[_panY * _bgSurface.width()];
|
||||||
|
byte *pixelsP = _bgSurface.getBasePtr(0, _bgSurface.height() - _panY);
|
||||||
|
Common::copy(pixelsP, pixelsP + _bgSurface.width() * _panY, linesTemp);
|
||||||
|
|
||||||
|
for (int y = _bgSurface.height() - 1; y >= _panY; --y) {
|
||||||
|
byte *destP = _bgSurface.getBasePtr(0, y);
|
||||||
|
byte *srcP = _bgSurface.getBasePtr(0, y - _panY);
|
||||||
|
Common::copy(srcP, srcP + _bgSurface.width(), destP);
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::copy(linesTemp, linesTemp + _panY * _bgSurface.width(), (byte *)_bgSurface.pixels);
|
||||||
|
delete[] linesTemp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scroll the text surface up by one row
|
||||||
|
byte *pixelsP = (byte *)_textSurface.pixels;
|
||||||
|
Common::copy(pixelsP + width(), pixelsP + _textSurface.width() * _textSurface.height(), pixelsP);
|
||||||
|
pixelsP = _textSurface.getBasePtr(0, _textSurface.height() - 1);
|
||||||
|
Common::set_to(pixelsP, pixelsP + _textSurface.width(), _vm->_palette->BLACK);
|
||||||
|
|
||||||
|
if (_scrollCount > 0) {
|
||||||
|
// Handling final scrolling of text off of screen
|
||||||
|
if (--_scrollCount == 0) {
|
||||||
|
scriptDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handling a text row
|
||||||
|
if (++_lineY == (_vm->_font->getHeight() + TEXTVIEW_LINE_SPACING))
|
||||||
|
processLines();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the view
|
||||||
|
int yp = (height() - _bgSurface.height()) / 2;
|
||||||
|
_bgSurface.copyTo(this, 0, yp);
|
||||||
|
_textSurface.copyTo(this, Common::Rect(0, 0, _textSurface.width(), _bgSurface.height()),
|
||||||
|
0, yp, _vm->_palette->BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextviewView::scriptDone() {
|
||||||
|
TextviewCallback fn = _callback;
|
||||||
|
M4Engine *vm = _vm;
|
||||||
|
|
||||||
|
// Remove this view from manager and destroy it
|
||||||
|
_vm->_viewManager->deleteView(this);
|
||||||
|
|
||||||
|
if (fn)
|
||||||
|
fn(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextviewView::processLines() {
|
||||||
|
if (_script->eos())
|
||||||
|
error("Attempted to read past end of response file");
|
||||||
|
|
||||||
|
while (!_script->eos()) {
|
||||||
|
_script->readLine(_currentLine, 79);
|
||||||
|
|
||||||
|
// Commented out line, so go loop for another
|
||||||
|
if (_currentLine[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Process the line
|
||||||
|
char *cStart = strchr(_currentLine, '[');
|
||||||
|
if (cStart) {
|
||||||
|
while (cStart) {
|
||||||
|
// Loop for possible multiple commands on one line
|
||||||
|
char *cEnd = strchr(_currentLine, ']');
|
||||||
|
if (!cEnd)
|
||||||
|
error("Unterminated command '%s' in response file", _currentLine);
|
||||||
|
|
||||||
|
*cEnd = '\0';
|
||||||
|
processCommand();
|
||||||
|
|
||||||
|
// Copy rest of line (if any) to start of buffer
|
||||||
|
strcpy(_currentLine, cEnd + 1);
|
||||||
|
|
||||||
|
cStart = strchr(_currentLine, '[');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentLine[0]) {
|
||||||
|
processText();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
processText();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextviewView::processCommand() {
|
||||||
|
char commandStr[80];
|
||||||
|
char *paramP;
|
||||||
|
strcpy(commandStr, _currentLine + 1);
|
||||||
|
str_upper(commandStr);
|
||||||
|
|
||||||
|
if (!strncmp(commandStr, "BACKGROUND", 10)) {
|
||||||
|
// Set the background
|
||||||
|
paramP = commandStr + 10;
|
||||||
|
int screenId = getParameter(¶mP);
|
||||||
|
_bgSurface.loadBackground(screenId, &_bgCurrent);
|
||||||
|
_vm->_palette->addRange(_bgCurrent);
|
||||||
|
_bgSurface.translate(_bgCurrent);
|
||||||
|
|
||||||
|
} else if (!strncmp(commandStr, "GO", 2)) {
|
||||||
|
_animating = true;
|
||||||
|
|
||||||
|
// Grab what the final palete will be
|
||||||
|
RGB8 destPalette[256];
|
||||||
|
_vm->_palette->grabPalette(destPalette, 0, 256);
|
||||||
|
|
||||||
|
// Copy the loaded background, if any, to the view surface
|
||||||
|
int yp = (height() - _bgSurface.height()) / 2;
|
||||||
|
_bgSurface.copyTo(this, 0, yp);
|
||||||
|
|
||||||
|
// Handle fade-in
|
||||||
|
_processEvents = false; // stop processing events during fade-in
|
||||||
|
_vm->_palette->fadeIn(TV_NUM_FADE_STEPS, TV_FADE_DELAY_MILLI, destPalette, 256);
|
||||||
|
_processEvents = true;
|
||||||
|
|
||||||
|
} else if (!strncmp(commandStr, "PAN", 3)) {
|
||||||
|
// Set panning values
|
||||||
|
paramP = commandStr + 3;
|
||||||
|
int panX = getParameter(¶mP);
|
||||||
|
int panY = getParameter(¶mP);
|
||||||
|
int panSpeed = getParameter(¶mP);
|
||||||
|
|
||||||
|
if ((panX != 0) || (panY != 0)) {
|
||||||
|
_panX = panX;
|
||||||
|
_panY = panY;
|
||||||
|
_panSpeed = panSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (!strncmp(commandStr, "DRIVER", 6)) {
|
||||||
|
// Set the driver to use
|
||||||
|
// TODO: Handling of the sound drivers
|
||||||
|
|
||||||
|
} else if (!strncmp(commandStr, "SOUND", 5)) {
|
||||||
|
// Set sound number
|
||||||
|
paramP = commandStr + 5;
|
||||||
|
//int soundId = getParameter(¶mP);
|
||||||
|
|
||||||
|
//TODO: Proper handling of the sound drivers/sounds
|
||||||
|
//if (!_soundDriverLoaded)
|
||||||
|
// error("Attempted to set sound without loading any driver\n");
|
||||||
|
|
||||||
|
} else if (!strncmp(commandStr, "COLOR", 5) && ((commandStr[5] == '0') || (commandStr[5] == '1'))) {
|
||||||
|
// Set the text colors
|
||||||
|
int index = commandStr[5] - '0';
|
||||||
|
paramP = commandStr + 6;
|
||||||
|
|
||||||
|
RGB8 palEntry;
|
||||||
|
palEntry.r = getParameter(¶mP) << 2;
|
||||||
|
palEntry.g = getParameter(¶mP) << 2;
|
||||||
|
palEntry.b = getParameter(¶mP) << 2;
|
||||||
|
_vm->_palette->setPalette(&palEntry, 5 + index, 1);
|
||||||
|
|
||||||
|
} else if (!strncmp(commandStr, "SPARE", 5)) {
|
||||||
|
// Sets a secondary background number that can be later switched in with a PAGE command
|
||||||
|
paramP = commandStr + 6;
|
||||||
|
int spareIndex = commandStr[5] - '0';
|
||||||
|
if ((spareIndex >= 0) && (spareIndex <= 9)) {
|
||||||
|
int screenId = getParameter(¶mP);
|
||||||
|
|
||||||
|
_spareScreens[spareIndex] = screenId;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (!strncmp(commandStr, "PAGE", 4)) {
|
||||||
|
// Signals to change to a previous specified secondary background
|
||||||
|
paramP = commandStr + 4;
|
||||||
|
int spareIndex = getParameter(¶mP);
|
||||||
|
|
||||||
|
// Only allow background switches if one isn't currently in progress
|
||||||
|
if (!_spareScreen && (_spareScreens[spareIndex] != 0)) {
|
||||||
|
_spareScreen = new M4Surface(width(), MADS_SURFACE_HEIGHT);
|
||||||
|
_spareScreen->loadBackground(_spareScreens[spareIndex], &_bgSpare);
|
||||||
|
_vm->_palette->addRange(_bgSpare);
|
||||||
|
_spareScreen->translate(_bgSpare);
|
||||||
|
|
||||||
|
_translationX = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
error("Unknown response command: '%s'", commandStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int TextviewView::getParameter(char **paramP) {
|
||||||
|
if ((**paramP != '=') && (**paramP != ','))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
++*paramP;
|
||||||
|
while ((**paramP >= '0') && (**paramP <= '9')) {
|
||||||
|
result = result * 10 + (**paramP - '0');
|
||||||
|
++*paramP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextviewView::processText() {
|
||||||
|
int lineWidth, xStart;
|
||||||
|
|
||||||
|
if (!strcmp(_currentLine, "***")) {
|
||||||
|
// Special signifier for end of script
|
||||||
|
_scrollCount = _vm->_font->getHeight() * 13;
|
||||||
|
_lineY = -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lineY = 0;
|
||||||
|
|
||||||
|
// Lines are always centered, except if line contains a '@', in which case the
|
||||||
|
// '@' marks the position that must be horizontally centered
|
||||||
|
char *centerP = strchr(_currentLine, '@');
|
||||||
|
if (centerP) {
|
||||||
|
*centerP = '\0';
|
||||||
|
xStart = (width() / 2) - _vm->_font->getWidth(_currentLine);
|
||||||
|
|
||||||
|
// Delete the @ character and shift back the remainder of the string
|
||||||
|
char *p = centerP + 1;
|
||||||
|
if (*p == ' ') ++p;
|
||||||
|
strcpy(centerP, p);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
lineWidth = _vm->_font->getWidth(_currentLine);
|
||||||
|
xStart = (width() - lineWidth) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the text line onto the bottom of the textSurface surface, which will allow it
|
||||||
|
// to gradually scroll onto the screen
|
||||||
|
int yp = _textSurface.height() - _vm->_font->getHeight() - TEXTVIEW_LINE_SPACING;
|
||||||
|
_textSurface.fillRect(Common::Rect(0, yp, _textSurface.width(), _textSurface.height()),
|
||||||
|
_vm->_palette->BLACK);
|
||||||
|
_vm->_font->writeString(&_textSurface, _currentLine, xStart, yp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
AnimviewView::AnimviewView(M4Engine *vm):
|
||||||
|
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())),
|
||||||
|
_bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT) {
|
||||||
|
|
||||||
|
_screenType = VIEWID_ANIMVIEW;
|
||||||
|
_screenFlags.layer = LAYER_BACKGROUND;
|
||||||
|
_screenFlags.visible = true;
|
||||||
|
_screenFlags.get = SCREVENT_ALL;
|
||||||
|
_callback = NULL;
|
||||||
|
_script = NULL;
|
||||||
|
_palData = NULL;
|
||||||
|
_previousUpdate = 0;
|
||||||
|
_transition = kTransitionNone;
|
||||||
|
reset();
|
||||||
|
|
||||||
|
// Set up system palette colors
|
||||||
|
_vm->_palette->setMadsSystemPalette();
|
||||||
|
|
||||||
|
empty();
|
||||||
|
_bgSurface.empty();
|
||||||
|
|
||||||
|
int y = (height() - MADS_SURFACE_HEIGHT) / 2;
|
||||||
|
setColor(2);
|
||||||
|
hLine(0, width() - 1, y - 2);
|
||||||
|
hLine(0, width() - 1, height() - y + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimviewView::~AnimviewView() {
|
||||||
|
if (_script)
|
||||||
|
_vm->res()->toss(_resourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimviewView::reset() {
|
||||||
|
_bgSurface.empty();
|
||||||
|
_soundDriverLoaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimviewView::setScript(const char *resourceName, AnimviewCallback callback) {
|
||||||
|
_callback = callback;
|
||||||
|
if (_script)
|
||||||
|
_vm->res()->toss(_resourceName);
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
strncpy(_resourceName, resourceName, 15);
|
||||||
|
_resourceName[15] = '\0';
|
||||||
|
if (!strchr(_resourceName, '.'))
|
||||||
|
strcat(_resourceName, ".res");
|
||||||
|
|
||||||
|
_script = _vm->res()->get(_resourceName);
|
||||||
|
|
||||||
|
processLines();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimviewView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
|
||||||
|
// Wait for the Escape key or a mouse press
|
||||||
|
if (((eventType == KEVENT_KEY) && (param == Common::KEYCODE_ESCAPE)) ||
|
||||||
|
(eventType == MEVENT_LEFT_RELEASE) || (eventType == MEVENT_RIGHT_RELEASE)) {
|
||||||
|
scriptDone();
|
||||||
|
captureEvents = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimviewView::updateState() {
|
||||||
|
char bgFile[10];
|
||||||
|
int bgNumber = 0;
|
||||||
|
|
||||||
|
// Only update state if wait period has expired
|
||||||
|
if (_previousUpdate > 0) {
|
||||||
|
if (g_system->getMillis() - _previousUpdate < 3000) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// time for an update
|
||||||
|
_previousUpdate = g_system->getMillis();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_previousUpdate = g_system->getMillis();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint32 currTime = g_system->getMillis();
|
||||||
|
|
||||||
|
strncpy(bgFile, _currentFile, 5);
|
||||||
|
bgFile[0] = bgFile[2];
|
||||||
|
bgFile[1] = bgFile[3];
|
||||||
|
bgFile[2] = bgFile[4];
|
||||||
|
bgFile[3] = '\0';
|
||||||
|
bgNumber = atoi(bgFile);
|
||||||
|
sprintf(bgFile, "rm%i.art", bgNumber);
|
||||||
|
|
||||||
|
// Not all scenes have a background. If there is one, refresh it
|
||||||
|
if (_vm->_resourceManager->resourceExists(bgFile)) {
|
||||||
|
if (_palData) {
|
||||||
|
_vm->_palette->deleteRange(_palData);
|
||||||
|
delete _palData;
|
||||||
|
}
|
||||||
|
_bgSurface.loadBackground(bgNumber, &_palData);
|
||||||
|
_vm->_palette->addRange(_palData);
|
||||||
|
_bgSurface.translate(_palData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab what the final palete will be
|
||||||
|
RGB8 destPalette[256];
|
||||||
|
_vm->_palette->grabPalette(destPalette, 0, 256);
|
||||||
|
|
||||||
|
// Handle scene transition
|
||||||
|
switch (_transition) {
|
||||||
|
case kTransitionNone:
|
||||||
|
// nothing to do
|
||||||
|
break;
|
||||||
|
case kTransitionFadeIn:
|
||||||
|
case kTransitionFadeIn2:
|
||||||
|
_vm->_palette->fadeIn(TV_NUM_FADE_STEPS, TV_FADE_DELAY_MILLI, destPalette, 256);
|
||||||
|
break;
|
||||||
|
case kTransitionBoxInBottomLeft:
|
||||||
|
case kTransitionBoxInBottomRight:
|
||||||
|
case kTransitionBoxInTopLeft:
|
||||||
|
case kTransitionBoxInTopRight:
|
||||||
|
// unused
|
||||||
|
warning("Unsupported box in scene effect");
|
||||||
|
break;
|
||||||
|
case kTransitionPanLeftToRight:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
case kTransitionPanRightToLeft:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
case kTransitionCircleIn:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// nothing to do
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the view
|
||||||
|
int yp = (height() - _bgSurface.height()) / 2;
|
||||||
|
_bgSurface.copyTo(this, 0, yp);
|
||||||
|
|
||||||
|
// Read next line
|
||||||
|
processLines();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimviewView::scriptDone() {
|
||||||
|
AnimviewCallback fn = _callback;
|
||||||
|
M4Engine *vm = _vm;
|
||||||
|
|
||||||
|
// Remove this view from manager and destroy it
|
||||||
|
_vm->_viewManager->deleteView(this);
|
||||||
|
|
||||||
|
if (fn)
|
||||||
|
fn(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimviewView::processLines() {
|
||||||
|
if (_script->eos()) {
|
||||||
|
// end of script, end animation
|
||||||
|
scriptDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!_script->eos()) {
|
||||||
|
_script->readLine(_currentLine, 79);
|
||||||
|
|
||||||
|
// Process the line
|
||||||
|
char *cStart = strchr(_currentLine, '-');
|
||||||
|
if (cStart) {
|
||||||
|
while (cStart) {
|
||||||
|
// Loop for possible multiple commands on one line
|
||||||
|
char *cEnd = strchr(_currentLine, ' ');
|
||||||
|
if (!cEnd)
|
||||||
|
error("Unterminated command '%s' in response file", _currentLine);
|
||||||
|
|
||||||
|
*cEnd = '\0';
|
||||||
|
processCommand();
|
||||||
|
|
||||||
|
// Copy rest of line (if any) to start of buffer
|
||||||
|
// Don't use strcpy() here, because if the
|
||||||
|
// rest of the line is the longer of the two
|
||||||
|
// strings, the memory areas will overlap.
|
||||||
|
memmove(_currentLine, cEnd + 1, strlen(cEnd + 1) + 1);
|
||||||
|
|
||||||
|
cStart = strchr(_currentLine, '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentLine[0]) {
|
||||||
|
sprintf(_currentFile, "%s", _currentLine);
|
||||||
|
//printf("File: %s\n", _currentLine);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
sprintf(_currentFile, "%s", _currentLine);
|
||||||
|
//printf("File: %s\n", _currentLine);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Switches are: (taken from the help of the original executable)
|
||||||
|
-b Toggle background load status off/on.
|
||||||
|
-c:char Specify sound card id letter.
|
||||||
|
-g Stay in graphics mode on exit.
|
||||||
|
-h[:ex] Disable EMS/XMS high memory support.
|
||||||
|
-i Switch sound interrupts mode off/on.
|
||||||
|
-j Wait for music to finish at end.
|
||||||
|
-k Keystroke jumps to end instead of abort.
|
||||||
|
-m Operate in non-MADS mode.
|
||||||
|
-o:xxx Specify opening special effect.
|
||||||
|
-p Switch MADS path mode to CONCAT.
|
||||||
|
-r[:abn] Resynch timer (always, beginning, never).
|
||||||
|
-s:file Specify sound file.
|
||||||
|
-u[:...] Use DMA speech [optional: addr,type,irq,drq].
|
||||||
|
-w Toggle white bars off/on.
|
||||||
|
-x Exit immediately after last frame.
|
||||||
|
-y Do not clear screen initially
|
||||||
|
-z Display statistics after run.
|
||||||
|
|
||||||
|
Opening special effects are:
|
||||||
|
0: no effect
|
||||||
|
1: fade in
|
||||||
|
2: fade in (looks to be the same as 1)
|
||||||
|
3: box in from bottom left (unused)
|
||||||
|
4: box in from bottom right (unused)
|
||||||
|
5: box in from top left (unused)
|
||||||
|
6: box in from top right (unused)
|
||||||
|
7: pan in from left to right
|
||||||
|
8: pan in from right to left
|
||||||
|
9: circle in (new scene appears in a circle that expands)
|
||||||
|
|
||||||
|
Animview is ran like this from the original games:
|
||||||
|
animview.exe @resfilename -c:P,220,20 -u:220,20,07,01 -p -a:mainmenu -p
|
||||||
|
|
||||||
|
Note that the first -p is necessary to watch the animation, otherwise
|
||||||
|
the program just exits
|
||||||
|
|
||||||
|
To watch an animation within the *.res file, just run animview like this:
|
||||||
|
animview.exe -x -r:b -o:2 animfilename -p
|
||||||
|
*/
|
||||||
|
void AnimviewView::processCommand() {
|
||||||
|
char commandStr[80];
|
||||||
|
strcpy(commandStr, _currentLine + 1);
|
||||||
|
str_upper(commandStr);
|
||||||
|
char *param = commandStr;
|
||||||
|
|
||||||
|
if (!strncmp(commandStr, "X", 1)) {
|
||||||
|
//printf("X ");
|
||||||
|
} else if (!strncmp(commandStr, "W", 1)) {
|
||||||
|
//printf("W ");
|
||||||
|
} else if (!strncmp(commandStr, "R", 1)) {
|
||||||
|
param = param + 2;
|
||||||
|
//printf("R:%s ", param);
|
||||||
|
} else if (!strncmp(commandStr, "O", 1)) {
|
||||||
|
param = param + 2;
|
||||||
|
//printf("O:%i ", atoi(param));
|
||||||
|
_transition = atoi(param);
|
||||||
|
} else {
|
||||||
|
error("Unknown response command: '%s'", commandStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
118
engines/m4/mads_anim.h
Normal file
118
engines/m4/mads_anim.h
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_MADS_ANIM_H
|
||||||
|
#define M4_MADS_ANIM_H
|
||||||
|
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
enum SceneTransition {
|
||||||
|
kTransitionNone = 0,
|
||||||
|
kTransitionFadeIn = 1,
|
||||||
|
kTransitionFadeIn2 = 2,
|
||||||
|
kTransitionBoxInBottomLeft = 3,
|
||||||
|
kTransitionBoxInBottomRight = 4,
|
||||||
|
kTransitionBoxInTopLeft = 5,
|
||||||
|
kTransitionBoxInTopRight = 6,
|
||||||
|
kTransitionPanLeftToRight = 7,
|
||||||
|
kTransitionPanRightToLeft = 8,
|
||||||
|
kTransitionCircleIn = 9
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*TextviewCallback)(M4Engine *vm);
|
||||||
|
|
||||||
|
class TextviewView: public View {
|
||||||
|
private:
|
||||||
|
bool _animating;
|
||||||
|
|
||||||
|
char _resourceName[80];
|
||||||
|
Common::SeekableReadStream *_script;
|
||||||
|
uint16 _spareScreens[10];
|
||||||
|
M4Surface *_spareScreen;
|
||||||
|
RGBList *_bgCurrent, *_bgSpare;
|
||||||
|
int _translationX;
|
||||||
|
int _panX, _panY, _panSpeed;
|
||||||
|
int _panCountdown;
|
||||||
|
char _currentLine[80];
|
||||||
|
uint32 _scrollTimeout;
|
||||||
|
int _scrollCount;
|
||||||
|
int _lineY;
|
||||||
|
M4Surface _bgSurface;
|
||||||
|
M4Surface _textSurface;
|
||||||
|
TextviewCallback _callback;
|
||||||
|
bool _soundDriverLoaded;
|
||||||
|
bool _processEvents;
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void processLines();
|
||||||
|
void processCommand();
|
||||||
|
void processText();
|
||||||
|
int getParameter(char **paramP);
|
||||||
|
public:
|
||||||
|
TextviewView(M4Engine *vm);
|
||||||
|
~TextviewView();
|
||||||
|
|
||||||
|
void setScript(const char *resourceName, TextviewCallback callback);
|
||||||
|
bool isAnimating() { return _animating; }
|
||||||
|
void scriptDone();
|
||||||
|
|
||||||
|
bool onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents);
|
||||||
|
void updateState();
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*AnimviewCallback)(M4Engine *vm);
|
||||||
|
|
||||||
|
class AnimviewView: public View {
|
||||||
|
private:
|
||||||
|
char _resourceName[80];
|
||||||
|
Common::SeekableReadStream *_script;
|
||||||
|
uint32 _previousUpdate;
|
||||||
|
char _currentLine[80];
|
||||||
|
char _currentFile[10];
|
||||||
|
M4Surface _bgSurface;
|
||||||
|
AnimviewCallback _callback;
|
||||||
|
bool _soundDriverLoaded;
|
||||||
|
RGBList *_palData;
|
||||||
|
int _transition;
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void processLines();
|
||||||
|
void processCommand();
|
||||||
|
public:
|
||||||
|
AnimviewView(M4Engine *vm);
|
||||||
|
~AnimviewView();
|
||||||
|
|
||||||
|
void setScript(const char *resourceName, AnimviewCallback callback);
|
||||||
|
void scriptDone();
|
||||||
|
|
||||||
|
bool onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents);
|
||||||
|
void updateState();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
586
engines/m4/mads_menus.cpp
Normal file
586
engines/m4/mads_menus.cpp
Normal file
|
@ -0,0 +1,586 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/mads_menus.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define REX_MENUSCREEN 990
|
||||||
|
#define PHANTOM_MENUSCREEN 920
|
||||||
|
#define DRAGON_MENUSCREEN 922
|
||||||
|
|
||||||
|
static Common::Point rexMenuItemPosList[6] = {
|
||||||
|
Common::Point(12, 68), Common::Point(12, 87), Common::Point(12, 107),
|
||||||
|
Common::Point(184, 75), Common::Point(245, 75), Common::Point(184, 99)
|
||||||
|
};
|
||||||
|
|
||||||
|
static Common::Point dragonMenuItemPosList[6] = {
|
||||||
|
Common::Point(46, 187), Common::Point(92, 187), Common::Point(138, 187),
|
||||||
|
Common::Point(184, 187), Common::Point(230, 187), Common::Point(276, 187)
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DRAGON_MENU_BUTTON_W = 45
|
||||||
|
#define DRAGON_MENU_BUTTON_H = 11
|
||||||
|
|
||||||
|
RexMainMenuView::RexMainMenuView(M4Engine *vm):
|
||||||
|
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())) {
|
||||||
|
|
||||||
|
_screenType = VIEWID_MAINMENU;
|
||||||
|
_screenFlags.get = SCREVENT_ALL;
|
||||||
|
|
||||||
|
_delayTimeout = 0;
|
||||||
|
_menuItem = NULL;
|
||||||
|
_menuItemIndex = 0;
|
||||||
|
_frameIndex = 0;
|
||||||
|
_highlightedIndex = -1;
|
||||||
|
_skipFlag = false;
|
||||||
|
|
||||||
|
// Load the background for the Rex Nebular game
|
||||||
|
_bgSurface = new M4Surface(width(), MADS_SURFACE_HEIGHT);
|
||||||
|
_bgSurface->loadBackground(REX_MENUSCREEN, &_bgPalData);
|
||||||
|
_vm->_palette->addRange(_bgPalData);
|
||||||
|
_bgSurface->translate(_bgPalData);
|
||||||
|
|
||||||
|
int row = (height() - MADS_SURFACE_HEIGHT) / 2;
|
||||||
|
_bgSurface->copyTo(this, 0, row);
|
||||||
|
|
||||||
|
// Add in the bounding lines for the background
|
||||||
|
setColor(2);
|
||||||
|
hLine(0, width() - 1, row - 1);
|
||||||
|
hLine(0, width() - 1, height() - row + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
RexMainMenuView::~RexMainMenuView() {
|
||||||
|
if (_menuItem)
|
||||||
|
delete _menuItem;
|
||||||
|
|
||||||
|
_vm->_palette->deleteRange(_bgPalData);
|
||||||
|
|
||||||
|
delete _bgPalData;
|
||||||
|
delete _bgSurface;
|
||||||
|
|
||||||
|
for (uint i = 0; i < _itemPalData.size(); ++i) {
|
||||||
|
_vm->_palette->deleteRange(_itemPalData[i]);
|
||||||
|
delete _itemPalData[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RexMainMenuView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
|
||||||
|
// Handle keypresses - these can be done at any time, even when the menu items are being drawn
|
||||||
|
if (eventType == KEVENT_KEY) {
|
||||||
|
switch (param) {
|
||||||
|
case Common::KEYCODE_ESCAPE:
|
||||||
|
case Common::KEYCODE_F6:
|
||||||
|
handleAction(EXIT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Common::KEYCODE_F1:
|
||||||
|
handleAction(START_GAME);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Common::KEYCODE_F2:
|
||||||
|
handleAction(RESUME_GAME);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Common::KEYCODE_F3:
|
||||||
|
handleAction(SHOW_INTRO);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Common::KEYCODE_F4:
|
||||||
|
handleAction(CREDITS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Common::KEYCODE_F5:
|
||||||
|
handleAction(QUOTES);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Common::KEYCODE_s:
|
||||||
|
// Goodness knows why, but Rex has a key to restart the menuitem animations
|
||||||
|
|
||||||
|
// Delete the current menu items
|
||||||
|
if (_menuItem)
|
||||||
|
delete _menuItem;
|
||||||
|
|
||||||
|
_vm->_palette->deleteRange(_bgPalData);
|
||||||
|
delete _bgPalData;
|
||||||
|
for (uint i = 0; i < _itemPalData.size(); ++i) {
|
||||||
|
_vm->_palette->deleteRange(_itemPalData[i]);
|
||||||
|
delete _itemPalData[i];
|
||||||
|
}
|
||||||
|
_itemPalData.clear();
|
||||||
|
|
||||||
|
// Reload the background surface, and restart the animation
|
||||||
|
_bgSurface->loadBackground(REX_MENUSCREEN, &_bgPalData);
|
||||||
|
_vm->_palette->addRange(_bgPalData);
|
||||||
|
_bgSurface->translate(_bgPalData);
|
||||||
|
|
||||||
|
_menuItemIndex = 0;
|
||||||
|
_skipFlag = false;
|
||||||
|
_menuItem = NULL;
|
||||||
|
_vm->_mouse->cursorOff();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Any other key skips the menu animation
|
||||||
|
_skipFlag = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int row = (height() - MADS_SURFACE_HEIGHT) / 2;
|
||||||
|
int menuIndex;
|
||||||
|
|
||||||
|
switch (eventType) {
|
||||||
|
case MEVENT_LEFT_CLICK:
|
||||||
|
case MEVENT_LEFT_DRAG:
|
||||||
|
if (_vm->_mouse->getCursorOn()) {
|
||||||
|
menuIndex = getHighlightedItem(x, y);
|
||||||
|
if (menuIndex != _highlightedIndex) {
|
||||||
|
_bgSurface->copyTo(this, 0, row);
|
||||||
|
|
||||||
|
_highlightedIndex = menuIndex;
|
||||||
|
if (_highlightedIndex != -1) {
|
||||||
|
M4Sprite *spr = _menuItem->getFrame(_highlightedIndex);
|
||||||
|
const Common::Point &pt = rexMenuItemPosList[_highlightedIndex];
|
||||||
|
spr->copyTo(this, pt.x, row + pt.y, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Skip the menu animation
|
||||||
|
_skipFlag = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MEVENT_LEFT_RELEASE:
|
||||||
|
if (_highlightedIndex != -1)
|
||||||
|
handleAction((MadsGameAction) _highlightedIndex);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RexMainMenuView::updateState() {
|
||||||
|
char resName[20];
|
||||||
|
Common::SeekableReadStream *data;
|
||||||
|
int row = (height() - MADS_SURFACE_HEIGHT) / 2;
|
||||||
|
int itemSize;
|
||||||
|
|
||||||
|
uint32 currTime = g_system->getMillis();
|
||||||
|
if (currTime < _delayTimeout)
|
||||||
|
return;
|
||||||
|
_delayTimeout = currTime + MADS_MENU_ANIM_DELAY;
|
||||||
|
|
||||||
|
// Rex Nebular handling to cycle through the animated display of the menu items
|
||||||
|
if (_menuItemIndex == 7)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If the user has chosen to skip the menu animation, show the menu immediately
|
||||||
|
if (_skipFlag && !_vm->_mouse->getCursorOn()) {
|
||||||
|
// Clear any pending animation
|
||||||
|
_bgSurface->copyTo(this, 0, row);
|
||||||
|
// Quickly loop through all the menuitems to display each's final frame
|
||||||
|
while (_menuItemIndex < 7) {
|
||||||
|
|
||||||
|
if (_menuItem) {
|
||||||
|
// Draw the final frame of the menuitem
|
||||||
|
M4Sprite *spr = _menuItem->getFrame(0);
|
||||||
|
itemSize = _menuItem->getFrame(0)->height();
|
||||||
|
spr->copyTo(this, rexMenuItemPosList[_menuItemIndex - 1].x,
|
||||||
|
rexMenuItemPosList[_menuItemIndex - 1].y + row + (itemSize / 2) - (spr->height() / 2), 0);
|
||||||
|
|
||||||
|
delete _menuItem;
|
||||||
|
copyTo(_bgSurface, Common::Rect(0, row, width(), row + MADS_SURFACE_HEIGHT), 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next sprite set
|
||||||
|
sprintf(resName, "RM%dA%d.SS", REX_MENUSCREEN, ++_menuItemIndex);
|
||||||
|
data = _vm->res()->get(resName);
|
||||||
|
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
|
||||||
|
_vm->res()->toss(resName);
|
||||||
|
|
||||||
|
// Slot it into available palette space
|
||||||
|
RGBList *palData = _menuItem->getRgbList();
|
||||||
|
_vm->_palette->addRange(palData);
|
||||||
|
_menuItem->translate(palData, true);
|
||||||
|
_itemPalData.push_back(palData);
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->_mouse->cursorOn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_menuItemIndex == 0) || (_frameIndex == 0)) {
|
||||||
|
// Get the next menu item
|
||||||
|
if (_menuItem) {
|
||||||
|
delete _menuItem;
|
||||||
|
|
||||||
|
// Copy over the current display surface area to the background, so the final frame
|
||||||
|
// of the previous menuitem should be kept on the screen
|
||||||
|
copyTo(_bgSurface, Common::Rect(0, row, width(), row + MADS_SURFACE_HEIGHT), 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the next menuitem resource
|
||||||
|
sprintf(resName, "RM%dA%d.SS", REX_MENUSCREEN, ++_menuItemIndex);
|
||||||
|
data = _vm->res()->get(resName);
|
||||||
|
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
|
||||||
|
_vm->res()->toss(resName);
|
||||||
|
|
||||||
|
// Slot it into available palette space
|
||||||
|
RGBList *palData = _menuItem->getRgbList();
|
||||||
|
_vm->_palette->addRange(palData);
|
||||||
|
_menuItem->translate(palData, true);
|
||||||
|
_itemPalData.push_back(palData);
|
||||||
|
|
||||||
|
_frameIndex = _menuItem->getCount() - 1;
|
||||||
|
|
||||||
|
// If the final resource is now loaded, which contains the highlighted versions of
|
||||||
|
// each menuitem, then the startup animation is complete
|
||||||
|
if (_menuItemIndex == 7) {
|
||||||
|
_vm->_mouse->cursorOn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
--_frameIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the next menuitem frame
|
||||||
|
|
||||||
|
itemSize = _menuItem->getFrame(0)->height();
|
||||||
|
|
||||||
|
_bgSurface->copyTo(this, 0, row);
|
||||||
|
M4Sprite *spr = _menuItem->getFrame(_frameIndex);
|
||||||
|
spr->copyTo(this, rexMenuItemPosList[_menuItemIndex - 1].x, rexMenuItemPosList[_menuItemIndex - 1].y +
|
||||||
|
row + (itemSize / 2) - (spr->height() / 2), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int RexMainMenuView::getHighlightedItem(int x, int y) {
|
||||||
|
y -= (height() - MADS_SURFACE_HEIGHT) / 2;
|
||||||
|
|
||||||
|
for (int index = 0; index < 6; ++index) {
|
||||||
|
const Common::Point &pt = rexMenuItemPosList[index];
|
||||||
|
M4Sprite *spr = _menuItem->getFrame(index);
|
||||||
|
|
||||||
|
if ((x >= pt.x) && (y >= pt.y) && (x < (pt.x + spr->width())) && (y < (pt.y + spr->height())))
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RexMainMenuView::handleAction(MadsGameAction action) {
|
||||||
|
M4Engine *vm = _vm;
|
||||||
|
vm->_mouse->cursorOff();
|
||||||
|
vm->_viewManager->deleteView(this);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case START_GAME:
|
||||||
|
case RESUME_GAME:
|
||||||
|
// Load a sample starting scene - note that, currently, calling loadScene automatically
|
||||||
|
// removes this menu screen from being displayed
|
||||||
|
vm->_mouse->cursorOn();
|
||||||
|
vm->_viewManager->addView(vm->_scene);
|
||||||
|
vm->_scene->loadScene(101);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SHOW_INTRO:
|
||||||
|
vm->_viewManager->showAnimView("@rexopen");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CREDITS:
|
||||||
|
vm->_viewManager->showTextView("credits");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case QUOTES:
|
||||||
|
vm->_viewManager->showTextView("quotes");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EXIT:
|
||||||
|
{
|
||||||
|
// When the Exit action is done from the menu, show one of two possible advertisements
|
||||||
|
|
||||||
|
// Activate the scene display with the specified scene
|
||||||
|
bool altAdvert = vm->_random->getRandomNumber(1000) >= 500;
|
||||||
|
vm->_scene->loadScene(altAdvert ? 995 : 996);
|
||||||
|
vm->_viewManager->addView(vm->_scene);
|
||||||
|
|
||||||
|
vm->_viewManager->refreshAll();
|
||||||
|
vm->delay(10000);
|
||||||
|
|
||||||
|
vm->_events->quitFlag = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
MadsMainMenuView::MadsMainMenuView(M4Engine *vm):
|
||||||
|
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MadsMainMenuView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MadsMainMenuView::updateState() {
|
||||||
|
// TODO: Implement me
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
DragonMainMenuView::DragonMainMenuView(M4Engine *vm):
|
||||||
|
View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())) {
|
||||||
|
|
||||||
|
_screenType = VIEWID_MAINMENU;
|
||||||
|
_screenFlags.get = SCREVENT_ALL;
|
||||||
|
|
||||||
|
_delayTimeout = 0;
|
||||||
|
_menuItem = NULL;
|
||||||
|
_menuItemIndex = 0;
|
||||||
|
_frameIndex = 0;
|
||||||
|
_highlightedIndex = -1;
|
||||||
|
_skipFlag = false;
|
||||||
|
|
||||||
|
// Load the background for the Dragonsphere game
|
||||||
|
this->loadBackground(942, &_bgPalData);
|
||||||
|
_vm->_palette->addRange(_bgPalData);
|
||||||
|
this->translate(_bgPalData);
|
||||||
|
}
|
||||||
|
|
||||||
|
DragonMainMenuView::~DragonMainMenuView() {
|
||||||
|
//if (_menuItem)
|
||||||
|
// delete _menuItem;
|
||||||
|
|
||||||
|
_vm->_palette->deleteRange(_bgPalData);
|
||||||
|
|
||||||
|
delete _bgPalData;
|
||||||
|
|
||||||
|
for (uint i = 0; i < _itemPalData.size(); ++i) {
|
||||||
|
_vm->_palette->deleteRange(_itemPalData[i]);
|
||||||
|
delete _itemPalData[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DragonMainMenuView::onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) {
|
||||||
|
char resName[20];
|
||||||
|
Common::SeekableReadStream *data;
|
||||||
|
|
||||||
|
// Handle keypresses - these can be done at any time, even when the menu items are being drawn
|
||||||
|
if (eventType == KEVENT_KEY) {
|
||||||
|
switch (param) {
|
||||||
|
case Common::KEYCODE_ESCAPE:
|
||||||
|
case Common::KEYCODE_F6:
|
||||||
|
handleAction(EXIT);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Common::KEYCODE_F1:
|
||||||
|
handleAction(START_GAME);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Common::KEYCODE_F2:
|
||||||
|
handleAction(RESUME_GAME);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Common::KEYCODE_F3:
|
||||||
|
handleAction(SHOW_INTRO);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Common::KEYCODE_F4:
|
||||||
|
handleAction(CREDITS);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Any other key skips the menu animation
|
||||||
|
_skipFlag = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int menuIndex;
|
||||||
|
|
||||||
|
switch (eventType) {
|
||||||
|
case MEVENT_LEFT_CLICK:
|
||||||
|
case MEVENT_LEFT_DRAG:
|
||||||
|
if (_vm->_mouse->getCursorOn()) {
|
||||||
|
menuIndex = getHighlightedItem(x, y);
|
||||||
|
if (menuIndex != _highlightedIndex) {
|
||||||
|
|
||||||
|
_highlightedIndex = menuIndex;
|
||||||
|
if (_highlightedIndex != -1) {
|
||||||
|
sprintf(resName, "MAIN%d.SS", menuIndex);
|
||||||
|
data = _vm->res()->get(resName);
|
||||||
|
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
|
||||||
|
_vm->res()->toss(resName);
|
||||||
|
|
||||||
|
M4Sprite *spr = _menuItem->getFrame(1);
|
||||||
|
spr->copyTo(this, spr->xOffset - 25, spr->yOffset - spr->height());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Skip the menu animation
|
||||||
|
_skipFlag = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MEVENT_LEFT_RELEASE:
|
||||||
|
if (_highlightedIndex != -1)
|
||||||
|
handleAction((MadsGameAction) _highlightedIndex);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DragonMainMenuView::updateState() {
|
||||||
|
char resName[20];
|
||||||
|
Common::SeekableReadStream *data;
|
||||||
|
RGBList *palData;
|
||||||
|
M4Sprite *spr;
|
||||||
|
|
||||||
|
if (_menuItemIndex == 6)
|
||||||
|
return;
|
||||||
|
|
||||||
|
while (_menuItemIndex < 6) {
|
||||||
|
sprintf(resName, "MAIN%d.SS", _menuItemIndex);
|
||||||
|
data = _vm->res()->get(resName);
|
||||||
|
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
|
||||||
|
_vm->res()->toss(resName);
|
||||||
|
|
||||||
|
// Slot it into available palette space
|
||||||
|
palData = _menuItem->getRgbList();
|
||||||
|
_vm->_palette->addRange(palData);
|
||||||
|
_menuItem->translate(palData, true);
|
||||||
|
_itemPalData.push_back(palData);
|
||||||
|
|
||||||
|
spr = _menuItem->getFrame(0);
|
||||||
|
spr->copyTo(this, spr->xOffset - 25, spr->yOffset - spr->height());
|
||||||
|
|
||||||
|
if (_menuItemIndex != 5)
|
||||||
|
delete _menuItem;
|
||||||
|
_menuItemIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sphere
|
||||||
|
sprintf(resName, "RM920X0.SS");
|
||||||
|
data = _vm->res()->get(resName);
|
||||||
|
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
|
||||||
|
_vm->res()->toss(resName);
|
||||||
|
|
||||||
|
// Slot it into available palette space
|
||||||
|
palData = _menuItem->getRgbList();
|
||||||
|
_vm->_palette->addRange(palData);
|
||||||
|
_menuItem->translate(palData, true);
|
||||||
|
_itemPalData.push_back(palData);
|
||||||
|
|
||||||
|
spr = _menuItem->getFrame(0); // empty sphere
|
||||||
|
spr->copyTo(this, spr->xOffset - 75, spr->yOffset - spr->height());
|
||||||
|
spr = _menuItem->getFrame(1); // dragon inside sphere
|
||||||
|
spr->copyTo(this, spr->xOffset - 75, spr->yOffset - spr->height());
|
||||||
|
|
||||||
|
// Dragonsphere letters
|
||||||
|
sprintf(resName, "RM920X3.SS");
|
||||||
|
data = _vm->res()->get(resName);
|
||||||
|
_menuItem = new SpriteAsset(_vm, data, data->size(), resName);
|
||||||
|
_vm->res()->toss(resName);
|
||||||
|
|
||||||
|
// Slot it into available palette space
|
||||||
|
palData = _menuItem->getRgbList();
|
||||||
|
_vm->_palette->addRange(palData);
|
||||||
|
_menuItem->translate(palData, true);
|
||||||
|
_itemPalData.push_back(palData);
|
||||||
|
|
||||||
|
spr = _menuItem->getFrame(1);
|
||||||
|
// FIXME: We assume that the transparent color is the color of the top left pixel
|
||||||
|
byte *transparentColor = (byte *)spr->pixels;
|
||||||
|
spr->copyTo(this, spr->xOffset - 140, spr->yOffset - spr->height(), (int)*transparentColor);
|
||||||
|
|
||||||
|
_vm->_mouse->cursorOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DragonMainMenuView::getHighlightedItem(int x, int y) {
|
||||||
|
y -= (height() - MADS_SURFACE_HEIGHT) / 2;
|
||||||
|
|
||||||
|
for (int index = 0; index < 6; ++index) {
|
||||||
|
const Common::Point &pt = dragonMenuItemPosList[index];
|
||||||
|
M4Sprite *spr = _menuItem->getFrame(0);
|
||||||
|
|
||||||
|
if ((x >= pt.x - 25) && (y >= pt.y - spr->height()) && (x < (pt.x - 25 + spr->width())) && (y < (pt.y))) {
|
||||||
|
printf("x = %d, y = %d, index = %d\n", x, y, index);
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DragonMainMenuView::handleAction(MadsGameAction action) {
|
||||||
|
M4Engine *vm = _vm;
|
||||||
|
vm->_mouse->cursorOff();
|
||||||
|
vm->_viewManager->deleteView(this);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case START_GAME:
|
||||||
|
case RESUME_GAME:
|
||||||
|
// Load a sample starting scene - note that, currently, calling loadScene automatically
|
||||||
|
// removes this menu screen from being displayed
|
||||||
|
vm->_mouse->cursorOn();
|
||||||
|
vm->_viewManager->addView(vm->_scene);
|
||||||
|
vm->_scene->loadScene(101);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case SHOW_INTRO:
|
||||||
|
vm->_viewManager->showAnimView("@dragon");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CREDITS:
|
||||||
|
vm->_viewManager->showTextView("credits");
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EXIT:
|
||||||
|
vm->_events->quitFlag = true;
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
91
engines/m4/mads_menus.h
Normal file
91
engines/m4/mads_menus.h
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_MADS_MENUS_H
|
||||||
|
#define M4_MADS_MENUS_H
|
||||||
|
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define MADS_MENU_ANIM_DELAY 70
|
||||||
|
|
||||||
|
enum MadsGameAction {START_GAME, RESUME_GAME, SHOW_INTRO, CREDITS, QUOTES, EXIT};
|
||||||
|
|
||||||
|
class RexMainMenuView: public View {
|
||||||
|
private:
|
||||||
|
M4Surface *_bgSurface;
|
||||||
|
RGBList *_bgPalData;
|
||||||
|
int _menuItemIndex;
|
||||||
|
int _frameIndex;
|
||||||
|
bool _skipFlag;
|
||||||
|
SpriteAsset *_menuItem;
|
||||||
|
Common::Array<RGBList *> _itemPalData;
|
||||||
|
uint32 _delayTimeout;
|
||||||
|
int _highlightedIndex;
|
||||||
|
|
||||||
|
int getHighlightedItem(int x, int y);
|
||||||
|
void handleAction(MadsGameAction action);
|
||||||
|
public:
|
||||||
|
RexMainMenuView(M4Engine *vm);
|
||||||
|
~RexMainMenuView();
|
||||||
|
|
||||||
|
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
|
||||||
|
void updateState();
|
||||||
|
};
|
||||||
|
|
||||||
|
class DragonMainMenuView: public View {
|
||||||
|
private:
|
||||||
|
//M4Surface *_bgSurface;
|
||||||
|
RGBList *_bgPalData;
|
||||||
|
int _menuItemIndex;
|
||||||
|
int _frameIndex;
|
||||||
|
bool _skipFlag;
|
||||||
|
SpriteAsset *_menuItem;
|
||||||
|
Common::Array<RGBList *> _itemPalData;
|
||||||
|
uint32 _delayTimeout;
|
||||||
|
int _highlightedIndex;
|
||||||
|
|
||||||
|
int getHighlightedItem(int x, int y);
|
||||||
|
void handleAction(MadsGameAction action);
|
||||||
|
public:
|
||||||
|
DragonMainMenuView(M4Engine *vm);
|
||||||
|
~DragonMainMenuView();
|
||||||
|
|
||||||
|
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
|
||||||
|
void updateState();
|
||||||
|
};
|
||||||
|
|
||||||
|
class MadsMainMenuView: public View {
|
||||||
|
public:
|
||||||
|
MadsMainMenuView(M4Engine *vm);
|
||||||
|
|
||||||
|
bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents);
|
||||||
|
void updateState();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
359
engines/m4/midi.cpp
Normal file
359
engines/m4/midi.cpp
Normal file
|
@ -0,0 +1,359 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// FIXME: This is cribbed together from the SAGA music player. It needs cleanup
|
||||||
|
// and testing.
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/midi.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
MidiPlayer::MidiPlayer(M4Engine *vm, MidiDriver *driver) : _vm(vm), _midiData(NULL), _driver(driver), _isPlaying(false), _passThrough(false), _isGM(false) {
|
||||||
|
memset(_channel, 0, sizeof(_channel));
|
||||||
|
_masterVolume = 0;
|
||||||
|
_parser = MidiParser::createParser_SMF();
|
||||||
|
_parser->setMidiDriver(this);
|
||||||
|
_parser->setTimerRate(getBaseTempo());
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiPlayer::~MidiPlayer() {
|
||||||
|
_driver->setTimerCallback(NULL, NULL);
|
||||||
|
_parser->setMidiDriver(NULL);
|
||||||
|
stopMusic();
|
||||||
|
close();
|
||||||
|
delete _parser;
|
||||||
|
delete _midiData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiPlayer::setVolume(int volume) {
|
||||||
|
Common::StackLock lock(_mutex);
|
||||||
|
|
||||||
|
if (volume < 0)
|
||||||
|
volume = 0;
|
||||||
|
else if (volume > 255)
|
||||||
|
volume = 255;
|
||||||
|
|
||||||
|
if (_masterVolume == volume)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_masterVolume = volume;
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
if (_channel[i]) {
|
||||||
|
_channel[i]->volume(_channelVolume[i] * _masterVolume / 255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MidiPlayer::open() {
|
||||||
|
// Don't ever call open without first setting the output driver!
|
||||||
|
if (!_driver)
|
||||||
|
return 255;
|
||||||
|
|
||||||
|
int ret = _driver->open();
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
_driver->setTimerCallback(this, &onTimer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiPlayer::close() {
|
||||||
|
stopMusic();
|
||||||
|
if (_driver)
|
||||||
|
_driver->close();
|
||||||
|
_driver = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiPlayer::send(uint32 b) {
|
||||||
|
if (_passThrough) {
|
||||||
|
_driver->send(b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte channel = (byte)(b & 0x0F);
|
||||||
|
if ((b & 0xFFF0) == 0x07B0) {
|
||||||
|
// Adjust volume changes by master volume
|
||||||
|
byte volume = (byte)((b >> 16) & 0x7F);
|
||||||
|
_channelVolume[channel] = volume;
|
||||||
|
volume = volume * _masterVolume / 255;
|
||||||
|
b = (b & 0xFF00FFFF) | (volume << 16);
|
||||||
|
} else if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
|
||||||
|
b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
|
||||||
|
}
|
||||||
|
else if ((b & 0xFFF0) == 0x007BB0) {
|
||||||
|
//Only respond to All Notes Off if this channel
|
||||||
|
//has currently been allocated
|
||||||
|
if (_channel[b & 0x0F])
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_channel[channel])
|
||||||
|
_channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
|
||||||
|
|
||||||
|
if (_channel[channel])
|
||||||
|
_channel[channel]->send(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiPlayer::metaEvent(byte type, byte *data, uint16 length) {
|
||||||
|
switch (type) {
|
||||||
|
case 0x2F:
|
||||||
|
// End of track. (Not called when auto-looping.)
|
||||||
|
stopMusic();
|
||||||
|
break;
|
||||||
|
case 0x51:
|
||||||
|
// Set tempo. Handled by the standard MIDI parser already.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warning("Unhandled meta event: %02x", type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiPlayer::onTimer(void *refCon) {
|
||||||
|
MidiPlayer *midi = (MidiPlayer *)refCon;
|
||||||
|
Common::StackLock lock(midi->_mutex);
|
||||||
|
|
||||||
|
if (midi->_isPlaying)
|
||||||
|
midi->_parser->onTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiPlayer::playMusic(const char *name, int32 vol, bool loop, int32 trigger, int32 scene) {
|
||||||
|
stopMusic();
|
||||||
|
|
||||||
|
char fullname[144];
|
||||||
|
_vm->res()->changeExtension(fullname, name, "HMP");
|
||||||
|
|
||||||
|
Common::SeekableReadStream *midiFile = _vm->res()->get(fullname);
|
||||||
|
byte *hmpData = new byte[midiFile->size()];
|
||||||
|
uint32 smfSize;
|
||||||
|
|
||||||
|
midiFile->read(hmpData, midiFile->size());
|
||||||
|
_midiData = convertHMPtoSMF(hmpData, midiFile->size(), smfSize);
|
||||||
|
delete[] hmpData;
|
||||||
|
_vm->res()->toss(fullname);
|
||||||
|
_vm->res()->purge();
|
||||||
|
|
||||||
|
if (_midiData) {
|
||||||
|
/*
|
||||||
|
FILE *out = fopen("music.mid", "wb");
|
||||||
|
fwrite(_midiData, smfSize, 1, out);
|
||||||
|
fclose(out);
|
||||||
|
*/
|
||||||
|
_parser->loadMusic(_midiData, smfSize);
|
||||||
|
_parser->property(MidiParser::mpAutoLoop, loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
setVolume(255);
|
||||||
|
|
||||||
|
_isPlaying = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MidiPlayer::stopMusic() {
|
||||||
|
Common::StackLock lock(_mutex);
|
||||||
|
|
||||||
|
_isPlaying = false;
|
||||||
|
if (_parser) {
|
||||||
|
_parser->unloadMusic();
|
||||||
|
}
|
||||||
|
delete[] _midiData;
|
||||||
|
_midiData = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function will convert HMP music into type 1 SMF, which our SMF parser
|
||||||
|
// will be able to handle. It is based on Hans de Goede's HMP 2 MIDI file
|
||||||
|
// converter, which in turn is "based on the conversion algorithms found in
|
||||||
|
// d1x, d2x-xl and jjffe". Hans's original code is licensed under the LGPL.
|
||||||
|
//
|
||||||
|
// TODO: It would probably be nicer to write a MIDI parser class to deal with
|
||||||
|
// HMP data directly. Though the multi-track nature of HMP makes that tricky.
|
||||||
|
|
||||||
|
byte *MidiPlayer::convertHMPtoSMF(byte *data, uint32 inSize, uint32 &outSize) {
|
||||||
|
Common::MemoryReadStream readS(data, inSize);
|
||||||
|
Common::MemoryWriteStreamDynamic writeS;
|
||||||
|
|
||||||
|
byte buf[8];
|
||||||
|
|
||||||
|
readS.read(buf, sizeof(buf));
|
||||||
|
if (memcmp(buf, "HMIMIDIP", 8) != 0) {
|
||||||
|
warning("convertHMPtoSMF: Invalid HMP header");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the number of tracks. Note that all the tracks are still part
|
||||||
|
// of the same song, just like in type 1 SMF files.
|
||||||
|
|
||||||
|
readS.seek(0x30);
|
||||||
|
|
||||||
|
uint32 numTracks = readS.readUint32LE();
|
||||||
|
|
||||||
|
// The first track starts on offset 0x300. It's currently unknown what
|
||||||
|
// the skipped data is for.
|
||||||
|
|
||||||
|
readS.seek(0x300);
|
||||||
|
|
||||||
|
// For some reason, we skip the first track entirely.
|
||||||
|
|
||||||
|
byte a = readS.readByte();
|
||||||
|
byte b = readS.readByte();
|
||||||
|
byte c = readS.readByte();
|
||||||
|
|
||||||
|
while (a != 0xFF || b != 0x2F || c != 0x00) {
|
||||||
|
a = b;
|
||||||
|
b = c;
|
||||||
|
c = readS.readByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The beginning of the MIDI header
|
||||||
|
static const byte midiHeader1[] = { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1 };
|
||||||
|
// The last 2 bytes of the midi header and track 0
|
||||||
|
static const byte midiHeader2[] = { 0, 0xC0, 'M', 'T', 'r', 'k', 0, 0, 0, 0x0B, 0, 0xFF, 0x51, 0x03, 0x18, 0x80, 0, 0, 0xFF, 0x2F, 0 };
|
||||||
|
|
||||||
|
|
||||||
|
// Write the MIDI header
|
||||||
|
writeS.write(midiHeader1, sizeof(midiHeader1));
|
||||||
|
|
||||||
|
// Write the number of tracks
|
||||||
|
writeS.writeUint16BE(numTracks);
|
||||||
|
|
||||||
|
// Write the rest of the MIDI header and track 0.
|
||||||
|
writeS.write(midiHeader2, sizeof(midiHeader2));
|
||||||
|
|
||||||
|
// Read and convert all the tracks
|
||||||
|
for (uint i = 1; i < numTracks; i++) {
|
||||||
|
if (readS.readUint32LE() != i) {
|
||||||
|
warning("convertHMPtoSMF: Invalid HMP track number");
|
||||||
|
delete[] writeS.getData();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 trackLength = readS.readUint32LE() - 12;
|
||||||
|
readS.readUint32LE(); // Unused?
|
||||||
|
|
||||||
|
// Write the track header
|
||||||
|
writeS.write("MTrk", 4);
|
||||||
|
|
||||||
|
// This is where we will write the length of the track.
|
||||||
|
uint32 trackLengthPos = writeS.pos();
|
||||||
|
writeS.writeUint32LE(0);
|
||||||
|
|
||||||
|
// In the original, this is cleared once at the beginning of
|
||||||
|
// the function, but surely the last command does not carry
|
||||||
|
// over to the next track?
|
||||||
|
|
||||||
|
byte lastCmd = 0;
|
||||||
|
|
||||||
|
// Now we can finally convert the track
|
||||||
|
uint32 endPos = readS.pos() + trackLength;
|
||||||
|
while (readS.pos() < endPos) {
|
||||||
|
// Convert the VLQ
|
||||||
|
byte vlq[4];
|
||||||
|
int j = -1;
|
||||||
|
|
||||||
|
do {
|
||||||
|
j++;
|
||||||
|
vlq[j] = readS.readByte();
|
||||||
|
} while (!(vlq[j] & 0x80));
|
||||||
|
|
||||||
|
for (int k = 0; k <= j; k++) {
|
||||||
|
a = vlq[j - k] & 0x7F;
|
||||||
|
if (k != j)
|
||||||
|
a |= 0x80;
|
||||||
|
writeS.writeByte(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
a = readS.readByte();
|
||||||
|
|
||||||
|
if (a == 0xFF) {
|
||||||
|
// META event
|
||||||
|
b = readS.readByte();
|
||||||
|
c = readS.readByte();
|
||||||
|
|
||||||
|
writeS.writeByte(a);
|
||||||
|
writeS.writeByte(b);
|
||||||
|
writeS.writeByte(c);
|
||||||
|
|
||||||
|
if (c > 0) {
|
||||||
|
byte *metaBuf = new byte[c];
|
||||||
|
readS.read(metaBuf, c);
|
||||||
|
writeS.write(metaBuf, c);
|
||||||
|
delete[] metaBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b == 0x2F) {
|
||||||
|
if (c != 0x00) {
|
||||||
|
warning("convertHMPtoSMF: End of track with non-zero size");
|
||||||
|
delete[] writeS.getData();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (a != lastCmd)
|
||||||
|
writeS.writeByte(a);
|
||||||
|
|
||||||
|
switch (a & 0xF0) {
|
||||||
|
case 0x80:
|
||||||
|
case 0x90:
|
||||||
|
case 0xA0:
|
||||||
|
case 0xB0:
|
||||||
|
case 0xE0:
|
||||||
|
b = readS.readByte();
|
||||||
|
c = readS.readByte();
|
||||||
|
writeS.writeByte(b);
|
||||||
|
writeS.writeByte(c);
|
||||||
|
break;
|
||||||
|
case 0xC0:
|
||||||
|
case 0xD0:
|
||||||
|
b = readS.readByte();
|
||||||
|
writeS.writeByte(b);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warning("convertHMPtoSMF: Invalid HMP command %02X", a);
|
||||||
|
delete[] writeS.getData();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastCmd = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readS.pos() != endPos) {
|
||||||
|
warning("convertHMPtoSMF: Invalid track length");
|
||||||
|
delete[] writeS.getData();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITE_BE_UINT32(writeS.getData() + trackLengthPos, writeS.pos() - trackLengthPos - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
outSize = writeS.size();
|
||||||
|
return writeS.getData();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
99
engines/m4/midi.h
Normal file
99
engines/m4/midi.h
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Music class
|
||||||
|
|
||||||
|
#ifndef M4_MIDI_H
|
||||||
|
#define M4_MIDI_H
|
||||||
|
|
||||||
|
#include "sound/mididrv.h"
|
||||||
|
#include "sound/midiparser.h"
|
||||||
|
#include "common/mutex.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
class MidiPlayer : public MidiDriver {
|
||||||
|
public:
|
||||||
|
MidiPlayer(M4Engine *vm, MidiDriver *driver);
|
||||||
|
~MidiPlayer();
|
||||||
|
|
||||||
|
bool isPlaying() { return _isPlaying; }
|
||||||
|
|
||||||
|
void setVolume(int volume);
|
||||||
|
int getVolume() { return _masterVolume; }
|
||||||
|
|
||||||
|
void setNativeMT32(bool b) { _nativeMT32 = b; }
|
||||||
|
bool hasNativeMT32() { return _nativeMT32; }
|
||||||
|
void playMusic(const char *name, int32 vol, bool loop, int32 trigger, int32 scene);
|
||||||
|
void stopMusic();
|
||||||
|
void setPassThrough(bool b) { _passThrough = b; }
|
||||||
|
|
||||||
|
void setGM(bool isGM) { _isGM = isGM; }
|
||||||
|
|
||||||
|
//MidiDriver interface implementation
|
||||||
|
int open();
|
||||||
|
void close();
|
||||||
|
void send(uint32 b);
|
||||||
|
|
||||||
|
void metaEvent(byte type, byte *data, uint16 length);
|
||||||
|
|
||||||
|
void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { }
|
||||||
|
uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; }
|
||||||
|
|
||||||
|
//Channel allocation functions
|
||||||
|
MidiChannel *allocateChannel() { return 0; }
|
||||||
|
MidiChannel *getPercussionChannel() { return 0; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void onTimer(void *data);
|
||||||
|
|
||||||
|
M4Engine *_vm;
|
||||||
|
byte *_midiData;
|
||||||
|
|
||||||
|
MidiChannel *_channel[16];
|
||||||
|
MidiDriver *_driver;
|
||||||
|
MidiParser *_parser;
|
||||||
|
byte _channelVolume[16];
|
||||||
|
bool _nativeMT32;
|
||||||
|
bool _isGM;
|
||||||
|
bool _passThrough;
|
||||||
|
|
||||||
|
bool _isPlaying;
|
||||||
|
bool _randomLoop;
|
||||||
|
byte _masterVolume;
|
||||||
|
|
||||||
|
byte *_musicData;
|
||||||
|
uint16 *_buf;
|
||||||
|
size_t _musicDataSize;
|
||||||
|
|
||||||
|
Common::Mutex _mutex;
|
||||||
|
|
||||||
|
byte *convertHMPtoSMF(byte *data, uint32 inSize, uint32 &outSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
42
engines/m4/module.mk
Normal file
42
engines/m4/module.mk
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
MODULE := engines/m4
|
||||||
|
|
||||||
|
MODULE_OBJS = \
|
||||||
|
actor.o \
|
||||||
|
animation.o \
|
||||||
|
assets.o \
|
||||||
|
compression.o \
|
||||||
|
console.o \
|
||||||
|
converse.o \
|
||||||
|
detection.o \
|
||||||
|
events.o \
|
||||||
|
font.o \
|
||||||
|
globals.o \
|
||||||
|
graphics.o \
|
||||||
|
gui.o \
|
||||||
|
hotspot.o \
|
||||||
|
m4.o \
|
||||||
|
m4_menus.o \
|
||||||
|
m4_views.o \
|
||||||
|
mads_anim.o \
|
||||||
|
mads_menus.o \
|
||||||
|
midi.o \
|
||||||
|
rails.o \
|
||||||
|
resource.o \
|
||||||
|
saveload.o \
|
||||||
|
scene.o \
|
||||||
|
script.o \
|
||||||
|
sound.o \
|
||||||
|
sprite.o \
|
||||||
|
viewmgr.o \
|
||||||
|
woodscript.o \
|
||||||
|
ws_machine.o \
|
||||||
|
ws_sequence.o
|
||||||
|
|
||||||
|
|
||||||
|
# This module can be built as a plugin
|
||||||
|
ifdef BUILD_PLUGINS
|
||||||
|
PLUGIN := 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Include common rules
|
||||||
|
include $(srcdir)/rules.mk
|
358
engines/m4/rails.cpp
Normal file
358
engines/m4/rails.cpp
Normal file
|
@ -0,0 +1,358 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO:
|
||||||
|
- rewrite functions (GetShortestPath etc.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "graphics/primitives.h"
|
||||||
|
#include "common/list.h"
|
||||||
|
#include "common/rect.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
|
||||||
|
#include "m4/rails.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define TOP_EDGE 1 << 0
|
||||||
|
#define LEFT_EDGE 1 << 1
|
||||||
|
#define BOTTOM_EDGE 1 << 2
|
||||||
|
#define RIGHT_EDGE 1 << 3
|
||||||
|
|
||||||
|
|
||||||
|
Rails::Rails() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Rails::~Rails() {
|
||||||
|
clearRails();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Rails::clearRails() {
|
||||||
|
uint32 i;
|
||||||
|
Common::List<NoWalkRect *>::iterator j;
|
||||||
|
RailNode *tempNode;
|
||||||
|
|
||||||
|
for (i = 0; i < _nodes.size(); i++) {
|
||||||
|
tempNode = _nodes[i];
|
||||||
|
_nodes.remove_at(i);
|
||||||
|
delete tempNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < _edges.size(); i++) {
|
||||||
|
_edges.remove_at(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = _noWalkRects.begin(); j != _noWalkRects.end(); ++j)
|
||||||
|
delete (*j);
|
||||||
|
_noWalkRects.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void checkPoint(int x, int y, int color, void *data) {
|
||||||
|
IsWalkableData *isWalkableData = (IsWalkableData*)data;
|
||||||
|
if (!isWalkableData->result)
|
||||||
|
return;
|
||||||
|
else {
|
||||||
|
M4Surface *codes = isWalkableData->codes;
|
||||||
|
if (x >= 0 && x < codes->w && y >= 0 && y < codes->h) {
|
||||||
|
isWalkableData->result = !((*((uint8*)codes->getBasePtr(x, y))) & 0x10);
|
||||||
|
} else {
|
||||||
|
isWalkableData->result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Rails::isLineWalkable(int x0, int y0, int x1, int y1) {
|
||||||
|
IsWalkableData isWalkableData;
|
||||||
|
isWalkableData.codes = _walkCodes;
|
||||||
|
isWalkableData.result = true;
|
||||||
|
Graphics::drawLine(x0, y0, x1, y1, 0, &checkPoint, &isWalkableData);
|
||||||
|
return isWalkableData.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function
|
||||||
|
uint8 getEndCode(int32 x, int32 y, Common::Rect rect) {
|
||||||
|
uint8 endCode = 0;
|
||||||
|
endCode = (x < rect.left) ? LEFT_EDGE : endCode;
|
||||||
|
endCode = (x > rect.right) ? RIGHT_EDGE : endCode;
|
||||||
|
endCode = (y < rect.top) ? endCode | TOP_EDGE : endCode;
|
||||||
|
endCode = (y > rect.bottom) ? endCode | BOTTOM_EDGE : endCode;
|
||||||
|
return endCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Rails::lineCrossesRect(int32 x1, int32 y1, int32 x2, int32 y2, Common::Rect rect) {
|
||||||
|
int32 mX, mY;
|
||||||
|
int32 pX1 = x1, pX2 = x2, pY1 = y1, pY2 = y2;
|
||||||
|
uint8 endCode1, endCode2, midCode;
|
||||||
|
|
||||||
|
if (rect.left > rect.right || rect.top > rect.bottom)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Cohen-Sutherland line clipping algorithm
|
||||||
|
|
||||||
|
endCode1 = getEndCode(pX1, pY1, rect);
|
||||||
|
endCode2 = getEndCode(pX2, pY2, rect);
|
||||||
|
|
||||||
|
if (!endCode1 || !endCode2) // if both endcodes are zero
|
||||||
|
return true; // point is inside the rectangle, therefore the line intersects
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (endCode1 & endCode2) // if both endcodes have a common bitset
|
||||||
|
return false; // line is completely off one edge
|
||||||
|
|
||||||
|
// calculate midpoint
|
||||||
|
mX = (pX1 + pX2)>>1;
|
||||||
|
mY = (pY1 + pY2)>>1;
|
||||||
|
|
||||||
|
// avoid round-off error: make sure that the midpoint isn't the same as one of the
|
||||||
|
// two endpoints
|
||||||
|
if (((mX == pX1) && (mY == pY1)) || ((mX == pX2) && (mY == pY2)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
midCode = getEndCode(mX, mY, rect);
|
||||||
|
|
||||||
|
if (!midCode) {
|
||||||
|
return true;
|
||||||
|
} else if (midCode & endCode1) {
|
||||||
|
// the midCode and an end point form a line segment completely off one edge, so
|
||||||
|
// remove that half of the line segment
|
||||||
|
pX1 = mX;
|
||||||
|
pY1 = mY;
|
||||||
|
endCode1 = midCode;
|
||||||
|
} else {
|
||||||
|
pX2 = mX;
|
||||||
|
pY2 = mY;
|
||||||
|
endCode2 = midCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Rails::linePassesThroughRect(int32 x1, int32 y1, int32 x2, int32 y2) {
|
||||||
|
if (_noWalkRects.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool intersected = false;
|
||||||
|
Common::List<NoWalkRect *>::iterator i;
|
||||||
|
|
||||||
|
for (i = _noWalkRects.begin(); i != _noWalkRects.end(); ++i) {
|
||||||
|
intersected = lineCrossesRect(x1, y1, x2, y2, Common::Rect((*i)->x1, (*i)->y1, (*i)->x2, (*i)->y2));
|
||||||
|
if (intersected)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return intersected;
|
||||||
|
}
|
||||||
|
|
||||||
|
long SqrtF16(long n) {
|
||||||
|
uint32 r = 0, s;
|
||||||
|
uint32 v = (uint32)n;
|
||||||
|
|
||||||
|
for (int i = 15; i <= 0; i--) {
|
||||||
|
s = r + (1L << i * 2);
|
||||||
|
r >>= 1;
|
||||||
|
if (s <= v) {
|
||||||
|
v -= s;
|
||||||
|
r |= (1L << i * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (long)r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rails::createEdge(int32 node1, int32 node2) {
|
||||||
|
uint32 index;
|
||||||
|
int32 x1, y1, x2, y2;
|
||||||
|
bool valid, finished;
|
||||||
|
long deltaX, deltaY, distance;
|
||||||
|
uint8 *walkCodePtr;
|
||||||
|
|
||||||
|
if ((node1 < 0) || (node1 >= MAXRAILNODES) || (node2 < 0) || (node2 >= MAXRAILNODES))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (node1 == node2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (node2 < node1)
|
||||||
|
SWAP(node1, node2); // ensure node1 < node2
|
||||||
|
|
||||||
|
// Find the table entry i.e. tableWidth * node1 + node2 and then subtract
|
||||||
|
// n(n+1)/2, since only the upper triangle of the table is stored
|
||||||
|
index = (MAXRAILNODES-1) * node1 + node2 - 1 - (node1*(node1+1)>>1);
|
||||||
|
if (index > _edges.size() - 1)
|
||||||
|
_edges.resize(index + 1);
|
||||||
|
_edges.insert_at(index, 0);
|
||||||
|
valid = true;
|
||||||
|
walkCodePtr = NULL;
|
||||||
|
finished = false;
|
||||||
|
|
||||||
|
if (_nodes.size() <= (uint32)node1 || _nodes.size() <= (uint32)node2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
x1 = _nodes[node1]->x;
|
||||||
|
y1 = _nodes[node1]->y;
|
||||||
|
x2 = _nodes[node2]->x;
|
||||||
|
y2 = _nodes[node2]->y;
|
||||||
|
|
||||||
|
// Make sure that the algorithm is symmetric
|
||||||
|
if (x2 < x1) {
|
||||||
|
SWAP(x1, x2);
|
||||||
|
SWAP(y1, y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = isLineWalkable(_nodes[node1]->x, _nodes[node1]->y,
|
||||||
|
_nodes[node2]->x, _nodes[node2]->y);
|
||||||
|
printf("test code says: %d\n", valid);
|
||||||
|
|
||||||
|
// Check if the line passes through a forbidden rectangle
|
||||||
|
if (valid) {
|
||||||
|
if (linePassesThroughRect(x1, y1, x2, y2)) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid) {
|
||||||
|
deltaX = ABS(((long)(x2 - x1)) << 16);
|
||||||
|
deltaY = ABS(((long)(y2 - y1)) << 16);
|
||||||
|
if ((deltaX >= 0x800000) || (deltaY >= 0x800000)) {
|
||||||
|
deltaX >>= 16;
|
||||||
|
deltaY >>= 16;
|
||||||
|
distance = (long)(SqrtF16(deltaX * deltaX + deltaY * deltaY) << 16);
|
||||||
|
} else {
|
||||||
|
distance = SqrtF16(FixedMul(deltaX, deltaX) + FixedMul(deltaY, deltaY)) << 8;
|
||||||
|
}
|
||||||
|
_edges.insert_at(index, (int16*)(distance >> 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("node1 = %d, node2 = %d, valid = %d\n", node1, node2, valid);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Rails::restoreNodeEdges(int32 nodeID) {
|
||||||
|
for (int32 i = 0; i < MAXRAILNODES; i++) {
|
||||||
|
createEdge(i, nodeID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rails::restoreEdgeList() {
|
||||||
|
int32 j;
|
||||||
|
for (int32 i = 0; i < MAXRAILNODES; i++) {
|
||||||
|
for (j = i + 1; j < MAXRAILNODES; j++) {
|
||||||
|
createEdge(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 Rails::addRailNode(int32 x, int32 y, bool restoreEdges) {
|
||||||
|
uint32 i = _nodes.size();
|
||||||
|
if (i >= MAXRAILNODES)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
RailNode *newNode = new RailNode();
|
||||||
|
newNode->nodeID = i;
|
||||||
|
newNode->x = x;
|
||||||
|
newNode->y = y;
|
||||||
|
_nodes.insert_at(i, newNode);
|
||||||
|
if (restoreEdges) {
|
||||||
|
for (uint32 j=0; j<_nodes.size(); j++)
|
||||||
|
createEdge(i, j);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Rails::removeRailNode(int32 nodeID, bool restoreEdges) {
|
||||||
|
if (nodeID < 0 || nodeID >= MAXRAILNODES)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_nodes.empty() || _edges.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RailNode *tempNode = _nodes[nodeID];
|
||||||
|
_nodes.remove_at(nodeID);
|
||||||
|
delete tempNode;
|
||||||
|
|
||||||
|
if (restoreEdges) {
|
||||||
|
restoreNodeEdges(nodeID);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16 Rails::getEdgeLength(int32 node1, int32 node2) {
|
||||||
|
int32 index;
|
||||||
|
if (_edges.empty() || node1 == node2)
|
||||||
|
return 0;
|
||||||
|
if (node2 < node1)
|
||||||
|
SWAP(node1, node2);
|
||||||
|
// Find the table entry i.e. tableWidth * node1 + node2 and then subtract
|
||||||
|
// n(n+1)/2, since only the upper triangle of the table is stored
|
||||||
|
index = (MAXRAILNODES-1)*node1 + node2 - 1 - (node1*(node1+1)>>1);
|
||||||
|
return *_edges[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rails::disposePath(RailNode *pathStart) {
|
||||||
|
RailNode *tempNode = pathStart;
|
||||||
|
while (tempNode) {
|
||||||
|
pathStart = pathStart->shortPath;
|
||||||
|
delete tempNode;
|
||||||
|
tempNode = pathStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static RailNode* duplicatePath(RailNode *pathStart) {
|
||||||
|
RailNode *newNode = NULL;
|
||||||
|
RailNode *firstNode = NULL;
|
||||||
|
RailNode *prevNode = NULL;
|
||||||
|
// A valid path is assumed
|
||||||
|
RailNode *pathNode = pathStart;
|
||||||
|
|
||||||
|
while (pathNode) {
|
||||||
|
newNode = new RailNode();
|
||||||
|
newNode->x = pathNode->x;
|
||||||
|
newNode->y = pathNode->y;
|
||||||
|
newNode->shortPath = NULL;
|
||||||
|
|
||||||
|
if (!firstNode)
|
||||||
|
firstNode = newNode;
|
||||||
|
else
|
||||||
|
prevNode->shortPath = newNode;
|
||||||
|
|
||||||
|
prevNode = newNode;
|
||||||
|
// Get the next node
|
||||||
|
pathNode = pathNode->shortPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return firstNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Rails::getShortestPath(int32 origID, int32 destID, RailNode **shortPath) {
|
||||||
|
// TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
97
engines/m4/rails.h
Normal file
97
engines/m4/rails.h
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_RAILS_H
|
||||||
|
#define M4_RAILS_H
|
||||||
|
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "common/list.h"
|
||||||
|
#include "common/array.h"
|
||||||
|
#include "common/rect.h"
|
||||||
|
|
||||||
|
// TODO: This needs cleaning up
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define MAXRAILNODES 32
|
||||||
|
#define PATH_END 0xffff
|
||||||
|
|
||||||
|
struct RailNode {
|
||||||
|
uint8 nodeID;
|
||||||
|
int32 x, y;
|
||||||
|
RailNode *shortPath;
|
||||||
|
int32 pathWeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NoWalkRect {
|
||||||
|
int32 x1, y1, x2, y2;
|
||||||
|
int32 alternateWalkToNode;
|
||||||
|
int32 walkAroundNode1;
|
||||||
|
int32 walkAroundNode2;
|
||||||
|
int32 walkAroundNode3;
|
||||||
|
int32 walkAroundNode4;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PathNode {
|
||||||
|
PathNode *next;
|
||||||
|
int8 nodeID;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IsWalkableData {
|
||||||
|
M4Surface *codes;
|
||||||
|
bool result;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Rails {
|
||||||
|
public:
|
||||||
|
Rails();
|
||||||
|
~Rails();
|
||||||
|
|
||||||
|
void setCodeSurface(M4Surface *surface) { _walkCodes = surface; }
|
||||||
|
void clearRails();
|
||||||
|
int32 addRailNode(int32 x, int32 y, bool restoreEdges);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Common::Array<RailNode *> _nodes;
|
||||||
|
Common::Array<int16 *> _edges;
|
||||||
|
Common::List<NoWalkRect *> _noWalkRects;
|
||||||
|
M4Surface *_walkCodes;
|
||||||
|
|
||||||
|
bool lineCrossesRect(int32 x1, int32 y1, int32 x2, int32 y2, Common::Rect rect);
|
||||||
|
bool linePassesThroughRect(int32 x1, int32 y1, int32 x2, int32 y2);
|
||||||
|
void createEdge(int32 node1, int32 node2);
|
||||||
|
bool removeRailNode(int32 nodeID, bool restoreEdges);
|
||||||
|
int16 getEdgeLength(int32 node1, int32 node2);
|
||||||
|
|
||||||
|
void restoreNodeEdges(int32 nodeID);
|
||||||
|
void restoreEdgeList();
|
||||||
|
void disposePath(RailNode *pathStart);
|
||||||
|
bool getShortestPath(int32 origID, int32 destID, RailNode **shortPath);
|
||||||
|
bool isLineWalkable(int x0, int y0, int x1, int y1);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
436
engines/m4/resource.cpp
Normal file
436
engines/m4/resource.cpp
Normal file
|
@ -0,0 +1,436 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/resource.h"
|
||||||
|
#include "m4/events.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
FileSystem::FileSystem(const char *hashFilename) {
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
_hagEntries[i].filename[0] = '\0';
|
||||||
|
_hagEntries[i].fileIndex = 0; // Was -1
|
||||||
|
_hagEntries[i].hagFile = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::File hashFile;
|
||||||
|
uint32 hashSize;
|
||||||
|
|
||||||
|
hashFile.open(hashFilename);
|
||||||
|
|
||||||
|
if (!hashFile.isOpen()) {
|
||||||
|
printf("FileSystem::FileSystem: error opening hash %s\n", hashFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
hashSize = hashFile.readUint32LE();
|
||||||
|
|
||||||
|
//printf("FileSystem::FileSystem: hashSize = %d\n", hashSize);
|
||||||
|
|
||||||
|
/* load file records and add them to the hash list */
|
||||||
|
for (uint i = 0; i < hashSize; i++) {
|
||||||
|
HashFileEntry entry;
|
||||||
|
hashFile.read(entry.filename, kM4MaxFilenameSize);
|
||||||
|
str_lower(entry.filename);
|
||||||
|
entry.hagfile = hashFile.readByte();
|
||||||
|
hashFile.readByte();
|
||||||
|
entry.offset = hashFile.readUint32LE();
|
||||||
|
entry.size = hashFile.readUint32LE();
|
||||||
|
hashFile.readUint32LE();
|
||||||
|
|
||||||
|
if (entry.filename[0]) {
|
||||||
|
/*
|
||||||
|
printf(" filename: %s\n", entry.filename);
|
||||||
|
printf(" hagfile: %d\n", entry.hagfile);
|
||||||
|
printf(" disks: %d\n", entry.disks);
|
||||||
|
printf(" offset: %08X\n", entry.offset);
|
||||||
|
printf(" size: %d\n", entry.size);
|
||||||
|
printf(" next: %08X\n", entry.next);
|
||||||
|
*/
|
||||||
|
_fileEntries[entry.filename] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* load hagfile records and update the list */
|
||||||
|
while (!hashFile.eof()) {
|
||||||
|
HashHagEntry entry;
|
||||||
|
hashFile.read(entry.filename, kM4MaxFilenameSize);
|
||||||
|
entry.fileIndex = hashFile.readByte();
|
||||||
|
if (hashFile.eof())
|
||||||
|
break;
|
||||||
|
|
||||||
|
changeExtension(_hagEntries[entry.fileIndex].filename, entry.filename, "HAG");
|
||||||
|
_hagEntries[entry.fileIndex].fileIndex = entry.fileIndex;
|
||||||
|
|
||||||
|
_hagEntries[entry.fileIndex].hagFile = new Common::File();
|
||||||
|
_hagEntries[entry.fileIndex].hagFile->open(_hagEntries[entry.fileIndex].filename);
|
||||||
|
|
||||||
|
if (!_hagEntries[entry.fileIndex].hagFile->isOpen()) {
|
||||||
|
printf("FileSystem::FileSystem: error opening hag %s\n", _hagEntries[entry.fileIndex].filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
hashFile.close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystem::~FileSystem() {
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (_hagEntries[i].hagFile)
|
||||||
|
delete _hagEntries[i].hagFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::SeekableReadStream *FileSystem::loadFile(const char *resourceName, bool preloadFlag) {
|
||||||
|
const HashFileEntry *hfe = getHashFileEntry(resourceName);
|
||||||
|
Common::SeekableReadStream *result = NULL;
|
||||||
|
|
||||||
|
if (hfe) {
|
||||||
|
//printf("FileSystem::loadFile() success opening %s\n", filename);
|
||||||
|
HashHagEntry *hagEntry = &_hagEntries[hfe->hagfile];
|
||||||
|
|
||||||
|
if (preloadFlag) {
|
||||||
|
// Creates a MemoryReadStream object that contains all of the resource in memory
|
||||||
|
hagEntry->hagFile->seek(hfe->offset);
|
||||||
|
result = _hagEntries[hfe->hagfile].hagFile->readStream(hfe->size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// Creates a SeekableSubReadStream, which will read the data in from disk as the
|
||||||
|
// caller reads in data
|
||||||
|
result = new Common::SeekableSubReadStream(hagEntry->hagFile, hfe->offset,
|
||||||
|
hfe->offset + hfe->size);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
printf("FileSystem::loadFile() error opening %s\n", resourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FileSystem::HashFileEntry *FileSystem::getHashFileEntry(const char *filename) {
|
||||||
|
char resourceName[20];
|
||||||
|
strcpy(resourceName, filename);
|
||||||
|
str_lower(resourceName);
|
||||||
|
|
||||||
|
FileHashMap::const_iterator entry = _fileEntries.find(filename);
|
||||||
|
if (entry != _fileEntries.end())
|
||||||
|
return &(entry->_value);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileSystem::changeExtension(char *destName, const char *sourceName, const char *extension) {
|
||||||
|
if (sourceName != destName)
|
||||||
|
strcpy(destName, sourceName);
|
||||||
|
char *dot = strrchr(destName, '.');
|
||||||
|
if (dot != NULL)
|
||||||
|
*dot = 0;
|
||||||
|
sprintf(destName, "%s.%s", destName, extension);
|
||||||
|
|
||||||
|
str_upper(destName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ResourceManager::~ResourceManager() {
|
||||||
|
ResourceIterator i;
|
||||||
|
for (i = _resources.begin(); i != _resources.end(); ++i) {
|
||||||
|
Resource *r = (*i).get();
|
||||||
|
delete r->stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::SeekableReadStream *ResourceManager::get(const char *resourceName, bool preloadFlag) {
|
||||||
|
char lowerName[kM4MaxFilenameSize];
|
||||||
|
|
||||||
|
strcpy(lowerName, resourceName);
|
||||||
|
str_lower(lowerName);
|
||||||
|
|
||||||
|
// Check whether the resource is already loaded
|
||||||
|
ResourceIterator i;
|
||||||
|
for (i = _resources.begin(); i != _resources.end(); ++i) {
|
||||||
|
Resource *r = (*i).get();
|
||||||
|
if (!strcmp(r->name, resourceName)) {
|
||||||
|
// Just in case resource was marked to be purged, reactive it again
|
||||||
|
r->flags &= ~kResourcePurge;
|
||||||
|
|
||||||
|
// Return the existing copy of the resource
|
||||||
|
r->stream->seek(0, SEEK_SET);
|
||||||
|
return r->stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the resource wasn't found in the list, load it from disk
|
||||||
|
Resource *newRes = new Resource();
|
||||||
|
strncpy(newRes->name, resourceName, 63);
|
||||||
|
newRes->name[63] = '\0';
|
||||||
|
newRes->flags = 0;
|
||||||
|
newRes->stream = loadResource(resourceName, preloadFlag);
|
||||||
|
|
||||||
|
_resources.push_back(ResourceList::value_type(newRes));
|
||||||
|
return newRes->stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceManager::toss(const char *resourceName) {
|
||||||
|
ResourceIterator i;
|
||||||
|
for (i = _resources.begin(); i != _resources.end(); ++i) {
|
||||||
|
Resource *r = (*i).get();
|
||||||
|
|
||||||
|
if (!strcmp(r->name, resourceName)) {
|
||||||
|
r->flags |= kResourcePurge;
|
||||||
|
//printf("M4ResourceManager::toss: mark resource %s to be purged\n", resourceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceManager::purge() {
|
||||||
|
ResourceIterator i = _resources.begin();
|
||||||
|
while (i != _resources.end()) {
|
||||||
|
Resource *r = (*i).get();
|
||||||
|
|
||||||
|
if (r->flags & kResourcePurge) {
|
||||||
|
delete r->stream;
|
||||||
|
i = _resources.erase(i);
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceManager::dump() {
|
||||||
|
_vm->_events->getConsole()->DebugPrintf("Scene resources:\n");
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
ResourceIterator i;
|
||||||
|
for (i = _resources.begin(); i != _resources.end(); ++i) {
|
||||||
|
Resource *r = (*i).get();
|
||||||
|
|
||||||
|
if (!(r->flags & kResourcePurge)) {
|
||||||
|
_vm->_events->getConsole()->DebugPrintf(
|
||||||
|
"Resource #%i, name: %s, handle pointer: %p, size: %d, flags: %02X\n",
|
||||||
|
index++, r->name, r->buffer, r->stream->size(), r->flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const char *madsConcatString = "MADSCONCAT";
|
||||||
|
|
||||||
|
ResourceType MADSResourceManager::getResourceType(const char *resourceName) {
|
||||||
|
if (!strncmp(resourceName, "RM", 2)) {
|
||||||
|
// Room resource
|
||||||
|
return RESTYPE_ROOM;
|
||||||
|
} else if (!strncmp(resourceName, "SC", 2)) {
|
||||||
|
// SC resource
|
||||||
|
return RESTYPE_SC;
|
||||||
|
} else if (strstr(resourceName, ".TXT")) {
|
||||||
|
// Text resource
|
||||||
|
return RESTYPE_TEXT;
|
||||||
|
} else if (strstr(resourceName, ".QUO")) {
|
||||||
|
// QUO resource
|
||||||
|
return RESTYPE_QUO;
|
||||||
|
} else if (*resourceName == 'I') {
|
||||||
|
// I resource
|
||||||
|
return RESTYPE_I;
|
||||||
|
} else if (!strncmp(resourceName, "OB", 2)) {
|
||||||
|
// OB resource
|
||||||
|
return RESTYPE_OB;
|
||||||
|
} else if (!strncmp(resourceName, "FONT", 4)) {
|
||||||
|
// FONT resource
|
||||||
|
return RESTYPE_FONT;
|
||||||
|
} else if (!strncmp(resourceName, "SOUND", 5)) {
|
||||||
|
// SOUND resource
|
||||||
|
return RESTYPE_SOUND;
|
||||||
|
} else if (!strncmp(resourceName, "SPCHC", 5)) {
|
||||||
|
// SPEECH resource
|
||||||
|
return RESTYPE_SPEECH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a known extension
|
||||||
|
const char *extPos = strchr(resourceName, '.');
|
||||||
|
if (extPos) {
|
||||||
|
++extPos;
|
||||||
|
if (!strcmp(extPos, "FL") || !strcmp(extPos, "LBM") || !strcmp(extPos, "ANM") ||
|
||||||
|
!strcmp(extPos, "AA") || !strcmp(extPos, "SS")) {
|
||||||
|
return RESTYPE_HAS_EXT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RESTYPE_NO_EXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *MADSResourceManager::getResourceFilename(const char *resourceName) {
|
||||||
|
static char outputFilename[64];
|
||||||
|
|
||||||
|
ResourceType resType = getResourceType(resourceName);
|
||||||
|
|
||||||
|
strcpy(outputFilename, "GLOBAL.HAG");
|
||||||
|
|
||||||
|
if ((resType == RESTYPE_ROOM) || (resType == RESTYPE_SC)) {
|
||||||
|
int value = atoi(resourceName + 2);
|
||||||
|
int hagFileNum = (resType == RESTYPE_ROOM) ? value / 100 : value;
|
||||||
|
|
||||||
|
if (hagFileNum > 0)
|
||||||
|
sprintf(outputFilename, "SECTION%d.HAG", hagFileNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resType == RESTYPE_SPEECH)
|
||||||
|
strcpy(outputFilename, "SPEECH.HAG");
|
||||||
|
|
||||||
|
return outputFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::SeekableReadStream *MADSResourceManager::loadResource(const char *resourceName, bool loadFlag) {
|
||||||
|
Common::File hagFile;
|
||||||
|
uint32 offset, size;
|
||||||
|
|
||||||
|
// If the first character is a '@' then look for an external file
|
||||||
|
|
||||||
|
if (*resourceName == '@') {
|
||||||
|
++resourceName;
|
||||||
|
|
||||||
|
hagFile.open(resourceName);
|
||||||
|
if (loadFlag)
|
||||||
|
return hagFile.readStream(hagFile.size());
|
||||||
|
else
|
||||||
|
return new Common::SeekableSubReadStream(&hagFile, 0, hagFile.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the first character is the wildcard (resource indicator), skip over it
|
||||||
|
if (*resourceName == '*')
|
||||||
|
++resourceName;
|
||||||
|
|
||||||
|
char resName[20];
|
||||||
|
strcpy(resName, resourceName);
|
||||||
|
str_upper(resName);
|
||||||
|
|
||||||
|
hagFile.open(getResourceFilename(resName));
|
||||||
|
|
||||||
|
// Validate hag file header
|
||||||
|
char headerBuffer[16];
|
||||||
|
if ((hagFile.read(headerBuffer, 16) != 16) || (strncmp(headerBuffer, madsConcatString, 10) != 0))
|
||||||
|
error("Invalid HAG file opened");
|
||||||
|
|
||||||
|
int numEntries = hagFile.readUint16LE();
|
||||||
|
|
||||||
|
int resIndex = -1;
|
||||||
|
while (++resIndex < numEntries) {
|
||||||
|
// Read in the details of the next resource
|
||||||
|
char resourceBuffer[14];
|
||||||
|
offset = hagFile.readUint32LE();
|
||||||
|
size = hagFile.readUint32LE();
|
||||||
|
hagFile.read(resourceBuffer, 14);
|
||||||
|
|
||||||
|
if (!strcmp(resName, resourceBuffer))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resIndex == numEntries)
|
||||||
|
error("Invalid resource '%s' specified", resourceName);
|
||||||
|
|
||||||
|
// Get the resource, either loading it in it's entirely or getting a stream reference
|
||||||
|
|
||||||
|
if (loadFlag) {
|
||||||
|
hagFile.seek(offset);
|
||||||
|
return hagFile.readStream(size);
|
||||||
|
} else {
|
||||||
|
return new Common::SeekableSubReadStream(&hagFile, offset, offset + size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MADSResourceManager::resourceExists(const char *resourceName) {
|
||||||
|
Common::File hagFile;
|
||||||
|
uint32 offset, size;
|
||||||
|
|
||||||
|
// If the first character is the wildcard (resource indicator), skip over it
|
||||||
|
if (*resourceName == '*')
|
||||||
|
++resourceName;
|
||||||
|
|
||||||
|
char resName[20];
|
||||||
|
strcpy(resName, resourceName);
|
||||||
|
str_upper(resName);
|
||||||
|
|
||||||
|
hagFile.open(getResourceFilename(resName));
|
||||||
|
|
||||||
|
// Validate hag file header
|
||||||
|
char headerBuffer[16];
|
||||||
|
if ((hagFile.read(headerBuffer, 16) != 16) || (strncmp(headerBuffer, madsConcatString, 10) != 0))
|
||||||
|
error("Invalid HAG file opened");
|
||||||
|
|
||||||
|
int numEntries = hagFile.readUint16LE();
|
||||||
|
|
||||||
|
int resIndex = -1;
|
||||||
|
while (++resIndex < numEntries) {
|
||||||
|
// Read in the details of the next resource
|
||||||
|
char resourceBuffer[14];
|
||||||
|
offset = hagFile.readUint32LE();
|
||||||
|
size = hagFile.readUint32LE();
|
||||||
|
hagFile.read(resourceBuffer, 14);
|
||||||
|
|
||||||
|
if (!strcmp(resName, resourceBuffer))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resIndex == numEntries)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
M4ResourceManager::M4ResourceManager(M4Engine *vm): ResourceManager(vm) {
|
||||||
|
_hfs = new FileSystem(_vm->getGameFile(kFileTypeHash));
|
||||||
|
}
|
||||||
|
|
||||||
|
M4ResourceManager::~M4ResourceManager() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::SeekableReadStream *M4ResourceManager::loadResource(const char *resourceName, bool preloadFlag) {
|
||||||
|
//printf("M4ResourceManager::loadResource() loading resource %s\n", resourceName);
|
||||||
|
Common::SeekableReadStream* result = NULL;
|
||||||
|
if (_hfs) {
|
||||||
|
// actually load the resource
|
||||||
|
result = _hfs->loadFile(resourceName, preloadFlag);
|
||||||
|
if (!result) {
|
||||||
|
error("M4ResourceManager::loadResource() Resource %s not found", resourceName);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error("M4ResourceManager::loadResource() No FileSystem attached");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool M4ResourceManager::resourceExists(const char *resourceName) {
|
||||||
|
return (_hfs->getHashFileEntry(resourceName) != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
141
engines/m4/resource.h
Normal file
141
engines/m4/resource.h
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_RESOURCE_H
|
||||||
|
#define M4_RESOURCE_H
|
||||||
|
|
||||||
|
#include "common/scummsys.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
#include "common/endian.h"
|
||||||
|
#include "common/file.h"
|
||||||
|
#include "common/hashmap.h"
|
||||||
|
#include "common/hash-str.h"
|
||||||
|
#include "common/list.h"
|
||||||
|
#include "common/ptr.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO:
|
||||||
|
- change array to HashMap if it turns out to be slow
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define MAX_RESOURCES 128
|
||||||
|
#define kM4MaxFilenameSize 33
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kResourcePurge = 1 << 1
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class FileSystem {
|
||||||
|
public:
|
||||||
|
struct HashHagEntry {
|
||||||
|
char filename[kM4MaxFilenameSize];
|
||||||
|
byte fileIndex;
|
||||||
|
Common::File *hagFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HashFileEntry {
|
||||||
|
char filename[kM4MaxFilenameSize];
|
||||||
|
byte hagfile;
|
||||||
|
uint32 offset, size;
|
||||||
|
};
|
||||||
|
|
||||||
|
FileSystem(const char *hashFilename);
|
||||||
|
~FileSystem();
|
||||||
|
|
||||||
|
Common::SeekableReadStream *loadFile(const char *resourceName, bool preloadFlag);
|
||||||
|
static void changeExtension(char *destName, const char *sourceName, const char *extension);
|
||||||
|
const HashFileEntry *getHashFileEntry(const char *filename);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef Common::HashMap<Common::String,HashFileEntry,Common::IgnoreCase_Hash,Common::IgnoreCase_EqualTo> FileHashMap;
|
||||||
|
|
||||||
|
HashHagEntry _hagEntries[10]; // GLOBAL.HAG and SECTION1.HAG to SECTION9.HAG
|
||||||
|
FileHashMap _fileEntries;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Resource {
|
||||||
|
char name[64];
|
||||||
|
Common::SeekableReadStream *stream;
|
||||||
|
uint8 *buffer;
|
||||||
|
uint8 flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResourceManager {
|
||||||
|
protected:
|
||||||
|
typedef Common::List<Common::SharedPtr<Resource> > ResourceList;
|
||||||
|
typedef ResourceList::iterator ResourceIterator;
|
||||||
|
ResourceList _resources;
|
||||||
|
M4Engine *_vm;
|
||||||
|
|
||||||
|
virtual Common::SeekableReadStream *loadResource(const char *resourceName, bool loadFlag) = 0;
|
||||||
|
public:
|
||||||
|
ResourceManager(M4Engine *vm): _vm(vm) {};
|
||||||
|
virtual ~ResourceManager();
|
||||||
|
|
||||||
|
Common::SeekableReadStream *get(const char *resourceName, bool loadFlag = true);
|
||||||
|
void toss(const char *resourceName);
|
||||||
|
void purge();
|
||||||
|
void dump();
|
||||||
|
virtual bool resourceExists(const char *resourceName) = 0;
|
||||||
|
|
||||||
|
Common::SeekableReadStream *openFile(const char *resourceName) { return get(resourceName, false); }
|
||||||
|
void changeExtension(char *destName, const char *sourceName, const char *extension) {
|
||||||
|
FileSystem::changeExtension(destName, sourceName, extension);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ResourceType {RESTYPE_ROOM, RESTYPE_SC, RESTYPE_TEXT, RESTYPE_QUO, RESTYPE_I,
|
||||||
|
RESTYPE_OB, RESTYPE_FONT, RESTYPE_SOUND, RESTYPE_SPEECH, RESTYPE_HAS_EXT, RESTYPE_NO_EXT};
|
||||||
|
|
||||||
|
class MADSResourceManager: public ResourceManager {
|
||||||
|
private:
|
||||||
|
ResourceType getResourceType(const char *resourceName);
|
||||||
|
const char *getResourceFilename(const char *resourceName);
|
||||||
|
protected:
|
||||||
|
Common::SeekableReadStream *loadResource(const char *resourceName, bool loadFlag);
|
||||||
|
public:
|
||||||
|
MADSResourceManager(M4Engine *vm): ResourceManager(vm) {};
|
||||||
|
bool resourceExists(const char *resourceName);
|
||||||
|
};
|
||||||
|
|
||||||
|
class M4ResourceManager: public ResourceManager {
|
||||||
|
protected:
|
||||||
|
Common::SeekableReadStream *loadResource(const char *resourceName, bool loadFlag);
|
||||||
|
public:
|
||||||
|
M4ResourceManager(M4Engine *vm);
|
||||||
|
~M4ResourceManager();
|
||||||
|
bool resourceExists(const char *resourceName);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FileSystem *_hfs;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
168
engines/m4/saveload.cpp
Normal file
168
engines/m4/saveload.cpp
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/file.h"
|
||||||
|
#include "common/savefile.h"
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/saveload.h"
|
||||||
|
#include "m4/sprite.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
const char *orionSavesList = "saves.dir";
|
||||||
|
|
||||||
|
SaveLoad::SaveLoad(M4Engine *vm) : _vm(vm) {
|
||||||
|
// For Orion Burger, check the existance of a 'saves.dir' file to determine whether to
|
||||||
|
// act exactly like the original. Otherwise, we'll use the ScummVM standard, where we'll
|
||||||
|
// keep all the data for a savegame in a single file
|
||||||
|
|
||||||
|
Common::File file;
|
||||||
|
_emulateOriginal = file.exists(orionSavesList);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *SaveLoad::generateSaveName(int slotNumber) {
|
||||||
|
static char buffer[15];
|
||||||
|
|
||||||
|
sprintf(buffer, _emulateOriginal ? "burg%.3d.sav" : "burger.%.3d", slotNumber);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SaveLoad::hasSaves() {
|
||||||
|
// Return true if a savegame file exists for the first slot
|
||||||
|
|
||||||
|
if (_emulateOriginal) {
|
||||||
|
Common::File f;
|
||||||
|
return f.exists(generateSaveName(1));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Common::ReadStream *f = _vm->saveManager()->openForLoading(generateSaveName(1));
|
||||||
|
if (f == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
delete f;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveGameList *SaveLoad::getSaves() {
|
||||||
|
SaveGameList *result = new SaveGameList();
|
||||||
|
char saveName[MAX_SAVEGAME_NAME];
|
||||||
|
Common::ReadStream *f = NULL;
|
||||||
|
|
||||||
|
if (_emulateOriginal) {
|
||||||
|
Common::File *saveFile = new Common::File();
|
||||||
|
saveFile->open(orionSavesList);
|
||||||
|
f = saveFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int slotNumber = 1; slotNumber <= 99; ++slotNumber) {
|
||||||
|
if (_emulateOriginal) {
|
||||||
|
// Read in savegame name from save directory
|
||||||
|
bool isPresent = (f->readByte() != 0);
|
||||||
|
f->read(&saveName[0], MAX_SAVEGAME_NAME);
|
||||||
|
|
||||||
|
if (isPresent)
|
||||||
|
result->push_back(Common::String(saveName));
|
||||||
|
else {
|
||||||
|
result->push_back(Common::String());
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Read in savegame name from savegame files directly
|
||||||
|
Common::ReadStream *saveFile = _vm->saveManager()->openForLoading(
|
||||||
|
generateSaveName(slotNumber));
|
||||||
|
if (!saveFile) {
|
||||||
|
// No savegame prsent at that slot
|
||||||
|
result->push_back(Common::String());
|
||||||
|
} else {
|
||||||
|
// Skip over byte offset
|
||||||
|
assert(saveFile->readUint32LE() < 0x100);
|
||||||
|
|
||||||
|
// Read in savegame name
|
||||||
|
saveFile->read(&saveName[0], MAX_SAVEGAME_NAME);
|
||||||
|
result->push_back(Common::String(saveName));
|
||||||
|
|
||||||
|
delete saveFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_emulateOriginal)
|
||||||
|
delete f;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
M4Surface *SaveLoad::getThumbnail(int slotNumber) {
|
||||||
|
Common::SeekableReadStream *saveFile;
|
||||||
|
uint32 dataOffset;
|
||||||
|
|
||||||
|
if (_emulateOriginal) {
|
||||||
|
// Get savegame file from original game folder
|
||||||
|
Common::File *f = new Common::File();
|
||||||
|
if (!f->open(generateSaveName(slotNumber))) {
|
||||||
|
delete f;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
saveFile = f;
|
||||||
|
} else {
|
||||||
|
// Open up savegame for access via savefile manager
|
||||||
|
saveFile = _vm->saveManager()->openForLoading(generateSaveName(slotNumber));
|
||||||
|
}
|
||||||
|
if (!saveFile)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
dataOffset = saveFile->readUint32LE();
|
||||||
|
assert(dataOffset < 0x100);
|
||||||
|
saveFile->seek(dataOffset, SEEK_CUR);
|
||||||
|
|
||||||
|
// Read in the sprite data
|
||||||
|
|
||||||
|
saveFile->seek(16, SEEK_CUR);
|
||||||
|
int width = saveFile->readUint32LE();
|
||||||
|
int height = saveFile->readUint32LE();
|
||||||
|
saveFile->seek(21, SEEK_CUR);
|
||||||
|
saveFile->readUint32LE(); // sprite data size
|
||||||
|
|
||||||
|
M4Sprite *result = new M4Sprite(saveFile, 0, 0, width, height);
|
||||||
|
delete saveFile;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SaveLoad::load(int slotNumber) {
|
||||||
|
// TODO: Currently it's hardcoded to return a failure
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SaveLoad::save(int slotNumber, Common::String saveName) {
|
||||||
|
// TODO: Currently it's hardcoded to return a failure
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // End of namespace M4
|
57
engines/m4/saveload.h
Normal file
57
engines/m4/saveload.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_SAVELOAD_H
|
||||||
|
#define M4_SAVELOAD_H
|
||||||
|
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "common/ptr.h"
|
||||||
|
|
||||||
|
#define MAX_SAVEGAME_NAME 80
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
typedef Common::List<Common::String> SaveGameList;
|
||||||
|
typedef SaveGameList::iterator SaveGameIterator;
|
||||||
|
|
||||||
|
class SaveLoad {
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
bool _emulateOriginal;
|
||||||
|
|
||||||
|
const char *generateSaveName(int slotNumber);
|
||||||
|
public:
|
||||||
|
SaveLoad(M4Engine *vm);
|
||||||
|
|
||||||
|
bool hasSaves();
|
||||||
|
SaveGameList *getSaves();
|
||||||
|
M4Surface *getThumbnail(int slotNumber);
|
||||||
|
bool load(int slotNumber);
|
||||||
|
bool save(int slotNumber, Common::String saveName);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
669
engines/m4/scene.cpp
Normal file
669
engines/m4/scene.cpp
Normal file
|
@ -0,0 +1,669 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/system.h"
|
||||||
|
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/scene.h"
|
||||||
|
#include "m4/events.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/rails.h"
|
||||||
|
#include "m4/font.h"
|
||||||
|
#include "m4/m4_views.h"
|
||||||
|
#include "m4/compression.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
Scene::Scene(M4Engine *vm): View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())) {
|
||||||
|
_screenType = VIEWID_SCENE;
|
||||||
|
|
||||||
|
_sceneResources.hotspots = new HotSpotList();
|
||||||
|
_sceneResources.parallax = new HotSpotList();
|
||||||
|
_sceneResources.props = new HotSpotList();
|
||||||
|
_backgroundSurface = new M4Surface();
|
||||||
|
_codeSurface = new M4Surface();
|
||||||
|
_madsInterfaceSurface = new M4Surface();
|
||||||
|
_sceneSprites = NULL;
|
||||||
|
_palData = NULL;
|
||||||
|
_interfacePal = NULL;
|
||||||
|
_inverseColorTable = NULL;
|
||||||
|
strcpy(_statusText, "");
|
||||||
|
_vm->_rails->setCodeSurface(_codeSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene::~Scene() {
|
||||||
|
_sceneResources.hotspots->clear();
|
||||||
|
_sceneResources.parallax->clear();
|
||||||
|
_sceneResources.props->clear();
|
||||||
|
|
||||||
|
delete _sceneResources.hotspots;
|
||||||
|
delete _sceneResources.parallax;
|
||||||
|
delete _sceneResources.props;
|
||||||
|
|
||||||
|
delete _backgroundSurface;
|
||||||
|
delete _codeSurface;
|
||||||
|
delete _madsInterfaceSurface;
|
||||||
|
|
||||||
|
if (_sceneSprites)
|
||||||
|
delete _sceneSprites;
|
||||||
|
|
||||||
|
_vm->_palette->deleteAllRanges();
|
||||||
|
|
||||||
|
if (_palData)
|
||||||
|
delete _palData;
|
||||||
|
|
||||||
|
if (_interfacePal)
|
||||||
|
delete _interfacePal;
|
||||||
|
|
||||||
|
if (_inverseColorTable)
|
||||||
|
delete[] _inverseColorTable;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::loadScene(int sceneNumber) {
|
||||||
|
_currentScene = sceneNumber;
|
||||||
|
|
||||||
|
// Close the menu if it's active
|
||||||
|
if (!_vm->isM4()) {
|
||||||
|
View *mainMenu = _vm->_viewManager->getView(VIEWID_MAINMENU);
|
||||||
|
if (mainMenu != NULL) {
|
||||||
|
_vm->_viewManager->deleteView(mainMenu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if we were loading a game
|
||||||
|
|
||||||
|
|
||||||
|
// Load scene background and set palette
|
||||||
|
if (_palData) {
|
||||||
|
_vm->_palette->deleteRange(_palData);
|
||||||
|
delete _palData;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_interfacePal) {
|
||||||
|
_vm->_palette->deleteRange(_interfacePal);
|
||||||
|
delete _interfacePal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_vm->isM4()) {
|
||||||
|
_backgroundSurface->loadBackground(sceneNumber);
|
||||||
|
_palData = NULL;
|
||||||
|
} else {
|
||||||
|
_backgroundSurface->loadBackground(sceneNumber, &_palData);
|
||||||
|
_vm->_palette->addRange(_palData);
|
||||||
|
_backgroundSurface->translate(_palData);
|
||||||
|
|
||||||
|
if (sceneNumber < 900) {
|
||||||
|
/*_backgroundSurface->fillRect(Common::Rect(0, MADS_SURFACE_HEIGHT,
|
||||||
|
_backgroundSurface->width(), _backgroundSurface->height()),
|
||||||
|
_vm->_palette->BLACK);*/
|
||||||
|
// TODO: interface palette
|
||||||
|
_madsInterfaceSurface->madsloadInterface(0, &_interfacePal);
|
||||||
|
_vm->_palette->addRange(_interfacePal);
|
||||||
|
_madsInterfaceSurface->translate(_interfacePal);
|
||||||
|
_backgroundSurface->copyFrom(_madsInterfaceSurface, Common::Rect(0, 0, 320, 44), 0, 200 - 44);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_vm->getGameType() == GType_Burger &&
|
||||||
|
sceneNumber != TITLE_SCENE_BURGER && sceneNumber != MAINMENU_SCENE_BURGER)
|
||||||
|
_vm->_interfaceView->setStatusText("");
|
||||||
|
|
||||||
|
// Load scene def file (*.CHK)
|
||||||
|
if (_vm->isM4()) {
|
||||||
|
loadSceneResources(sceneNumber);
|
||||||
|
loadSceneInverseColorTable(sceneNumber);
|
||||||
|
} else {
|
||||||
|
// Don't load other screen resources for system screens
|
||||||
|
if (sceneNumber >= 900)
|
||||||
|
return;
|
||||||
|
|
||||||
|
loadSceneHotSpotsMads(sceneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: set walker scaling
|
||||||
|
// TODO: destroy woodscript buffer
|
||||||
|
|
||||||
|
// Load scene walk path file (*.COD/*.WW?)
|
||||||
|
loadSceneCodes(sceneNumber);
|
||||||
|
|
||||||
|
// Load inverse color table file (*.IPL)
|
||||||
|
loadSceneInverseColorTable(sceneNumber);
|
||||||
|
|
||||||
|
if (_vm->isM4()) {
|
||||||
|
|
||||||
|
if (_vm->getGameType() != GType_Burger) {
|
||||||
|
// Load scene sprites file (*.SSB)
|
||||||
|
loadSceneSprites(sceneNumber);
|
||||||
|
|
||||||
|
// Load scene sprite codes file (*.SSC)
|
||||||
|
loadSceneSpriteCodes(sceneNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (sceneNumber != TITLE_SCENE_BURGER && sceneNumber != MAINMENU_SCENE_BURGER) {
|
||||||
|
_vm->_interfaceView->show();
|
||||||
|
showSprites();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge resources
|
||||||
|
_vm->res()->purge();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::loadSceneResources(int sceneNumber) {
|
||||||
|
char filename[kM4MaxFilenameSize];
|
||||||
|
int i = 0, x = 0, y = 0;
|
||||||
|
sprintf(filename, "%i.chk", sceneNumber);
|
||||||
|
|
||||||
|
Common::SeekableReadStream *sceneS = _vm->res()->get(filename);
|
||||||
|
|
||||||
|
if (sceneS != NULL) {
|
||||||
|
sceneS->read(_sceneResources.artBase, MAX_CHK_FILENAME_SIZE);
|
||||||
|
sceneS->read(_sceneResources.pictureBase, MAX_CHK_FILENAME_SIZE);
|
||||||
|
_sceneResources.hotspotCount = sceneS->readUint32LE();
|
||||||
|
_sceneResources.parallaxCount = sceneS->readUint32LE();
|
||||||
|
_sceneResources.propsCount = sceneS->readUint32LE();
|
||||||
|
_sceneResources.frontY = sceneS->readUint32LE();
|
||||||
|
_sceneResources.backY = sceneS->readUint32LE();
|
||||||
|
_sceneResources.frontScale = sceneS->readUint32LE();
|
||||||
|
_sceneResources.backScale = sceneS->readUint32LE();
|
||||||
|
for (i = 0; i < 16; i++)
|
||||||
|
_sceneResources.depthTable[i] = sceneS->readUint16LE();
|
||||||
|
_sceneResources.railNodeCount = sceneS->readUint32LE();
|
||||||
|
|
||||||
|
// Clear rails from previous scene
|
||||||
|
_vm->_rails->clearRails();
|
||||||
|
|
||||||
|
for (i = 0; i < _sceneResources.railNodeCount; i++) {
|
||||||
|
x = sceneS->readUint32LE();
|
||||||
|
y = sceneS->readUint32LE();
|
||||||
|
if (_vm->_rails->addRailNode(x, y, true) < 0) {
|
||||||
|
warning("Too many rail nodes defined for scene");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear current hotspot lists
|
||||||
|
_sceneResources.hotspots->clear();
|
||||||
|
_sceneResources.parallax->clear();
|
||||||
|
_sceneResources.props->clear();
|
||||||
|
|
||||||
|
_sceneResources.hotspots->loadHotSpotsM4(sceneS, _sceneResources.hotspotCount);
|
||||||
|
_sceneResources.parallax->loadHotSpotsM4(sceneS, _sceneResources.parallaxCount);
|
||||||
|
_sceneResources.props->loadHotSpotsM4(sceneS, _sceneResources.propsCount);
|
||||||
|
|
||||||
|
// Note that toss() deletes the MemoryReadStream
|
||||||
|
_vm->res()->toss(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::loadSceneHotSpotsMads(int sceneNumber) {
|
||||||
|
char filename[kM4MaxFilenameSize];
|
||||||
|
sprintf(filename, "rm%i.hh", sceneNumber);
|
||||||
|
MadsPack hotSpotData(filename, _vm);
|
||||||
|
Common::SeekableReadStream *hotspotStream = hotSpotData.getItemStream(0);
|
||||||
|
|
||||||
|
int hotspotCount = hotspotStream->readUint16LE();
|
||||||
|
delete hotspotStream;
|
||||||
|
|
||||||
|
HotSpotList *hotspotList = _sceneResources.hotspots;
|
||||||
|
_sceneResources.hotspotCount = hotspotCount;
|
||||||
|
|
||||||
|
hotspotStream = hotSpotData.getItemStream(1);
|
||||||
|
|
||||||
|
// Clear current hotspot lists
|
||||||
|
_sceneResources.hotspots->clear();
|
||||||
|
|
||||||
|
_sceneResources.hotspots->loadHotSpotsMads(hotspotStream, _sceneResources.hotspotCount);
|
||||||
|
|
||||||
|
delete hotspotStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::loadSceneCodes(int sceneNumber, int index) {
|
||||||
|
char filename[kM4MaxFilenameSize];
|
||||||
|
Common::SeekableReadStream *sceneS;
|
||||||
|
|
||||||
|
if (_vm->isM4()) {
|
||||||
|
sprintf(filename, "%i.cod", sceneNumber);
|
||||||
|
sceneS = _vm->res()->openFile(filename);
|
||||||
|
_codeSurface->loadCodesM4(sceneS);
|
||||||
|
_vm->res()->toss(filename);
|
||||||
|
} else if (_vm->getGameType() == GType_Phantom || _vm->getGameType() == GType_DragonSphere) {
|
||||||
|
sprintf(filename, "rm%i.ww%i", sceneNumber, index);
|
||||||
|
MadsPack walkData(filename, _vm);
|
||||||
|
sceneS = walkData.getItemStream(0);
|
||||||
|
_codeSurface->loadCodesMads(sceneS);
|
||||||
|
_vm->res()->toss(filename);
|
||||||
|
} else if (_vm->getGameType() == GType_RexNebular) {
|
||||||
|
// TODO
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::loadSceneInverseColorTable(int sceneNumber) {
|
||||||
|
char filename[kM4MaxFilenameSize];
|
||||||
|
Common::SeekableReadStream *iplS;
|
||||||
|
|
||||||
|
if (_vm->isM4()) {
|
||||||
|
sprintf(filename, "%i.ipl", sceneNumber);
|
||||||
|
iplS = _vm->res()->openFile(filename);
|
||||||
|
if (_inverseColorTable)
|
||||||
|
delete[] _inverseColorTable;
|
||||||
|
_inverseColorTable = new byte[iplS->size()];
|
||||||
|
iplS->read(_inverseColorTable, iplS->size());
|
||||||
|
_vm->res()->toss(filename);
|
||||||
|
} else {
|
||||||
|
// TODO?
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Scene::loadSceneSprites(int sceneNumber) {
|
||||||
|
char filename[kM4MaxFilenameSize];
|
||||||
|
sprintf(filename, "%i.ssb", sceneNumber);
|
||||||
|
|
||||||
|
Common::SeekableReadStream *sceneS = _vm->res()->get(filename);
|
||||||
|
_sceneSprites = new SpriteAsset(_vm, sceneS, sceneS->size(), filename);
|
||||||
|
_vm->res()->toss(filename);
|
||||||
|
|
||||||
|
printf("Scene has %d sprites, each one having %d colors\n", _sceneSprites->getCount(), _sceneSprites->getColorCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::loadSceneSpriteCodes(int sceneNumber) {
|
||||||
|
char filename[kM4MaxFilenameSize];
|
||||||
|
sprintf(filename, "%i.ssc", sceneNumber);
|
||||||
|
|
||||||
|
Common::SeekableReadStream *sceneS = _vm->res()->get(filename);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
if (sceneS != NULL) {
|
||||||
|
SpriteAsset* _sceneSpriteCodes = new SpriteAsset(_vm, sceneS, sceneS->size(), filename);
|
||||||
|
int colorCount = _sceneSpriteCodes->getColorCount();
|
||||||
|
// RGB8* spritePalette = _sceneSpriteCodes->getPalette();
|
||||||
|
//_vm->_palette->setPalette(spritePalette, 0, colorCount);
|
||||||
|
|
||||||
|
printf("Scene has %d sprite codes, each one having %d colors\n", _sceneSpriteCodes->getCount(), colorCount);
|
||||||
|
|
||||||
|
// Note that toss() deletes the MemoryReadStream
|
||||||
|
_vm->res()->toss(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::showSprites() {
|
||||||
|
// TODO: This is all experimental code, it needs heavy restructuring
|
||||||
|
// and cleanup
|
||||||
|
|
||||||
|
// taken from set_walker_scaling() in adv_walk.cpp. A proper implementation will need
|
||||||
|
// to store these in global variables
|
||||||
|
int minScaling = FixedDiv(_sceneResources.backScale << 16, 100 << 16);
|
||||||
|
int maxScaling = FixedDiv(_sceneResources.frontScale << 16, 100 << 16);
|
||||||
|
int scaler;
|
||||||
|
|
||||||
|
_vm->_actor->setWalkerDirection(kFacingSouthEast);
|
||||||
|
//_vm->_actor->setWalkerPalette();
|
||||||
|
|
||||||
|
// taken from set_walker_scaling() in adv_walk.cpp
|
||||||
|
if (_sceneResources.frontY == _sceneResources.backY)
|
||||||
|
scaler = 0;
|
||||||
|
else
|
||||||
|
scaler = FixedDiv(maxScaling - minScaling,
|
||||||
|
(_sceneResources.frontY << 16) - (_sceneResources.backY << 16));
|
||||||
|
|
||||||
|
// FIXME: For now, we (incorrectly) scale the walker to 50% of the scene's max scaling
|
||||||
|
_vm->_actor->setWalkerScaling(scaler / 2);
|
||||||
|
// Test code to display the protagonist
|
||||||
|
_vm->_actor->placeWalkerSpriteAt(0, 320, 200);
|
||||||
|
|
||||||
|
// Test code to display scene sprites
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::checkHotspotAtMousePos(int x, int y) {
|
||||||
|
if (_vm->getGameType() == GType_Riddle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: loads of things to do here, only the mouse cursor and the status
|
||||||
|
// text is changed for now
|
||||||
|
|
||||||
|
// Only scene hotspots are checked for now, not parallax/props, as the
|
||||||
|
// latter ones are not used by Orion Burger
|
||||||
|
HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y);
|
||||||
|
if (currentHotSpot != NULL && currentHotSpot->getActive()) {
|
||||||
|
if (_vm->_mouse->getCursorNum() != CURSOR_LOOK &&
|
||||||
|
_vm->_mouse->getCursorNum() != CURSOR_TAKE &&
|
||||||
|
_vm->_mouse->getCursorNum() != CURSOR_USE &&
|
||||||
|
_vm->_interfaceView->_inventory.getSelectedIndex() == -1) {
|
||||||
|
_vm->_mouse->setCursorNum(currentHotSpot->getCursor());
|
||||||
|
}
|
||||||
|
_vm->_interfaceView->setStatusText(currentHotSpot->getPrep());
|
||||||
|
} else {
|
||||||
|
if (_vm->_mouse->getCursorNum() != CURSOR_LOOK &&
|
||||||
|
_vm->_mouse->getCursorNum() != CURSOR_TAKE &&
|
||||||
|
_vm->_mouse->getCursorNum() != CURSOR_USE &&
|
||||||
|
_vm->_interfaceView->_inventory.getSelectedIndex() == -1) {
|
||||||
|
_vm->_mouse->setCursorNum(0);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::checkHotspotAtMousePosMads(int x, int y) {
|
||||||
|
HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y);
|
||||||
|
if (currentHotSpot != NULL) {
|
||||||
|
_vm->_mouse->setCursorNum(currentHotSpot->getCursor());
|
||||||
|
|
||||||
|
// This is the "easy" interface, which updates the status text when the mouse is moved
|
||||||
|
// TODO: toggle this code for easy/normal interface mode
|
||||||
|
char statusText[50];
|
||||||
|
if (currentHotSpot->getVerbID() != 0) {
|
||||||
|
sprintf(statusText, "%s %s\n", currentHotSpot->getVerb(), currentHotSpot->getVocab());
|
||||||
|
} else {
|
||||||
|
sprintf(statusText, "%s %s\n", _vm->_globals->getVocab(kVerbWalkTo), currentHotSpot->getVocab());
|
||||||
|
}
|
||||||
|
|
||||||
|
statusText[0] = toupper(statusText[0]); // capitalize first letter
|
||||||
|
setMADSStatusText(statusText);
|
||||||
|
} else {
|
||||||
|
_vm->_mouse->setCursorNum(0);
|
||||||
|
setMADSStatusText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test function, shows all scene hotspots
|
||||||
|
void Scene::showHotSpots() {
|
||||||
|
int i = 0;
|
||||||
|
HotSpot *currentHotSpot;
|
||||||
|
// hotspots (green)
|
||||||
|
for (i = 0; i < _sceneResources.hotspotCount; i++) {
|
||||||
|
currentHotSpot = _sceneResources.hotspots->get(i);
|
||||||
|
_backgroundSurface->frameRect(currentHotSpot->getRect(), _vm->_palette->GREEN);
|
||||||
|
}
|
||||||
|
if (_vm->isM4()) {
|
||||||
|
// parallax (yellow)
|
||||||
|
for (i = 0; i < _sceneResources.parallaxCount; i++) {
|
||||||
|
currentHotSpot = _sceneResources.parallax->get(i);
|
||||||
|
_backgroundSurface->frameRect(currentHotSpot->getRect(), _vm->_palette->YELLOW);
|
||||||
|
}
|
||||||
|
// props (red)
|
||||||
|
for (i = 0; i < _sceneResources.propsCount; i++) {
|
||||||
|
currentHotSpot = _sceneResources.props->get(i);
|
||||||
|
_backgroundSurface->frameRect(currentHotSpot->getRect(), _vm->_palette->RED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test function, shows all scene codes
|
||||||
|
void Scene::showCodes() {
|
||||||
|
uint8 *pixelData = (uint8*)_codeSurface->pixels;
|
||||||
|
for (int i = 0; i < _codeSurface->w * _codeSurface->h; i++)
|
||||||
|
if (pixelData[i] & 0x10)
|
||||||
|
pixelData[i] = 0xFF;
|
||||||
|
else
|
||||||
|
pixelData[i] = 0;
|
||||||
|
|
||||||
|
byte colors[256 * 4];
|
||||||
|
memset(colors, 0, sizeof(colors));
|
||||||
|
colors[255 * 4 + 0] = 255;
|
||||||
|
colors[255 * 4 + 1] = 255;
|
||||||
|
colors[255 * 4 + 2] = 255;
|
||||||
|
_vm->_palette->setPalette(colors, 0, 256);
|
||||||
|
|
||||||
|
_backgroundSurface->copyFrom(_codeSurface, Common::Rect(0, 0, 640, 480), 0, 0);
|
||||||
|
//_system->copyRectToScreen((byte *)codes->pixels, codes->w, 0, 0, codes->w, codes->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::playIntro() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::update() {
|
||||||
|
// TODO: Needs a proper implementation
|
||||||
|
// NOTE: Don't copy the background when in M4 mode or WoodScript anims won't be shown
|
||||||
|
if (!_vm->isM4()) {
|
||||||
|
_backgroundSurface->copyTo(this);
|
||||||
|
|
||||||
|
if (_statusText[0]) {
|
||||||
|
// Text colors are inverted in Dragonsphere
|
||||||
|
if (_vm->getGameType() == GType_DragonSphere)
|
||||||
|
_vm->_font->setColors(_vm->_palette->BLACK, _vm->_palette->WHITE, _vm->_palette->BLACK);
|
||||||
|
else
|
||||||
|
_vm->_font->setColors(_vm->_palette->WHITE, _vm->_palette->BLACK, _vm->_palette->BLACK);
|
||||||
|
|
||||||
|
_vm->_font->setFont(FONT_MAIN_MADS);
|
||||||
|
_vm->_font->writeString(this, _statusText, (width() - _vm->_font->getWidth(_statusText)) / 2, 142, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::onRefresh(RectList *rects, M4Surface *destSurface) {
|
||||||
|
update();
|
||||||
|
View::onRefresh(rects, destSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Scene::onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents) {
|
||||||
|
//if (_vm->getGameType() != GType_Burger)
|
||||||
|
// return false;
|
||||||
|
|
||||||
|
// If the game is currently paused, don't do any scene processing
|
||||||
|
if (_vm->_kernel->paused)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (eventType) {
|
||||||
|
case MEVENT_LEFT_CLICK:
|
||||||
|
{
|
||||||
|
if (_vm->getGameType() == GType_Burger) {
|
||||||
|
// Place a Wilbur sprite with the correct facing
|
||||||
|
HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y);
|
||||||
|
if (currentHotSpot != NULL && currentHotSpot->getActive()) {
|
||||||
|
update();
|
||||||
|
_vm->_actor->setWalkerDirection(currentHotSpot->getFacing());
|
||||||
|
int posX = currentHotSpot->getFeetX();
|
||||||
|
int posY = currentHotSpot->getFeetY() -
|
||||||
|
scaleValue(_vm->_actor->getWalkerHeight(), _vm->_actor->getWalkerScaling(), 0);
|
||||||
|
//_vm->_actor->placeWalkerSpriteAt(0, posX, posY);
|
||||||
|
|
||||||
|
// Player said.... (for scene scripts)
|
||||||
|
printf("Player said: %s %s\n", currentHotSpot->getVerb(), currentHotSpot->getVocab());
|
||||||
|
|
||||||
|
// FIXME: This should be moved somewhere else, and is incomplete
|
||||||
|
if (_vm->_interfaceView->_inventory.getSelectedIndex() == -1) {
|
||||||
|
if (_vm->_mouse->getVerb() == NULL) {
|
||||||
|
strcpy(_vm->_player->verb, currentHotSpot->getVerb());
|
||||||
|
} else {
|
||||||
|
strcpy(_vm->_player->verb, _vm->_mouse->getVerb());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
strcpy(_vm->_player->verb, _vm->_interfaceView->_inventory.getSelectedObjectName());
|
||||||
|
}
|
||||||
|
strcpy(_vm->_player->noun, currentHotSpot->getVocab());
|
||||||
|
strcpy(_vm->_player->object, "");
|
||||||
|
_vm->_player->commandReady = true;
|
||||||
|
|
||||||
|
printf("## Player said: %s %s\n", _vm->_player->verb, _vm->_player->noun);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_vm->isM4()) {
|
||||||
|
HotSpot *currentHotSpot = _sceneResources.hotspots->findByXY(x, y);
|
||||||
|
if (currentHotSpot != NULL) {
|
||||||
|
char statusText[50];
|
||||||
|
if (currentHotSpot->getVerbID() != 0) {
|
||||||
|
sprintf(statusText, "%s %s\n", currentHotSpot->getVerb(), currentHotSpot->getVocab());
|
||||||
|
} else {
|
||||||
|
sprintf(statusText, "%s %s\n", _vm->_globals->getVocab(kVerbWalkTo), currentHotSpot->getVocab());
|
||||||
|
}
|
||||||
|
|
||||||
|
statusText[0] = toupper(statusText[0]); // capitalize first letter
|
||||||
|
setMADSStatusText(statusText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MEVENT_RIGHT_CLICK:
|
||||||
|
if (_vm->getGameType() == GType_Burger) {
|
||||||
|
nextCommonCursor();
|
||||||
|
_vm->_interfaceView->_inventory.clearSelected();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MEVENT_MOVE:
|
||||||
|
if (_vm->isM4())
|
||||||
|
checkHotspotAtMousePos(x, y);
|
||||||
|
else
|
||||||
|
checkHotspotAtMousePosMads(x, y);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::nextCommonCursor() {
|
||||||
|
int cursorIndex = _vm->_mouse->getCursorNum();
|
||||||
|
|
||||||
|
switch (cursorIndex) {
|
||||||
|
case CURSOR_ARROW:
|
||||||
|
cursorIndex = CURSOR_LOOK;
|
||||||
|
break;
|
||||||
|
case CURSOR_LOOK:
|
||||||
|
cursorIndex = CURSOR_TAKE;
|
||||||
|
break;
|
||||||
|
case CURSOR_TAKE:
|
||||||
|
cursorIndex = CURSOR_USE;
|
||||||
|
break;
|
||||||
|
case CURSOR_USE:
|
||||||
|
cursorIndex = CURSOR_ARROW;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cursorIndex = CURSOR_ARROW;
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->_mouse->setCursorNum(cursorIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum boxSprites {
|
||||||
|
topLeft = 0,
|
||||||
|
topRight = 1,
|
||||||
|
bottomLeft = 2,
|
||||||
|
bottomRight = 3,
|
||||||
|
left = 4,
|
||||||
|
right = 5,
|
||||||
|
top = 6,
|
||||||
|
bottom = 7,
|
||||||
|
topMiddle = 8,
|
||||||
|
filler1 = 9,
|
||||||
|
filler2 = 10
|
||||||
|
// TODO: finish this
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: calculate width and height, show text, show face if it exists
|
||||||
|
// TODO: this has been tested with Dragonsphere only, there are some differences
|
||||||
|
// in the sprites used in Phantom
|
||||||
|
void Scene::showMADSV2TextBox(char *text, int x, int y, char *faceName) {
|
||||||
|
int repeatX = 40; // FIXME: this is hardcoded
|
||||||
|
int repeatY = 30; // FIXME: this is hardcoded
|
||||||
|
int curX = x, curY = y;
|
||||||
|
int topRightX = x; // TODO: this is probably not needed
|
||||||
|
Common::SeekableReadStream *data = _vm->res()->get("box.ss");
|
||||||
|
SpriteAsset *boxSprites = new SpriteAsset(_vm, data, data->size(), "box.ss");
|
||||||
|
_vm->res()->toss("box.ss");
|
||||||
|
|
||||||
|
RGBList *palData = new RGBList(boxSprites->getColorCount(), boxSprites->getPalette(), true);
|
||||||
|
_vm->_palette->addRange(palData);
|
||||||
|
|
||||||
|
for (int i = 0; i < boxSprites->getCount(); i++)
|
||||||
|
boxSprites->getFrame(i)->translate(palData); // sprite pixel translation
|
||||||
|
|
||||||
|
// Top left corner
|
||||||
|
boxSprites->getFrame(topLeft)->copyTo(_backgroundSurface, x, curY);
|
||||||
|
curX += boxSprites->getFrame(topLeft)->width();
|
||||||
|
|
||||||
|
// Top line
|
||||||
|
for (int i = 0; i < repeatX; i++) {
|
||||||
|
boxSprites->getFrame(top)->copyTo(_backgroundSurface, curX, curY + 3);
|
||||||
|
curX += boxSprites->getFrame(top)->width();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Top right corner
|
||||||
|
boxSprites->getFrame(topRight)->copyTo(_backgroundSurface, curX, curY);
|
||||||
|
topRightX = curX;
|
||||||
|
|
||||||
|
// Top middle
|
||||||
|
// FIXME: the transparent color for this is also the black border color
|
||||||
|
boxSprites->getFrame(topMiddle)->copyTo(_backgroundSurface,
|
||||||
|
x + (curX - x) / 2 - boxSprites->getFrame(topMiddle)->width() / 2,
|
||||||
|
curY - 5, 167);
|
||||||
|
curX = x;
|
||||||
|
curY += boxSprites->getFrame(topLeft)->height();
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Draw contents
|
||||||
|
for (int i = 0; i < repeatY; i++) {
|
||||||
|
for (int j = 0; j < repeatX; j++) {
|
||||||
|
if (j == 0) {
|
||||||
|
boxSprites->getFrame(left)->copyTo(_backgroundSurface, curX + 3, curY);
|
||||||
|
curX += boxSprites->getFrame(left)->width();
|
||||||
|
} else if (j == repeatX - 1) {
|
||||||
|
curX = topRightX - 2;
|
||||||
|
boxSprites->getFrame(right)->copyTo(_backgroundSurface, curX + 3, curY + 1);
|
||||||
|
} else {
|
||||||
|
// TODO: the background of the contents follows a pattern which is not understood yet
|
||||||
|
if (j % 2 == 0) {
|
||||||
|
boxSprites->getFrame(filler1)->copyTo(_backgroundSurface, curX + 3, curY);
|
||||||
|
curX += boxSprites->getFrame(filler1)->width();
|
||||||
|
} else {
|
||||||
|
boxSprites->getFrame(filler2)->copyTo(_backgroundSurface, curX + 3, curY);
|
||||||
|
curX += boxSprites->getFrame(filler2)->width();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // for j
|
||||||
|
curX = x;
|
||||||
|
curY += boxSprites->getFrame(left)->height();
|
||||||
|
} // for i
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------------------------
|
||||||
|
curX = x;
|
||||||
|
|
||||||
|
// Bottom left corner
|
||||||
|
boxSprites->getFrame(bottomLeft)->copyTo(_backgroundSurface, curX, curY);
|
||||||
|
curX += boxSprites->getFrame(bottomLeft)->width();
|
||||||
|
|
||||||
|
// Bottom line
|
||||||
|
for (int i = 0; i < repeatX; i++) {
|
||||||
|
boxSprites->getFrame(bottom)->copyTo(_backgroundSurface, curX, curY + 1);
|
||||||
|
curX += boxSprites->getFrame(bottom)->width();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bottom right corner
|
||||||
|
boxSprites->getFrame(bottomRight)->copyTo(_backgroundSurface, curX, curY + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
124
engines/m4/scene.h
Normal file
124
engines/m4/scene.h
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_SCENE_H
|
||||||
|
#define M4_SCENE_H
|
||||||
|
|
||||||
|
class View;
|
||||||
|
|
||||||
|
#include "m4/assets.h"
|
||||||
|
#include "m4/hotspot.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define TITLE_SCENE_BURGER 951 // 951 = intro, 901 = demo menu, 971 = first scene
|
||||||
|
#define MAINMENU_SCENE_BURGER 903
|
||||||
|
#define FIRST_SCENE 101
|
||||||
|
#define MAX_CHK_FILENAME_SIZE 144
|
||||||
|
|
||||||
|
#define INTERFACE_HEIGHT 106
|
||||||
|
#define MADS_SURFACE_HEIGHT 156
|
||||||
|
|
||||||
|
enum MADSVerbs {
|
||||||
|
kVerbLook = 2,
|
||||||
|
kVerbTake = 3,
|
||||||
|
kVerbPush = 4,
|
||||||
|
kVerbOpen = 5,
|
||||||
|
kVerbPut = 6,
|
||||||
|
kVerbTalkTo = 7,
|
||||||
|
kVerbGive = 8,
|
||||||
|
kVerbPull = 9,
|
||||||
|
kVerbClose = 10,
|
||||||
|
kVerbThrow = 11,
|
||||||
|
kVerbWalkTo = 12
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SceneResources {
|
||||||
|
char artBase[MAX_CHK_FILENAME_SIZE];
|
||||||
|
char pictureBase[MAX_CHK_FILENAME_SIZE];
|
||||||
|
int32 hotspotCount;
|
||||||
|
HotSpotList *hotspots;
|
||||||
|
int32 parallaxCount;
|
||||||
|
HotSpotList *parallax;
|
||||||
|
int32 propsCount;
|
||||||
|
HotSpotList *props;
|
||||||
|
int32 frontY, backY;
|
||||||
|
int32 frontScale, backScale;
|
||||||
|
int16 depthTable[16];
|
||||||
|
int32 railNodeCount; // # of rails
|
||||||
|
};
|
||||||
|
|
||||||
|
class Scene: public View {
|
||||||
|
public:
|
||||||
|
Scene(M4Engine *vm);
|
||||||
|
~Scene();
|
||||||
|
|
||||||
|
// TODO: perhaps move playIntro() someplace else?
|
||||||
|
void playIntro();
|
||||||
|
void loadScene(int sceneNumber);
|
||||||
|
void loadSceneResources(int sceneNumber);
|
||||||
|
void loadSceneHotSpotsMads(int sceneNumber);
|
||||||
|
void loadSceneCodes(int sceneNumber, int index = 0);
|
||||||
|
void loadSceneInverseColorTable(int sceneNumber);
|
||||||
|
void loadSceneSprites(int sceneNumber);
|
||||||
|
void loadSceneSpriteCodes(int sceneNumber);
|
||||||
|
void showSprites();
|
||||||
|
void checkHotspotAtMousePos(int x, int y);
|
||||||
|
void checkHotspotAtMousePosMads(int x, int y);
|
||||||
|
void showHotSpots();
|
||||||
|
void showCodes();
|
||||||
|
int getCurrentScene() { return _currentScene; }
|
||||||
|
SceneResources getSceneResources() { return _sceneResources; }
|
||||||
|
M4Surface *getBackgroundSurface() const { return _backgroundSurface; }
|
||||||
|
byte *getInverseColorTable() const { return _inverseColorTable; }
|
||||||
|
void update();
|
||||||
|
void setMADSStatusText(const char *text) { strcpy(_statusText, text); }
|
||||||
|
void showMADSV2TextBox(char *text, int x, int y, char *faceName);
|
||||||
|
|
||||||
|
void onRefresh(RectList *rects, M4Surface *destSurface);
|
||||||
|
bool onEvent(M4EventType eventType, int param1, int x, int y, bool &captureEvents);
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _currentScene;
|
||||||
|
M4Surface *_backgroundSurface;
|
||||||
|
M4Surface *_codeSurface;
|
||||||
|
M4Surface *_madsInterfaceSurface;
|
||||||
|
byte *_inverseColorTable;
|
||||||
|
RGBList *_palData;
|
||||||
|
RGBList *_interfacePal;
|
||||||
|
SceneResources _sceneResources;
|
||||||
|
HotSpotList _sceneHotspots;
|
||||||
|
SpriteAsset *_sceneSprites;
|
||||||
|
SpriteAsset *_walkerSprite;
|
||||||
|
char _statusText[100];
|
||||||
|
|
||||||
|
void nextCommonCursor();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
1406
engines/m4/script.cpp
Normal file
1406
engines/m4/script.cpp
Normal file
File diff suppressed because it is too large
Load diff
457
engines/m4/script.h
Normal file
457
engines/m4/script.h
Normal file
|
@ -0,0 +1,457 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_SCRIPT_H
|
||||||
|
#define M4_SCRIPT_H
|
||||||
|
|
||||||
|
#include "common/file.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
#include "common/hashmap.h"
|
||||||
|
#include "common/str.h"
|
||||||
|
#include "common/stack.h"
|
||||||
|
|
||||||
|
#include "m4/woodscript.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
const unsigned long kScriptFileMagic = 0x5845344D;
|
||||||
|
const unsigned long kScriptFileVersion = 1;
|
||||||
|
|
||||||
|
enum ScriptValueType {
|
||||||
|
kInteger,
|
||||||
|
kConstString,
|
||||||
|
kLogicVar,
|
||||||
|
kLogicVarRef,
|
||||||
|
kGameVar,
|
||||||
|
kKernelVar,
|
||||||
|
kDataRef,
|
||||||
|
kRegister,
|
||||||
|
kStackVar
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ScriptDataType {
|
||||||
|
kStreamBreakSeries,
|
||||||
|
kStreamPlaySeries,
|
||||||
|
kSaidArray,
|
||||||
|
kParserArray,
|
||||||
|
kSpeechArray,
|
||||||
|
kCreditsArray,
|
||||||
|
kInvObj,
|
||||||
|
kMineRoom,
|
||||||
|
kButtonItem
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptInterpreter;
|
||||||
|
|
||||||
|
class StringTable {
|
||||||
|
public:
|
||||||
|
StringTable();
|
||||||
|
~StringTable();
|
||||||
|
void load(Common::File *fd);
|
||||||
|
int size() { return _strings.size(); }
|
||||||
|
const char *operator[](uint32 index) const {
|
||||||
|
assert(index < _strings.size() );
|
||||||
|
return _strings[index];
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
Common::Array<const char*> _strings;
|
||||||
|
char *_stringsData;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScriptValue {
|
||||||
|
|
||||||
|
ScriptValueType type;
|
||||||
|
|
||||||
|
union {
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
ScriptValue() : type(kInteger), value(0) {};
|
||||||
|
ScriptValue(ScriptValueType itype, int ivalue) : type(itype), value(ivalue) {};
|
||||||
|
|
||||||
|
ScriptValue(const int intValue) : type(kInteger), value(intValue) {};
|
||||||
|
|
||||||
|
ScriptValue& operator=(const int intValue) {
|
||||||
|
type = kInteger;
|
||||||
|
value = intValue;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptDataItem {
|
||||||
|
public:
|
||||||
|
ScriptDataItem() : _inter(NULL) {}
|
||||||
|
ScriptDataItem(ScriptInterpreter *inter) : _inter(inter) {}
|
||||||
|
virtual ~ScriptDataItem() {}
|
||||||
|
virtual void load(Common::File *fd) = 0;
|
||||||
|
static int type() { return -1; }
|
||||||
|
protected:
|
||||||
|
ScriptInterpreter *_inter;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptDataCache {
|
||||||
|
public:
|
||||||
|
ScriptDataCache(ScriptInterpreter *inter) : _inter(inter) {
|
||||||
|
}
|
||||||
|
~ScriptDataCache() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
template<class T>
|
||||||
|
T *load(Common::File *fd, uint32 ofs) {
|
||||||
|
T *item;
|
||||||
|
if (_cache.contains(ofs)) {
|
||||||
|
item = (T*)(_cache[ofs]);
|
||||||
|
} else {
|
||||||
|
item = new T(_inter);
|
||||||
|
fd->seek(ofs + 4); // "+4" skips the data size
|
||||||
|
item->load(fd);
|
||||||
|
_cache[ofs] = item;
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
void clear() {
|
||||||
|
// TODO: Free all cached items
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
typedef Common::HashMap<uint32, ScriptDataItem*> CacheMap;
|
||||||
|
CacheMap _cache;
|
||||||
|
ScriptInterpreter *_inter;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SeriesStreamBreakItem {
|
||||||
|
int frameNum;
|
||||||
|
const char *digiName;
|
||||||
|
int digiChannel;
|
||||||
|
int digiVolume;
|
||||||
|
int trigger;
|
||||||
|
int flags;
|
||||||
|
ScriptValue variable;
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SeriesStreamBreakList : public ScriptDataItem {
|
||||||
|
public:
|
||||||
|
SeriesStreamBreakList(ScriptInterpreter *inter) : ScriptDataItem(inter) {}
|
||||||
|
~SeriesStreamBreakList();
|
||||||
|
void load(Common::File *fd);
|
||||||
|
int size() const { return _items.size(); }
|
||||||
|
SeriesStreamBreakItem *operator[](int index) const { return _items[index]; }
|
||||||
|
static int type() { return 0; }
|
||||||
|
protected:
|
||||||
|
Common::Array<SeriesStreamBreakItem*> _items;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SaidArrayItem {
|
||||||
|
const char *itemName;
|
||||||
|
const char *digiNameLook;
|
||||||
|
const char *digiNameTake;
|
||||||
|
const char *digiNameGear;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SaidArray : public ScriptDataItem {
|
||||||
|
public:
|
||||||
|
SaidArray(ScriptInterpreter *inter) : ScriptDataItem(inter) {}
|
||||||
|
~SaidArray();
|
||||||
|
void load(Common::File *fd);
|
||||||
|
int size() const { return _items.size(); }
|
||||||
|
SaidArrayItem *operator[](int index) const { return _items[index]; }
|
||||||
|
static int type() { return 2; }
|
||||||
|
protected:
|
||||||
|
Common::Array<SaidArrayItem*> _items;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ParserArrayItem {
|
||||||
|
const char *w0;
|
||||||
|
const char *w1;
|
||||||
|
int trigger;
|
||||||
|
ScriptValue testVariable;
|
||||||
|
int testValue;
|
||||||
|
ScriptValue variable;
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ParserArray : public ScriptDataItem {
|
||||||
|
public:
|
||||||
|
ParserArray(ScriptInterpreter *inter) : ScriptDataItem(inter) {}
|
||||||
|
~ParserArray();
|
||||||
|
void load(Common::File *fd);
|
||||||
|
int size() const { return _items.size(); }
|
||||||
|
ParserArrayItem *operator[](int index) const { return _items[index]; }
|
||||||
|
static int type() { return 3; }
|
||||||
|
protected:
|
||||||
|
Common::Array<ParserArrayItem*> _items;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptFunction {
|
||||||
|
public:
|
||||||
|
ScriptFunction(ScriptInterpreter *inter);
|
||||||
|
~ScriptFunction();
|
||||||
|
void load(Common::File *fd);
|
||||||
|
void jumpAbsolute(uint32 ofs);
|
||||||
|
void jumpRelative(int32 ofs);
|
||||||
|
byte readByte();
|
||||||
|
uint32 readUint32();
|
||||||
|
protected:
|
||||||
|
ScriptInterpreter *_inter;
|
||||||
|
Common::MemoryReadStream *_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScriptFunctionEntry {
|
||||||
|
uint32 offset;
|
||||||
|
ScriptFunction *func;
|
||||||
|
ScriptFunctionEntry(uint32 funcOffset) : offset(funcOffset), func(NULL) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScriptDataEntry {
|
||||||
|
uint32 offset;
|
||||||
|
ScriptDataType type;
|
||||||
|
ScriptDataEntry(uint32 dataOffset, ScriptDataType dataType) : offset(dataOffset), type(dataType) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ScriptKernelVariable {
|
||||||
|
kGameLanguage,
|
||||||
|
kGameVersion,
|
||||||
|
kGameCurrentRoom,
|
||||||
|
kGameNewRoom,
|
||||||
|
kGamePreviousRoom,
|
||||||
|
kGameNewSection,
|
||||||
|
kKernelTrigger,
|
||||||
|
kKernelTriggerMode,
|
||||||
|
kKernelFirstFade,
|
||||||
|
kKernelSuppressFadeUp,
|
||||||
|
kKernelContinueHandlingTrigger,
|
||||||
|
kKernelUseDebugMonitor,
|
||||||
|
kPlayerPosX,
|
||||||
|
kPlayerPosY,
|
||||||
|
kPlayerFacing,
|
||||||
|
kPlayerScale,
|
||||||
|
kPlayerDepth,
|
||||||
|
kPlayerWalkX,
|
||||||
|
kPlayerWalkY,
|
||||||
|
kPlayerReadyToWalk,
|
||||||
|
kPlayerNeedToWalk,
|
||||||
|
kPlayerCommandReady,
|
||||||
|
kPlayerWalkerInThisScene,
|
||||||
|
kPlayerVerb,
|
||||||
|
kWalkerInitialized,
|
||||||
|
kCallDaemonEveryLoop,
|
||||||
|
kConvCurrentTalker,
|
||||||
|
kConvCurrentNode,
|
||||||
|
kConvCurrentEntry,
|
||||||
|
kConvSoundToPlay,
|
||||||
|
kInterfaceVisible
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptInterpreter {
|
||||||
|
public:
|
||||||
|
ScriptInterpreter(M4Engine *vm);
|
||||||
|
~ScriptInterpreter();
|
||||||
|
/* Opens a M4 program file */
|
||||||
|
void open(const char *filename);
|
||||||
|
void close();
|
||||||
|
/* Loads a function via the index. Creates the function object if it's not already loaded. */
|
||||||
|
ScriptFunction *loadFunction(uint32 index);
|
||||||
|
/* Loads a function via the exported name. */
|
||||||
|
ScriptFunction *loadFunction(const Common::String &name);
|
||||||
|
/* Unload all loaded functions.
|
||||||
|
This should be called before entering a new room to free unused functions. */
|
||||||
|
void unloadFunctions();
|
||||||
|
//TODO void unloadData();
|
||||||
|
/* Executes a function. */
|
||||||
|
int runFunction(ScriptFunction *scriptFunction);
|
||||||
|
|
||||||
|
void push(const ScriptValue &value);
|
||||||
|
void pop(ScriptValue &value);
|
||||||
|
void dumpStack();
|
||||||
|
void dumpRegisters();
|
||||||
|
void dumpGlobalVars();
|
||||||
|
|
||||||
|
int toInteger(const ScriptValue &value);
|
||||||
|
|
||||||
|
const char *toString(const ScriptValue &value);
|
||||||
|
|
||||||
|
// Is this ok?
|
||||||
|
template<class T>
|
||||||
|
const T& toData(const ScriptValue &value) {
|
||||||
|
printf("ScriptInterpreter::toData() index = %d; type = %d; max = %d\n", value.value, _data[value.value]->type, _data.size());
|
||||||
|
assert((uint32)value.value < _data.size());
|
||||||
|
return *(_dataCache->load<T>(_scriptFile, _data[value.value]->offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *getGlobalString(int index) const {
|
||||||
|
return _constStrings[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *loadGlobalString(Common::File *fd);
|
||||||
|
|
||||||
|
void test();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
M4Engine *_vm;
|
||||||
|
|
||||||
|
typedef Common::HashMap<Common::String, uint32, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FunctionNameMap;
|
||||||
|
Common::File *_scriptFile;
|
||||||
|
/* An array of offset/ScriptFunction* pairs for each script function */
|
||||||
|
Common::Array<ScriptFunctionEntry*> _functions;
|
||||||
|
|
||||||
|
// DEBUG only
|
||||||
|
Common::Array<Common::String> _scriptFunctionNames;
|
||||||
|
|
||||||
|
Common::Array<ScriptDataEntry*> _data;
|
||||||
|
/* Maps function name -> index of function in _functions array */
|
||||||
|
FunctionNameMap _functionNames;
|
||||||
|
StringTable _constStrings;
|
||||||
|
/* The currently running function */
|
||||||
|
ScriptFunction *_runningFunction;
|
||||||
|
int _localStackPtr;
|
||||||
|
|
||||||
|
ScriptValue _registers[8];
|
||||||
|
|
||||||
|
ScriptValue _stack[512];
|
||||||
|
int _stackPtr;
|
||||||
|
|
||||||
|
int _globalVarCount;
|
||||||
|
ScriptValue _globalVars[1024];
|
||||||
|
|
||||||
|
int _logicGlobals[512];
|
||||||
|
|
||||||
|
int _cmpFlags;
|
||||||
|
|
||||||
|
ScriptDataCache *_dataCache;
|
||||||
|
|
||||||
|
int _lineNum;
|
||||||
|
|
||||||
|
typedef int (ScriptInterpreter::*KernelFunction)();
|
||||||
|
struct KernelFunctionEntry {
|
||||||
|
KernelFunction proc;
|
||||||
|
const char *desc;
|
||||||
|
};
|
||||||
|
const KernelFunctionEntry *_kernelFunctions;
|
||||||
|
uint16 _kernelFunctionsMax;
|
||||||
|
|
||||||
|
struct KernelVariableEntry {
|
||||||
|
ScriptKernelVariable var;
|
||||||
|
const char *desc;
|
||||||
|
};
|
||||||
|
const KernelVariableEntry *_kernelVars;
|
||||||
|
int16 _kernelVarsMax;
|
||||||
|
|
||||||
|
void initScriptKernel();
|
||||||
|
|
||||||
|
void loadValue(ScriptValue &value);
|
||||||
|
void writeValue(ScriptValue &value);
|
||||||
|
void copyValue(ScriptValue &destValue, ScriptValue &sourceValue);
|
||||||
|
void derefValue(ScriptValue &value);
|
||||||
|
|
||||||
|
void callKernelFunction(uint32 index);
|
||||||
|
ScriptValue getArg(uint32 index);
|
||||||
|
void dumpArgs(uint32 count);
|
||||||
|
|
||||||
|
void callFunction(uint32 index);
|
||||||
|
|
||||||
|
bool execOpcode(byte opcode);
|
||||||
|
|
||||||
|
// Kernel functions
|
||||||
|
int o1_handleStreamBreak();
|
||||||
|
int o1_handlePlayBreak();
|
||||||
|
int o1_dispatchTriggerOnSoundState();
|
||||||
|
int o1_getRangedRandomValue();
|
||||||
|
int o1_getTicks();
|
||||||
|
int o1_preloadSound();
|
||||||
|
int o1_unloadSound();
|
||||||
|
int o1_stopSound();
|
||||||
|
int o1_playSound();
|
||||||
|
int o1_playLoopingSound();
|
||||||
|
int o1_setSoundVolume();
|
||||||
|
int o1_getSoundStatus();
|
||||||
|
int o1_getSoundDuration();
|
||||||
|
int o1_loadSeries();
|
||||||
|
int o1_unloadSeries();
|
||||||
|
int o1_showSeries();
|
||||||
|
int o1_playSeries();
|
||||||
|
int o1_setSeriesFrameRate();
|
||||||
|
int o1_playBreakSeries();
|
||||||
|
int o1_preloadBreakSeries();
|
||||||
|
int o1_unloadBreakSeries();
|
||||||
|
int o1_startBreakSeries();
|
||||||
|
int o1_dispatchTrigger();
|
||||||
|
int o1_terminateMachine();
|
||||||
|
int o1_sendWoodScriptMessage();
|
||||||
|
int o1_runConversation();
|
||||||
|
int o1_loadConversation();
|
||||||
|
int o1_exportConversationValue();
|
||||||
|
int o1_exportConversationPointer();
|
||||||
|
int o1_fadeInit();
|
||||||
|
int o1_fadeSetStart();
|
||||||
|
int o1_fadeToBlack();
|
||||||
|
int o1_initPaletteCycle();
|
||||||
|
int o1_stopPaletteCycle();
|
||||||
|
int o1_setHotspot();
|
||||||
|
int o1_hideWalker();
|
||||||
|
int o1_showWalker();
|
||||||
|
int o1_setWalkerLocation();
|
||||||
|
int o1_setWalkerFacing();
|
||||||
|
int o1_walk();
|
||||||
|
int o1_overrideCrunchTime();
|
||||||
|
int o1_addBlockingRect();
|
||||||
|
int o1_triggerTimerProc();
|
||||||
|
int o1_setPlayerCommandsAllowed();
|
||||||
|
int o1_getPlayerCommandsAllowed();
|
||||||
|
int o1_updatePlayerInfo();
|
||||||
|
int o1_hasPlayerSaid();
|
||||||
|
int o1_hasPlayerSaidAny();
|
||||||
|
int o1_playerHotspotWalkOverride();
|
||||||
|
int o1_setPlayerFacingAngle();
|
||||||
|
int o1_disablePlayerFadeToBlack();
|
||||||
|
int o1_enablePlayer();
|
||||||
|
int o1_disablePlayer();
|
||||||
|
int o1_freshenSentence();
|
||||||
|
int o1_playerHasItem();
|
||||||
|
int o1_playerGiveItem();
|
||||||
|
int o1_moveObject();
|
||||||
|
int o1_setStopSoundsBetweenRooms();
|
||||||
|
int o1_backupPalette();
|
||||||
|
int o1_unloadWilburWalker();
|
||||||
|
int o1_globalTriggerProc();
|
||||||
|
int o1_wilburSpeech();
|
||||||
|
int o1_wilburSaid();
|
||||||
|
int o1_wilburParse();
|
||||||
|
int o1_wilburTalk();
|
||||||
|
int o1_wilburFinishedTalking();
|
||||||
|
//int ();
|
||||||
|
|
||||||
|
// Kernel vars
|
||||||
|
void getKernelVar(int index, ScriptValue &value);
|
||||||
|
void setKernelVar(int index, const ScriptValue &value);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
136
engines/m4/scripttab.h
Normal file
136
engines/m4/scripttab.h
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
/* This file has been autogenerated by the linker.
|
||||||
|
Do not edit it or merge it with script.cpp! */
|
||||||
|
#define FUNCTION(x) { &ScriptInterpreter::x, #x }
|
||||||
|
static KernelFunctionEntry kernelFunctions[] = {
|
||||||
|
/* 000 */
|
||||||
|
FUNCTION(o1_handleStreamBreak),
|
||||||
|
FUNCTION(o1_handlePlayBreak),
|
||||||
|
FUNCTION(o1_dispatchTriggerOnSoundState),
|
||||||
|
FUNCTION(o1_getRangedRandomValue),
|
||||||
|
/* 004 */
|
||||||
|
FUNCTION(o1_getTicks),
|
||||||
|
FUNCTION(o1_preloadSound),
|
||||||
|
FUNCTION(o1_unloadSound),
|
||||||
|
FUNCTION(o1_stopSound),
|
||||||
|
/* 008 */
|
||||||
|
FUNCTION(o1_playSound),
|
||||||
|
FUNCTION(o1_playLoopingSound),
|
||||||
|
FUNCTION(o1_setSoundVolume),
|
||||||
|
FUNCTION(o1_getSoundStatus),
|
||||||
|
/* 012 */
|
||||||
|
FUNCTION(o1_getSoundDuration),
|
||||||
|
FUNCTION(o1_loadSeries),
|
||||||
|
FUNCTION(o1_unloadSeries),
|
||||||
|
FUNCTION(o1_showSeries),
|
||||||
|
/* 016 */
|
||||||
|
FUNCTION(o1_playSeries),
|
||||||
|
FUNCTION(o1_setSeriesFrameRate),
|
||||||
|
FUNCTION(o1_playBreakSeries),
|
||||||
|
FUNCTION(o1_preloadBreakSeries),
|
||||||
|
/* 020 */
|
||||||
|
FUNCTION(o1_unloadBreakSeries),
|
||||||
|
FUNCTION(o1_startBreakSeries),
|
||||||
|
FUNCTION(o1_dispatchTrigger),
|
||||||
|
FUNCTION(o1_terminateMachine),
|
||||||
|
/* 024 */
|
||||||
|
FUNCTION(o1_sendWoodScriptMessage),
|
||||||
|
FUNCTION(o1_runConversation),
|
||||||
|
FUNCTION(o1_runConversation),
|
||||||
|
FUNCTION(o1_loadConversation),
|
||||||
|
/* 028 */
|
||||||
|
FUNCTION(o1_exportConversationValue),
|
||||||
|
FUNCTION(o1_exportConversationPointer),
|
||||||
|
FUNCTION(o1_runConversation),
|
||||||
|
FUNCTION(o1_fadeInit),
|
||||||
|
/* 032 */
|
||||||
|
FUNCTION(o1_fadeSetStart),
|
||||||
|
FUNCTION(o1_fadeToBlack),
|
||||||
|
FUNCTION(o1_initPaletteCycle),
|
||||||
|
FUNCTION(o1_stopPaletteCycle),
|
||||||
|
/* 036 */
|
||||||
|
FUNCTION(o1_setHotspot),
|
||||||
|
FUNCTION(o1_hideWalker),
|
||||||
|
FUNCTION(o1_showWalker),
|
||||||
|
FUNCTION(o1_setWalkerLocation),
|
||||||
|
/* 040 */
|
||||||
|
FUNCTION(o1_setWalkerFacing),
|
||||||
|
FUNCTION(o1_walk),
|
||||||
|
FUNCTION(o1_overrideCrunchTime),
|
||||||
|
FUNCTION(o1_addBlockingRect),
|
||||||
|
/* 044 */
|
||||||
|
FUNCTION(o1_triggerTimerProc),
|
||||||
|
FUNCTION(o1_setPlayerCommandsAllowed),
|
||||||
|
FUNCTION(o1_getPlayerCommandsAllowed),
|
||||||
|
FUNCTION(o1_updatePlayerInfo),
|
||||||
|
/* 048 */
|
||||||
|
FUNCTION(o1_hasPlayerSaid),
|
||||||
|
FUNCTION(o1_hasPlayerSaidAny),
|
||||||
|
FUNCTION(o1_playerHotspotWalkOverride),
|
||||||
|
FUNCTION(o1_setPlayerFacingAngle),
|
||||||
|
/* 052 */
|
||||||
|
FUNCTION(o1_disablePlayerFadeToBlack),
|
||||||
|
FUNCTION(o1_enablePlayer),
|
||||||
|
FUNCTION(o1_disablePlayer),
|
||||||
|
FUNCTION(o1_freshenSentence),
|
||||||
|
/* 056 */
|
||||||
|
FUNCTION(o1_playerHasItem),
|
||||||
|
FUNCTION(o1_playerGiveItem),
|
||||||
|
FUNCTION(o1_moveObject),
|
||||||
|
FUNCTION(o1_setStopSoundsBetweenRooms),
|
||||||
|
/* 060 */
|
||||||
|
FUNCTION(o1_backupPalette),
|
||||||
|
FUNCTION(o1_unloadWilburWalker),
|
||||||
|
FUNCTION(o1_globalTriggerProc),
|
||||||
|
FUNCTION(o1_wilburSpeech),
|
||||||
|
/* 064 */
|
||||||
|
FUNCTION(o1_wilburParse),
|
||||||
|
FUNCTION(o1_wilburSaid),
|
||||||
|
FUNCTION(o1_wilburTalk),
|
||||||
|
FUNCTION(o1_wilburFinishedTalking)
|
||||||
|
};
|
||||||
|
#undef FUNCTION
|
||||||
|
|
||||||
|
#define VARIABLE(x) { x, #x }
|
||||||
|
static KernelVariableEntry kernelVars[] = {
|
||||||
|
/* 000 */
|
||||||
|
VARIABLE(kGameLanguage),
|
||||||
|
VARIABLE(kGameVersion),
|
||||||
|
VARIABLE(kGameCurrentRoom),
|
||||||
|
VARIABLE(kGameNewRoom),
|
||||||
|
/* 004 */
|
||||||
|
VARIABLE(kGamePreviousRoom),
|
||||||
|
VARIABLE(kGameNewSection),
|
||||||
|
VARIABLE(kKernelTrigger),
|
||||||
|
VARIABLE(kKernelTriggerMode),
|
||||||
|
/* 008 */
|
||||||
|
VARIABLE(kKernelFirstFade),
|
||||||
|
VARIABLE(kKernelSuppressFadeUp),
|
||||||
|
VARIABLE(kKernelContinueHandlingTrigger),
|
||||||
|
VARIABLE(kKernelUseDebugMonitor),
|
||||||
|
/* 012 */
|
||||||
|
VARIABLE(kPlayerPosX),
|
||||||
|
VARIABLE(kPlayerPosY),
|
||||||
|
VARIABLE(kPlayerFacing),
|
||||||
|
VARIABLE(kPlayerScale),
|
||||||
|
/* 016 */
|
||||||
|
VARIABLE(kPlayerDepth),
|
||||||
|
VARIABLE(kPlayerWalkX),
|
||||||
|
VARIABLE(kPlayerWalkY),
|
||||||
|
VARIABLE(kPlayerReadyToWalk),
|
||||||
|
/* 020 */
|
||||||
|
VARIABLE(kPlayerNeedToWalk),
|
||||||
|
VARIABLE(kPlayerCommandReady),
|
||||||
|
VARIABLE(kPlayerWalkerInThisScene),
|
||||||
|
VARIABLE(kPlayerVerb),
|
||||||
|
/* 024 */
|
||||||
|
VARIABLE(kWalkerInitialized),
|
||||||
|
VARIABLE(kCallDaemonEveryLoop),
|
||||||
|
VARIABLE(kConvCurrentTalker),
|
||||||
|
VARIABLE(kConvCurrentNode),
|
||||||
|
/* 028 */
|
||||||
|
VARIABLE(kConvCurrentEntry),
|
||||||
|
VARIABLE(kConvSoundToPlay),
|
||||||
|
VARIABLE(kInterfaceVisible)
|
||||||
|
};
|
||||||
|
#undef VARIABLE
|
||||||
|
|
283
engines/m4/sound.cpp
Normal file
283
engines/m4/sound.cpp
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/sound.h"
|
||||||
|
#include "m4/compression.h"
|
||||||
|
|
||||||
|
#include "sound/audiostream.h"
|
||||||
|
#include "sound/mixer.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
Sound::Sound(M4Engine *vm, Audio::Mixer *mixer, int volume) :
|
||||||
|
_vm(vm), _mixer(mixer) {
|
||||||
|
|
||||||
|
for (int i = 0; i < SOUND_HANDLES; i++)
|
||||||
|
_handles[i].type = kFreeHandle;
|
||||||
|
|
||||||
|
_dsrFileLoaded = false;
|
||||||
|
|
||||||
|
setVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sound::~Sound() {
|
||||||
|
unloadDSRFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
SndHandle *Sound::getHandle() {
|
||||||
|
for (int i = 0; i < SOUND_HANDLES; i++) {
|
||||||
|
if (_handles[i].type == kFreeHandle)
|
||||||
|
return &_handles[i];
|
||||||
|
|
||||||
|
if (!_mixer->isSoundHandleActive(_handles[i].handle)) {
|
||||||
|
_handles[i].type = kFreeHandle;
|
||||||
|
return &_handles[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error("Sound::getHandle(): Too many sound handles");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sound::isHandleActive(SndHandle *handle) {
|
||||||
|
return (_mixer->isSoundHandleActive(handle->handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::playSound(const char *soundName, int volume, bool loop, int channel) {
|
||||||
|
byte flags;
|
||||||
|
Common::SeekableReadStream *soundStream = _vm->res()->get(soundName);
|
||||||
|
SndHandle *handle;
|
||||||
|
if (channel < 0) {
|
||||||
|
handle = getHandle();
|
||||||
|
} else {
|
||||||
|
if (_handles[channel].type == kFreeHandle) {
|
||||||
|
handle = &_handles[channel];
|
||||||
|
} else {
|
||||||
|
warning("Attempted to play a sound on a channel that isn't free");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bufferSize = soundStream->size();
|
||||||
|
byte *buffer = new byte[bufferSize];
|
||||||
|
soundStream->read(buffer, bufferSize);
|
||||||
|
_vm->res()->toss(soundName);
|
||||||
|
|
||||||
|
handle->type = kEffectHandle;
|
||||||
|
flags = Audio::Mixer::FLAG_AUTOFREE;
|
||||||
|
flags |= Audio::Mixer::FLAG_UNSIGNED;
|
||||||
|
|
||||||
|
if (loop)
|
||||||
|
flags |= Audio::Mixer::FLAG_LOOP;
|
||||||
|
|
||||||
|
_vm->res()->toss(soundName);
|
||||||
|
|
||||||
|
// Sound format is 8bit mono, unsigned, 11025kHz
|
||||||
|
_mixer->playRaw(Audio::Mixer::kSFXSoundType, &handle->handle, buffer, bufferSize, 11025, flags, -1, volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::pauseSound() {
|
||||||
|
for (int i = 0; i < SOUND_HANDLES; i++) {
|
||||||
|
if (_handles[i].type == kEffectHandle)
|
||||||
|
_mixer->pauseHandle(_handles[i].handle, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::resumeSound() {
|
||||||
|
for (int i = 0; i < SOUND_HANDLES; i++) {
|
||||||
|
if (_handles[i].type == kEffectHandle)
|
||||||
|
_mixer->pauseHandle(_handles[i].handle, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::stopSound(int channel) {
|
||||||
|
if (channel >= 0) {
|
||||||
|
if (_handles[channel].type == kEffectHandle) {
|
||||||
|
_mixer->stopHandle(_handles[channel].handle);
|
||||||
|
_handles[channel].type = kFreeHandle;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
warning("Attempted to stop a sound on a channel that is already free");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < SOUND_HANDLES; i++) {
|
||||||
|
if (_handles[i].type == kEffectHandle) {
|
||||||
|
_mixer->stopHandle(_handles[i].handle);
|
||||||
|
_handles[i].type = kFreeHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::playVoice(const char *soundName, int volume) {
|
||||||
|
byte flags;
|
||||||
|
Common::SeekableReadStream *soundStream = _vm->res()->get(soundName);
|
||||||
|
SndHandle *handle = getHandle();
|
||||||
|
byte *buffer;
|
||||||
|
|
||||||
|
buffer = new byte[soundStream->size()];
|
||||||
|
soundStream->read(buffer, soundStream->size());
|
||||||
|
|
||||||
|
handle->type = kEffectHandle;
|
||||||
|
flags = Audio::Mixer::FLAG_AUTOFREE;
|
||||||
|
flags |= Audio::Mixer::FLAG_UNSIGNED;
|
||||||
|
|
||||||
|
_vm->res()->toss(soundName);
|
||||||
|
|
||||||
|
// Voice format is 8bit mono, unsigned, 11025kHz
|
||||||
|
_mixer->playRaw(Audio::Mixer::kSFXSoundType, &handle->handle, buffer, soundStream->size(), 11025, flags, -1, volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::pauseVoice() {
|
||||||
|
for (int i = 0; i < SOUND_HANDLES; i++)
|
||||||
|
if (_handles[i].type == kVoiceHandle)
|
||||||
|
_mixer->pauseHandle(_handles[i].handle, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::resumeVoice() {
|
||||||
|
for (int i = 0; i < SOUND_HANDLES; i++)
|
||||||
|
if (_handles[i].type == kVoiceHandle)
|
||||||
|
_mixer->pauseHandle(_handles[i].handle, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::stopVoice() {
|
||||||
|
for (int i = 0; i < SOUND_HANDLES; i++)
|
||||||
|
if (_handles[i].type == kVoiceHandle) {
|
||||||
|
_mixer->stopHandle(_handles[i].handle);
|
||||||
|
_handles[i].type = kFreeHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::stopAll() {
|
||||||
|
stopVoice();
|
||||||
|
stopSound();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::setVolume(int volume) {
|
||||||
|
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volume);
|
||||||
|
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::loadDSRFile(const char *fileName) {
|
||||||
|
if (_dsrFileLoaded)
|
||||||
|
unloadDSRFile();
|
||||||
|
|
||||||
|
Common::SeekableReadStream *fileStream = _vm->res()->get(fileName);
|
||||||
|
|
||||||
|
sprintf(_dsrFile.fileName, "%s", fileName);
|
||||||
|
|
||||||
|
// Read header
|
||||||
|
_dsrFile.entryCount = fileStream->readUint16LE();
|
||||||
|
//printf("DSR has %i entries\n", _dsrFile.entryCount);
|
||||||
|
|
||||||
|
for (int i = 0; i < _dsrFile.entryCount; i++) {
|
||||||
|
DSREntry* newEntry = new DSREntry();
|
||||||
|
newEntry->frequency = fileStream->readUint16LE();
|
||||||
|
newEntry->channels = fileStream->readUint32LE();
|
||||||
|
newEntry->compSize = fileStream->readUint32LE();
|
||||||
|
newEntry->uncompSize = fileStream->readUint32LE();
|
||||||
|
newEntry->offset = fileStream->readUint32LE();
|
||||||
|
_dsrFile.dsrEntries.push_back(newEntry);
|
||||||
|
|
||||||
|
/*
|
||||||
|
printf("%i: ", i);
|
||||||
|
printf("frequency: %i ", newEntry->frequency);
|
||||||
|
printf("channels: %i ", newEntry->channels);
|
||||||
|
printf("comp: %i ", newEntry->compSize);
|
||||||
|
printf("uncomp: %i ", newEntry->uncompSize);
|
||||||
|
printf("offset: %i ", newEntry->offset);
|
||||||
|
printf("\n");
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->res()->toss(fileName);
|
||||||
|
|
||||||
|
_dsrFileLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::unloadDSRFile() {
|
||||||
|
if (!_dsrFileLoaded)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < _dsrFile.entryCount; i++) {
|
||||||
|
_dsrFile.dsrEntries.remove_at(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
_dsrFile.entryCount = 0;
|
||||||
|
strcpy(_dsrFile.fileName, "");
|
||||||
|
_dsrFileLoaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::playDSRSound(int soundIndex, int volume, bool loop) {
|
||||||
|
if (!_dsrFileLoaded) {
|
||||||
|
warning("DSR file not loaded, not playing sound");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (soundIndex < 0 || soundIndex > _dsrFile.entryCount - 1) {
|
||||||
|
warning("Invalid sound index: %i, not playing sound", soundIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte flags;
|
||||||
|
SndHandle *handle = getHandle();
|
||||||
|
|
||||||
|
handle->type = kEffectHandle;
|
||||||
|
flags = Audio::Mixer::FLAG_AUTOFREE;
|
||||||
|
flags |= Audio::Mixer::FLAG_UNSIGNED;
|
||||||
|
|
||||||
|
if (loop)
|
||||||
|
flags |= Audio::Mixer::FLAG_LOOP;
|
||||||
|
|
||||||
|
// Get sound data
|
||||||
|
FabDecompressor fab;
|
||||||
|
byte *compData = new byte[_dsrFile.dsrEntries[soundIndex]->compSize];
|
||||||
|
byte *buffer = new byte[_dsrFile.dsrEntries[soundIndex]->uncompSize];
|
||||||
|
Common::SeekableReadStream *fileStream = _vm->res()->get(_dsrFile.fileName);
|
||||||
|
fileStream->seek(_dsrFile.dsrEntries[soundIndex]->offset, SEEK_SET);
|
||||||
|
fileStream->read(compData, _dsrFile.dsrEntries[soundIndex]->compSize);
|
||||||
|
_vm->res()->toss(_dsrFile.fileName);
|
||||||
|
|
||||||
|
fab.decompress(compData, _dsrFile.dsrEntries[soundIndex]->compSize,
|
||||||
|
buffer, _dsrFile.dsrEntries[soundIndex]->uncompSize);
|
||||||
|
|
||||||
|
// Play sound
|
||||||
|
_mixer->playRaw(Audio::Mixer::kSFXSoundType, &handle->handle, buffer,
|
||||||
|
_dsrFile.dsrEntries[soundIndex]->uncompSize,
|
||||||
|
_dsrFile.dsrEntries[soundIndex]->frequency, flags, -1, volume);
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Dump the sound file
|
||||||
|
FILE *destFile = fopen("sound.raw", "wb");
|
||||||
|
fwrite(_dsrFile.dsrEntries[soundIndex]->data, _dsrFile.dsrEntries[soundIndex]->uncompSize, 1, destFile);
|
||||||
|
fclose(destFile);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
112
engines/m4/sound.h
Normal file
112
engines/m4/sound.h
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Sound class
|
||||||
|
|
||||||
|
#ifndef M4_SOUND_H
|
||||||
|
#define M4_SOUND_H
|
||||||
|
|
||||||
|
#include "common/file.h"
|
||||||
|
#include "common/array.h"
|
||||||
|
#include "sound/mixer.h"
|
||||||
|
#include "sound/mp3.h"
|
||||||
|
#include "sound/vorbis.h"
|
||||||
|
#include "sound/flac.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
#define SOUND_HANDLES 10
|
||||||
|
|
||||||
|
enum SOUND_FLAGS {
|
||||||
|
SOUND_LOOP = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
enum sndHandleType {
|
||||||
|
kFreeHandle,
|
||||||
|
kEffectHandle,
|
||||||
|
kVoiceHandle
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SndHandle {
|
||||||
|
Audio::SoundHandle handle;
|
||||||
|
sndHandleType type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DSREntry {
|
||||||
|
int16 frequency;
|
||||||
|
int channels;
|
||||||
|
int32 compSize;
|
||||||
|
int32 uncompSize;
|
||||||
|
int32 offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DSRFile {
|
||||||
|
char fileName[20];
|
||||||
|
int entryCount;
|
||||||
|
Common::Array<DSREntry *> dsrEntries;
|
||||||
|
};
|
||||||
|
|
||||||
|
class M4Engine;
|
||||||
|
|
||||||
|
class Sound {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Sound(M4Engine *vm, Audio::Mixer *mixer, int volume);
|
||||||
|
~Sound();
|
||||||
|
|
||||||
|
void playSound(const char *soundName, int volume, bool loop, int channel = -1);
|
||||||
|
void pauseSound();
|
||||||
|
void resumeSound();
|
||||||
|
void stopSound(int channel = -1);
|
||||||
|
|
||||||
|
void playVoice(const char *soundName, int volume);
|
||||||
|
void pauseVoice();
|
||||||
|
void resumeVoice();
|
||||||
|
void stopVoice();
|
||||||
|
|
||||||
|
void stopAll();
|
||||||
|
|
||||||
|
void setVolume(int volume);
|
||||||
|
|
||||||
|
bool isHandleActive(SndHandle *handle);
|
||||||
|
SndHandle *getHandle();
|
||||||
|
|
||||||
|
void loadDSRFile(const char *fileName);
|
||||||
|
void unloadDSRFile();
|
||||||
|
void playDSRSound(int soundIndex, int volume, bool loop);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
M4Engine *_vm;
|
||||||
|
Audio::Mixer *_mixer;
|
||||||
|
SndHandle _handles[SOUND_HANDLES];
|
||||||
|
|
||||||
|
DSRFile _dsrFile;
|
||||||
|
bool _dsrFileLoaded;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
174
engines/m4/sprite.cpp
Normal file
174
engines/m4/sprite.cpp
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "common/rect.h"
|
||||||
|
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/resource.h"
|
||||||
|
#include "m4/sprite.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kEndOfLine = 0,
|
||||||
|
kEndOfSprite = 1,
|
||||||
|
kMarker = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
M4Sprite::M4Sprite(Common::SeekableReadStream* source, int xOfs, int yOfs, int widthVal, int heightVal, bool decodeRle, uint8 encodingVal)
|
||||||
|
: M4Surface(widthVal, heightVal), encoding(encodingVal) {
|
||||||
|
|
||||||
|
if (_vm->isM4()) {
|
||||||
|
if (decodeRle) {
|
||||||
|
loadRle(source);
|
||||||
|
} else {
|
||||||
|
// Raw sprite data, load directly
|
||||||
|
byte *dst = getData();
|
||||||
|
source->read(dst, widthVal * heightVal);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loadMadsSprite(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
xOffset = xOfs;
|
||||||
|
yOffset = yOfs;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void M4Sprite::loadRle(Common::SeekableReadStream* rleData) {
|
||||||
|
byte *dst = getData();
|
||||||
|
while (1) {
|
||||||
|
byte len = rleData->readByte();
|
||||||
|
if (len == 0) {
|
||||||
|
len = rleData->readByte();
|
||||||
|
if (len <= kMarker) {
|
||||||
|
if (len == kEndOfSprite)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
while (len--) {
|
||||||
|
*dst++ = rleData->readByte();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
byte value = rleData->readByte();
|
||||||
|
while (len--)
|
||||||
|
*dst++ = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void M4Sprite::loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int destY) {
|
||||||
|
int lineNum = 0;
|
||||||
|
byte *dst = getBasePtr(destX, destY);
|
||||||
|
while (1) {
|
||||||
|
byte len = rleData->readByte();
|
||||||
|
if (len == 0) {
|
||||||
|
len = rleData->readByte();
|
||||||
|
if (len <= kMarker) {
|
||||||
|
if (len == kEndOfLine) {
|
||||||
|
dst = getBasePtr(destX, destY + lineNum);
|
||||||
|
lineNum++;
|
||||||
|
} else if (len == kEndOfSprite)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
while (len--) {
|
||||||
|
byte pixel = rleData->readByte();
|
||||||
|
if (pixel == 0)
|
||||||
|
dst++;
|
||||||
|
else
|
||||||
|
*dst++ = pixel;
|
||||||
|
/* NOTE: The change below behaved differently than the old code,
|
||||||
|
so I put the old code back in again above.
|
||||||
|
If the pixel value is 0, nothing should be written to the
|
||||||
|
output buffer, since 0 means transparent. */
|
||||||
|
//*dst++ = (pixel == 0xFD) ? 0 : pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
byte value = rleData->readByte();
|
||||||
|
if (value == 0)
|
||||||
|
dst += len;
|
||||||
|
else
|
||||||
|
while (len--)
|
||||||
|
*dst++ = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The sprite outlines (pixel value 0xFD) are not shown
|
||||||
|
void M4Sprite::loadMadsSprite(Common::SeekableReadStream* source) {
|
||||||
|
byte *outp, *lineStart;
|
||||||
|
bool newLine = false;
|
||||||
|
|
||||||
|
outp = getData();
|
||||||
|
lineStart = getData();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
byte cmd1, cmd2, count, pixel;
|
||||||
|
|
||||||
|
if (newLine) {
|
||||||
|
outp = lineStart + w;
|
||||||
|
lineStart = outp;
|
||||||
|
newLine = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd1 = source->readByte();
|
||||||
|
|
||||||
|
if (cmd1 == 0xFC)
|
||||||
|
break;
|
||||||
|
else if (cmd1 == 0xFF)
|
||||||
|
newLine = true;
|
||||||
|
else if (cmd1 == 0xFD) {
|
||||||
|
while (!newLine) {
|
||||||
|
count = source->readByte();
|
||||||
|
if (count == 0xFF) {
|
||||||
|
newLine = true;
|
||||||
|
} else {
|
||||||
|
pixel = source->readByte();
|
||||||
|
while (count--)
|
||||||
|
*outp++ = (pixel == 0xFD) ? 0 : pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (!newLine) {
|
||||||
|
cmd2 = source->readByte();
|
||||||
|
if (cmd2 == 0xFF) {
|
||||||
|
newLine = true;
|
||||||
|
} else if (cmd2 == 0xFE) {
|
||||||
|
count = source->readByte();
|
||||||
|
pixel = source->readByte();
|
||||||
|
while (count--)
|
||||||
|
*outp++ = (pixel == 0xFD) ? 0 : pixel;
|
||||||
|
} else {
|
||||||
|
*outp++ = (cmd2 == 0xFD) ? 0 : cmd2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
122
engines/m4/sprite.h
Normal file
122
engines/m4/sprite.h
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_SPRITE_H
|
||||||
|
#define M4_SPRITE_H
|
||||||
|
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "common/endian.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO:
|
||||||
|
- change DrawRequestX and RendCell
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int32 x; // x position relative to GrBuff(0, 0)
|
||||||
|
int32 y; // y position relative to GrBuff(0, 0)
|
||||||
|
int32 scale_x; // x scale factor (can be negative for reverse draw)
|
||||||
|
int32 scale_y; // y scale factor (can't be negative)
|
||||||
|
uint8* depth_map; // depth code array for destination (doesn't care if srcDepth is 0)
|
||||||
|
BGR8 *Pal; // palette for shadow draw (doesn't care if SHADOW bit is not set in Src.encoding)
|
||||||
|
uint8* ICT; // Inverse Color Table (doesn't care if SHADOW bit is not set in Src.encoding)
|
||||||
|
uint8 depth; // depth code for source (0 if no depth processing)
|
||||||
|
} DrawRequestX;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint32 Pack;
|
||||||
|
uint32 Stream;
|
||||||
|
long hot_x;
|
||||||
|
long hot_y;
|
||||||
|
uint32 Width;
|
||||||
|
uint32 Height;
|
||||||
|
uint32 Comp;
|
||||||
|
uint32 Reserved[8];
|
||||||
|
uint8* data;
|
||||||
|
} RendCell;
|
||||||
|
|
||||||
|
#define SS_HEADER_NUM_FIELDS 14
|
||||||
|
struct SpriteSeriesHeader {
|
||||||
|
uint32 header;
|
||||||
|
uint32 size;
|
||||||
|
uint32 packing;
|
||||||
|
uint32 frameRate;
|
||||||
|
uint32 pixSpeed;
|
||||||
|
uint32 maxWidth;
|
||||||
|
uint32 maxHeight;
|
||||||
|
uint32 reserved3;
|
||||||
|
uint32 reserved4;
|
||||||
|
uint32 reserved5;
|
||||||
|
uint32 reserved6;
|
||||||
|
uint32 reserved7;
|
||||||
|
uint32 reserved8;
|
||||||
|
uint32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SF_HEADER_NUM_FIELDS 15
|
||||||
|
struct SpriteFrameHeader {
|
||||||
|
uint32 pack;
|
||||||
|
uint32 stream;
|
||||||
|
uint32 x;
|
||||||
|
uint32 y;
|
||||||
|
uint32 width;
|
||||||
|
uint32 height;
|
||||||
|
uint32 comp;
|
||||||
|
uint32 reserved1;
|
||||||
|
uint32 reserved2;
|
||||||
|
uint32 reserved3;
|
||||||
|
uint32 reserved4;
|
||||||
|
uint32 reserved5;
|
||||||
|
uint32 reserved6;
|
||||||
|
uint32 reserved7;
|
||||||
|
uint32 reserved8;
|
||||||
|
};
|
||||||
|
|
||||||
|
class M4Sprite: public M4Surface {
|
||||||
|
public:
|
||||||
|
int x, y;
|
||||||
|
int xOffset, yOffset;
|
||||||
|
uint8 encoding;
|
||||||
|
|
||||||
|
M4Sprite(M4Engine *vm): M4Surface() {}
|
||||||
|
M4Sprite(M4Engine *vm, int widthVal, int heightVal): M4Surface(widthVal, heightVal), xOffset(0), yOffset(0) {}
|
||||||
|
// Loads a sprite from the given stream, and optionally decompresses the RLE-encoded data
|
||||||
|
M4Sprite(Common::SeekableReadStream* source, int xOfs, int yOfs, int widthVal, int heightVal, bool decodeRle = true, uint8 encodingVal = 0);
|
||||||
|
// Loads an RLE compressed sprite; the surface must have been created before
|
||||||
|
void loadRle(Common::SeekableReadStream* rleData);
|
||||||
|
void loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int destY);
|
||||||
|
void loadMadsSprite(Common::SeekableReadStream* source);
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
#endif
|
436
engines/m4/viewmgr.cpp
Normal file
436
engines/m4/viewmgr.cpp
Normal file
|
@ -0,0 +1,436 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: Views have a _coords rect, so I'm not sure if x/y is needed in the onRefresh
|
||||||
|
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
#include "m4/mads_anim.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
void returnToMainMenuFn(M4Engine *vm) {
|
||||||
|
vm->_palette->resetColorCounts();
|
||||||
|
vm->_palette->setMadsSystemPalette();
|
||||||
|
|
||||||
|
vm->loadMenu(MAIN_MENU);
|
||||||
|
}
|
||||||
|
|
||||||
|
RectList::RectList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
RectList::~RectList() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void RectList::addRect(int x1, int y1, int x2, int y2) {
|
||||||
|
addRect(Common::Rect(x1, y1, x2, y2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RectList::addRect(const Common::Rect &rect) {
|
||||||
|
/* TODO:
|
||||||
|
Implement the following:
|
||||||
|
- Don't add the Rect if it's contained in any Rect in the list
|
||||||
|
- Split up the Rect if it intersects any Rect in the list
|
||||||
|
and add the resulting partial Rects instead
|
||||||
|
*/
|
||||||
|
push_back(rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
HotkeyList::HotkeyList(View *owner) {
|
||||||
|
_view = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
HotkeyList::~HotkeyList() {
|
||||||
|
for (uint32 i = 0; i < _hotkeys.size(); i++)
|
||||||
|
delete _hotkeys[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotkeyList::add(uint32 key, Hotkey::Callback callback) {
|
||||||
|
_hotkeys.push_back(new Hotkey(key, callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void HotkeyList::remove(uint32 key) {
|
||||||
|
for (uint32 i = 0; i < _hotkeys.size(); i++) {
|
||||||
|
if (_hotkeys[i]->key == key) {
|
||||||
|
delete _hotkeys[i];
|
||||||
|
_hotkeys.remove_at(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HotkeyList::call(uint32 key) {
|
||||||
|
for (uint32 i = 0; i < _hotkeys.size(); i++) {
|
||||||
|
if (_hotkeys[i]->key == key) {
|
||||||
|
if (_hotkeys[i]->callback)
|
||||||
|
(_hotkeys[i]->callback)(_vm, _view, key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// View constructor
|
||||||
|
|
||||||
|
View::View(M4Engine *vm, const Common::Rect &viewBounds, bool transparent):
|
||||||
|
_hotkeys(HotkeyList(this)), M4Surface(viewBounds.width(), viewBounds.height()), _vm(vm) {
|
||||||
|
SCREEN_FLAGS_DEFAULT;
|
||||||
|
_coords = viewBounds;
|
||||||
|
_transparent = transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
View::View(M4Engine *vm, int x, int y, bool transparent): _hotkeys(HotkeyList(this)), M4Surface(), _vm(vm) {
|
||||||
|
SCREEN_FLAGS_DEFAULT;
|
||||||
|
_coords.left = x;
|
||||||
|
_coords.top = y;
|
||||||
|
_coords.right = _vm->_screen->width();
|
||||||
|
_coords.bottom = _vm->_screen->height();
|
||||||
|
_transparent = transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::getCoordinates(Common::Rect &rect) {
|
||||||
|
rect = _coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::extract(int *status) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::show() {
|
||||||
|
_screenFlags.visible = true;
|
||||||
|
_vm->_viewManager->moveToFront(this);
|
||||||
|
_vm->_viewManager->restore(_coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::hide() {
|
||||||
|
_screenFlags.visible = false;
|
||||||
|
_vm->_viewManager->restore(_coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::moveToBack() {
|
||||||
|
_vm->_viewManager->moveToBack(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::moveAbsolute(int x, int y) {
|
||||||
|
// TODO: Handle clipping and offscreen
|
||||||
|
Common::Rect oldCoords = _coords;
|
||||||
|
_coords.moveTo(x, y);
|
||||||
|
_vm->_viewManager->restore(oldCoords);
|
||||||
|
_vm->_viewManager->restore(_coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::moveRelative(int x, int y) {
|
||||||
|
// TODO: Handle clipping and offscreen
|
||||||
|
Common::Rect oldCoords = _coords;
|
||||||
|
_coords.translate(x, y);
|
||||||
|
_vm->_viewManager->restore(oldCoords);
|
||||||
|
_vm->_viewManager->restore(_coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::resize(int newWidth, int newHeight) {
|
||||||
|
Common::Rect oldCoords = _coords;
|
||||||
|
if (newWidth >= 0)
|
||||||
|
_coords.setWidth(newWidth);
|
||||||
|
if (newHeight >= 0)
|
||||||
|
_coords.setHeight(newHeight);
|
||||||
|
_vm->_viewManager->restore(oldCoords);
|
||||||
|
_vm->_viewManager->restore(_coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::restore(int x1, int y1, int x2, int y2) {
|
||||||
|
_vm->_viewManager->restore(_coords.left + x1, _coords.top + y1, _coords.left + x2, _coords.top + y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::onRefresh(RectList *rects, M4Surface *destSurface) {
|
||||||
|
assert(destSurface);
|
||||||
|
|
||||||
|
if (rects == NULL)
|
||||||
|
// No rect list specified, so copy entire surface
|
||||||
|
copyTo(destSurface, _coords.left, _coords.top, _transparent ? 0 : -1);
|
||||||
|
else {
|
||||||
|
// Loop through the set of specified rectangles
|
||||||
|
RectList::iterator i;
|
||||||
|
for (i = rects->begin(); i != rects->end(); ++i) {
|
||||||
|
Common::Rect &destRect = *i;
|
||||||
|
Common::Rect srcBounds(destRect.left - _coords.left, destRect.top - _coords.top,
|
||||||
|
destRect.right - _coords.left, destRect.bottom - _coords.top);
|
||||||
|
copyTo(destSurface, srcBounds, destRect.left, destRect.top, _transparent ? 0 : -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ViewManager::ViewManager(M4Engine *vm): _systemHotkeys(HotkeyList(NULL)), _vm(vm) {
|
||||||
|
_captureScreen = NULL;
|
||||||
|
_captureEvents = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewManager::~ViewManager() {
|
||||||
|
// Delete any remaining active views
|
||||||
|
ListIterator i;
|
||||||
|
for (i = _views.begin(); i != _views.end(); ++i)
|
||||||
|
delete (*i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::addView(View *view) {
|
||||||
|
_views.push_back(view);
|
||||||
|
moveToFront(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning: After calling this method, the passed view object will no longer be valid
|
||||||
|
|
||||||
|
void ViewManager::deleteView(View *view) {
|
||||||
|
_views.remove(view);
|
||||||
|
delete view;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::handleEvents(const Common::Event &event) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::handleKeyboardEvents(uint32 keycode) {
|
||||||
|
Common::Point mousePos = _vm->_mouse->currentPos();
|
||||||
|
View *view;
|
||||||
|
bool blockedFlag;
|
||||||
|
bool foundFlag;
|
||||||
|
bool handledFlag;
|
||||||
|
|
||||||
|
// Scan view list for one which accepts or blocks keyboard events. If one is found,
|
||||||
|
// then the event is passed to it
|
||||||
|
|
||||||
|
view = NULL;
|
||||||
|
handledFlag = false;
|
||||||
|
foundFlag = false;
|
||||||
|
blockedFlag = false;
|
||||||
|
|
||||||
|
// Loop from the front to back view
|
||||||
|
ListIterator i;
|
||||||
|
for (i = _views.reverse_begin(); (i != _views.end()) && !foundFlag; --i) {
|
||||||
|
view = *i;
|
||||||
|
if (!view->isVisible()) continue;
|
||||||
|
|
||||||
|
if (view->screenFlags().blocks & SCREVENT_KEY)
|
||||||
|
blockedFlag = true;
|
||||||
|
if (view->screenFlags().get & SCREVENT_KEY) {
|
||||||
|
foundFlag = true;
|
||||||
|
handledFlag = (view->onEvent)(KEVENT_KEY, keycode, mousePos.x, mousePos.y, _captureEvents);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan view list for one with a hotkey list, aborting if a view is found that either
|
||||||
|
// blocks keyboard events, or has a hotkey list that includes the keycode
|
||||||
|
|
||||||
|
blockedFlag = false;
|
||||||
|
for (i = _views.reverse_begin(); (i != _views.end()) && !foundFlag && !blockedFlag; --i) {
|
||||||
|
view = *i;
|
||||||
|
if (!view->isVisible()) continue;
|
||||||
|
|
||||||
|
if (view->screenFlags().blocks & SCREVENT_KEY)
|
||||||
|
blockedFlag = true;
|
||||||
|
if (view->screenFlags().get & SCREVENT_KEY) {
|
||||||
|
if (view->hotkeys().call(keycode)) {
|
||||||
|
handledFlag = true;
|
||||||
|
_captureEvents = false;
|
||||||
|
//_vm->_dialogs->keyMouseCollision(); // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final check: if no view handled or blocked the key, check against the system hotkey list
|
||||||
|
|
||||||
|
if (!handledFlag && !blockedFlag) {
|
||||||
|
handledFlag = _systemHotkeys.call(keycode);
|
||||||
|
if (handledFlag) {
|
||||||
|
_captureEvents = false;
|
||||||
|
//_vm->_dialogs->keyMouseCollision(); // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::handleMouseEvents(M4EventType event) {
|
||||||
|
Common::Point mousePos = _vm->_mouse->currentPos();
|
||||||
|
ListIterator i;
|
||||||
|
View *view;
|
||||||
|
bool blockedFlag;
|
||||||
|
bool foundFlag;
|
||||||
|
|
||||||
|
// If a window sets the _captureEvents flag to true, it will receive all events until
|
||||||
|
// it sets it to false, even if it's not the top window
|
||||||
|
if (_captureEvents) {
|
||||||
|
if (_captureScreen->screenFlags().get & SCREVENT_MOUSE)
|
||||||
|
(_captureScreen->onEvent)(event, 0, mousePos.x, mousePos.y, _captureEvents);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
blockedFlag = false;
|
||||||
|
foundFlag = false;
|
||||||
|
view = NULL;
|
||||||
|
|
||||||
|
// Loop from the front to back view
|
||||||
|
for (i = _views.reverse_begin(); (i != _views.end()) && !foundFlag && !blockedFlag; --i) {
|
||||||
|
view = *i;
|
||||||
|
if (!view->isVisible()) continue;
|
||||||
|
|
||||||
|
if (view->screenFlags().blocks & SCREVENT_MOUSE)
|
||||||
|
blockedFlag = true;
|
||||||
|
if ((view->screenFlags().get & SCREVENT_MOUSE) && view->isInside(mousePos.x, mousePos.y))
|
||||||
|
foundFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundFlag)
|
||||||
|
view->onEvent(event, 0, mousePos.x, mousePos.y, _captureEvents);
|
||||||
|
else
|
||||||
|
_captureEvents = false;
|
||||||
|
if (_captureEvents)
|
||||||
|
_captureScreen = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::restore(int x1, int y1, int x2, int y2) {
|
||||||
|
RectList *rl = new RectList();
|
||||||
|
Common::Rect redrawBounds(x1, y1, x2, y2);
|
||||||
|
rl->addRect(x1, y1, x2, y2);
|
||||||
|
|
||||||
|
for (ListIterator i = _views.begin(); i != _views.end(); ++i) {
|
||||||
|
View *v = *i;
|
||||||
|
|
||||||
|
if (v->isVisible() && v->bounds().intersects(redrawBounds))
|
||||||
|
v->onRefresh(rl, _vm->_screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->_screen->update();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::restore(const Common::Rect &rect) {
|
||||||
|
restore(rect.left, rect.top, rect.right, rect.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::moveToFront(View *view) {
|
||||||
|
if (_views.size() < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_views.remove(view);
|
||||||
|
|
||||||
|
ListIterator i = _views.begin();
|
||||||
|
while ((i != _views.end()) && ((*i)->layer() <= view->layer()))
|
||||||
|
++i;
|
||||||
|
|
||||||
|
_views.insert(i, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::moveToBack(View *view) {
|
||||||
|
if (_views.size() < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_views.remove(view);
|
||||||
|
|
||||||
|
ListIterator i = _views.begin();
|
||||||
|
while ((i != _views.end()) && ((*i)->layer() < view->layer()))
|
||||||
|
++i;
|
||||||
|
|
||||||
|
_views.insert(i, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
View *ViewManager::getView(int screenType) {
|
||||||
|
ListIterator i = _views.begin();
|
||||||
|
while (i != _views.end()) {
|
||||||
|
if ((*i)->screenType() == screenType)
|
||||||
|
return *i;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::updateState() {
|
||||||
|
Common::List<View *> viewList = _views;
|
||||||
|
|
||||||
|
for (ListIterator i = viewList.begin(); i != viewList.end(); ++i) {
|
||||||
|
if (_vm->_events->quitFlag)
|
||||||
|
return;
|
||||||
|
|
||||||
|
View *v = *i;
|
||||||
|
v->updateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::refreshAll() {
|
||||||
|
_vm->_screen->empty();
|
||||||
|
|
||||||
|
for (ListIterator i = _views.begin(); i != _views.end(); ++i) {
|
||||||
|
View *v = *i;
|
||||||
|
|
||||||
|
if (v->isVisible())
|
||||||
|
v->onRefresh(NULL, _vm->_screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
_vm->_screen->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::showTextView(const char *textViewName, bool returnToMainMenu) {
|
||||||
|
// Deactivate the scene if it's currently active
|
||||||
|
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
|
||||||
|
if (view != NULL)
|
||||||
|
_vm->_viewManager->deleteView(view);
|
||||||
|
|
||||||
|
// Deactivate the main menu if it's currently active
|
||||||
|
view = _vm->_viewManager->getView(VIEWID_MAINMENU);
|
||||||
|
if (view != NULL)
|
||||||
|
_vm->_viewManager->deleteView(view);
|
||||||
|
|
||||||
|
// Activate the textview view
|
||||||
|
_vm->_font->setFont(FONT_CONVERSATION_MADS);
|
||||||
|
TextviewView *textView = new TextviewView(_vm);
|
||||||
|
_vm->_viewManager->addView(textView);
|
||||||
|
if (returnToMainMenu)
|
||||||
|
textView->setScript(textViewName, returnToMainMenuFn);
|
||||||
|
else
|
||||||
|
textView->setScript(textViewName, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ViewManager::showAnimView(const char *animViewName, bool returnToMainMenu) {
|
||||||
|
// Deactivate the scene if it's currently active
|
||||||
|
View *view = _vm->_viewManager->getView(VIEWID_SCENE);
|
||||||
|
if (view != NULL)
|
||||||
|
_vm->_viewManager->deleteView(view);
|
||||||
|
|
||||||
|
// Deactivate the main menu if it's currently active
|
||||||
|
view = _vm->_viewManager->getView(VIEWID_MAINMENU);
|
||||||
|
if (view != NULL)
|
||||||
|
_vm->_viewManager->deleteView(view);
|
||||||
|
|
||||||
|
// Activate the animview view
|
||||||
|
AnimviewView *animView = new AnimviewView(_vm);
|
||||||
|
_vm->_viewManager->addView(animView);
|
||||||
|
if (returnToMainMenu)
|
||||||
|
animView->setScript(animViewName, returnToMainMenuFn);
|
||||||
|
else
|
||||||
|
animView->setScript(animViewName, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace M4
|
191
engines/m4/viewmgr.h
Normal file
191
engines/m4/viewmgr.h
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_VIEWMGR_H
|
||||||
|
#define M4_VIEWMGR_H
|
||||||
|
|
||||||
|
#include "common/array.h"
|
||||||
|
#include "common/list.h"
|
||||||
|
#include "common/events.h"
|
||||||
|
#include "common/rect.h"
|
||||||
|
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/events.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
class View;
|
||||||
|
class ViewManager;
|
||||||
|
|
||||||
|
enum {SCREEN_DIALOG, SCREEN_BUFFER, SCREEN_TEXT, SCREEN_TRANSPARENT};
|
||||||
|
enum ScreenEventType {SCREVENT_NONE = 0, SCREVENT_KEY = 1, SCREVENT_MOUSE = 2, SCREVENT_ALL = 3};
|
||||||
|
enum ScreenLayers {
|
||||||
|
LAYER_BACKGROUND = 0, LAYER_DRIFTER = 1, LAYER_INTERFACE = 1, LAYER_FLOATER = 2,
|
||||||
|
LAYER_SURFACE = 3, LAYER_MENU = 9, LAYER_MOUSE = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ViewIds {
|
||||||
|
VIEWID_MAINMENU = 1,
|
||||||
|
VIEWID_SCENE = 2,
|
||||||
|
VIEWID_TEXTVIEW = 3,
|
||||||
|
VIEWID_ANIMVIEW = 4,
|
||||||
|
VIEWID_MENU = 69,
|
||||||
|
VIEWID_CONVERSATION = 48,
|
||||||
|
VIEWID_INTERFACE = 49
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScreenFlags {
|
||||||
|
bool visible:1;
|
||||||
|
bool transparent:1;
|
||||||
|
bool immovable:1;
|
||||||
|
|
||||||
|
enum ScreenEventType blocks:2;
|
||||||
|
enum ScreenEventType get:2;
|
||||||
|
|
||||||
|
uint layer:4;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SCREEN_FLAGS_DEFAULT _screenFlags.layer = LAYER_DRIFTER; \
|
||||||
|
_screenFlags.get = SCREVENT_ALL; _screenFlags.blocks = SCREVENT_NONE; \
|
||||||
|
_screenFlags.visible = true;
|
||||||
|
#define SCREEN_FLAGS_ALERT _screenFlags.layer = LAYER_FLOATER \
|
||||||
|
_screenFlags.get = SCREVENT_ALL; _screenFlags.blocks = SCREVENT_ALL; \
|
||||||
|
_screenFlags.visible = true;
|
||||||
|
|
||||||
|
class RectList: public Common::Array<Common::Rect> {
|
||||||
|
public:
|
||||||
|
RectList();
|
||||||
|
~RectList();
|
||||||
|
void addRect(int x1, int y1, int x2, int y2);
|
||||||
|
void addRect(const Common::Rect &rect);
|
||||||
|
|
||||||
|
// Common::Rect& operator [](int idx) { return _rects[idx]; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Hotkey {
|
||||||
|
public:
|
||||||
|
typedef void (*Callback)(M4Engine *vm, View *view, uint32 key);
|
||||||
|
Hotkey(uint32 keyVal, Hotkey::Callback callbackFn) : key(keyVal), callback(callbackFn) {};
|
||||||
|
uint32 key;
|
||||||
|
Hotkey::Callback callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HotkeyList {
|
||||||
|
public:
|
||||||
|
HotkeyList(View *owner);
|
||||||
|
~HotkeyList();
|
||||||
|
void add(uint32 key, Hotkey::Callback callback);
|
||||||
|
void remove(uint32 key);
|
||||||
|
bool call(uint32 key);
|
||||||
|
private:
|
||||||
|
Common::Array<Hotkey*> _hotkeys;
|
||||||
|
View *_view;
|
||||||
|
};
|
||||||
|
|
||||||
|
class View: public M4Surface {
|
||||||
|
public:
|
||||||
|
View(M4Engine *vm, const Common::Rect &viewBounds, bool transparent = false);
|
||||||
|
View(M4Engine *vm, int x = 0, int y = 0, bool transparent = false);
|
||||||
|
virtual ~View() {}
|
||||||
|
|
||||||
|
void getCoordinates(Common::Rect &rect);
|
||||||
|
void extract(int *status);
|
||||||
|
virtual void show();
|
||||||
|
virtual void hide();
|
||||||
|
void moveToFront() {}
|
||||||
|
void moveToBack();
|
||||||
|
void moveAbsolute(int x, int y);
|
||||||
|
void moveRelative(int x, int y);
|
||||||
|
void resize(int newWidth, int newHeight);
|
||||||
|
void restore(int x1, int y1, int x2, int y2);
|
||||||
|
|
||||||
|
Common::Rect bounds() const { return _coords; }
|
||||||
|
bool isInside(int x, int y) const { return _coords.contains(x, y); }
|
||||||
|
ScreenFlags screenFlags() const { return _screenFlags; }
|
||||||
|
int screenType() const { return _screenType; }
|
||||||
|
bool isOffscreen() const { return !_screenFlags.visible; }
|
||||||
|
bool isTransparent() const { return _screenFlags.transparent; }
|
||||||
|
bool isVisible() const { return _screenFlags.visible; }
|
||||||
|
uint layer() const { return _screenFlags.layer; }
|
||||||
|
HotkeyList &hotkeys() { return _hotkeys; }
|
||||||
|
|
||||||
|
virtual void onRefresh(RectList *rects, M4Surface *destSurface);
|
||||||
|
virtual bool onEvent(M4EventType eventType, int param, int x, int y, bool &captureEvents) { return false; }
|
||||||
|
virtual void updateState() {};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
M4Engine *_vm;
|
||||||
|
Common::Rect _coords;
|
||||||
|
HotkeyList _hotkeys;
|
||||||
|
int _screenType;
|
||||||
|
ScreenFlags _screenFlags;
|
||||||
|
bool _transparent;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ViewManager {
|
||||||
|
private:
|
||||||
|
M4Engine *_vm;
|
||||||
|
HotkeyList _systemHotkeys;
|
||||||
|
Common::List<View *> _views;
|
||||||
|
View *_captureScreen;
|
||||||
|
bool _captureEvents;
|
||||||
|
public:
|
||||||
|
typedef Common::List<View *>::iterator ListIterator;
|
||||||
|
|
||||||
|
ViewManager(M4Engine *vm);
|
||||||
|
~ViewManager();
|
||||||
|
|
||||||
|
void addView(View *view);
|
||||||
|
void deleteView(View *view);
|
||||||
|
|
||||||
|
void handleEvents(const Common::Event &event);
|
||||||
|
void handleKeyboardEvents(uint32 keycode);
|
||||||
|
void handleMouseEvents(M4EventType event);
|
||||||
|
void restore(int x1, int y1, int x2, int y2);
|
||||||
|
void restore(const Common::Rect &rect);
|
||||||
|
|
||||||
|
void moveToFront(View *view);
|
||||||
|
void moveToBack(View *view);
|
||||||
|
|
||||||
|
Common::List<View *> views() const { return _views; }
|
||||||
|
bool contains(View *key) const {
|
||||||
|
return find(_views.begin(), _views.end(), key) != _views.end();
|
||||||
|
}
|
||||||
|
bool contains(int screenType) { return getView(screenType) != NULL; }
|
||||||
|
View *getView(int screenType);
|
||||||
|
int viewCount() { return _views.size(); }
|
||||||
|
|
||||||
|
void showTextView(const char *textViewName, bool returnToMainMenu = true);
|
||||||
|
void showAnimView(const char *animViewName, bool returnToMainMenu = true);
|
||||||
|
|
||||||
|
void updateState();
|
||||||
|
void refreshAll();
|
||||||
|
HotkeyList &systemHotkeys() { return _systemHotkeys; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
398
engines/m4/woodscript.cpp
Normal file
398
engines/m4/woodscript.cpp
Normal file
|
@ -0,0 +1,398 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/woodscript.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
// FIXME: Put in Engine/WoodScript class
|
||||||
|
RGB8 _mainPalette[256];
|
||||||
|
|
||||||
|
//Woodscript Assembler/Compiler
|
||||||
|
|
||||||
|
int32 Bytecode::_dataFormats[] = {0, 5, 8, 12, 16};
|
||||||
|
|
||||||
|
Bytecode::Bytecode(WoodScript *ws, byte *code, int32 codeSize, Sequence *seq) {
|
||||||
|
_ws = ws;
|
||||||
|
_code = new Common::MemoryReadStream(code, codeSize);
|
||||||
|
_sequence = seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bytecode::~Bytecode() {
|
||||||
|
delete _code;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Bytecode::loadInstruction(Instruction &instruction) {
|
||||||
|
|
||||||
|
//printf("Bytecode::loadInstruction() ip = %08X\n", _code->pos());
|
||||||
|
|
||||||
|
int32 format, data;
|
||||||
|
uint32 code, code2;
|
||||||
|
|
||||||
|
code = _code->readUint32LE();
|
||||||
|
|
||||||
|
instruction.instr = (code >> 25) & 0xFF;
|
||||||
|
instruction.argp[0] = NULL;
|
||||||
|
instruction.argp[1] = NULL;
|
||||||
|
instruction.argp[2] = NULL;
|
||||||
|
instruction.argc = 0;
|
||||||
|
|
||||||
|
// Maybe make this a for-loop?
|
||||||
|
|
||||||
|
format = (code >> 22) & 7;
|
||||||
|
if (format) {
|
||||||
|
/* Load argument 1 */
|
||||||
|
data = code & 0xFFFF;
|
||||||
|
decodeArgument(format, data, instruction.argp[0], instruction.argv[0]);
|
||||||
|
instruction.argc++;
|
||||||
|
/* Load argument 2 */
|
||||||
|
format = (code >> 19) & 7;
|
||||||
|
if (format) {
|
||||||
|
code2 = _code->readUint32LE();
|
||||||
|
data = (code2 >> 16) & 0xFFFF;
|
||||||
|
decodeArgument(format, data, instruction.argp[1], instruction.argv[1]);
|
||||||
|
instruction.argc++;
|
||||||
|
/* Load argument 3 */
|
||||||
|
format = (code >> 16) & 7;
|
||||||
|
if (format) {
|
||||||
|
data = code2 & 0xFFFF;
|
||||||
|
decodeArgument(format, data, instruction.argp[2], instruction.argv[2]);
|
||||||
|
instruction.argc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0; //FIXME check if instruction size is needed by caller
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bytecode::jumpAbsolute(int32 ofs) {
|
||||||
|
_code->seek(ofs * 4);
|
||||||
|
//printf("Bytecode::jumpAbsolute() ofs = %08X\n", _code->pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bytecode::jumpRelative(int32 ofs) {
|
||||||
|
_code->seek(ofs * 4, SEEK_CUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bytecode::setSequence(Sequence *seq) {
|
||||||
|
_sequence = seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bytecode::setCode(byte *code, int32 codeSize) {
|
||||||
|
delete _code;
|
||||||
|
_code = new Common::MemoryReadStream(code, codeSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sequence *Bytecode::sequence() const {
|
||||||
|
assert(_sequence);
|
||||||
|
return _sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bytecode::decodeArgument(int32 format, int32 data, long *&arg, long &value) {
|
||||||
|
|
||||||
|
int32 index;
|
||||||
|
|
||||||
|
if (format == 1) {
|
||||||
|
if (data & 0x8000)
|
||||||
|
index = _sequence->indexReg();
|
||||||
|
else
|
||||||
|
index = data & 0x0FFF;
|
||||||
|
switch (data & 0x7000) {
|
||||||
|
case 0x0000:
|
||||||
|
arg = sequence()->getParentVarPtr(index);
|
||||||
|
value = *arg;
|
||||||
|
break;
|
||||||
|
case 0x1000:
|
||||||
|
arg = sequence()->getVarPtr(index);
|
||||||
|
value = *arg;
|
||||||
|
break;
|
||||||
|
case 0x2000:
|
||||||
|
arg = sequence()->getDataPtr(index);
|
||||||
|
value = *arg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (format == 2) {
|
||||||
|
if (data & 0x8000)
|
||||||
|
index = _sequence->indexReg();
|
||||||
|
else
|
||||||
|
index = data & 0x0FFF;
|
||||||
|
arg = _ws->getGlobalPtr(index);
|
||||||
|
value = *arg;
|
||||||
|
} else {
|
||||||
|
if (data & 0x8000) {
|
||||||
|
value = -(data & 0x7FFF) << (_dataFormats[format - 3]);
|
||||||
|
} else {
|
||||||
|
value = (data & 0x7FFF) << (_dataFormats[format - 3]);
|
||||||
|
}
|
||||||
|
arg = &value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WoodScript::WoodScript(M4Engine *vm) {
|
||||||
|
_vm = vm;
|
||||||
|
_machineId = 0;
|
||||||
|
_assets = new AssetManager(vm);
|
||||||
|
_globals = new long[256]; //FIXME Find out how many globals there should be
|
||||||
|
memset(_globals, 0, sizeof(long));
|
||||||
|
|
||||||
|
_backgroundSurface = NULL;
|
||||||
|
|
||||||
|
Common::Rect viewBounds = Common::Rect(0, 0, 640, 480);
|
||||||
|
//_surfaceView = new View(viewBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
WoodScript::~WoodScript() {
|
||||||
|
delete _assets;
|
||||||
|
delete[] _globals;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sequence *WoodScript::createSequence(Machine *machine, int32 sequenceHash) {
|
||||||
|
Sequence *sequence = new Sequence(this, machine, sequenceHash);
|
||||||
|
_sequences.push_back(sequence);
|
||||||
|
_layers.push_back(sequence);
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::runSequencePrograms() {
|
||||||
|
// A lot TODO
|
||||||
|
for (Common::Array<Sequence*>::iterator it = _sequences.begin(); it != _sequences.end(); it++) {
|
||||||
|
Sequence *sequence = *it;
|
||||||
|
if (sequence->isActive()) {
|
||||||
|
sequence->runProgram();
|
||||||
|
if (sequence->isTerminated() && sequence->hasEndOfSequenceRequestPending()) {
|
||||||
|
_endOfSequenceRequestList.push_back(sequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::runEndOfSequenceRequests() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::runTimerSequenceRequests() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Machine *WoodScript::createMachine(int32 machineHash, Sequence *parentSeq,
|
||||||
|
int32 dataHash, int32 dataRowIndex, int callbackHandler, const char *machineName) {
|
||||||
|
|
||||||
|
//printf("WoodScript::createMachine(%d)\n", machineHash); fflush(stdout);
|
||||||
|
|
||||||
|
Machine *machine = new Machine(this, machineHash, parentSeq, dataHash, dataRowIndex, callbackHandler, machineName, _machineId);
|
||||||
|
_machineId++;
|
||||||
|
|
||||||
|
_machines.push_back(machine);
|
||||||
|
|
||||||
|
// goto first state for initialization
|
||||||
|
machine->enterState();
|
||||||
|
|
||||||
|
return machine;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 WoodScript::loadSeries(const char* seriesName, int32 hash, RGB8* palette) {
|
||||||
|
return _assets->addSpriteAsset(seriesName, hash, palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::unloadSeries(int32 hash) {
|
||||||
|
_assets->clearAssets(kAssetTypeCELS, hash, hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::setSeriesFramerate(Machine *machine, int32 frameRate) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Machine *WoodScript::playSeries(const char *seriesName, long layer, uint32 flags, int32 triggerNum,
|
||||||
|
int32 frameRate, int32 loopCount, int32 s, int32 x, int32 y,
|
||||||
|
int32 firstFrame, int32 lastFrame) {
|
||||||
|
|
||||||
|
//printf("WoodScript::playSeries(%s)\n", seriesName);
|
||||||
|
|
||||||
|
RGB8 *palette = NULL;
|
||||||
|
if (flags & SERIES_LOAD_PALETTE)
|
||||||
|
palette = &_mainPalette[0];
|
||||||
|
|
||||||
|
int32 spriteHash = _assets->addSpriteAsset(seriesName, -1, palette);
|
||||||
|
|
||||||
|
_globals[kGlobTemp1] = (long)spriteHash << 24;
|
||||||
|
_globals[kGlobTemp2] = layer << 16;
|
||||||
|
_globals[kGlobTemp3] = _vm->_kernel->createTrigger(triggerNum);
|
||||||
|
_globals[kGlobTemp4] = frameRate << 16;
|
||||||
|
_globals[kGlobTemp5] = loopCount << 16;
|
||||||
|
_globals[kGlobTemp6] = (s << 16) / 100;
|
||||||
|
_globals[kGlobTemp7] = x << 16;
|
||||||
|
_globals[kGlobTemp8] = y << 16;
|
||||||
|
_globals[kGlobTemp9] = firstFrame << 16;
|
||||||
|
_globals[kGlobTemp10] = lastFrame << 16;
|
||||||
|
_globals[kGlobTemp11] = (flags & SERIES_PINGPONG) ? 0x10000 : 0;
|
||||||
|
_globals[kGlobTemp12] = (flags & SERIES_BACKWARD) ? 0x10000 : 0;
|
||||||
|
_globals[kGlobTemp13] = (flags & SERIES_RANDOM) ? 0x10000 : 0;
|
||||||
|
_globals[kGlobTemp14] = (flags & SERIES_STICK) ? 0x10000 : 0;
|
||||||
|
_globals[kGlobTemp15] = (flags & SERIES_LOOP_TRIGGER) ? 0x10000 : 0;
|
||||||
|
_globals[kGlobTemp16] = (flags & SERIES_HORZ_FLIP) ? 0x10000 : 0;
|
||||||
|
|
||||||
|
return createMachine(0, NULL, -1, -1, kCallbackTriggerDispatch, seriesName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Machine *WoodScript::showSeries(const char *seriesName, long layer, uint32 flags, int32 triggerNum,
|
||||||
|
int32 duration, int32 index, int32 s, int32 x, int32 y) {
|
||||||
|
|
||||||
|
RGB8 *palette = NULL;
|
||||||
|
if (flags & SERIES_LOAD_PALETTE)
|
||||||
|
palette = &_mainPalette[0];
|
||||||
|
|
||||||
|
int32 spriteHash = _assets->addSpriteAsset(seriesName, -1, palette);
|
||||||
|
|
||||||
|
_globals[kGlobTemp1] = spriteHash << 24;
|
||||||
|
_globals[kGlobTemp2] = layer << 16;
|
||||||
|
_globals[kGlobTemp3] = _vm->_kernel->createTrigger(triggerNum);
|
||||||
|
_globals[kGlobTemp4] = duration << 16;
|
||||||
|
_globals[kGlobTemp5] = index << 16;
|
||||||
|
_globals[kGlobTemp6] = (s << 16) / 100;
|
||||||
|
_globals[kGlobTemp7] = x << 16;
|
||||||
|
_globals[kGlobTemp8] = y << 16;
|
||||||
|
_globals[kGlobTemp14] = (flags & SERIES_STICK) ? 0x10000 : 0;
|
||||||
|
_globals[kGlobTemp16] = (flags & SERIES_HORZ_FLIP) ? 0x10000 : 0;
|
||||||
|
|
||||||
|
return createMachine(1, NULL, -1, -1, kCallbackTriggerDispatch, seriesName);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Machine *WoodScript::streamSeries(const char *seriesName, int32 frameRate, long layer, int32 triggerNum) {
|
||||||
|
//printf("WoodScript::streamSeries(%s)\n", seriesName);
|
||||||
|
_globals[kGlobTemp1] = frameRate << 16;
|
||||||
|
/* FIXME: Single frames from a stream series will be decompressed on-the-fly, contrary to
|
||||||
|
"normal" sprite series, to save some memory, and since no random access to single
|
||||||
|
frames is needed, this is ok.
|
||||||
|
*/
|
||||||
|
_globals[kGlobTemp4] = 0; // The actual stream is opened in the Sequence
|
||||||
|
_globals[kGlobTemp5] = 0;//TODO: kernel_trigger_create(triggerNum); // trigger
|
||||||
|
_globals[kGlobTemp6] = layer << 16; // layer
|
||||||
|
return createMachine(6, NULL, -1, -1, kCallbackTriggerDispatch, seriesName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::update() {
|
||||||
|
// TODO: Don't show hidden sequences etc.
|
||||||
|
|
||||||
|
// TODO: For now, prevent any engine action if a menu is being displayed - eventually this should be
|
||||||
|
// changed to a proper check of the engine paused variable, which the menus should set while active
|
||||||
|
if (_vm->_viewManager->getView(VIEWID_MENU) != NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//TODO: Include _pauseTime
|
||||||
|
uint32 clockTime = g_system->getMillis() / 60; // FIXME: g_system
|
||||||
|
_globals[kGlobTimeDelta] = clockTime - _globals[kGlobTime];
|
||||||
|
_globals[kGlobTime] += _globals[kGlobTimeDelta];
|
||||||
|
|
||||||
|
runSequencePrograms();
|
||||||
|
|
||||||
|
if (_backgroundSurface) {
|
||||||
|
// FIXME: For now, copy the whole surface. Later, copy only the rectangles that need updating.
|
||||||
|
_backgroundSurface->copyTo(_surfaceView);
|
||||||
|
} else {
|
||||||
|
// "This should never happen."
|
||||||
|
_surfaceView->fillRect(Common::Rect(0, 0, 640, 480), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// FIXME: This should be done when a new palette is set
|
||||||
|
byte palette[1024];
|
||||||
|
g_system->grabPalette(palette, 0, 256);
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
_mainPalette[i].r = palette[i * 4 + 0];
|
||||||
|
_mainPalette[i].g = palette[i * 4 + 1];
|
||||||
|
_mainPalette[i].b = palette[i * 4 + 2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Common::Array<Sequence*>::iterator it = _layers.begin(); it != _layers.end(); it++) {
|
||||||
|
Sequence *sequence = *it;
|
||||||
|
|
||||||
|
// TODO: Use correct clipRect etc.
|
||||||
|
Common::Rect clipRect = Common::Rect(0, 0, 640, 480);
|
||||||
|
Common::Rect updateRect;
|
||||||
|
|
||||||
|
sequence->draw(_surfaceView, clipRect, updateRect);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle end-of-sequence requests
|
||||||
|
if (_endOfSequenceRequestList.size() > 0) {
|
||||||
|
for (Common::Array<Sequence*>::iterator it = _endOfSequenceRequestList.begin(); it != _endOfSequenceRequestList.end(); it++) {
|
||||||
|
Sequence *sequence = *it;
|
||||||
|
|
||||||
|
EndOfSequenceRequestItem endOfSequenceRequestItem = sequence->getEndOfSequenceRequestItem();
|
||||||
|
sequence->getMachine()->execBlock(endOfSequenceRequestItem.codeOffset, endOfSequenceRequestItem.count);
|
||||||
|
}
|
||||||
|
_endOfSequenceRequestList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::clear() {
|
||||||
|
|
||||||
|
for (Common::Array<Sequence*>::iterator it = _sequences.begin(); it != _sequences.end(); it++)
|
||||||
|
delete *it;
|
||||||
|
_sequences.clear();
|
||||||
|
|
||||||
|
for (Common::Array<Machine*>::iterator it = _machines.begin(); it != _machines.end(); it++)
|
||||||
|
delete *it;
|
||||||
|
_machines.clear();
|
||||||
|
|
||||||
|
_layers.clear();
|
||||||
|
_endOfSequenceRequestList.clear();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::setDepthTable(int16 *depthTable) {
|
||||||
|
_depthTable = depthTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
long *WoodScript::getGlobalPtr(int index) {
|
||||||
|
return &_globals[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
long WoodScript::getGlobal(int index) {
|
||||||
|
return _globals[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::setGlobal(int index, long value) {
|
||||||
|
_globals[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::setBackgroundSurface(M4Surface *backgroundSurface) {
|
||||||
|
_backgroundSurface = backgroundSurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WoodScript::setSurfaceView(View *view) {
|
||||||
|
_surfaceView = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
RGB8 *WoodScript::getMainPalette() const {
|
||||||
|
return _mainPalette;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
351
engines/m4/woodscript.h
Normal file
351
engines/m4/woodscript.h
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef M4_WOODSCRIPT_H
|
||||||
|
#define M4_WOODSCRIPT_H
|
||||||
|
|
||||||
|
#include "common/scummsys.h"
|
||||||
|
#include "common/util.h"
|
||||||
|
#include "common/str.h"
|
||||||
|
#include "common/array.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
#include "graphics/surface.h"
|
||||||
|
|
||||||
|
#include "m4/globals.h"
|
||||||
|
#include "m4/assets.h"
|
||||||
|
#include "m4/resource.h"
|
||||||
|
#include "m4/sprite.h"
|
||||||
|
#include "m4/m4.h"
|
||||||
|
#include "m4/graphics.h"
|
||||||
|
#include "m4/viewmgr.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
class M4Engine;
|
||||||
|
class WoodScript;
|
||||||
|
class Machine;
|
||||||
|
class Sequence;
|
||||||
|
class AssetManager;
|
||||||
|
class View;
|
||||||
|
|
||||||
|
struct Instruction {
|
||||||
|
int32 instr;
|
||||||
|
long *argp[3];
|
||||||
|
long argv[3];
|
||||||
|
int argc;
|
||||||
|
// Helper method; many opcode functions can get either a defined value or a random value
|
||||||
|
long getValue() {
|
||||||
|
if (argc == 3)
|
||||||
|
return _vm->imath_ranged_rand16(argv[1], argv[2]);
|
||||||
|
else
|
||||||
|
return argv[1];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Bytecode {
|
||||||
|
public:
|
||||||
|
Bytecode(WoodScript *ws, byte *code, int32 codeSize, Sequence *seq);
|
||||||
|
~Bytecode();
|
||||||
|
int loadInstruction(Instruction &instruction);
|
||||||
|
void jumpAbsolute(int32 ofs);
|
||||||
|
void jumpRelative(int32 ofs);
|
||||||
|
void setSequence(Sequence *sequence);
|
||||||
|
void setCode(byte *code, int32 codeSize);
|
||||||
|
Sequence *sequence() const;
|
||||||
|
uint32 pos() const { return _code->pos() / 4; }
|
||||||
|
protected:
|
||||||
|
WoodScript *_ws;
|
||||||
|
Common::MemoryReadStream *_code;
|
||||||
|
Sequence *_sequence;
|
||||||
|
static int32 _dataFormats[];
|
||||||
|
bool decodeArgument(int32 format, int32 data, long *&arg, long &value);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EndOfSequenceRequestItem {
|
||||||
|
int32 codeOffset, count;
|
||||||
|
EndOfSequenceRequestItem() : codeOffset(-1) {}
|
||||||
|
bool isValid() const { return codeOffset >= 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Common::Array<EndOfSequenceRequestItem> EndOfSequenceRequestList;
|
||||||
|
|
||||||
|
class Sequence {
|
||||||
|
public:
|
||||||
|
Sequence(WoodScript *ws, Machine *machine, int32 sequenceHash);
|
||||||
|
~Sequence();
|
||||||
|
|
||||||
|
void pause();
|
||||||
|
void resume();
|
||||||
|
void issueEndOfSequenceRequest(int32 codeOffset, int32 count);
|
||||||
|
void cancelEndOfSequenceRequest();
|
||||||
|
|
||||||
|
bool runProgram();
|
||||||
|
|
||||||
|
bool changeProgram(int32 sequenceHash);
|
||||||
|
|
||||||
|
void clearVars();
|
||||||
|
|
||||||
|
long *getVarPtr(int index);
|
||||||
|
long *getParentVarPtr(int index);
|
||||||
|
long *getDataPtr(int index);
|
||||||
|
|
||||||
|
void setActive(bool active) { _active = active; }
|
||||||
|
bool isActive() const { return _active; }
|
||||||
|
|
||||||
|
bool isTerminated() const { return _terminated; }
|
||||||
|
|
||||||
|
void draw(M4Surface *surface, const Common::Rect &clipRect, Common::Rect &updateRect);
|
||||||
|
|
||||||
|
bool s1_end(Instruction &instruction);
|
||||||
|
bool s1_clearVars(Instruction &instruction);
|
||||||
|
bool s1_set(Instruction &instruction);
|
||||||
|
bool s1_compare(Instruction &instruction);
|
||||||
|
bool s1_add(Instruction &instruction);
|
||||||
|
bool s1_sub(Instruction &instruction);
|
||||||
|
bool s1_mul(Instruction &instruction);
|
||||||
|
bool s1_div(Instruction &instruction);
|
||||||
|
bool s1_and(Instruction &instruction);
|
||||||
|
bool s1_or(Instruction &instruction);
|
||||||
|
bool s1_not(Instruction &instruction);
|
||||||
|
bool s1_sin(Instruction &instruction);
|
||||||
|
bool s1_cos(Instruction &instruction);
|
||||||
|
bool s1_abs(Instruction &instruction);
|
||||||
|
bool s1_min(Instruction &instruction);
|
||||||
|
bool s1_max(Instruction &instruction);
|
||||||
|
bool s1_mod(Instruction &instruction);
|
||||||
|
bool s1_floor(Instruction &instruction);
|
||||||
|
bool s1_round(Instruction &instruction);
|
||||||
|
bool s1_ceil(Instruction &instruction);
|
||||||
|
bool s1_point(Instruction &instruction);
|
||||||
|
bool s1_dist2d(Instruction &instruction);
|
||||||
|
bool s1_crunch(Instruction &instruction);
|
||||||
|
bool s1_branch(Instruction &instruction);
|
||||||
|
bool s1_setFrame(Instruction &instruction);
|
||||||
|
bool s1_sendMessage(Instruction &instruction);
|
||||||
|
bool s1_push(Instruction &instruction);
|
||||||
|
bool s1_pop(Instruction &instruction);
|
||||||
|
bool s1_jumpSub(Instruction &instruction);
|
||||||
|
bool s1_return(Instruction &instruction);
|
||||||
|
bool s1_getFrameCount(Instruction &instruction);
|
||||||
|
bool s1_getFrameRate(Instruction &instruction);
|
||||||
|
bool s1_getCelsPixSpeed(Instruction &instruction);
|
||||||
|
bool s1_setIndex(Instruction &instruction);
|
||||||
|
bool s1_setLayer(Instruction &instruction);
|
||||||
|
bool s1_setDepth(Instruction &instruction);
|
||||||
|
bool s1_setData(Instruction &instruction);
|
||||||
|
bool s1_openStream(Instruction &instruction);
|
||||||
|
bool s1_streamNextFrame(Instruction &instruction);
|
||||||
|
bool s1_closeStream(Instruction &instruction);
|
||||||
|
|
||||||
|
int32 indexReg() const { return _indexReg; }
|
||||||
|
|
||||||
|
EndOfSequenceRequestItem getEndOfSequenceRequestItem() const { return _endOfSequenceRequest; }
|
||||||
|
bool hasEndOfSequenceRequestPending() const { return _endOfSequenceRequest.isValid(); }
|
||||||
|
void resetEndOfSequenceRequest() { _endOfSequenceRequest.codeOffset = -1; }
|
||||||
|
|
||||||
|
Machine *getMachine() const { return _machine; }
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WoodScript *_ws;
|
||||||
|
Bytecode *_code;
|
||||||
|
|
||||||
|
long *_vars;
|
||||||
|
bool _active, _terminated;
|
||||||
|
Machine *_machine;
|
||||||
|
Sequence *_parentSequence;
|
||||||
|
int32 _layer;
|
||||||
|
int32 _startTime, _switchTime;
|
||||||
|
long *_dataRow;
|
||||||
|
int32 _localVarCount;
|
||||||
|
int32 _cmpFlags;
|
||||||
|
|
||||||
|
EndOfSequenceRequestItem _endOfSequenceRequest;
|
||||||
|
|
||||||
|
int32 _indexReg;
|
||||||
|
|
||||||
|
M4Sprite *_curFrame;
|
||||||
|
|
||||||
|
int32 _sequenceHash;
|
||||||
|
|
||||||
|
int32 _returnHashes[8]; //FIXME: Use constant instead of 8
|
||||||
|
uint32 _returnOffsets[8];
|
||||||
|
int32 _returnStackIndex;
|
||||||
|
|
||||||
|
Common::SeekableReadStream *_stream;
|
||||||
|
SpriteAsset *_streamSpriteAsset;
|
||||||
|
|
||||||
|
bool streamOpen();
|
||||||
|
bool streamNextFrame();
|
||||||
|
void streamClose();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class Machine {
|
||||||
|
public:
|
||||||
|
Machine(WoodScript *ws, int32 machineHash, Sequence *parentSeq, int32 dataHash,
|
||||||
|
int32 dataRowIndex, int callbackHandler, Common::String machineName, int32 id);
|
||||||
|
~Machine();
|
||||||
|
|
||||||
|
void clearMessages();
|
||||||
|
void clearPersistentMessages();
|
||||||
|
void restorePersistentMessages();
|
||||||
|
void sendMessage(uint32 messageHash, long messageValue, Machine *sender);
|
||||||
|
void resetSwitchTime();
|
||||||
|
bool changeSequenceProgram(int32 sequenceHash);
|
||||||
|
|
||||||
|
bool searchMessages(uint32 messageHash, uint32 messageValue, Machine *sender);
|
||||||
|
bool searchPersistentMessages(uint32 messageHash, uint32 messageValue, Machine *sender);
|
||||||
|
|
||||||
|
void enterState();
|
||||||
|
int32 execInstruction();
|
||||||
|
void execBlock(int32 offset, int32 count);
|
||||||
|
int32 getState() { return _currentState; }
|
||||||
|
|
||||||
|
int32 getId() const { return _id; }
|
||||||
|
|
||||||
|
bool m1_gotoState(Instruction &instruction);
|
||||||
|
bool m1_jump(Instruction &instruction);
|
||||||
|
bool m1_terminate(Instruction &instruction);
|
||||||
|
bool m1_startSequence(Instruction &instruction);
|
||||||
|
bool m1_pauseSequence(Instruction &instruction);
|
||||||
|
bool m1_resumeSequence(Instruction &instruction);
|
||||||
|
bool m1_storeValue(Instruction &instruction);
|
||||||
|
bool m1_sendMessage(Instruction &instruction);
|
||||||
|
bool m1_broadcastMessage(Instruction &instruction);
|
||||||
|
bool m1_replyMessage(Instruction &instruction);
|
||||||
|
bool m1_sendSystemMessage(Instruction &instruction);
|
||||||
|
bool m1_createMachine(Instruction &instruction);
|
||||||
|
bool m1_createMachineEx(Instruction &instruction);
|
||||||
|
bool m1_clearVars(Instruction &instruction);
|
||||||
|
|
||||||
|
void m1_onEndSequence(Instruction &instruction);
|
||||||
|
void m1_onMessage(Instruction &instruction);
|
||||||
|
void m1_switchLt(Instruction &instruction);
|
||||||
|
void m1_switchLe(Instruction &instruction);
|
||||||
|
void m1_switchEq(Instruction &instruction);
|
||||||
|
void m1_switchNe(Instruction &instruction);
|
||||||
|
void m1_switchGe(Instruction &instruction);
|
||||||
|
void m1_switchGt(Instruction &instruction);
|
||||||
|
|
||||||
|
long *dataRow() const { return _dataRow; }
|
||||||
|
Sequence *parentSequence() const { return _parentSequence; }
|
||||||
|
Common::String name() const { return _name; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WoodScript *_ws;
|
||||||
|
Bytecode *_code;
|
||||||
|
|
||||||
|
Common::String _name;
|
||||||
|
Sequence *_sequence, *_parentSequence;
|
||||||
|
byte *_mach;
|
||||||
|
int32 _machHash, _machineCodeOffset;
|
||||||
|
int32 _stateCount, _stateTableOffset;
|
||||||
|
long *_dataRow;
|
||||||
|
int32 _id, _recursionLevel, _currentState, _targetCount;
|
||||||
|
/* TODO:
|
||||||
|
m->msgReplyXM = NULL;
|
||||||
|
m->CintrMsg = CintrMsg;
|
||||||
|
_walkPath
|
||||||
|
_messages
|
||||||
|
_persistentMessages
|
||||||
|
_usedPersistentMessages
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
class WoodScript {
|
||||||
|
public:
|
||||||
|
|
||||||
|
WoodScript(M4Engine *vm);
|
||||||
|
~WoodScript();
|
||||||
|
|
||||||
|
Machine *createMachine(int32 machineHash, Sequence *parentSeq, int32 dataHash, int32 dataRowIndex, int callbackHandler, const char *machineName);
|
||||||
|
Sequence *createSequence(Machine *machine, int32 sequenceHash);
|
||||||
|
|
||||||
|
void runSequencePrograms();
|
||||||
|
void runEndOfSequenceRequests();
|
||||||
|
void runTimerSequenceRequests();
|
||||||
|
|
||||||
|
/* Series */
|
||||||
|
// Move to own class, e.g. SeriesPlayer
|
||||||
|
int32 loadSeries(const char* seriesName, int32 hash, RGB8* palette);
|
||||||
|
void unloadSeries(int32 hash);
|
||||||
|
void setSeriesFramerate(Machine *machine, int32 frameRate);
|
||||||
|
Machine *playSeries(const char *seriesName, long layer, uint32 flags, int32 triggerNum,
|
||||||
|
int32 frameRate, int32 loopCount, int32 s, int32 x, int32 y,
|
||||||
|
int32 firstFrame, int32 lastFrame);
|
||||||
|
Machine *showSeries(const char *seriesName, long layer, uint32 flags, int32 triggerNum,
|
||||||
|
int32 duration, int32 index, int32 s, int32 x, int32 y);
|
||||||
|
Machine *streamSeries(const char *seriesName, int32 frameRate, long layer, int32 triggerNum);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/* Misc */
|
||||||
|
void setDepthTable(int16 *depthTable);
|
||||||
|
|
||||||
|
long *getGlobalPtr(int index);
|
||||||
|
long getGlobal(int index);
|
||||||
|
void setGlobal(int index, long value);
|
||||||
|
|
||||||
|
AssetManager *assets() const { return _assets; }
|
||||||
|
|
||||||
|
// Sets the untouched, clean surface which contains the room background
|
||||||
|
void setBackgroundSurface(M4Surface *backgroundSurface);
|
||||||
|
// Sets the view which is used for drawing
|
||||||
|
void setSurfaceView(View *view);
|
||||||
|
|
||||||
|
RGB8 *getMainPalette() const;
|
||||||
|
|
||||||
|
void setInverseColorTable(byte *inverseColorTable) { _inverseColorTable = inverseColorTable; }
|
||||||
|
byte *getInverseColorTable() const { return _inverseColorTable; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
M4Engine *_vm;
|
||||||
|
AssetManager *_assets;
|
||||||
|
|
||||||
|
Common::Array<Sequence*> _sequences, _layers;
|
||||||
|
Common::Array<Machine*> _machines;
|
||||||
|
int32 _machineId;
|
||||||
|
|
||||||
|
long *_globals;
|
||||||
|
|
||||||
|
Common::Array<Sequence*> _endOfSequenceRequestList;
|
||||||
|
|
||||||
|
int32 _indexReg;
|
||||||
|
|
||||||
|
/* Misc */
|
||||||
|
int16 *_depthTable;
|
||||||
|
byte *_inverseColorTable;
|
||||||
|
M4Surface *_backgroundSurface;
|
||||||
|
View *_surfaceView;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // End of namespace M4
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
422
engines/m4/ws_machine.cpp
Normal file
422
engines/m4/ws_machine.cpp
Normal file
|
@ -0,0 +1,422 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/woodscript.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
bool (Machine::*machineCommandsTable[])(Instruction &instruction) = {
|
||||||
|
NULL,
|
||||||
|
NULL,//TODO: nop
|
||||||
|
&Machine::m1_gotoState,
|
||||||
|
&Machine::m1_jump,
|
||||||
|
&Machine::m1_terminate,
|
||||||
|
&Machine::m1_startSequence,
|
||||||
|
&Machine::m1_pauseSequence,
|
||||||
|
&Machine::m1_resumeSequence,
|
||||||
|
&Machine::m1_storeValue,
|
||||||
|
&Machine::m1_sendMessage,
|
||||||
|
&Machine::m1_broadcastMessage,
|
||||||
|
&Machine::m1_replyMessage,
|
||||||
|
&Machine::m1_sendSystemMessage,
|
||||||
|
&Machine::m1_createMachine,
|
||||||
|
&Machine::m1_createMachineEx,
|
||||||
|
&Machine::m1_clearVars
|
||||||
|
};
|
||||||
|
|
||||||
|
void (Machine::*machineConditionalsTable[])(Instruction &instruction) = {
|
||||||
|
NULL,//TODO: after
|
||||||
|
&Machine::m1_onEndSequence,
|
||||||
|
&Machine::m1_onMessage,
|
||||||
|
NULL,//TODO: on_p_msg
|
||||||
|
&Machine::m1_switchLt,
|
||||||
|
&Machine::m1_switchLe,
|
||||||
|
&Machine::m1_switchEq,
|
||||||
|
&Machine::m1_switchNe,
|
||||||
|
&Machine::m1_switchGe,
|
||||||
|
&Machine::m1_switchGt,
|
||||||
|
};
|
||||||
|
|
||||||
|
Machine::Machine(WoodScript *ws, int32 machineHash, Sequence *parentSeq, int32 dataHash,
|
||||||
|
int32 dataRowIndex, int callbackHandler, Common::String machineName, int32 id) {
|
||||||
|
|
||||||
|
_ws = ws;
|
||||||
|
|
||||||
|
_machHash = machineHash;
|
||||||
|
_name = machineName;
|
||||||
|
_id = id;
|
||||||
|
|
||||||
|
// initialize the machine's bytecode
|
||||||
|
MachineAsset *machineAsset = _ws->assets()->getMachine(_machHash);
|
||||||
|
byte *code;
|
||||||
|
uint32 codeSize;
|
||||||
|
machineAsset->getCode(code, codeSize);
|
||||||
|
_code = new Bytecode(_ws, code, codeSize, NULL);
|
||||||
|
|
||||||
|
// initialize the machine's data
|
||||||
|
if (dataHash >= 0) {
|
||||||
|
DataAsset *dataAsset = _ws->assets()->getData(dataHash);
|
||||||
|
_dataRow = dataAsset->getRow(dataRowIndex);
|
||||||
|
} else {
|
||||||
|
_dataRow = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_recursionLevel = 0;
|
||||||
|
_currentState = 0;
|
||||||
|
_sequence = NULL;
|
||||||
|
_parentSequence = parentSeq;
|
||||||
|
_targetCount = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Machine::~Machine() {
|
||||||
|
delete _code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::clearMessages() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::clearPersistentMessages() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::restorePersistentMessages() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::sendMessage(uint32 messageHash, long messageValue, Machine *sender) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::resetSwitchTime() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::changeSequenceProgram(int32 sequenceHash) {
|
||||||
|
return _sequence->changeProgram(sequenceHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::searchMessages(uint32 messageHash, uint32 messageValue, Machine *sender) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::searchPersistentMessages(uint32 messageHash, uint32 messageValue, Machine *sender) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::enterState() {
|
||||||
|
|
||||||
|
MachineAsset *machineAsset = _ws->assets()->getMachine(_machHash);
|
||||||
|
|
||||||
|
_code->jumpAbsolute(machineAsset->getStateOffset(_currentState));
|
||||||
|
|
||||||
|
int32 instruction = -1;
|
||||||
|
|
||||||
|
_recursionLevel++;
|
||||||
|
|
||||||
|
int32 oldId = _id;
|
||||||
|
int32 oldRecursionLevel = _recursionLevel;
|
||||||
|
|
||||||
|
while (instruction && instruction != 4 && _id == oldId && _recursionLevel == oldRecursionLevel) {
|
||||||
|
instruction = execInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instruction != 4 && _id == oldId && _recursionLevel == oldRecursionLevel) {
|
||||||
|
_recursionLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 Machine::execInstruction() {
|
||||||
|
|
||||||
|
//printf("Machine::execInstruction()\n"); fflush(stdout);
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
Instruction instruction;
|
||||||
|
//Sequence *sequence;
|
||||||
|
int32 machID = _id;
|
||||||
|
|
||||||
|
_code->loadInstruction(instruction);
|
||||||
|
|
||||||
|
if (instruction.instr >= 64) {
|
||||||
|
if (machineConditionalsTable[instruction.instr - 64] != NULL)
|
||||||
|
(this->*machineConditionalsTable[instruction.instr - 64])(instruction);
|
||||||
|
/* The next line is to yield on unimplemented opcodes */
|
||||||
|
else { fflush(stdout); g_system->delayMillis(5000); }
|
||||||
|
} else if (instruction.instr > 0) {
|
||||||
|
if (machineCommandsTable[instruction.instr] != NULL)
|
||||||
|
done = !(this->*machineCommandsTable[instruction.instr])(instruction);
|
||||||
|
/* The next line is to yield on unimplemented opcodes */
|
||||||
|
else { fflush(stdout); g_system->delayMillis(5000); }
|
||||||
|
if (done) {
|
||||||
|
if (_id == machID) {
|
||||||
|
//TODO: Cancel all requests
|
||||||
|
if (_currentState == -1) {
|
||||||
|
// TODO: Set terminated flag and delete machine in WoodScript update
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// initialize new state
|
||||||
|
enterState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return instruction.instr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::execBlock(int32 offset, int32 count) {
|
||||||
|
|
||||||
|
// MachineAsset *machineAsset = _ws->assets()->getMachine(_machHash);
|
||||||
|
|
||||||
|
int32 startOffset = offset, endOffset = offset + count;
|
||||||
|
|
||||||
|
_recursionLevel++;
|
||||||
|
|
||||||
|
int32 oldId = _id;
|
||||||
|
int32 oldRecursionLevel = _recursionLevel;
|
||||||
|
|
||||||
|
_code->jumpAbsolute(offset);
|
||||||
|
|
||||||
|
int32 instruction = -1;
|
||||||
|
|
||||||
|
//printf("---------------------------------------\n"); fflush(stdout);
|
||||||
|
|
||||||
|
while (instruction && instruction != 4 && _id == oldId && _recursionLevel == oldRecursionLevel &&
|
||||||
|
_code->pos() >= (uint32)startOffset && _code->pos() < (uint32)endOffset) {
|
||||||
|
|
||||||
|
instruction = execInstruction();
|
||||||
|
//g_system->delayMillis(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("---------------------------------------\n"); fflush(stdout);
|
||||||
|
|
||||||
|
if (instruction == 3) {
|
||||||
|
execInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (instruction != 4 && _id == oldId && _recursionLevel == oldRecursionLevel) {
|
||||||
|
_recursionLevel--;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_gotoState(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_gotoState() state = %d\n", (int32)instruction.argv[0] >> 16);
|
||||||
|
|
||||||
|
_currentState = (int32)instruction.argv[0] >> 16;
|
||||||
|
_recursionLevel = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_jump(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_jump() ofs = %08X\n", (int32)instruction.argv[0] >> 16);
|
||||||
|
|
||||||
|
_code->jumpRelative((int32)instruction.argv[0] >> 16);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_terminate(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_terminate()\n"); fflush(stdout);
|
||||||
|
|
||||||
|
_currentState = -1;
|
||||||
|
_recursionLevel = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_startSequence(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_startSequence() sequence hash = %d\n", (uint32)instruction.argv[0] >> 16); fflush(stdout);
|
||||||
|
|
||||||
|
int32 sequenceHash = instruction.argv[0] >> 16;
|
||||||
|
if (_sequence == NULL) {
|
||||||
|
//printf("Machine::m1_startSequence() creating new sequence\n");
|
||||||
|
_sequence = _ws->createSequence(this, sequenceHash);
|
||||||
|
_code->setSequence(_sequence);
|
||||||
|
} else {
|
||||||
|
//printf("Machine::m1_startSequence() using existing sequence\n");
|
||||||
|
_sequence->changeProgram(sequenceHash);
|
||||||
|
//_code->setSequence(_sequence);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_pauseSequence(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_pauseSequence()\n"); fflush(stdout);
|
||||||
|
|
||||||
|
_sequence->pause();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_resumeSequence(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_resumeSequence()\n"); fflush(stdout);
|
||||||
|
|
||||||
|
_sequence->resume();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_storeValue(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_storeValue() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
|
||||||
|
|
||||||
|
*instruction.argp[0] = instruction.getValue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_sendMessage(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_sendMessage() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//TODO
|
||||||
|
long messageValue;
|
||||||
|
|
||||||
|
if (instruction.argc == 3) {
|
||||||
|
messageValue = instruction.argv[2];
|
||||||
|
} else {
|
||||||
|
messageValue = 0;
|
||||||
|
}
|
||||||
|
//_ws->sendMessage((uint32)instruction.argv[1], messageValue, (uint32)instruction.argv[0] >> 16);
|
||||||
|
//void SendWSMessage(uint32 msgHash, long msgValue, machine *recvM, uint32 machHash, machine *sendM, int32 msgCount) {
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_broadcastMessage(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_broadcastMessage() %p = %d (%08X)\n", (void*)instruction.argp[0], (uint32)instruction.argv[1], (uint32)instruction.argv[1]);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//TODO
|
||||||
|
long messageValue;
|
||||||
|
|
||||||
|
if (instruction.argc == 3) {
|
||||||
|
messageValue = instruction.argv[2];
|
||||||
|
} else {
|
||||||
|
messageValue = 0;
|
||||||
|
}
|
||||||
|
//_ws->sendMessage((uint32)instruction.argv[1], messageValue, (uint32)instruction.argv[0] >> 16);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_replyMessage(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_replyMessage() messageHash = %d; messageValue = %d\n", (uint32)instruction.argv[0], (uint32)instruction.argv[1]);
|
||||||
|
#if 0
|
||||||
|
if (myArg2) {
|
||||||
|
msgValue = *myArg2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msgValue = 0;
|
||||||
|
}
|
||||||
|
SendWSMessage(*myArg1, msgValue, m->msgReplyXM, 0, m, 1);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_sendSystemMessage(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_sendSystemMessage() messageValue = %d\n", (uint32)instruction.argv[0]);
|
||||||
|
#if 0
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_createMachine(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_createMachine()\n");
|
||||||
|
#if 0
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_createMachineEx(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_createMachineEx()\n");
|
||||||
|
#if 0
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Machine::m1_clearVars(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_clearVars()\n"); fflush(stdout);
|
||||||
|
|
||||||
|
_sequence->clearVars();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Machine::m1_onEndSequence(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_onEndSequence() count = %08X\n", (uint32)instruction.argv[0] >> 16); fflush(stdout);
|
||||||
|
|
||||||
|
int32 count = instruction.argv[0] >> 16;
|
||||||
|
_sequence->issueEndOfSequenceRequest(_code->pos(), count);
|
||||||
|
_code->jumpRelative(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::m1_onMessage(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_onEndSequence() count = %08X\n", (uint32)instruction.argv[0] >> 16); fflush(stdout);
|
||||||
|
|
||||||
|
// TODO: Add message to list
|
||||||
|
|
||||||
|
int32 count = instruction.argv[0] >> 16;
|
||||||
|
_code->jumpRelative(count);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::m1_switchLt(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_switchLt() %d < %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
|
||||||
|
|
||||||
|
if (instruction.argv[1] >= instruction.argv[2])
|
||||||
|
_code->jumpRelative(instruction.argv[0] >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::m1_switchLe(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_switchLe() %d <= %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
|
||||||
|
|
||||||
|
if (instruction.argv[1] > instruction.argv[2])
|
||||||
|
_code->jumpRelative(instruction.argv[0] >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::m1_switchEq(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_switchEq() %d == %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
|
||||||
|
|
||||||
|
if (instruction.argv[1] != instruction.argv[2])
|
||||||
|
_code->jumpRelative(instruction.argv[0] >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::m1_switchNe(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_switchNe() %d != %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
|
||||||
|
|
||||||
|
if (instruction.argv[1] == instruction.argv[2])
|
||||||
|
_code->jumpRelative(instruction.argv[0] >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::m1_switchGe(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_switchGe() %d >= %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
|
||||||
|
|
||||||
|
if (instruction.argv[1] < instruction.argv[2])
|
||||||
|
_code->jumpRelative(instruction.argv[0] >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Machine::m1_switchGt(Instruction &instruction) {
|
||||||
|
//printf("Machine::m1_switchGt() %d > %d -> %08X\n", (uint32)instruction.argv[1], (uint32)instruction.argv[2], (uint32)instruction.argv[0] >> 16); fflush(stdout);
|
||||||
|
|
||||||
|
if (instruction.argv[1] <= instruction.argv[2])
|
||||||
|
_code->jumpRelative(instruction.argv[0] >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
764
engines/m4/ws_sequence.cpp
Normal file
764
engines/m4/ws_sequence.cpp
Normal file
|
@ -0,0 +1,764 @@
|
||||||
|
/* 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 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.
|
||||||
|
*
|
||||||
|
* $URL$
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "m4/woodscript.h"
|
||||||
|
|
||||||
|
namespace M4 {
|
||||||
|
|
||||||
|
long sinCosTable[320] = {
|
||||||
|
0, 1608, 3215, 4821, 6423, 8022, 9616, 11204,
|
||||||
|
12785, 14359, 15923, 17479, 19024, 20557, 22078, 23586,
|
||||||
|
25079, 26557, 28020, 29465, 30893, 32302, 33692, 35061,
|
||||||
|
36409, 37736, 39039, 40319, 41575, 42806, 44011, 45189,
|
||||||
|
46340, 47464, 48558, 49624, 50660, 51665, 52639, 53581,
|
||||||
|
54491, 55368, 56212, 57022, 57797, 58538, 59243, 59913,
|
||||||
|
60547, 61144, 61705, 62228, 62714, 63162, 63571, 63943,
|
||||||
|
64276, 64571, 64826, 65043, 65220, 65358, 65457, 65516,
|
||||||
|
65536, 65516, 65457, 65358, 65220, 65043, 64826, 64571,
|
||||||
|
64276, 63943, 63571, 63162, 62714, 62228, 61705, 61144,
|
||||||
|
60547, 59913, 59243, 58538, 57797, 57022, 56212, 55368,
|
||||||
|
54491, 53581, 52639, 51665, 50660, 49624, 48558, 47464,
|
||||||
|
46340, 45189, 44011, 42806, 41575, 40319, 39039, 37736,
|
||||||
|
36409, 35061, 33692, 32302, 30893, 29465, 28020, 26557,
|
||||||
|
25079, 23586, 22078, 20557, 19024, 17479, 15923, 14359,
|
||||||
|
12785, 11204, 9616, 8022, 6423, 4821, 3215, 1608,
|
||||||
|
0, -1608, -3215, -4821, -6423, -8022, -9616, -11204,
|
||||||
|
-12785, -14359, -15923, -17479, -19024, -20557, -22078, -23586,
|
||||||
|
-25079, -26557, -28020, -29465, -30893, -32302, -33692, -35061,
|
||||||
|
-36409, -37736, -39039, -40319, -41575, -42806, -44011, -45189,
|
||||||
|
-46340, -47464, -48558, -49624, -50660, -51665, -52639, -53581,
|
||||||
|
-54491, -55368, -56212, -57022, -57797, -58538, -59243, -59913,
|
||||||
|
-60547, -61144, -61705, -62228, -62714, -63162, -63571, -63943,
|
||||||
|
-64276, -64571, -64826, -65043, -65220, -65358, -65457, -65516,
|
||||||
|
-65536, -65516, -65457, -65358, -65220, -65043, -64826, -64571,
|
||||||
|
-64276, -63943, -63571, -63162, -62714, -62228, -61705, -61144,
|
||||||
|
-60547, -59913, -59243, -58538, -57797, -57022, -56212, -55368,
|
||||||
|
-54491, -53581, -52639, -51665, -50660, -49624, -48558, -47464,
|
||||||
|
-46340, -45189, -44011, -42806, -41575, -40319, -39039, -37736,
|
||||||
|
-36409, -35061, -33692, -32302, -30893, -29465, -28020, -26557,
|
||||||
|
-25079, -23586, -22078, -20557, -19024, -17479, -15923, -14359,
|
||||||
|
-12785, -11204, -9616, -8022, -6423, -4821, -3215, -1608,
|
||||||
|
0, 1608, 3215, 4821, 6423, 8022, 9616, 11204,
|
||||||
|
12785, 14359, 15923, 17479, 19024, 20557, 22078, 23586,
|
||||||
|
25079, 26557, 28020, 29465, 30893, 32302, 33692, 35061,
|
||||||
|
36409, 37736, 39039, 40319, 41575, 42806, 44011, 45189,
|
||||||
|
46340, 47464, 48558, 49624, 50660, 51665, 52639, 53581,
|
||||||
|
54491, 55368, 56212, 57022, 57797, 58538, 59243, 59913,
|
||||||
|
60547, 61144, 61705, 62228, 62714, 63162, 63571, 63943,
|
||||||
|
64276, 64571, 64826, 65043, 65220, 65358, 65457, 65516
|
||||||
|
};
|
||||||
|
|
||||||
|
long *sinTable = &(sinCosTable[0]);
|
||||||
|
long *cosTable = &(sinCosTable[64]);
|
||||||
|
|
||||||
|
// FIXME: Tables
|
||||||
|
|
||||||
|
const int sequenceVariableCount = 33;
|
||||||
|
|
||||||
|
enum SequenceVariables {
|
||||||
|
kSeqVarTimer = 0,
|
||||||
|
kSeqVarTag = 1,
|
||||||
|
kSeqVarLayer = 2,
|
||||||
|
kSeqVarWidth = 3,
|
||||||
|
kSeqVarHeight = 4,
|
||||||
|
kSeqVarX = 5,
|
||||||
|
kSeqVarY = 6,
|
||||||
|
kSeqVarScale = 7,
|
||||||
|
kSeqVarR = 8,
|
||||||
|
kSeqVarSpriteHash = 9,
|
||||||
|
kSeqVarSpriteFrameNumber = 10,
|
||||||
|
kSeqVarSpriteFrameCount = 11,
|
||||||
|
kSeqVarSpriteFrameRate = 12,
|
||||||
|
kSeqVarSpriteFramePixelSpeed = 13,
|
||||||
|
kSeqVarTargetS = 14,
|
||||||
|
kSeqVarTargetR = 15,
|
||||||
|
kSeqVarTargetX = 16,
|
||||||
|
kSeqVarTargetY = 17,
|
||||||
|
kSeqVarDeltaS = 18,
|
||||||
|
kSeqVarDeltaR = 19,
|
||||||
|
kSeqVarDeltaX = 20,
|
||||||
|
kSeqVarDeltaY = 21,
|
||||||
|
kSeqVarVelocity = 22,
|
||||||
|
kSeqVarTheta = 23,
|
||||||
|
kSeqVarTemp1 = 24,
|
||||||
|
kSeqVarTemp2 = 25,
|
||||||
|
kSeqVarTemp3 = 26,
|
||||||
|
kSeqVarTemp4 = 27,
|
||||||
|
kSeqVarTemp5 = 28,
|
||||||
|
kSeqVarTemp6 = 29,
|
||||||
|
kSeqVarTemp7 = 30,
|
||||||
|
kSeqVarTemp8 = 31,
|
||||||
|
kSeqVarMachineID = 32
|
||||||
|
};
|
||||||
|
|
||||||
|
bool (Sequence::*sequenceCommandsTable[])(Instruction &instruction) = {
|
||||||
|
&Sequence::s1_end,
|
||||||
|
&Sequence::s1_clearVars,
|
||||||
|
&Sequence::s1_set,
|
||||||
|
&Sequence::s1_compare,
|
||||||
|
&Sequence::s1_add,
|
||||||
|
&Sequence::s1_sub,
|
||||||
|
&Sequence::s1_mul,
|
||||||
|
&Sequence::s1_div,
|
||||||
|
&Sequence::s1_and,
|
||||||
|
&Sequence::s1_or,
|
||||||
|
&Sequence::s1_not,
|
||||||
|
&Sequence::s1_sin,
|
||||||
|
&Sequence::s1_cos,
|
||||||
|
&Sequence::s1_abs,
|
||||||
|
&Sequence::s1_min,
|
||||||
|
&Sequence::s1_max,
|
||||||
|
&Sequence::s1_mod,
|
||||||
|
&Sequence::s1_floor,
|
||||||
|
&Sequence::s1_round,
|
||||||
|
&Sequence::s1_ceil,
|
||||||
|
&Sequence::s1_point,
|
||||||
|
&Sequence::s1_dist2d,
|
||||||
|
&Sequence::s1_crunch,
|
||||||
|
&Sequence::s1_branch,
|
||||||
|
&Sequence::s1_setFrame,
|
||||||
|
&Sequence::s1_sendMessage,
|
||||||
|
&Sequence::s1_push,
|
||||||
|
&Sequence::s1_pop,
|
||||||
|
&Sequence::s1_jumpSub,
|
||||||
|
&Sequence::s1_return,
|
||||||
|
&Sequence::s1_getFrameCount,
|
||||||
|
&Sequence::s1_getFrameRate,
|
||||||
|
&Sequence::s1_getCelsPixSpeed,
|
||||||
|
&Sequence::s1_setIndex,
|
||||||
|
&Sequence::s1_setLayer,
|
||||||
|
&Sequence::s1_setDepth,
|
||||||
|
&Sequence::s1_setData,
|
||||||
|
&Sequence::s1_openStream,
|
||||||
|
&Sequence::s1_streamNextFrame,
|
||||||
|
&Sequence::s1_closeStream
|
||||||
|
};
|
||||||
|
|
||||||
|
Sequence::Sequence(WoodScript *ws, Machine *machine, int32 sequenceHash) {
|
||||||
|
|
||||||
|
_ws = ws;
|
||||||
|
|
||||||
|
SequenceAsset *sequenceAsset = _ws->assets()->getSequence(sequenceHash);
|
||||||
|
|
||||||
|
// initialize the sequence's bytecode
|
||||||
|
byte *code;
|
||||||
|
uint32 codeSize;
|
||||||
|
sequenceAsset->getCode(code, codeSize);
|
||||||
|
_code = new Bytecode(_ws, code, codeSize, this);
|
||||||
|
|
||||||
|
_active = true;
|
||||||
|
_sequenceHash = sequenceHash;
|
||||||
|
_machine = machine;
|
||||||
|
_parentSequence = _machine->parentSequence();
|
||||||
|
_dataRow = _machine->dataRow();
|
||||||
|
_startTime = 0;
|
||||||
|
_switchTime = 0;
|
||||||
|
//TODO _flags = 0;
|
||||||
|
_localVarCount = sequenceAsset->localVarCount();
|
||||||
|
_vars = new long[sequenceVariableCount + _localVarCount];
|
||||||
|
_returnStackIndex = 0;
|
||||||
|
_layer = 0;
|
||||||
|
_terminated = false;
|
||||||
|
|
||||||
|
clearVars();
|
||||||
|
_vars[kSeqVarMachineID] = _machine->getId();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Sequence::~Sequence() {
|
||||||
|
delete _code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequence::pause() {
|
||||||
|
_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequence::resume() {
|
||||||
|
_active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequence::issueEndOfSequenceRequest(int32 codeOffset, int32 count) {
|
||||||
|
|
||||||
|
//printf("Sequence::issueEndOfSequenceRequest(%04X, %04X)\n", codeOffset, count); fflush(stdout);
|
||||||
|
//g_system->delayMillis(5000);
|
||||||
|
|
||||||
|
_endOfSequenceRequest.codeOffset = codeOffset;
|
||||||
|
_endOfSequenceRequest.count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequence::cancelEndOfSequenceRequest() {
|
||||||
|
_endOfSequenceRequest.codeOffset = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::runProgram() {
|
||||||
|
|
||||||
|
bool done = true;
|
||||||
|
|
||||||
|
//printf("_ws->getGlobal(kGlobTime) = %ld, _switchTime = %d\n", _ws->getGlobal(kGlobTime), _switchTime);
|
||||||
|
|
||||||
|
if (_switchTime >= 0 && _ws->getGlobal(kGlobTime) >= _switchTime)
|
||||||
|
done = false;
|
||||||
|
|
||||||
|
_vars[kSeqVarTimer] -= _ws->getGlobal(kGlobTimeDelta) << 16;
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
Instruction instruction;
|
||||||
|
_code->loadInstruction(instruction);
|
||||||
|
if (sequenceCommandsTable[instruction.instr] != NULL)
|
||||||
|
done = !(this->*sequenceCommandsTable[instruction.instr])(instruction);
|
||||||
|
else { fflush(stdout); /*g_system->delayMillis(1000);*/ }
|
||||||
|
}
|
||||||
|
|
||||||
|
return _terminated;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequence::clearVars() {
|
||||||
|
for (int i = 0; i < sequenceVariableCount + _localVarCount; i++)
|
||||||
|
_vars[i] = 0;
|
||||||
|
// set default scaling to 100%
|
||||||
|
_vars[kSeqVarScale] = 0x10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::changeProgram(int32 sequenceHash) {
|
||||||
|
|
||||||
|
SequenceAsset *sequenceAsset = _ws->assets()->getSequence(sequenceHash);
|
||||||
|
|
||||||
|
if (sequenceAsset->localVarCount() > _localVarCount) {
|
||||||
|
//printf("Sequence::changeProgram(%d) sequenceAsset->localVarCount() > _localVarCount\n", sequenceHash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the sequence's bytecode
|
||||||
|
byte *code;
|
||||||
|
uint32 codeSize;
|
||||||
|
sequenceAsset->getCode(code, codeSize);
|
||||||
|
_code->setCode(code, codeSize);
|
||||||
|
|
||||||
|
// Reset status variables
|
||||||
|
_switchTime = 0;
|
||||||
|
_active = true;
|
||||||
|
_terminated = false;
|
||||||
|
_endOfSequenceRequest.codeOffset = -1;
|
||||||
|
|
||||||
|
_sequenceHash = sequenceHash;
|
||||||
|
_returnStackIndex = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
long *Sequence::getVarPtr(int index) {
|
||||||
|
return &_vars[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
long *Sequence::getParentVarPtr(int index) {
|
||||||
|
return _parentSequence->getVarPtr(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
long *Sequence::getDataPtr(int index) {
|
||||||
|
return &_dataRow[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequence::draw(M4Surface *surface, const Common::Rect &clipRect, Common::Rect &updateRect) {
|
||||||
|
|
||||||
|
SpriteInfo info;
|
||||||
|
|
||||||
|
info.sprite = _curFrame;
|
||||||
|
info.hotX = _curFrame->xOffset;
|
||||||
|
info.hotY = _curFrame->yOffset;
|
||||||
|
info.encoding = _curFrame->encoding;
|
||||||
|
info.inverseColorTable = _vm->_scene->getInverseColorTable();
|
||||||
|
info.palette = _ws->getMainPalette();
|
||||||
|
info.width = _curFrame->w;
|
||||||
|
info.height = _curFrame->h;
|
||||||
|
int32 scaler = FixedMul(_vars[kSeqVarScale], 100 << 16) >> 16;
|
||||||
|
info.scaleX = _vars[kSeqVarWidth] < 0 ? -scaler : scaler;
|
||||||
|
info.scaleY = scaler;
|
||||||
|
surface->drawSprite(_vars[kSeqVarX] >> 16, _vars[kSeqVarY] >> 16, info, clipRect);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_end(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_end()\n");
|
||||||
|
|
||||||
|
_terminated = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_clearVars(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_clearVars()\n");
|
||||||
|
|
||||||
|
clearVars();
|
||||||
|
_vars[kSeqVarMachineID] = _machine->getId();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_set(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_set()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] = instruction.getValue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_compare(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_compare()\n");
|
||||||
|
|
||||||
|
long value = instruction.getValue();
|
||||||
|
if (instruction.argv[0] < value)
|
||||||
|
_cmpFlags = -1;
|
||||||
|
else if (instruction.argv[0] > value)
|
||||||
|
_cmpFlags = 1;
|
||||||
|
else
|
||||||
|
_cmpFlags = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_add(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_add()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] += instruction.getValue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_sub(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_sub()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] -= instruction.getValue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_mul(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_mul()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] = FixedMul(instruction.argv[0], instruction.getValue());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_div(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_div()\n");
|
||||||
|
|
||||||
|
// TODO: Catch divisor = 0 in FixedDiv
|
||||||
|
*instruction.argp[0] = FixedDiv(instruction.argv[0], instruction.getValue());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_and(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_and()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] = instruction.argv[0] & instruction.getValue();
|
||||||
|
if (*instruction.argp[0])
|
||||||
|
_cmpFlags = 0;
|
||||||
|
else
|
||||||
|
_cmpFlags = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_or(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_or()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] = instruction.argv[0] | instruction.getValue();
|
||||||
|
if (*instruction.argp[0])
|
||||||
|
_cmpFlags = 0;
|
||||||
|
else
|
||||||
|
_cmpFlags = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_not(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_not()\n");
|
||||||
|
|
||||||
|
if (instruction.argv[0] == 0) {
|
||||||
|
*instruction.argp[0] = 0x10000;
|
||||||
|
_cmpFlags = 1;
|
||||||
|
} else {
|
||||||
|
*instruction.argp[0] = 0;
|
||||||
|
_cmpFlags = 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_sin(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_sin()\n");
|
||||||
|
|
||||||
|
int32 tempAngle = *instruction.argp[1] >> 16;
|
||||||
|
if (tempAngle < 0)
|
||||||
|
tempAngle = 0x0100 - ((-tempAngle) & 0xff);
|
||||||
|
else
|
||||||
|
tempAngle &= 0xff;
|
||||||
|
|
||||||
|
*instruction.argp[0] = -cosTable[tempAngle];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_cos(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_cos()\n");
|
||||||
|
|
||||||
|
int32 tempAngle = *instruction.argp[1] >> 16;
|
||||||
|
if (tempAngle < 0)
|
||||||
|
tempAngle = 0x0100 - ((-tempAngle) & 0xff);
|
||||||
|
else
|
||||||
|
tempAngle &= 0xff;
|
||||||
|
|
||||||
|
*instruction.argp[0] = sinTable[tempAngle];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_abs(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_abs()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] = ABS(instruction.argv[1]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_min(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_min()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] = MIN(instruction.argv[1], instruction.argv[2]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_max(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_max()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] = MAX(instruction.argv[1], instruction.argv[2]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_mod(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_mod()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] = instruction.argv[0] % instruction.getValue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_floor(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_floor()\n");
|
||||||
|
|
||||||
|
*instruction.argp[0] = instruction.getValue() & 0xffff0000;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_round(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_round()\n");
|
||||||
|
|
||||||
|
if ((*instruction.argp[1] & 0xffff) >= 0x8000)
|
||||||
|
*instruction.argp[0] = (*instruction.argp[1] + 0x10000) & 0xffff0000;
|
||||||
|
else
|
||||||
|
*instruction.argp[0] = *instruction.argp[1] & 0xffff0000;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_ceil(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_ceil()\n");
|
||||||
|
|
||||||
|
if ((*instruction.argp[1] & 0xffff) >= 0)
|
||||||
|
*instruction.argp[0] = (*instruction.argp[1] + 0x10000) & 0xffff0000;
|
||||||
|
else
|
||||||
|
*instruction.argp[0] = *instruction.argp[1] & 0xffff0000;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_point(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_point()\n");
|
||||||
|
// TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_dist2d(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_dist2d()\n");
|
||||||
|
// TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_crunch(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_crunch()\n");
|
||||||
|
|
||||||
|
long deltaTime;
|
||||||
|
|
||||||
|
if (instruction.argc == 2) {
|
||||||
|
deltaTime = _vm->imath_ranged_rand16(instruction.argv[0], instruction.argv[1]);
|
||||||
|
} else if (instruction.argc == 1) {
|
||||||
|
deltaTime = instruction.argv[0];
|
||||||
|
} else {
|
||||||
|
deltaTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_startTime = _ws->getGlobal(kGlobTime);
|
||||||
|
|
||||||
|
//printf("deltaTime = %ld\n", deltaTime >> 16); fflush(stdout);
|
||||||
|
//g_system->delayMillis(5000);
|
||||||
|
|
||||||
|
if (deltaTime >= 0) {
|
||||||
|
_switchTime = _ws->getGlobal(kGlobTime) + (deltaTime >> 16);
|
||||||
|
//printf("_ws->getGlobal(kGlobTime) = %ld\n", _ws->getGlobal(kGlobTime)); fflush(stdout);
|
||||||
|
//g_system->delayMillis(5000);
|
||||||
|
} else {
|
||||||
|
_switchTime = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Update if walking etc.
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_branch(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_branch()\n");
|
||||||
|
|
||||||
|
uint32 ofs = instruction.argv[1] >> 16;
|
||||||
|
switch (instruction.argv[0] >> 16) {
|
||||||
|
case 0: // jmp
|
||||||
|
_code->jumpRelative(ofs);
|
||||||
|
break;
|
||||||
|
case 1: // <
|
||||||
|
if (_cmpFlags < 0)
|
||||||
|
_code->jumpRelative(ofs);
|
||||||
|
break;
|
||||||
|
case 2: // <=
|
||||||
|
if (_cmpFlags <= 0)
|
||||||
|
_code->jumpRelative(ofs);
|
||||||
|
break;
|
||||||
|
case 3: // ==
|
||||||
|
if (_cmpFlags == 0)
|
||||||
|
_code->jumpRelative(ofs);
|
||||||
|
break;
|
||||||
|
case 4: // !=
|
||||||
|
if (_cmpFlags != 0)
|
||||||
|
_code->jumpRelative(ofs);
|
||||||
|
break;
|
||||||
|
case 5: // >=
|
||||||
|
if (_cmpFlags >= 0)
|
||||||
|
_code->jumpRelative(ofs);
|
||||||
|
break;
|
||||||
|
case 6: // >
|
||||||
|
if (_cmpFlags > 0)
|
||||||
|
_code->jumpRelative(ofs);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_setFrame(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_setFrame()\n");
|
||||||
|
|
||||||
|
int32 frameIndex;
|
||||||
|
if (instruction.argc == 3) {
|
||||||
|
frameIndex = _vm->imath_ranged_rand(instruction.argv[1] >> 16, instruction.argv[2] >> 16);
|
||||||
|
} else if (instruction.argc == 2) {
|
||||||
|
frameIndex = instruction.argv[1] >> 16;
|
||||||
|
} else {
|
||||||
|
frameIndex = (instruction.argv[0] & 0xFF0000) >> 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("Sequence::s1_setFrame() spriteHash = %d\n", (uint32)instruction.argv[0] >> 24);
|
||||||
|
//printf("Sequence::s1_setFrame() frameIndex = %d\n", frameIndex);
|
||||||
|
|
||||||
|
SpriteAsset *spriteAsset = _ws->assets()->getSprite((uint32)instruction.argv[0] >> 24);
|
||||||
|
_curFrame = spriteAsset->getFrame(frameIndex);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_sendMessage(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_sendMessage()\n");
|
||||||
|
// TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_push(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_push()\n");
|
||||||
|
// TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_pop(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_pop()\n");
|
||||||
|
// TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_jumpSub(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_jumpSub()\n");
|
||||||
|
|
||||||
|
_returnHashes[_returnStackIndex] = _sequenceHash;
|
||||||
|
_returnOffsets[_returnStackIndex] = _code->pos();
|
||||||
|
_returnStackIndex++;
|
||||||
|
|
||||||
|
_sequenceHash = instruction.argv[0] >> 16;
|
||||||
|
|
||||||
|
SequenceAsset *sequenceAsset = _ws->assets()->getSequence(_sequenceHash);
|
||||||
|
|
||||||
|
// initialize the sequence's bytecode
|
||||||
|
byte *code;
|
||||||
|
uint32 codeSize;
|
||||||
|
sequenceAsset->getCode(code, codeSize);
|
||||||
|
_code->setCode(code, codeSize);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_return(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_return()\n");
|
||||||
|
|
||||||
|
if (_returnStackIndex <= 0)
|
||||||
|
return s1_end(instruction);
|
||||||
|
|
||||||
|
_returnStackIndex--;
|
||||||
|
|
||||||
|
_sequenceHash = _returnHashes[_returnStackIndex];
|
||||||
|
uint32 ofs = _returnOffsets[_returnStackIndex];
|
||||||
|
|
||||||
|
SequenceAsset *sequenceAsset = _ws->assets()->getSequence(_sequenceHash);
|
||||||
|
|
||||||
|
// initialize the sequence's bytecode
|
||||||
|
byte *code;
|
||||||
|
uint32 codeSize;
|
||||||
|
sequenceAsset->getCode(code, codeSize);
|
||||||
|
_code->setCode(code, codeSize);
|
||||||
|
_code->jumpAbsolute(ofs);
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_getFrameCount(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_getFrameCount()\n");
|
||||||
|
|
||||||
|
SpriteAsset *spriteAsset = _ws->assets()->getSprite(instruction.argv[1] >> 24);
|
||||||
|
*instruction.argp[0] = spriteAsset->getCount() << 16;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_getFrameRate(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_getFrameRate()\n");
|
||||||
|
|
||||||
|
SpriteAsset *spriteAsset = _ws->assets()->getSprite(instruction.argv[1] >> 24);
|
||||||
|
*instruction.argp[0] = spriteAsset->getFrameRate();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_getCelsPixSpeed(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_getCelsPixSpeed()\n");
|
||||||
|
// TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_setIndex(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_setIndex()\n");
|
||||||
|
// TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_setLayer(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_setLayer()\n");
|
||||||
|
//TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_setDepth(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_setDepth()\n");
|
||||||
|
//TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_setData(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_setData()\n");
|
||||||
|
//TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_openStream(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_openStream()\n");
|
||||||
|
|
||||||
|
_stream = _vm->res()->openFile(_machine->name().c_str());
|
||||||
|
streamOpen();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_streamNextFrame(Instruction &instruction) {
|
||||||
|
//printf("Sequence::s1_streamNextFrame()\n");
|
||||||
|
|
||||||
|
streamNextFrame();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::s1_closeStream(Instruction &instruction) {
|
||||||
|
printf("Sequence::s1_closeStream()\n");
|
||||||
|
//TODO
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::streamOpen() {
|
||||||
|
|
||||||
|
_streamSpriteAsset = new SpriteAsset(_vm, _stream, _stream->size(), "stream", true);
|
||||||
|
|
||||||
|
_vars[kSeqVarSpriteFrameNumber] = -0x10000;
|
||||||
|
_vars[kSeqVarSpriteFrameCount] = _streamSpriteAsset->getCount() << 16;
|
||||||
|
_vars[kSeqVarSpriteFrameRate] = _streamSpriteAsset->getFrameRate() << 16;
|
||||||
|
|
||||||
|
//printf("Sequence::streamOpen() frames = %d; max = %d x %d\n", _streamSpriteAsset->getCount(), _streamSpriteAsset->getMaxFrameWidth(), _streamSpriteAsset->getMaxFrameHeight());
|
||||||
|
//fflush(stdout);
|
||||||
|
|
||||||
|
_curFrame = new M4Sprite(_vm, _streamSpriteAsset->getMaxFrameWidth(), _streamSpriteAsset->getMaxFrameHeight());
|
||||||
|
streamNextFrame();
|
||||||
|
|
||||||
|
// TODO: Just a hack to see the series with the correct palette.
|
||||||
|
_vm->_palette->setPalette(_streamSpriteAsset->getPalette(), 0, 256);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sequence::streamNextFrame() {
|
||||||
|
|
||||||
|
_vars[kSeqVarSpriteFrameNumber] += 0x10000;
|
||||||
|
|
||||||
|
int32 frameNum = _vars[kSeqVarSpriteFrameNumber] >> 16;
|
||||||
|
if (frameNum >= _streamSpriteAsset->getCount()) {
|
||||||
|
// End reached
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_streamSpriteAsset->loadStreamingFrame(_curFrame, frameNum, _vars[kSeqVarX], _vars[kSeqVarY]);
|
||||||
|
|
||||||
|
_vars[kSeqVarWidth] = _curFrame->w << 16;
|
||||||
|
_vars[kSeqVarHeight] = _curFrame->h << 16;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sequence::streamClose() {
|
||||||
|
_stream = NULL;
|
||||||
|
_vm->res()->toss(_machine->name().c_str());
|
||||||
|
//_vm->res()->purge();
|
||||||
|
delete _streamSpriteAsset;
|
||||||
|
delete _curFrame;
|
||||||
|
_stream = NULL;
|
||||||
|
_streamSpriteAsset = NULL;
|
||||||
|
_curFrame = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue