#include "transform_system.h" #include "game.h" #include "map.h" void TransformSystem::initSprite(int spriteId, vec2i loc) { Sprite& sprite = game_.getSprite(spriteId); sprite.loc = loc; spritesByY_.emplace(loc.y(), spriteId); } void TransformSystem::setUpCollision(int spriteId, vec2i offset, vec2i size, bool solid) { Sprite& sprite = game_.getSprite(spriteId); sprite.collidable = true; sprite.collisionOffset = offset; sprite.collisionSize = size; sprite.solid = solid; addCollidable(spriteId); } void TransformSystem::moveSprite(int spriteId, vec2i newLoc) { Sprite& sprite = game_.getSprite(spriteId); if (sprite.collidable) { removeCollidable(spriteId); } bool changedY = (sprite.loc.y() != newLoc.y()); if (changedY) { spritesByY_.erase(std::make_tuple(sprite.loc.y(), spriteId)); } sprite.loc = newLoc; if (changedY) { spritesByY_.emplace(newLoc.y(), spriteId); } if (sprite.collidable) { addCollidable(spriteId); } } CollisionResult TransformSystem::checkCollision(int spriteId, vec2i newLoc, Direction dir) { CollisionResult result; Sprite& sprite = game_.getSprite(spriteId); const Map& map = game_.getMap(); bool blocked = false; vec2i oldColUL = sprite.loc + sprite.collisionOffset; vec2i oldColDR = oldColUL + sprite.collisionSize; vec2i newColUL = newLoc + sprite.collisionOffset; vec2i newColDR = newColUL + sprite.collisionSize; vec2i oldTileUL = oldColUL / map.getTileSize(); vec2i newTileUL = newColUL / map.getTileSize(); vec2i oldTileDR = oldColDR / map.getTileSize(); vec2i newTileDR = newColDR / map.getTileSize(); if (dirHasDir(sprite.dir, Direction::right)) { if (newTileDR.x() > oldTileDR.x()) { for (int y = newTileUL.y(); y <= newTileDR.y(); y++) { if (map.isBlocked(newTileDR.x(), y)) { result.horiz.blocked = true; result.horiz.dir = Direction::right; break; } } } if (!result.horiz.blocked) { auto it = rightCollidables_.lower_bound({oldColDR.x(), INT_MAX}); for (; (it != std::end(rightCollidables_) && std::get<0>(it->first) <= newColDR.x()); it++) { if (newColDR.y() >= it->second.lower && newColUL.y() <= it->second.upper) { int colliderSpriteId = std::get<1>(it->first); Sprite& collider = game_.getSprite(colliderSpriteId); result.horiz.blocked = collider.solid; result.horiz.dir = Direction::right; result.horiz.colliderSprite = colliderSpriteId; break; } } } } if (dirHasDir(sprite.dir, Direction::left)) { if (newTileUL.x() < oldTileUL.x()) { for (int y = newTileUL.y(); y <= newTileDR.y(); y++) { if (map.isBlocked(newTileUL.x(), y)) { result.horiz.blocked = true; result.horiz.dir = Direction::left; break; } } } if (!result.horiz.blocked) { auto it = leftCollidables_.lower_bound({oldColUL.x(), 0}); for (; (it != std::end(leftCollidables_) && std::get<0>(it->first) >= newColUL.x()); it++) { if (newColDR.y() >= it->second.lower && newColUL.y() <= it->second.upper) { int colliderSpriteId = std::get<1>(it->first); Sprite& collider = game_.getSprite(colliderSpriteId); result.horiz.blocked = collider.solid; result.horiz.dir = Direction::left; result.horiz.colliderSprite = colliderSpriteId; break; } } } } if (dirHasDir(sprite.dir, Direction::down)) { if (newTileDR.y() > oldTileDR.y()) { for (int x = newTileUL.x(); x <= newTileDR.x(); x++) { if (map.isBlocked(x, newTileDR.y())) { result.vert.blocked = true; result.vert.dir = Direction::down; break; } } } if (!result.vert.blocked) { auto it = downCollidables_.lower_bound({oldColDR.y(), INT_MAX}); for (; (it != std::end(downCollidables_) && std::get<0>(it->first) <= newColDR.y()); it++) { if (newColDR.x() >= it->second.lower && newColUL.x() <= it->second.upper) { int colliderSpriteId = std::get<1>(it->first); Sprite& collider = game_.getSprite(colliderSpriteId); result.horiz.blocked = collider.solid; result.horiz.dir = Direction::down; result.horiz.colliderSprite = colliderSpriteId; break; } } } } if (dirHasDir(sprite.dir, Direction::up)) { if (newTileUL.y() < oldTileUL.y()) { for (int x = newTileUL.x(); x <= newTileDR.x(); x++) { if (map.isBlocked(x, newTileUL.y())) { result.vert.blocked = true; result.vert.dir = Direction::up; break; } } } if (!result.vert.blocked) { auto it = upCollidables_.lower_bound({oldColUL.y(), 0}); for (; (it != std::end(upCollidables_) && std::get<0>(it->first) >= newColUL.y()); it++) { if (newColDR.x() >= it->second.lower && newColUL.x() <= it->second.upper) { int colliderSpriteId = std::get<1>(it->first); Sprite& collider = game_.getSprite(colliderSpriteId); result.horiz.blocked = collider.solid; result.horiz.dir = Direction::up; result.horiz.colliderSprite = colliderSpriteId; break; } } } } return result; } void TransformSystem::addCollidable(int spriteId) { Sprite& sprite = game_.getSprite(spriteId); vec2i colUL = sprite.loc + sprite.collisionOffset; vec2i colDR = colUL + sprite.collisionSize; leftCollidables_.emplace(std::piecewise_construct, std::tie(colDR.x(), spriteId), std::tie(colUL.y(), colDR.y())); rightCollidables_.emplace(std::piecewise_construct, std::tie(colUL.x(), spriteId), std::tie(colUL.y(), colDR.y())); upCollidables_.emplace(std::piecewise_construct, std::tie(colDR.y(), spriteId), std::tie(colUL.x(), colDR.x())); downCollidables_.emplace(std::piecewise_construct, std::tie(colUL.y(), spriteId), std::tie(colUL.x(), colDR.x())); } void TransformSystem::removeCollidable(int spriteId) { Sprite& sprite = game_.getSprite(spriteId); vec2i colUL = sprite.loc + sprite.collisionOffset; vec2i colDR = colUL + sprite.collisionSize; leftCollidables_.erase({ colDR.x(), spriteId }); rightCollidables_.erase({ colUL.x(), spriteId }); upCollidables_.erase({ colDR.y(), spriteId }); downCollidables_.erase({ colUL.y(), spriteId }); } void TransformSystem::clearSpriteCache() { spritesByY_.clear(); leftCollidables_.clear(); rightCollidables_.clear(); upCollidables_.clear(); downCollidables_.clear(); }