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
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    /**
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
358        if(this->player_ == NULL)
359        {
360            this->player_ = player;
361            this->players_[player].state_ = PlayerState::Alive;
362        }
363    }
364
365
366
367    void Tetris::startBrick(void)
368    {
369        if(this->player_ == NULL)
370            return;
371
372        unsigned int cameraIndex = 0;
373        if(this->activeBrick_ != NULL)
374        {
375            // Get camera settings
376            cameraIndex = this->activeBrick_->getCurrentCameraIndex();
377            this->player_->stopControl();
378            // destroy old active brick
379            this->activeBrick_->destroy();
380        }
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();
400    }
401
402    void Tetris::createBrick(void)             //TODO: random rotation offset between 0 and 3 (times 90°)
403    {
404        // create new futureBrick_
405        this->futureBrick_ = new TetrisBrick(this->center_->getContext());
406
407
408        // Apply the stone template to the stone.
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();
415       
416        this->futureBrick_->setPosition(xPos, yPos, 0.0f);
417        this->futureBrick_->setGame(this);
418    }
419
420
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
432    /*TetrisCenterpoint* Tetris::getCenterpoint(void) const
433    {
434        return this->center_;
435    }*/
436
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
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
499}
Note: See TracBrowser for help on using the repository browser.