#include "StarWorld.hpp" #include "StarScriptedEntity.hpp" namespace Star { bool World::isServer() const { return connection() == ServerConnectionId; } bool World::isClient() const { return !isServer(); } List World::entityQuery(RectF const& boundBox, EntityFilter selector) const { List list; forEachEntity(boundBox, [&](EntityPtr const& entity) { if (!selector || selector(entity)) list.append(entity); }); return list; } List World::entityLineQuery(Vec2F const& begin, Vec2F const& end, EntityFilter selector) const { List list; forEachEntityLine(begin, end, [&](EntityPtr const& entity) { if (!selector || selector(entity)) list.append(entity); }); return list; } List World::entitiesAtTile(Vec2I const& pos, EntityFilter selector) const { List list; forEachEntityAtTile(pos, [&](TileEntityPtr entity) { if (!selector || selector(entity)) list.append(std::move(entity)); }); return list; } List World::findEmptyTiles(Vec2I pos, unsigned maxDist, size_t maxAmount, bool excludeEphemeral) const { List res; if (!tileIsOccupied(pos, TileLayer::Foreground, excludeEphemeral)) res.append(pos); if (res.size() >= maxAmount) return res; // searches manhattan distance counterclockwise from right for (int distance = 1; distance <= (int)maxDist; distance++) { const int totalSpots = 4 * distance; int xDiff = distance; int yDiff = 0; int dx = -1; int dy = 1; for (int i = 0; i < totalSpots; i++) { if (!tileIsOccupied(pos + Vec2I(xDiff, yDiff), TileLayer::Foreground)) { res.append(pos + Vec2I(xDiff, yDiff)); if (res.size() >= maxAmount) { return res; } } xDiff += dx; yDiff += dy; if (abs(xDiff) == distance) dx *= -1; if (abs(yDiff) == distance) dy *= -1; } } return res; } bool World::canModifyTile(Vec2I const& pos, TileModification const& modification, bool allowEntityOverlap) const { return !validTileModifications({{pos, modification}}, allowEntityOverlap).empty(); } bool World::modifyTile(Vec2I const& pos, TileModification const& modification, bool allowEntityOverlap) { return applyTileModifications({{pos, modification}}, allowEntityOverlap).empty(); } TileDamageResult World::damageTile(Vec2I const& tilePosition, TileLayer layer, Vec2F const& sourcePosition, TileDamage const& tileDamage, Maybe sourceEntity) { return damageTiles({tilePosition}, layer, sourcePosition, tileDamage, sourceEntity); } EntityPtr World::closestEntityInSight(Vec2F const& center, float radius, CollisionSet const& collisionSet, EntityFilter selector) const { return closestEntity(center, radius, [=](EntityPtr const& entity) { return selector(entity) && !lineTileCollision(center, entity->position(), collisionSet); }); } bool World::pointCollision(Vec2F const& point, CollisionSet const& collisionSet) const { bool collided = false; forEachCollisionBlock(RectI::withCenter(Vec2I(point), {3, 3}), [&](CollisionBlock const& block) { if (collided || !isColliding(block.kind, collisionSet)) return; if (block.poly.contains(point)) collided = true; }); return collided; } Maybe>> World::lineCollision(Line2F const& line, CollisionSet const& collisionSet) const { auto geometry = this->geometry(); Maybe intersectPoly; Maybe closestIntersection; forEachCollisionBlock(RectI::integral(RectF::boundBoxOf(line.min(), line.max()).padded(1)), [&](CollisionBlock const& block) { if (block.poly.isNull() || !isColliding(block.kind, collisionSet)) return; Vec2F nearMin = geometry.nearestTo(block.poly.center(), line.min()); auto intersection = block.poly.lineIntersection(Line2F(nearMin, nearMin + line.diff())); if (intersection && (!closestIntersection || intersection->along < closestIntersection->along)) { intersectPoly = block.poly; closestIntersection = intersection; } }); if (closestIntersection) { auto point = line.eval(closestIntersection->along); auto normal = closestIntersection->intersectedSide.apply([&](uint64_t side) { return intersectPoly->normal(side); }); return make_pair(point, normal); } return {}; } bool World::polyCollision(PolyF const& poly, CollisionSet const& collisionSet) const { auto geometry = this->geometry(); Vec2F polyCenter = poly.center(); PolyF translatedPoly; bool collided = false; forEachCollisionBlock(RectI::integral(poly.boundBox()).padded(1), [&](CollisionBlock const& block) { if (collided || !isColliding(block.kind, collisionSet)) return; Vec2F center = block.poly.center(); Vec2F newCenter = geometry.nearestTo(polyCenter, center); translatedPoly = block.poly; translatedPoly.translate(newCenter - center); if (poly.intersects(translatedPoly)) collided = true; }); return collided; } }