namespace hr { namespace ads_game { expiry_data gen_expire(cell *c) { expiry_data ed; ed.score = 20 / randd() - 15; ed.score_id = treasure_id(treasure_of(c)); return ed; } bool expired(const expiry_data& ed, const player_data& pdata) { return pdata.score[ed.score_id] > ed.score; } vector history; std::unordered_map ci_at; using worldline_visitor = std::function; void compute_life(cell *c, transmatrix S1, const worldline_visitor& wv) { ld t = 0; int iter = 0; cell *cur_c = c; auto cur_w = hybrid::get_where(c); while(t < TAU) { iter++; auto last_w = cur_w; auto next_w = cur_w; transmatrix next_S1; ld next_t; ld last_time = t; cell *next_c = nullptr; binsearch(t, t+90._deg, [&] (ld t1) { S1 = S1 * chg_shift(t1 - last_time); last_time = t1; virtualRebase(cur_c, S1); cur_w = hybrid::get_where(cur_c); if(cur_w.first != last_w.first) { next_c = cur_c; next_w = cur_w; next_S1 = S1; next_t = t1; return true; } return false; }, 10); if(!next_c) return; S1 = next_S1; cur_w = next_w; t = next_t; cur_c = next_c; if(iter > 1000) { println(hlog, "compute_life c=", cur_c, " w=", cur_w, "t=", t, " S1=", S1); fixmatrix_ads(S1); } if(iter > 1100) break; if(wv(cur_w.first, t)) break; } } map genstats; int gen_budget; void gen_terrain(cell *c, cellinfo& ci, int level = 0) { setdist(c, 7, nullptr); if(level >= ci.mpd_terrain) return; if(!hyperbolic) { println(hlog, "wrong geometry detected in gen_terrain!"); exit(1); } if(ci.mpd_terrain > level + 1) gen_terrain(c, ci, level+1); forCellCM(c1, c) gen_terrain(c1, ci_at[c1], level+1); genstats[level]++; if(level == 2) { int r = hrand(100); if(r < wall_frequency(c)) { int t = hrand(2); if(t == 0) forCellCM(c1, c) if(hrand(100) < 50) if(c1->land == c->land) forCellCM(c2, c1) if(hrand(100) < 50) if(c2->land == c->land) if(ci_at[c2].type == wtNone) ci_at[c2].type = wtDestructible; if(t == 1) forCellCM(c1, c) if(hrand(100) < 50) if(c1->land == c->land) forCellCM(c2, c1) if(hrand(100) < 50) if(c1->land == c->land) if(ci_at[c2].type < wtSolid) ci_at[c2].type = wtSolid; } r = hrand(100); if(r < gate_frequency(c)) ci_at[c].type = wtGate; } ci.mpd_terrain = level; if(c->land == laBarrier) ci_at[c].type = wtBarrier; } void add_rock(cell *c, cellinfo& ci, const ads_matrix& T) { bool fail = false; compute_life(hybrid::get_at(c, 0), unshift(T), [&] (cell *c, ld t) { setdist(c, 7, nullptr); if(c->land == laBarrier) fail = true; return false; }); if(fail) return; eResourceType rt = eResourceType(rand() % 6); auto r = std::make_unique (oRock, c, T, rock_color[rt]); r->resource = rt; r->expire = gen_expire(c); r->shape = &(rand() % 2 ? shape_rock2 : shape_rock); if(geometry != gTwistedProduct) { println(hlog, "wrong geometry detected in gen_rocks 2!"); 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(T), cleanup); /* for(int i=0; ishape[0]); i += 2) { // exact check is too slow here hyperpoint h; h[0] = r->shape[0][i]; h[1] = r->shape[0][i+1]; h[2] = 0; h[3] = 1; */ if(0) for(int i=0; i<4; i++) { hyperpoint h = spin(90*degree*i) * twist::uxpush(0.15) * C0; compute_life(hybrid::get_at(c, 0), unshift(r->at) * rgpushxto0(h), cleanup); } ci.rocks.emplace_back(std::move(r)); } void add_turret(cell *c, cellinfo& ci, const ads_matrix& T) { auto r = std::make_unique (oTurret, c, T, 0xC0C060FF); r->expire = gen_expire(c); 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)); } void gen_resource(cell *c, shiftmatrix from, eResourceType rsrc, const expiry_data& expire); void add_rsrc(cell *c, cellinfo& ci, const ads_matrix& T) { eResourceType rt = eResourceType(rand() % 6); if(rt == rtGoldRocks && c->land == laJungle) rt = rtGoldGate; if(rt == rtGoldRocks && c->land == laHunting) rt = rtGoldTurret; gen_resource(c, T, rt, gen_expire(c)); } int turrets; struct placement { ld alpha; ld r; ld shift; ld spinshift; ld rapidity; ads_matrix get() { return spin(alpha) * twist::uxpush(r/2) * chg_shift(shift) * spin(spinshift) * lorentz(0, 3, rapidity); }; }; /* if maxr equals cgi.rhexf, any point inside the cell equally likely */ placement get_placement(cell *c, ld maxr, ld max_rapidity) { cell *c1 = nullptr; placement p; while(c1 != c) { ld vol = randd() * wvolarea_auto(maxr); p.r = binsearch(0, maxr, [vol] (ld r) { return wvolarea_auto(r) > vol; }); p.alpha = randd() * TAU; hyperpoint h = spin(p.alpha) * xpush0(p.r); c1 = c; virtualRebase(c1, h); } p.shift = randd() * TAU; p.spinshift = randd() * TAU; p.rapidity = randd() * max_rapidity; return p; } 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); forCellCM(c1, c) gen_rocks(c1, ci_at[c1], radius-1); if(!hyperbolic) { println(hlog, "wrong geometry detected in gen_rocks 1!"); exit(1); } if(radius == 0) { int q = rpoisson(rock_density * rock_frequency(c)); for(int i=0; i(oParticle, c, from * spin(randd() * TAU * spread) * lorentz(0, 2, (.5 + randd() * .5) * spd), col ); r->shape = &shape_particle; r->life_end = randd() * t; r->life_start = 0; ro.emplace_back(std::move(r)); } } void gen_resource(cell *c, shiftmatrix from, eResourceType rsrc, const expiry_data& expire) { if(!rsrc) return; auto r = std::make_unique(oResource, c, from, rsrc_color[rsrc]); r->shape = rsrc_shape[rsrc]; r->life_end = HUGE_VAL; r->life_start = 0; r->resource = rsrc; r->expire = expire; ci_at[c].rocks.emplace_back(std::move(r)); } bool pointcrash(hyperpoint h, const vector& vf) { int winding = 0; vector kleins; for(auto& p: vf) kleins.push_back(kleinize(p.h) - h); auto take = [&] (hyperpoint& a, hyperpoint& b) { if(asign(a[1], b[1]) && xcross(b[0], b[1], a[0], a[1]) < 1e-6) winding++; }; for(int i=1; i 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 (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) { if(!currentmap) { println(hlog, "no currentmap!"); return; } PIU({ handle_crashes(); }); return; } vector missiles; vector rocks; vector resources; vector turrets; for(auto m: displayed) { if(m->type == oMissile) missiles.push_back(m); 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); } hybrid::in_underlying_geometry([&] { for(auto m: missiles) { hyperpoint h = kleinize(m->pt_main.h); for(auto r: rocks) { if(pointcrash(h, r->pts)) { m->life_end = m->pt_main.shift; r->life_end = r->pt_main.shift; 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); if(r->type != oTurret) gen_resource(r->owner, r->at * ads_matrix(Id, r->life_end), r->resource, r->expire); playSound(nullptr, "hit-crush3"); }); } } } if(!game_over) for(int i=0; ipts)) ads_crash_ship(); } for(auto r: resources) { if(pointcrash(h, r->pts)) { r->life_end = r->pt_main.shift; gain_resource(r->resource); } } hyperpoint h1 = normalize(h); bool crashed = false; hybrid::in_actual([&] { swap(h1[2], h1[3]); ads_point rel = ads_inverse(current * vctrV) * ads_point(h1, 0); cell *c = hybrid::get_at(vctr, 0); virtualRebase(c, rel.h); optimize_shift(rel); auto w = hybrid::get_where(c); auto& ci = ci_at[w.first]; ld t = rel.shift + w.second * cgi.plevel; if(ci.type == wtDestructible || ci.type == wtSolid || (ci.type == wtGate && (int(floor(t)) & 3) == 0) || ci.type == wtBarrier) { if(!crashed && ship_pt > invincibility_pt) println(hlog, "crashed at t = ", t / TAU, " shift = ", rel.shift/TAU, " sec = ", w.second*cgi.plevel/TAU); crashed = true; } }); if(crashed) ads_crash_ship(); } }); } }}