namespace hr { namespace ads_game { enum eObjType { oRock, oMissile, oParticle }; struct ads_object { eObjType type; cell *owner; ads_matrix at; color_t col; ld life_start, life_end; cross_result pt_main; vector 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 }; struct cellinfo { int mpd_terrain; /* 0 = fully generated terrain */ int rock_dist; /* rocks generated in this radius */ vector rocks; eWalltype type; cellinfo() { mpd_terrain = 4; rock_dist = -1; type = wtNone; } }; 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 < 2 * M_PI) { 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+M_PI/2, [&] (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; }, 20); 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) { if(level >= ci.mpd_terrain) return; 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 < 5) { forCellCM(c1, c) if(hrand(100) < 50) forCellCM(c2, c1) if(hrand(100) < 50) if(ci_at[c2].type == wtNone) ci_at[c2].type = wtDestructible; } else if(r < 10) { forCellCM(c1, c) if(hrand(100) < 50) forCellCM(c2, c1) if(hrand(100) < 50) if(ci_at[c2].type < wtSolid) ci_at[c2].type = wtSolid; } else if(r < 12) ci_at[c].type = wtGate; } ci.mpd_terrain = level; } 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(geometry != gNormal) { println(hlog, "wrong geometry detected in gen_rocks 1!"); exit(1); } if(radius == 0) { hybrid::in_actual([&] { int q = rpoisson(.25); auto add_rock = [&] (ads_object&& r) { if(geometry != gRotSpace) { println(hlog, "wrong geometry detected in gen_rocks 2!"); exit(1); } compute_life(hybrid::get_at(c, 0), unshift(r.at), [&] (cell *c, ld t) { auto& ci = ci_at[c]; hybrid::in_underlying_geometry([&] { gen_terrain(c, ci); }); ci.type = wtNone; return false; }); ci.rocks.emplace_back(r); }; for(int i=0; i missiles; vector rocks; for(auto m: displayed) { if(m->type == oMissile) missiles.push_back(m); if(m->type == oRock) rocks.push_back(m); } hybrid::in_underlying_geometry([&] { for(auto m: missiles) { hyperpoint h = kleinize(m->pt_main.h); for(auto r: rocks) { int winding = 0; vector kleins; for(auto& p: r->pts) 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