Skip to content

Commit 6c50ae5

Browse files
committed
way smarter missiles
1 parent 1b34998 commit 6c50ae5

File tree

3 files changed

+60
-25
lines changed

3 files changed

+60
-25
lines changed

include/entities.hpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ Entity* idLookup(uint32_t);
114114

115115
struct Quad {
116116
void collideAttract(Entity* e, bool, bool);
117-
void put(Entity* e);
117+
void put(Entity* e, int reclevel);
118118
Quad& getChild(uint8_t at);
119119
uint32_t unstaircasize();
120120
void postBuild();
@@ -219,9 +219,11 @@ struct Missile: public Projectile {
219219
Entity* target = nullptr;
220220
Entity* owner = nullptr;
221221

222-
static double mass, accel, rotateSpeed, maxThrustAngle, easeInFactor, startingFuel;
222+
static double mass, accel, rotateSpeed, maxThrustAngle, startingFuel, leastItimeDecrease, fullThrustThreshold;
223223
double fuel,
224-
resFuel;
224+
resFuel,
225+
prevItime = 0.0;
226+
bool thrust = true;
225227

226228
std::unique_ptr<sf::CircleShape> warning;
227229
};

include/globals.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,8 @@ inline std::map<std::string, Var> vars {
169169
{"missile_accel", {Double, &Missile::accel}},
170170
{"missile_rotateSpeed", {Double, &Missile::rotateSpeed}},
171171
{"missile_maxThrustAngle", {Double, &Missile::maxThrustAngle}},
172-
{"missile_easeInFactor", {Double, &Missile::easeInFactor}},
172+
{"missile_leastItimeDecrease", {Double, &Missile::leastItimeDecrease}},
173+
{"missile_fullThrustThreshold", {Double, &Missile::fullThrustThreshold}},
173174
{"missile_startingFuel", {Double, &Missile::startingFuel}},
174175
{"triangle_mass", {Double, &Triangle::mass}},
175176
{"triangle_accel", {Double, &Triangle::accel}},

src/entities.cpp

+53-21
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,9 @@ double Missile::mass = 1.0e3,
260260
Missile::accel = 196.0,
261261
Missile::rotateSpeed = 240.0,
262262
Missile::maxThrustAngle = 45.0 * degToRad,
263-
Missile::easeInFactor = 0.8,
264-
Missile::startingFuel = 80.0;
263+
Missile::startingFuel = 80.0,
264+
Missile::leastItimeDecrease = 0.4,
265+
Missile::fullThrustThreshold = 1.5;
265266

266267
double Triangle::mass = 1.0e7,
267268
Triangle::accel = 96.0,
@@ -415,19 +416,24 @@ Quad& Quad::getChild(uint8_t at) {
415416
}
416417
return quadtree[children[at]];
417418
}
418-
void Quad::put(Entity* e) {
419+
void Quad::put(Entity* e, int reclevel) {
419420
mass += e->mass;
420421
comx += e->mass * e->x;
421422
comy += e->mass * e->y;
422423
hasGravitators = hasGravitators || e->gravitates;
424+
if (reclevel > 512) {
425+
printf("body {ptr %lli, id %i, type %i, x %f, y %f, vx %f, vy %f, radius %f} exceeded quadtree recursion limit.\n", (size_t)e, e->id, e->type(), e->x, e->y, e->velX, e->velY, e->radius);
426+
e->active = false;
427+
return;
428+
}
423429
if (used) {
424-
getChild((e->x > x + size * 0.5) + 2 * (e->y > y + size * 0.5)).put(e);
430+
getChild((e->x > x + size * 0.5) + 2 * (e->y > y + size * 0.5)).put(e, reclevel + 1);
425431
if (entity) {
426432
if (entity->ghost && entity->parent_id == e->id) [[unlikely]] {
427433
entity = nullptr;
428434
return;
429435
}
430-
getChild((entity->x > x + size * 0.5) + 2 * (entity->y > y + size * 0.5)).put(entity);
436+
getChild((entity->x > x + size * 0.5) + 2 * (entity->y > y + size * 0.5)).put(entity, reclevel + 1);
431437
entity = nullptr;
432438
}
433439
} else {
@@ -552,7 +558,7 @@ void buildQuadtree() {
552558
quadsConstructed = 1;
553559
for (size_t i = 0; i < updateGroup.size(); i++) {
554560
try {
555-
quadtree[0].put(updateGroup[i]);
561+
quadtree[0].put(updateGroup[i], 0);
556562
} catch (const std::bad_alloc& except) {
557563
free(quadtree);
558564
quadsAllocated = (int)(quadsAllocated * extraQuadAllocation);
@@ -892,7 +898,7 @@ void CelestialBody::collide(Entity* with, bool specialOnly) {
892898
if (!with->active) [[unlikely]] {
893899
return;
894900
}
895-
if (authority && star && with->type() == Entities::Triangle) [[unlikely]] {
901+
if (authority && star && with->type() == Entities::Triangle) {
896902
if (with->player || !isServer || with == ownEntity) {
897903
if (isServer) {
898904
std::string sendMessage;
@@ -1066,23 +1072,49 @@ Missile::Missile() : Projectile() {
10661072
}
10671073
}
10681074

1075+
// guesses time to intercept since the actual equation is pretty much unsolvable
1076+
double guessItime(double prev, double x0, double vel, double y0, double halfaccel) {
1077+
double x = x0 + vel * prev;
1078+
double d = dst(x, y0);
1079+
double dd = vel * x / d;
1080+
return (dd + std::sqrt(dd * dd + 4.0 * halfaccel * (d - dd * prev))) / (2 * halfaccel);
1081+
}
1082+
double accelAt(double time, double fuel, double thrust) {
1083+
double fuel1 = fuel * std::exp(-time / fuel);
1084+
double dV = thrust * (fuel - fuel1);
1085+
return dV / time;
1086+
}
10691087
void Missile::update2() {
10701088
if (target) {
1071-
double dVx = target->velX - velX, dVy = target->velY - velY;
1072-
double dX = target->x - x, dY = target->y - y;
1073-
double inHeading = std::atan2(dY, dX), tangentHeading = inHeading + 0.5 * PI;
1074-
double velHeading = std::atan2(dVy, dVx);
1075-
double tangentVel = dst(dVx, dVy) * std::cos(deltaAngleRad(tangentHeading, velHeading));
1076-
double accel = this->accel * fuel / startingFuel;
1077-
double dtaccel = delta * accel;
1078-
double targetRotation = inHeading + (std::abs(tangentVel) < dtaccel ? std::atan2(tangentVel, dtaccel - std::abs(tangentVel))
1079-
: (std::abs(tangentVel) * easeInFactor > accel ? (tangentVel > 0.0 ? 0.5 * PI : 0.5 * -PI) : std::atan2(tangentVel * easeInFactor, accel)));
1080-
rotateVel += delta * (deltaAngleRad((rotation + 1.5 * (rotateVel > 0.0 ? rotateVel : -rotateVel) * rotateVel / rotateSpeed) * degToRad, targetRotation) > 0.0 ? rotateSpeed : -rotateSpeed);
1081-
double thrustDirection = rotation * degToRad + std::max(-maxThrustAngle, std::min(maxThrustAngle, deltaAngleRad(rotation * degToRad, targetRotation)));
1082-
if (std::abs(deltaAngleRad(targetRotation, rotation * degToRad)) < 0.5 * PI) {
1083-
addVelocity(dtaccel * std::cos(thrustDirection), dtaccel * std::sin(thrustDirection));
1084-
fuel -= delta * fuel / startingFuel;
1089+
double dVx = target->velX - velX;
1090+
double dVy = target->velY - velY;
1091+
double dX = target->x - x;
1092+
double dY = target->y - y;
1093+
double refRot = std::atan2(dVy, dVx);
1094+
double vel = dVx / std::cos(refRot);
1095+
double projX = dX * std::cos(refRot) + dY * std::sin(refRot);
1096+
double projY = dY * std::cos(refRot) - dX * std::sin(refRot);
1097+
double accel = this->accel * fuel / startingFuel;
1098+
double halfaccel = accel * 0.5;
1099+
double itime = guessItime(0.0, -projX, -vel, projY, halfaccel);
1100+
itime = guessItime(itime, -projX, -vel, projY, accelAt(itime, fuel, halfaccel));
1101+
itime = guessItime(itime, -projX, -vel, projY, accelAt(itime, fuel, halfaccel));
1102+
itime = guessItime(itime, -projX, -vel, projY, accelAt(itime, fuel, halfaccel));
1103+
bool fullthrust = itime < fuel * fullThrustThreshold;
1104+
bool thrust = ((prevItime - itime) < leastItimeDecrease * delta || fullthrust) && fuel > 0.0;
1105+
double targetRot = std::atan2(dY + dVy * itime, dX + dVx * itime);
1106+
double finangle = degToRad * (rotation + std::abs(rotateVel) * rotateVel / (2.0 * rotateSpeed));
1107+
rotateVel += delta * (deltaAngleRad(finangle, targetRot) > 0.0 ? rotateSpeed : -rotateSpeed);
1108+
if (std::abs(deltaAngleRad(targetRot, rotation * degToRad)) < maxThrustAngle && thrust) {
1109+
double actaccel = fullthrust ? this->accel : accel;
1110+
addVelocity(actaccel * delta * std::cos(targetRot), actaccel * delta * std::sin(targetRot));
1111+
fuel -= delta * (fullthrust ? 1.0 : fuel / startingFuel);
10851112
}
1113+
/* if (!simulating) {
1114+
printf("pX %f pY %f vel %f halfaccel %f g1 %f g2 %f it %f\n", projX, projY, vel, halfaccel, guess1, guess2, itime);
1115+
printf("dX %f, dY %f, ang %f, target %f\n", dX, dY, radToDeg * std::atan2(dY, dX), radToDeg * targetRot);
1116+
} */
1117+
prevItime = itime;
10861118
}
10871119
Entity::update2();
10881120
}

0 commit comments

Comments
 (0)