Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 11151 was 11083, checked in by muemart, 9 years ago

Fix some clang-tidy warnings.
Also, Serialise.h was doing some C-style casts that ended up being const casts. I moved those const casts as close to the source as possible and changed the loadAndIncrease functions to not do that.

  • Property svn:eol-style set to native
File size: 16.3 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
[11071]68        this->activeBrick_ = nullptr;
[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
[11071]74        this->player_ = nullptr;
[9348]75        this->setHUDTemplate("TetrisHUD");
[11071]76        this->futureBrick_ = nullptr;
[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();
[11071]98            this->activeBrick_ = nullptr;
[9348]99        }
100        if (this->futureBrick_)
101        {
102            this->futureBrick_->destroy();
[11071]103            this->futureBrick_ = nullptr;
[9348]104        }
[8564]105
[11071]106        for (TetrisStone* stone : this->stones_)
107            stone->destroy();
[9348]108        this->stones_.clear();
[8249]109    }
[8537]110
[8249]111    void Tetris::tick(float dt)
112    {
113        SUPER(Tetris, tick, dt);
[8537]114
[11071]115        if((this->activeBrick_ != nullptr)&&(!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
[11071]138        for(TetrisStone* someStone : this->stones_)
[8563]139        {
[11071]140            const Vector3& currentStonePosition = someStone->getPosition(); //!< Saves the position of the currentStone
[8563]141
[11083]142            if((position.x == currentStonePosition.x) && std::abs(position.y-currentStonePosition.y) < this->center_->getStoneSize())
[8566]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
[11071]194        for(TetrisStone* someStone : this->stones_)
[8565]195        {
[9348]196            //Vector3 currentStonePosition = rotateVector((*it)->getPosition(), this->activeBrick_->getRotationCount());
[11071]197            const Vector3& currentStonePosition = someStone->getPosition(); //!< Saves the position of the currentStone
[8565]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        {
[11083]224            float baseOffset = std::abs(stone->getPosition().y);
[9801]225            if (this->activeBrick_->getRotationCount() == 1 || this->activeBrick_->getRotationCount() == 3)
[11083]226                baseOffset = std::abs(stone->getPosition().x);
[9801]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    {
[11071]292        if (this->center_ != nullptr) // There needs to be a TetrisCenterpoint, i.e. the area the game takes place.
[8249]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);
[11071]325        if(this->activeBrick_ != nullptr)
[9348]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.
[11071]343        for (const auto& mapEntry : this->players_)
344            if (mapEntry.first->isHumanPlayer() && (mapEntry.first->isReadyToSpawn() || this->bForceSpawn_))
345                this->spawnPlayer(mapEntry.first);
[8249]346    }
[9833]347   
348    bool Tetris::playerLeft(PlayerInfo* player)
349    {
350        bool left = Gametype::playerLeft(player);
351        if(player && player->isHumanPlayer())
352        {
[11071]353            if(this->activeBrick_ != nullptr)
[9834]354            {
355                this->player_->stopControl();
356            }
357            this->cleanup();
[9833]358        }
359        return left;
360    }
[8249]361
362    /**
363    @brief
364        Spawns the input player.
365    @param player
366        The player to be spawned.
367    */
368    void Tetris::spawnPlayer(PlayerInfo* player)
369    {
370        assert(player);
371
[11071]372        if(this->player_ == nullptr)
[8249]373        {
374            this->player_ = player;
375            this->players_[player].state_ = PlayerState::Alive;
376        }
377    }
378
[9348]379
380
381    void Tetris::startBrick(void)
[8249]382    {
[11071]383        if(this->player_ == nullptr)
[8249]384            return;
[8680]385
386        unsigned int cameraIndex = 0;
[11071]387        if(this->activeBrick_ != nullptr)
[8680]388        {
389            // Get camera settings
[9348]390            cameraIndex = this->activeBrick_->getCurrentCameraIndex();
[8249]391            this->player_->stopControl();
[9348]392            // destroy old active brick
393            this->activeBrick_->destroy();
[8680]394        }
[9348]395
396        // Make the last brick to be created the active brick.
397        this->activeBrick_ = this->futureBrick_;
[11071]398        this->futureBrick_ = nullptr;
[9348]399
400        // set its position
401        this->player_->startControl(this->activeBrick_);
402        float xPos = (this->center_->getWidth()/2 + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
403        float yPos = (this->center_->getHeight()-0.5f)*this->center_->getStoneSize();
404        this->activeBrick_->setPosition(xPos, yPos, 0.0f);
405        this->activeBrick_->setVelocity(0.0f, -this->center_->getStoneSpeed(), 0.0f);
406        this->activeBrick_->setCameraPosition(cameraIndex);
407
408        // create a new future brick
409        this->createBrick();
410
411        // check if the new brick is in a valid position, otherwise end the game
412        if (!this->isValidBrickPosition(this->activeBrick_))
413            this->end();
[8249]414    }
415
[9348]416    void Tetris::createBrick(void)             //TODO: random rotation offset between 0 and 3 (times 90°)
[8249]417    {
[9348]418        // create new futureBrick_
[9667]419        this->futureBrick_ = new TetrisBrick(this->center_->getContext());
[9348]420
421
[8249]422        // Apply the stone template to the stone.
[9348]423        this->futureBrick_->addTemplate(this->center_->getBrickTemplate());
424
425        // Attach the brick to the Centerpoint and set the position of the brick to be at the left side.
426        this->center_->attach(this->futureBrick_);
427        float xPos = (this->center_->getWidth()*1.6f + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
428        float yPos = (this->center_->getHeight()-5.1f)*this->center_->getStoneSize();
[9801]429       
[9348]430        this->futureBrick_->setPosition(xPos, yPos, 0.0f);
431        this->futureBrick_->setGame(this);
[8249]432    }
433
[9348]434
[8249]435    /**
436    @brief
437        Get the player.
438    @return
[11071]439        Returns a pointer to the player. If there is no player, nullptr is returned.
[8249]440    */
441    PlayerInfo* Tetris::getPlayer(void) const
442    {
443        return this->player_;
444    }
445
[9348]446    /*TetrisCenterpoint* Tetris::getCenterpoint(void) const
447    {
448        return this->center_;
449    }*/
450
[8537]451    /**
452    @brief Set the TetrisCenterpoint (the playing field).
453    @param center A pointer to the TetrisCenterpoint to be set.
454    */
455    void Tetris::setCenterpoint(TetrisCenterpoint* center)
456    {
457        this->center_ = center;
458    }
459
[9348]460    /**
461    @brief Check each row if it is full. Removes all full rows. Update
462    @brief Manages score.
463    */
464    void Tetris::findFullRows()
465    {
466        unsigned int correctPosition = 0;
467        unsigned int stonesPerRow = 0;
468        for (unsigned int row = 0; row < this->center_->getHeight(); row++)
469        {
470            stonesPerRow = 0;
[11071]471            for(std::list<StrongPtr<TetrisStone>>::iterator it = this->stones_.begin(); it != this->stones_.end(); )
[9348]472            {
[11071]473                std::list<StrongPtr<TetrisStone>>::iterator it_temp = it++;
[9348]474                correctPosition = static_cast<unsigned int>(((*it_temp)->getPosition().y - 5)/this->center_->getStoneSize());
475                if(correctPosition == row)
476                {
477                    stonesPerRow++;
478                    if(stonesPerRow == this->center_->getWidth())
479                    {
480                        clearRow(row);
481                        row--; //the row counter has to be decreased in order to detect multiple rows!
482                        this->playerScored(this->player_);// add points
483                        //increase the stone's speed
484                        this->center_->setStoneSpeed(this->center_->getStoneSpeed()+1.0f);
485                    }
486                }
487            }
488        }
489    }
490
491    void Tetris::clearRow(unsigned int row)
492    {// clear the full row
[11071]493        for(std::list<StrongPtr<TetrisStone>>::iterator it = this->stones_.begin(); it != this->stones_.end(); )
[9348]494        {
495            if(static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize()) == row)
496            {
497                (*it)->destroy();
498                this->stones_.erase(it++);
499            }
500            else
501                ++it;
502        }
503      // adjust height of stones above the deleted row //TODO: check if this could be a source of a bug.
[11071]504        for(TetrisStone* stone : this->stones_)
[9348]505        {
[11071]506            if(static_cast<unsigned int>((stone->getPosition().y - 5)/this->center_->getStoneSize()) > row)
507                stone->setPosition(stone->getPosition()-Vector3(0,10,0));
[9348]508        }
509
510    }
511
512
[8249]513}
Note: See TracBrowser for help on using the repository browser.