Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 9296 was 9286, checked in by jo, 12 years ago

Merged pCuts branch.

  • 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        /*for(int i = 0;i < this->stones_.size(); i++) //TODO: Why isn't there any code like this
98        {                                              // compensating the 'new' statement?
99            delete this->stones_[i];
100        }//*/
101
102    }
103
104    void Tetris::tick(float dt)
105    {
106        SUPER(Tetris, tick, dt);
107
108        if((this->activeBrick_ != NULL)&&(!this->hasEnded()))
109        {
110                this->endGameCriteria_ += dt;
111            if(!this->isValidBrickPosition(this->activeBrick_, this->activeBrick_->getPosition()))
112            {
113                this->activeBrick_->setVelocity(Vector3::ZERO);
114                this->activeBrick_->releaseStones(this->center_);
115                //delete this->activeBrick_; //releasing the memory
116                this->findFullRows();
117                if(this->endGameCriteria_ < 0.1f) //end game if two bricks are created within a 0.1s interval.
118                    this->end();
119                this->createBrick();
120                this->startBrick();
121                this->endGameCriteria_ = 0.0f;
122            }
123        }
124    }
125
126    bool Tetris::isValidMove(TetrisStone* stone, const Vector3& position)
127    {
128        assert(stone);
129
130        if(position.x < this->center_->getStoneSize()/2.0)  //!< If the stone touches the left edge of the level
131            return false;
132        else if(position.x > (this->center_->getWidth()-0.5)*this->center_->getStoneSize()) //!< If the stone touches the right edge of the level
133            return false;
134
135        for(std::vector<TetrisStone*>::const_iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
136        {
137            if(stone == *it)
138                continue;
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        // we use a reverse iterator because we have to check for collisions with the topmost stones first
191        for(std::vector<TetrisStone*>::const_reverse_iterator it = this->stones_.rbegin(); it != this->stones_.rend(); ++it)
192        {
193            if(this->activeBrick_->contains(*it))//skip the moving brick' stones
194                continue;
195            //Vector3 currentStonePosition = rotateVector((*it)->getPosition(), this->activeBrick_->getRotationCount());
196            const Vector3& currentStonePosition = (*it)->getPosition(); //!< Saves the position of the currentStone
197            //!< 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                int 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        // after we checked for collision with all stones, we also check for collision with the bottom
213        if(position.y < this->center_->getStoneSize()/2.0f) //!< If the stone has reached the bottom of the level
214        {
215                int yOffset = stone->getPosition().y + this->center_->getStoneSize()/2.0f;//calculate offset
216                if(yOffset < 0) //catch brake-throughs
217                    yOffset = 0;
218                this->activeBrick_->setPosition(Vector3(this->activeBrick_->getPosition().x, yOffset, this->activeBrick_->getPosition().z));
219            return false;
220        }
221
222        return true;
223    }
224    /**
225     * @brief This function determines wether a brick touches another brick or the ground.
226     *
227     */
228    bool Tetris::isValidBrickPosition(TetrisBrick* brick, const Vector3& position)
229    {
230        assert(brick);
231
232        for (unsigned int i = 0; i < brick->getNumberOfStones(); i++ )
233        {
234            TetrisStone* stone = brick->getStone(i);
235            Vector3 stonePosition = rotateVector(stone->getPosition(), brick->getRotationCount());
236            if(! this->isValidStonePosition(stone, position + stonePosition) )
237                return false;
238        }
239        return true;
240    }
241
242    /**
243    @brief
244        A Vector3 is rolled 90 * degrees * amount (anticlockwise rotation)
245    */
246    Vector3 Tetris::rotateVector(Vector3 position, unsigned int amount)
247    {
248        int temp = 0;
249        for(unsigned int i = 0; i < amount; i++)
250        {
251            temp = position.x;
252            position.x = -position.y;
253            position.y = temp;
254        }
255        return position;
256    }
257
258    /**
259    @brief
260        Starts the Tetris minigame.
261    */
262    void Tetris::start()
263    {
264        if (this->center_ != NULL) // There needs to be a TetrisCenterpoint, i.e. the area the game takes place.
265        {
266            // Create the futureBrick_
267            this->createBrick();
268            // Create the first brick.
269            this->createBrick();
270        }
271        else // If no centerpoint was specified, an error is thrown and the level is exited.
272        {
273            orxout(internal_error) << "Tetris: No Centerpoint specified." << endl;
274            GSLevel::startMainMenu();
275            return;
276        }
277
278        // Start the timer. After it has expired the stone is started.
279        this->starttimer_.startTimer();
280
281        // Set variable to temporarily force the player to spawn.
282        bool temp = this->bForceSpawn_;
283        this->bForceSpawn_ = true;
284
285        // Call start for the parent class.
286        Deathmatch::start();
287
288        // Reset the variable.
289        this->bForceSpawn_ = temp;
290    }
291
292    /**
293    @brief
294        Ends the Tetris minigame.
295    */
296    void Tetris::end()
297    {
298        this->activeBrick_->setVelocity(Vector3::ZERO);
299        if(this->activeBrick_ != NULL)
300        {
301            this->player_->stopControl();
302        }
303
304        this->cleanup();
305
306        // Call end for the parent class.
307        Deathmatch::end();
308    }
309
310    /**
311    @brief
312        Spawns player.
313    */
314    void Tetris::spawnPlayersIfRequested()
315    {
316        // Spawn a human player.
317        for (std::map<PlayerInfo*, Player>::iterator it = this->players_.begin(); it != this->players_.end(); ++it)
318            if (it->first->isHumanPlayer() && (it->first->isReadyToSpawn() || this->bForceSpawn_))
319                this->spawnPlayer(it->first);
320    }
321
322    /**
323    @brief
324        Spawns the input player.
325    @param player
326        The player to be spawned.
327    */
328    void Tetris::spawnPlayer(PlayerInfo* player)
329    {
330        assert(player);
331
332        if(this->player_ == NULL)
333        {
334            this->player_ = player;
335            this->players_[player].state_ = PlayerState::Alive;
336        }
337    }
338
339
340
341    void Tetris::startBrick(void)
342    {
343        if(this->player_ == NULL)
344            return;
345
346        unsigned int cameraIndex = 0;
347        if(this->activeBrick_ != NULL)
348        {
349            // Get camera settings
350            cameraIndex = this->activeBrick_->getCurrentCameraIndex();
351            this->player_->stopControl();
352        }
353
354        // Make the last brick to be created the active brick.
355        this->activeBrick_ = this->bricks_.back();
356
357        this->player_->startControl(this->activeBrick_);
358        this->activeBrick_->setVelocity(0.0f, -this->center_->getStoneSpeed(), 0.0f);
359        this->activeBrick_->setCameraPosition(cameraIndex);
360    }
361
362    void Tetris::createBrick(void)             //TODO: random rotation offset between 0 and 3 (times 90°)
363    {
364        // Use the futureBrick_ as new currentBrick by setting its position and storing it in this->bricks
365        if(this->futureBrick_ != NULL)
366        {
367            for (unsigned int i = 0; i < this->futureBrick_->getNumberOfStones(); i++)
368            {
369                this->stones_.push_back(this->futureBrick_->getStone(i));
370            }
371            float xPos = (this->center_->getWidth()/2 + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
372            float yPos = (this->center_->getHeight()-0.5f)*this->center_->getStoneSize();
373            this->futureBrick_->setPosition(xPos, yPos, 0.0f);
374            this->bricks_.push_back(this->futureBrick_);
375        }
376        // create new futureBrick_
377        this->futureBrick_ = new TetrisBrick(this->center_);
378
379
380        // Apply the stone template to the stone.
381        this->futureBrick_->addTemplate(this->center_->getBrickTemplate());
382
383        // Attach the brick to the Centerpoint and set the position of the brick to be at the left side.
384        this->center_->attach(this->futureBrick_);
385        float xPos = (this->center_->getWidth()*1.6 + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
386        float yPos = (this->center_->getHeight()-5.1f)*this->center_->getStoneSize();
387        this->futureBrick_->setPosition(xPos, yPos, 0.0f);
388        this->futureBrick_->setGame(this);
389    }
390
391
392    /**
393    @brief
394        Get the player.
395    @return
396        Returns a pointer to the player. If there is no player, NULL is returned.
397    */
398    PlayerInfo* Tetris::getPlayer(void) const
399    {
400        return this->player_;
401    }
402
403    /*TetrisCenterpoint* Tetris::getCenterpoint(void) const
404    {
405        return this->center_;
406    }*/
407
408    /**
409    @brief Set the TetrisCenterpoint (the playing field).
410    @param center A pointer to the TetrisCenterpoint to be set.
411    */
412    void Tetris::setCenterpoint(TetrisCenterpoint* center)
413    {
414        this->center_ = center;
415    }
416
417    /**
418    @brief Check each row if it is full. Removes all full rows. Update
419    @brief Manages score.
420    */
421    void Tetris::findFullRows()
422    {
423        unsigned int correctPosition = 0;
424        unsigned int stonesPerRow = 0;
425        for (unsigned int row = 0; row < this->center_->getHeight(); row++)
426        {
427            stonesPerRow = 0;
428            for(std::vector<TetrisStone*>::iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
429            {
430                correctPosition = static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize());
431                if(correctPosition == row)
432                {
433                    stonesPerRow++;
434                    if(stonesPerRow == this->center_->getWidth())
435                    {
436                        clearRow(row);
437                        row--; //the row counter has to be decreased in order to detect multiple rows!
438                        this->playerScored(this->player_);// add points
439                        //increase the stone's speed
440                        this->center_->setStoneSpeed(this->center_->getStoneSpeed()+1.0f);
441                    }
442                }
443            }
444        }
445    }
446
447    void Tetris::clearRow(unsigned int row)
448    {// clear the full row
449        for(std::vector<TetrisStone*>::iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
450        {
451            if(static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize()) == row)
452                (*it)->setPosition(Vector3(-50,-50,100));
453                //{(*it)->destroy(); this->stones_.erase(it); orxout()<< "destroy row "<<endl;}//experimental
454        }
455      // adjust height of stones above the deleted row //TODO: check if this could be a source of a bug.
456        for(std::vector<TetrisStone*>::iterator it2 = this->stones_.begin(); it2 != this->stones_.end(); ++it2)
457        {
458            if(static_cast<unsigned int>(((*it2)->getPosition().y - 5)/this->center_->getStoneSize()) > row)
459                (*it2)->setPosition((*it2)->getPosition()-Vector3(0,10,0));
460        }
461
462    }
463
464
465}
Note: See TracBrowser for help on using the repository browser.