Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9816 was 9803, checked in by jo, 11 years ago

The brick falldown acceleration is increased, but the velocity is limited, such that occasional break-throughs are prevented.

  • Property svn:eol-style set to native
File size: 16.2 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    }
347
348    /**
349    @brief
350        Spawns the input player.
351    @param player
352        The player to be spawned.
353    */
354    void Tetris::spawnPlayer(PlayerInfo* player)
355    {
356        assert(player);
357
[8564]358        if(this->player_ == NULL)
[8249]359        {
360            this->player_ = player;
361            this->players_[player].state_ = PlayerState::Alive;
362        }
363    }
364
[9348]365
366
367    void Tetris::startBrick(void)
[8249]368    {
369        if(this->player_ == NULL)
370            return;
[8680]371
372        unsigned int cameraIndex = 0;
[9348]373        if(this->activeBrick_ != NULL)
[8680]374        {
375            // Get camera settings
[9348]376            cameraIndex = this->activeBrick_->getCurrentCameraIndex();
[8249]377            this->player_->stopControl();
[9348]378            // destroy old active brick
379            this->activeBrick_->destroy();
[8680]380        }
[9348]381
382        // Make the last brick to be created the active brick.
383        this->activeBrick_ = this->futureBrick_;
384        this->futureBrick_ = 0;
385
386        // set its position
387        this->player_->startControl(this->activeBrick_);
388        float xPos = (this->center_->getWidth()/2 + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
389        float yPos = (this->center_->getHeight()-0.5f)*this->center_->getStoneSize();
390        this->activeBrick_->setPosition(xPos, yPos, 0.0f);
391        this->activeBrick_->setVelocity(0.0f, -this->center_->getStoneSpeed(), 0.0f);
392        this->activeBrick_->setCameraPosition(cameraIndex);
393
394        // create a new future brick
395        this->createBrick();
396
397        // check if the new brick is in a valid position, otherwise end the game
398        if (!this->isValidBrickPosition(this->activeBrick_))
399            this->end();
[8249]400    }
401
[9348]402    void Tetris::createBrick(void)             //TODO: random rotation offset between 0 and 3 (times 90°)
[8249]403    {
[9348]404        // create new futureBrick_
[9667]405        this->futureBrick_ = new TetrisBrick(this->center_->getContext());
[9348]406
407
[8249]408        // Apply the stone template to the stone.
[9348]409        this->futureBrick_->addTemplate(this->center_->getBrickTemplate());
410
411        // Attach the brick to the Centerpoint and set the position of the brick to be at the left side.
412        this->center_->attach(this->futureBrick_);
413        float xPos = (this->center_->getWidth()*1.6f + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
414        float yPos = (this->center_->getHeight()-5.1f)*this->center_->getStoneSize();
[9801]415       
[9348]416        this->futureBrick_->setPosition(xPos, yPos, 0.0f);
417        this->futureBrick_->setGame(this);
[8249]418    }
419
[9348]420
[8249]421    /**
422    @brief
423        Get the player.
424    @return
425        Returns a pointer to the player. If there is no player, NULL is returned.
426    */
427    PlayerInfo* Tetris::getPlayer(void) const
428    {
429        return this->player_;
430    }
431
[9348]432    /*TetrisCenterpoint* Tetris::getCenterpoint(void) const
433    {
434        return this->center_;
435    }*/
436
[8537]437    /**
438    @brief Set the TetrisCenterpoint (the playing field).
439    @param center A pointer to the TetrisCenterpoint to be set.
440    */
441    void Tetris::setCenterpoint(TetrisCenterpoint* center)
442    {
443        this->center_ = center;
444    }
445
[9348]446    /**
447    @brief Check each row if it is full. Removes all full rows. Update
448    @brief Manages score.
449    */
450    void Tetris::findFullRows()
451    {
452        unsigned int correctPosition = 0;
453        unsigned int stonesPerRow = 0;
454        for (unsigned int row = 0; row < this->center_->getHeight(); row++)
455        {
456            stonesPerRow = 0;
457            for(std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); )
458            {
459                std::list<SmartPtr<TetrisStone> >::iterator it_temp = it++;
460                correctPosition = static_cast<unsigned int>(((*it_temp)->getPosition().y - 5)/this->center_->getStoneSize());
461                if(correctPosition == row)
462                {
463                    stonesPerRow++;
464                    if(stonesPerRow == this->center_->getWidth())
465                    {
466                        clearRow(row);
467                        row--; //the row counter has to be decreased in order to detect multiple rows!
468                        this->playerScored(this->player_);// add points
469                        //increase the stone's speed
470                        this->center_->setStoneSpeed(this->center_->getStoneSpeed()+1.0f);
471                    }
472                }
473            }
474        }
475    }
476
477    void Tetris::clearRow(unsigned int row)
478    {// clear the full row
479        for(std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); )
480        {
481            if(static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize()) == row)
482            {
483                (*it)->destroy();
484                this->stones_.erase(it++);
485            }
486            else
487                ++it;
488        }
489      // adjust height of stones above the deleted row //TODO: check if this could be a source of a bug.
490        for(std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
491        {
492            if(static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize()) > row)
493                (*it)->setPosition((*it)->getPosition()-Vector3(0,10,0));
494        }
495
496    }
497
498
[8249]499}
Note: See TracBrowser for help on using the repository browser.