osb/source/game/interfaces/StarWorld.cpp
2023-06-20 14:33:09 +10:00

154 lines
5.0 KiB
C++

#include "StarWorld.hpp"
#include "StarScriptedEntity.hpp"
namespace Star {
bool World::isServer() const {
return connection() == ServerConnectionId;
}
bool World::isClient() const {
return !isServer();
}
List<EntityPtr> World::entityQuery(RectF const& boundBox, EntityFilter selector) const {
List<EntityPtr> list;
forEachEntity(boundBox, [&](EntityPtr const& entity) {
if (!selector || selector(entity))
list.append(entity);
});
return list;
}
List<EntityPtr> World::entityLineQuery(Vec2F const& begin, Vec2F const& end, EntityFilter selector) const {
List<EntityPtr> list;
forEachEntityLine(begin, end, [&](EntityPtr const& entity) {
if (!selector || selector(entity))
list.append(entity);
});
return list;
}
List<TileEntityPtr> World::entitiesAtTile(Vec2I const& pos, EntityFilter selector) const {
List<TileEntityPtr> list;
forEachEntityAtTile(pos, [&](TileEntityPtr entity) {
if (!selector || selector(entity))
list.append(move(entity));
});
return list;
}
List<Vec2I> World::findEmptyTiles(Vec2I pos, unsigned maxDist, size_t maxAmount, bool excludeEphemeral) const {
List<Vec2I> 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<EntityId> 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<pair<Vec2F, Maybe<Vec2F>>> World::lineCollision(Line2F const& line, CollisionSet const& collisionSet) const {
auto geometry = this->geometry();
Maybe<PolyF> intersectPoly;
Maybe<PolyF::LineIntersectResult> 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;
}
}