Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9328 was 9328, checked in by landauf, 12 years ago

fixed two possible crashes

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