1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-10-18 06:30:41 +00:00

ads:: turrets

This commit is contained in:
Zeno Rogue 2024-09-17 17:41:23 +02:00
parent ce7058de5f
commit eba9dd5624
5 changed files with 269 additions and 65 deletions

View File

@ -8,14 +8,6 @@ cross_result findflat(shiftpoint h) {
return cross0(current * rgpushxto0(h));
}
struct cell_to_draw {
cross_result center;
ld d;
cell *c;
ads_matrix V;
bool operator < (const cell_to_draw& c2) const { return d > c2.d; }
};
void apply_duality(shiftmatrix& S) {
if(use_duality == 1) {
S.T = unshift(S);
@ -109,18 +101,26 @@ void draw_game_cell(const cell_to_draw& cd) {
queuestr(shiftless(rgpushxto0(cd.center.h)), .1, str, 0xFF4040, 8);
}
for(auto& r: ci.rocks) {
auto& rock = *r;
// need i-loop because new rocks can be created in handle_turret
for(int i=0; i<isize(ci.rocks); i++) {
auto& rock = *ci.rocks[i];
if(!paused) {
if(rock.type == oRock && rock.expire < pdata.score) { rock.resource = rtNone; rock.col = rock_color[rtNone]; rock.expire = 999999; }
if(rock.type == oResource && rock.expire < pdata.score) { rock.resource = rtNone; rock.col = rsrc_color[rtNone]; rock.shape = rsrc_shape[rtNone]; rock.expire = 999999; }
}
ld ang = 0;
ads_matrix M;
hybrid::in_actual([&]{
dynamicval<eGeometry> b(geometry, gTwistedProduct);
auto h = V * rock.at;
rock.pt_main = cross0(current * h);
M = V * rock.at;
rock.pt_main = cross0(current * M);
if(rock.type == oTurret) handle_turret(&rock, ang);
if(ang) M = M * spin(ang);
});
if(rock.pt_main.shift < rock.life_start || rock.pt_main.shift > rock.life_end) continue;
@ -130,7 +130,7 @@ void draw_game_cell(const cell_to_draw& cd) {
auto& shape = *rock.shape;
for(int i=0; i<isize(shape); i += 2) {
hybrid::in_actual([&]{
auto h = V * rock.at * twist::uxpush(shape[i] * ads_scale) * twist::uypush(shape[i+1] * ads_scale);
auto h = M * twist::uxpush(shape[i] * ads_scale) * twist::uypush(shape[i+1] * ads_scale);
cross_result f = cross0(current * h);
rock.pts.push_back(f);
});
@ -162,6 +162,7 @@ void draw_game_cell(const cell_to_draw& cd) {
curvepoint(rock.pts[0].h);
queuecurve(shiftless(Id),
rock.type == oMissile ? missile_color :
rock.type == oTurretMissile ? missile_color :
rock.type == oParticle ? rock.col :
0x000000FF, rock.col, obj_prio[rock.type]);
}
@ -255,6 +256,7 @@ void view_footer() {
void view_ads_game() {
displayed.clear();
cds_last = std::move(cds); cds.clear();
bool hv = mhybrid;
@ -319,6 +321,7 @@ void view_ads_game() {
i++; if(i > draw_per_frame) break;
auto& cd = dq.top();
cds[cd.c] = cd;
draw_game_cell(cd);
cell *c = cd.c;

View File

@ -149,4 +149,75 @@ int spacetime_qty = 30;
color_t ghost_color = 0x800080FF;
/* types */
enum eObjType { oRock, oMissile, oParticle, oResource, oMainRock, oTurret, oTurretMissile };
enum eResourceType { rtNone, rtHull, rtGold, rtAmmo, rtFuel, rtOxygen };
enum eWalltype { wtNone, wtDestructible, wtSolid, wtGate };
PPR obj_prio[7] = { PPR::MONSTER_BODY, PPR::ITEMa, PPR::ITEM_BELOW, PPR::ITEM, PPR::MONSTER_HEAD, PPR::MONSTER_BODY, PPR::ITEMa };
struct cell_to_draw {
cross_result center;
ld d;
cell *c;
ads_matrix V;
bool operator < (const cell_to_draw& c2) const { return d > c2.d; }
};
/** all cell_to_draw drawn currently */
std::unordered_map<cell*, cell_to_draw> cds, cds_last;
struct turret_state {
ld angle, dist;
int index;
ld err;
};
struct ads_object {
eObjType type;
eResourceType resource;
cell *owner;
ads_matrix at;
color_t col;
int expire;
vector<ld>* shape;
ld last_shot;
int hlast;
map<ld, turret_state> turret_states;
ld life_start, life_end;
cross_result pt_main;
vector<cross_result> pts;
ads_object(eObjType t, cell *_owner, const ads_matrix& T, color_t _col) : type(t), owner(_owner), at(T), col(_col) {
life_start = -HUGE_VAL;
life_end = HUGE_VAL;
}
};
struct shipstate {
ads_matrix at;
ads_matrix current;
ld start;
ld duration;
ld ang;
ads_matrix vctrV;
cell *vctr;
};
struct cellinfo {
int mpd_terrain; /* 0 = fully generated terrain */
int rock_dist; /* rocks generated in this radius */
vector<std::unique_ptr<ads_object>> rocks;
vector<shipstate> shipstates;
eWalltype type;
cellinfo() {
mpd_terrain = 4;
rock_dist = -1;
type = wtNone;
}
};
}}

View File

@ -2,60 +2,12 @@ namespace hr {
namespace ads_game {
enum eObjType { oRock, oMissile, oParticle, oResource, oMainRock };
PPR obj_prio[5] = { PPR::MONSTER_BODY, PPR::ITEMa, PPR::ITEM_BELOW, PPR::ITEM, PPR::MONSTER_HEAD };
struct ads_object {
eObjType type;
eResourceType resource;
cell *owner;
ads_matrix at;
color_t col;
int expire;
vector<ld>* shape;
ld life_start, life_end;
cross_result pt_main;
vector<cross_result> pts;
ads_object(eObjType t, cell *_owner, const ads_matrix& T, color_t _col) : type(t), owner(_owner), at(T), col(_col) {
life_start = -HUGE_VAL;
life_end = HUGE_VAL;
}
};
enum eWalltype { wtNone, wtDestructible, wtSolid, wtGate };
int gen_expire() {
return 20 / randd() - 15;
}
struct shipstate {
ads_matrix at;
ads_matrix current;
ld start;
ld duration;
ld ang;
ads_matrix vctrV;
cell *vctr;
};
vector<shipstate> history;
struct cellinfo {
int mpd_terrain; /* 0 = fully generated terrain */
int rock_dist; /* rocks generated in this radius */
vector<std::unique_ptr<ads_object>> rocks;
vector<shipstate> shipstates;
eWalltype type;
cellinfo() {
mpd_terrain = 4;
rock_dist = -1;
type = wtNone;
}
};
std::unordered_map<cell*, cellinfo> ci_at;
using worldline_visitor = std::function<bool(cell*, ld)>;
@ -165,6 +117,31 @@ void add_rock(cell *c, cellinfo& ci, const ads_matrix& T) {
ci.rocks.emplace_back(std::move(r));
}
void add_turret(cell *c, cellinfo& ci, const ads_matrix& T) {
auto r = std::make_unique<ads_object> (oTurret, c, T, 0xC0C060FF);
r->expire = gen_expire();
r->shape = &shape_turret;
r->last_shot = -1;
r->hlast = 0;
if(geometry != gTwistedProduct) { println(hlog, "wrong geometry detected in gen_turret!"); exit(1); }
int q = 0;
auto cleanup = [&] (cell *c, ld t) {
auto& ci = ci_at[c];
hybrid::in_underlying_geometry([&] { gen_terrain(c, ci); });
ci.type = wtNone;
q++;
return false;
};
if(q == 0) ci.type = wtNone;
compute_life(hybrid::get_at(c, 0), unshift(r->at), cleanup);
ci.rocks.emplace_back(std::move(r));
}
int turrets;
void gen_rocks(cell *c, cellinfo& ci, int radius) {
if(radius <= ci.rock_dist) return;
if(ci.rock_dist < radius - 1) gen_rocks(c, ci, radius-1);
@ -192,6 +169,27 @@ void gen_rocks(cell *c, cellinfo& ci, int radius) {
add_rock(c, ci, ads_matrix(spin(alpha) * twist::uxpush(r/2) * chg_shift(randd() * TAU) * spin(randd() * TAU) * lorentz(0, 3, randd() * rock_max_rapidity)));
});
}
q = rpoisson(rock_density / 50);
if(celldist(c) == 2) q += rpoisson(0.1);
for(int i=0; i<q; i++) {
ld maxr = cgi.rhexf;
cell *c1 = nullptr;
ld r, alpha;
while(c1 != c) {
ld vol = randd() * wvolarea_auto(maxr);
r = binsearch(0, maxr, [vol] (ld r) { return wvolarea_auto(r) > vol; });
alpha = randd() * TAU;
hyperpoint h = spin(alpha) * xpush0(r);
c1 = c;
virtualRebase(c1, h);
}
hybrid::in_actual([&] {
add_turret(c, ci, ads_matrix(spin(alpha) * twist::uxpush(r/2) * chg_shift(randd() * TAU) * spin(randd() * TAU) * lorentz(0, 3, randd() * rock_max_rapidity/10)));
turrets++;
});
}
}
ci.rock_dist = radius;
}
@ -246,6 +244,134 @@ void ads_crash_ship() {
});
}
// -1 : T1 is in the past of T2
// =0 : T1 is elsewhere from T2
// +1 : T1 is in the future of T2
hyperpoint hcopy;
int spacetime_relation(const ads_matrix& T1, const ads_matrix& T2) {
auto h = ads_inverse(T1) * (T2 * C0);
if(h.shift > 90._deg) return 1;
if(h.shift < -90._deg) return -1;
auto h1 = unshift(h);
hcopy = h1;
if(h1[0] * h1[0] + h1[1] * h1[1] > h1[2] * h1[2]) return 0;
return h1[2] > 0 ? 1 : -1;
}
bool bad_turret = false;
void handle_turret(ads_object *t, ld& angle_at_time) {
ld ctime = t->pt_main.shift;
auto p = at_or_null(cds_last, t->owner);
if(!p) return;
auto t1 = p->V * t->at * ads_matrix(Id, ctime);
auto& ts = t->turret_states;
auto it1 = ts.lower_bound(ctime);
if(it1->first == ctime) {
if(it1->second.err < 0.01) angle_at_time = it1->second.angle;
return;
}
auto it0 = it1; if(it0 != ts.begin()) it0--;
int tv0 = (it1 == ts.begin()) ? 0 : it0->second.index;
int tv1 = (it1 == ts.end()) ? isize(history) : it1->second.index;
while(tv0 < tv1) {
int tvm = (tv0 + tv1) / 2;
auto& hi = history[tvm];
auto p1 = at_or_null(cds_last, hi.vctr);
if(!p1) { tv0 = tvm+1; continue; }
ads_matrix at1 = p1->V * hi.at;
auto rel = spacetime_relation(t1, at1);
if(rel == -1) tv0 = tvm+1;
else tv1 = tvm;
}
// println(hlog, "tv0 search returns ", tv0, "/", isize(history), " for ctime = ", ctime);
if(tv0 == 0 || tv0 == isize(history)) { return; }
auto& hi = history[tv0];
auto p1 = at_or_null(cds_last, hi.vctr);
if(!p1) return;
ads_matrix at1 = p1->V * hi.at;
turret_state nts;
if(bad_turret) {
auto h = ads_inverse(t1) * (at1 * C0);
auto h1 = unshift(h);
nts.angle = -atan2(h1[1], h1[0]);
nts.dist = acosh(h1[3]) - turret_length;
nts.err = 0;
}
else {
auto hitpoint = [&] (ld alph, ld dist) {
return ads_inverse(at1) * t1 * spin(alph) * twist::uxpush(turret_length) * lorentz(0, 2, ads_missile_rapidity) * ads_point(C0, dist);
};
auto opt_hitpoint = [&] (ld alph, ld dist) { return unshift(hitpoint(alph, dist)); };
if(it1 == ts.begin() || it0->first < ctime - 0.1 || it0->second.err > 0.01) {
ld best_err = HUGE_VAL;
for(int av=0; av<24; av++) for(ld dist=0.01; dist < 2; dist += 0.01) {
ld alph = av * TAU / 24;
ld err = sqhypot_d(2, hitpoint(alph, dist).h);
if(err < best_err) { best_err = err; nts.angle = alph; nts.dist = dist; }
}
// println(hlog, "the closest hit at alpha = ", nts.angle, " and dist = ", nts.dist, " (err = ", best_err, ")");
}
else {
nts.angle = it0->second.angle;
nts.dist = it0->second.dist;
}
/* Newton method */
for(int it=0; it<3; it++) {
ld eps = 1e-4;
hyperpoint h0 = opt_hitpoint(nts.angle, nts.dist);
hyperpoint hx = opt_hitpoint(nts.angle + eps, nts.dist);
hyperpoint hy = opt_hitpoint(nts.angle, nts.dist + eps);
// println(hlog, tie(nts.angle, nts.dist), " : ", h0);
transmatrix T = Id;
set_column(T, 0, hx-h0);
set_column(T, 1, hy-h0);
transmatrix T2 = inverse2(T);
// f(x) = h0 + T * (x-x0) / eps = 0
// -h0 * eps = T * (x-x0)
// T2 * (-h0 * eps) = x - x0
hyperpoint x = T2 * (-h0 * eps);
nts.angle += x[0];
nts.dist += x[1];
}
nts.err = sqhypot_d(2, hitpoint(nts.angle, nts.dist).h);
}
// println(hlog, "nts values are: ", tie(nts.angle, nts.dist, nts.err));
if(nts.err < 0.01 && ctime > t->last_shot + 1 && it0->second.err < 0.01) {
t->last_shot = t->last_shot + floor(ctime - t->last_shot);
ld angle = lerp(it0->second.angle, nts.angle, ilerp(it0->first, ctime, t->last_shot));
// println(hlog, "shooting at angle ", angle, " at time ", t->last_shot);
ads_matrix S0 = ads_inverse(p->V) * t1 * spin(angle) * twist::uxpush(turret_length * ads_scale) * lorentz(0, 2, ads_missile_rapidity);
auto r = std::make_unique<ads_object> (oTurretMissile, t->owner, S0, rsrc_color[rtAmmo]);
r->shape = &shape_missile;
r->life_start = 0; r->life_end = M_PI;
ci_at[t->owner].rocks.emplace_back(std::move(r));
// println(hlog, "OK");
}
nts.index = tv0;
t->turret_states[ctime] = nts;
if(nts.err < 0.01) angle_at_time = nts.angle;
}
void handle_crashes() {
if(paused) return;
if(mtwisted) {
@ -256,11 +382,14 @@ void handle_crashes() {
vector<ads_object*> missiles;
vector<ads_object*> rocks;
vector<ads_object*> resources;
vector<ads_object*> turrets;
for(auto m: displayed) {
if(m->type == oMissile)
missiles.push_back(m);
if(m->type == oRock)
if(m->type == oRock || m->type == oTurret)
rocks.push_back(m);
if(m->type == oTurret)
turrets.push_back(m);
if(m->type == oResource)
resources.push_back(m);
}
@ -274,7 +403,7 @@ void handle_crashes() {
hybrid::in_actual([&] {
gen_particles(rpoisson(crash_particle_qty), m->owner, m->at * ads_matrix(Id, m->life_end), missile_color, crash_particle_rapidity, crash_particle_life);
gen_particles(rpoisson(crash_particle_qty), r->owner, r->at * ads_matrix(Id, r->life_end), r->col, crash_particle_rapidity, crash_particle_life);
gen_resource(r->owner, r->at * ads_matrix(Id, r->life_end), r->resource, r->expire);
if(r->type != oTurret) gen_resource(r->owner, r->at * ads_matrix(Id, r->life_end), r->resource, r->expire);
playSound(nullptr, "hit-crush3");
});
}

View File

@ -2,8 +2,6 @@ namespace hr {
namespace ads_game {
enum eResourceType { rtNone, rtHull, rtGold, rtAmmo, rtFuel, rtOxygen };
color_t rock_color[6] = { 0x703800FF, 0xC0A080FF, 0xC08010FF, 0xC04000FF, 0x408000FF, 0x8040A0FF, };
color_t rsrc_color[6] = { 0x404040FF, 0x40C0C0FF, 0xFFD500FF, 0xFF0000FF, 0x00FF00FF, 0x0000FFFF };

View File

@ -12,6 +12,9 @@ vector<ld> shape_weapon = {-0.0731165, 0.0596477, -0.047071, 0.0268977, 0.080775
vector<ld> shape_fuel = {0.0802337, 0.0224383, 0.0802337, -0.0224383, 0.0224383, -0.0802337, -0.0224383, -0.0802337, -0.0802337, -0.0224383, -0.0802337, 0.0224383, -0.0224383, 0.0802337, 0.0224383, 0.0802337, };
vector<ld> shape_airtank = {-0.101054, 0.0134738, -0.0904219, 0.014429, -0.0779099, 0.0442451, 0.078873, 0.043284, 0.0894665, 0.0259742, 0.0894665, -0.0259742, 0.078873, -0.043284, -0.0779099, -0.0442451, -0.0904219, -0.014429, -0.101054, -0.0134738, };
vector<ld> shape_ship = { 0.0699706, 0, 0.0509304, 0.019032, 0.0056909, 0.023788, 0.0318813, 0.0309258, 0.0330715, 0.0368693, 0.00331668, 0.0380512, -0.0630665, 0.0699568, -0.0619577, 0.041535, -0.0678691, 0.0415233, -0.0678946, 0.0261072, -0.0572505, 0.0237463, -0.0572505, -0.0237463, -0.0678946, -0.0261072, -0.0678691, -0.0415233, -0.0619577, -0.041535, -0.0630665, -0.0699568, 0.00331668, -0.0380512, 0.0330715, -0.0368693, 0.0318813, -0.0309258, 0.0056909, -0.023788, 0.0509304, -0.019032 };
vector<ld> shape_turret = { 0.154282, 0.0304832, 0.134789, 0.0173922, 0.101045, 0.013638, 0.0836262, 0.0210614, 0.083667, 0.0489607, 0.0595074, 0.0812028, 0.00620363, 0.115388, -0.0862034, 0.0682185, -0.0544688, 0.0358999, -0.0544688, -0.0358999, -0.0862034, -0.0682185, 0.00620363, -0.115388, 0.0595074, -0.0812028, 0.083667, -0.0489607, 0.0836262, -0.0210614, 0.101045, -0.013638, 0.134789, -0.0173922, 0.154282, -0.0304832 };
const ld turret_length = 0; // 0.15;
struct ship_model: gi_extension {
map<ld, hpcshape> ship_at_scale;