Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/pCuts/src/modules/tetris/Tetris.cc @ 9090

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

Fighting with the controller. 'Undefined reference to orxonox::OverlayText' won. I don't see the error at the moment.

  • Property svn:eol-style set to native
File size: 14.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 *BUG b) the falling brick is set the wrong way after a (brick-brick) collision, if the falling brick was turned
29 *BUG c) destroying the old stones causes segfault -> WeakPointer as solution ?
30 *BUG d) wrong collision detection: sometimes stones "bounce off"
31 *BUG e) multiple rows are not cleared in one round
32 *
33 *
34 *TASK b) write a hud (show points gained; new brick)
35 *TASK c) end the game in a nicer way
36 *TASK d) save the highscore
37 *TASK e) eye candy
38 */
39
40/**
41    @file Tetris.cc
42    @brief Implementation of the Tetris class.
43*/
44
45#include "Tetris.h"
46
47#include "core/CoreIncludes.h"
48#include "core/EventIncludes.h"
49#include "core/command/Executor.h"
50
51#include "gamestates/GSLevel.h"
52
53#include "TetrisCenterpoint.h"
54#include "TetrisStone.h"
55#include "TetrisBrick.h"
56#include "infos/PlayerInfo.h"
57
58namespace orxonox
59{
60
61    CreateUnloadableFactory(Tetris);
62
63    /**
64    @brief
65        Constructor. Registers and initializes the object.
66    */
67    Tetris::Tetris(BaseObject* creator) : Deathmatch(creator)
68    {
69        RegisterObject(Tetris);
70
71        this->activeBrick_ = NULL;
72
73        // Pre-set the timer, but don't start it yet.
74        this->starttimer_.setTimer(1.0, false, createExecutor(createFunctor(&Tetris::startBrick, this)));
75        this->starttimer_.stopTimer();
76
77        this->player_ = NULL;
78        this->endGameCriteria_ = 0.0f;
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;
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 )) // wrong position??
167            {
168                return false;
169            }
170        }
171        return true;
172
173    }
174
175
176
177    bool Tetris::isValidStonePosition(TetrisStone* stone, const Vector3& position)
178    {
179        assert(stone);
180
181        // we use a reverse iterator because we have to check for collisions with the topmost stones first
182        for(std::vector<TetrisStone*>::const_reverse_iterator it = this->stones_.rbegin(); it != this->stones_.rend(); ++it)
183        {
184            if(this->activeBrick_->contains(*it))//skip the moving brick' stones
185                continue;
186            //Vector3 currentStonePosition = rotateVector((*it)->getPosition(), this->activeBrick_->getRotationCount());
187            const Vector3& currentStonePosition = (*it)->getPosition(); //!< Saves the position of the currentStone
188            //!< Saves the position of the currentStone
189
190            //filter out cases where the falling stone is already below a steady stone
191            if(position.y < currentStonePosition.y - this->center_->getStoneSize()/2.0f)
192                continue;
193            if((position.x == currentStonePosition.x) && (position.y < currentStonePosition.y + this->center_->getStoneSize()))
194            {
195                this->activeBrick_->setPosition(Vector3(this->activeBrick_->getPosition().x, currentStonePosition.y+this->center_->getStoneSize(), this->activeBrick_->getPosition().z));
196                return false;
197            }// This case applies if the stones overlap partially vertically
198        }
199
200        // after we checked for collision with all stones, we also check for collision with the bottom
201        if(position.y < this->center_->getStoneSize()/2.0f) //!< If the stone has reached the bottom of the level
202        {
203                int yOffset = stone->getPosition().y;//calculate offset
204                this->activeBrick_->setPosition(Vector3(this->activeBrick_->getPosition().x, this->center_->getStoneSize()/2.0f+yOffset, this->activeBrick_->getPosition().z));
205            return false;
206        }
207
208        return true;
209    }
210
211    bool Tetris::isValidBrickPosition(TetrisBrick* brick, const Vector3& position)
212    {
213        assert(brick);
214
215        for (unsigned int i = 0; i < brick->getNumberOfStones(); i++ )
216        {
217            TetrisStone* stone = brick->getStone(i);
218            Vector3 stonePosition = rotateVector(stone->getPosition(), brick->getRotationCount());
219            if(! this->isValidStonePosition(stone, position + stonePosition) )
220                return false;
221        }
222        return true;
223    }
224
225    /**
226    @brief
227        A Vector3 is rolled 90 * degrees * amount (anticlockwise rotation)
228    */
229    Vector3 Tetris::rotateVector(Vector3 position, unsigned int amount)
230    {
231        int temp = 0;
232        for(unsigned int i = 0; i < amount; i++)
233        {
234            temp = position.x;
235            position.x = -position.y;
236            position.y = temp;
237        }
238        return position;
239    }
240
241    /**
242    @brief
243        Starts the Tetris minigame.
244    */
245    void Tetris::start()
246    {
247        if (this->center_ != NULL) // There needs to be a TetrisCenterpoint, i.e. the area the game takes place.
248        {
249            // Create the first brick.
250            this->createBrick();
251        }
252        else // If no centerpoint was specified, an error is thrown and the level is exited.
253        {
254            orxout(internal_error) << "Tetris: No Centerpoint specified." << endl;
255            GSLevel::startMainMenu();
256            return;
257        }
258
259        // Start the timer. After it has expired the stone is started.
260        this->starttimer_.startTimer();
261
262        // Set variable to temporarily force the player to spawn.
263        bool temp = this->bForceSpawn_;
264        this->bForceSpawn_ = true;
265
266        // Call start for the parent class.
267        Deathmatch::start();
268
269        // Reset the variable.
270        this->bForceSpawn_ = temp;
271    }
272
273    /**
274    @brief
275        Ends the Tetris minigame.
276    */
277    void Tetris::end()
278    {
279        this->activeBrick_->setVelocity(Vector3::ZERO);
280        if(this->activeBrick_ != NULL)
281        {
282            this->player_->stopControl();
283        }
284
285        this->cleanup();
286
287        // Call end for the parent class.
288        Deathmatch::end();
289    }
290
291    /**
292    @brief
293        Spawns player.
294    */
295    void Tetris::spawnPlayersIfRequested()
296    {
297        // Spawn a human player.
298        for (std::map<PlayerInfo*, Player>::iterator it = this->players_.begin(); it != this->players_.end(); ++it)
299            if (it->first->isHumanPlayer() && (it->first->isReadyToSpawn() || this->bForceSpawn_))
300                this->spawnPlayer(it->first);
301    }
302
303    /**
304    @brief
305        Spawns the input player.
306    @param player
307        The player to be spawned.
308    */
309    void Tetris::spawnPlayer(PlayerInfo* player)
310    {
311        assert(player);
312
313        if(this->player_ == NULL)
314        {
315            this->player_ = player;
316            this->players_[player].state_ = PlayerState::Alive;
317        }
318    }
319
320
321
322    void Tetris::startBrick(void)
323    {
324        if(this->player_ == NULL)
325            return;
326
327        unsigned int cameraIndex = 0;
328        if(this->activeBrick_ != NULL)
329        {
330            // Get camera settings
331            cameraIndex = this->activeBrick_->getCurrentCameraIndex();
332            this->player_->stopControl();
333        }
334
335        // Make the last brick to be created the active brick.
336        this->activeBrick_ = this->bricks_.back();
337
338        this->player_->startControl(this->activeBrick_);
339        this->activeBrick_->setVelocity(0.0f, -this->center_->getStoneSpeed(), 0.0f);
340        this->activeBrick_->setCameraPosition(cameraIndex);
341    }
342
343    void Tetris::createBrick(void)             //TODO: random rotation offset between 0 and 3 (times 90°)
344    {
345        // Create a new brick and add it to the list of bricks && to the list of stones.
346        TetrisBrick* brick = new TetrisBrick(this->center_);
347        this->bricks_.push_back(brick);
348        for (unsigned int i = 0; i < brick->getNumberOfStones(); i++)
349        {
350            this->stones_.push_back(brick->getStone(i));
351        }
352
353        // Apply the stone template to the stone.
354        brick->addTemplate(this->center_->getBrickTemplate());
355
356        // Attach the brick to the Centerpoint and set the position of the brick to be at the top middle.
357        this->center_->attach(brick);
358        float xPos = (this->center_->getWidth()/2 + ((this->center_->getWidth() % 2)*2-1)/2.0f)*this->center_->getStoneSize();
359        float yPos = (this->center_->getHeight()-0.5f)*this->center_->getStoneSize();
360        brick->setPosition(xPos, yPos, 0.0f);
361        brick->setGame(this);
362    }
363
364
365    /**
366    @brief
367        Get the player.
368    @return
369        Returns a pointer to the player. If there is no player, NULL is returned.
370    */
371    PlayerInfo* Tetris::getPlayer(void) const
372    {
373        return this->player_;
374    }
375
376    /*TetrisCenterpoint* Tetris::getCenterpoint(void) const
377    {
378        return this->center_;
379    }*/
380
381    /**
382    @brief Set the TetrisCenterpoint (the playing field).
383    @param center A pointer to the TetrisCenterpoint to be set.
384    */
385    void Tetris::setCenterpoint(TetrisCenterpoint* center)
386    {
387        this->center_ = center;
388    }
389
390    /**
391    @brief Check each row if it is full. Remove all full rows. Let all stones above the deleted row sink down.
392    @brief Manage score.
393    */
394    void Tetris::findFullRows()
395    {
396        unsigned int correctPosition = 0;
397        unsigned int stonesPerRow = 0;
398        for (unsigned int row = 0; row < this->center_->getHeight(); row++)
399        {
400            stonesPerRow = 0;
401            for(std::vector<TetrisStone*>::const_reverse_iterator it = this->stones_.rbegin(); it != this->stones_.rend(); ++it)
402            {
403                correctPosition = static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize());
404                if(correctPosition == row)
405                {
406                    stonesPerRow++;
407                    if(stonesPerRow == this->center_->getWidth())
408                    {
409                        clearRow(row);
410                        this->playerScored(this->player_);// add points
411                        //increase the stone's speed
412                        this->center_->setStoneSpeed(this->center_->getStoneSpeed()+1.0f);
413                    }
414                }
415
416            }
417
418        }
419    }
420
421    void Tetris::clearRow(unsigned int row)
422    {// clear the full row
423        for(std::vector<TetrisStone*>::iterator it = this->stones_.begin(); it != this->stones_.end(); ++it)
424        {
425            if(static_cast<unsigned int>(((*it)->getPosition().y - 5)/this->center_->getStoneSize()) == row)
426                (*it)->setPosition(Vector3(-50,-50,100));
427                //{(*it)->destroy(); this->stones_.erase(it); orxout()<< "destroy row "<<endl;}//experimental
428        }
429      // adjust height of stones above the deleted row //TODO: check if this could be a source of a bug.
430        for(std::vector<TetrisStone*>::iterator it2 = this->stones_.begin(); it2 != this->stones_.end(); ++it2)
431        {
432            if(static_cast<unsigned int>(((*it2)->getPosition().y - 5)/this->center_->getStoneSize()) > row)
433                (*it2)->setPosition((*it2)->getPosition()-Vector3(0,10,0));
434        }
435
436    }
437
438
439}
Note: See TracBrowser for help on using the repository browser.