2022-09-11 10:16:50 +00:00
|
|
|
namespace hr {
|
|
|
|
|
|
|
|
namespace ads_game {
|
|
|
|
|
2022-09-17 15:34:54 +00:00
|
|
|
color_t shipcolor = 0x2020FFFF;
|
|
|
|
|
2022-09-11 11:44:28 +00:00
|
|
|
cross_result findflat(shiftpoint h) {
|
2022-09-11 10:16:50 +00:00
|
|
|
return cross0(current * rgpushxto0(h));
|
|
|
|
}
|
|
|
|
|
2022-09-18 21:36:47 +00:00
|
|
|
struct cell_to_draw {
|
2022-09-11 11:44:28 +00:00
|
|
|
cross_result center;
|
2022-09-18 21:36:47 +00:00
|
|
|
ld d;
|
|
|
|
cell *c;
|
|
|
|
ads_matrix V;
|
|
|
|
bool operator < (const cell_to_draw& c2) const { return d > c2.d; }
|
|
|
|
};
|
|
|
|
|
2022-10-15 12:23:53 +00:00
|
|
|
void apply_duality(shiftmatrix& S) {
|
|
|
|
if(use_duality == 1) {
|
|
|
|
S.T = unshift(S);
|
|
|
|
S.shift = 0; // get_shift_cycles(S.shift);
|
|
|
|
S.T = Duality * S.T * Duality;
|
|
|
|
};
|
|
|
|
if(use_duality == 2) {
|
2022-11-12 21:38:45 +00:00
|
|
|
S = ads_matrix(Id, -90._deg) * S * ads_matrix(Id, +90._deg);
|
|
|
|
S.T = spin(90._deg) * S.T;
|
2022-10-15 12:23:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-18 21:36:47 +00:00
|
|
|
void draw_game_cell(const cell_to_draw& cd) {
|
2022-10-13 23:01:50 +00:00
|
|
|
bool hv = rotspace;
|
2022-09-18 21:36:47 +00:00
|
|
|
using cellptr = cell*;
|
|
|
|
const cellptr& c = cd.c;
|
|
|
|
const ads_matrix& V = cd.V;
|
|
|
|
|
2022-09-11 11:44:28 +00:00
|
|
|
vector<cross_result> hlist;
|
2022-09-11 10:16:50 +00:00
|
|
|
|
|
|
|
hybrid::in_actual([&]{
|
|
|
|
for(int i=0; i<=c->type; i++) {
|
|
|
|
hyperpoint ha = hybrid::get_corner(c, i, 2, 0);
|
|
|
|
hlist.push_back(findflat(V * ha));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
auto& ci = ci_at[c];
|
|
|
|
if(ci.mpd_terrain > 0) {
|
|
|
|
if(!gen_budget) return;
|
|
|
|
gen_budget--;
|
|
|
|
}
|
2022-10-13 23:01:50 +00:00
|
|
|
PIU({
|
|
|
|
gen_terrain(c, ci);
|
|
|
|
gen_rocks(c, ci, 0);
|
|
|
|
});
|
2022-09-11 10:16:50 +00:00
|
|
|
|
|
|
|
auto& t = ci.type;
|
|
|
|
|
2022-10-13 23:01:50 +00:00
|
|
|
if(hv) ;
|
|
|
|
else if(t == wtGate) {
|
2022-09-11 10:16:50 +00:00
|
|
|
ld minv = hlist[0].shift;
|
|
|
|
ld maxv = hlist[0].shift;
|
|
|
|
for(auto& h: hlist) {
|
|
|
|
ld v = h.shift;
|
|
|
|
if(v < minv) minv = v;
|
|
|
|
if(v > maxv) maxv = v;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto draw_slice = [&] (ld a, ld b, color_t col) {
|
|
|
|
vector<hyperpoint> slice;
|
|
|
|
for(int i=0; i<c->type; i++) {
|
|
|
|
if(hlist[i].shift >= a && hlist[i].shift <= b)
|
|
|
|
slice.push_back(hlist[i].h);
|
|
|
|
if((hlist[i].shift < a) ^ (hlist[i+1].shift < a)) {
|
|
|
|
ld p = ilerp(hlist[i].shift, hlist[i+1].shift, a);
|
|
|
|
hyperpoint h1 = lerp(hlist[i].h, hlist[i+1].h, p);
|
|
|
|
slice.push_back(h1);
|
|
|
|
}
|
|
|
|
if((hlist[i].shift > b) ^ (hlist[i+1].shift > b)) {
|
|
|
|
ld p = ilerp(hlist[i].shift, hlist[i+1].shift, b);
|
|
|
|
hyperpoint h1 = lerp(hlist[i].h, hlist[i+1].h, p);
|
|
|
|
slice.push_back(h1);
|
|
|
|
}
|
|
|
|
if(hlist[i+1].shift < a && hlist[i].shift > b)
|
|
|
|
swap((&slice.back())[-1], slice.back());
|
|
|
|
}
|
|
|
|
if(isize(slice) < 3) return;
|
|
|
|
for(auto e: slice) curvepoint(e);
|
|
|
|
curvepoint(slice[0]);
|
2022-09-28 19:06:07 +00:00
|
|
|
queuecurve(shiftless(Id), 0xFFFFFFFF, col, PPR::WALL);
|
2022-09-11 10:16:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
for(int v=floor(minv); v<maxv+1; v++) {
|
|
|
|
draw_slice(v, v+1, (v & 3) ? 0x080828FF : 0xA04020FF);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
color_t col =
|
|
|
|
t == wtSolid ? 0x603000FF :
|
|
|
|
t == wtDestructible ? 0x301800FF :
|
|
|
|
0x181818FF;
|
|
|
|
|
|
|
|
for(auto h: hlist) curvepoint(h.h);
|
2022-09-18 21:36:47 +00:00
|
|
|
addaura(shiftless(cd.center.h), col >> 8, 0);
|
2022-09-11 10:16:50 +00:00
|
|
|
queuecurve(shiftless(Id), 0x101010FF, col, PPR::WALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(view_proper_times) {
|
2022-09-28 20:10:09 +00:00
|
|
|
string str = format(tformat, cd.center.shift / ads_time_unit);
|
2022-09-18 21:36:47 +00:00
|
|
|
queuestr(shiftless(rgpushxto0(cd.center.h)), .1, str, 0xFF4040, 8);
|
2022-09-11 10:16:50 +00:00
|
|
|
}
|
|
|
|
|
2022-09-12 09:49:28 +00:00
|
|
|
for(auto& r: ci.rocks) {
|
|
|
|
auto& rock = *r;
|
2022-09-18 15:29:01 +00:00
|
|
|
|
|
|
|
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; }
|
|
|
|
}
|
2022-09-11 10:16:50 +00:00
|
|
|
|
2022-09-11 11:42:51 +00:00
|
|
|
hybrid::in_actual([&]{
|
2022-09-11 10:16:50 +00:00
|
|
|
dynamicval<eGeometry> b(geometry, gRotSpace);
|
|
|
|
auto h = V * rock.at;
|
2022-09-11 11:42:51 +00:00
|
|
|
rock.pt_main = cross0(current * h);
|
2022-09-11 10:16:50 +00:00
|
|
|
});
|
2022-09-11 11:42:51 +00:00
|
|
|
|
|
|
|
if(rock.pt_main.shift < rock.life_start || rock.pt_main.shift > rock.life_end) continue;
|
|
|
|
displayed.push_back(&rock);
|
2022-09-11 10:16:50 +00:00
|
|
|
|
2022-09-12 09:49:28 +00:00
|
|
|
rock.pts.clear();
|
|
|
|
auto& shape = *rock.shape;
|
2022-09-11 10:16:50 +00:00
|
|
|
for(int i=0; i<isize(shape); i += 2) {
|
|
|
|
hybrid::in_actual([&]{
|
2022-09-28 20:10:09 +00:00
|
|
|
auto h = V * rock.at * rots::uxpush(shape[i] * ads_scale) * rots::uypush(shape[i+1] * ads_scale);
|
2022-09-11 11:44:28 +00:00
|
|
|
cross_result f = cross0(current * h);
|
2022-09-11 11:42:51 +00:00
|
|
|
rock.pts.push_back(f);
|
2022-09-11 10:16:50 +00:00
|
|
|
});
|
|
|
|
}
|
2022-10-15 12:25:31 +00:00
|
|
|
|
2022-10-13 23:01:50 +00:00
|
|
|
if(hv) {
|
2022-10-15 12:25:31 +00:00
|
|
|
ld t = rock.life_start;
|
|
|
|
if(t < -100) t = 0;
|
|
|
|
ld shift = floor((rock.pt_main.shift - t) / spacetime_step) * spacetime_step + t;
|
|
|
|
ads_point M = current * (V * rock.at) * ads_matrix(Id, shift) * C0;
|
2022-10-13 23:01:50 +00:00
|
|
|
optimize_shift(M);
|
2022-10-15 12:25:31 +00:00
|
|
|
for(int z0=-spacetime_qty; z0<=spacetime_qty; z0++) {
|
|
|
|
ld z = z0 * spacetime_step;
|
|
|
|
if((shift+z) < rock.life_start) continue;
|
|
|
|
if((shift+z) > rock.life_end) continue;
|
2022-10-13 23:01:50 +00:00
|
|
|
for(int i=0; i<isize(shape); i += 2) {
|
|
|
|
auto h = rots::uxpush(shape[i] * ads_scale) * rots::uypush(shape[i+1] * ads_scale) * C0;
|
|
|
|
curvepoint(h);
|
|
|
|
}
|
|
|
|
curvepoint_first();
|
2022-10-15 12:23:53 +00:00
|
|
|
ads_matrix S = current * V * rock.at * ads_matrix(Id, rock.pt_main.shift+z);
|
|
|
|
apply_duality(S);
|
|
|
|
queuecurve(S, rock.col, 0, PPR::LINE);
|
2022-10-13 23:01:50 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-11 10:16:50 +00:00
|
|
|
|
2022-10-13 23:01:50 +00:00
|
|
|
else {
|
|
|
|
for(auto h: rock.pts) curvepoint(h.h);
|
|
|
|
curvepoint(rock.pts[0].h);
|
|
|
|
queuecurve(shiftless(Id),
|
|
|
|
rock.type == oMissile ? missile_color :
|
|
|
|
rock.type == oParticle ? rock.col :
|
|
|
|
0x000000FF, rock.col, obj_prio[rock.type]);
|
|
|
|
}
|
2022-09-11 10:16:50 +00:00
|
|
|
|
2022-09-12 12:57:00 +00:00
|
|
|
if(view_proper_times && rock.type != oParticle) {
|
2022-09-28 20:10:09 +00:00
|
|
|
string str = format(tformat, rock.pt_main.shift / ads_time_unit);
|
2022-09-11 11:42:51 +00:00
|
|
|
queuestr(shiftless(rgpushxto0(rock.pt_main.h)), .1, str, 0xFFFFFF, 8);
|
2022-09-11 10:16:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-17 15:34:54 +00:00
|
|
|
/* todo: binary search */
|
|
|
|
if(paused) for(auto& rock: ci.shipstates) {
|
2022-09-19 21:16:41 +00:00
|
|
|
cross_result cr;
|
2022-10-13 23:01:50 +00:00
|
|
|
|
|
|
|
if(hv) {
|
|
|
|
auto& shape = shape_ship;
|
|
|
|
for(int i=0; i<isize(shape); i += 2) {
|
|
|
|
auto h = rots::uxpush(shape[i] * ads_scale) * rots::uypush(shape[i+1] * ads_scale) * C0;
|
|
|
|
curvepoint(h);
|
|
|
|
}
|
|
|
|
curvepoint_first();
|
2022-10-15 12:23:53 +00:00
|
|
|
ads_matrix S = current * V * rock.at;
|
|
|
|
S = S * spin(-(rock.ang+90)*degree);
|
|
|
|
apply_duality(S);
|
|
|
|
S = S * spin(+(rock.ang+90)*degree);
|
|
|
|
queuecurve(S, shipcolor, 0, PPR::LINE);
|
2022-10-13 23:01:50 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-09-17 15:34:54 +00:00
|
|
|
hybrid::in_actual([&]{
|
|
|
|
dynamicval<eGeometry> b(geometry, gRotSpace);
|
|
|
|
auto h = V * rock.at;
|
2022-09-19 21:16:41 +00:00
|
|
|
cr = cross0(current * h);
|
2022-09-17 15:34:54 +00:00
|
|
|
});
|
2022-09-19 21:16:41 +00:00
|
|
|
|
|
|
|
if(cr.shift < -1e-6 || cr.shift > rock.duration + 1e-6) continue;
|
2022-09-17 15:34:54 +00:00
|
|
|
vector<hyperpoint> pts;
|
|
|
|
|
|
|
|
auto& shape = shape_ship;
|
|
|
|
for(int i=0; i<isize(shape); i += 2) {
|
|
|
|
hybrid::in_actual([&]{
|
2022-09-28 20:10:09 +00:00
|
|
|
auto h = V * rock.at * rgpushxto0(normalize(hyperpoint(shape[i] * ads_scale, shape[i+1] * ads_scale, 1, 0)));
|
2022-09-17 15:34:54 +00:00
|
|
|
pts.push_back(cross0(current * h).h);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
for(auto h: pts) curvepoint(h);
|
|
|
|
curvepoint(pts[0]);
|
2022-09-28 19:06:07 +00:00
|
|
|
queuecurve(shiftless(Id), 0xFF, shipcolor, PPR::MONSTER_FOOT);
|
2022-09-19 21:16:41 +00:00
|
|
|
|
2022-09-29 09:40:21 +00:00
|
|
|
if(view_proper_times) {
|
|
|
|
string str = format(tformat, (cr.shift + rock.start) / ads_time_unit);
|
|
|
|
queuestr(shiftless(rgpushxto0(cr.h)), .1, str, 0xC0C0C0, 8);
|
|
|
|
}
|
2022-09-17 15:34:54 +00:00
|
|
|
}
|
|
|
|
|
2022-10-13 23:01:50 +00:00
|
|
|
if(paused && c == vctr_ship && !game_over && !in_replay && !hv) {
|
2022-09-25 20:20:18 +00:00
|
|
|
cross_result cr;
|
|
|
|
hybrid::in_actual([&]{
|
|
|
|
auto h = ads_inverse(current_ship * vctrV_ship);
|
|
|
|
cr = cross0(current * V * h);
|
|
|
|
// println(hlog, current * V * h);
|
|
|
|
});
|
2022-09-29 09:38:54 +00:00
|
|
|
if(cr.shift < M_PI && cr.shift < M_PI) {
|
2022-09-25 20:20:18 +00:00
|
|
|
vector<hyperpoint> pts;
|
|
|
|
for(int i=0; i<=360; i++) {
|
|
|
|
hybrid::in_actual([&]{
|
|
|
|
auto h = ads_inverse(current_ship * vctrV_ship) * spin(i*degree);
|
|
|
|
auto cr = cross0_light(current * V * h);
|
|
|
|
pts.push_back(cr.h);
|
|
|
|
});
|
|
|
|
}
|
2022-09-18 09:24:43 +00:00
|
|
|
for(auto h: pts) curvepoint(h);
|
2022-09-29 09:38:54 +00:00
|
|
|
queuecurve(shiftless(Id), 0xFF0000C0, cr.shift > 0 ? 0x00000060 : 0xFFFFFF10, PPR::SUPERLINE);
|
2022-09-18 09:24:43 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-11 10:16:50 +00:00
|
|
|
}
|
|
|
|
|
2022-09-29 10:11:50 +00:00
|
|
|
void view_footer() {
|
|
|
|
if(inHighQual) return;
|
|
|
|
string str = "Relative Hell ";
|
|
|
|
str += VER_RH;
|
|
|
|
if(main_rock) str += " dS";
|
|
|
|
else str += " AdS";
|
|
|
|
if(paused) str += " (paused)";
|
|
|
|
if(!nofps) str += XLAT(" fps: ") + its(calcfps());
|
|
|
|
str += " " + copyright_shown;
|
|
|
|
|
|
|
|
queuestr(10, vid.yres-vid.fsize, 0, vid.fsize/2, str, 0x202020, 0, 0);
|
|
|
|
}
|
|
|
|
|
2022-09-17 14:47:22 +00:00
|
|
|
void view_ads_game() {
|
2022-09-11 11:42:51 +00:00
|
|
|
displayed.clear();
|
2022-09-11 10:16:50 +00:00
|
|
|
|
2023-01-31 15:40:09 +00:00
|
|
|
bool hv = mhybrid;
|
2022-10-13 23:01:50 +00:00
|
|
|
|
2022-09-17 14:47:22 +00:00
|
|
|
hybrid::in_actual([&] {
|
2022-09-18 11:34:30 +00:00
|
|
|
gen_budget = max_gen_per_frame;
|
2022-09-17 14:47:22 +00:00
|
|
|
|
|
|
|
vctr = new_vctr;
|
|
|
|
vctrV = new_vctrV;
|
|
|
|
cross_result base;
|
|
|
|
|
2022-09-11 10:16:50 +00:00
|
|
|
// todo rebase
|
|
|
|
base = findflat(ads_point(C0, 0));
|
|
|
|
// println(hlog, base.h);
|
|
|
|
ld ebase = 0;
|
|
|
|
hybrid::in_underlying_geometry([&]{
|
|
|
|
ebase = hdist0(base.h);
|
|
|
|
// println(hlog, "dist base pre = ", hdist0(base.h), " at ", base.shift);
|
|
|
|
});
|
|
|
|
// println(hlog, base.h[2]*base.h[2] - base.h[1]*base.h[1] - base.h[0] * base.h[0]);
|
|
|
|
|
|
|
|
for(int u=0; u<30; u++) {
|
|
|
|
auto bcurrent = current;
|
|
|
|
|
2022-11-12 21:38:45 +00:00
|
|
|
transmatrix T = rgpushxto0( spin(TAU*u/30) * xpush0(0.5) );
|
2022-09-11 10:16:50 +00:00
|
|
|
|
|
|
|
current.T = current.T * T;
|
|
|
|
auto base1 = findflat(ads_point(C0, 0));
|
|
|
|
ld ebase1 = 0;
|
|
|
|
hybrid::in_underlying_geometry([&] {
|
|
|
|
ebase1 = hdist0(base1.h);
|
|
|
|
});
|
|
|
|
if(ebase1 < ebase) { vctrV.T = inverse(T) * vctrV.T; }
|
|
|
|
else { current = bcurrent; }
|
|
|
|
}
|
|
|
|
}
|
2022-09-17 14:47:22 +00:00
|
|
|
);
|
2022-09-11 10:16:50 +00:00
|
|
|
|
2022-09-17 14:47:22 +00:00
|
|
|
if(1) {
|
2022-09-11 10:16:50 +00:00
|
|
|
set<cell*> visited;
|
2022-09-18 21:36:47 +00:00
|
|
|
std::priority_queue<cell_to_draw> dq;
|
|
|
|
auto visit = [&] (cell *c, const ads_matrix& V) {
|
2022-09-18 15:02:15 +00:00
|
|
|
if(visited.count(c)) return;
|
|
|
|
visited.insert(c);
|
2022-09-18 21:36:47 +00:00
|
|
|
|
|
|
|
cell_to_draw cd;
|
|
|
|
cd.c = c;
|
|
|
|
cd.V = V;
|
|
|
|
cd.center = findflat(V * C0);
|
|
|
|
cd.d = hdist0(cd.center.h);
|
|
|
|
if(cd.d < vctr_dist) vctr_dist = cd.d, new_vctr = c, new_vctrV = V;
|
|
|
|
|
|
|
|
dq.emplace(cd);
|
2022-09-11 10:16:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
hybrid::in_actual([&] {
|
|
|
|
vctr_dist = HUGE_VAL;
|
2022-09-18 21:36:47 +00:00
|
|
|
visit(vctr, vctrV);
|
2022-09-11 10:16:50 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
while(!dq.empty()) {
|
2022-09-18 11:34:30 +00:00
|
|
|
|
|
|
|
i++; if(i > draw_per_frame) break;
|
2022-09-18 21:36:47 +00:00
|
|
|
auto& cd = dq.top();
|
|
|
|
draw_game_cell(cd);
|
2022-09-11 10:16:50 +00:00
|
|
|
|
2022-09-18 21:36:47 +00:00
|
|
|
cell *c = cd.c;
|
|
|
|
ads_matrix V = cd.V;
|
|
|
|
dq.pop();
|
2022-09-11 10:16:50 +00:00
|
|
|
|
|
|
|
hybrid::in_actual([&] {
|
2022-09-18 15:02:15 +00:00
|
|
|
auto csl = hybrid::get_at(c, 0);
|
|
|
|
for(int i=0; i<c->type; i++) {
|
|
|
|
cell *csl2 = csl->cmove(i);
|
|
|
|
auto V1 = V * currentmap->adj(csl, i);
|
2022-09-11 10:16:50 +00:00
|
|
|
optimize_shift(V1);
|
2022-09-18 14:49:08 +00:00
|
|
|
|
2022-09-18 15:02:15 +00:00
|
|
|
auto g = hybrid::get_where(csl2);
|
2022-09-18 21:36:47 +00:00
|
|
|
adjust_to_zero(V1, g, cgi.plevel);
|
2022-09-18 15:28:48 +00:00
|
|
|
|
2022-09-18 21:36:47 +00:00
|
|
|
visit(g.first, V1);
|
2022-09-11 10:16:50 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-10-15 15:56:15 +00:00
|
|
|
if(!game_over && !paused && !in_replay && !hv && !which_cross) {
|
2022-09-11 10:16:50 +00:00
|
|
|
poly_outline = 0xFF;
|
2022-09-12 10:57:52 +00:00
|
|
|
if(ship_pt < invincibility_pt) {
|
2022-09-28 20:10:09 +00:00
|
|
|
ld u = (invincibility_pt-ship_pt) / ads_how_much_invincibility;
|
2022-09-12 10:57:52 +00:00
|
|
|
poly_outline = gradient(shipcolor, rsrc_color[rtHull], 0, 0.5 + cos(5*u*TAU), 1);
|
|
|
|
}
|
2022-09-28 23:39:16 +00:00
|
|
|
queuepolyat(shiftless(spin(ang*degree) * Id), make_shape(), shipcolor, PPR::MONSTER_HAIR);
|
2022-09-12 10:57:52 +00:00
|
|
|
poly_outline = 0xFF;
|
2022-09-11 10:16:50 +00:00
|
|
|
|
|
|
|
if(view_proper_times) {
|
2022-09-28 20:10:09 +00:00
|
|
|
string str = format(tformat, ship_pt / ads_time_unit);
|
2022-09-11 10:16:50 +00:00
|
|
|
queuestr(shiftless(Id), .1, str, 0xFFFFFF, 8);
|
|
|
|
}
|
|
|
|
}
|
2022-09-17 13:16:04 +00:00
|
|
|
|
|
|
|
if(paused && view_proper_times) {
|
2022-09-28 20:10:09 +00:00
|
|
|
string str = format(tformat, view_pt / ads_time_unit);
|
2022-09-17 13:16:04 +00:00
|
|
|
queuestr(shiftless(Id), .1, str, 0xFFFF00, 8);
|
|
|
|
}
|
2022-09-17 14:47:22 +00:00
|
|
|
}
|
2022-09-29 10:11:50 +00:00
|
|
|
|
|
|
|
copyright_shown = "";
|
|
|
|
view_footer();
|
2022-09-11 10:16:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}}
|