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
Line 
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:
25 *      Johannes Ritz
26 *
27 *
28 *
29 *
30 *TASK c) end the game in a nicer way
31 *TASK d) save the highscore
32 *TASK e) eye candy
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"
50#include "TetrisBrick.h"
51#include "infos/PlayerInfo.h"
52#include <cmath>
53
54namespace orxonox
55{
56
57    RegisterUnloadableClass(Tetris);
58
59    /**
60    @brief
61        Constructor. Registers and initializes the object.
62    @ingroup Tetris
63    */
64    Tetris::Tetris(Context* context) : Deathmatch(context)
65    {
66        RegisterObject(Tetris);
67
68        this->activeBrick_ = 0;
69
70        // Pre-set the timer, but don't start it yet.
71        this->starttimer_.setTimer(1.0, false, createExecutor(createFunctor(&Tetris::startBrick, this)));
72        this->starttimer_.stopTimer();
73
74        this->player_ = NULL;
75        this->setHUDTemplate("TetrisHUD");
76        this->futureBrick_ = 0;
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    {
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        }
105
106        for (std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
107            (*it)->destroy();
108        this->stones_.clear();
109    }
110
111    void Tetris::tick(float dt)
112    {
113        SUPER(Tetris, tick, dt);
114
115        if((this->activeBrick_ != NULL)&&(!this->hasEnded()))
116        {
117            if(!this->isValidBrickPosition(this->activeBrick_))
118            {
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();
125            }
126        } 
127    }
128
129    bool Tetris::isValidMove(TetrisStone* stone, const Vector3& position)
130    {
131        assert(stone);
132
133        if(position.x < this->center_->getStoneSize()/2.0)  //!< If the stone touches the left edge of the level
134            return false;
135        else if(position.x > (this->center_->getWidth()-0.5)*this->center_->getStoneSize()) //!< If the stone touches the right edge of the level
136            return false;
137
138        for(std::list<SmartPtr<TetrisStone> >::const_iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
139        {
140            const Vector3& currentStonePosition = (*it)->getPosition(); //!< Saves the position of the currentStone
141
142            if((position.x == currentStonePosition.x) && abs(position.y-currentStonePosition.y) < this->center_->getStoneSize())
143                return false;
144        }
145
146        return true;
147    }
148
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
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)
190    {
191        assert(stone);
192
193        // check for collisions with all stones
194        for(std::list<SmartPtr<TetrisStone> >::const_iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
195        {
196            //Vector3 currentStonePosition = rotateVector((*it)->getPosition(), this->activeBrick_->getRotationCount());
197            const Vector3& currentStonePosition = (*it)->getPosition(); //!< Saves the position of the currentStone
198
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;
202            if((position.x == currentStonePosition.x) && (position.y < currentStonePosition.y + this->center_->getStoneSize()))
203            {
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));
208                return false;
209            }// This case applies if the stones overlap partially vertically
210        }
211
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);
222        if(position.y < this->center_->getStoneSize()/2.0f) //!< If the stone has reached the bottom of the level
223        {
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
228            if(yOffset < 0) //catch brake-throughs
229                yOffset = 0;
230            this->activeBrick_->setPosition(Vector3(this->activeBrick_->getPosition().x, yOffset, this->activeBrick_->getPosition().z));
231            return false;
232        }
233        return true;
234    }
235   
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);
243
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());
251            if(! this->checkStoneStoneCollision(stone, brickPosition + stonePosition) )
252            {
253                return false;
254            }
255        }
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       
267        return true;
268    }
269
270    /**
271    @brief
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
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        {
294            // Create the first brick.
295            this->createBrick();
296        }
297        else // If no centerpoint was specified, an error is thrown and the level is exited.
298        {
299            orxout(internal_error) << "Tetris: No Centerpoint specified." << endl;
300            GSLevel::startMainMenu();
301            return;
302        }
303
304        // Start the timer. After it has expired the stone is started.
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    {
324        this->activeBrick_->setVelocity(Vector3::ZERO);
325        if(this->activeBrick_ != NULL)
326        {
327            this->player_->stopControl();
328        }
329
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    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    }
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
368        if(this->player_ == NULL)
369        {
370            this->player_ = player;
371            this->players_[player].state_ = PlayerState::Alive;
372        }
373    }
374
375
376
377    void Tetris::startBrick(void)
378    {
379        if(this->player_ == NULL)
380            return;
381
382        unsigned int cameraIndex = 0;
383        if(this->activeBrick_ != NULL)
384        {
385            // Get camera settings
386            cameraIndex = this->activeBrick_->getCurrentCameraIndex();
387            this->player_->stopControl();
388            // destroy old active brick
389            this->activeBrick_->destroy();
390        }
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();
410    }
411
412    void Tetris::createBrick(void)             //TODO: random rotation offset between 0 and 3 (times 90°)
413    {
414        // create new futureBrick_
415        this->futureBrick_ = new TetrisBrick(this->center_->getContext());
416
417
418        // Apply the stone template to the stone.
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();
425       
426        this->futureBrick_->setPosition(xPos, yPos, 0.0f);
427        this->futureBrick_->setGame(this);
428    }
429
430
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
442    /*TetrisCenterpoint* Tetris::getCenterpoint(void) const
443    {
444        return this->center_;
445    }*/
446
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
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
509}
Note: See TracBrowser for help on using the repository browser.