Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9329 was 9329, checked in by landauf, 13 years ago

fixed collision bug in tetris which made stones overlap in some cases (especially with low fps or high game speed)

  • Property svn:eol-style set to native
File size: 15.6 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_ = 0;
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_ = 0;
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->activeBrick_)
98        {
99            this->activeBrick_->destroy();
100            this->activeBrick_ = 0;
101        }
102        if (this->futureBrick_)
103        {
104            this->futureBrick_->destroy();
105            this->futureBrick_ = 0;
106        }
107
108        for (std::list<SmartPtr<TetrisStone> >::iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
109            (*it)->destroy();
110        this->stones_.clear();
111    }
112
113    void Tetris::tick(float dt)
114    {
115        SUPER(Tetris, tick, dt);
116
117        if((this->activeBrick_ != NULL)&&(!this->hasEnded()))
118        {
119            this->endGameCriteria_ += dt;
120            if(!this->isValidBrickPosition(this->activeBrick_))
121            {
122                for (unsigned int i = 0; i < this->activeBrick_->getNumberOfStones(); i++)
123                    this->stones_.push_back(this->activeBrick_->getStone(i));
124                this->activeBrick_->setVelocity(Vector3::ZERO);
125                this->activeBrick_->releaseStones(this->center_);
126                this->findFullRows();
127                if(this->endGameCriteria_ < 0.1f) //end game if two bricks are created within a 0.1s interval.
128                    this->end();
129                else
130                    this->startBrick();
131                this->endGameCriteria_ = 0.0f;
132            }
133        }
134    }
135
136    bool Tetris::isValidMove(TetrisStone* stone, const Vector3& position)
137    {
138        assert(stone);
139
140        if(position.x < this->center_->getStoneSize()/2.0)  //!< If the stone touches the left edge of the level
141            return false;
142        else if(position.x > (this->center_->getWidth()-0.5)*this->center_->getStoneSize()) //!< If the stone touches the right edge of the level
143            return false;
144
145        for(std::list<SmartPtr<TetrisStone> >::const_iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
146        {
147            const Vector3& currentStonePosition = (*it)->getPosition(); //!< Saves the position of the currentStone
148
149            if((position.x == currentStonePosition.x) && abs(position.y-currentStonePosition.y) < this->center_->getStoneSize())
150                return false;
151        }
152
153        return true;
154    }
155
156    /**
157    @brief
158        Check for each stone in a brick if it is moved the right way.
159    */
160    bool Tetris::isValidMove(TetrisBrick* brick, const Vector3& position, bool isRotation = false)
161    {
162        assert(brick);
163
164        for (unsigned int i = 0; i < brick->getNumberOfStones(); i++ )
165        {
166            TetrisStone* stone = brick->getStone(i);
167            Vector3 stonePosition; //the current stone's offset to position
168            if(isRotation)
169                stonePosition = rotateVector(stone->getPosition(), brick->getRotationCount()+1);
170            else
171                stonePosition = rotateVector(stone->getPosition(), brick->getRotationCount());
172
173            if(! this->isValidMove(stone, position + stonePosition ))
174            {
175                return false;
176            }
177
178            //catch illegal rotation (such that collisions with ground are not permitted)
179            if(isRotation)
180            {
181                if((position + stonePosition).y < this->center_->getStoneSize()/2.0f) //!< If the stone has reached the bottom of the level
182                {
183                    return false;
184                }
185            }
186        }
187        return true;
188
189    }
190
191
192
193    bool Tetris::isValidStonePosition(TetrisStone* stone, const Vector3& position)
194    {
195        assert(stone);
196
197        // check for collisions with all stones
198        for(std::list<SmartPtr<TetrisStone> >::const_iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
199        {
200            //Vector3 currentStonePosition = rotateVector((*it)->getPosition(), this->activeBrick_->getRotationCount());
201            const Vector3& currentStonePosition = (*it)->getPosition(); //!< Saves the position of the currentStone
202            //!< Saves the position of the currentStone
203
204            //filter out cases where the falling stone is already below a steady stone
205            if(position.y < currentStonePosition.y - this->center_->getStoneSize()/2.0f)
206                continue;
207            if((position.x == currentStonePosition.x) && (position.y < currentStonePosition.y + this->center_->getStoneSize()))
208            {
209                float y_offset = static_cast<int>((this->activeBrick_->getPosition().y-currentStonePosition.y+10)/10)*10 + currentStonePosition.y;
210                if(y_offset < 0) //filter out extreme cases (very rare bug)
211                    y_offset = 0;
212                this->activeBrick_->setPosition(Vector3(this->activeBrick_->getPosition().x, y_offset, this->activeBrick_->getPosition().z));
213                return false;
214            }// This case applies if the stones overlap partially vertically
215        }
216
217        // after we checked for collision with all stones, we also check for collision with the bottom
218        if(position.y < this->center_->getStoneSize()/2.0f) //!< If the stone has reached the bottom of the level
219        {
220            float yOffset = stone->getPosition().y + this->center_->getStoneSize()/2.0f;//calculate offset
221            if(yOffset < 0) //catch brake-throughs
222                yOffset = 0;
223            this->activeBrick_->setPosition(Vector3(this->activeBrick_->getPosition().x, yOffset, this->activeBrick_->getPosition().z));
224            return false;
225        }
226
227        return true;
228    }
229    /**
230     * @brief This function determines wether a brick touches another brick or the ground.
231     *
232     */
233    bool Tetris::isValidBrickPosition(TetrisBrick* brick)
234    {
235        assert(brick);
236
237        const Vector3& brickPosition = this->activeBrick_->getPosition();
238
239        // check all stones in the brick
240        for (unsigned int i = 0; i < brick->getNumberOfStones(); i++ )
241        {
242            TetrisStone* stone = brick->getStone(i);
243            const Vector3& stonePosition = rotateVector(stone->getPosition(), brick->getRotationCount());
244            if(! this->isValidStonePosition(stone, brickPosition + stonePosition) )
245            {
246                // recurse because all stones have to checked again after the brick was re-positioned
247                this->isValidBrickPosition(brick);
248                return false;
249            }
250        }
251        return true;
252    }
253
254    /**
255    @brief
256        A Vector3 is rolled 90 * degrees * amount (anticlockwise rotation)
257    */
258    Vector3 Tetris::rotateVector(Vector3 position, unsigned int amount)
259    {
260        float temp = 0;
261        for(unsigned int i = 0; i < amount; i++)
262        {
263            temp = position.x;
264            position.x = -position.y;
265            position.y = temp;
266        }
267        return position;
268    }
269
270    /**
271    @brief
272        Starts the Tetris minigame.
273    */
274    void Tetris::start()
275    {
276        if (this->center_ != NULL) // There needs to be a TetrisCenterpoint, i.e. the area the game takes place.
277        {
278            // Create the first brick.
279            this->createBrick();
280        }
281        else // If no centerpoint was specified, an error is thrown and the level is exited.
282        {
283            orxout(internal_error) << "Tetris: No Centerpoint specified." << endl;
284            GSLevel::startMainMenu();
285            return;
286        }
287
288        // Start the timer. After it has expired the stone is started.
289        this->starttimer_.startTimer();
290
291        // Set variable to temporarily force the player to spawn.
292        bool temp = this->bForceSpawn_;
293        this->bForceSpawn_ = true;
294
295        // Call start for the parent class.
296        Deathmatch::start();
297
298        // Reset the variable.
299        this->bForceSpawn_ = temp;
300    }
301
302    /**
303    @brief
304        Ends the Tetris minigame.
305    */
306    void Tetris::end()
307    {
308        this->activeBrick_->setVelocity(Vector3::ZERO);
309        if(this->activeBrick_ != NULL)
310        {
311            this->player_->stopControl();
312        }
313
314        this->cleanup();
315
316        // Call end for the parent class.
317        Deathmatch::end();
318    }
319
320    /**
321    @brief
322        Spawns player.
323    */
324    void Tetris::spawnPlayersIfRequested()
325    {
326        // Spawn a human player.
327        for (std::map<PlayerInfo*, Player>::iterator it = this->players_.begin(); it != this->players_.end(); ++it)
328            if (it->first->isHumanPlayer() && (it->first->isReadyToSpawn() || this->bForceSpawn_))
329                this->spawnPlayer(it->first);
330    }
331
332    /**
333    @brief
334        Spawns the input player.
335    @param player
336        The player to be spawned.
337    */
338    void Tetris::spawnPlayer(PlayerInfo* player)
339    {
340        assert(player);
341
342        if(this->player_ == NULL)
343        {
344            this->player_ = player;
345            this->players_[player].state_ = PlayerState::Alive;
346        }
347    }
348
349
350
351    void Tetris::startBrick(void)
352    {
353        if(this->player_ == NULL)
354            return;
355
356        unsigned int cameraIndex = 0;
357        if(this->activeBrick_ != NULL)
358        {
359            // Get camera settings
360            cameraIndex = this->activeBrick_->getCurrentCameraIndex();
361            this->player_->stopControl();
362            // destroy old active brick
363            this->activeBrick_->destroy();
364        }
365
366        // Make the last brick to be created the active brick.
367        this->activeBrick_ = this->futureBrick_;
368        this->futureBrick_ = 0;
369
370        // set its position
371        this->player_->startControl(this->activeBrick_);
372        float xPos = (this->center_->getWidth()/2 + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
373        float yPos = (this->center_->getHeight()-0.5f)*this->center_->getStoneSize();
374        this->activeBrick_->setPosition(xPos, yPos, 0.0f);
375        this->activeBrick_->setVelocity(0.0f, -this->center_->getStoneSpeed(), 0.0f);
376        this->activeBrick_->setCameraPosition(cameraIndex);
377
378        // create a new future brick
379        this->createBrick();
380    }
381
382    void Tetris::createBrick(void)             //TODO: random rotation offset between 0 and 3 (times 90°)
383    {
384        // create new futureBrick_
385        this->futureBrick_ = new TetrisBrick(this->center_);
386
387
388        // Apply the stone template to the stone.
389        this->futureBrick_->addTemplate(this->center_->getBrickTemplate());
390
391        // Attach the brick to the Centerpoint and set the position of the brick to be at the left side.
392        this->center_->attach(this->futureBrick_);
393        float xPos = (this->center_->getWidth()*1.6f + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
394        float yPos = (this->center_->getHeight()-5.1f)*this->center_->getStoneSize();
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.