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