Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

small refactoring of tetris gametype, fixed memory leak

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