Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

Room.cpp

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004 Ivo Danihelka (ivo@danihelka.net)
00003  *
00004  * This program is free software; you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License as published by
00006  * the Free Software Foundation; either version 2 of the License, or
00007  * (at your option) any later version.
00008  */
00009 #include "Room.h"
00010 
00011 #include "WavyPicture.h"
00012 #include "Field.h"
00013 #include "FinderAlg.h"
00014 #include "ResSoundPack.h"
00015 #include "Controls.h"
00016 #include "PhaseLocker.h"
00017 #include "Planner.h"
00018 #include "View.h"
00019 
00020 #include "Log.h"
00021 #include "Rules.h"
00022 #include "LogicException.h"
00023 #include "LoadException.h"
00024 #include "Unit.h"
00025 #include "TimerAgent.h"
00026 #include "SubTitleAgent.h"
00027 #include "DialogStack.h"
00028 #include "SoundAgent.h"
00029 #include "OptionAgent.h"
00030 #include "ModelList.h"
00031 #include "Landslip.h"
00032 #include "MouseStroke.h"
00033 #include "MouseControl.h"
00034 
00035 #include <assert.h>
00036 
00037 //-----------------------------------------------------------------
00038 /**
00039  * Create room holder.
00040  * @param w room width
00041  * @param h room height
00042  * @param picture room background
00043  * @param locker shared locker for anim
00044  * @param levelScript shared planner to interrupt
00045  */
00046 Room::Room(int w, int h, const Path &picture,
00047         PhaseLocker *locker, Planner *levelScript)
00048 {
00049     m_locker = locker;
00050     m_levelScript = levelScript;
00051     m_fastFalling = false;
00052     m_bg = new WavyPicture(picture, V2(0, 0));
00053     m_field = new Field(w, h);
00054     m_finder = new FinderAlg(w, h);
00055     m_controls = new Controls(m_locker);
00056     m_view = new View(ModelList(&m_models));
00057     m_lastAction = Cube::ACTION_NO;
00058     m_soundPack = new ResSoundPack();
00059     m_startTime = TimerAgent::agent()->getCycles();
00060 }
00061 //-----------------------------------------------------------------
00062 /**
00063  * Delete field and models.
00064  */
00065 Room::~Room()
00066 {
00067     m_soundPack->removeAll();
00068     delete m_soundPack;
00069     m_levelScript->killPlan();
00070     m_levelScript->dialogs()->removeAll();
00071     SubTitleAgent::agent()->removeAll();
00072     delete m_controls;
00073     delete m_view;
00074 
00075     //NOTE: models must be removed before field because they unmask self
00076     Cube::t_models::iterator end = m_models.end();
00077     for (Cube::t_models::iterator i = m_models.begin(); i != end; ++i) {
00078         delete (*i);
00079     }
00080 
00081     delete m_finder;
00082     delete m_field;
00083     delete m_bg;
00084 }
00085 //-----------------------------------------------------------------
00086 /**
00087  * Set waves on background.
00088  */
00089     void
00090 Room::setWaves(float amplitude, float periode, float speed)
00091 {
00092     m_bg->setWamp(amplitude);
00093     m_bg->setWperiode(periode);
00094     m_bg->setWspeed(speed);
00095 }
00096 //-----------------------------------------------------------------
00097     void
00098 Room::addDecor(Decor *new_decor)
00099 {
00100     m_view->addDecor(new_decor);
00101 }
00102 //-----------------------------------------------------------------
00103 /**
00104  * Add model at scene.
00105  * @param new_model new object
00106  * @param new_unit driver for the object or NULL
00107  * @return model index
00108  */
00109     int
00110 Room::addModel(Cube *new_model, Unit *new_unit)
00111 {
00112     new_model->rules()->takeField(m_field);
00113     m_models.push_back(new_model);
00114 
00115     if (new_unit) {
00116         new_unit->takeModel(new_model);
00117         m_controls->addUnit(new_unit);
00118     }
00119 
00120     int model_index = m_models.size() - 1;
00121     new_model->setIndex(model_index);
00122     return model_index;
00123 }
00124 //-----------------------------------------------------------------
00125 /**
00126  * Return model at index.
00127  * @throws LogicException when model_index is out of range
00128  */
00129     Cube *
00130 Room::getModel(int model_index)
00131 {
00132     Cube *result = NULL;
00133     if (0 <= model_index && model_index < (int)m_models.size()) {
00134         result = m_models[model_index];
00135     }
00136     else {
00137         throw LogicException(ExInfo("bad model index")
00138                 .addInfo("model_index", model_index));
00139     }
00140 
00141     return result;
00142 }
00143 //-----------------------------------------------------------------
00144 /**
00145  * Return model at location.
00146  */
00147     Cube *
00148 Room::askField(const V2 &loc)
00149 {
00150     return m_field->getModel(loc);
00151 }
00152 //-----------------------------------------------------------------
00153 /**
00154  * Update all models.
00155  * Prepare new move, let models fall, let models drive, release old position.
00156  */
00157     void
00158 Room::nextRound(const InputProvider *input)
00159 {
00160     if (m_fastFalling) {
00161         while (beginFall()) {
00162             finishRound();
00163         }
00164     }
00165     else {
00166         beginFall();
00167     }
00168 
00169     if (isFresh()) {
00170         if (m_controls->driving(input)) {
00171             m_lastAction = Cube::ACTION_MOVE;
00172         }
00173         else {
00174             MouseControl rat(m_controls, m_view, m_finder);
00175             if (rat.mouseDrive(input)) {
00176                 m_lastAction = Cube::ACTION_MOVE;
00177             }
00178         }
00179     }
00180     finishRound();
00181 }
00182 //-----------------------------------------------------------------
00183 /**
00184  * Play sound like some object has fall.
00185  * NOTE: only one sound is played even more objects have fall
00186  */
00187     void
00188 Room::playImpact(Cube::eWeight impact)
00189 {
00190     switch (impact) {
00191         case Cube::NONE:
00192             break;
00193         case Cube::LIGHT:
00194             playSound("impact_light", 50);
00195             break;
00196         case Cube::HEAVY:
00197             playSound("impact_heavy", 50);
00198             break;
00199         default:
00200             assert(!"unknown impact weight");
00201     }
00202 }
00203 //-----------------------------------------------------------------
00204 /**
00205  * Play sound like a fish die.
00206  * @param model fresh dead fish
00207  */
00208     void
00209 Room::playDead(Cube *model)
00210 {
00211     m_levelScript->dialogs()->killSound(model->getIndex());
00212     switch (model->getPower()) {
00213         case Cube::LIGHT:
00214             playSound("dead_small");
00215             break;
00216         case Cube::HEAVY:
00217             playSound("dead_big");
00218             break;
00219         default:
00220             LOG_WARNING(ExInfo("curious power of dead fish")
00221                     .addInfo("power", model->getPower()));
00222             break;
00223     }
00224 }
00225 //-----------------------------------------------------------------
00226 /**
00227  * Move all models to new position
00228  * and check dead fihes.
00229  */
00230     void
00231 Room::prepareRound()
00232 {
00233     bool interrupt = false;
00234 
00235     //NOTE: we must call this functions sequential for all objects
00236     Cube::t_models::iterator end = m_models.end();
00237     for (Cube::t_models::iterator i = m_models.begin(); i != end; ++i) {
00238         (*i)->rules()->occupyNewPos();
00239     }
00240     for (Cube::t_models::iterator j = m_models.begin(); j != end; ++j) {
00241         bool die = (*j)->rules()->checkDead(m_lastAction);
00242         interrupt |= die;
00243         if (die) {
00244             playDead(*j);
00245         }
00246     }
00247     for (Cube::t_models::iterator l = m_models.begin(); l != end; ++l) {
00248         (*l)->rules()->changeState();
00249     }
00250 
00251     if (interrupt) {
00252         m_levelScript->interruptPlan();
00253     }
00254 }
00255 //-----------------------------------------------------------------
00256 /**
00257  * Let models to go out of screen.
00258  * @param interactive whether do anim
00259  * @return true when a model went out
00260  */
00261     bool
00262 Room::fallout(bool interactive)
00263 {
00264     bool wentOut = false;
00265     Cube::t_models::iterator end = m_models.end();
00266     for (Cube::t_models::iterator i = m_models.begin(); i != end; ++i) {
00267         if (!(*i)->isLost()) {
00268             int outDepth = (*i)->rules()->actionOut();
00269             if (outDepth > 0) {
00270                 wentOut = true;
00271                 if (interactive) {
00272                     m_locker->ensurePhases(3);
00273                 }
00274             }
00275             else if (outDepth == -1) {
00276                 m_levelScript->interruptPlan();
00277             }
00278         }
00279     }
00280 
00281     return wentOut;
00282 }
00283 //-----------------------------------------------------------------
00284 /**
00285  * Let things fall.
00286  * @return true when something is falling.
00287  */
00288     bool
00289 Room::falldown(bool interactive)
00290 {
00291     ModelList models(&m_models);
00292     Landslip slip(models);
00293 
00294     bool falling = slip.computeFall();
00295     if (interactive) {
00296         playImpact(slip.getImpact());
00297     }
00298     return falling;
00299 }
00300 //-----------------------------------------------------------------
00301 /**
00302  * Let models to release their old position.
00303  * @param interactive whether ensure phases for motion animation
00304  */
00305     void
00306 Room::finishRound(bool interactive)
00307 {
00308     if (interactive) {
00309         m_controls->lockPhases();
00310     }
00311     m_view->noteNewRound(m_locker->getLocked());
00312 
00313     Cube::t_models::iterator end = m_models.end();
00314     for (Cube::t_models::iterator i = m_models.begin(); i != end; ++i) {
00315         (*i)->rules()->finishRound();
00316     }
00317 }
00318 
00319 //-----------------------------------------------------------------
00320     void
00321 Room::switchFish()
00322 {
00323     m_controls->switchActive();
00324 }
00325 //-----------------------------------------------------------------
00326     void
00327 Room::controlEvent(const KeyStroke &stroke)
00328 {
00329     m_controls->controlEvent(stroke);
00330 }
00331 //-----------------------------------------------------------------
00332     void
00333 Room::controlMouse(const MouseStroke &button)
00334 {
00335     if (button.isLeft()) {
00336         V2 fieldPos = m_view->getFieldPos(button.getLoc());
00337         Cube *model = askField(fieldPos);
00338         m_controls->activateSelected(model);
00339     }
00340 }
00341 
00342 //-----------------------------------------------------------------
00343 const StepCounter *
00344 Room::stepCounter() const
00345 {
00346     return m_controls;
00347 }
00348 //-----------------------------------------------------------------
00349     void
00350 Room::checkActive()
00351 {
00352     return m_controls->checkActive();
00353 }
00354 //-----------------------------------------------------------------
00355     void
00356 Room::unBusyUnits()
00357 {
00358     Cube::t_models::iterator end = m_models.end();
00359     for (Cube::t_models::iterator i = m_models.begin(); i != end; ++i) {
00360         (*i)->setBusy(false);
00361     }
00362 }
00363 //-----------------------------------------------------------------
00364 /**
00365  * Load this move, let object to fall fast.
00366  * Don't play sound.
00367  * @throws LoadException for bad moves
00368  */
00369     void
00370 Room::loadMove(char move)
00371 {
00372     static const bool NO_INTERACTIVE = false;
00373     bool falling = true;
00374     while (falling) {
00375         falling = beginFall(NO_INTERACTIVE);
00376         makeMove(move);
00377 
00378         finishRound(NO_INTERACTIVE);
00379     }
00380 }
00381 //-----------------------------------------------------------------
00382 /**
00383  * Begin round.
00384  * Let objects fall.
00385  * First objects can fall out of room (even upward),
00386  * when nothing is going out, then objects can fall down by gravity.
00387  *
00388  * @param interactive whether play sound and do anim
00389  * @return true when something was falling
00390  */
00391     bool
00392 Room::beginFall(bool interactive)
00393 {
00394     prepareRound();
00395     m_lastAction = Cube::ACTION_NO;
00396 
00397     if (fallout(interactive)) {
00398         m_lastAction = Cube::ACTION_MOVE;
00399     }
00400     else {
00401         if (falldown(interactive)) {
00402             m_lastAction = Cube::ACTION_FALL;
00403         }
00404     }
00405     return m_lastAction != Cube::ACTION_NO;
00406 }
00407 //-----------------------------------------------------------------
00408 /**
00409  * Try make single move.
00410  * @return true for success or false when something has moved before
00411  * @throws LoadException for bad moves
00412  */
00413     bool
00414 Room::makeMove(char move)
00415 {
00416     bool result = false;
00417     if (isFresh()) {
00418         if (!m_controls->makeMove(move)) {
00419             throw LoadException(ExInfo("load error - bad move")
00420                     .addInfo("move", std::string(1, move)));
00421         }
00422         m_lastAction = Cube::ACTION_MOVE;
00423         result = true;
00424     }
00425     return result;
00426 }
00427 //-----------------------------------------------------------------
00428 /**
00429  * Returns true when there is no unit which will be able to move.
00430  */
00431 bool
00432 Room::cannotMove() const
00433 {
00434     return m_controls->cannotMove();
00435 }
00436 //-----------------------------------------------------------------
00437 /**
00438  * Returns true when all goals can be solved.
00439  */
00440 bool
00441 Room::isSolvable() const
00442 {
00443     Cube::t_models::const_iterator end = m_models.end();
00444     for (Cube::t_models::const_iterator i = m_models.begin(); i != end; ++i) {
00445         if ((*i)->isWrong()) {
00446             return false;
00447         }
00448     }
00449     return true;
00450 }
00451 //-----------------------------------------------------------------
00452 /**
00453  * Returns true when all goal all satisfied.
00454  * Right time to ask is after finishRound.
00455  * NOTE: room is not solved when somethig is still falling
00456  */
00457 bool
00458 Room::isSolved() const
00459 {
00460     if (!isFresh()) {
00461         return false;
00462     }
00463     Cube::t_models::const_iterator end = m_models.end();
00464     for (Cube::t_models::const_iterator i = m_models.begin(); i != end; ++i) {
00465         if (!(*i)->isSatisfy()) {
00466             return false;
00467         }
00468     }
00469     return true;
00470 }
00471 
00472 //-----------------------------------------------------------------
00473 int
00474 Room::getW() const
00475 {
00476     return m_field->getW();
00477 }
00478 //-----------------------------------------------------------------
00479 int
00480 Room::getH() const
00481 {
00482     return m_field->getH();
00483 }
00484 //-----------------------------------------------------------------
00485 int
00486 Room::getCycles() const
00487 {
00488     return TimerAgent::agent()->getCycles() - m_startTime;
00489 }
00490 //-----------------------------------------------------------------
00491     void
00492 Room::addSound(const std::string &name, const Path &file)
00493 {
00494     m_soundPack->addSound(name, file);
00495 }
00496 //-----------------------------------------------------------------
00497     void
00498 Room::playSound(const std::string &name, int volume)
00499 {
00500     if (OptionAgent::agent()->getAsBool("sound", true)) {
00501         SoundAgent::agent()->playSound(
00502             m_soundPack->getRandomRes(name), volume);
00503     }
00504 }
00505 //-----------------------------------------------------------------
00506 /**
00507  * Shift room content.
00508  * NOTE: background is not shifted
00509  */
00510     void
00511 Room::setScreenShift(const V2 &shift)
00512 {
00513     m_view->setScreenShift(shift);
00514 }
00515 //-----------------------------------------------------------------
00516 void
00517 Room::changeBg(const Path &picture)
00518 {
00519     m_bg->changePicture(picture);
00520 }
00521 //-----------------------------------------------------------------
00522     void
00523 Room::drawOn(SDL_Surface *screen)
00524 {
00525     m_bg->drawOn(screen);
00526     m_view->drawOn(screen);
00527 }
00528 

Generated on Wed Jun 1 09:54:31 2005 for Fish Fillets - Next Generation by  doxygen 1.4.2