diff options
Diffstat (limited to 'engines/gob/pregob')
24 files changed, 6012 insertions, 0 deletions
diff --git a/engines/gob/pregob/gctfile.cpp b/engines/gob/pregob/gctfile.cpp new file mode 100644 index 0000000000..08c32cda76 --- /dev/null +++ b/engines/gob/pregob/gctfile.cpp @@ -0,0 +1,306 @@ +/* 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/random.h" +#include "common/stream.h" + +#include "gob/surface.h" +#include "gob/video.h" + +#include "gob/pregob/gctfile.h" + +namespace Gob { + +GCTFile::Chunk::Chunk() : type(kChunkTypeNone) { +} + + +GCTFile::GCTFile(Common::SeekableReadStream &gct, Common::RandomSource &rnd) : _rnd(&rnd), + _areaLeft(0), _areaTop(0), _areaRight(0), _areaBottom(0), _currentItem(0xFFFF) { + + load(gct); +} + +GCTFile::~GCTFile() { +} + +void GCTFile::load(Common::SeekableReadStream &gct) { + gct.skip(4); // Required buffer size + gct.skip(2); // Unknown + + // Read the selector and line counts for each item + const uint16 itemCount = gct.readUint16LE(); + _items.resize(itemCount); + + for (Items::iterator i = _items.begin(); i != _items.end(); ++i) { + const uint16 selector = gct.readUint16LE(); + const uint16 lineCount = gct.readUint16LE(); + + i->selector = selector; + i->lines.resize(lineCount); + } + + // Read all item lines + for (Items::iterator i = _items.begin(); i != _items.end(); ++i) { + for (Lines::iterator l = i->lines.begin(); l != i->lines.end(); ++l) { + const uint16 lineSize = gct.readUint16LE(); + + readLine(gct, *l, lineSize); + } + } + + if (gct.err()) + error("GCTFile::load(): Failed reading GCT"); +} + +void GCTFile::readLine(Common::SeekableReadStream &gct, Line &line, uint16 lineSize) const { + line.chunks.push_back(Chunk()); + + while (lineSize > 0) { + byte c = gct.readByte(); + lineSize--; + + if (c == 0) { + // Command byte + + if (lineSize == 0) + break; + + byte cmd = gct.readByte(); + lineSize--; + + // Line end command + if (cmd == 0) + break; + + // Item reference command + if (cmd == 1) { + if (lineSize < 2) { + warning("GCTFile::readLine(): Item reference command is missing parameters"); + break; + } + + const uint32 itemRef = gct.readUint16LE(); + lineSize -= 2; + + line.chunks.push_back(Chunk()); + line.chunks.back().type = kChunkTypeItem; + line.chunks.back().item = itemRef; + + line.chunks.push_back(Chunk()); + continue; + } + + warning("GCTFile::readLine(): Invalid command 0x%02X", cmd); + break; + } + + // Text + line.chunks.back().type = kChunkTypeString; + line.chunks.back().text += (char)c; + } + + // Skip bytes we didn't read (because of errors) + gct.skip(lineSize); + + // Remove empty chunks from the end of the list + while (!line.chunks.empty() && (line.chunks.back().type == kChunkTypeNone)) + line.chunks.pop_back(); +} + +uint16 GCTFile::getLineCount(uint item) const { + if (item >= _items.size()) + return 0; + + return _items[item].lines.size(); +} + +void GCTFile::selectLine(uint item, uint16 line) { + if ((item >= _items.size()) && (item != kSelectorAll) && (item != kSelectorRandom)) + return; + + _items[item].selector = line; +} + +void GCTFile::setText(uint item, uint16 line, const Common::String &text) { + if ((item >= _items.size()) || (line >= _items[item].lines.size())) + return; + + _items[item].lines[line].chunks.clear(); + _items[item].lines[line].chunks.push_back(Chunk()); + + _items[item].lines[line].chunks.back().type = kChunkTypeString; + _items[item].lines[line].chunks.back().text = text; +} + +void GCTFile::setText(uint item, const Common::String &text) { + if (item >= _items.size()) + return; + + _items[item].selector = 0; + + _items[item].lines.resize(1); + + setText(item, 0, text); +} + +void GCTFile::reset() { + _currentItem = 0xFFFF; + _currentText.clear(); +} + +Common::String GCTFile::getLineText(const Line &line) const { + Common::String text; + + // Go over all chunks in this line + for (Chunks::const_iterator c = line.chunks.begin(); c != line.chunks.end(); ++c) { + // A chunk is either a direct string, or a reference to another item + + if (c->type == kChunkTypeItem) { + Common::List<Common::String> lines; + + getItemText(c->item, lines); + if (lines.empty()) + continue; + + if (lines.size() > 1) + warning("GCTFile::getLineText(): Referenced item has multiple lines"); + + text += lines.front(); + } else if (c->type == kChunkTypeString) + text += c->text; + } + + return text; +} + +void GCTFile::getItemText(uint item, Common::List<Common::String> &text) const { + text.clear(); + + if ((item >= _items.size()) || _items[item].lines.empty()) + return; + + uint16 line = _items[item].selector; + + // Draw all lines + if (line == kSelectorAll) { + for (Lines::const_iterator l = _items[item].lines.begin(); l != _items[item].lines.end(); ++l) + text.push_back(getLineText(*l)); + + return; + } + + // Draw random line + if (line == kSelectorRandom) + line = _rnd->getRandomNumber(_items[item].lines.size() - 1); + + if (line >= _items[item].lines.size()) + return; + + text.push_back(getLineText(_items[item].lines[line])); +} + +void GCTFile::setArea(int16 left, int16 top, int16 right, int16 bottom) { + trashBuffer(); + + _hasArea = false; + + const int16 width = right - left + 1; + const int16 height = bottom - top + 1; + if ((width <= 0) || (height <= 0)) + return; + + _areaLeft = left; + _areaTop = top; + _areaRight = right; + _areaBottom = bottom; + + _hasArea = true; + + resizeBuffer(width, height); +} + +bool GCTFile::clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + return restoreScreen(dest, left, top, right, bottom); +} + +bool GCTFile::fill(Surface &dest, uint8 color, int16 &left, int16 &top, int16 &right, int16 &bottom) { + left = _areaLeft; + top = _areaTop; + right = _areaRight; + bottom = _areaBottom; + + if (!hasSavedBackground()) + saveScreen(dest, left, top, right, bottom); + + dest.fillRect(left, top, right, bottom, color); + + return true; +} + +bool GCTFile::finished() const { + return (_currentItem != 0xFFFF) && _currentText.empty(); +} + +bool GCTFile::draw(Surface &dest, uint16 item, const Font &font, uint8 color, + int16 &left, int16 &top, int16 &right, int16 &bottom) { + + if ((item >= _items.size()) || !_hasArea) + return false; + + left = _areaLeft; + top = _areaTop; + right = _areaRight; + bottom = _areaBottom; + + const int16 width = right - left + 1; + const int16 height = bottom - top + 1; + + const uint lineCount = height / font.getCharHeight(); + if (lineCount == 0) + return false; + + if (!hasSavedBackground()) + saveScreen(dest, left, top, right, bottom); + + if (item != _currentItem) { + _currentItem = item; + + getItemText(_currentItem, _currentText); + } + + if (_currentText.empty()) + return false; + + int16 y = top; + for (uint i = 0; (i < lineCount) && !_currentText.empty(); i++, y += font.getCharHeight()) { + const Common::String &line = _currentText.front(); + const int16 x = left + ((width - (line.size() * font.getCharWidth())) / 2); + + font.drawString(line, x, y, color, 0, true, dest); + _currentText.pop_front(); + } + + return true; +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/gctfile.h b/engines/gob/pregob/gctfile.h new file mode 100644 index 0000000000..ed6351b7a8 --- /dev/null +++ b/engines/gob/pregob/gctfile.h @@ -0,0 +1,149 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_GCTFILE_H +#define GOB_PREGOB_GCTFILE_H + +#include "common/str.h" +#include "common/array.h" +#include "common/list.h" + +#include "gob/backbuffer.h" + +namespace Common { + class RandomSource; + class SeekableReadStream; +} + +namespace Gob { + +class Surface; +class Font; + +class GCTFile : public BackBuffer { +public: + static const uint16 kSelectorAll = 0xFFFE; ///< Print all lines. + static const uint16 kSelectorRandom = 0xFFFF; ///< Print a random line. + + + GCTFile(Common::SeekableReadStream &gct, Common::RandomSource &rnd); + ~GCTFile(); + + /** Return the number of lines in an item. */ + uint16 getLineCount(uint item) const; + + /** Set the area the text will be printed in. */ + void setArea(int16 left, int16 top, int16 right, int16 bottom); + + /** Set which line of this item should be printed. */ + void selectLine(uint item, uint16 line); + + /** Change the text of an items' line. */ + void setText(uint item, uint16 line, const Common::String &text); + /** Change the item into one one line and set that line's text. */ + void setText(uint item, const Common::String &text); + + /** Reset the item drawing state. */ + void reset(); + + /** Clear the drawn text, restoring the original content. */ + bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Fill the text area with a color. */ + bool fill(Surface &dest, uint8 color, int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Draw an item onto the surface, until all text has been drawn or the area is filled. */ + bool draw(Surface &dest, uint16 item, const Font &font, uint8 color, + int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Did we draw all text? */ + bool finished() const; + +private: + /** The type of a chunk. */ + enum ChunkType { + kChunkTypeNone = 0, ///< Do nothing. + kChunkTypeString , ///< A direct string. + kChunkTypeItem ///< A reference to an item to print instead. + }; + + /** A chunk in an item text line. */ + struct Chunk { + ChunkType type; ///< The type of the chunk. + + Common::String text; ///< Text to print. + + int item; ///< Item to print instead. + + Chunk(); + }; + + typedef Common::List<Chunk> Chunks; + + /** A line in an item. */ + struct Line { + Chunks chunks; ///< The chunks that make up the line. + }; + + typedef Common::Array<Line> Lines; + + /** A GCT item. */ + struct Item { + Lines lines; ///< The text lines in the item + uint16 selector; ///< Which line to print. + }; + + typedef Common::Array<Item> Items; + + + Common::RandomSource *_rnd; + + Items _items; ///< All GCT items. + + // The area on which to print + bool _hasArea; + int16 _areaLeft; + int16 _areaTop; + int16 _areaRight; + int16 _areaBottom; + + /** Index of the current item we're drawing. */ + uint16 _currentItem; + /** Text left to draw. */ + Common::List<Common::String> _currentText; + + + // -- Loading helpers -- + + void load(Common::SeekableReadStream &gct); + void readLine(Common::SeekableReadStream &gct, Line &line, uint16 lineSize) const; + + + // -- Draw helpers -- + + Common::String getLineText(const Line &line) const; + void getItemText(uint item, Common::List<Common::String> &text) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_GCTFILE_H diff --git a/engines/gob/pregob/onceupon/abracadabra.cpp b/engines/gob/pregob/onceupon/abracadabra.cpp new file mode 100644 index 0000000000..2cf6855ef8 --- /dev/null +++ b/engines/gob/pregob/onceupon/abracadabra.cpp @@ -0,0 +1,137 @@ +/* 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/textconsole.h" + +#include "gob/gob.h" + +#include "gob/pregob/onceupon/abracadabra.h" + +static const uint8 kCopyProtectionColors[7] = { + 14, 11, 13, 1, 7, 12, 2 +}; + +static const uint8 kCopyProtectionShapes[7 * 20] = { + 3, 4, 3, 0, 1, 2, 0, 2, 2, 0, 2, 4, 0, 3, 4, 1, 1, 4, 1, 3, + 0, 2, 0, 4, 2, 4, 4, 2, 3, 0, 1, 1, 1, 1, 3, 0, 4, 2, 3, 4, + 0, 0, 1, 2, 1, 1, 2, 4, 3, 1, 4, 2, 4, 4, 2, 4, 1, 2, 3, 3, + 1, 0, 2, 3, 4, 2, 3, 2, 2, 0, 0, 0, 4, 2, 3, 4, 4, 0, 4, 1, + 4, 2, 1, 1, 1, 1, 4, 3, 4, 2, 3, 0, 0, 3, 0, 2, 3, 0, 2, 4, + 4, 2, 4, 3, 0, 4, 0, 2, 3, 1, 4, 1, 3, 1, 0, 0, 2, 1, 3, 2, + 3, 1, 0, 3, 1, 3, 4, 2, 4, 4, 3, 2, 0, 2, 0, 1, 2, 0, 1, 4 +}; + +static const uint8 kCopyProtectionObfuscate[4] = { + 1, 0, 2, 3 +}; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton Abracadabra::kAnimalsButtons = { + true, 131, 127, 183, 164, 193, 0, 243, 35, 132, 128, 0 +}; + +const OnceUpon::MenuButton Abracadabra::kAnimalButtons[] = { + {false, 37, 89, 95, 127, 37, 89, 95, 127, 131, 25, 0}, + {false, 114, 65, 172, 111, 114, 65, 172, 111, 131, 25, 1}, + {false, 186, 72, 227, 96, 186, 72, 227, 96, 139, 25, 2}, + {false, 249, 87, 282, 112, 249, 87, 282, 112, 143, 25, 3}, + {false, 180, 102, 234, 138, 180, 102, 234, 138, 133, 25, 4}, + {false, 197, 145, 242, 173, 197, 145, 242, 173, 137, 25, 5}, + {false, 113, 151, 171, 176, 113, 151, 171, 176, 131, 25, 6}, + {false, 114, 122, 151, 150, 114, 122, 151, 150, 141, 25, 7}, + {false, 36, 136, 94, 176, 36, 136, 94, 176, 131, 25, 8}, + {false, 243, 123, 295, 155, 243, 123, 295, 155, 136, 25, 9} +}; + +const char *Abracadabra::kAnimalNames[] = { + "loup", + "drag", + "arai", + "crap", + "crab", + "mous", + "saut", + "guep", + "rhin", + "scor" +}; + +// The houses where the stork can drop a bundle +const OnceUpon::MenuButton Abracadabra::kStorkHouses[] = { + {false, 16, 80, 87, 125, 0, 0, 0, 0, 0, 0, 0}, // Castle , Lord & Lady + {false, 61, 123, 96, 149, 0, 0, 0, 0, 0, 0, 1}, // Cottage, Farmers + {false, 199, 118, 226, 137, 0, 0, 0, 0, 0, 0, 2}, // Hut , Woodcutters + {false, 229, 91, 304, 188, 0, 0, 0, 0, 0, 0, 3} // Palace , King & Queen +}; + +// The stork bundle drop parameters +const Stork::BundleDrop Abracadabra::kStorkBundleDrops[] = { + { 14, 65, 127, true }, + { 14, 76, 152, true }, + { 14, 204, 137, true }, + { 11, 275, 179, false } +}; + +// Parameters for the stork section. +const OnceUpon::StorkParam Abracadabra::kStorkParam = { + "present.cmp", ARRAYSIZE(kStorkHouses), kStorkHouses, kStorkBundleDrops +}; + + +Abracadabra::Abracadabra(GobEngine *vm) : OnceUpon(vm) { +} + +Abracadabra::~Abracadabra() { +} + +void Abracadabra::run() { + init(); + + // Copy protection + bool correctCP = doCopyProtection(kCopyProtectionColors, kCopyProtectionShapes, kCopyProtectionObfuscate); + if (_vm->shouldQuit() || !correctCP) + return; + + // Show the intro + showIntro(); + if (_vm->shouldQuit()) + return; + + // Handle the start menu + doStartMenu(&kAnimalsButtons, ARRAYSIZE(kAnimalButtons), kAnimalButtons, kAnimalNames); + if (_vm->shouldQuit()) + return; + + // Play the actual game + playGame(); +} + +const OnceUpon::StorkParam &Abracadabra::getStorkParameters() const { + return kStorkParam; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/abracadabra.h b/engines/gob/pregob/onceupon/abracadabra.h new file mode 100644 index 0000000000..8048213f5f --- /dev/null +++ b/engines/gob/pregob/onceupon/abracadabra.h @@ -0,0 +1,61 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_ABRACADABRA_H +#define GOB_PREGOB_ONCEUPON_ABRACADABRA_H + +#include "gob/pregob/onceupon/onceupon.h" + +namespace Gob { + +namespace OnceUpon { + +class Abracadabra : public OnceUpon { +public: + Abracadabra(GobEngine *vm); + ~Abracadabra(); + + void run(); + +protected: + const StorkParam &getStorkParameters() const; + +private: + /** Definition of the menu button that leads to the animal names screen. */ + static const MenuButton kAnimalsButtons; + + /** Definition of the buttons that make up the animals in the animal names screen. */ + static const MenuButton kAnimalButtons[]; + /** File prefixes for the name of each animal. */ + static const char *kAnimalNames[]; + + // Parameters for the stork section. + static const MenuButton kStorkHouses[]; + static const Stork::BundleDrop kStorkBundleDrops[]; + static const struct StorkParam kStorkParam; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_ABRACADABRA_H diff --git a/engines/gob/pregob/onceupon/babayaga.cpp b/engines/gob/pregob/onceupon/babayaga.cpp new file mode 100644 index 0000000000..ef56b9dd0b --- /dev/null +++ b/engines/gob/pregob/onceupon/babayaga.cpp @@ -0,0 +1,137 @@ +/* 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/textconsole.h" + +#include "gob/gob.h" + +#include "gob/pregob/onceupon/babayaga.h" + +static const uint8 kCopyProtectionColors[7] = { + 14, 11, 13, 1, 7, 12, 2 +}; + +static const uint8 kCopyProtectionShapes[7 * 20] = { + 0, 0, 1, 2, 1, 1, 2, 4, 3, 1, 4, 2, 4, 4, 2, 4, 1, 2, 3, 3, + 3, 1, 0, 3, 1, 3, 4, 2, 4, 4, 3, 2, 0, 2, 0, 1, 2, 0, 1, 4, + 1, 0, 2, 3, 4, 2, 3, 2, 2, 0, 0, 0, 4, 2, 3, 4, 4, 0, 4, 1, + 0, 2, 0, 4, 2, 4, 4, 2, 3, 0, 1, 1, 1, 1, 3, 0, 4, 2, 3, 4, + 3, 4, 3, 0, 1, 2, 0, 2, 2, 0, 2, 4, 0, 3, 4, 1, 1, 4, 1, 3, + 4, 2, 1, 1, 1, 1, 4, 3, 4, 2, 3, 0, 0, 3, 0, 2, 3, 0, 2, 4, + 4, 2, 4, 3, 0, 4, 0, 2, 3, 1, 4, 1, 3, 1, 0, 0, 2, 1, 3, 2 +}; + +static const uint8 kCopyProtectionObfuscate[4] = { + 0, 1, 2, 3 +}; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton BabaYaga::kAnimalsButtons = { + true, 131, 127, 183, 164, 193, 0, 245, 37, 131, 127, 0 +}; + +const OnceUpon::MenuButton BabaYaga::kAnimalButtons[] = { + {false, 34, 84, 92, 127, 34, 84, 92, 127, 131, 25, 0}, + {false, 114, 65, 172, 111, 114, 65, 172, 111, 131, 25, 1}, + {false, 186, 72, 227, 96, 186, 72, 227, 96, 139, 25, 2}, + {false, 249, 87, 282, 112, 249, 87, 282, 112, 143, 25, 3}, + {false, 180, 97, 234, 138, 180, 97, 234, 138, 133, 25, 4}, + {false, 197, 145, 242, 173, 197, 145, 242, 173, 137, 25, 5}, + {false, 113, 156, 171, 176, 113, 156, 171, 176, 131, 25, 6}, + {false, 114, 127, 151, 150, 114, 127, 151, 150, 141, 25, 7}, + {false, 36, 136, 94, 176, 36, 136, 94, 176, 131, 25, 8}, + {false, 245, 123, 293, 155, 245, 123, 293, 155, 136, 25, 9} +}; + +const char *BabaYaga::kAnimalNames[] = { + "vaut", + "drag", + "arai", + "gren", + "fauc", + "abei", + "serp", + "tort", + "sang", + "rena" +}; + +// The houses where the stork can drop a bundle +const OnceUpon::MenuButton BabaYaga::kStorkHouses[] = { + {false, 16, 80, 87, 125, 0, 0, 0, 0, 0, 0, 0}, // Castle , Lord & Lady + {false, 61, 123, 96, 149, 0, 0, 0, 0, 0, 0, 1}, // Cottage, Farmers + {false, 199, 118, 226, 137, 0, 0, 0, 0, 0, 0, 2}, // Hut , Woodcutters + {false, 229, 91, 304, 188, 0, 0, 0, 0, 0, 0, 3} // Palace , King & Queen +}; + +// The stork bundle drop parameters +const Stork::BundleDrop BabaYaga::kStorkBundleDrops[] = { + { 14, 35, 129, true }, + { 14, 70, 148, true }, + { 14, 206, 136, true }, + { 11, 260, 225, false } +}; + +// Parameters for the stork section. +const OnceUpon::StorkParam BabaYaga::kStorkParam = { + "present2.cmp", ARRAYSIZE(kStorkHouses), kStorkHouses, kStorkBundleDrops +}; + + +BabaYaga::BabaYaga(GobEngine *vm) : OnceUpon(vm) { +} + +BabaYaga::~BabaYaga() { +} + +void BabaYaga::run() { + init(); + + // Copy protection + bool correctCP = doCopyProtection(kCopyProtectionColors, kCopyProtectionShapes, kCopyProtectionObfuscate); + if (_vm->shouldQuit() || !correctCP) + return; + + // Show the intro + showIntro(); + if (_vm->shouldQuit()) + return; + + // Handle the start menu + doStartMenu(&kAnimalsButtons, ARRAYSIZE(kAnimalButtons), kAnimalButtons, kAnimalNames); + if (_vm->shouldQuit()) + return; + + // Play the actual game + playGame(); +} + +const OnceUpon::StorkParam &BabaYaga::getStorkParameters() const { + return kStorkParam; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/babayaga.h b/engines/gob/pregob/onceupon/babayaga.h new file mode 100644 index 0000000000..0241f78f4e --- /dev/null +++ b/engines/gob/pregob/onceupon/babayaga.h @@ -0,0 +1,61 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_BABAYAGA_H +#define GOB_PREGOB_ONCEUPON_BABAYAGA_H + +#include "gob/pregob/onceupon/onceupon.h" + +namespace Gob { + +namespace OnceUpon { + +class BabaYaga : public OnceUpon { +public: + BabaYaga(GobEngine *vm); + ~BabaYaga(); + + void run(); + +protected: + const StorkParam &getStorkParameters() const; + +private: + /** Definition of the menu button that leads to the animal names screen. */ + static const MenuButton kAnimalsButtons; + + /** Definition of the buttons that make up the animals in the animal names screen. */ + static const MenuButton kAnimalButtons[]; + /** File prefixes for the name of each animal. */ + static const char *kAnimalNames[]; + + // Parameters for the stork section. + static const MenuButton kStorkHouses[]; + static const Stork::BundleDrop kStorkBundleDrops[]; + static const struct StorkParam kStorkParam; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_BABAYAGA_H diff --git a/engines/gob/pregob/onceupon/brokenstrings.h b/engines/gob/pregob/onceupon/brokenstrings.h new file mode 100644 index 0000000000..89acb1c6bd --- /dev/null +++ b/engines/gob/pregob/onceupon/brokenstrings.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H +#define GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H + +struct BrokenString { + const char *wrong; + const char *correct; +}; + +struct BrokenStringLanguage { + const BrokenString *strings; + uint count; +}; + +static const BrokenString kBrokenStringsGerman[] = { + { "Zeichungen von Kaki," , "Zeichnungen von Kaki," }, + { "die es in seine Wachtr\204ume", "die es in seine Tagtr\204ume" }, + { " Spielerfahrung" , " Spielerfahren" }, + { " Fortgeschrittene" , " Fortgeschritten" }, + { "die Vespe" , "die Wespe" }, + { "das Rhinoceros" , "das Rhinozeros" }, + { "die Heusschrecke" , "die Heuschrecke" }, + { "Das, von Drachen gebrachte" , "Das vom Drachen gebrachte" }, + { "Am Waldesrand es sieht" , "Am Waldesrand sieht es" }, + { " das Kind den Palast." , "das Kind den Palast." }, + { "Am Waldessaum sieht" , "Am Waldesrand sieht" }, + { "tipp auf ESC!" , "dr\201cke ESC!" }, + { "Wohin fliegt der Drachen?" , "Wohin fliegt der Drache?" } +}; + +static const BrokenStringLanguage kBrokenStrings[kLanguageCount] = { + { 0, 0 }, // French + { kBrokenStringsGerman, ARRAYSIZE(kBrokenStringsGerman) }, // German + { 0, 0 }, // English + { 0, 0 }, // Spanish + { 0, 0 }, // Italian +}; + +#endif // GOB_PREGOB_ONCEUPON_BROKENSTRINGS_H diff --git a/engines/gob/pregob/onceupon/chargenchild.cpp b/engines/gob/pregob/onceupon/chargenchild.cpp new file mode 100644 index 0000000000..ba099e4937 --- /dev/null +++ b/engines/gob/pregob/onceupon/chargenchild.cpp @@ -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. + * + */ + +#include "gob/surface.h" +#include "gob/anifile.h" + +#include "gob/pregob/onceupon/chargenchild.h" + +enum Animation { + kAnimWalkLeft = 0, + kAnimWalkRight = 1, + kAnimJumpLeft = 2, + kAnimJumpRight = 3, + kAnimTapFoot = 14 +}; + +namespace Gob { + +namespace OnceUpon { + +CharGenChild::CharGenChild(const ANIFile &ani) : ANIObject(ani) { + setPosition(265, 110); + setAnimation(kAnimWalkLeft); + setVisible(true); + setPause(false); +} + +CharGenChild::~CharGenChild() { +} + +void CharGenChild::advance() { + bool wasLastFrame = lastFrame(); + + ANIObject::advance(); + + int16 x, y, left, top, width, height; + getPosition(x, y); + getFramePosition(left, top); + getFrameSize(width, height); + + const int16 right = left + width - 1; + + switch (getAnimation()) { + case kAnimWalkLeft: + if (left <= 147) + setAnimation(kAnimWalkRight); + break; + + case kAnimWalkRight: + if (right >= 290) { + setAnimation(kAnimJumpLeft); + + setPosition(x, y - 14); + } + break; + + case kAnimJumpLeft: + if (wasLastFrame) { + setAnimation(kAnimTapFoot); + + setPosition(x, y - 10); + } + break; + + case kAnimTapFoot: + if (wasLastFrame) { + setAnimation(kAnimJumpRight); + + setPosition(x, y + 10); + } + break; + + case kAnimJumpRight: + if (wasLastFrame) { + setAnimation(kAnimWalkLeft); + + setPosition(x, y + 14); + } + break; + } +} + +CharGenChild::Sound CharGenChild::shouldPlaySound() const { + const uint16 anim = getAnimation(); + const uint16 frame = getFrame(); + + if (((anim == kAnimWalkLeft) || (anim == kAnimWalkRight)) && ((frame == 1) || (frame == 6))) + return kSoundWalk; + + if (((anim == kAnimJumpLeft) || (anim == kAnimJumpRight)) && (frame == 0)) + return kSoundJump; + + return kSoundNone; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/chargenchild.h b/engines/gob/pregob/onceupon/chargenchild.h new file mode 100644 index 0000000000..3b09ef112a --- /dev/null +++ b/engines/gob/pregob/onceupon/chargenchild.h @@ -0,0 +1,60 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_CHARGENCHILD_H +#define GOB_PREGOB_ONCEUPON_CHARGENCHILD_H + +#include "common/system.h" + +#include "gob/aniobject.h" + +namespace Gob { + +class Surface; +class ANIFile; + +namespace OnceUpon { + +/** The child running around on the character generator screen. */ +class CharGenChild : public ANIObject { +public: + enum Sound { + kSoundNone = 0, + kSoundWalk , + kSoundJump + }; + + CharGenChild(const ANIFile &ani); + ~CharGenChild(); + + /** Advance the animation to the next frame. */ + void advance(); + + /** Should we play a sound right now? */ + Sound shouldPlaySound() const; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_CHARGENCHILD_H diff --git a/engines/gob/pregob/onceupon/onceupon.cpp b/engines/gob/pregob/onceupon/onceupon.cpp new file mode 100644 index 0000000000..e4c2df34c0 --- /dev/null +++ b/engines/gob/pregob/onceupon/onceupon.cpp @@ -0,0 +1,1904 @@ +/* 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 "gob/gob.h" +#include "gob/global.h" +#include "gob/util.h" +#include "gob/dataio.h" +#include "gob/surface.h" +#include "gob/draw.h" +#include "gob/video.h" +#include "gob/anifile.h" +#include "gob/aniobject.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/txtfile.h" +#include "gob/pregob/gctfile.h" + +#include "gob/pregob/onceupon/onceupon.h" +#include "gob/pregob/onceupon/palettes.h" +#include "gob/pregob/onceupon/title.h" +#include "gob/pregob/onceupon/parents.h" +#include "gob/pregob/onceupon/chargenchild.h" + +static const uint kLanguageCount = 5; + +static const uint kCopyProtectionHelpStringCount = 3; + +static const char *kCopyProtectionHelpStrings[Gob::OnceUpon::OnceUpon::kLanguageCount][kCopyProtectionHelpStringCount] = { + { // French + "Consulte le livret des animaux, rep\212re la", + "page correspondant \205 la couleur de l\'\202cran", + "et clique le symbole associ\202 \205 l\'animal affich\202.", + }, + { // German + "Suche im Tieralbum die Seite, die der Farbe auf", + "dem Bildschirm entspricht und klicke auf das", + "Tiersymbol.", + }, + { // English + "Consult the book of animals, find the page", + "corresponding to the colour of screen and click", + "the symbol associated with the animal displayed.", + }, + { // Spanish + "Consulta el libro de los animales, localiza la ", + "p\240gina que corresponde al color de la pantalla.", + "Cliquea el s\241mbolo asociado al animal que aparece.", + }, + { // Italian + "Guarda il libretto degli animali, trova la", + "pagina che corrisponde al colore dello schermo,", + "clicca il simbolo associato all\'animale presentato", + } +}; + +static const char *kCopyProtectionWrongStrings[Gob::OnceUpon::OnceUpon::kLanguageCount] = { + "Tu t\'es tromp\202, dommage...", // French + "Schade, du hast dich geirrt." , // German + "You are wrong, what a pity!" , // English + "Te equivocas, l\240stima..." , // Spanish + "Sei Sbagliato, peccato..." // Italian +}; + +static const uint kCopyProtectionShapeCount = 5; + +static const int16 kCopyProtectionShapeCoords[kCopyProtectionShapeCount][6] = { + { 0, 51, 26, 75, 60, 154}, + { 28, 51, 58, 81, 96, 151}, + { 60, 51, 94, 79, 136, 152}, + { 96, 51, 136, 71, 180, 155}, + {140, 51, 170, 77, 228, 153} +}; + +enum ClownAnimation { + kClownAnimationClownCheer = 0, + kClownAnimationClownStand = 1, + kClownAnimationClownCry = 6 +}; + +// 12 seconds delay for one area full of GCT text +static const uint32 kGCTDelay = 12000; + +namespace Gob { + +namespace OnceUpon { + +const OnceUpon::MenuButton OnceUpon::kMainMenuDifficultyButton[] = { + {false, 29, 18, 77, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyBeginner}, + {false, 133, 18, 181, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyIntermediate}, + {false, 241, 18, 289, 57, 0, 0, 0, 0, 0, 0, (int)kDifficultyAdvanced}, +}; + +const OnceUpon::MenuButton OnceUpon::kSectionButtons[] = { + {false, 27, 121, 91, 179, 0, 0, 0, 0, 0, 0, 0}, + { true, 95, 121, 159, 179, 4, 1, 56, 49, 100, 126, 2}, + { true, 163, 121, 227, 179, 64, 1, 120, 49, 168, 126, 6}, + { true, 231, 121, 295, 179, 128, 1, 184, 49, 236, 126, 10} +}; + +const OnceUpon::MenuButton OnceUpon::kIngameButtons[] = { + {true, 108, 83, 139, 116, 0, 0, 31, 34, 108, 83, 0}, + {true, 144, 83, 175, 116, 36, 0, 67, 34, 144, 83, 1}, + {true, 180, 83, 211, 116, 72, 0, 103, 34, 180, 83, 2} +}; + +const OnceUpon::MenuButton OnceUpon::kAnimalNamesBack = { + true, 19, 13, 50, 46, 36, 0, 67, 34, 19, 13, 1 +}; + +const OnceUpon::MenuButton OnceUpon::kLanguageButtons[] = { + {true, 43, 80, 93, 115, 0, 55, 50, 90, 43, 80, 0}, + {true, 132, 80, 182, 115, 53, 55, 103, 90, 132, 80, 1}, + {true, 234, 80, 284, 115, 106, 55, 156, 90, 234, 80, 2}, + {true, 43, 138, 93, 173, 159, 55, 209, 90, 43, 138, 3}, + {true, 132, 138, 182, 173, 212, 55, 262, 90, 132, 138, 4}, + {true, 234, 138, 284, 173, 265, 55, 315, 90, 234, 138, 2} +}; + +const char *OnceUpon::kSound[kSoundCount] = { + "diamant.snd", // kSoundClick + "cigogne.snd", // kSoundStork + "saute.snd" // kSoundJump +}; + +const OnceUpon::SectionFunc OnceUpon::kSectionFuncs[kSectionCount] = { + &OnceUpon::sectionStork, + &OnceUpon::sectionChapter1, + &OnceUpon::sectionParents, + &OnceUpon::sectionChapter2, + &OnceUpon::sectionForest0, + &OnceUpon::sectionChapter3, + &OnceUpon::sectionEvilCastle, + &OnceUpon::sectionChapter4, + &OnceUpon::sectionForest1, + &OnceUpon::sectionChapter5, + &OnceUpon::sectionBossFight, + &OnceUpon::sectionChapter6, + &OnceUpon::sectionForest2, + &OnceUpon::sectionChapter7, + &OnceUpon::sectionEnd +}; + + +OnceUpon::ScreenBackup::ScreenBackup() : palette(-1), changedCursor(false), cursorVisible(false) { + screen = new Surface(320, 200, 1); +} + +OnceUpon::ScreenBackup::~ScreenBackup() { + delete screen; +} + + +OnceUpon::OnceUpon(GobEngine *vm) : PreGob(vm), _openedArchives(false), + _jeudak(0), _lettre(0), _plettre(0), _glettre(0) { + +} + +OnceUpon::~OnceUpon() { + deinit(); +} + +void OnceUpon::init() { + deinit(); + + // Open data files + + bool hasSTK1 = _vm->_dataIO->openArchive("stk1.stk", true); + bool hasSTK2 = _vm->_dataIO->openArchive("stk2.stk", true); + bool hasSTK3 = _vm->_dataIO->openArchive("stk3.stk", true); + + if (!hasSTK1 || !hasSTK2 || !hasSTK3) + error("OnceUpon::OnceUpon(): Failed to open archives"); + + _openedArchives = true; + + // Open fonts + + _jeudak = _vm->_draw->loadFont("jeudak.let"); + _lettre = _vm->_draw->loadFont("lettre.let"); + _plettre = _vm->_draw->loadFont("plettre.let"); + _glettre = _vm->_draw->loadFont("glettre.let"); + + if (!_jeudak || !_lettre || !_plettre || !_glettre) + error("OnceUpon::OnceUpon(): Failed to fonts (%d, %d, %d, %d)", + _jeudak != 0, _lettre != 0, _plettre != 0, _glettre != 0); + + // Verify the language + + if (_vm->_global->_language == kLanguageAmerican) + _vm->_global->_language = kLanguageBritish; + + if ((_vm->_global->_language >= kLanguageCount)) + error("We do not support the language \"%s\".\n" + "If you are certain that your game copy includes this language,\n" + "please contact the ScummVM team with details about this version.\n" + "Thanks", _vm->getLangDesc(_vm->_global->_language)); + + // Load all our sounds and init the screen + + loadSounds(kSound, kSoundCount); + initScreen(); + + // We start with an invalid palette + _palette = -1; + + // No quit requested at start + _quit = false; + + // We start with no selected difficulty and at section 0 + _difficulty = kDifficultyCount; + _section = 0; + + // Default name + _name = "Nemo"; + + // Default character properties + _house = 0; + _head = 0; + _colorHair = 0; + _colorJacket = 0; + _colorTrousers = 0; +} + +void OnceUpon::deinit() { + // Free sounds + freeSounds(); + + // Free fonts + + delete _jeudak; + delete _lettre; + delete _plettre; + delete _glettre; + + _jeudak = 0; + _lettre = 0; + _plettre = 0; + _glettre = 0; + + // Close archives + + if (_openedArchives) { + _vm->_dataIO->closeArchive(true); + _vm->_dataIO->closeArchive(true); + _vm->_dataIO->closeArchive(true); + } + + _openedArchives = false; +} + +void OnceUpon::setGamePalette(uint palette) { + if (palette >= kPaletteCount) + return; + + _palette = palette; + + setPalette(kGamePalettes[palette], kPaletteSize); +} + +void OnceUpon::setGameCursor() { + Surface cursor(320, 16, 1); + + // Set the default game cursor + _vm->_video->drawPackedSprite("icon.cmp", cursor); + setCursor(cursor, 105, 0, 120, 15, 0, 0); +} + +void OnceUpon::drawLineByLine(const Surface &src, int16 left, int16 top, int16 right, int16 bottom, + int16 x, int16 y) const { + + // A special way of drawing something: + // Draw every other line "downwards", wait a bit after each line + // Then, draw the remaining lines "upwards" and again wait a bit after each line. + + if (_vm->shouldQuit()) + return; + + const int16 width = right - left + 1; + const int16 height = bottom - top + 1; + + if ((width <= 0) || (height <= 0)) + return; + + // Draw the even lines downwards + for (int16 i = 0; i < height; i += 2) { + if (_vm->shouldQuit()) + return; + + _vm->_draw->_backSurface->blit(src, left, top + i, right, top + i, x, y + i); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, x, y + i, x + width - 1, y + 1); + _vm->_draw->blitInvalidated(); + + _vm->_util->longDelay(1); + } + + // Draw the odd lines upwards + for (int16 i = (height & 1) ? height : (height - 1); i >= 0; i -= 2) { + if (_vm->shouldQuit()) + return; + + _vm->_draw->_backSurface->blit(src, left, top + i, right, top + i, x, y + i); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, x, y + i, x + width - 1, y + 1); + _vm->_draw->blitInvalidated(); + + _vm->_util->longDelay(1); + } +} + +void OnceUpon::backupScreen(ScreenBackup &backup, bool setDefaultCursor) { + // Backup the screen and palette + backup.screen->blit(*_vm->_draw->_backSurface); + backup.palette = _palette; + + // Backup the cursor + + backup.cursorVisible = isCursorVisible(); + + backup.changedCursor = false; + if (setDefaultCursor) { + backup.changedCursor = true; + + addCursor(); + setGameCursor(); + } +} + +void OnceUpon::restoreScreen(ScreenBackup &backup) { + if (_vm->shouldQuit()) + return; + + // Restore the screen + _vm->_draw->_backSurface->blit(*backup.screen); + _vm->_draw->forceBlit(); + + // Restore the palette + if (backup.palette >= 0) + setGamePalette(backup.palette); + + // Restore the cursor + + if (!backup.cursorVisible) + hideCursor(); + + if (backup.changedCursor) + removeCursor(); + + backup.changedCursor = false; +} + +void OnceUpon::fixTXTStrings(TXTFile &txt) const { + TXTFile::LineArray &lines = txt.getLines(); + for (uint i = 0; i < lines.size(); i++) + lines[i].text = fixString(lines[i].text); +} + +#include "gob/pregob/onceupon/brokenstrings.h" +Common::String OnceUpon::fixString(const Common::String &str) const { + const BrokenStringLanguage &broken = kBrokenStrings[_vm->_global->_language]; + + for (uint i = 0; i < broken.count; i++) { + if (str == broken.strings[i].wrong) + return broken.strings[i].correct; + } + + return str; +} + +enum ClownAnimation { + kClownAnimationStand = 0, + kClownAnimationCheer = 1, + kClownAnimationCry = 2 +}; + +const PreGob::AnimProperties OnceUpon::kClownAnimations[] = { + { 1, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 0, 0, ANIObject::kModeOnce , true, false, false, 0, 0}, + { 6, 0, ANIObject::kModeOnce , true, false, false, 0, 0} +}; + +enum CopyProtectionState { + kCPStateSetup, // Set up the screen + kCPStateWaitUser, // Waiting for the user to pick a shape + kCPStateWaitClown, // Waiting for the clown animation to finish + kCPStateFinish // Finishing +}; + +bool OnceUpon::doCopyProtection(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4]) { + fadeOut(); + setPalette(kCopyProtectionPalette, kPaletteSize); + + // Load the copy protection sprites + Surface sprites[2] = {Surface(320, 200, 1), Surface(320, 200, 1)}; + + _vm->_video->drawPackedSprite("grille1.cmp", sprites[0]); + _vm->_video->drawPackedSprite("grille2.cmp", sprites[1]); + + // Load the clown animation + ANIFile ani (_vm, "grille.ani", 320); + ANIList anims; + + loadAnims(anims, ani, 1, &kClownAnimations[kClownAnimationStand]); + + // Set the copy protection cursor + setCursor(sprites[1], 5, 110, 20, 134, 3, 0); + + // We start with 2 tries left, not having a correct answer and the copy protection not set up yet + CopyProtectionState state = kCPStateSetup; + + uint8 triesLeft = 2; + int8 animalShape = -1; + bool hasCorrect = false; + + while (!_vm->shouldQuit() && (state != kCPStateFinish)) { + clearAnim(anims); + + // Set up the screen + if (state == kCPStateSetup) { + animalShape = cpSetup(colors, shapes, obfuscate, sprites); + + setAnim(*anims[0], kClownAnimations[kClownAnimationStand]); + state = kCPStateWaitUser; + } + + drawAnim(anims); + + // If we're waiting for the clown and he finished, evaluate if we're finished + if (!anims[0]->isVisible() && (state == kCPStateWaitClown)) + state = (hasCorrect || (--triesLeft == 0)) ? kCPStateFinish : kCPStateSetup; + + showCursor(); + fadeIn(); + + endFrame(true); + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + checkInput(mouseX, mouseY, mouseButtons); + + if (state == kCPStateWaitUser) { + // Look if we clicked a shaped and got it right + + int8 guessedShape = -1; + if (mouseButtons == kMouseButtonsLeft) + guessedShape = cpFindShape(mouseX, mouseY); + + if (guessedShape >= 0) { + hasCorrect = guessedShape == animalShape; + animalShape = -1; + + setAnim(*anims[0], kClownAnimations[hasCorrect ? kClownAnimationCheer : kClownAnimationCry]); + state = kCPStateWaitClown; + } + } + } + + freeAnims(anims); + + fadeOut(); + hideCursor(); + clearScreen(); + + // Display the "You are wrong" screen + if (!hasCorrect) + cpWrong(); + + return hasCorrect; +} + +int8 OnceUpon::cpSetup(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4], + const Surface sprites[2]) { + + fadeOut(); + hideCursor(); + + // Get a random animal and animal color + int8 animalColor = _vm->_util->getRandom(7); + while ((colors[animalColor] == 1) || (colors[animalColor] == 7) || (colors[animalColor] == 11)) + animalColor = _vm->_util->getRandom(7); + + int8 animal = _vm->_util->getRandom(20); + + int8 animalShape = shapes[animalColor * 20 + animal]; + if (animal < 4) + animal = obfuscate[animal]; + + // Get the position of the animal sprite + int16 animalLeft = (animal % 4) * 80; + int16 animalTop = (animal / 4) * 50; + + uint8 sprite = 0; + if (animalTop >= 200) { + animalTop -= 200; + sprite = 1; + } + + int16 animalRight = animalLeft + 80 - 1; + int16 animalBottom = animalTop + 50 - 1; + + // Fill with the animal color + _vm->_draw->_backSurface->fill(colors[animalColor]); + + // Print the help line strings + for (uint i = 0; i < kCopyProtectionHelpStringCount; i++) { + const char * const helpString = kCopyProtectionHelpStrings[_vm->_global->_language][i]; + + const int x = 160 - (strlen(helpString) * _plettre->getCharWidth()) / 2; + const int y = i * 10 + 5; + + _plettre->drawString(helpString, x, y, 8, 0, true, *_vm->_draw->_backSurface); + } + + // White rectangle with black border + _vm->_draw->_backSurface->fillRect( 93, 43, 226, 134, 15); + _vm->_draw->_backSurface->drawRect( 92, 42, 227, 135, 0); + + // Draw the animal in the animal color + _vm->_draw->_backSurface->fillRect(120, 63, 199, 112, colors[animalColor]); + _vm->_draw->_backSurface->blit(sprites[sprite], animalLeft, animalTop, animalRight, animalBottom, 120, 63, 0); + + // Draw the shapes + for (uint i = 0; i < kCopyProtectionShapeCount; i++) { + const int16 * const coords = kCopyProtectionShapeCoords[i]; + + _vm->_draw->_backSurface->blit(sprites[1], coords[0], coords[1], coords[2], coords[3], coords[4], coords[5], 0); + } + + _vm->_draw->forceBlit(); + + return animalShape; +} + +int8 OnceUpon::cpFindShape(int16 x, int16 y) const { + // Look through all shapes and check if the coordinates are inside one of them + for (uint i = 0; i < kCopyProtectionShapeCount; i++) { + const int16 * const coords = kCopyProtectionShapeCoords[i]; + + const int16 left = coords[4]; + const int16 top = coords[5]; + const int16 right = coords[4] + (coords[2] - coords[0] + 1) - 1; + const int16 bottom = coords[5] + (coords[3] - coords[1] + 1) - 1; + + if ((x >= left) && (x <= right) && (y >= top) && (y <= bottom)) + return i; + } + + return -1; +} + +void OnceUpon::cpWrong() { + // Display the "You are wrong" string, centered + + const char * const wrongString = kCopyProtectionWrongStrings[_vm->_global->_language]; + const int wrongX = 160 - (strlen(wrongString) * _plettre->getCharWidth()) / 2; + + _vm->_draw->_backSurface->clear(); + _plettre->drawString(wrongString, wrongX, 100, 15, 0, true, *_vm->_draw->_backSurface); + + _vm->_draw->forceBlit(); + + fadeIn(); + + waitInput(); + + fadeOut(); + clearScreen(); +} + +void OnceUpon::showIntro() { + // Show all intro parts + + // "Loading" + showWait(10); + if (_vm->shouldQuit()) + return; + + // Quote about fairy tales + showQuote(); + if (_vm->shouldQuit()) + return; + + // Once Upon A Time title + showTitle(); + if (_vm->shouldQuit()) + return; + + // Game title screen + showChapter(0); + if (_vm->shouldQuit()) + return; + + // "Loading" + showWait(17); +} + +void OnceUpon::showWait(uint palette) { + // Show the loading floppy + + fadeOut(); + clearScreen(); + setGamePalette(palette); + + Surface wait(320, 43, 1); + + _vm->_video->drawPackedSprite("wait.cmp", wait); + _vm->_draw->_backSurface->blit(wait, 0, 0, 72, 33, 122, 84); + + _vm->_draw->forceBlit(); + + fadeIn(); +} + +void OnceUpon::showQuote() { + // Show the quote about fairytales + + fadeOut(); + clearScreen(); + setGamePalette(11); + + static const Font *fonts[3] = { _plettre, _glettre, _plettre }; + + TXTFile *quote = loadTXT(getLocFile("gene.tx"), TXTFile::kFormatStringPositionColorFont); + quote->draw(*_vm->_draw->_backSurface, fonts, ARRAYSIZE(fonts)); + delete quote; + + _vm->_draw->forceBlit(); + + fadeIn(); + + waitInput(); + + fadeOut(); +} + +const PreGob::AnimProperties OnceUpon::kTitleAnimation = { + 8, 0, ANIObject::kModeContinuous, true, false, false, 0, 0 +}; + +void OnceUpon::showTitle() { + fadeOut(); + setGamePalette(10); + + Title title(_vm); + title.play(); +} + +void OnceUpon::showChapter(int chapter) { + // Display the intro text to a chapter + + fadeOut(); + clearScreen(); + setGamePalette(11); + + // Parchment background + _vm->_video->drawPackedSprite("parch.cmp", *_vm->_draw->_backSurface); + + static const Font *fonts[3] = { _plettre, _glettre, _plettre }; + + const Common::String chapterFile = getLocFile(Common::String::format("gene%d.tx", chapter)); + + TXTFile *gameTitle = loadTXT(chapterFile, TXTFile::kFormatStringPositionColorFont); + gameTitle->draw(*_vm->_draw->_backSurface, fonts, ARRAYSIZE(fonts)); + delete gameTitle; + + _vm->_draw->forceBlit(); + + fadeIn(); + + waitInput(); + + fadeOut(); +} + +void OnceUpon::showByeBye() { + fadeOut(); + hideCursor(); + clearScreen(); + setGamePalette(1); + + _plettre->drawString("Bye Bye....", 140, 80, 2, 0, true, *_vm->_draw->_backSurface); + _vm->_draw->forceBlit(); + + fadeIn(); + + _vm->_util->longDelay(1000); + + fadeOut(); +} + +void OnceUpon::doStartMenu(const MenuButton *animalsButton, uint animalCount, + const MenuButton *animalButtons, const char * const *animalNames) { + clearScreen(); + + // Wait until we clicked on of the difficulty buttons and are ready to start playing + while (!_vm->shouldQuit()) { + MenuAction action = handleStartMenu(animalsButton); + if (action == kMenuActionPlay) + break; + + // If we pressed the "listen to animal names" button, handle that screen + if (action == kMenuActionAnimals) + handleAnimalNames(animalCount, animalButtons, animalNames); + } +} + +OnceUpon::MenuAction OnceUpon::handleStartMenu(const MenuButton *animalsButton) { + ScreenBackup screenBackup; + backupScreen(screenBackup, true); + + fadeOut(); + setGamePalette(17); + drawStartMenu(animalsButton); + showCursor(); + fadeIn(); + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + if (key == kKeyEscape) + // ESC -> Quit + return kMenuActionQuit; + + if (mouseButtons != kMouseButtonsLeft) + continue; + + playSound(kSoundClick); + + // If we clicked on a difficulty button, show the selected difficulty and start the game + int diff = checkButton(kMainMenuDifficultyButton, ARRAYSIZE(kMainMenuDifficultyButton), mouseX, mouseY); + if (diff >= 0) { + _difficulty = (Difficulty)diff; + action = kMenuActionPlay; + + drawStartMenu(animalsButton); + _vm->_util->longDelay(1000); + } + + if (animalsButton && (checkButton(animalsButton, 1, mouseX, mouseY) != -1)) + action = kMenuActionAnimals; + + } + + fadeOut(); + restoreScreen(screenBackup); + + return action; +} + +OnceUpon::MenuAction OnceUpon::handleMainMenu() { + ScreenBackup screenBackup; + backupScreen(screenBackup, true); + + fadeOut(); + setGamePalette(17); + drawMainMenu(); + showCursor(); + fadeIn(); + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + if (key == kKeyEscape) + // ESC -> Quit + return kMenuActionQuit; + + if (mouseButtons != kMouseButtonsLeft) + continue; + + playSound(kSoundClick); + + // If we clicked on a difficulty button, change the current difficulty level + int diff = checkButton(kMainMenuDifficultyButton, ARRAYSIZE(kMainMenuDifficultyButton), mouseX, mouseY); + if ((diff >= 0) && (diff != (int)_difficulty)) { + _difficulty = (Difficulty)diff; + + drawMainMenu(); + } + + // If we clicked on a section button, restart the game from this section + int section = checkButton(kSectionButtons, ARRAYSIZE(kSectionButtons), mouseX, mouseY); + if ((section >= 0) && (section <= _section)) { + _section = section; + action = kMenuActionRestart; + } + + } + + fadeOut(); + restoreScreen(screenBackup); + + return action; +} + +OnceUpon::MenuAction OnceUpon::handleIngameMenu() { + ScreenBackup screenBackup; + backupScreen(screenBackup, true); + + drawIngameMenu(); + showCursor(); + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + if ((key == kKeyEscape) || (mouseButtons == kMouseButtonsRight)) + // ESC or right mouse button -> Dismiss the menu + action = kMenuActionPlay; + + if (mouseButtons != kMouseButtonsLeft) + continue; + + int button = checkButton(kIngameButtons, ARRAYSIZE(kIngameButtons), mouseX, mouseY); + if (button == 0) + action = kMenuActionQuit; + else if (button == 1) + action = kMenuActionMainMenu; + else if (button == 2) + action = kMenuActionPlay; + + } + + clearIngameMenu(*screenBackup.screen); + restoreScreen(screenBackup); + + return action; +} + +void OnceUpon::drawStartMenu(const MenuButton *animalsButton) { + // Draw the background + _vm->_video->drawPackedSprite("menu2.cmp", *_vm->_draw->_backSurface); + + // Draw the "Listen to animal names" button + if (animalsButton) { + Surface elements(320, 38, 1); + _vm->_video->drawPackedSprite("elemenu.cmp", elements); + _vm->_draw->_backSurface->fillRect(animalsButton->left , animalsButton->top, + animalsButton->right, animalsButton->bottom, 0); + drawButton(*_vm->_draw->_backSurface, elements, *animalsButton); + } + + // Highlight the current difficulty + drawMenuDifficulty(); + + _vm->_draw->forceBlit(); +} + +void OnceUpon::drawMainMenu() { + // Draw the background + _vm->_video->drawPackedSprite("menu.cmp", *_vm->_draw->_backSurface); + + // Highlight the current difficulty + drawMenuDifficulty(); + + // Draw the section buttons + Surface elements(320, 200, 1); + _vm->_video->drawPackedSprite("elemenu.cmp", elements); + + for (uint i = 0; i < ARRAYSIZE(kSectionButtons); i++) { + const MenuButton &button = kSectionButtons[i]; + + if (!button.needDraw) + continue; + + if (_section >= (int)button.id) + drawButton(*_vm->_draw->_backSurface, elements, button); + } + + _vm->_draw->forceBlit(); +} + +void OnceUpon::drawIngameMenu() { + Surface menu(320, 34, 1); + _vm->_video->drawPackedSprite("icon.cmp", menu); + + // Draw the menu in a special way, button by button + for (uint i = 0; i < ARRAYSIZE(kIngameButtons); i++) { + const MenuButton &button = kIngameButtons[i]; + + drawLineByLine(menu, button.srcLeft, button.srcTop, button.srcRight, button.srcBottom, + button.dstX, button.dstY); + } + + _vm->_draw->forceBlit(); + _vm->_video->retrace(); +} + +void OnceUpon::drawMenuDifficulty() { + if (_difficulty == kDifficultyCount) + return; + + TXTFile *difficulties = loadTXT(getLocFile("diffic.tx"), TXTFile::kFormatStringPositionColor); + + // Draw the difficulty name + difficulties->draw((uint) _difficulty, *_vm->_draw->_backSurface, &_plettre, 1); + + // Draw a border around the current difficulty + drawButtonBorder(kMainMenuDifficultyButton[_difficulty], difficulties->getLines()[_difficulty].color); + + delete difficulties; +} + +void OnceUpon::clearIngameMenu(const Surface &background) { + if (_vm->shouldQuit()) + return; + + // Find the area encompassing the whole ingame menu + + int16 left = 0x7FFF; + int16 top = 0x7FFF; + int16 right = 0x0000; + int16 bottom = 0x0000; + + for (uint i = 0; i < ARRAYSIZE(kIngameButtons); i++) { + const MenuButton &button = kIngameButtons[i]; + + if (!button.needDraw) + continue; + + left = MIN<int16>(left , button.dstX); + top = MIN<int16>(top , button.dstY); + right = MAX<int16>(right , button.dstX + (button.srcRight - button.srcLeft + 1) - 1); + bottom = MAX<int16>(bottom, button.dstY + (button.srcBottom - button.srcTop + 1) - 1); + } + + if ((left > right) || (top > bottom)) + return; + + // Clear it line by line + drawLineByLine(background, left, top, right, bottom, left, top); +} + +OnceUpon::MenuAction OnceUpon::doIngameMenu() { + // Show the ingame menu + MenuAction action = handleIngameMenu(); + + if ((action == kMenuActionQuit) || _vm->shouldQuit()) { + + // User pressed the quit button, or quit ScummVM + _quit = true; + action = kMenuActionQuit; + + } else if (action == kMenuActionPlay) { + + // User pressed the return to game button + action = kMenuActionPlay; + + } else if (kMenuActionMainMenu) { + + // User pressed the return to main menu button + action = handleMainMenu(); + } + + return action; +} + +OnceUpon::MenuAction OnceUpon::doIngameMenu(int16 &key, MouseButtons &mouseButtons) { + if ((key != kKeyEscape) && (mouseButtons != kMouseButtonsRight)) + return kMenuActionNone; + + key = 0; + mouseButtons = kMouseButtonsNone; + + MenuAction action = doIngameMenu(); + if (action == kMenuActionPlay) + action = kMenuActionNone; + + return action; +} + +int OnceUpon::checkButton(const MenuButton *buttons, uint count, int16 x, int16 y, int failValue) const { + // Look through all buttons, and return the ID of the button we're in + + for (uint i = 0; i < count; i++) { + const MenuButton &button = buttons[i]; + + if ((x >= button.left) && (x <= button.right) && (y >= button.top) && (y <= button.bottom)) + return (int)button.id; + } + + // We're in none of these buttons, return the fail value + return failValue; +} + +void OnceUpon::drawButton(Surface &dest, const Surface &src, const MenuButton &button, int transp) const { + dest.blit(src, button.srcLeft, button.srcTop, button.srcRight, button.srcBottom, button.dstX, button.dstY, transp); +} + +void OnceUpon::drawButtons(Surface &dest, const Surface &src, const MenuButton *buttons, uint count, int transp) const { + for (uint i = 0; i < count; i++) { + const MenuButton &button = buttons[i]; + + if (!button.needDraw) + continue; + + drawButton(dest, src, button, transp); + } +} + +void OnceUpon::drawButtonBorder(const MenuButton &button, uint8 color) { + _vm->_draw->_backSurface->drawRect(button.left, button.top, button.right, button.bottom, color); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, button.left, button.top, button.right, button.bottom); +} + +enum AnimalNamesState { + kANStateChoose, // We're in the animal chooser + kANStateNames, // We're in the language chooser + kANStateFinish // We're finished +}; + +void OnceUpon::handleAnimalNames(uint count, const MenuButton *buttons, const char * const *names) { + fadeOut(); + clearScreen(); + setGamePalette(19); + + bool cursorVisible = isCursorVisible(); + + // Set the cursor + addCursor(); + setGameCursor(); + + anSetupChooser(); + + int8 _animal = -1; + + AnimalNamesState state = kANStateChoose; + while (!_vm->shouldQuit() && (state != kANStateFinish)) { + showCursor(); + fadeIn(); + + endFrame(true); + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + checkInput(mouseX, mouseY, mouseButtons); + + // If we moused over an animal button, draw a border around it + int animal = checkButton(buttons, count, mouseX, mouseY); + if ((state == kANStateChoose) && (animal != _animal)) { + // Erase the old border + if (_animal >= 0) + drawButtonBorder(buttons[_animal], 15); + + _animal = animal; + + // Draw the new border + if (_animal >= 0) + drawButtonBorder(buttons[_animal], 10); + } + + if (mouseButtons != kMouseButtonsLeft) + continue; + + playSound(kSoundClick); + + // We clicked on a language button, play the animal name + int language = checkButton(kLanguageButtons, ARRAYSIZE(kLanguageButtons), mouseX, mouseY); + if ((state == kANStateNames) && (language >= 0)) + anPlayAnimalName(names[_animal], language); + + // We clicked on an animal + if ((state == kANStateChoose) && (_animal >= 0)) { + anSetupNames(buttons[_animal]); + + state = kANStateNames; + } + + // If we clicked on the back button, go back + if (checkButton(&kAnimalNamesBack, 1, mouseX, mouseY) != -1) { + if (state == kANStateNames) { + anSetupChooser(); + + state = kANStateChoose; + } else if (state == kANStateChoose) + state = kANStateFinish; + } + } + + fadeOut(); + + // Restore the cursor + if (!cursorVisible) + hideCursor(); + removeCursor(); +} + +void OnceUpon::anSetupChooser() { + fadeOut(); + + _vm->_video->drawPackedSprite("dico.cmp", *_vm->_draw->_backSurface); + + // Draw the back button + Surface menu(320, 34, 1); + _vm->_video->drawPackedSprite("icon.cmp", menu); + drawButton(*_vm->_draw->_backSurface, menu, kAnimalNamesBack); + + // "Choose an animal" + TXTFile *choose = loadTXT(getLocFile("choisi.tx"), TXTFile::kFormatStringPosition); + choose->draw(*_vm->_draw->_backSurface, &_plettre, 1, 14); + delete choose; + + _vm->_draw->forceBlit(); +} + +void OnceUpon::anSetupNames(const MenuButton &animal) { + fadeOut(); + + Surface background(320, 200, 1); + + _vm->_video->drawPackedSprite("dico.cmp", background); + + // Draw the background and clear what we don't need + _vm->_draw->_backSurface->blit(background); + _vm->_draw->_backSurface->fillRect(19, 19, 302, 186, 15); + + // Draw the back button + Surface menu(320, 34, 1); + _vm->_video->drawPackedSprite("icon.cmp", menu); + drawButton(*_vm->_draw->_backSurface, menu, kAnimalNamesBack); + + // Draw the animal + drawButton(*_vm->_draw->_backSurface, background, animal); + + // Draw the language buttons + Surface elements(320, 200, 1); + _vm->_video->drawPackedSprite("elemenu.cmp", elements); + drawButtons(*_vm->_draw->_backSurface, elements, kLanguageButtons, ARRAYSIZE(kLanguageButtons)); + + // Draw the language names + _plettre->drawString("Fran\207ais", 43, 70, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("Deutsch" , 136, 70, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("English" , 238, 70, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("Italiano" , 43, 128, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("Espa\244ol" , 136, 128, 10, 15, true, *_vm->_draw->_backSurface); + _plettre->drawString("English" , 238, 128, 10, 15, true, *_vm->_draw->_backSurface); + + _vm->_draw->forceBlit(); +} + +void OnceUpon::anPlayAnimalName(const Common::String &animal, uint language) { + // Sound file to play + Common::String soundFile = animal + "_" + kLanguageSuffixLong[language] + ".snd"; + + // Get the name of the animal + TXTFile *names = loadTXT(animal + ".anm", TXTFile::kFormatString); + Common::String name = names->getLines()[language].text; + delete names; + + // It should be centered on the screen + const int nameX = 160 - (name.size() * _plettre->getCharWidth()) / 2; + + // Backup the screen surface + Surface backup(162, 23, 1); + backup.blit(*_vm->_draw->_backSurface, 78, 123, 239, 145, 0, 0); + + // Draw the name border + Surface nameBorder(162, 23, 1); + _vm->_video->drawPackedSprite("mot.cmp", nameBorder); + _vm->_draw->_backSurface->blit(nameBorder, 0, 0, 161, 22, 78, 123); + + // Print the animal name + _plettre->drawString(name, nameX, 129, 10, 0, true, *_vm->_draw->_backSurface); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 78, 123, 239, 145); + + playSoundFile(soundFile); + + // Restore the screen + _vm->_draw->_backSurface->blit(backup, 0, 0, 161, 22, 78, 123); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 78, 123, 239, 145); +} + +void OnceUpon::playGame() { + while (!_vm->shouldQuit() && !_quit) { + // Play a section and advance to the next section if we finished it + if (playSection()) + _section = MIN(_section + 1, kSectionCount - 1); + } + + // If we quit through the game and not through ScummVM, show the "Bye Bye" screen + if (!_vm->shouldQuit()) + showByeBye(); +} + +bool OnceUpon::playSection() { + if ((_section < 0) || (_section >= ARRAYSIZE(kSectionFuncs))) { + _quit = true; + return false; + } + + return (this->*kSectionFuncs[_section])(); +} + +const PreGob::AnimProperties OnceUpon::kSectionStorkAnimations[] = { + { 0, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 1, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 2, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 3, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 4, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 5, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 6, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 7, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 8, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {17, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {16, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {15, 0, ANIObject::kModeContinuous, true, false, false, 0, 0} +}; + +enum StorkState { + kStorkStateWaitUser, + kStorkStateWaitBundle, + kStorkStateFinish +}; + +bool OnceUpon::sectionStork() { + fadeOut(); + hideCursor(); + setGamePalette(0); + setGameCursor(); + + const StorkParam ¶m = getStorkParameters(); + + Surface backdrop(320, 200, 1); + + // Draw the frame + _vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + + // Draw the backdrop + _vm->_video->drawPackedSprite(param.backdrop, backdrop); + _vm->_draw->_backSurface->blit(backdrop, 0, 0, 288, 175, 16, 12); + + // "Where does the stork go?" + TXTFile *whereStork = loadTXT(getLocFile("ouva.tx"), TXTFile::kFormatStringPositionColor); + whereStork->draw(*_vm->_draw->_backSurface, &_plettre, 1); + + // Where the stork actually goes + GCTFile *thereStork = loadGCT(getLocFile("choix.gc")); + thereStork->setArea(17, 18, 303, 41); + + ANIFile ani(_vm, "present.ani", 320); + ANIList anims; + + Stork *stork = new Stork(_vm, ani); + + loadAnims(anims, ani, ARRAYSIZE(kSectionStorkAnimations), kSectionStorkAnimations); + anims.push_back(stork); + + drawAnim(anims); + + _vm->_draw->forceBlit(); + + int8 storkSoundWait = 0; + + StorkState state = kStorkStateWaitUser; + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (state != kStorkStateFinish)) { + // Play the stork sound + if (--storkSoundWait == 0) + playSound(kSoundStork); + if (storkSoundWait <= 0) + storkSoundWait = 50 - _vm->_util->getRandom(30); + + // Check if the bundle landed + if ((state == kStorkStateWaitBundle) && stork->hasBundleLanded()) + state = kStorkStateFinish; + + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + + action = doIngameMenu(key, mouseButtons); + if (action != kMenuActionNone) { + state = kStorkStateFinish; + break; + } + + clearAnim(anims); + + if (mouseButtons == kMouseButtonsLeft) { + stopSound(); + playSound(kSoundClick); + + int house = checkButton(param.houses, param.houseCount, mouseX, mouseY); + if ((state == kStorkStateWaitUser) && (house >= 0)) { + + _house = house; + + stork->dropBundle(param.drops[house]); + state = kStorkStateWaitBundle; + + // Remove the "Where does the stork go?" text + int16 left, top, right, bottom; + if (whereStork->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + // Print the text where the stork actually goes + thereStork->selectLine(3, house); // The house + thereStork->selectLine(4, house); // The house's inhabitants + if (thereStork->draw(*_vm->_draw->_backSurface, 2, *_plettre, 10, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + } + } + + drawAnim(anims); + showCursor(); + fadeIn(); + + endFrame(true); + } + + freeAnims(anims); + delete thereStork; + delete whereStork; + + fadeOut(); + hideCursor(); + + // Didn't complete the section + if (action != kMenuActionNone) + return false; + + // Move on to the character generator + + CharGenAction charGenAction = kCharGenRestart; + while (charGenAction == kCharGenRestart) + charGenAction = characterGenerator(); + + // Did we successfully create a character? + return charGenAction == kCharGenDone; +} + +const OnceUpon::MenuButton OnceUpon::kCharGenHeadButtons[] = { + {true, 106, 146, 152, 180, 0, 0, 47, 34, 106, 146, 0}, + {true, 155, 146, 201, 180, 49, 0, 96, 34, 155, 146, 1}, + {true, 204, 146, 250, 180, 98, 0, 145, 34, 204, 146, 2}, + {true, 253, 146, 299, 180, 147, 0, 194, 34, 253, 146, 3} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenHeads[] = { + {true, 0, 0, 0, 0, 29, 4, 68, 31, 40, 51, 0}, + {true, 0, 0, 0, 0, 83, 4, 113, 31, 45, 51, 1}, + {true, 0, 0, 0, 0, 132, 4, 162, 31, 45, 51, 2}, + {true, 0, 0, 0, 0, 182, 4, 211, 31, 45, 51, 3} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenHairButtons[] = { + {true, 105, 55, 124, 70, 271, 1, 289, 15, 105, 55, 0x04}, + {true, 105, 74, 124, 89, 271, 20, 289, 34, 105, 74, 0x07} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenJacketButtons[] = { + {true, 105, 90, 124, 105, 271, 39, 289, 53, 105, 90, 0x06}, + {true, 105, 109, 124, 124, 271, 58, 289, 72, 105, 109, 0x02} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenTrousersButtons[] = { + {true, 105, 140, 124, 155, 271, 77, 289, 91, 105, 140, 0x01}, + {true, 105, 159, 124, 174, 271, 96, 289, 110, 105, 159, 0x03} +}; + +const OnceUpon::MenuButton OnceUpon::kCharGenNameEntry[] = { + {true, 0, 0, 0, 0, 0, 38, 54, 48, 140, 145, 0}, + {true, 0, 0, 0, 0, 106, 38, 159, 48, 195, 145, 0}, + {true, 0, 0, 0, 0, 0, 105, 54, 121, 140, 156, 0}, + {true, 0, 0, 0, 0, 106, 105, 159, 121, 195, 156, 0} +}; + +enum CharGenState { + kCharGenStateHead = 0, // Choose a head + kCharGenStateHair , // Choose hair color + kCharGenStateJacket , // Choose jacket color + kCharGenStateTrousers , // Choose trousers color + kCharGenStateName , // Choose name + kCharGenStateSure , // "Are you sure?" + kCharGenStateStoryName , // "We're going to tell the story of $NAME" + kCharGenStateFinish // Finished +}; + +void OnceUpon::charGenSetup(uint stage) { + Surface choix(320, 200, 1), elchoix(320, 200, 1), paperDoll(65, 137, 1); + + _vm->_video->drawPackedSprite("choix.cmp" , choix); + _vm->_video->drawPackedSprite("elchoix.cmp", elchoix); + + paperDoll.blit(choix, 200, 0, 264, 136, 0, 0); + + GCTFile *text = loadGCT(getLocFile("choix.gc")); + text->setArea(17, 18, 303, 41); + text->setText(9, _name); + + // Background + _vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + _vm->_draw->_backSurface->fillRect(16, 50, 303, 187, 5); + + // Character sprite frame + _vm->_draw->_backSurface->blit(choix, 0, 38, 159, 121, 140, 54); + + // Recolor the paper doll parts + if (_colorHair != 0xFF) + elchoix.recolor(0x0C, _colorHair); + + if (_colorJacket != 0xFF) + paperDoll.recolor(0x0A, _colorJacket); + + if (_colorTrousers != 0xFF) + paperDoll.recolor(0x09, _colorTrousers); + + _vm->_draw->_backSurface->blit(paperDoll, 32, 51); + + // Paper doll head + if (_head != 0xFF) + drawButton(*_vm->_draw->_backSurface, elchoix, kCharGenHeads[_head], 0); + + if (stage == kCharGenStateHead) { + // Head buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenHeadButtons, ARRAYSIZE(kCharGenHeadButtons)); + + // "Choose a head" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 5, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateHair) { + // Hair color buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenHairButtons, ARRAYSIZE(kCharGenHairButtons)); + + // "What color is the hair?" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 6, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateJacket) { + // Jacket color buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenJacketButtons, ARRAYSIZE(kCharGenJacketButtons)); + + // "What color is the jacket?" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 7, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateTrousers) { + // Trousers color buttons + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenTrousersButtons, ARRAYSIZE(kCharGenTrousersButtons)); + + // "What color are the trousers?" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 8, *_plettre, 10, left, top, right, bottom); + + } else if (stage == kCharGenStateName) { + // Name entry field + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenNameEntry, ARRAYSIZE(kCharGenNameEntry)); + + // "Enter name" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 10, *_plettre, 10, left, top, right, bottom); + + charGenDrawName(); + } else if (stage == kCharGenStateSure) { + // Name entry field + drawButtons(*_vm->_draw->_backSurface, choix, kCharGenNameEntry, ARRAYSIZE(kCharGenNameEntry)); + + // "Are you sure?" + TXTFile *sure = loadTXT(getLocFile("estu.tx"), TXTFile::kFormatStringPositionColor); + sure->draw(*_vm->_draw->_backSurface, &_plettre, 1); + delete sure; + + charGenDrawName(); + } else if (stage == kCharGenStateStoryName) { + + // "We're going to tell the story of $NAME" + int16 left, top, right, bottom; + text->draw(*_vm->_draw->_backSurface, 11, *_plettre, 10, left, top, right, bottom); + } + + delete text; +} + +bool OnceUpon::enterString(Common::String &name, int16 key, uint maxLength, const Font &font) { + if (key == 0) + return true; + + if (key == kKeyBackspace) { + name.deleteLastChar(); + return true; + } + + if (key == kKeySpace) + key = ' '; + + if ((key >= ' ') && (key <= 0xFF)) { + if (name.size() >= maxLength) + return false; + + if (!font.hasChar(key)) + return false; + + name += (char) key; + return true; + } + + return false; +} + +void OnceUpon::charGenDrawName() { + _vm->_draw->_backSurface->fillRect(147, 151, 243, 166, 1); + + const int16 nameY = 151 + ((166 - 151 + 1 - _plettre->getCharHeight()) / 2); + const int16 nameX = 147 + ((243 - 147 + 1 - (15 * _plettre->getCharWidth ())) / 2); + + _plettre->drawString(_name, nameX, nameY, 10, 0, true, *_vm->_draw->_backSurface); + + const int16 cursorLeft = nameX + _name.size() * _plettre->getCharWidth(); + const int16 cursorTop = nameY; + const int16 cursorRight = cursorLeft + _plettre->getCharWidth() - 1; + const int16 cursorBottom = cursorTop + _plettre->getCharHeight() - 1; + + _vm->_draw->_backSurface->fillRect(cursorLeft, cursorTop, cursorRight, cursorBottom, 10); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 151, 243, 166); +} + +OnceUpon::CharGenAction OnceUpon::characterGenerator() { + fadeOut(); + hideCursor(); + setGameCursor(); + + showWait(1); + + _name.clear(); + + _head = 0xFF; + _colorHair = 0xFF; + _colorJacket = 0xFF; + _colorTrousers = 0xFF; + + CharGenState state = kCharGenStateHead; + charGenSetup(state); + + ANIFile ani(_vm, "ba.ani", 320); + + ani.recolor(0x0F, 0x0C); + ani.recolor(0x0E, 0x0A); + ani.recolor(0x08, 0x09); + + CharGenChild *child = new CharGenChild(ani); + + ANIList anims; + anims.push_back(child); + + fadeOut(); + _vm->_draw->forceBlit(); + + CharGenAction action = kCharGenRestart; + while (!_vm->shouldQuit() && (state != kCharGenStateFinish)) { + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + + MenuAction menuAction = doIngameMenu(key, mouseButtons); + if (menuAction != kMenuActionNone) { + state = kCharGenStateFinish; + action = kCharGenAbort; + break; + } + + clearAnim(anims); + + if (state == kCharGenStateStoryName) { + if ((mouseButtons != kMouseButtonsNone) || (key != 0)) { + state = kCharGenStateFinish; + action = kCharGenDone; + break; + } + } + + if (state == kCharGenStateSure) { + // Not sure => restart + if ((key == 'N') || (key == 'n')) { // No / Nein / Non + state = kCharGenStateFinish; + action = kCharGenRestart; + break; + } + + if ((key == 'Y') || (key == 'y') || // Yes + (key == 'J') || (key == 'j') || // Ja + (key == 'S') || (key == 's') || // Si + (key == 'O') || (key == 'o')) { // Oui + + state = kCharGenStateStoryName; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + } + + if (state == kCharGenStateName) { + if (enterString(_name, key, 14, *_plettre)) { + _vm->_draw->_backSurface->fillRect(147, 151, 243, 166, 1); + + const int16 nameY = 151 + ((166 - 151 + 1 - _plettre->getCharHeight()) / 2); + const int16 nameX = 147 + ((243 - 147 + 1 - (15 * _plettre->getCharWidth ())) / 2); + + _plettre->drawString(_name, nameX, nameY, 10, 0, true, *_vm->_draw->_backSurface); + + const int16 cursorLeft = nameX + _name.size() * _plettre->getCharWidth(); + const int16 cursorTop = nameY; + const int16 cursorRight = cursorLeft + _plettre->getCharWidth() - 1; + const int16 cursorBottom = cursorTop + _plettre->getCharHeight() - 1; + + _vm->_draw->_backSurface->fillRect(cursorLeft, cursorTop, cursorRight, cursorBottom, 10); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 147, 151, 243, 166); + } + + if ((key == kKeyReturn) && !_name.empty()) { + _name.trim(); + _name.setChar(Util::toCP850Upper(_name[0]), 0); + + state = kCharGenStateSure; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + } + + if (mouseButtons == kMouseButtonsLeft) { + stopSound(); + playSound(kSoundClick); + + int trousers = checkButton(kCharGenTrousersButtons, ARRAYSIZE(kCharGenTrousersButtons), mouseX, mouseY); + if ((state == kCharGenStateTrousers) && (trousers >= 0)) { + _colorTrousers = trousers; + + ani.recolor(0x09, _colorTrousers); + + state = kCharGenStateName; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + + int jacket = checkButton(kCharGenJacketButtons, ARRAYSIZE(kCharGenJacketButtons), mouseX, mouseY); + if ((state == kCharGenStateJacket) && (jacket >= 0)) { + _colorJacket = jacket; + + ani.recolor(0x0A, _colorJacket); + + state = kCharGenStateTrousers; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + + int hair = checkButton(kCharGenHairButtons, ARRAYSIZE(kCharGenHairButtons), mouseX, mouseY); + if ((state == kCharGenStateHair) && (hair >= 0)) { + _colorHair = hair; + + ani.recolor(0x0C, _colorHair); + + state = kCharGenStateJacket; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + + int head = checkButton(kCharGenHeadButtons, ARRAYSIZE(kCharGenHeadButtons), mouseX, mouseY); + if ((state == kCharGenStateHead) && (head >= 0)) { + _head = head; + + state = kCharGenStateHair; + charGenSetup(state); + _vm->_draw->forceBlit(); + } + } + + drawAnim(anims); + + // Play the child sounds + CharGenChild::Sound childSound = child->shouldPlaySound(); + if (childSound == CharGenChild::kSoundWalk) { + beep(50, 10); + } else if (childSound == CharGenChild::kSoundJump) { + stopSound(); + playSound(kSoundJump); + } + + showCursor(); + fadeIn(); + + endFrame(true); + } + + fadeOut(); + hideCursor(); + + freeAnims(anims); + + if (_vm->shouldQuit()) + return kCharGenAbort; + + return action; +} + +bool OnceUpon::sectionChapter1() { + showChapter(1); + return true; +} + +bool OnceUpon::sectionParents() { + fadeOut(); + setGamePalette(14); + clearScreen(); + + const Common::String seq = ((_house == 1) || (_house == 2)) ? "parents.seq" : "parents2.seq"; + const Common::String gct = getLocFile("mefait.gc"); + + Parents parents(_vm, seq, gct, _name, _house, *_plettre, kGamePalettes[14], kGamePalettes[13], kPaletteSize); + parents.play(); + + warning("OnceUpon::sectionParents(): TODO: Item search"); + return true; +} + +bool OnceUpon::sectionChapter2() { + showChapter(2); + return true; +} + +bool OnceUpon::sectionForest0() { + warning("OnceUpon::sectionForest0(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter3() { + showChapter(3); + return true; +} + +bool OnceUpon::sectionEvilCastle() { + warning("OnceUpon::sectionEvilCastle(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter4() { + showChapter(4); + return true; +} + +bool OnceUpon::sectionForest1() { + warning("OnceUpon::sectionForest1(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter5() { + showChapter(5); + return true; +} + +bool OnceUpon::sectionBossFight() { + warning("OnceUpon::sectionBossFight(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter6() { + showChapter(6); + return true; +} + +bool OnceUpon::sectionForest2() { + warning("OnceUpon::sectionForest2(): TODO"); + return true; +} + +bool OnceUpon::sectionChapter7() { + showChapter(7); + return true; +} + +const PreGob::AnimProperties OnceUpon::kSectionEndAnimations[] = { + { 0, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 6, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + { 9, 0, ANIObject::kModeContinuous, true, false, false, 0, 0}, + {11, 0, ANIObject::kModeContinuous, true, false, false, 0, 0} +}; + +bool OnceUpon::sectionEnd() { + fadeOut(); + setGamePalette(9); + + _vm->_video->drawPackedSprite("cadre.cmp", *_vm->_draw->_backSurface); + + Surface endBackground(320, 200, 1); + _vm->_video->drawPackedSprite("fin.cmp", endBackground); + + _vm->_draw->_backSurface->blit(endBackground, 0, 0, 288, 137, 16, 50); + + GCTFile *endText = loadGCT(getLocFile("final.gc")); + endText->setArea(17, 18, 303, 41); + endText->setText(1, _name); + + ANIFile ani(_vm, "fin.ani", 320); + ANIList anims; + + loadAnims(anims, ani, ARRAYSIZE(kSectionEndAnimations), kSectionEndAnimations); + drawAnim(anims); + + _vm->_draw->forceBlit(); + + uint32 textStartTime = 0; + + MenuAction action = kMenuActionNone; + while (!_vm->shouldQuit() && (action == kMenuActionNone)) { + // Check user input + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + int16 key = checkInput(mouseX, mouseY, mouseButtons); + + action = doIngameMenu(key, mouseButtons); + if (action != kMenuActionNone) + break; + + clearAnim(anims); + + // Pressed a key or mouse button => Skip to next area-full of text + if ((mouseButtons == kMouseButtonsLeft) || (key != 0)) + textStartTime = 0; + + // Draw the next area-full of text + uint32 now = _vm->_util->getTimeKey(); + if (!endText->finished() && ((textStartTime == 0) || (now >= (textStartTime + kGCTDelay)))) { + textStartTime = now; + + int16 left, top, right, bottom; + if (endText->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + if (endText->draw(*_vm->_draw->_backSurface, 0, *_plettre, 10, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + } + + drawAnim(anims); + fadeIn(); + + endFrame(true); + } + + freeAnims(anims); + delete endText; + + // Restart requested + if (action == kMenuActionRestart) + return false; + + // Last scene. Even if we didn't explicitly request a quit, the game ends here + _quit = true; + return false; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/onceupon.h b/engines/gob/pregob/onceupon/onceupon.h new file mode 100644 index 0000000000..66ef877618 --- /dev/null +++ b/engines/gob/pregob/onceupon/onceupon.h @@ -0,0 +1,344 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_ONCEUPON_H +#define GOB_PREGOB_ONCEUPON_ONCEUPON_H + +#include "common/system.h" +#include "common/str.h" + +#include "gob/pregob/pregob.h" + +#include "gob/pregob/onceupon/stork.h" + +namespace Gob { + +class Surface; +class Font; + +class ANIObject; + +namespace OnceUpon { + +class OnceUpon : public PreGob { +public: + /** Number of languages we support. */ + static const uint kLanguageCount = 5; + + + OnceUpon(GobEngine *vm); + ~OnceUpon(); + + +protected: + /** A description of a menu button. */ + struct MenuButton { + bool needDraw; ///< Does the button need drawing? + + int16 left; ///< Left coordinate of the button. + int16 top; ///< Top coordinate of the button. + int16 right; ///< Right coordinate of the button. + int16 bottom; ///< Bottom coordinate of the button. + + int16 srcLeft; ///< Left coordinate of the button's sprite. + int16 srcTop; ///< Top coordinate of the button's sprite. + int16 srcRight; ///< Right coordinate of the button's sprite. + int16 srcBottom; ///< Right coordinate of the button's sprite. + + int16 dstX; ///< Destination X coordinate of the button's sprite. + int16 dstY; ///< Destination Y coordinate of the button's sprite. + + uint id; ///< The button's ID. + }; + + /** Parameters for the stork section. */ + struct StorkParam { + const char *backdrop; ///< Backdrop image file. + + uint houseCount; ///< Number of houses. + const MenuButton *houses; ///< House button definitions. + + const Stork::BundleDrop *drops; ///< The bundle drop parameters. + }; + + void init(); + void deinit(); + + /** Handle the copy protection. + * + * @param colors Colors the copy protection animals can be. + * @param shapes The shape that's the correct answer for each animal in each color. + * @param obfuscate Extra obfuscate table. correctShape = shapes[colors][obfuscate[animal]]. + * @return true if the user guessed the correct shape, false otherwise. + */ + bool doCopyProtection(const uint8 colors[7], const uint8 shapes[7 * 20], const uint8 obfuscate[4]); + + /** Show the intro. */ + void showIntro(); + + /** Handle the start menu. + * + * @param animalsButton Definition of the menu button that leads to the animal names screen. Can be 0. + * @param animalCount Number of animals in the animal names screen. + * @param animalButtons Definition of the buttons that make up the animals in the animal names screen. + * @param animalNames File prefixes for the name of each animal. + */ + void doStartMenu(const MenuButton *animalsButton, uint animalCount, + const MenuButton *animalButtons, const char * const *animalNames); + + /** Play the game proper. */ + void playGame(); + + + /** Return the parameters for the stork section. */ + virtual const StorkParam &getStorkParameters() const = 0; + + +private: + /** All actions a user can request in a menu. */ + enum MenuAction { + kMenuActionNone = 0, ///< No action. + kMenuActionAnimals , ///< Do the animal names. + kMenuActionPlay , ///< Play the game. + kMenuActionRestart , ///< Restart the section. + kMenuActionMainMenu, ///< Go to the main menu. + kMenuActionQuit ///< Quit the game. + }; + + /** Difficulty levels. */ + enum Difficulty { + kDifficultyBeginner = 0, + kDifficultyIntermediate = 1, + kDifficultyAdvanced = 2, + kDifficultyCount + }; + + /** The different sounds common in the game. */ + enum Sound { + kSoundClick = 0, + kSoundStork , + kSoundJump , + kSoundCount + }; + + /** Action the character generation wants us to take. */ + enum CharGenAction { + kCharGenDone = 0, ///< Created a character, move on. + kCharGenAbort , ///< Aborted the character generation. + kCharGenRestart ///< Restart the character generation. + }; + + /** A complete screen backup. */ + struct ScreenBackup { + Surface *screen; ///< Screen contents. + int palette; ///< Screen palette. + + bool changedCursor; ///< Did we change the cursor? + bool cursorVisible; ///< Was the cursor visible? + + ScreenBackup(); + ~ScreenBackup(); + }; + + + /** The number of game sections. */ + static const int kSectionCount = 15; + + static const MenuButton kMainMenuDifficultyButton[]; ///< Difficulty buttons. + static const MenuButton kSectionButtons[]; ///< Section buttons. + + static const MenuButton kIngameButtons[]; ///< Ingame menu buttons. + + static const MenuButton kAnimalNamesBack; ///< "Back" button in the animal names screens. + static const MenuButton kLanguageButtons[]; ///< Language buttons in the animal names screen. + + static const MenuButton kSectionStorkHouses[]; + + static const MenuButton kCharGenHeadButtons[]; + static const MenuButton kCharGenHeads[]; + static const MenuButton kCharGenHairButtons[]; + static const MenuButton kCharGenJacketButtons[]; + static const MenuButton kCharGenTrousersButtons[]; + static const MenuButton kCharGenNameEntry[]; + + /** All general game sounds we know about. */ + static const char *kSound[kSoundCount]; + + + static const AnimProperties kClownAnimations[]; + static const AnimProperties kTitleAnimation; + static const AnimProperties kSectionStorkAnimations[]; + static const AnimProperties kSectionEndAnimations[]; + + + /** Function pointer type for a section handler. */ + typedef bool (OnceUpon::*SectionFunc)(); + /** Section handler function. */ + static const SectionFunc kSectionFuncs[kSectionCount]; + + + /** Did we open the game archives? */ + bool _openedArchives; + + // Fonts + Font *_jeudak; + Font *_lettre; + Font *_plettre; + Font *_glettre; + + /** The current palette. */ + int _palette; + + bool _quit; ///< Did the user request a normal game quit? + + Difficulty _difficulty; ///< The current difficulty. + int _section; ///< The current game section. + + Common::String _name; ///< The name of the child. + + uint8 _house; + + uint8 _head; + uint8 _colorHair; + uint8 _colorJacket; + uint8 _colorTrousers; + + + // -- General helpers -- + + void setGamePalette(uint palette); ///< Set a game palette. + void setGameCursor(); ///< Set the default game cursor. + + /** Draw this sprite in a fancy, animated line-by-line way. */ + void drawLineByLine(const Surface &src, int16 left, int16 top, int16 right, int16 bottom, + int16 x, int16 y) const; + + /** Backup the screen contents. */ + void backupScreen(ScreenBackup &backup, bool setDefaultCursor = false); + /** Restore the screen contents with a previously made backup. */ + void restoreScreen(ScreenBackup &backup); + + Common::String fixString(const Common::String &str) const; ///< Fix a string if necessary. + void fixTXTStrings(TXTFile &txt) const; ///< Fix all strings in a TXT. + + + // -- Copy protection helpers -- + + /** Set up the copy protection. */ + int8 cpSetup(const uint8 colors[7], const uint8 shapes[7 * 20], + const uint8 obfuscate[4], const Surface sprites[2]); + /** Find the shape under these coordinates. */ + int8 cpFindShape(int16 x, int16 y) const; + /** Display the "You are wrong" screen. */ + void cpWrong(); + + + // -- Show different game screens -- + + void showWait(uint palette = 0xFFFF); ///< Show the wait / loading screen. + void showQuote(); ///< Show the quote about fairytales. + void showTitle(); ///< Show the Once Upon A Time title. + void showChapter(int chapter); ///< Show a chapter intro text. + void showByeBye(); ///< Show the "bye bye" screen. + + /** Handle the "listen to animal names" part. */ + void handleAnimalNames(uint count, const MenuButton *buttons, const char * const *names); + + + // -- Menu helpers -- + + MenuAction handleStartMenu(const MenuButton *animalsButton); ///< Handle the start menu. + MenuAction handleMainMenu(); ///< Handle the main menu. + MenuAction handleIngameMenu(); ///< Handle the ingame menu. + + void drawStartMenu(const MenuButton *animalsButton); ///< Draw the start menu. + void drawMainMenu(); ///< Draw the main menu. + void drawIngameMenu(); ///< Draw the ingame menu. + + /** Draw the difficulty label. */ + void drawMenuDifficulty(); + + /** Clear the ingame menu in an animated way. */ + void clearIngameMenu(const Surface &background); + + /** Handle the whole ingame menu. */ + MenuAction doIngameMenu(); + /** Handle the whole ingame menu if ESC or right mouse button was pressed. */ + MenuAction doIngameMenu(int16 &key, MouseButtons &mouseButtons); + + + // -- Menu button helpers -- + + /** Find the button under these coordinates. */ + int checkButton(const MenuButton *buttons, uint count, int16 x, int16 y, int failValue = -1) const; + + /** Draw a menu button. */ + void drawButton (Surface &dest, const Surface &src, const MenuButton &button, int transp = -1) const; + /** Draw multiple menu buttons. */ + void drawButtons(Surface &dest, const Surface &src, const MenuButton *buttons, uint count, int transp = -1) const; + + /** Draw a border around a button. */ + void drawButtonBorder(const MenuButton &button, uint8 color); + + + // -- Animal names helpers -- + + /** Set up the animal chooser. */ + void anSetupChooser(); + /** Set up the language chooser for one animal. */ + void anSetupNames(const MenuButton &animal); + /** Play / Display the name of an animal in one language. */ + void anPlayAnimalName(const Common::String &animal, uint language); + + + // -- Game sections -- + + bool playSection(); + + bool sectionStork(); + bool sectionChapter1(); + bool sectionParents(); + bool sectionChapter2(); + bool sectionForest0(); + bool sectionChapter3(); + bool sectionEvilCastle(); + bool sectionChapter4(); + bool sectionForest1(); + bool sectionChapter5(); + bool sectionBossFight(); + bool sectionChapter6(); + bool sectionForest2(); + bool sectionChapter7(); + bool sectionEnd(); + + CharGenAction characterGenerator(); + void charGenSetup(uint stage); + void charGenDrawName(); + + static bool enterString(Common::String &name, int16 key, uint maxLength, const Font &font); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_ONCEUPON_H diff --git a/engines/gob/pregob/onceupon/palettes.h b/engines/gob/pregob/onceupon/palettes.h new file mode 100644 index 0000000000..952581041c --- /dev/null +++ b/engines/gob/pregob/onceupon/palettes.h @@ -0,0 +1,411 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_PALETTES_H +#define GOB_PREGOB_ONCEUPON_PALETTES_H + +static const int kPaletteSize = 16; +static const uint kPaletteCount = 20; + +static const byte kCopyProtectionPalette[3 * kPaletteSize] = { + 0x00, 0x00, 0x00, + 0x19, 0x00, 0x19, + 0x00, 0x3F, 0x00, + 0x00, 0x2A, 0x2A, + 0x2A, 0x00, 0x00, + 0x2A, 0x00, 0x2A, + 0x2A, 0x15, 0x00, + 0x00, 0x19, 0x12, + 0x00, 0x00, 0x00, + 0x15, 0x15, 0x3F, + 0x15, 0x3F, 0x15, + 0x00, 0x20, 0x3F, + 0x3F, 0x00, 0x00, + 0x3F, 0x00, 0x20, + 0x3F, 0x3F, 0x00, + 0x3F, 0x3F, 0x3F +}; + +static const byte kGamePalettes[kPaletteCount][3 * kPaletteSize] = { + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, + 0x00, 0x00, 0x18, + 0x00, 0x00, 0x3C, + 0x1C, 0x28, 0x00, + 0x10, 0x18, 0x00, + 0x1C, 0x1C, 0x20, + 0x14, 0x14, 0x14, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x14, 0x20, 0x04, + 0x3C, 0x2C, 0x00, + 0x02, 0x00, 0x18, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x38, 0x20, 0x3C, + 0x2C, 0x10, 0x30, + 0x20, 0x08, 0x28, + 0x14, 0x00, 0x1C, + 0x20, 0x20, 0x38, + 0x18, 0x18, 0x2C, + 0x10, 0x10, 0x24, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3C, 0x20, 0x20, + 0x24, 0x14, 0x14, + 0x1C, 0x10, 0x10, + 0x14, 0x0C, 0x0C, + 0x1C, 0x1C, 0x1C, + 0x18, 0x18, 0x18, + 0x10, 0x10, 0x10, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x10, 0x28, 0x1C, + 0x10, 0x1C, 0x10, + 0x10, 0x14, 0x0C, + 0x1C, 0x1C, 0x3C, + 0x24, 0x24, 0x3C, + 0x18, 0x18, 0x24, + 0x10, 0x10, 0x18, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3F, 0x26, 0x3F, + 0x36, 0x1C, 0x36, + 0x2C, 0x12, 0x2A, + 0x27, 0x0C, 0x24, + 0x22, 0x07, 0x1E, + 0x1D, 0x03, 0x18, + 0x16, 0x00, 0x10, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3A, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3F, 0x39, 0x26, + 0x38, 0x34, 0x1C, + 0x30, 0x2F, 0x13, + 0x27, 0x29, 0x0C, + 0x1D, 0x22, 0x07, + 0x14, 0x1B, 0x03, + 0x0C, 0x14, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3A, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x24, 0x3C, 0x3C, + 0x1C, 0x34, 0x38, + 0x14, 0x2C, 0x30, + 0x0C, 0x20, 0x2C, + 0x08, 0x18, 0x28, + 0x04, 0x10, 0x20, + 0x00, 0x08, 0x1C, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x24, + 0x38, 0x24, 0x1C, + 0x30, 0x1C, 0x14, + 0x28, 0x18, 0x0C, + 0x20, 0x10, 0x04, + 0x1C, 0x0C, 0x00, + 0x14, 0x08, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x3C, 0x34, 0x24, + 0x38, 0x2C, 0x1C, + 0x30, 0x24, 0x14, + 0x2C, 0x1C, 0x10, + 0x30, 0x30, 0x3C, + 0x1C, 0x1C, 0x38, + 0x0C, 0x0C, 0x38, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0C, + 0x02, 0x03, 0x14, + 0x07, 0x07, 0x1D, + 0x0E, 0x0E, 0x25, + 0x17, 0x17, 0x2E, + 0x21, 0x22, 0x36, + 0x2F, 0x2F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3B, 0x0D, + 0x3A, 0x31, 0x0A, + 0x35, 0x28, 0x07, + 0x30, 0x21, 0x04, + 0x2B, 0x19, 0x02, + 0x26, 0x12, 0x01, + 0x16, 0x0B, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, + 0x21, 0x01, 0x00, + 0x2A, 0x02, 0x00, + 0x33, 0x03, 0x00, + 0x3D, 0x06, 0x00, + 0x2A, 0x19, 0x05, + 0x15, 0x14, 0x14, + 0x22, 0x1F, 0x1E, + 0x2F, 0x2C, 0x28, + 0x3F, 0x3C, 0x29, + 0x3F, 0x38, 0x0B, + 0x3B, 0x30, 0x0A, + 0x37, 0x29, 0x08, + 0x33, 0x23, 0x07, + 0x2F, 0x1D, 0x06 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x1C, 0x38, + 0x34, 0x30, 0x28, + 0x2C, 0x24, 0x1C, + 0x24, 0x18, 0x10, + 0x1C, 0x10, 0x08, + 0x14, 0x04, 0x04, + 0x10, 0x00, 0x00, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x1C, 0x38, + 0x34, 0x30, 0x28, + 0x2C, 0x24, 0x1C, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x3F, 0x3F, 0x3F, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x1A, 0x30, 0x37, + 0x14, 0x28, 0x31, + 0x10, 0x20, 0x2C, + 0x0C, 0x19, 0x27, + 0x08, 0x12, 0x21, + 0x05, 0x0C, 0x1C, + 0x03, 0x07, 0x16, + 0x01, 0x03, 0x11, + 0x00, 0x00, 0x0C, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x34, 0x30, 0x34, + 0x30, 0x24, 0x30, + 0x28, 0x1C, 0x28, + 0x24, 0x14, 0x24, + 0x1C, 0x0C, 0x1C, + 0x18, 0x08, 0x18, + 0x14, 0x04, 0x14, + 0x0C, 0x04, 0x0C, + 0x08, 0x00, 0x08, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x2C, 0x24, 0x0C, + 0x34, 0x34, 0x28, + 0x2C, 0x2C, 0x1C, + 0x24, 0x24, 0x10, + 0x1C, 0x18, 0x08, + 0x14, 0x14, 0x08, + 0x10, 0x10, 0x04, + 0x0C, 0x0C, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x38, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + 0x14, 0x28, 0x31, + 0x10, 0x20, 0x2C, + 0x0C, 0x19, 0x27, + 0x08, 0x12, 0x21, + 0x05, 0x0C, 0x1C, + 0x03, 0x07, 0x16, + 0x01, 0x03, 0x11, + 0x00, 0x3C, 0x00, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x10, 0x28, 0x1C, + 0x10, 0x1C, 0x10, + 0x10, 0x14, 0x0C, + 0x1C, 0x1C, 0x3C, + 0x24, 0x24, 0x3C, + 0x18, 0x18, 0x24, + 0x10, 0x10, 0x18, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + }, + { + 0x00, 0x00, 0x00, + 0x10, 0x28, 0x1C, + 0x10, 0x1C, 0x10, + 0x10, 0x14, 0x0C, + 0x1C, 0x1C, 0x3C, + 0x24, 0x24, 0x3C, + 0x18, 0x18, 0x24, + 0x10, 0x10, 0x18, + 0x14, 0x20, 0x04, + 0x00, 0x00, 0x24, + 0x3C, 0x3C, 0x3C, + 0x00, 0x00, 0x00, + 0x3C, 0x2C, 0x00, + 0x3C, 0x18, 0x00, + 0x3C, 0x04, 0x00, + 0x1C, 0x00, 0x00 + } +}; + +#endif // GOB_PREGOB_ONCEUPON_PALETTES_H diff --git a/engines/gob/pregob/onceupon/parents.cpp b/engines/gob/pregob/onceupon/parents.cpp new file mode 100644 index 0000000000..cdaee6a38d --- /dev/null +++ b/engines/gob/pregob/onceupon/parents.cpp @@ -0,0 +1,217 @@ +/* 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 "gob/gob.h" +#include "gob/global.h" +#include "gob/dataio.h" +#include "gob/palanim.h" +#include "gob/draw.h" +#include "gob/video.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/gctfile.h" + +#include "gob/pregob/onceupon/palettes.h" +#include "gob/pregob/onceupon/parents.h" + +namespace Gob { + +namespace OnceUpon { + +const char *Parents::kSound[kSoundCount] = { + "rire.snd", // kSoundCackle + "tonn.snd" // kSoundThunder +}; + +// So that every GCT line is displayed for 12 seconds +const uint16 Parents::kLoop[kLoopCount][3] = { + { 72, 77, 33}, + {105, 109, 38}, + {141, 145, 38}, + {446, 454, 23}, + {456, 464, 23}, + {466, 474, 23}, + {476, 484, 23} +}; + + +Parents::Parents(GobEngine *vm, const Common::String &seq, const Common::String &gct, + const Common::String &childName, uint8 house, const Font &font, + const byte *normalPalette, const byte *brightPalette, uint paletteSize) : + SEQFile(vm, seq), + _gct(0), _house(house), _font(&font), + _paletteSize(paletteSize), _normalPalette(normalPalette), _brightPalette(brightPalette) { + + // Load sounds + for (int i = 0; i < kSoundCount; i++) + _vm->_sound->sampleLoad(&_sounds[i], SOUND_SND, kSound[i]); + + // Load GCT + Common::SeekableReadStream *gctStream = _vm->_dataIO->getFile(gct); + if (gctStream) { + _gct = new GCTFile(*gctStream, _vm->_rnd); + + delete gctStream; + } else + error("Parents::Parents(): Failed to open \"%s\"", gct.c_str()); + + _gct->setArea(17, 18, 303, 41); + _gct->setText(1, childName); + + _gct->selectLine(2, _house); + _gct->selectLine(4, _house); + + for (uint i = 0; i < kLoopCount; i++) + _loopID[i] = addLoop(kLoop[i][0], kLoop[i][1], kLoop[i][2]); +} + +Parents::~Parents() { + delete _gct; +} + +void Parents::play() { + _currentLoop = 0; + + SEQFile::play(true, 496, 15); + + // After playback, fade out + if (!_vm->shouldQuit()) + _vm->_palAnim->fade(0, 0, 0); +} + +void Parents::handleFrameEvent() { + switch (getFrame()) { + case 0: + // On fame 0, fade in + _vm->_draw->forceBlit(); + _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); + break; + + case 4: + drawGCT(0); + break; + + case 55: + drawGCT(3, 0); + break; + + case 79: + drawGCT(_house + 5, 1); + break; + + case 110: + drawGCT(_house + 9, 2); + break; + + case 146: + drawGCT(17); + break; + + case 198: + drawGCT(13); + break; + + case 445: + drawGCT(14, 3); + break; + + case 455: + drawGCT(18, 4); + break; + + case 465: + drawGCT(19, 5); + break; + + case 475: + drawGCT(20, 6); + break; + + case 188: + case 228: + case 237: + case 257: + case 275: + case 426: + lightningEffect(); + break; + + case 203: + case 243: + case 252: + case 272: + case 290: + case 441: + playSound(kSoundThunder); + break; + + case 340: + playSound(kSoundCackle); + break; + } +} + +void Parents::handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons) { + if ((key == kKeyEscape) || (mouseButtons == kMouseButtonsRight)) + abortPlay(); + + if (((key == kKeySpace) || (mouseButtons == kMouseButtonsLeft)) && (_currentLoop < kLoopCount)) + skipLoop(_loopID[_currentLoop]); +} + +void Parents::playSound(Sound sound) { + _vm->_sound->blasterStop(0); + _vm->_sound->blasterPlay(&_sounds[sound], 0, 0); +} + +void Parents::lightningEffect() { + for (int i = 0; (i < 5) && !_vm->shouldQuit(); i++) { + + setPalette(_brightPalette, _paletteSize); + _vm->_util->delay(5); + + setPalette(_normalPalette, _paletteSize); + _vm->_util->delay(5); + } +} + +void Parents::setPalette(const byte *palette, uint size) { + memcpy(_vm->_draw->_vgaPalette, palette, 3 * size); + + _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc); + _vm->_video->retrace(); +} + +void Parents::drawGCT(uint item, uint loop) { + int16 left, top, right, bottom; + if (_gct->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + if (_gct->draw(*_vm->_draw->_backSurface, item, *_font, 10, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + _currentLoop = loop; +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/parents.h b/engines/gob/pregob/onceupon/parents.h new file mode 100644 index 0000000000..f5c8307b73 --- /dev/null +++ b/engines/gob/pregob/onceupon/parents.h @@ -0,0 +1,94 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_PARENTS_H +#define GOB_PREGOB_ONCEUPON_PARENTS_H + +#include "gob/sound/sounddesc.h" + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +class Font; + +class GCTFile; + +namespace OnceUpon { + +/** The home / parents animation sequence. */ +class Parents : public SEQFile { +public: + Parents(GobEngine *vm, const Common::String &seq, const Common::String &gct, + const Common::String &childName, uint8 house, const Font &font, + const byte *normalPalette, const byte *brightPalette, uint paletteSize); + ~Parents(); + + void play(); + +protected: + void handleFrameEvent(); + void handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons); + +private: + static const uint kLoopCount = 7; + + static const uint16 kLoop[kLoopCount][3]; + + enum Sound { + kSoundCackle = 0, + kSoundThunder , + kSoundCount + }; + + static const char *kSound[kSoundCount]; + + + uint8 _house; + + const Font *_font; + + uint _paletteSize; + const byte *_normalPalette; + const byte *_brightPalette; + + SoundDesc _sounds[kSoundCount]; + + GCTFile *_gct; + + uint _loopID[kLoopCount]; + uint _currentLoop; + + + void lightningEffect(); + + void playSound(Sound sound); + void setPalette(const byte *palette, uint size); + + void drawGCT(uint item, uint loop = 0xFFFF); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_PARENTS_H diff --git a/engines/gob/pregob/onceupon/stork.cpp b/engines/gob/pregob/onceupon/stork.cpp new file mode 100644 index 0000000000..3c38037d08 --- /dev/null +++ b/engines/gob/pregob/onceupon/stork.cpp @@ -0,0 +1,234 @@ +/* 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/str.h" + +#include "gob/gob.h" +#include "gob/surface.h" +#include "gob/anifile.h" +#include "gob/video.h" + +#include "gob/pregob/onceupon/stork.h" + +enum Animation { + kAnimFlyNearWithBundle = 9, + kAnimFlyFarWithBundle = 12, + kAnimFlyNearWithoutBundle = 10, + kAnimFlyFarWithoutBundle = 13, + kAnimBundleNear = 11, + kAnimBundleFar = 14 +}; + +namespace Gob { + +namespace OnceUpon { + +Stork::Stork(GobEngine *vm, const ANIFile &ani) : ANIObject(ani), _shouldDrop(false) { + _frame = new Surface(320, 200, 1); + vm->_video->drawPackedSprite("cadre.cmp", *_frame); + + _bundle = new ANIObject(ani); + + _bundle->setVisible(false); + _bundle->setPause(true); + + setState(kStateFlyNearWithBundle, kAnimFlyNearWithBundle, -80); +} + +Stork::~Stork() { + delete _frame; + + delete _bundle; +} + +bool Stork::hasBundleLanded() const { + if (!_shouldDrop || !_bundle->isVisible() || _bundle->isPaused()) + return false; + + int16 x, y, width, height; + _bundle->getFramePosition(x, y); + _bundle->getFrameSize(width, height); + + return (y + height) >= _bundleDrop.landY; +} + +void Stork::dropBundle(const BundleDrop &drop) { + if (_shouldDrop) + return; + + _shouldDrop = true; + _bundleDrop = drop; +} + +bool Stork::draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + left = 0x7FFF; + top = 0x7FFF; + right = 0x0000; + bottom = 0x0000; + + bool drawn = ANIObject::draw(dest, left, top, right, bottom); + if (drawn) { + // Left frame edge + if (left <= 15) + dest.blit(*_frame, left, top, MIN<int16>(15, right), bottom, left, top); + + // Right frame edge + if (right >= 304) + dest.blit(*_frame, MAX<int16>(304, left), top, right, bottom, MAX<int16>(304, left), top); + } + + int16 bLeft, bTop, bRight, bBottom; + if (_bundle->draw(dest, bLeft, bTop, bRight, bBottom)) { + // Bottom frame edge + if (bBottom >= 188) + dest.blit(*_frame, bLeft, MAX<int16>(188, bTop), bRight, bBottom, bLeft, MAX<int16>(188, bTop)); + + left = MIN(left , bLeft ); + top = MIN(top , bTop ); + right = MAX(right , bRight ); + bottom = MAX(bottom, bBottom); + + drawn = true; + } + + return drawn; +} + +bool Stork::clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom) { + left = 0x7FFF; + top = 0x7FFF; + right = 0x0000; + bottom = 0x0000; + + bool cleared = _bundle->clear(dest, left, top, right, bottom); + + int16 sLeft, sTop, sRight, sBottom; + if (ANIObject::clear(dest, sLeft, sTop, sRight, sBottom)) { + left = MIN(left , sLeft ); + top = MIN(top , sTop ); + right = MAX(right , sRight ); + bottom = MAX(bottom, sBottom); + + cleared = true; + } + + return cleared; +} + +void Stork::advance() { + _bundle->advance(); + + ANIObject::advance(); + + int16 curX, curY, curWidth, curHeight; + getFramePosition(curX, curY, 0); + getFrameSize(curWidth, curHeight, 0); + + const int16 curRight = curX + curWidth - 1; + + int16 nextX, nextY, nextWidth, nextHeight; + getFramePosition(nextX, nextY, 1); + getFrameSize(nextWidth, nextHeight, 1); + + const int16 nextRight = nextX + nextWidth - 1; + + switch (_state) { + case kStateFlyNearWithBundle: + if (curX >= 330) + setState(kStateFlyFarWithBundle, kAnimFlyFarWithBundle, 330); + + if ((curRight <= _bundleDrop.dropX) && + (nextRight >= _bundleDrop.dropX) && _shouldDrop && !_bundleDrop.dropWhileFar) + dropBundle(kStateFlyNearWithoutBundle, kAnimFlyNearWithoutBundle); + + break; + + case kStateFlyFarWithBundle: + if (curX <= -80) + setState(kStateFlyNearWithBundle, kAnimFlyNearWithBundle, -80); + + if ((curX >= _bundleDrop.dropX) && + (nextX <= _bundleDrop.dropX) && _shouldDrop && _bundleDrop.dropWhileFar) + dropBundle(kStateFlyFarWithoutBundle, kAnimFlyFarWithoutBundle); + + break; + + case kStateFlyNearWithoutBundle: + if (curX >= 330) + setState(kStateFlyFarWithoutBundle, kAnimFlyFarWithoutBundle, 330); + break; + + case kStateFlyFarWithoutBundle: + if (curX <= -80) + setState(kStateFlyNearWithoutBundle, kAnimFlyNearWithoutBundle, -80); + break; + + default: + break; + } +} + +void Stork::dropBundle(State state, uint16 anim) { + setState(state, anim); + + int16 x, y, width, height; + getFramePosition(x, y); + getFrameSize(width, height); + + _bundle->setAnimation(_bundleDrop.anim); + _bundle->setPause(false); + _bundle->setVisible(true); + + int16 bWidth, bHeight; + _bundle->getFrameSize(bWidth, bHeight); + + // Drop position + x = _bundleDrop.dropX; + y = y + height - bHeight; + + // If the stork is flying near (from left to right), drop the bundle at the right edge + if (!_bundleDrop.dropWhileFar) + x = x - bWidth; + + _bundle->setPosition(x, y); +} + +void Stork::setState(State state, uint16 anim) { + setAnimation(anim); + setVisible(true); + setPause(false); + + _state = state; +} + +void Stork::setState(State state, uint16 anim, int16 x) { + setState(state, anim); + setPosition(); + + int16 pX, pY; + getPosition(pX, pY); + setPosition( x, pY); +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/stork.h b/engines/gob/pregob/onceupon/stork.h new file mode 100644 index 0000000000..756f5258c7 --- /dev/null +++ b/engines/gob/pregob/onceupon/stork.h @@ -0,0 +1,103 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_STORK_H +#define GOB_PREGOB_ONCEUPON_STORK_H + +#include "common/system.h" + +#include "gob/aniobject.h" + +namespace Common { + class String; +} + +namespace Gob { + +class GobEngine; + +class Surface; +class ANIFile; + +namespace OnceUpon { + +/** The stork in Baba Yaga / dragon in Abracadabra. */ +class Stork : public ANIObject { +public: + /** Information on how to drop the bundle. */ + struct BundleDrop { + int16 anim; ///< Animation of the bundle floating down + + int16 dropX; ///< X position the stork drops the bundle + int16 landY; ///< Y position the bundle lands + + bool dropWhileFar; ///< Does the stork drop the bundle while far instead of near? + }; + + Stork(GobEngine *vm, const ANIFile &ani); + ~Stork(); + + /** Has the bundle landed? */ + bool hasBundleLanded() const; + + /** Drop the bundle. */ + void dropBundle(const BundleDrop &drop); + + /** Draw the current frame onto the surface and return the affected rectangle. */ + bool draw(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + /** Draw the current frame from the surface and return the affected rectangle. */ + bool clear(Surface &dest, int16 &left, int16 &top, int16 &right, int16 &bottom); + + /** Advance the animation to the next frame. */ + void advance(); + +private: + enum State { + kStateFlyNearWithBundle = 0, + kStateFlyFarWithBundle , + kStateFlyNearWithoutBundle , + kStateFlyFarWithoutBundle + }; + + + GobEngine *_vm; + + Surface *_frame; + ANIObject *_bundle; + + State _state; + + bool _shouldDrop; + BundleDrop _bundleDrop; + + + void setState(State state, uint16 anim); + void setState(State state, uint16 anim, int16 x); + + void dropBundle(State state, uint16 anim); +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_STORK_H diff --git a/engines/gob/pregob/onceupon/title.cpp b/engines/gob/pregob/onceupon/title.cpp new file mode 100644 index 0000000000..5163ff6822 --- /dev/null +++ b/engines/gob/pregob/onceupon/title.cpp @@ -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. + * + */ + +#include "gob/gob.h" +#include "gob/global.h" +#include "gob/palanim.h" +#include "gob/draw.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/onceupon/title.h" + +namespace Gob { + +namespace OnceUpon { + +Title::Title(GobEngine *vm) : SEQFile(vm, "ville.seq") { +} + +Title::~Title() { +} + +void Title::play() { + SEQFile::play(true, 0xFFFF, 15); + + // After playback, fade out and stop the music + if (!_vm->shouldQuit()) + _vm->_palAnim->fade(0, 0, 0); + + stopMusic(); +} + +void Title::handleFrameEvent() { + // On fame 0, start the music and fade in + if (getFrame() == 0) { + playMusic(); + + _vm->_draw->forceBlit(); + _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); + } +} + +void Title::playMusic() { + // Look at what platform this is and play the appropriate music type + + if (_vm->getPlatform() == Common::kPlatformPC) + playMusicDOS(); + else if (_vm->getPlatform() == Common::kPlatformAmiga) + playMusicAmiga(); + else if (_vm->getPlatform() == Common::kPlatformAtariST) + playMusicAtariST(); +} + +void Title::playMusicDOS() { + // Play an AdLib track + + _vm->_sound->adlibLoadTBR("babayaga.tbr"); + _vm->_sound->adlibLoadMDY("babayaga.mdy"); + _vm->_sound->adlibSetRepeating(-1); + _vm->_sound->adlibPlay(); +} + +void Title::playMusicAmiga() { + // Play a Protracker track + + _vm->_sound->protrackerPlay("mod.babayaga"); +} + +void Title::playMusicAtariST() { + // Play a Soundblaster composition + + static const int16 titleMusic[21] = { 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, -1}; + static const char * const titleFiles[ 3] = {"baba1.snd", "baba2.snd", "baba3.snd"}; + + for (uint i = 0; i < ARRAYSIZE(titleFiles); i++) + _vm->_sound->sampleLoad(_vm->_sound->sampleGetBySlot(i), SOUND_SND, titleFiles[i]); + + _vm->_sound->blasterPlayComposition(titleMusic, 0); + _vm->_sound->blasterRepeatComposition(-1); +} + +void Title::stopMusic() { + // Just stop everything + + _vm->_sound->adlibSetRepeating(0); + _vm->_sound->blasterRepeatComposition(0); + + _vm->_sound->adlibStop(); + _vm->_sound->blasterStopComposition(); + _vm->_sound->protrackerStop(); + + for (int i = 0; i < ::Gob::Sound::kSoundsCount; i++) + _vm->_sound->sampleFree(_vm->_sound->sampleGetBySlot(i)); +} + +} // End of namespace OnceUpon + +} // End of namespace Gob diff --git a/engines/gob/pregob/onceupon/title.h b/engines/gob/pregob/onceupon/title.h new file mode 100644 index 0000000000..5e7ef76d40 --- /dev/null +++ b/engines/gob/pregob/onceupon/title.h @@ -0,0 +1,55 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_ONCEUPON_TITLE_H +#define GOB_PREGOB_ONCEUPON_TITLE_H + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +namespace OnceUpon { + +/** The Once Upon A Time title animation sequence. */ +class Title : public SEQFile { +public: + Title(GobEngine *vm); + ~Title(); + + void play(); + +protected: + void handleFrameEvent(); + +private: + void playMusic(); ///< Play the title music. + void playMusicDOS(); ///< Play the title music of the DOS version. + void playMusicAmiga(); ///< Play the title music of the Amiga version. + void playMusicAtariST(); ///< Play the title music of the Atari ST version. + void stopMusic(); ///< Stop the title music. +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_TITLE_H diff --git a/engines/gob/pregob/pregob.cpp b/engines/gob/pregob/pregob.cpp new file mode 100644 index 0000000000..54eb3c6795 --- /dev/null +++ b/engines/gob/pregob/pregob.cpp @@ -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. + * + */ + +#include "graphics/cursorman.h" + +#include "gob/gob.h" +#include "gob/global.h" +#include "gob/util.h" +#include "gob/surface.h" +#include "gob/dataio.h" +#include "gob/palanim.h" +#include "gob/draw.h" +#include "gob/video.h" +#include "gob/aniobject.h" + +#include "gob/sound/sound.h" + +#include "gob/pregob/pregob.h" +#include "gob/pregob/gctfile.h" + + +namespace Gob { + +const char PreGob::kLanguageSuffixShort[5] = { 't', 'g', 'a', 'e', 'i'}; +const char *PreGob::kLanguageSuffixLong [5] = {"fr", "al", "an", "it", "es"}; + + +PreGob::PreGob(GobEngine *vm) : _vm(vm), _fadedOut(false) { +} + +PreGob::~PreGob() { +} + +void PreGob::fadeOut() { + if (_fadedOut || _vm->shouldQuit()) + return; + + // Fade to black + _vm->_palAnim->fade(0, 0, 0); + + _fadedOut = true; +} + +void PreGob::fadeIn() { + if (!_fadedOut || _vm->shouldQuit()) + return; + + // Fade to palette + _vm->_draw->blitInvalidated(); + _vm->_palAnim->fade(_vm->_global->_pPaletteDesc, 0, 0); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); + + _fadedOut = false; +} + +void PreGob::clearScreen() { + _vm->_draw->_backSurface->clear(); + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); + _vm->_draw->blitInvalidated(); + _vm->_video->retrace(); +} + +void PreGob::initScreen() { + _vm->_util->setFrameRate(15); + + _fadedOut = true; + + _vm->_draw->initScreen(); + + _vm->_draw->_backSurface->clear(); + _vm->_util->clearPalette(); + + _vm->_draw->forceBlit(); + _vm->_video->retrace(); + + _vm->_util->processInput(); +} + +void PreGob::setPalette(const byte *palette, uint16 size) { + memcpy(_vm->_draw->_vgaPalette, palette, 3 * size); + + // If we didn't fade out prior, immediately set the palette + if (!_fadedOut) + _vm->_video->setFullPalette(_vm->_global->_pPaletteDesc); +} + +void PreGob::addCursor() { + CursorMan.pushCursor(0, 0, 0, 0, 0, 0); +} + +void PreGob::removeCursor() { + CursorMan.popCursor(); +} + +void PreGob::setCursor(Surface &sprite, int16 hotspotX, int16 hotspotY) { + CursorMan.replaceCursor(sprite.getData(), sprite.getWidth(), sprite.getHeight(), hotspotX, hotspotY, 0); +} + +void PreGob::setCursor(Surface &sprite, int16 left, int16 top, int16 right, int16 bottom, + int16 hotspotX, int16 hotspotY) { + + const int width = right - left + 1; + const int height = bottom - top + 1; + + if ((width <= 0) || (height <= 0)) + return; + + Surface cursor(width, height, 1); + + cursor.blit(sprite, left, top, right, bottom, 0, 0); + + setCursor(cursor, hotspotX, hotspotX); +} + +void PreGob::showCursor() { + CursorMan.showMouse(true); + + _vm->_draw->_showCursor = 4; +} + +void PreGob::hideCursor() { + CursorMan.showMouse(false); + + _vm->_draw->_showCursor = 0; +} + +bool PreGob::isCursorVisible() const { + return CursorMan.isVisible(); +} + +void PreGob::loadSounds(const char * const *sounds, uint soundCount) { + freeSounds(); + + _sounds.resize(soundCount); + + for (uint i = 0; i < soundCount; i++) + loadSound(_sounds[i], sounds[i]); +} + +void PreGob::freeSounds() { + _sounds.clear(); +} + +bool PreGob::loadSound(SoundDesc &sound, const Common::String &file) const { + return _vm->_sound->sampleLoad(&sound, SOUND_SND, file.c_str()); +} + +void PreGob::playSound(uint sound, int16 frequency, int16 repCount) { + if (sound >= _sounds.size()) + return; + + _vm->_sound->blasterPlay(&_sounds[sound], repCount, frequency); +} + +void PreGob::stopSound() { + _vm->_sound->blasterStop(0); +} + +void PreGob::playSoundFile(const Common::String &file, int16 frequency, int16 repCount, bool interruptible) { + stopSound(); + + SoundDesc sound; + if (!loadSound(sound, file)) + return; + + _vm->_sound->blasterPlay(&sound, repCount, frequency); + + _vm->_util->forceMouseUp(); + + bool finished = false; + while (!_vm->shouldQuit() && !finished && _vm->_sound->blasterPlayingSound()) { + endFrame(true); + + finished = hasInput(); + } + + _vm->_util->forceMouseUp(); + + stopSound(); +} + +void PreGob::beep(int16 frequency, int32 length) { + _vm->_sound->speakerOn(frequency, length); +} + +void PreGob::endFrame(bool doInput) { + _vm->_draw->blitInvalidated(); + _vm->_util->waitEndFrame(); + + if (doInput) + _vm->_util->processInput(); +} + +int16 PreGob::checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons) { + _vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons); + _vm->_util->forceMouseUp(); + + return _vm->_util->checkKey(); +} + +int16 PreGob::waitInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons) { + bool finished = false; + + int16 key = 0; + while (!_vm->shouldQuit() && !finished) { + endFrame(true); + + key = checkInput(mouseX, mouseY, mouseButtons); + + finished = (mouseButtons != kMouseButtonsNone) || (key != 0); + } + + return key; +} + +int16 PreGob::waitInput() { + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + return waitInput(mouseX, mouseY, mouseButtons); +} + +bool PreGob::hasInput() { + int16 mouseX, mouseY; + MouseButtons mouseButtons; + + return checkInput(mouseX, mouseY, mouseButtons) || (mouseButtons != kMouseButtonsNone); +} + +void PreGob::clearAnim(ANIObject &anim) { + int16 left, top, right, bottom; + + if (anim.clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); +} + +void PreGob::drawAnim(ANIObject &anim) { + int16 left, top, right, bottom; + + if (anim.draw(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + anim.advance(); +} + +void PreGob::redrawAnim(ANIObject &anim) { + clearAnim(anim); + drawAnim(anim); +} + +void PreGob::clearAnim(const ANIList &anims) { + for (int i = (anims.size() - 1); i >= 0; i--) + clearAnim(*anims[i]); +} + +void PreGob::drawAnim(const ANIList &anims) { + for (ANIList::const_iterator a = anims.begin(); a != anims.end(); ++a) + drawAnim(**a); +} + +void PreGob::redrawAnim(const ANIList &anims) { + clearAnim(anims); + drawAnim(anims); +} + +void PreGob::loadAnims(ANIList &anims, ANIFile &ani, uint count, const AnimProperties *props) const { + freeAnims(anims); + + anims.resize(count); + for (uint i = 0; i < count; i++) { + anims[i] = new ANIObject(ani); + + setAnim(*anims[i], props[i]); + } +} + +void PreGob::freeAnims(ANIList &anims) const { + for (ANIList::iterator a = anims.begin(); a != anims.end(); ++a) + delete *a; + + anims.clear(); +} + +void PreGob::setAnim(ANIObject &anim, const AnimProperties &props) const { + anim.setAnimation(props.animation); + anim.setFrame(props.frame); + anim.setMode(props.mode); + anim.setPause(props.paused); + anim.setVisible(props.visible); + + if (props.hasPosition) + anim.setPosition(props.x, props.y); + else + anim.setPosition(); +} + +Common::String PreGob::getLocFile(const Common::String &file) const { + if (_vm->_global->_language >= ARRAYSIZE(kLanguageSuffixShort)) + return file; + + return file + kLanguageSuffixShort[_vm->_global->_language]; +} + +TXTFile *PreGob::loadTXT(const Common::String &txtFile, TXTFile::Format format) const { + Common::SeekableReadStream *txtStream = _vm->_dataIO->getFile(txtFile); + if (!txtStream) + error("PreGob::loadTXT(): Failed to open \"%s\"", txtFile.c_str()); + + TXTFile *txt = new TXTFile(*txtStream, format); + + delete txtStream; + + fixTXTStrings(*txt); + + return txt; +} + +void PreGob::fixTXTStrings(TXTFile &txt) const { +} + +GCTFile *PreGob::loadGCT(const Common::String &gctFile) const { + Common::SeekableReadStream *gctStream = _vm->_dataIO->getFile(gctFile); + if (!gctStream) + error("PreGob::loadGCT(): Failed to open \"%s\"", gctFile.c_str()); + + GCTFile *gct = new GCTFile(*gctStream, _vm->_rnd); + + delete gctStream; + + return gct; +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/pregob.h b/engines/gob/pregob/pregob.h new file mode 100644 index 0000000000..632f85b88e --- /dev/null +++ b/engines/gob/pregob/pregob.h @@ -0,0 +1,194 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_PREGOB_H +#define GOB_PREGOB_PREGOB_H + +#include "common/str.h" +#include "common/array.h" + +#include "gob/util.h" +#include "gob/aniobject.h" + +#include "gob/sound/sounddesc.h" + +#include "gob/pregob/txtfile.h" + +namespace Gob { + +class GobEngine; +class Surface; + +class GCTFile; + +class PreGob { +public: + PreGob(GobEngine *vm); + virtual ~PreGob(); + + virtual void run() = 0; + + struct AnimProperties { + uint16 animation; + uint16 frame; + + ANIObject::Mode mode; + + bool visible; + bool paused; + + bool hasPosition; + int16 x; + int16 y; + }; + +protected: + typedef Common::Array<ANIObject *> ANIList; + + static const char kLanguageSuffixShort[5]; + static const char *kLanguageSuffixLong [5]; + + + GobEngine *_vm; + + + // -- Graphics -- + + /** Initialize the game screen. */ + void initScreen(); + + void fadeOut(); ///< Fade to black. + void fadeIn(); ///< Fade to the current palette. + + void clearScreen(); + + /** Change the palette. + * + * @param palette The palette to change to. + * @param size Size of the palette in colors. + */ + void setPalette(const byte *palette, uint16 size); ///< Change the palette + + /** Add a new cursor that can be manipulated to the stack. */ + void addCursor(); + /** Remove the top-most cursor from the stack. */ + void removeCursor(); + + /** Set the current cursor. */ + void setCursor(Surface &sprite, int16 hotspotX, int16 hotspotY); + /** Set the current cursor. */ + void setCursor(Surface &sprite, int16 left, int16 top, int16 right, int16 bottom, + int16 hotspotX, int16 hotspotY); + + /** Show the cursor. */ + void showCursor(); + /** Hide the cursor. */ + void hideCursor(); + + /** Is the cursor currently visible? */ + bool isCursorVisible() const; + + /** Remove an animation from the screen. */ + void clearAnim(ANIObject &anim); + /** Draw an animation to the screen, advancing it. */ + void drawAnim(ANIObject &anim); + /** Clear and draw an animation to the screen, advancing it. */ + void redrawAnim(ANIObject &anim); + + /** Remove animations from the screen. */ + void clearAnim(const ANIList &anims); + /** Draw animations to the screen, advancing them. */ + void drawAnim(const ANIList &anims); + /** Clear and draw animations to the screen, advancing them. */ + void redrawAnim(const ANIList &anims); + + void loadAnims(ANIList &anims, ANIFile &ani, uint count, const AnimProperties *props) const; + void freeAnims(ANIList &anims) const; + + void setAnim(ANIObject &anim, const AnimProperties &props) const; + + /** Wait for the frame to end, handling screen updates and optionally update input. */ + void endFrame(bool doInput); + + + // -- Sound -- + + /** Load all sounds that can be played interactively in the game. */ + void loadSounds(const char * const *sounds, uint soundCount); + /** Free all loaded sound. */ + void freeSounds(); + + /** Play a loaded sound. */ + void playSound(uint sound, int16 frequency = 0, int16 repCount = 0); + /** Stop all sound playback. */ + void stopSound(); + + /** Play a sound until it ends or is interrupted by a keypress. */ + void playSoundFile(const Common::String &file, int16 frequency = 0, int16 repCount = 0, bool interruptible = true); + + /** Beep the PC speaker. */ + void beep(int16 frequency, int32 length); + + + // -- Input -- + + /** Check mouse and keyboard input. */ + int16 checkInput(int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons); + /** Wait for mouse or keyboard input. */ + int16 waitInput (int16 &mouseX, int16 &mouseY, MouseButtons &mouseButtons); + /** Wait for mouse or keyboard input, but don't care about what was done with the mouse. */ + int16 waitInput(); + /** Did we have mouse or keyboard input? */ + bool hasInput(); + + + // -- TXT helpers -- + + /** Get the name of a localized file. */ + Common::String getLocFile(const Common::String &file) const; + /** Open a TXT file. */ + TXTFile *loadTXT(const Common::String &txtFile, TXTFile::Format format) const; + + /** Called by loadTXT() to fix strings within the TXT file. */ + virtual void fixTXTStrings(TXTFile &txt) const; + + + // -- GCT helpers -- + + GCTFile *loadGCT(const Common::String &gctFile) const; + + +private: + /** Did we fade out? */ + bool _fadedOut; + + /** All loaded sounds. */ + Common::Array<SoundDesc> _sounds; + + + /** Load a sound file. */ + bool loadSound(SoundDesc &sound, const Common::String &file) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_PREGOB_H diff --git a/engines/gob/pregob/seqfile.cpp b/engines/gob/pregob/seqfile.cpp new file mode 100644 index 0000000000..91973bbb85 --- /dev/null +++ b/engines/gob/pregob/seqfile.cpp @@ -0,0 +1,384 @@ +/* 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/str.h" +#include "common/stream.h" + +#include "gob/gob.h" +#include "gob/dataio.h" +#include "gob/draw.h" +#include "gob/decfile.h" +#include "gob/anifile.h" +#include "gob/aniobject.h" + +#include "gob/pregob/seqfile.h" + +namespace Gob { + +SEQFile::SEQFile(GobEngine *vm, const Common::String &fileName) : _vm(vm) { + for (uint i = 0; i < kObjectCount; i++) + _objects[i].object = 0; + + Common::SeekableReadStream *seq = _vm->_dataIO->getFile(Util::setExtension(fileName, ".SEQ")); + if (!seq) { + warning("SEQFile::SEQFile(): No such file \"%s\"", fileName.c_str()); + return; + } + + load(*seq); + + delete seq; +} + +SEQFile::~SEQFile() { + for (uint i = 0; i < kObjectCount; i++) + delete _objects[i].object; + + for (Backgrounds::iterator b = _backgrounds.begin(); b != _backgrounds.end(); ++b) + delete *b; + + for (Animations::iterator a = _animations.begin(); a != _animations.end(); ++a) + delete *a; +} + +void SEQFile::load(Common::SeekableReadStream &seq) { + const uint16 decCount = (uint16)seq.readByte() + 1; + const uint16 aniCount = (uint16)seq.readByte() + 1; + + // Load backgrounds + _backgrounds.reserve(decCount); + for (uint i = 0; i < decCount; i++) { + const Common::String dec = Util::readString(seq, 13); + + if (!_vm->_dataIO->hasFile(dec)) { + warning("SEQFile::load(): No such background \"%s\"", dec.c_str()); + return; + } + + _backgrounds.push_back(new DECFile(_vm, dec, 320, 200)); + } + + // Load animations + _animations.reserve(aniCount); + for (uint i = 0; i < aniCount; i++) { + const Common::String ani = Util::readString(seq, 13); + + if (!_vm->_dataIO->hasFile(ani)) { + warning("SEQFile::load(): No such animation \"%s\"", ani.c_str()); + return; + } + + _animations.push_back(new ANIFile(_vm, ani)); + } + + _frameRate = seq.readUint16LE(); + + // Load background change keys + + const uint16 bgKeyCount = seq.readUint16LE(); + _bgKeys.resize(bgKeyCount); + + for (uint16 i = 0; i < bgKeyCount; i++) { + const uint16 frame = seq.readUint16LE(); + const uint16 index = seq.readUint16LE(); + + _bgKeys[i].frame = frame; + _bgKeys[i].background = index < _backgrounds.size() ? _backgrounds[index] : 0; + } + + // Load animation keys for all 4 objects + + for (uint i = 0; i < kObjectCount; i++) { + const uint16 animKeyCount = seq.readUint16LE(); + _animKeys.reserve(_animKeys.size() + animKeyCount); + + for (uint16 j = 0; j < animKeyCount; j++) { + _animKeys.push_back(AnimationKey()); + + const uint16 frame = seq.readUint16LE(); + const uint16 index = seq.readUint16LE(); + + uint16 animation; + const ANIFile *ani = findANI(index, animation); + + _animKeys.back().object = i; + _animKeys.back().frame = frame; + _animKeys.back().ani = ani; + _animKeys.back().animation = animation; + _animKeys.back().x = seq.readSint16LE(); + _animKeys.back().y = seq.readSint16LE(); + _animKeys.back().order = seq.readSint16LE(); + } + } + +} + +const ANIFile *SEQFile::findANI(uint16 index, uint16 &animation) { + animation = 0xFFFF; + + // 0xFFFF = remove animation + if (index == 0xFFFF) + return 0; + + for (Animations::const_iterator a = _animations.begin(); a != _animations.end(); ++a) { + if (index < (*a)->getAnimationCount()) { + animation = index; + return *a; + } + + index -= (*a)->getAnimationCount(); + } + + return 0; +} + +void SEQFile::play(bool abortable, uint16 endFrame, uint16 frameRate) { + if (_bgKeys.empty() && _animKeys.empty()) + // Nothing to do + return; + + // Init + + _frame = 0; + _abortPlay = false; + + for (uint i = 0; i < kObjectCount; i++) { + delete _objects[i].object; + + _objects[i].object = 0; + _objects[i].order = 0; + } + + for (Loops::iterator l = _loops.begin(); l != _loops.end(); ++l) + l->currentLoop = 0; + + // Set the frame rate + + int16 frameRateBack = _vm->_util->getFrameRate(); + + if (frameRate == 0) + frameRate = _frameRate; + + _vm->_util->setFrameRate(frameRate); + + _abortable = abortable; + + while (!_vm->shouldQuit() && !_abortPlay) { + // Handle the frame contents + playFrame(); + + // Handle extra frame events + handleFrameEvent(); + + // Wait for the frame to end + _vm->_draw->blitInvalidated(); + _vm->_util->waitEndFrame(); + + // Handle input + + _vm->_util->processInput(); + + int16 key = _vm->_util->checkKey(); + + int16 mouseX, mouseY; + MouseButtons mouseButtons; + _vm->_util->getMouseState(&mouseX, &mouseY, &mouseButtons); + _vm->_util->forceMouseUp(); + + handleInput(key, mouseX, mouseY, mouseButtons); + + // Loop + + bool looped = false; + for (Loops::iterator l = _loops.begin(); l != _loops.end(); ++l) { + if ((l->endFrame == _frame) && (l->currentLoop < l->loopCount)) { + _frame = l->startFrame; + + l->currentLoop++; + looped = true; + } + } + + // If we didn't loop, advance the frame and look if we should end here + + if (!looped) { + _frame++; + if (_frame >= endFrame) + break; + } + } + + // Restore the frame rate + _vm->_util->setFrameRate(frameRateBack); +} + +void SEQFile::playFrame() { + // Remove the current animation frames + clearAnims(); + + // Handle background keys, directly updating the background + for (BackgroundKeys::const_iterator b = _bgKeys.begin(); b != _bgKeys.end(); ++b) { + if (!b->background || (b->frame != _frame)) + continue; + + b->background->draw(*_vm->_draw->_backSurface); + + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, 0, 0, 319, 199); + } + + // Handle the animation keys, updating the objects + for (AnimationKeys::const_iterator a = _animKeys.begin(); a != _animKeys.end(); ++a) { + if (a->frame != _frame) + continue; + + Object &object = _objects[a->object]; + + delete object.object; + object.object = 0; + + // No valid animation => remove + if ((a->animation == 0xFFFF) || !a->ani) + continue; + + // Change the animation + + object.object = new ANIObject(*a->ani); + + object.object->setAnimation(a->animation); + object.object->setPosition(a->x, a->y); + object.object->setVisible(true); + object.object->setPause(false); + + object.order = a->order; + } + + // Draw the animations + drawAnims(); +} + +// NOTE: This is really not at all efficient. However, since there's only a +// small number of objects, it should matter. We really do need a stable +// sort, though, so Common::sort() is out. +SEQFile::Objects SEQFile::getOrderedObjects() { + int16 minOrder = (int16)0x7FFF; + int16 maxOrder = (int16)0x8000; + + Objects objects; + + // Find the span of order values + for (uint i = 0; i < kObjectCount; i++) { + if (!_objects[i].object) + continue; + + minOrder = MIN(minOrder, _objects[i].order); + maxOrder = MAX(maxOrder, _objects[i].order); + } + + // Stably sort the objects by order value + for (int16 o = minOrder; o <= maxOrder; o++) + for (uint i = 0; i < kObjectCount; i++) + if (_objects[i].object && (_objects[i].order == o)) + objects.push_back(_objects[i]); + + return objects; +} + +void SEQFile::clearAnims() { + Objects objects = getOrderedObjects(); + + // Remove the animation frames, in reverse drawing order + for (Objects::iterator o = objects.reverse_begin(); o != objects.end(); --o) { + int16 left, top, right, bottom; + + if (o->object->clear(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + } +} + +void SEQFile::drawAnims() { + Objects objects = getOrderedObjects(); + + // Draw the animation frames and advance the animation + for (Objects::iterator o = objects.begin(); o != objects.end(); ++o) { + int16 left, top, right, bottom; + + if (o->object->draw(*_vm->_draw->_backSurface, left, top, right, bottom)) + _vm->_draw->dirtiedRect(_vm->_draw->_backSurface, left, top, right, bottom); + + o->object->advance(); + } +} + +uint16 SEQFile::getFrame() const { + return _frame; +} + +void SEQFile::seekFrame(uint16 frame) { + _frame = frame; +} + +uint SEQFile::addLoop(uint16 startFrame, uint16 endFrame, uint16 loopCount) { + _loops.resize(_loops.size() + 1); + + _loops.back().startFrame = startFrame; + _loops.back().endFrame = endFrame; + _loops.back().loopCount = loopCount; + _loops.back().currentLoop = 0; + _loops.back().empty = false; + + return _loops.size() - 1; +} + +void SEQFile::skipLoop(uint loopID) { + if (loopID >= _loops.size()) + return; + + _loops[loopID].currentLoop = 0xFFFF; +} + +void SEQFile::delLoop(uint loopID) { + if (loopID >= _loops.size()) + return; + + _loops[loopID].empty = true; + + cleanLoops(); +} + +void SEQFile::cleanLoops() { + while (!_loops.empty() && _loops.back().empty) + _loops.pop_back(); +} + +void SEQFile::abortPlay() { + _abortPlay = true; +} + +void SEQFile::handleFrameEvent() { +} + +void SEQFile::handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons) { + if (_abortable && ((key != 0) || (mouseButtons != kMouseButtonsNone))) + abortPlay(); +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/seqfile.h b/engines/gob/pregob/seqfile.h new file mode 100644 index 0000000000..5e12962ef9 --- /dev/null +++ b/engines/gob/pregob/seqfile.h @@ -0,0 +1,193 @@ +/* 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. + * + */ + +#ifndef GOB_PREGOB_SEQFILE_H +#define GOB_PREGOB_SEQFILE_H + +#include "common/system.h" +#include "common/array.h" +#include "common/list.h" + +#include "gob/util.h" + +namespace Common { + class String; + class SeekableReadStream; +} + +namespace Gob { + +class GobEngine; + +class DECFile; +class ANIFile; +class ANIObject; + +/** A SEQ file, describing a complex animation sequence. + * + * Used in early hardcoded gob games. + * The principle is similar to the Mult class (see mult.h), but instead + * of depending on all the externally loaded animations, backgrounds and + * objects, a SEQ file references animation and background directly by + * filename. + */ +class SEQFile { +public: + SEQFile(GobEngine *vm, const Common::String &fileName); + virtual ~SEQFile(); + + /** Play the SEQ. + * + * @param abortable If true, end playback on any user input. + * @param endFrame The frame on where to end, or 0xFFFF for infinite playback. + * @param frameRate The frame rate at which to play the SEQ, or 0 for playing at + * the speed the SEQ itself wants to. + */ + void play(bool abortable = true, uint16 endFrame = 0xFFFF, uint16 frameRate = 0); + + +protected: + GobEngine *_vm; + + + /** Returns the current frame number. */ + uint16 getFrame() const; + + /** Seek to a specific frame. */ + void seekFrame(uint16 frame); + + /** Add a frame loop. */ + uint addLoop(uint16 startFrame, uint16 endFrame, uint16 loopCount); + + /** Skip a frame loop. */ + void skipLoop(uint loopID); + + /** Delete a frame loop. */ + void delLoop(uint loopID); + + /** Ends SEQ playback. */ + void abortPlay(); + + /** Callback for special frame events. */ + virtual void handleFrameEvent(); + /** Callback for special user input handling. */ + virtual void handleInput(int16 key, int16 mouseX, int16 mouseY, MouseButtons mouseButtons); + + +private: + /** Number of animation objects that are visible at the same time. */ + static const uint kObjectCount = 4; + + /** A key for changing the background. */ + struct BackgroundKey { + uint16 frame; ///< Frame the change is to happen. + + const DECFile *background; ///< The background to use. + }; + + /** A key for playing an object animation. */ + struct AnimationKey { + uint object; ///< The object this key belongs to. + + uint16 frame; ///< Frame the change is to happen. + + const ANIFile *ani; ///< The ANI to use. + + uint16 animation; ///< The animation to use. + + int16 x; ///< X position of the animation. + int16 y; ///< Y position of the animation. + + int16 order; ///< Used to determine in which order to draw the objects. + }; + + /** A managed animation object. */ + struct Object { + ANIObject *object; ///< The actual animation object. + + int16 order; ///< The current drawing order. + }; + + /** A frame loop. */ + struct Loop { + uint16 startFrame; + uint16 endFrame; + + uint16 loopCount; + uint16 currentLoop; + + bool empty; + }; + + typedef Common::Array<DECFile *> Backgrounds; + typedef Common::Array<ANIFile *> Animations; + + typedef Common::Array<BackgroundKey> BackgroundKeys; + typedef Common::Array<AnimationKey> AnimationKeys; + + typedef Common::List<Object> Objects; + + typedef Common::Array<Loop> Loops; + + + uint16 _frame; ///< The current frame. + bool _abortPlay; ///< Was the end of the playback requested? + + uint16 _frameRate; + + Backgrounds _backgrounds; ///< All backgrounds in this SEQ. + Animations _animations; ///< All animations in this SEQ. + + BackgroundKeys _bgKeys; ///< The background change keyframes. + AnimationKeys _animKeys; ///< The animation change keyframes. + + Object _objects[kObjectCount]; ///< The managed animation objects. + + Loops _loops; + + /** Whether the playback should be abortable by user input. */ + bool _abortable; + + + // -- Loading helpers -- + + void load(Common::SeekableReadStream &seq); + + const ANIFile *findANI(uint16 index, uint16 &animation); + + // -- Playback helpers -- + + void playFrame(); + + /** Get a list of objects ordered by drawing order. */ + Objects getOrderedObjects(); + + void clearAnims(); ///< Remove all animation frames. + void drawAnims(); ///< Draw the animation frames. + + /** Look if we can compact the loop array. */ + void cleanLoops(); +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_SEQFILE_H diff --git a/engines/gob/pregob/txtfile.cpp b/engines/gob/pregob/txtfile.cpp new file mode 100644 index 0000000000..3ff0d4b039 --- /dev/null +++ b/engines/gob/pregob/txtfile.cpp @@ -0,0 +1,232 @@ +/* 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/stream.h" + +#include "gob/draw.h" + +#include "gob/pregob/txtfile.h" + +namespace Gob { + +TXTFile::TXTFile(Common::SeekableReadStream &txt, Format format) { + load(txt, format); +} + +TXTFile::~TXTFile() { +} + +TXTFile::LineArray &TXTFile::getLines() { + return _lines; +} + +void TXTFile::load(Common::SeekableReadStream &txt, Format format) { + if (format == kFormatStringPositionColorFont) { + int numLines = getInt(txt); + + _lines.reserve(numLines); + } + + while (!txt.eos()) { + Line line; + + line.text = getStr(txt); + line.x = (format >= kFormatStringPosition) ? getInt(txt) : 0; + line.y = (format >= kFormatStringPosition) ? getInt(txt) : 0; + line.color = (format >= kFormatStringPositionColor) ? getInt(txt) : 0; + line.font = (format >= kFormatStringPositionColorFont) ? getInt(txt) : 0; + + _lines.push_back(line); + } + + while (!_lines.empty() && _lines.back().text.empty()) + _lines.pop_back(); +} + +bool TXTFile::draw(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color) { + + trashBuffer(); + + if (!getArea(left, top, right, bottom, fonts, fontCount)) + return false; + + resizeBuffer(right - left + 1, bottom - top + 1); + saveScreen(surface, left, top, right, bottom); + + for (LineArray::const_iterator l = _lines.begin(); l != _lines.end(); ++l) { + if (l->font >= fontCount) + continue; + + fonts[l->font]->drawString(l->text, l->x, l->y, (color < 0) ? l->color : color, 0, true, surface); + } + + return true; +} + +bool TXTFile::draw(uint line, Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color) { + + trashBuffer(); + + if (!getArea(line, left, top, right, bottom, fonts, fontCount)) + return false; + + resizeBuffer(right - left + 1, bottom - top + 1); + saveScreen(surface, left, top, right, bottom); + + const Line &l = _lines[line]; + + fonts[l.font]->drawString(l.text, l.x, l.y, (color < 0) ? l.color : color, 0, true, surface); + + return true; +} + +bool TXTFile::draw(Surface &surface, const Font * const *fonts, uint fontCount, int color) { + int16 left, top, right, bottom; + + return draw(surface, left, top, right, bottom, fonts, fontCount, color); +} + +bool TXTFile::draw(uint line, Surface &surface, const Font * const *fonts, uint fontCount, int color) { + int16 left, top, right, bottom; + + return draw(line, surface, left, top, right, bottom, fonts, fontCount, color); +} + +bool TXTFile::clear(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom) { + return restoreScreen(surface, left, top, right, bottom); +} + +bool TXTFile::getArea(int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const { + + bool hasLine = false; + + left = 0x7FFF; + top = 0x7FFF; + right = 0x0000; + bottom = 0x0000; + + for (uint i = 0; i < _lines.size(); i++) { + int16 lLeft, lTop, lRight, lBottom; + + if (getArea(i, lLeft, lTop, lRight, lBottom, fonts, fontCount)) { + left = MIN(left , lLeft ); + top = MIN(top , lTop ); + right = MAX(right , lRight ); + bottom = MAX(bottom, lBottom); + + hasLine = true; + } + } + + return hasLine; +} + +bool TXTFile::getArea(uint line, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const { + + + if ((line >= _lines.size()) || (_lines[line].font >= fontCount)) + return false; + + const Line &l = _lines[line]; + + left = l.x; + top = l.y; + right = l.x + l.text.size() * fonts[l.font]->getCharWidth() - 1; + bottom = l.y + fonts[l.font]->getCharHeight() - 1; + + return true; +} + +Common::String TXTFile::getStr(Common::SeekableReadStream &txt) { + // Skip all ' ', '\n' and '\r' + while (!txt.eos()) { + char c = txt.readByte(); + if (txt.eos()) + break; + + if ((c != ' ') && (c != '\n') && (c != '\r')) { + txt.seek(-1, SEEK_CUR); + break; + } + } + + if (txt.eos()) + return ""; + + // Read string until ' ', '\n' or '\r' + Common::String string; + while (!txt.eos()) { + char c = txt.readByte(); + if ((c == ' ') || (c == '\n') || (c == '\r')) + break; + + string += c; + } + + // Replace all '#' with ' ' and throw out non-printables + Common::String cleanString; + + for (uint i = 0; i < string.size(); i++) { + if (string[i] == '#') + cleanString += ' '; + else if ((unsigned char)string[i] >= ' ') + cleanString += string[i]; + } + + return cleanString; +} + +int TXTFile::getInt(Common::SeekableReadStream &txt) { + // Skip all [^-0-9] + while (!txt.eos()) { + char c = txt.readByte(); + if (txt.eos()) + break; + + if ((c == '-') || ((c >= '0') && (c <= '9'))) { + txt.seek(-1, SEEK_CUR); + break; + } + } + + if (txt.eos()) + return 0; + + // Read until [^-0-9] + Common::String string; + while (!txt.eos()) { + char c = txt.readByte(); + if ((c != '-') && ((c < '0') || (c > '9'))) + break; + + string += c; + } + + // Convert to integer + return atoi(string.c_str()); +} + +} // End of namespace Gob diff --git a/engines/gob/pregob/txtfile.h b/engines/gob/pregob/txtfile.h new file mode 100644 index 0000000000..c623b58859 --- /dev/null +++ b/engines/gob/pregob/txtfile.h @@ -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. + * + */ + +#ifndef GOB_PREGOB_TXTFILE_H +#define GOB_PREGOB_TXTFILE_H + +#include "common/system.h" +#include "common/str.h" +#include "common/array.h" + +#include "gob/backbuffer.h" + +namespace Common { + class SeekableReadStream; +} + +namespace Gob { + +class Surface; +class Font; + +class TXTFile : public BackBuffer { +public: + enum Format { + kFormatString, + kFormatStringPosition, + kFormatStringPositionColor, + kFormatStringPositionColorFont + }; + + struct Line { + Common::String text; + int x, y; + int color; + uint font; + }; + + typedef Common::Array<Line> LineArray; + + TXTFile(Common::SeekableReadStream &txt, Format format); + ~TXTFile(); + + LineArray &getLines(); + + bool draw( Surface &surface, const Font * const *fonts, uint fontCount, int color = -1); + bool draw(uint line, Surface &surface, const Font * const *fonts, uint fontCount, int color = -1); + + bool draw( Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color = -1); + bool draw(uint line, Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount, int color = -1); + + bool clear(Surface &surface, int16 &left, int16 &top, int16 &right, int16 &bottom); + +private: + LineArray _lines; + + void load(Common::SeekableReadStream &txt, Format format); + + Common::String getStr(Common::SeekableReadStream &txt); + int getInt(Common::SeekableReadStream &txt); + + + bool getArea( int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const; + bool getArea(uint line, int16 &left, int16 &top, int16 &right, int16 &bottom, + const Font * const *fonts, uint fontCount) const; +}; + +} // End of namespace Gob + +#endif // GOB_PREGOB_TXTFILE_H |