From 0b030dd341b00007b969805ff6d488a51a1a97c7 Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Thu, 5 Jul 2012 11:00:26 +0200 Subject: GOB: Implement parts of the Stork section in Once Upon A Time No GCT texts yet ("The stork is bringing a sweet baby to $PLACE where $PEOPLE live"), and the character creator is also still missing. --- engines/gob/module.mk | 1 + engines/gob/pregob/onceupon/abracadabra.cpp | 25 +++ engines/gob/pregob/onceupon/abracadabra.h | 8 + engines/gob/pregob/onceupon/babayaga.cpp | 25 +++ engines/gob/pregob/onceupon/babayaga.h | 8 + engines/gob/pregob/onceupon/brokenstrings.h | 3 +- engines/gob/pregob/onceupon/onceupon.cpp | 124 ++++++++++++++- engines/gob/pregob/onceupon/onceupon.h | 19 +++ engines/gob/pregob/onceupon/stork.cpp | 234 ++++++++++++++++++++++++++++ engines/gob/pregob/onceupon/stork.h | 103 ++++++++++++ 10 files changed, 547 insertions(+), 3 deletions(-) create mode 100644 engines/gob/pregob/onceupon/stork.cpp create mode 100644 engines/gob/pregob/onceupon/stork.h diff --git a/engines/gob/module.mk b/engines/gob/module.mk index f8b477b92b..1b3f400684 100644 --- a/engines/gob/module.mk +++ b/engines/gob/module.mk @@ -83,6 +83,7 @@ MODULE_OBJS := \ pregob/onceupon/onceupon.o \ pregob/onceupon/abracadabra.o \ pregob/onceupon/babayaga.o \ + pregob/onceupon/stork.o \ minigames/geisha/evilfish.o \ minigames/geisha/oko.o \ minigames/geisha/meter.o \ diff --git a/engines/gob/pregob/onceupon/abracadabra.cpp b/engines/gob/pregob/onceupon/abracadabra.cpp index 2e4b5f21e7..0d644d9056 100644 --- a/engines/gob/pregob/onceupon/abracadabra.cpp +++ b/engines/gob/pregob/onceupon/abracadabra.cpp @@ -78,6 +78,27 @@ const char *Abracadabra::kAnimalNames[] = { "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}, + {false, 61, 123, 96, 149, 0, 0, 0, 0, 0, 0, 1}, + {false, 199, 118, 226, 137, 0, 0, 0, 0, 0, 0, 2}, + {false, 229, 91, 304, 188, 0, 0, 0, 0, 0, 0, 3} +}; + +// 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) { } @@ -107,6 +128,10 @@ void Abracadabra::run() { 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 index f2075fc750..8048213f5f 100644 --- a/engines/gob/pregob/onceupon/abracadabra.h +++ b/engines/gob/pregob/onceupon/abracadabra.h @@ -36,6 +36,9 @@ public: void run(); +protected: + const StorkParam &getStorkParameters() const; + private: /** Definition of the menu button that leads to the animal names screen. */ static const MenuButton kAnimalsButtons; @@ -44,6 +47,11 @@ private: 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 diff --git a/engines/gob/pregob/onceupon/babayaga.cpp b/engines/gob/pregob/onceupon/babayaga.cpp index 6f27f469e3..6aa60310b5 100644 --- a/engines/gob/pregob/onceupon/babayaga.cpp +++ b/engines/gob/pregob/onceupon/babayaga.cpp @@ -78,6 +78,27 @@ const char *BabaYaga::kAnimalNames[] = { "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}, + {false, 61, 123, 96, 149, 0, 0, 0, 0, 0, 0, 1}, + {false, 199, 118, 226, 137, 0, 0, 0, 0, 0, 0, 2}, + {false, 229, 91, 304, 188, 0, 0, 0, 0, 0, 0, 3} +}; + +// 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) { } @@ -107,6 +128,10 @@ void BabaYaga::run() { 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 index 181b6f4d54..0241f78f4e 100644 --- a/engines/gob/pregob/onceupon/babayaga.h +++ b/engines/gob/pregob/onceupon/babayaga.h @@ -36,6 +36,9 @@ public: void run(); +protected: + const StorkParam &getStorkParameters() const; + private: /** Definition of the menu button that leads to the animal names screen. */ static const MenuButton kAnimalsButtons; @@ -44,6 +47,11 @@ private: 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 diff --git a/engines/gob/pregob/onceupon/brokenstrings.h b/engines/gob/pregob/onceupon/brokenstrings.h index 0a40493cc1..89acb1c6bd 100644 --- a/engines/gob/pregob/onceupon/brokenstrings.h +++ b/engines/gob/pregob/onceupon/brokenstrings.h @@ -45,7 +45,8 @@ static const BrokenString kBrokenStringsGerman[] = { { "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!" } + { "tipp auf ESC!" , "dr\201cke ESC!" }, + { "Wohin fliegt der Drachen?" , "Wohin fliegt der Drache?" } }; static const BrokenStringLanguage kBrokenStrings[kLanguageCount] = { diff --git a/engines/gob/pregob/onceupon/onceupon.cpp b/engines/gob/pregob/onceupon/onceupon.cpp index aed1b45e02..aa08f6f1fd 100644 --- a/engines/gob/pregob/onceupon/onceupon.cpp +++ b/engines/gob/pregob/onceupon/onceupon.cpp @@ -128,7 +128,8 @@ const OnceUpon::MenuButton OnceUpon::kLanguageButtons[] = { }; const char *OnceUpon::kSound[kSoundMAX] = { - "diamant.snd" + "diamant.snd", // kSoundClick + "cigogne.snd" // kSoundStork }; const OnceUpon::SectionFunc OnceUpon::kSectionFuncs[kSectionCount] = { @@ -1293,9 +1294,128 @@ bool OnceUpon::playSection() { 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() { warning("OnceUpon::sectionStork(): TODO"); - return true; + + 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); + + 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)) { + clearAnim(anims); + + // 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; + } + + if (mouseButtons == kMouseButtonsLeft) { + stopSound(); + playSound(kSoundClick); + + int house = checkButton(param.houses, param.houseCount, mouseX, mouseY); + if ((state == kStorkStateWaitUser) && (house >= 0)) { + + 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); + } + } + + drawAnim(anims); + showCursor(); + fadeIn(); + + endFrame(true); + } + + freeAnims(anims); + delete whereStork; + + fadeOut(); + hideCursor(); + + // Completed the section => move one + if (action == kMenuActionNone) + return true; + + // Didn't complete the section + return false; } bool OnceUpon::sectionChapter1() { diff --git a/engines/gob/pregob/onceupon/onceupon.h b/engines/gob/pregob/onceupon/onceupon.h index caaf155d06..b9aef04dc2 100644 --- a/engines/gob/pregob/onceupon/onceupon.h +++ b/engines/gob/pregob/onceupon/onceupon.h @@ -27,6 +27,8 @@ #include "gob/pregob/pregob.h" +#include "gob/pregob/onceupon/stork.h" + namespace Gob { class Surface; @@ -67,6 +69,15 @@ protected: 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(); @@ -97,6 +108,10 @@ protected: 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 { @@ -119,6 +134,7 @@ private: /** The different sounds common in the game. */ enum Sound { kSoundClick = 0, + kSoundStork , kSoundMAX }; @@ -146,12 +162,15 @@ private: 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[]; + /** All general game sounds we know about. */ static const char *kSound[kSoundMAX]; static const AnimProperties kClownAnimations[]; static const AnimProperties kTitleAnimation; + static const AnimProperties kSectionStorkAnimations[]; static const AnimProperties kSectionEndAnimations[]; 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(15, right), bottom, left, top); + + // Right frame edge + if (right >= 304) + dest.blit(*_frame, MAX(304, left), top, right, bottom, MAX(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(188, bTop), bRight, bBottom, bLeft, MAX(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..d26a887c97 --- /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 + }; + + + void setState(State state, uint16 anim); + void setState(State state, uint16 anim, int16 x); + + void dropBundle(State state, uint16 anim); + + + GobEngine *_vm; + + Surface *_frame; + ANIObject *_bundle; + + State _state; + + bool _shouldDrop; + BundleDrop _bundleDrop; +}; + +} // End of namespace OnceUpon + +} // End of namespace Gob + +#endif // GOB_PREGOB_ONCEUPON_STORK_H -- cgit v1.2.3