154 lines
5.0 KiB
C++
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;
|
||
|
}
|
||
|
|
||
|
}
|