1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
#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(std::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;
}
}
|