Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/presentation2012merge/src/modules/tetris/Tetris.cc @ 9338

Last change on this file since 9338 was 9330, checked in by landauf, 13 years ago

added deterministic stop criterion to end tetris match (independent of time)

  • Property svn:eol-style set to native
File size: 15.5 KB
RevLine 
[8249]1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      ...
24 *   Co-authors:
[9286]25 *      Johannes Ritz
[8249]26 *
[9286]27 *
28 *BUG c) destroying the old stones causes segfault -> WeakPointer as solution ?
29 *BUG d) wrong collision detection: sometimes stones "bounce off"
30 *
31 *
32 *TASK c) end the game in a nicer way
33 *TASK d) save the highscore
34 *TASK e) eye candy
[8249]35 */
36
37/**
38    @file Tetris.cc
39    @brief Implementation of the Tetris class.
40*/
41
42#include "Tetris.h"
43
44#include "core/CoreIncludes.h"
45#include "core/EventIncludes.h"
46#include "core/command/Executor.h"
47
48#include "gamestates/GSLevel.h"
49
50#include "TetrisCenterpoint.h"
51#include "TetrisStone.h"
[9286]52#include "TetrisBrick.h"
[8249]53#include "infos/PlayerInfo.h"
54
55namespace orxonox
56{
57
58    CreateUnloadableFactory(Tetris);
59
60    /**
61    @brief
62        Constructor. Registers and initializes the object.
[9286]63    @ingroup Tetris
[8249]64    */
65    Tetris::Tetris(BaseObject* creator) : Deathmatch(creator)
66    {
67        RegisterObject(Tetris);
68
[9328]69        this->activeBrick_ = 0;
[8249]70
71        // Pre-set the timer, but don't start it yet.
[9286]72        this->starttimer_.setTimer(1.0, false, createExecutor(createFunctor(&Tetris::startBrick, this)));
[8249]73        this->starttimer_.stopTimer();
[8564]74
75        this->player_ = NULL;
[9286]76        this->setHUDTemplate("TetrisHUD");
[9328]77        this->futureBrick_ = 0;
[8249]78    }
79
80    /**
81    @brief
82        Destructor. Cleans up, if initialized.
83    */
84    Tetris::~Tetris()
85    {
86        if (this->isInitialized())
87            this->cleanup();
88    }
89
90    /**
91    @brief
92        Cleans up the Gametype.
93    */
94    void Tetris::cleanup()
95    {
[9328]96        if (this->activeBrick_)
[9326]97        {
[9328]98            this->activeBrick_->destroy();
99            this->activeBrick_ = 0;
100        }
101        if (this->futureBrick_)
102        {
103            this->futureBrick_->destroy();
104            this->futureBrick_ = 0;
105        }
[8564]106
[9328]107        for (std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
108            (*it)->destroy();
109        this->stones_.clear();
[8249]110    }
[8537]111
[8249]112    void Tetris::tick(float dt)
113    {
114        SUPER(Tetris, tick, dt);
[8537]115
[9286]116        if((this->activeBrick_ != NULL)&&(!this->hasEnded()))
[8537]117        {
[9329]118            if(!this->isValidBrickPosition(this->activeBrick_))
[8563]119            {
[9326]120                for (unsigned int i = 0; i < this->activeBrick_->getNumberOfStones(); i++)
121                    this->stones_.push_back(this->activeBrick_->getStone(i));
[9286]122                this->activeBrick_->setVelocity(Vector3::ZERO);
123                this->activeBrick_->releaseStones(this->center_);
124                this->findFullRows();
[9330]125                this->startBrick();
[8563]126            }
[8537]127        }
128    }
129
[8566]130    bool Tetris::isValidMove(TetrisStone* stone, const Vector3& position)
[8537]131    {
132        assert(stone);
[8563]133
[8537]134        if(position.x < this->center_->getStoneSize()/2.0)  //!< If the stone touches the left edge of the level
[8566]135            return false;
[8537]136        else if(position.x > (this->center_->getWidth()-0.5)*this->center_->getStoneSize()) //!< If the stone touches the right edge of the level
[8566]137            return false;
[8488]138
[9328]139        for(std::list<SmartPtr<TetrisStone> >::const_iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
[8563]140        {
141            const Vector3& currentStonePosition = (*it)->getPosition(); //!< Saves the position of the currentStone
142
[8566]143            if((position.x == currentStonePosition.x) && abs(position.y-currentStonePosition.y) < this->center_->getStoneSize())
144                return false;
[8565]145        }
146
[8566]147        return true;
[8565]148    }
149
[9286]150    /**
151    @brief
152        Check for each stone in a brick if it is moved the right way.
153    */
154    bool Tetris::isValidMove(TetrisBrick* brick, const Vector3& position, bool isRotation = false)
155    {
156        assert(brick);
157
158        for (unsigned int i = 0; i < brick->getNumberOfStones(); i++ )
159        {
160            TetrisStone* stone = brick->getStone(i);
161            Vector3 stonePosition; //the current stone's offset to position
162            if(isRotation)
[9323]163                stonePosition = rotateVector(stone->getPosition(), brick->getRotationCount()+1);
[9286]164            else
165                stonePosition = rotateVector(stone->getPosition(), brick->getRotationCount());
166
167            if(! this->isValidMove(stone, position + stonePosition ))
168            {
169                return false;
170            }
171
172            //catch illegal rotation (such that collisions with ground are not permitted)
173            if(isRotation)
174            {
175                if((position + stonePosition).y < this->center_->getStoneSize()/2.0f) //!< If the stone has reached the bottom of the level
176                {
177                    return false;
178                }
179            }
180        }
181        return true;
182
183    }
184
185
186
[8567]187    bool Tetris::isValidStonePosition(TetrisStone* stone, const Vector3& position)
[8565]188    {
189        assert(stone);
190
[9329]191        // check for collisions with all stones
192        for(std::list<SmartPtr<TetrisStone> >::const_iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
[8565]193        {
[9286]194            //Vector3 currentStonePosition = rotateVector((*it)->getPosition(), this->activeBrick_->getRotationCount());
[8565]195            const Vector3& currentStonePosition = (*it)->getPosition(); //!< Saves the position of the currentStone
[9286]196            //!< Saves the position of the currentStone
[8565]197
[9286]198            //filter out cases where the falling stone is already below a steady stone
199            if(position.y < currentStonePosition.y - this->center_->getStoneSize()/2.0f)
200                continue;
[8565]201            if((position.x == currentStonePosition.x) && (position.y < currentStonePosition.y + this->center_->getStoneSize()))
[8563]202            {
[9322]203                float y_offset = static_cast<int>((this->activeBrick_->getPosition().y-currentStonePosition.y+10)/10)*10 + currentStonePosition.y;
[9286]204                if(y_offset < 0) //filter out extreme cases (very rare bug)
[9323]205                    y_offset = 0;
[9286]206                this->activeBrick_->setPosition(Vector3(this->activeBrick_->getPosition().x, y_offset, this->activeBrick_->getPosition().z));
[8567]207                return false;
[8563]208            }// This case applies if the stones overlap partially vertically
209        }
210
[8661]211        // after we checked for collision with all stones, we also check for collision with the bottom
212        if(position.y < this->center_->getStoneSize()/2.0f) //!< If the stone has reached the bottom of the level
213        {
[9323]214            float yOffset = stone->getPosition().y + this->center_->getStoneSize()/2.0f;//calculate offset
215            if(yOffset < 0) //catch brake-throughs
216                yOffset = 0;
217            this->activeBrick_->setPosition(Vector3(this->activeBrick_->getPosition().x, yOffset, this->activeBrick_->getPosition().z));
[8661]218            return false;
219        }
220
[8567]221        return true;
[8249]222    }
[9286]223    /**
224     * @brief This function determines wether a brick touches another brick or the ground.
225     *
226     */
[9329]227    bool Tetris::isValidBrickPosition(TetrisBrick* brick)
[9286]228    {
229        assert(brick);
[8249]230
[9329]231        const Vector3& brickPosition = this->activeBrick_->getPosition();
232
233        // check all stones in the brick
[9286]234        for (unsigned int i = 0; i < brick->getNumberOfStones(); i++ )
235        {
236            TetrisStone* stone = brick->getStone(i);
[9329]237            const Vector3& stonePosition = rotateVector(stone->getPosition(), brick->getRotationCount());
238            if(! this->isValidStonePosition(stone, brickPosition + stonePosition) )
239            {
240                // recurse because all stones have to checked again after the brick was re-positioned
241                this->isValidBrickPosition(brick);
[9286]242                return false;
[9329]243            }
[9286]244        }
245        return true;
246    }
247
[8249]248    /**
249    @brief
[9286]250        A Vector3 is rolled 90 * degrees * amount (anticlockwise rotation)
251    */
252    Vector3 Tetris::rotateVector(Vector3 position, unsigned int amount)
253    {
[9323]254        float temp = 0;
[9286]255        for(unsigned int i = 0; i < amount; i++)
256        {
257            temp = position.x;
258            position.x = -position.y;
259            position.y = temp;
260        }
261        return position;
262    }
263
264    /**
265    @brief
[8249]266        Starts the Tetris minigame.
267    */
268    void Tetris::start()
269    {
270        if (this->center_ != NULL) // There needs to be a TetrisCenterpoint, i.e. the area the game takes place.
271        {
[9286]272            // Create the first brick.
273            this->createBrick();
[8249]274        }
275        else // If no centerpoint was specified, an error is thrown and the level is exited.
276        {
[8858]277            orxout(internal_error) << "Tetris: No Centerpoint specified." << endl;
[8249]278            GSLevel::startMainMenu();
279            return;
280        }
281
[8537]282        // Start the timer. After it has expired the stone is started.
[8249]283        this->starttimer_.startTimer();
284
285        // Set variable to temporarily force the player to spawn.
286        bool temp = this->bForceSpawn_;
287        this->bForceSpawn_ = true;
288
289        // Call start for the parent class.
290        Deathmatch::start();
291
292        // Reset the variable.
293        this->bForceSpawn_ = temp;
294    }
295
296    /**
297    @brief
298        Ends the Tetris minigame.
299    */
300    void Tetris::end()
301    {
[9286]302        this->activeBrick_->setVelocity(Vector3::ZERO);
303        if(this->activeBrick_ != NULL)
304        {
305            this->player_->stopControl();
306        }
307
[8249]308        this->cleanup();
309
310        // Call end for the parent class.
311        Deathmatch::end();
312    }
313
314    /**
315    @brief
316        Spawns player.
317    */
318    void Tetris::spawnPlayersIfRequested()
319    {
320        // Spawn a human player.
321        for (std::map<PlayerInfo*, Player>::iterator it = this->players_.begin(); it != this->players_.end(); ++it)
322            if (it->first->isHumanPlayer() && (it->first->isReadyToSpawn() || this->bForceSpawn_))
323                this->spawnPlayer(it->first);
324    }
325
326    /**
327    @brief
328        Spawns the input player.
329    @param player
330        The player to be spawned.
331    */
332    void Tetris::spawnPlayer(PlayerInfo* player)
333    {
334        assert(player);
335
[8564]336        if(this->player_ == NULL)
[8249]337        {
338            this->player_ = player;
339            this->players_[player].state_ = PlayerState::Alive;
340        }
341    }
342
[9286]343
344
345    void Tetris::startBrick(void)
[8249]346    {
347        if(this->player_ == NULL)
348            return;
[8680]349
350        unsigned int cameraIndex = 0;
[9286]351        if(this->activeBrick_ != NULL)
[8680]352        {
353            // Get camera settings
[9286]354            cameraIndex = this->activeBrick_->getCurrentCameraIndex();
[8249]355            this->player_->stopControl();
[9326]356            // destroy old active brick
357            this->activeBrick_->destroy();
[8680]358        }
[9286]359
360        // Make the last brick to be created the active brick.
[9326]361        this->activeBrick_ = this->futureBrick_;
[9328]362        this->futureBrick_ = 0;
[9286]363
[9326]364        // set its position
[9286]365        this->player_->startControl(this->activeBrick_);
[9326]366        float xPos = (this->center_->getWidth()/2 + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
367        float yPos = (this->center_->getHeight()-0.5f)*this->center_->getStoneSize();
368        this->activeBrick_->setPosition(xPos, yPos, 0.0f);
[9286]369        this->activeBrick_->setVelocity(0.0f, -this->center_->getStoneSpeed(), 0.0f);
370        this->activeBrick_->setCameraPosition(cameraIndex);
[9326]371
372        // create a new future brick
373        this->createBrick();
[9330]374
375        // check if the new brick is in a valid position, otherwise end the game
376        if (!this->isValidBrickPosition(this->activeBrick_))
377            this->end();
[8249]378    }
379
[9286]380    void Tetris::createBrick(void)             //TODO: random rotation offset between 0 and 3 (times 90°)
[8249]381    {
[9286]382        // create new futureBrick_
383        this->futureBrick_ = new TetrisBrick(this->center_);
384
385
[8249]386        // Apply the stone template to the stone.
[9286]387        this->futureBrick_->addTemplate(this->center_->getBrickTemplate());
388
389        // Attach the brick to the Centerpoint and set the position of the brick to be at the left side.
390        this->center_->attach(this->futureBrick_);
[9322]391        float xPos = (this->center_->getWidth()*1.6f + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
[9286]392        float yPos = (this->center_->getHeight()-5.1f)*this->center_->getStoneSize();
393        this->futureBrick_->setPosition(xPos, yPos, 0.0f);
394        this->futureBrick_->setGame(this);
[8249]395    }
396
[9286]397
[8249]398    /**
399    @brief
400        Get the player.
401    @return
402        Returns a pointer to the player. If there is no player, NULL is returned.
403    */
404    PlayerInfo* Tetris::getPlayer(void) const
405    {
406        return this->player_;
407    }
408
[9286]409    /*TetrisCenterpoint* Tetris::getCenterpoint(void) const
410    {
411        return this->center_;
412    }*/
413
[8537]414    /**
415    @brief Set the TetrisCenterpoint (the playing field).
416    @param center A pointer to the TetrisCenterpoint to be set.
417    */
418    void Tetris::setCenterpoint(TetrisCenterpoint* center)
419    {
420        this->center_ = center;
421    }
422
[9286]423    /**
424    @brief Check each row if it is full. Removes all full rows. Update
425    @brief Manages score.
426    */
427    void Tetris::findFullRows()
428    {
[9323]429        unsigned int correctPosition = 0;
430        unsigned int stonesPerRow = 0;
431        for (unsigned int row = 0; row < this->center_->getHeight(); row++)
432        {
433            stonesPerRow = 0;
[9328]434            for(std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); )
[9286]435            {
[9328]436                std::list<SmartPtr<TetrisStone> >::iterator it_temp = it++;
[9326]437                correctPosition = static_cast<unsigned int>(((*it_temp)->getPosition().y - 5)/this->center_->getStoneSize());
[9286]438                if(correctPosition == row)
439                {
440                    stonesPerRow++;
441                    if(stonesPerRow == this->center_->getWidth())
442                    {
443                        clearRow(row);
444                        row--; //the row counter has to be decreased in order to detect multiple rows!
445                        this->playerScored(this->player_);// add points
446                        //increase the stone's speed
447                        this->center_->setStoneSpeed(this->center_->getStoneSpeed()+1.0f);
448                    }
449                }
450            }
451        }
452    }
453
454    void Tetris::clearRow(unsigned int row)
455    {// clear the full row
[9328]456        for(std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); )
[9286]457        {
458            if(static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize()) == row)
[9326]459            {
460                (*it)->destroy();
461                this->stones_.erase(it++);
462            }
463            else
464                ++it;
[9286]465        }
466      // adjust height of stones above the deleted row //TODO: check if this could be a source of a bug.
[9328]467        for(std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
[9286]468        {
[9326]469            if(static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize()) > row)
470                (*it)->setPosition((*it)->getPosition()-Vector3(0,10,0));
[9286]471        }
472
473    }
474
475
[8249]476}
Note: See TracBrowser for help on using the repository browser.