28 std::unordered_map<uint32_t, float> frontX;
35 for (
auto&& [tf, type, room] : players) {
39 auto it = frontX.find(room.id);
40 if (it == frontX.end() || tf.position.x > it->second) {
41 frontX[room.id] = tf.position.x;
51 if (!transformsRes || !velocitiesRes || !patternsRes || !typesRes || !roomsRes) {
54 auto &transforms = transformsRes->get();
55 auto &velocities = velocitiesRes->get();
56 auto &patterns = patternsRes->get();
57 auto &types = typesRes->get();
58 auto &rooms = roomsRes->get();
60 std::unordered_map<uint32_t, std::vector<std::pair<ecs::Entity, Vec2f>>> bosses;
61 for (
auto entity : transforms.entities()) {
62 if (!types.has(entity) || !rooms.has(entity)) {
65 const auto &type = types[entity];
66 const auto &room = rooms[entity];
68 bosses[room.id].push_back({entity, transforms[entity].position});
72 constexpr float minAheadDefault = 250.0f;
73 constexpr float minAheadBoss = 500.0f;
74 constexpr float anchorDefault = 900.0f;
75 constexpr float anchorBoss = 1100.0f;
76 constexpr float followGain = 2.0f;
78 static std::unordered_map<ecs::Entity, ecs::Entity> shieldBoss;
79 static std::unordered_map<ecs::Entity, Vec2f> shieldOffsets;
81 for (
auto entity : transforms.entities()) {
82 if (!velocities.has(entity) || !patterns.has(entity) ||
83 !types.has(entity) || !rooms.has(entity)) {
87 auto &tf = transforms[entity];
88 auto &vel = velocities[entity];
89 auto &pat = patterns[entity];
90 auto &type = types[entity];
91 auto &room = rooms[entity];
100 auto itBosses = bosses.find(room.id);
101 if (itBosses == bosses.end() || itBosses->second.empty()) {
107 bool bossFound =
false;
109 auto linkIt = shieldBoss.find(entity);
110 if (linkIt != shieldBoss.end()) {
111 bossEntity = linkIt->second;
112 for (
const auto& [bEntity, bPos] : itBosses->second) {
113 if (bEntity == bossEntity) {
122 float bestDist2 = std::numeric_limits<float>::max();
123 for (
const auto& [bEntity, bPos] : itBosses->second) {
124 const float dx = bPos.x - tf.position.x;
125 const float dy = bPos.y - tf.position.y;
126 const float dist2 = dx * dx + dy * dy;
127 if (dist2 < bestDist2) {
129 bossEntity = bEntity;
134 shieldBoss[entity] = bossEntity;
135 shieldOffsets[entity] = tf.position - bossPos;
138 auto offIt = shieldOffsets.find(entity);
139 if (offIt == shieldOffsets.end()) {
140 shieldOffsets[entity] = tf.position - bossPos;
141 offIt = shieldOffsets.find(entity);
144 const Vec2f targetPos = bossPos + offIt->second;
145 const Vec2f delta = targetPos - tf.position;
146 const float maxSpeed = std::max(60.0f, pat.speed);
147 vel.direction.x = std::clamp(delta.
x * 2.5f, -maxSpeed, maxSpeed);
148 vel.direction.y = std::clamp(delta.
y * 2.5f, -maxSpeed, maxSpeed);
153 tf.position.x < frontX[room.id] - 100.0f) {
157 auto it = frontX.find(room.id);
158 if (it != frontX.end()) {
159 const float dx = it->second - tf.position.x;
160 vel.direction.x = std::clamp(dx * followGain, -pat.speed, pat.speed);
162 vel.direction.x = 0.f;
166 const float minAhead = isBoss ? minAheadBoss : minAheadDefault;
167 const float anchorX = isBoss ? anchorBoss : anchorDefault;
169 float targetX = anchorX;
170 auto it = frontX.find(room.id);
171 if (it != frontX.end()) {
172 targetX = std::max(anchorX, it->second + minAhead);
174 const float dx = targetX - tf.position.x;
175 const float maxXSpeed = std::max(60.0f, pat.speed);
176 const float desiredX = std::clamp(dx * followGain, -maxXSpeed, maxXSpeed);
177 vel.direction.x = desiredX;
180 switch (pat.pattern) {
182 vel.direction.y = 0.f;
186 vel.direction.y = pat.amplitude * std::sin(
_time * pat.frequency);
190 const float zigzagPeriod = 2.0f;
191 const float phase = std::fmod(
_time * pat.frequency, zigzagPeriod);
192 vel.direction.y = (phase < zigzagPeriod / 2.0f) ? pat.amplitude : -pat.amplitude;
197 const float angle =
_time * pat.frequency;
198 const float circleX = pat.amplitude * std::cos(angle);
199 const float circleY = pat.amplitude * std::sin(angle);
200 vel.direction.x += circleX * 4.0f;
201 vel.direction.y = circleY * 2.0f;
206 float nearestDist = std::numeric_limits<float>::max();
207 Vec2f nearestPlayerPos = {tf.position.
x, tf.position.y};
215 for (
auto&& [pTf, pType, pRoom] : players) {
217 const float dx = pTf.position.x - tf.position.x;
218 const float dy = pTf.position.y - tf.position.y;
219 const float dist = std::sqrt(dx * dx + dy * dy);
220 if (dist < nearestDist) {
222 nearestPlayerPos = pTf.position;
226 const float dx = nearestPlayerPos.
x - tf.position.x;
227 const float dy = nearestPlayerPos.
y - tf.position.y;
228 const float distance = std::sqrt(dx * dx + dy * dy);
230 if (distance > 0.1f) {
231 vel.direction.x = (dx / distance) * pat.speed;
232 vel.direction.y = (dy / distance) * pat.speed;
234 vel.direction.x = 0.f;
235 vel.direction.y = 0.f;
241 vel.direction.y = 0.f;