#include "blocks.hpp" #include "global_vars.hpp" #include "maploader.hpp" #include "objectids.hpp" #include "sprites.hpp" #include "editor_visitor.hpp" #include #include #include "visitors/mario_visitor.hpp" #include "visitors/bounce_visitor.hpp" #include "blocks/simpleblocks.hpp" #include "blocks/coineditorblock.hpp" #include "blocks/coinblock.hpp" #include "blocks/mushroomblock.hpp" #include "blocks/goombablock.hpp" #include "mario.hpp" #define CAN_BE_DESTROYED_FLAG 0x0000000000000001 #define HAS_COLLISION 0x0000000000000002 MarioBlock::MarioBlock(int x, int y, const std::shared_ptr &renderer, std::shared_ptr texture, SDL_Rect src, bool can_be_destroyed, bool destructible) : RectangleRender(x * BLOCK_SIZE, 1 - (16 - y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, renderer, texture, src) { _can_be_destroyed = can_be_destroyed; _destructible = can_be_destroyed && destructible; setMovementSpeed(1); _coins = 0; _mushroom = false; _base_src = src; } void MarioBlock::visit(SDLPP::Visitor &visitor) { #ifdef EDITOR if (!_tool && _terrain && visitor.getVisitorType() == VisitorType::Terrain) { destroy(); } if (!_tool && !_terrain && (visitor.getVisitorType() == VisitorType::Modifier || visitor.getVisitorType() == VisitorType::Character)) { destroy(); } #else if (visitor.getFromId() == MARIO_TOP_DETECT) { auto &mario_visitor = dynamic_cast(visitor); if (mario_visitor.canDestroy()) { if ((_destructible && !hasCoin() && !hasMushroom()) || (_can_be_destroyed && mario_visitor.isBig() && !hasCoin() && !hasMushroom())) { destroy(); } else if (_bouncable) { BounceVisitor bv; bv.setVisitorType(VisitorType::Terrain); setPos(getPos() - SDLPP::Vec2D(0, BLOCK_SIZE)); if (getCollisions().size() < 2) addCollision( SDLPP::RectColider(0.1, 0.1, 0.8, 0.8, BOUNCE_COLLISION)); updateSizeAndPosition(); g_playground->visitCollisions(*this, bv); setPos(getPos() + SDLPP::Vec2D(0, BLOCK_SIZE)); updateSizeAndPosition(); if (bv.canBounce()) bounce(); } if (hasCoin()) { removeCoin(); auto coin = createTerrainBlock(COIN_ID, LandType::OVERWORLD, renderer); coin->setPos(getPos()); std::dynamic_pointer_cast(coin)->setParent(this); dynamic_cast(visitor).setCoin(); dynamic_cast(visitor).setCoinBlock(coin); } if (hasMushroom()) { removeMushroom(); auto mushroom = createTerrainBlock(MUSHROOM_ID, LandType::OVERWORLD, renderer); mushroom->setPos(getPos()); std::dynamic_pointer_cast(mushroom)->setParent(this); std::dynamic_pointer_cast(mushroom)->setFireFlower(mario_visitor.isBig()); dynamic_cast(visitor).setMushroomBlock(mushroom); if(mario_visitor.isBig()) { harden(); } } } } #endif visitor.visit(*this); } void MarioBlock::setTool(bool tool) { _tool = tool; } void MarioBlock::setTerrain(bool terrain) { _terrain = terrain; } void MarioBlock::bounce() { if (_bouncing) { return; } _bouncing = true; og_pos = getPos(); ticks_to_bounce = bounce_ticks; setMovement(0, -bounce_speed); } void MarioBlock::travelToPos(const SDLPP::Vec2D &target) { if (_traveling) { return; } _traveling = true; _target = target; auto movement = (_target - getPos()); auto abs_mov_x = movement.getX(); if (abs_mov_x < 0) { abs_mov_x *= -1; } auto abs_mov_y = movement.getY(); if (abs_mov_y < 0) { abs_mov_y *= -1; } movement = movement / (abs_mov_x > abs_mov_y ? abs_mov_x : abs_mov_y); movement = movement * travel_speed; setMovement(movement.getX(), movement.getY()); } void MarioBlock::gravity(int ticks) { if (_on_ground) { return; } _ticks_till_gravity -= ticks; if (_ticks_till_gravity < 0) { addMovement(0, _gravity_acceleration); _ticks_till_gravity = _base_gravity_ticks; } } bool MarioBlock::isBouncing() const { return _bouncing; } bool MarioBlock::isTraveling() const { return _traveling; } void MarioBlock::custom_move(int ticks) { if (!_bouncing && !_traveling) { return; } if (_bouncing) { if (getMovement().getY() < 0) { ticks_to_bounce -= ticks; if (ticks_to_bounce < 0) { setMovement(0, bounce_speed); ticks_to_bounce = bounce_ticks; } } else { if (getPos().getY() >= og_pos.getY()) { setMovement(0, 0); setPos(getPos().getX(), og_pos.getY()); _bouncing = false; } } } if (_traveling) { bool overshot_x = (getMovement().getX() < 0 && getPos().getX() <= _target.getX()) || (getMovement().getX() > 0 && getPos().getX() >= _target.getX()); bool overshot_y = (getMovement().getY() < 0 && getPos().getY() <= _target.getY()) || (getMovement().getY() > 0 && getPos().getY() >= _target.getY()); if (overshot_x) { setPos(_target.getX(), getPos().getY()); setMovement(0, getMovement().getY()); } if (overshot_y) { setPos(getPos().getX(), _target.getY()); setMovement(getMovement().getX(), 0); } if (getMovement() == SDLPP::Vec2D(0, 0)) { _traveling = false; } } } void MarioBlock::setType(LandType::Value type) { _type = type; setWorldTypeSrc(_type); } LandType::Value MarioBlock::getType() const { return _type; } bool MarioBlock::hasCoin() { return _coins > 0; } bool MarioBlock::hasMushroom() { return _mushroom; } bool MarioBlock::hasTeleport() { return !_teleport_level.empty(); } void MarioBlock::removeCoin() { _coins--; } void MarioBlock::removeMushroom() { _mushroom = false; } void MarioBlock::addMushroom() { _mushroom = true; } void MarioBlock::setCoinCount(int coins) { _coins = coins; } void MarioBlock::setTeleportLevel(const std::string &level) { _teleport_level = level; } void MarioBlock::setDestructible(bool destructible) { _destructible = destructible; } void MarioBlock::ensureCollision() { if (getCollisions().size() == 0) { addCollision(SDLPP::RectColider(0, 0, 1, 1)); } } void MarioBlock::setWorldTypeSrc(LandType::Value world) { auto rect = _base_src; switch (world) { case LandType::OVERWORLD: rect.x += OVERWORLD_SHIFT.getX(); rect.y += OVERWORLD_SHIFT.getY(); break; case LandType::UNDERWORLD: rect.x += UNDERWORLD_SHIFT.getX(); rect.y += UNDERWORLD_SHIFT.getY(); break; case LandType::WATER: rect.x += WATER_SHIFT.getX(); rect.y += WATER_SHIFT.getY(); break; case LandType::BOWSER: rect.x += BOWSER_SHIFT.getX(); rect.y += BOWSER_SHIFT.getY(); break; } setTextureSourceRect(rect); } const std::vector possibleBlocks = { FLOOR_ID, STEP_ID, HILL_TOP_ID, HILL_DOTS_LEFT_ID, HILL_FILL_ID, HILL_INCLINE_ID, HILL_DOTS_RIGHT_ID, HILL_DECLINE_ID, CLOUD_LEFT_TOP_ID, CLOUD_MIDDLE_TOP_ID, CLOUD_RIGHT_TOP_ID, VINE_TOP_ID, CLOUD_LEFT_BOTTOM_ID, CLOUD_MIDDLE_BOTTOM_ID, CLOUD_RIGHT_BOTTOM_ID, VINE_BOTTOM_ID, BRICK_TOP_ID, BRICK_ID, FLAG_ID, WATER_TOP_ID, BUSH_LEFT_ID, BUSH_MIDDLE_ID, BUSH_RIGHT_ID, WATER_FILL_ID, PIPE_LEFT_TOP_ID, PIPE_RIGHT_TOP_ID, CASTLE_TOWER_ID, CASTLE_TOWER_FILLED_ID, PIPE_LEFT_BOTTOM_ID, PIPE_RIGHT_BOTTOM_ID, CASTLE_LEFT_ID, CASTLE_RIGHT_ID, POLE_TOP_ID, CASTLE_ENTRY_ID, SIDEWAY_PIPE_END_TOP_ID, SIDEWAY_PIPE_MIDDLE_TOP_ID, POLE_BOTTOM_ID, CASTLE_BLACK_ID, SIDEWAY_PIPE_END_BOTTOM_ID, SIDEWAY_PIPE_MIDDLE_BOTTOM_ID, SIDEWAY_PIPE_CONNECTOR_TOP_ID, TREE_PLATFORM_TOP_LEFT_ID, TREE_PLATFORM_TOP_MIDDLE_ID, TREE_PLATFORM_TOP_RIGHT_ID, SIDEWAY_PIPE_CONNECTOR_BOTTOM_ID, MUSHROOM_PLATFORM_TOP_LEFT_ID, MUSHROOM_PLATFORM_TOP_MIDDLE_ID, MUSHROOM_PLATFORM_TOP_RIGHT_ID, TREE_PLATFORM_BARK_ID, MUSHROOM_PLATFORM_BARK_TOP_ID, TREE_LEAVES_TOP_ID, TREE_LEAVES_SMALL_ID, CANNON_TOWER_ID, MUSHROOM_PLATFORM_BARK_BOTTOM_ID, TREE_LEAVES_BOTTOM_ID, TREE_BARK_ID, CANNON_PEDESTAL_ID, CANNON_ID, }; const std::vector possibleMods = { DESTRUCTIBLE_MODIFIER_ID, BACKGROUND_MODIFIER_ID, COIN_MODIFIER_ID, MUSHROOM_MODIFIER_ID, TELEPORT_MODIFIER_ID, }; const std::vector possibleCharacters = { MARIO_ID, GOOMBA_ID, }; const std::vector possibleLands = { LandType::OVERWORLD, LandType::UNDERWORLD, LandType::WATER, LandType::BOWSER }; std::shared_ptr createBlockById(uint64_t id, int x, int y, std::shared_ptr &renderer) { std::shared_ptr result = nullptr; switch (id) { case FLOOR_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case HILL_INCLINE_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case HILL_DECLINE_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case HILL_DOTS_RIGHT_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case HILL_DOTS_LEFT_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case HILL_FILL_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case HILL_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case BUSH_LEFT_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case BUSH_MIDDLE_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case BUSH_RIGHT_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CLOUD_LEFT_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CLOUD_MIDDLE_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CLOUD_RIGHT_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CLOUD_LEFT_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CLOUD_MIDDLE_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CLOUD_RIGHT_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case PIPE_LEFT_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case PIPE_RIGHT_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case PIPE_LEFT_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case PIPE_RIGHT_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CASTLE_LEFT_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CASTLE_RIGHT_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CASTLE_BLACK_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CASTLE_ENTRY_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CASTLE_TOWER_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CASTLE_TOWER_FILLED_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case VINE_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case VINE_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case POLE_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case POLE_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case FLAG_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case STEP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case BRICK_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case BRICK_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case SIDEWAY_PIPE_END_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case SIDEWAY_PIPE_END_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case SIDEWAY_PIPE_MIDDLE_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case SIDEWAY_PIPE_MIDDLE_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case SIDEWAY_PIPE_CONNECTOR_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case SIDEWAY_PIPE_CONNECTOR_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case TREE_PLATFORM_TOP_LEFT_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case TREE_PLATFORM_TOP_MIDDLE_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case TREE_PLATFORM_TOP_RIGHT_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case TREE_PLATFORM_BARK_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case WATER_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case WATER_FILL_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case MUSHROOM_PLATFORM_TOP_LEFT_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case MUSHROOM_PLATFORM_TOP_MIDDLE_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case MUSHROOM_PLATFORM_TOP_RIGHT_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case MUSHROOM_PLATFORM_BARK_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case MUSHROOM_PLATFORM_BARK_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case TREE_BARK_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case TREE_LEAVES_SMALL_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case TREE_LEAVES_TOP_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case TREE_LEAVES_BOTTOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CANNON_TOWER_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CANNON_PEDESTAL_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case CANNON_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case MARIO_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer, nullptr)); break; case GOOMBA_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; #ifdef EDITOR case DESTRUCTIBLE_MODIFIER_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case BACKGROUND_MODIFIER_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case COIN_MODIFIER_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case MUSHROOM_MODIFIER_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case TELEPORT_MODIFIER_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; #endif #ifndef EDITOR case COIN_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; case MUSHROOM_ID: result = std::static_pointer_cast( std::make_shared(x, y, renderer)); break; #endif } return result; } std::shared_ptr createBlock(std::shared_ptr &renderer, int x, int y, uint64_t id, LandType::Value land_type, bool destructible, bool editor) { auto block = createBlockById(id, x, y, renderer); if (block == nullptr) { return nullptr; } block->setAlignment(SDLPP::OBJ_CENTER, SDLPP::OBJ_CENTER); block->setStatic(); block->setType(land_type); if (destructible) { block->setDestructible(); } if (editor) { block->ensureCollision(); } return block; } // TODO coin count std::shared_ptr createTerrainBlock(uint64_t block_id, LandType::Value type, std::shared_ptr &renderer, int x, int y, bool destructible, bool editor) { return createBlock(renderer, x, y, block_id, type, destructible, editor); } std::shared_ptr createTerrainBlock(uint64_t block_id, LandType::Value type, std::shared_ptr &renderer, bool destructible, bool editor) { return createTerrainBlock(block_id, type, renderer, 0, 0, destructible, editor); } std::shared_ptr createMario(LandType::Value type, std::shared_ptr &renderer, int x, int y, bool editor) { // TODO add type additions auto mario = createBlock(renderer, x, y, MARIO_ID, type, false, true); if (editor) { mario->setTerrain(false); mario->removeCollisions(); mario->ensureCollision(); } return mario; } enum BlockRole::Value getBlockRole(uint64_t id) { if (id >= 0x7000) return BlockRole::TERRAIN; if (id == MARIO_ID) return BlockRole::MARIO; if (id < MARIO_ID) return BlockRole::CHARACTER; return BlockRole::MODIFIER; } void MarioBlock::setBaseRect(SDL_Rect rect) { _base_src = rect; setType(getType()); } void MarioBlock::checkVisibility(double rightmost_x) { // we assume that object's X > 0 as otherwise it would be destroyed if (!getHidden() && getAbsolutePos().getX() < rightmost_x) { _was_visible = true; } } bool MarioBlock::wasVisible() const { return _was_visible; } void MarioBlock::harden() { _can_be_destroyed = false; setDestructible(false); setBouncable(false); setTextureSourceRect(HARD_SRC); } const std::string &MarioBlock::getTeleportLevel() { return _teleport_level; }