Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/modules/tetris/Tetris.cc @ 9833

Last change on this file since 9833 was 9833, checked in by jo, 10 years ago

Fixing cleanup bug, that causes orxonox to crash when tetris was quitted before calling Tetris::end() and thus missing the cleanup process.

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