From 625e7866a410558b5da7cee04fdb4b374dbbbf43 Mon Sep 17 00:00:00 2001 From: zvon Date: Thu, 10 Sep 2020 16:50:36 +0200 Subject: [PATCH] TETRIS: Show shadow of where the piece will fall --- sdlpp.hpp | 59 +++++++++-- tetris.cpp | 300 ++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 303 insertions(+), 56 deletions(-) diff --git a/sdlpp.hpp b/sdlpp.hpp index 320b378..9c4020d 100644 --- a/sdlpp.hpp +++ b/sdlpp.hpp @@ -1,3 +1,4 @@ +// TODO mutex guard instead of lock/unlock #ifndef SDLPP_HPP #define SDLPP_HPP @@ -293,7 +294,7 @@ class Scene; class RenderObject { public: - RenderObject( std::shared_ptr< Renderer > &r ) : renderer( r ) {} + RenderObject( const std::shared_ptr< Renderer > &r ) : renderer( r ) {} virtual ~RenderObject() {} virtual void render() = 0; virtual int leftmost() = 0; @@ -307,8 +308,9 @@ public: virtual void specialAction( int code ) = 0; virtual std::pair< std::pair< double, double >, std::pair< double, double > > - getDoubleRect() = 0; + getDoubleRect() const = 0; virtual void setPos( double x, double y ) = 0; + virtual void setPos(const std::pair &pos) = 0; virtual std::pair< double, double > getPos() const = 0; bool colidesWith( const RenderObject &other ) const { if ( !hasCollisions() || !other.hasCollisions() || getHidden() || @@ -336,7 +338,7 @@ public: getCollisions() const { return collisions; } - virtual void setTexture( std::shared_ptr< Texture > &t ) { + virtual void setTexture( const std::shared_ptr< Texture > &t ) { texture = t; } virtual void setTexture( const std::string &img_path ) { @@ -421,6 +423,9 @@ public: void setStatic(bool stat = true) { is_static = stat; } + std::shared_ptr getRenderer() const { + return renderer; + } protected: std::vector< std::shared_ptr< CollisionPolygon > > collisions; @@ -475,6 +480,36 @@ public: } render_mutex.unlock(); } + void setZIndex( const std::shared_ptr< RenderObject > &obj, int index ) { + std::lock_guard guard(render_mutex); + int original_index = 0; + for(long unsigned int i = 0; i < render_objects.size(); i++) { + if(render_objects[i] == obj) { + original_index = i; + } + } + if(original_index == index) + return; + if(original_index > index) + original_index++; + render_objects.insert(render_objects.begin() + index, obj); + render_objects.erase(render_objects.begin() + original_index); + } + void moveDownZ( const std::shared_ptr &obj ) { + moveZ(obj, -1); + } + void moveUpZ( const std::shared_ptr &obj ) { + moveZ(obj, 1); + } + void moveZ( const std::shared_ptr &obj, int addition ) { + int original_index = 0; + for(long unsigned int i = 0; i < render_objects.size(); i++) { + if(render_objects[i] == obj) { + original_index = i; + } + } + std::iter_swap(render_objects.begin() + original_index, render_objects.begin() + original_index + addition); + } //TODO addCollision std::shared_ptr< RenderObject > getObject( int index ) { return render_objects[index]; @@ -847,7 +882,7 @@ public: RectangleRender() = delete; virtual ~RectangleRender(){}; RectangleRender( double x, double y, double w, double h, - std::shared_ptr< Renderer > &r ) + const std::shared_ptr< Renderer > &r ) : RenderObject( r ) { og_x = x_ = x; og_y = y_ = y; @@ -856,19 +891,20 @@ public: updateSizeAndPosition(); } RectangleRender( double x, double y, double w, double h, - std::shared_ptr< Renderer > &r, - std::shared_ptr< Texture > &t ) + const std::shared_ptr< Renderer > &r, + const std::shared_ptr< Texture > &t ) : RectangleRender( x, y, w, h, r ) { setTexture( t ); } RectangleRender( double x, double y, double w, double h, - std::shared_ptr< Renderer > &r, + const std::shared_ptr< Renderer > &r, const std::string &img_or_color, bool is_polygon = false ) : RectangleRender( x, y, w, h, r ) { if ( !is_polygon ) { setTexture( img_or_color ); } else { setColor( img_or_color ); + color = img_or_color; } } virtual void setColor( const std::string &color ) override { @@ -924,7 +960,7 @@ public: virtual void custom_move( int /*UNUSED*/ ) override {} virtual std::pair< std::pair< double, double >, std::pair< double, double > > - getDoubleRect() override { + getDoubleRect() const override { return { { og_x, og_y }, { og_w, og_h } }; } virtual void setPos( double x, double y ) override { @@ -932,6 +968,9 @@ public: og_y = y; updateSizeAndPosition(); } + virtual void setPos(const std::pair &pos) override { + setPos(pos.first, pos.second); + } virtual std::pair< double, double > getPos() const override { return { og_x, og_y }; } @@ -985,6 +1024,9 @@ public: // TODO ACTUALLY copy, don't just copy pointers to textures and whatnot, create new textures!!! return std::make_shared(*this); } + std::string getColor() const { + return color; + } protected: void updateXY() { @@ -1014,6 +1056,7 @@ protected: double h_; bool centerx = false; SDL_Rect rect; + std::string color = ""; }; class TextRenderer : public RectangleRender { diff --git a/tetris.cpp b/tetris.cpp index 5d6ccb1..e099a5d 100644 --- a/tetris.cpp +++ b/tetris.cpp @@ -7,6 +7,9 @@ #define COLIDER_ID 0x00000001 #define BRICK_ID 0x00000002 #define GAME_OVER 0x00000003 +#define SHADOW_ID 0x00000004 +#define BORDER_ID 0x00000005 +#define FLOOR_ID 0x00000006 #define LEFT_BORDER 0.3 #define RIGHT_BORDER 0.7 @@ -16,6 +19,7 @@ #define TICKS_TILL_FALL 500 #define TICKS_TILL_DESCEND 50 +#define TICKS_TILL_MOVE 100 #define TETRIS_BRICK 0 #define TETRIS_T 1 @@ -35,6 +39,7 @@ int game_over_select = 0; int game_over_max = 1; int ticks_till_fall = TICKS_TILL_FALL; int ticks_till_descend = TICKS_TILL_DESCEND; +int ticks_till_movement = TICKS_TILL_MOVE; std::vector< std::shared_ptr< SDLPP::RectangleRender > > pause_options; std::vector< std::shared_ptr< SDLPP::RectangleRender > > game_over_options; std::shared_ptr< SDLPP::TextRenderer > score_texture; @@ -55,16 +60,26 @@ class TetrisBlock : public SDLPP::RectangleRender { public: TetrisBlock() = delete; TetrisBlock( double x, double y, double w, double h, - std::shared_ptr< SDLPP::Renderer > &r, - const std::string &img_or_color, bool is_polygon = false, - int index = 0 ) + const std::shared_ptr< SDLPP::Renderer > &r, + const std::string &img_or_color, bool is_polygon, + int index, std::shared_ptr scene ) : RectangleRender( x, y, w, h, r, img_or_color, is_polygon ) { _index = index; bag[_index]--; + _scene = scene; } + TetrisBlock( const TetrisBlock &other ) : TetrisBlock(other.getDoubleRect().first.first,other.getDoubleRect().first.second,other.getDoubleRect().second.first,other.getDoubleRect().second.second,other.getRenderer(), other.getColor(), true, other._index, other._scene) {} ~TetrisBlock() { bag[_index]++; } + virtual std::shared_ptr copySelf() { + return std::make_shared(*this); + } + std::shared_ptr copyInScene() { + auto ret = std::shared_ptr(new TetrisBlock(*this)); + _scene->addObject(ret); + return ret; + } bool isSamePos(const SDLPP::RenderObject &other) const { auto mypos = getPos(); auto otherpos = other.getPos(); @@ -77,8 +92,24 @@ public: private: int _index = 0; + std::shared_ptr _scene; }; +std::shared_ptr< TetrisBlock > +createTetrisBlock( double x, double y, const std::string &color, + const std::string &outline, int index, + std::shared_ptr< SDLPP::Renderer > renderer, + std::shared_ptr< SDLPP::Scene > scene ) { + auto ret = std::make_shared< TetrisBlock >( x, y, BLOCK_SIZE, BLOCK_SIZE, + renderer, color, true, index, scene ); + ret->setOutlineColor( outline ); + ret->addCollision( SDLPP::Rect( 0.1, 0.1, 0.8, 0.8 ) ); + ret->setId( BRICK_ID ); + ret->centerX(); + scene->addObject( ret ); + return ret; +} + class TetrisPiece { public: TetrisPiece() { @@ -151,6 +182,19 @@ public: piece->setPos( pos.first, pos.second ); } } + void setPos(const std::pair &pos) { + setPos(pos.first, pos.second); + } + std::pair getPos() { + auto &piece = pieces[0]; + auto &relpositions = pieces_rel_position[0]; + auto pos = piece->getPos(); + pos.first += relpositions[0] * BLOCK_SIZE; + pos.first -= relpositions[1] * BLOCK_SIZE; + pos.second += relpositions[2] * BLOCK_SIZE; + pos.second -= relpositions[3] * BLOCK_SIZE; + return pos; + } void clear() { pieces.clear(); pieces_rel_position.clear(); @@ -161,9 +205,18 @@ public: void stopDescend() { descend = false; } + void startMovement() { + userMovement += 1; + } + void stopMovement() { + userMovement -= 1; + } bool isDescending() { return descend; } + bool isMoving() { + return userMovement > 0; + } bool isLeft(const SDLPP::RenderObject &block) const { return isPosition(block, 0); } @@ -180,6 +233,34 @@ public: rotate_allowed = false; } + void turnIntoShadow() { + for(auto &block : getObjects() ) { + block->setId(SHADOW_ID); + block->setColor("#AAAAAAAA"); + } + } + std::shared_ptr copySelf() { + auto ret = std::make_shared(); + for(int i = 0; i < 4; i++) { + auto block = pieces[i]->copyInScene(); + block->centerX(); + ret->addBlockInPos(block, pieces_rel_position[i]); + } + return ret; + } + void destroy() { + for(auto &x : getObjects()) { + x->destroy(); + } + } + void addMovement(int x, int y) { + movement.first += x; + movement.second += y; + } + std::pair getMovement() const { + return movement; + } + private: bool isPosition(const SDLPP::RenderObject &block, int pos) const { for(int i = 0; i < 4; i++) { @@ -189,16 +270,28 @@ private: } return false; } + void resetBlock(int index, std::shared_ptr< TetrisBlock > piece) { + piece->setPos(pieces[index]->getPos()); + pieces[index] = piece; + } + void addBlockInPos(std::shared_ptr piece, const std::vector &relpos) { + pieces.push_back( piece ); + pieces_rel_position.push_back(relpos); + } std::vector< std::vector< int > > pieces_rel_position; std::vector< std::shared_ptr< TetrisBlock > > pieces; std::vector< std::pair< double, double > > original_pos; bool descend = false; + int userMovement = 0; bool rotate_allowed = true; + std::pair movement = {0,0}; }; std::vector< std::shared_ptr< SDLPP::RectangleRender > > line_coliders; std::shared_ptr< TetrisPiece > cur_object; std::shared_ptr< TetrisPiece > next_object; +std::shared_ptr< TetrisPiece > cur_shadow; +std::shared_ptr< SDLPP::RectangleRender > shadow_colider; void doInput( std::shared_ptr< SDLPP::Scene > scene ); void doInputPause(); @@ -207,21 +300,6 @@ bool quit = false; std::mutex movement_mutex; -std::shared_ptr< TetrisBlock > -createTetrisBlock( double x, double y, const std::string &color, - const std::string &outline, int index, - std::shared_ptr< SDLPP::Renderer > renderer, - std::shared_ptr< SDLPP::Scene > scene ) { - auto ret = std::make_shared< TetrisBlock >( x, y, BLOCK_SIZE, BLOCK_SIZE, - renderer, color, true, index ); - ret->setOutlineColor( outline ); - ret->addCollision( SDLPP::Rect( 0.1, 0.1, 0.8, 0.8 ) ); - ret->setId( BRICK_ID ); - ret->centerX(); - scene->addObject( ret ); - return ret; -} - std::shared_ptr< TetrisPiece > tetrisBrick( std::shared_ptr< SDLPP::Renderer > renderer, std::shared_ptr< SDLPP::Scene > scene ) { @@ -465,6 +543,38 @@ void addStuff( SDLPP::Scene &scene, std::shared_ptr< SDLPP::Renderer > &r ) { line_coliders.push_back( colider ); scene.addObject( colider ); } + + auto shcol = std::make_shared< SDLPP::RectangleRender >( 0, TOP_BORDER, BLOCK_SIZE, BOTTOM_BORDER - TOP_BORDER, r ); + shcol->addCollision(SDLPP::Rect( 0.1, 0.01, 0.8, 0.98 )); + shcol->setId( COLIDER_ID ); + shcol->setStatic(); + shcol->centerX(); + shadow_colider = shcol; + scene.addObject( shadow_colider ); + + auto border = std::make_shared< SDLPP::RectangleRender >( LEFT_BORDER - 1, TOP_BORDER, 1, BOTTOM_BORDER, r); + border->setId( BORDER_ID ); + border->setStatic(); + border->centerX(); + border->addCollision(SDLPP::Rect( 0, 0, 0.99, 1)); + border->setColiderColor("#FF00FF"); + scene.addObject(border); + + border = std::make_shared< SDLPP::RectangleRender >( RIGHT_BORDER, TOP_BORDER, 1, BOTTOM_BORDER, r); + border->setId( BORDER_ID ); + border->setStatic(); + border->centerX(); + border->addCollision(SDLPP::Rect( 0.01, 0, 1, 1)); + border->setColiderColor("#FF00FF"); + scene.addObject(border); + + auto floor = std::make_shared< SDLPP::RectangleRender >( LEFT_BORDER, BOTTOM_BORDER, RIGHT_BORDER - LEFT_BORDER, 1, r); + floor->setId( FLOOR_ID ); + floor->setStatic(); + floor->centerX(); + floor->addCollision(SDLPP::Rect(0, 0.01, 1, 1)); + floor->setColiderColor("#00FF00"); + scene.addObject(floor); } void updateScore() { @@ -535,7 +645,8 @@ void quitGame() { quit = true; } -void checkRotation( std::shared_ptr piece, SDLPP::Scene &scene ) { +bool checkRotation( std::shared_ptr piece, SDLPP::Scene &scene ) { + bool ret = true; bool crash = true; int left = 0x01; int right = 0x02; @@ -564,6 +675,7 @@ void checkRotation( std::shared_ptr piece, SDLPP::Scene &scene ) { if ( crash ) { if ( flags & bottom || (flags & left && flags & right) ) { piece->revert(); + ret = false; } else { if( flags & left ) piece->movePiece(BLOCK_SIZE, 0); @@ -596,6 +708,7 @@ void checkRotation( std::shared_ptr piece, SDLPP::Scene &scene ) { break; if((is_left && is_right) || (is_left && was_right) || (is_right && was_left)) { piece->revert(); + ret = false; break; } was_left = is_left; @@ -607,13 +720,55 @@ void checkRotation( std::shared_ptr piece, SDLPP::Scene &scene ) { // either bottom or up if(!is_left && !is_right) { piece->revert(); + ret = false; break; } } + return ret; +} + +void updateShadow(SDLPP::Scene &scene) { + if(!cur_object) { + cur_shadow->destroy(); + cur_shadow.reset(); + return; + } + cur_shadow->setPos(cur_object->getPos()); + double shadow_drop = BOTTOM_BORDER; + auto &invalid_objects = cur_object->getObjects(); + for( auto &x : cur_shadow->getObjects() ) { + if(BOTTOM_BORDER - x->getPos().second < shadow_drop) + shadow_drop = BOTTOM_BORDER - x->getPos().second; + shadow_colider->setPos(x->getPos().first, TOP_BORDER); + auto collisions = scene.getCollisions( *shadow_colider, { BRICK_ID } ); + auto curY = x->getPos().second; + for(auto &col : collisions) { + auto colY = col->getPos().second; + if(std::find(invalid_objects.begin(), invalid_objects.end(), col) != invalid_objects.end()) + continue; + if(colY - curY < shadow_drop) + shadow_drop = colY - curY; + } + } + shadow_drop -= BLOCK_SIZE; + cur_shadow->setPos(cur_shadow->getPos().first, cur_shadow->getPos().second + shadow_drop); +} + +bool validPos(SDLPP::Scene &scene, std::shared_ptr piece) { + auto ret = true; + for ( auto &x : piece->getObjects() ) { + if(x->getId() != 2) + std::cout << "ID: " << x->getId() << std::endl; + auto collisions = scene.getCollisions( *x, { BRICK_ID, FLOOR_ID, BORDER_ID } ); + if ( collisions.size() > 1 ) { + ret = false; + break; + } + } + return ret; } void handleKeyDown( SDL_Keycode key, SDLPP::Scene &scene ) { - bool crash = false; switch ( key ) { case SDLK_ESCAPE: { @@ -627,44 +782,42 @@ void handleKeyDown( SDL_Keycode key, SDLPP::Scene &scene ) { if(!cur_object) break; cur_object->movePiece(-BLOCK_SIZE, 0); - - for ( auto &x : cur_object->getObjects() ) { - auto collisions = scene.getCollisions( *x, { BRICK_ID } ); - auto pos = x->getPos(); - if ( collisions.size() > 1 || pos.first < ( LEFT_BORDER - 0.01 ) ) - crash = true; - } - if ( crash ) + if(!validPos(scene, cur_object)) cur_object->movePiece(BLOCK_SIZE, 0); + updateShadow(scene); + + ticks_till_movement = 2*TICKS_TILL_MOVE; + cur_object->startMovement(); + cur_object->addMovement(-1,0); break; case SDLK_RIGHT: case SDLK_d: if(!cur_object) break; cur_object->movePiece(BLOCK_SIZE, 0); - - for ( auto &x : cur_object->getObjects() ) { - auto collisions = scene.getCollisions( *x, { BRICK_ID } ); - auto pos = x->getPos(); - if ( collisions.size() > 1 || pos.first > RIGHT_BORDER - BLOCK_SIZE + 0.01 ) { - crash = true; - } - } - if ( crash ) + if(!validPos(scene, cur_object)) cur_object->movePiece(-BLOCK_SIZE, 0); + updateShadow(scene); + + ticks_till_movement = 2*TICKS_TILL_MOVE; + cur_object->startMovement(); + cur_object->addMovement(1,0); break; case SDLK_DOWN: case SDLK_s: if(!cur_object) break; cur_object->startDescend(); + cur_object->addMovement(0,1); break; case SDLK_UP: case SDLK_w: if(!cur_object) break; cur_object->rotate(); - checkRotation( cur_object, scene ); + if( checkRotation( cur_object, scene ) ) + cur_shadow->rotate(); + updateShadow(scene); break; case SDLK_r: scene.getRenderer().setRenderColiders( @@ -675,9 +828,40 @@ void handleKeyDown( SDL_Keycode key, SDLPP::Scene &scene ) { } void handleKeyUp( SDL_Keycode key ) { - if ( key == SDLK_DOWN || key == SDLK_s ) { - if(cur_object) - cur_object->stopDescend(); + switch(key) { + case SDLK_DOWN: + case SDLK_s: + if(!cur_object) + break; + if(cur_object->isDescending()) { + cur_object->stopDescend(); + cur_object->addMovement(0,-1); + ticks_till_descend = TICKS_TILL_DESCEND; + } + break; + case SDLK_LEFT: + case SDLK_a: + if(!cur_object) + break; + if(cur_object->isMoving()) { + cur_object->stopMovement(); + cur_object->addMovement(1,0); + if(cur_object->isMoving()) + ticks_till_movement = TICKS_TILL_MOVE; + } + break; + case SDLK_RIGHT: + case SDLK_d: + if(!cur_object) + break; + if(cur_object->isMoving()) { + cur_object->stopDescend(); + cur_object->addMovement(-1,0); + if(cur_object->isMoving()) + ticks_till_movement = TICKS_TILL_MOVE; + } + default: + break; } } @@ -866,30 +1050,43 @@ void pollEventsGameOver() { } void moveThem( std::shared_ptr< SDLPP::Scene > scene, int ticks ) { + auto movement = cur_object->getMovement(); ticks_till_fall -= ticks; if ( cur_object->isDescending() ) ticks_till_descend -= ticks; + if ( cur_object->isMoving() ) + ticks_till_movement -= ticks; if ( ticks_till_fall > 0 ) { if ( cur_object->isDescending() && ticks_till_descend <= 0 ) { ticks_till_descend = TICKS_TILL_DESCEND; - goto fall; + cur_object->movePiece(0, movement.second * BLOCK_SIZE); + if(!validPos(*scene, cur_object)) { + cur_object->movePiece(0, movement.second * -BLOCK_SIZE); + return; + } else + goto check_floor; + } + if ( cur_object->isMoving() && ticks_till_movement <= 0 ) { + ticks_till_movement = TICKS_TILL_MOVE; + cur_object->movePiece(movement.first * BLOCK_SIZE, 0); + if(!validPos(*scene, cur_object)) { + cur_object->movePiece(movement.first * -BLOCK_SIZE, 0); + return; + } else + goto check_floor; } return; } ticks_till_fall = TICKS_TILL_FALL; -fall: cur_object->movePiece(0, BLOCK_SIZE); +check_floor: bool fell = false; for ( auto &x : cur_object->getObjects() ) { - auto collisions = scene->getCollisions( *x, { BRICK_ID } ); + auto collisions = scene->getCollisions( *x, { BRICK_ID, FLOOR_ID } ); if ( collisions.size() > 1 ) { fell = true; break; } - if ( x->getPos().second >= 1 ) { - fell = true; - break; - } } if ( fell ) { cur_object->movePiece(0, -BLOCK_SIZE); @@ -903,6 +1100,7 @@ fall: } cur_object.reset(); } + updateShadow(*scene); } void doInput( std::shared_ptr< SDLPP::Scene > scene ) { @@ -1008,6 +1206,12 @@ int main() { std::lock_guard< std::mutex > guard( movement_mutex ); cur_object = next_object; cur_object->setPos( 0.5, TOP_BORDER - BLOCK_SIZE ); + cur_shadow = cur_object->copySelf(); + cur_shadow->turnIntoShadow(); + for(auto &piece : cur_shadow->getObjects()) { + active_scene->moveZ(piece, -4); + } + updateShadow(*main_scene); auto rand_index = std::rand() / ( ( RAND_MAX + 1u ) / 7 ); int retries = 0; while ( bag[rand_index] < 4 ) {