Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9801 was 9801, checked in by jo, 10 years ago

Eridicating a Tetris Bug, related to the positioning of a TetrisBrick in case of a brick-bottom-collision.

  • 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 *
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    bool Tetris::isValidStonePosition(TetrisStone* stone, const Vector3& position)
187    {
188        assert(stone);
189
190        // check for collisions with all stones
191        for(std::list<SmartPtr<TetrisStone> >::const_iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
192        {
193            //Vector3 currentStonePosition = rotateVector((*it)->getPosition(), this->activeBrick_->getRotationCount());
194            const Vector3& currentStonePosition = (*it)->getPosition(); //!< Saves the position of the currentStone
195            //!< Saves the position of the currentStone
196
197            //filter out cases where the falling stone is already below a steady stone
198            if(position.y < currentStonePosition.y - this->center_->getStoneSize()/2.0f)
199                continue;
200            if((position.x == currentStonePosition.x) && (position.y < currentStonePosition.y + this->center_->getStoneSize()))
201            {
202                float y_offset = static_cast<int>((this->activeBrick_->getPosition().y-currentStonePosition.y+10)/10)*10 + currentStonePosition.y;
203                if(y_offset < 0) //filter out extreme cases (very rare bug)
204                    y_offset = 0;
205                this->activeBrick_->setPosition(Vector3(this->activeBrick_->getPosition().x, y_offset, this->activeBrick_->getPosition().z));
206                return false;
207            }// This case applies if the stones overlap partially vertically
208        }
209
210        // after we checked for collision with all stones, we also check for collision with the bottom
211        if(position.y < this->center_->getStoneSize()/2.0f) //!< If the stone has reached the bottom of the level
212        {
213            float baseOffset = abs(stone->getPosition().y);
214            if (this->activeBrick_->getRotationCount() == 1 || this->activeBrick_->getRotationCount() == 3)
215                baseOffset = abs(stone->getPosition().x);
216            float yOffset = baseOffset + this->center_->getStoneSize()/2.0f;//calculate offset
217            if(yOffset < 0) //catch brake-throughs
218                yOffset = 0;
219            this->activeBrick_->setPosition(Vector3(this->activeBrick_->getPosition().x, yOffset, this->activeBrick_->getPosition().z));
220            return false;
221        }
222
223        return true;
224    }
225    /**
226     * @brief This function determines wether a brick touches another brick or the ground.
227     *
228     */
229    bool Tetris::isValidBrickPosition(TetrisBrick* brick)
230    {
231        assert(brick);
232
233        const Vector3& brickPosition = this->activeBrick_->getPosition();
234
235        // check all stones in the brick
236        for (unsigned int i = 0; i < brick->getNumberOfStones(); i++ )
237        {
238            TetrisStone* stone = brick->getStone(i);
239            const Vector3& stonePosition = rotateVector(stone->getPosition(), brick->getRotationCount());
240            if(! this->isValidStonePosition(stone, brickPosition + stonePosition) )
241            {
242                // recurse because all stones have to checked again after the brick was re-positioned
243                return false;
244            }
245        }
246        return true;
247    }
248
249    /**
250    @brief
251        A Vector3 is rolled 90 * degrees * amount (anticlockwise rotation)
252    */
253    Vector3 Tetris::rotateVector(Vector3 position, unsigned int amount)
254    {
255        float temp = 0;
256        for(unsigned int i = 0; i < amount; i++)
257        {
258            temp = position.x;
259            position.x = -position.y;
260            position.y = temp;
261        }
262        return position;
263    }
264
265    /**
266    @brief
267        Starts the Tetris minigame.
268    */
269    void Tetris::start()
270    {
271        if (this->center_ != NULL) // There needs to be a TetrisCenterpoint, i.e. the area the game takes place.
272        {
273            // Create the first brick.
274            this->createBrick();
275        }
276        else // If no centerpoint was specified, an error is thrown and the level is exited.
277        {
278            orxout(internal_error) << "Tetris: No Centerpoint specified." << endl;
279            GSLevel::startMainMenu();
280            return;
281        }
282
283        // Start the timer. After it has expired the stone is started.
284        this->starttimer_.startTimer();
285
286        // Set variable to temporarily force the player to spawn.
287        bool temp = this->bForceSpawn_;
288        this->bForceSpawn_ = true;
289
290        // Call start for the parent class.
291        Deathmatch::start();
292
293        // Reset the variable.
294        this->bForceSpawn_ = temp;
295    }
296
297    /**
298    @brief
299        Ends the Tetris minigame.
300    */
301    void Tetris::end()
302    {
303        this->activeBrick_->setVelocity(Vector3::ZERO);
304        if(this->activeBrick_ != NULL)
305        {
306            this->player_->stopControl();
307        }
308
309        this->cleanup();
310
311        // Call end for the parent class.
312        Deathmatch::end();
313    }
314
315    /**
316    @brief
317        Spawns player.
318    */
319    void Tetris::spawnPlayersIfRequested()
320    {
321        // Spawn a human player.
322        for (std::map<PlayerInfo*, Player>::iterator it = this->players_.begin(); it != this->players_.end(); ++it)
323            if (it->first->isHumanPlayer() && (it->first->isReadyToSpawn() || this->bForceSpawn_))
324                this->spawnPlayer(it->first);
325    }
326
327    /**
328    @brief
329        Spawns the input player.
330    @param player
331        The player to be spawned.
332    */
333    void Tetris::spawnPlayer(PlayerInfo* player)
334    {
335        assert(player);
336
337        if(this->player_ == NULL)
338        {
339            this->player_ = player;
340            this->players_[player].state_ = PlayerState::Alive;
341        }
342    }
343
344
345
346    void Tetris::startBrick(void)
347    {
348        if(this->player_ == NULL)
349            return;
350
351        unsigned int cameraIndex = 0;
352        if(this->activeBrick_ != NULL)
353        {
354            // Get camera settings
355            cameraIndex = this->activeBrick_->getCurrentCameraIndex();
356            this->player_->stopControl();
357            // destroy old active brick
358            this->activeBrick_->destroy();
359        }
360
361        // Make the last brick to be created the active brick.
362        this->activeBrick_ = this->futureBrick_;
363        this->futureBrick_ = 0;
364
365        // set its position
366        this->player_->startControl(this->activeBrick_);
367        float xPos = (this->center_->getWidth()/2 + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
368        float yPos = (this->center_->getHeight()-0.5f)*this->center_->getStoneSize();
369        this->activeBrick_->setPosition(xPos, yPos, 0.0f);
370        this->activeBrick_->setVelocity(0.0f, -this->center_->getStoneSpeed(), 0.0f);
371        this->activeBrick_->setCameraPosition(cameraIndex);
372
373        // create a new future brick
374        this->createBrick();
375
376        // check if the new brick is in a valid position, otherwise end the game
377        if (!this->isValidBrickPosition(this->activeBrick_))
378            this->end();
379    }
380
381    void Tetris::createBrick(void)             //TODO: random rotation offset between 0 and 3 (times 90°)
382    {
383        // create new futureBrick_
384        this->futureBrick_ = new TetrisBrick(this->center_->getContext());
385
386
387        // Apply the stone template to the stone.
388        this->futureBrick_->addTemplate(this->center_->getBrickTemplate());
389
390        // Attach the brick to the Centerpoint and set the position of the brick to be at the left side.
391        this->center_->attach(this->futureBrick_);
392        float xPos = (this->center_->getWidth()*1.6f + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
393        float yPos = (this->center_->getHeight()-5.1f)*this->center_->getStoneSize();
394       
395        this->futureBrick_->setPosition(xPos, yPos, 0.0f);
396        this->futureBrick_->setGame(this);
397    }
398
399
400    /**
401    @brief
402        Get the player.
403    @return
404        Returns a pointer to the player. If there is no player, NULL is returned.
405    */
406    PlayerInfo* Tetris::getPlayer(void) const
407    {
408        return this->player_;
409    }
410
411    /*TetrisCenterpoint* Tetris::getCenterpoint(void) const
412    {
413        return this->center_;
414    }*/
415
416    /**
417    @brief Set the TetrisCenterpoint (the playing field).
418    @param center A pointer to the TetrisCenterpoint to be set.
419    */
420    void Tetris::setCenterpoint(TetrisCenterpoint* center)
421    {
422        this->center_ = center;
423    }
424
425    /**
426    @brief Check each row if it is full. Removes all full rows. Update
427    @brief Manages score.
428    */
429    void Tetris::findFullRows()
430    {
431        unsigned int correctPosition = 0;
432        unsigned int stonesPerRow = 0;
433        for (unsigned int row = 0; row < this->center_->getHeight(); row++)
434        {
435            stonesPerRow = 0;
436            for(std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); )
437            {
438                std::list<SmartPtr<TetrisStone> >::iterator it_temp = it++;
439                correctPosition = static_cast<unsigned int>(((*it_temp)->getPosition().y - 5)/this->center_->getStoneSize());
440                if(correctPosition == row)
441                {
442                    stonesPerRow++;
443                    if(stonesPerRow == this->center_->getWidth())
444                    {
445                        clearRow(row);
446                        row--; //the row counter has to be decreased in order to detect multiple rows!
447                        this->playerScored(this->player_);// add points
448                        //increase the stone's speed
449                        this->center_->setStoneSpeed(this->center_->getStoneSpeed()+1.0f);
450                    }
451                }
452            }
453        }
454    }
455
456    void Tetris::clearRow(unsigned int row)
457    {// clear the full row
458        for(std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); )
459        {
460            if(static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize()) == row)
461            {
462                (*it)->destroy();
463                this->stones_.erase(it++);
464            }
465            else
466                ++it;
467        }
468      // adjust height of stones above the deleted row //TODO: check if this could be a source of a bug.
469        for(std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
470        {
471            if(static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize()) > row)
472                (*it)->setPosition((*it)->getPosition()-Vector3(0,10,0));
473        }
474
475    }
476
477
478}
Note: See TracBrowser for help on using the repository browser.