scummvm/engines/supernova/supernova.cpp

494 lines
12 KiB
C++
Raw Normal View History

/* 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.
*
*/
#include "common/config-manager.h"
#include "common/debug.h"
#include "common/debug-channels.h"
#include "common/error.h"
#include "common/events.h"
#include "common/file.h"
#include "common/fs.h"
#include "common/str.h"
#include "common/system.h"
#include "engines/util.h"
#include "graphics/cursorman.h"
#include "graphics/surface.h"
#include "graphics/screen.h"
#include "graphics/palette.h"
#include "supernova/supernova.h"
//#include "supernova/rooms.h"
namespace Supernova {
const char *const Object::defaultDescription = "Es ist nichts Besonderes daran.";
ObjectType operator|(ObjectType a, ObjectType b) {
return static_cast<ObjectType>(+a | +b);
}
ObjectType operator&(ObjectType a, ObjectType b) {
return static_cast<ObjectType>(+a & +b);
}
ObjectType operator^(ObjectType a, ObjectType b) {
return static_cast<ObjectType>(+a ^ +b);
}
ObjectType &operator|=(ObjectType &a, ObjectType b) {
return a = a | b;
}
ObjectType &operator&=(ObjectType &a, ObjectType b) {
return a = a & b;
}
ObjectType &operator^=(ObjectType &a, ObjectType b) {
return a = a ^ b;
}
SupernovaEngine::SupernovaEngine(OSystem *syst)
: Engine(syst)
, _console(NULL)
2017-06-10 00:17:19 +02:00
, _brightness(255)
, _menuBrightness(255)
, _imageIndex(10)
, _sectionIndex(0)
, _delay(33)
, _gameRunning(true)
{
// const Common::FSNode gameDataDir(ConfMan.get("path"));
// SearchMan.addSubDirectoryMatching(gameDataDir, "sound");
// setup engine specific debug channels
DebugMan.addDebugChannel(kDebugGeneral, "general", "Supernova general debug channel");
_rnd = new Common::RandomSource("supernova");
}
SupernovaEngine::~SupernovaEngine() {
DebugMan.clearAllDebugChannels();
delete _rnd;
delete _console;
}
Common::Error SupernovaEngine::run() {
initGraphics(kScreenWidth, kScreenHeight);
debug(_system->getScreenFormat().toString().c_str());
_console = new Console(this);
initData();
2017-06-10 00:17:19 +02:00
initPalette();
paletteFadeIn();
CursorMan.showMouse(true);
while (_gameRunning) {
updateEvents();
renderImage(_imageIndex, _sectionIndex);
renderText(Common::String::format("%u | %u", _imageIndex, _sectionIndex).c_str(), 0, 190, kColorLightRed);
_system->updateScreen();
_system->delayMillis(_delay);
}
//deinit timer/sound/..
stopSound();
return Common::kNoError;
}
2017-06-18 17:44:33 +02:00
// Emulates DOS int 1A/00
uint SupernovaEngine::getDOSTicks() {
TimeDate systemTime;
_system->getTimeAndDate(systemTime);
return static_cast<uint>((systemTime.tm_hour * 24 +
systemTime.tm_min * 60 +
systemTime.tm_sec) * 18.2065);
}
void SupernovaEngine::updateEvents() {
Common::Event event;
while (g_system->getEventManager()->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_QUIT:
case Common::EVENT_RTL:
_gameRunning = false;
break;
case Common::EVENT_KEYDOWN:
if (event.kbd.keycode == Common::KEYCODE_d && event.kbd.hasFlags(Common::KBD_CTRL)) {
2017-06-10 00:17:19 +02:00
paletteFadeOut();
}
if (event.kbd.keycode == Common::KEYCODE_d && !event.kbd.hasFlags(Common::KBD_CTRL)) {
paletteFadeIn();
}
if (event.kbd.keycode == Common::KEYCODE_q) {
playSound(48, 13530);
}
if (event.kbd.keycode == Common::KEYCODE_w) {
_sectionIndex = 0;
++_imageIndex;
if (_imageIndex == 31) {
renderText("Das Schicksal", 44, 132, kColorWhite99);
renderText("des Horst Hummel", 35, 142, kColorWhite99);
renderText("Teil 1:", 64, 120, kColorLightBlue);
2017-06-10 00:17:19 +02:00
}
}
if (event.kbd.keycode == Common::KEYCODE_e) {
renderImage(_imageIndex, 0);
renderImage(_imageIndex, ++_sectionIndex);
}
break;
default:
break;
}
}
}
void SupernovaEngine::initData() {
}
void SupernovaEngine::initPalette() {
2017-06-10 00:17:19 +02:00
_system->getPaletteManager()->setPalette(initVGAPalette, 0, 256);
}
void SupernovaEngine::playSound(int filenumber, int offset) {
Common::File *file = new Common::File;
if (!file->open(Common::String::format("msn_data.0%2d", filenumber))) {
error("File %s could not be read!", file->getName());
}
file->seek(offset);
Audio::SeekableAudioStream *audioStream = Audio::makeRawStream(file, 11931, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
stopSound();
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, audioStream);
}
void SupernovaEngine::stopSound() {
if (_mixer->isSoundHandleActive(_soundHandle))
_mixer->stopHandle(_soundHandle);
}
void playSoundMod(int filenumber)
{
if (filenumber != 49 || filenumber != 52) {
error("File not supposed to be played!");
}
Common::File *file = new Common::File;
if (!file->open(Common::String::format("msn_data.0%2d", filenumber))) {
error("File %s could not be read!", file->getName());
}
// play Supernova MOD file
}
void SupernovaEngine::renderImage(int filenumber, int section, bool fullscreen) {
Common::File file;
if (!file.open(Common::String::format("msn_data.0%2d", filenumber))) {
error("File %s could not be read!", file.getName());
}
_image.loadStream(file);
_image.loadSection(section);
_system->getPaletteManager()->setPalette(_image.getPalette(), 16, 239);
2017-06-10 00:17:19 +02:00
paletteBrightness();
if (fullscreen) {
_system->copyRectToScreen(_image.getSurface()->getPixels(), 320, 0, 0, 320, 200);
} else {
size_t offset = _image._section[section].y1 * 320 + _image._section[section].x1;
_system->copyRectToScreen(static_cast<const byte *>(_image.getSurface()->getPixels()) + offset,
320,
_image._section[section].x1,
_image._section[section].y1,
_image._section[section].x2 - _image._section[section].x1,
_image._section[section].y2 - _image._section[section].y1);
}
}
static int characterWidth(const char *text) {
int charWidth = 0;
while (*text != '\0') {
byte c = *text++;
if (c < 32) {
continue;
} else if (c == 225) {
c = 35;
}
for (size_t i = 0; i < 5; ++i) {
++charWidth;
if (font[c - 32][i] == 0xff) {
break;
}
}
}
return charWidth;
}
2017-06-07 18:34:11 +02:00
void SupernovaEngine::renderMessage(char *text, MessagePosition position) {
char *row[20];
char *p = text;
size_t numRows = 0;
int rowWidthMax = 0;
int x = 0;
int y = 0;
byte textColor = 0;
while (*p != '\0') {
row[numRows] = p;
++numRows;
while ((*p != '\0') && (*p != '|')) {
++p;
}
if (*p == '|') {
*p = '\0';
++p;
}
}
for (size_t i = 0; i < numRows; ++i) {
int rowWidth = characterWidth(row[i]);
if (rowWidth > rowWidthMax)
rowWidthMax = rowWidth;
}
switch (position) {
case kMessageNormal:
x = rowWidthMax / 2 - 160;
textColor = COL_MELD;
break;
case kMessageTop:
x = rowWidthMax / 2 - 160;
textColor = kColorLightYellow;
break;
case kMessageCenter:
x = rowWidthMax / 2 - 160;
textColor = kColorLightRed;
break;
case kMessageLeft:
x = 3;
textColor = kColorLightYellow;
break;
case kMessageRight:
x = 317 - rowWidthMax;
textColor = kColorLightGreen;
break;
}
if (position == kMessageNormal) {
y = 70 - ((numRows * 9) / 2);
} else if (position == kMessageTop) {
y = 5;
} else {
y = 142;
}
int message_columns = x - 3;
int message_rows = y - 3;
int message_width = rowWidthMax + 6;
int message_height = numRows * 9 + 5;
renderBox(message_columns, message_rows, message_width, message_height, HGR_MELD);
for (size_t i = 0; i < numRows; ++i) {
renderText(row[i], x, y, textColor);
y += 9;
}
// timer1 = (Common::strnlen(text, BUFSIZ) + 20) * textspeed / 10;
}
void SupernovaEngine::renderText(const char *text, int x, int y, byte color) {
Graphics::Surface *screen = _system->lockScreen();
byte *cursor = static_cast<byte *>(screen->getBasePtr(x, y));
byte c;
while ((c = *text++) != '\0') {
if (c < 32) {
continue;
} else if (c == 225) {
c = 128;
}
for (size_t i = 0; i < 5; ++i) {
if (font[c - 32][i] == 0xff) {
++cursor;
break;
}
byte *ascentLine = cursor;
for (byte j = font[c - 32][i]; j != 0; j >>= 1) {
if (j & 1) {
*cursor = color;
}
cursor += kScreenWidth;
}
cursor = ++ascentLine;
}
}
_system->unlockScreen();
}
2017-06-07 18:34:11 +02:00
void SupernovaEngine::renderBox(int x, int y, int width, int height, byte color) {
Graphics::Surface *screen = _system->lockScreen();
screen->fillRect(Common::Rect(x, y, width, height), color);
2017-06-07 18:34:11 +02:00
_system->unlockScreen();
}
2017-06-10 00:17:19 +02:00
void SupernovaEngine::paletteBrightness() {
byte palette[768];
_system->getPaletteManager()->grabPalette(palette, 0, 255);
2017-06-10 00:17:19 +02:00
for (size_t i = 0; i < 48; ++i) {
palette[i] = (initVGAPalette[i] * _menuBrightness) >> 8;
}
for (size_t i = 0; i < 717; ++i) {
2017-06-10 00:17:19 +02:00
const byte *imagePalette;
if (_image.getPalette()) {
imagePalette = _image.getPalette();
} else {
imagePalette = palette;
}
palette[i + 48] = (imagePalette[i] * _brightness) >> 8;
2017-06-10 00:17:19 +02:00
}
_system->getPaletteManager()->setPalette(palette, 0, 255);
}
2017-06-10 00:17:19 +02:00
void SupernovaEngine::paletteFadeOut() {
// TODO: scene 0 (newspaper article in intro, mode 0x11)
// needs to be handled differently
2017-06-10 00:17:19 +02:00
while (_brightness > 20) {
_menuBrightness = _brightness;
paletteBrightness();
_brightness -= 20;
_system->updateScreen();
_system->delayMillis(_delay);
2017-06-10 00:17:19 +02:00
}
_menuBrightness = 0;
_brightness = 0;
paletteBrightness();
_system->updateScreen();
}
void SupernovaEngine::paletteFadeIn() {
// TODO: scene 0 (newspaper article in intro, mode 0x11)
// needs to be handled differently
2017-06-10 00:17:19 +02:00
while (_brightness < 235) {
_menuBrightness = _brightness;
paletteBrightness();
_brightness += 20;
_system->updateScreen();
_system->delayMillis(_delay);
2017-06-10 00:17:19 +02:00
}
_menuBrightness = 255;
_brightness = 255;
paletteBrightness();
_system->updateScreen();
}
2017-06-15 22:57:59 +02:00
Inventory::Inventory()
: _numObjects(0)
{}
// TODO: Update Inventory surface for scrolling
void Inventory::add(Object &obj) {
if (_numObjects < kMaxCarry)
_inventory[_numObjects] = &obj;
// if (inventory_amount>8) inventory_scroll = ((inventory_amount+1)/2)*2-8;
// show_inventory();
2017-06-15 22:57:59 +02:00
}
// TODO: Update Inventory surface for scrolling
void Inventory::remove(Object &obj) {
for (size_t i = 0; i < _numObjects; ++i) {
if (_inventory[i] == &obj) {
--_numObjects;
while (i < _numObjects) {
_inventory[i] = _inventory[i + 1];
++i;
}
obj.disableProperty(CARRIED);
}
}
}
Object *Inventory::get(size_t index) const {
2017-06-15 22:57:59 +02:00
if (index < _numObjects)
return _inventory[index];
return NULL;
}
Object *Inventory::get(ObjectID id) const {
for (size_t i = 0; i < _numObjects; ++i) {
if (_inventory[i]->_id == id)
return _inventory[i];
}
return NULL;
}
ScreenBufferStack::ScreenBufferStack()
: _last(_buffer) {
}
void ScreenBufferStack::push(int x, int y, int width, int height, int pitch) {
if (_last == ARRAYEND(_buffer))
return;
byte *pixels = new byte[width * height];
const byte *screen = static_cast<byte *>(g_system->lockScreen()->getBasePtr(x, y));
for (int i = 0; i < height; ++i) {
Common::copy(screen, screen + width, pixels);
screen += pitch * i;
}
g_system->unlockScreen();
_last->_x = x;
_last->_y = y;
_last->_width = width;
_last->_height = height;
_last->_pitch = pitch;
_last->_pixels = pixels;
++_last;
}
void ScreenBufferStack::restore() {
if (_last == _buffer)
return;
g_system->lockScreen()->copyRectToSurface(
_last->_pixels, _last->_width, _last->_x, _last->_y,
_last->_width, _last->_height);
g_system->unlockScreen();
--_last;
}
2017-06-10 00:17:19 +02:00
}