Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9696 was 9667, checked in by landauf, 11 years ago

merged core6 back to trunk

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