aboutsummaryrefslogtreecommitdiffstats
path: root/src/game/build_graph.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/build_graph.cpp')
-rw-r--r--src/game/build_graph.cpp158
1 files changed, 153 insertions, 5 deletions
diff --git a/src/game/build_graph.cpp b/src/game/build_graph.cpp
index a1195af..2398b9c 100644
--- a/src/game/build_graph.cpp
+++ b/src/game/build_graph.cpp
@@ -331,10 +331,25 @@ void BuildGraph::apply_splash(float x, float y, float radius, float damage,
e.hp -= damage * (1.0f - d / radius);
if (e.hp <= 0.0f) brk.push_back(ei);
}
- if (brk.empty()) return;
- for (auto it = brk.rbegin(); it != brk.rend(); ++it)
- edges.erase(edges.begin() + *it);
- rebuild_topology();
+ if (!brk.empty()) {
+ for (auto it = brk.rbegin(); it != brk.rend(); ++it)
+ edges.erase(edges.begin() + *it);
+ rebuild_topology();
+ }
+
+ // Splash also damages devices mounted within the radius.
+ for (auto& dv : devices) {
+ if (!dv.alive) continue;
+ float mx = 0.0f, my = 0.0f; int mounts = 0;
+ if (dv.node_a >= 0 && dv.node_a < (int)nodes.size())
+ { mx += nodes[dv.node_a].x; my += nodes[dv.node_a].y; mounts++; }
+ if (dv.node_b >= 0 && dv.node_b < (int)nodes.size())
+ { mx += nodes[dv.node_b].x; my += nodes[dv.node_b].y; mounts++; }
+ if (mounts == 0) continue;
+ mx /= mounts; my /= mounts;
+ float d = std::sqrt((mx-x)*(mx-x) + (my-y)*(my-y));
+ if (d < radius) { dv.hp -= damage * (1.0f - d / radius); if (dv.hp <= 0.0f) dv.alive = false; }
+ }
}
int BuildGraph::find_blocking_edge(float x, float y, float radius) const {
@@ -367,7 +382,17 @@ float BuildGraph::beam_fire(float ox, float oy, float dx, float dy, float range,
const BuildNode& B = nodes[edges[i].node_b];
float ex = B.x - A.x, ey = B.y - A.y;
float denom = dx * ey - dy * ex;
- if (std::fabs(denom) < 1e-6f) continue; // parallel
+ if (std::fabs(denom) < 1e-6f) {
+ // Ray parallel to segment. Check for collinear overlap.
+ float perp = std::fabs((ox - A.x) * ey - (oy - A.y) * ex);
+ if (perp > 1e-4f) continue; // parallel, no overlap
+ float ta = (A.x - ox) * dx + (A.y - oy) * dy;
+ float tb = (B.x - ox) * dx + (B.y - oy) * dy;
+ if (ta > tb) std::swap(ta, tb);
+ if (tb < 0.0f || ta > range) continue; // no overlap
+ crosses.push_back({std::max(0.0f, ta), i});
+ continue;
+ }
float t = ((A.x - ox) * ey - (A.y - oy) * ex) / denom; // dist along ray
float u = ((A.x - ox) * dy - (A.y - oy) * dx) / denom; // param along segment
if (t >= 0.0f && t <= range && u >= 0.0f && u <= 1.0f) crosses.push_back({t, i});
@@ -395,6 +420,28 @@ float BuildGraph::beam_fire(float ox, float oy, float dx, float dy, float range,
edges.erase(edges.begin() + *it);
rebuild_topology();
}
+
+ // Beam also damages devices within threshold distance of its path.
+ {
+ float ex = hit_x - ox, ey = hit_y - oy;
+ float len2 = ex*ex + ey*ey;
+ if (len2 > 1e-6f) {
+ for (auto& dv : devices) {
+ if (!dv.alive) continue;
+ float mx = 0.0f, my = 0.0f; int mounts = 0;
+ if (dv.node_a >= 0 && dv.node_a < (int)nodes.size())
+ { mx += nodes[dv.node_a].x; my += nodes[dv.node_a].y; mounts++; }
+ if (dv.node_b >= 0 && dv.node_b < (int)nodes.size())
+ { mx += nodes[dv.node_b].x; my += nodes[dv.node_b].y; mounts++; }
+ if (mounts == 0) continue;
+ mx /= mounts; my /= mounts;
+ float t = std::clamp(((mx-ox)*ex + (my-oy)*ey) / len2, 0.0f, 1.0f);
+ float d = std::hypot(mx - (ox + t*ex), my - (oy + t*ey));
+ if (d < 0.6f) { dv.hp -= damage; if (dv.hp <= 0.0f) dv.alive = false; }
+ }
+ }
+ }
+
return stop;
}
@@ -439,6 +486,20 @@ void BuildGraph::update_fire(float dt) {
edges.erase(edges.begin() + *it);
rebuild_topology();
}
+
+ // Fire also damages devices mounted on burning struts.
+ for (int ei = 0; ei < (int)edges.size(); ei++) {
+ const auto& e = edges[ei];
+ if (!e.burning) continue;
+ float fd = materials[e.mat].burn_rate * dt;
+ for (auto& dv : devices) {
+ if (!dv.alive) continue;
+ if (dv.node_a == e.node_a || dv.node_a == e.node_b ||
+ dv.node_b == e.node_a || dv.node_b == e.node_b) {
+ dv.hp -= fd; if (dv.hp <= 0.0f) dv.alive = false;
+ }
+ }
+ }
}
void BuildGraph::break_edge(int edge_id) {
@@ -460,10 +521,97 @@ void BuildGraph::break_node(int node_id) {
if (e.node_a > node_id) e.node_a--;
if (e.node_b > node_id) e.node_b--;
}
+
+ // Fix up device mount indices: invalidate mounts on the removed node,
+ // shift indices past it, and mark devices dead when both mounts are gone.
+ for (auto& dv : devices) {
+ if (!dv.alive) continue;
+ if (dv.node_a == node_id) dv.node_a = -1;
+ if (dv.node_b == node_id) dv.node_b = -1;
+ if (dv.node_a > node_id) dv.node_a--;
+ if (dv.node_b > node_id) dv.node_b--;
+ if (dv.node_a < 0 && dv.node_b < 0) dv.alive = false;
+ }
+
rebuild_topology();
}
// =============================================================================
+// Devices mounted on the strut graph
+// =============================================================================
+
+int BuildGraph::mount_device(int na, int nb, int device_type, int team, float hp) {
+ if (na < 0 || na >= (int)nodes.size()) return -1;
+ if (nb < 0 || nb >= (int)nodes.size()) return -1;
+ if (na == nb) return -1;
+
+ MountedDevice md{};
+ md.node_a = na;
+ md.node_b = nb;
+ md.type = device_type;
+ md.team = team;
+ md.hp = hp;
+ md.max_hp = hp;
+ md.alive = true;
+ devices.push_back(md);
+ return (int)devices.size() - 1;
+}
+
+void BuildGraph::unmount_device(int device_id) {
+ if (device_id < 0 || device_id >= (int)devices.size()) return;
+ devices[device_id].alive = false;
+}
+
+bool BuildGraph::is_device_alive(int device_id) const {
+ if (device_id < 0 || device_id >= (int)devices.size()) return false;
+ return devices[device_id].alive && devices[device_id].hp > 0.0f;
+}
+
+float BuildGraph::get_device_hp(int device_id) const {
+ if (device_id < 0 || device_id >= (int)devices.size()) return 0.0f;
+ return devices[device_id].hp;
+}
+
+bool BuildGraph::damage_device(int device_id, float amount) {
+ if (device_id < 0 || device_id >= (int)devices.size()) return false;
+ auto& dv = devices[device_id];
+ if (!dv.alive || dv.hp <= 0.0f) return false;
+ dv.hp -= amount;
+ if (dv.hp <= 0.0f) {
+ dv.alive = false;
+ return true;
+ }
+ return false;
+}
+
+int BuildGraph::nearest_device(float x, float y, float radius) const {
+ int best = -1;
+ float best_d2 = radius * radius;
+ for (int i = 0; i < (int)devices.size(); i++) {
+ if (!devices[i].alive || devices[i].hp <= 0.0f) continue;
+ // Device position = midpoint of its valid mount nodes.
+ float mx = 0.0f, my = 0.0f;
+ int mounts = 0;
+ if (devices[i].node_a >= 0 && devices[i].node_a < (int)nodes.size()) {
+ mx += nodes[devices[i].node_a].x;
+ my += nodes[devices[i].node_a].y;
+ mounts++;
+ }
+ if (devices[i].node_b >= 0 && devices[i].node_b < (int)nodes.size()) {
+ mx += nodes[devices[i].node_b].x;
+ my += nodes[devices[i].node_b].y;
+ mounts++;
+ }
+ if (mounts == 0) continue;
+ mx /= mounts; my /= mounts;
+ float dx = mx - x, dy = my - y;
+ float d2 = dx*dx + dy*dy;
+ if (d2 < best_d2) { best_d2 = d2; best = i; }
+ }
+ return best;
+}
+
+// =============================================================================
// Queries
// =============================================================================