1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-11-24 05:17:17 +00:00

nilrider:: first commit

This commit is contained in:
Zeno Rogue 2022-04-24 19:48:45 +02:00
parent e25b8b94c2
commit 920bf96088
7 changed files with 1841 additions and 0 deletions

312
rogueviz/nilrider/level.cpp Normal file
View File

@ -0,0 +1,312 @@
namespace nilrider {
void level::init() {
if(initialized) return;
initialized = true;
unil_texture = new texture::texture_data;
auto& tex = *unil_texture;
real_minx = HUGE_VAL;
real_miny = HUGE_VAL;
real_maxx = -HUGE_VAL;
real_maxy = -HUGE_VAL;
if(flags & nrlPolar)
scale = 1;
else
scale = abs(maxx - minx) / isize(map_tiles[0]);
println(hlog, "SCALE IS ", this->scale);
int tY = isize(map_tiles);
int tX = isize(map_tiles[0]);
tex.twidth = tex.tx = tX * 64;
tex.theight = tex.ty = tY * 64;
tex.stretched = false;
tex.strx = tex.tx;
tex.stry = tex.ty;
tex.base_x = 0;
tex.base_y = 0;
tex.whitetexture();
println(hlog, "tX=", tX, " tY=", tY, " tex=", tex.tx, "x", tex.ty);
for(int y=0; y<tex.ty; y++)
for(int x=0; x<tex.tx; x++) {
int bx = x / 64;
int by = y / 64;
char bmch = map_tiles[by][bx];
int sx = (x % 64) / 4;
int sy = (y % 64) / 4;
if(bmch == '!') continue;
char smch = submaps[bmch][sy][sx];
tex.get_texture_pixel(x, y) = bcols[smch];
tex.get_texture_pixel(x, y) |= 0xFF000000;
tex.get_texture_pixel(x, y) ^= hrand(0x1000000) & 0xF0F0F;
// (x * 256 / TEXSIZE + (((y * 256) / TEXSIZE) << 8)) | 0xFF000000;
}
tex.loadTextureGL();
start.where = mappt(startx+.5, starty+.5, 1);
start.t = 0;
current = start;
println(hlog, "start.where = ", start.where);
println(hlog, "current.where = ", current.where, " : ", format("%p", &current));
for(int s=0; s<2; s++) {
cgi.bshape(s == 0 ? shFloor : shPlanFloor, PPR::WALL);
shFloor.flags |= POLY_TRIANGLES;
shPlanFloor.flags |= POLY_TRIANGLES;
auto pt = [&] (int x, int y) {
if(s == 0) uniltinf.tvertices.push_back(glhr::makevertex(x * 1. / tX / 16, y * 1. / tY / 16, 0));
hyperpoint h = mappt(x, y, 16);
real_minx = min(real_minx, h[0]);
real_maxx = max(real_maxx, h[0]);
real_miny = min(real_miny, h[1]);
real_maxy = max(real_maxy, h[1]);
if(s == 1) h[2] = h[3] = 1;
// h[2] = h[0] * h[1] / 2 + .1;
// h[3] = 1;
cgi.hpcpush(h);
// println(hlog, "entered ", h);
// cgi.hpcpush(hyperpoint(rand() % 10 - 5, rand() % 10 - 5, rand() % 10 - 5, 1));
};
for(int y=0; y<tY * 16; y++)
for(int x=0; x<tX * 16; x++) {
char bmch = map_tiles[y/16][x/16];
if(bmch == '!') continue;
pt(x, y);
pt(x, y+1);
pt(x+1, y);
pt(x+1, y+1);
pt(x+1, y);
pt(x, y+1);
}
cgi.finishshape();
}
if(1) {
cgi.bshape(shField, PPR::WALL);
shField.flags |= POLY_TRIANGLES;
auto pt = [&] (hyperpoint p) {
hyperpoint h = mappt(p[0], p[1], 16);
h[2] += p[2];
cgi.hpcpush(h);
// cgi.hpcpush(hyperpoint(rand() % 10 - 5, rand() % 10 - 5, rand() % 10 - 5, 1));
};
for(int y=0; y<tY * 16; y++)
for(int x=0; x<tX * 16; x++) {
int bx = x / 16;
int by = y / 16;
char bmch = map_tiles[by][bx];
if(bmch == 'f' && (x&1) && (y&1)) {
for(int s=0; s<4; s++) {
hyperpoint st = point3(x+.1, y+.1, 0);
hyperpoint a = spin(90*degree*s) * point3(.1, .1, 0);
hyperpoint b = spin(90*degree*(s+1)) * point3(.1, .1, 0);
hyperpoint hi = point3(0, 0, 1);
for(int z=0; z<3; z++) {
ld z1 = (3-z) / 3.;
ld z2 = (2-z) / 3.;
pt(st + a * z1 + hi*z);
pt(st + b * z1 + hi*z);
pt(st + a * z2 + hi*(z+1));
pt(st + a * z2 + hi*(z+1));
pt(st + b * z1 + hi*z);
pt(st + b * z2 + hi*(z+1));
}
}
}
}
cgi.finishshape();
}
if(1) {
cgi.bshape(shCastle, PPR::WALL);
shCastle.flags |= POLY_TRIANGLES;
for(int y=0; y<tY; y++)
for(int x=0; x<tX; x++) {
char bmch = map_tiles[y][x];
if(bmch == 'r') {
for(int s=0; s<4; s++) {
hyperpoint ctr = mappt(x+.5, y+.5, 1);
ctr[2] += safe_alt(ctr) + .5 * scale;
ld need = safe_alt(ctr, -1) / scale / scale;
int max_y = need * 2 + 1;
hyperpoint a = spin(90*degree*s) * point3(1, 0, 0);
hyperpoint b = spin(90*degree*s) * point3(0, 1, 0);
auto pt = [&] (ld af, ld bf, ld yf) {
hyperpoint ha = a * af * scale; ha[3] = 1;
hyperpoint hb = b * bf * scale; hb[3] = 1;
hyperpoint res = rgpushxto0(ctr) * rgpushxto0(ha) * rgpushxto0(hb) * point31(0, 0, yf * scale * scale);
cgi.hpcpush(res);
};
auto ptf = [&] (ld af, ld bf, ld yf) {
pt(af, bf, yf);
castle_tinf.tvertices.push_back(glhr::makevertex(bf, yf*4, 0));
};
auto ptc = [&] (ld af, ld bf, ld yf, ld xt, ld yt) {
pt(af, bf, yf);
castle_tinf.tvertices.push_back(glhr::makevertex(xt, yt, 0));
};
for(int w=0; w<2; w++)
for(int as=0; as<8; as++)
for(int y=0; y<max_y; y++) {
ld xf = w ? .4 : .5;
ld y1 = -y/2.;
ld y2 = -(y+1)/2.;
ld asd = (as-4) / 8.;
ld asd1 = (as-3) / 8.;
auto oasd = asd / 4;
if(w) asd *= .8, asd1 *= .8, oasd *= .8 * .8;
ptf(xf, asd, y1 - oasd);
ptf(xf, asd1, y1 - oasd);
ptf(xf, asd, y2 - oasd);
ptf(xf, asd, y2 - oasd);
ptf(xf, asd1, y1 - oasd);
ptf(xf, asd1, y2 - oasd);
}
ld x1 = 1/32.;
ld x2 = 1/4. + x1;
ld y1 = 1/32.;
ld y2 = 1/8. + x1;
for(int as=0; as<8; as++) {
ld asd = (as-4) / 8.;
ld asd1 = (as-3) / 8.;
ld asdw = asd * .8;
ld asdw1 = asd1 * .8;
ld asd2 = (as-5) / 8.;
//ld asdw2 = asd2 * .8;
ld oasd = asd / 4;
ld oasdw = oasd * .8 * .8;
ld oasd2 = asd2 / 4;
ld oasdw2 = oasd2 * .8 * .8;
/* tops */
ptc(.5, asd, -oasd, x1, y1);
ptc(.5, asd1, - oasd, x1, y2);
ptc(.4, asdw, -oasdw, x2, y1);
ptc(.4, asdw, -oasdw, x2, y1);
ptc(.5, asd1, - oasd, x1, y2);
ptc(.4, asdw1, -oasdw, x2, y2);
/* sides */
ptc(.5, asd, -oasd, x1, y1);
ptc(.5, asd, -oasd2, x1, y2);
ptc(.4, asdw, -oasdw, x2, y1);
ptc(.4, asdw, -oasdw, x2, y1);
ptc(.5, asd, -oasd2, x1, y2);
ptc(.4, asdw, -oasdw2, x2, y2);
}
}
}
if(bmch == 'o') {
hyperpoint h = mappt(x+.5, y+.5, 1);
h[2] += safe_alt(h) + 1;
statues.emplace_back(statue{rgpushxto0(h), &shBall, 0xFFFFFFFF});
}
if(bmch == 'x') {
hyperpoint h = mappt(x+.5, y+.5, 1);
statues.emplace_back(statue{rgpushxto0(h), &shGeostatue, 0xFFFFFFFF});
}
}
cgi.finishshape();
// println(hlog, shFloor[i].s, " to ", shFloor[i].e);
}
cgi.extra_vertices();
init_plan();
}
/* convert ASCII map coordinates to Heisenberg coordinates */
hyperpoint level::mappt(ld x, ld y, int s) {
int tY = isize(map_tiles);
int tX = isize(map_tiles[0]);
x /= s;
y /= s;
hyperpoint h;
h[0] = lerp(minx, maxx, x / tX);
h[1] = lerp(miny, maxy, y / tY);
if(flags & nrlPolar)
tie(h[0], h[1]) = make_pair(h[1] * sin(h[0]), h[1] * cos(h[0]));
h[2] = surface(h);
h[3] = 1;
return h;
};
/*
plan.emplace_back(start.where, hpxy(0, 1));
plan.emplace_back(mappt(4.5, 10.5), hpxy(1, 1));
plan.emplace_back(mappt(0.01, 3, 1), hpxy(0, -2));
plan.emplace_back(mappt(2, 3.99, 1), hpxy(4, 0));
plan.emplace_back(mappt(42, 3.99, 1), hpxy(4, 0));
*/
void level::init_plan() {
plan.emplace_back(start.where, hpxy(0, -1));
plan.emplace_back(mappt(6.8, 10.2, 1), hpxy(1.5, -1.5));
plan.emplace_back(mappt(10.5, 10.5, 1), hpxy(1.5, 1.5));
plan.emplace_back(mappt(10.5, 4.5, 1), hpxy(-1.5, 1.5));
plan.emplace_back(mappt(4.5, 4.5, 1), hpxy(-1.5, 1.5));
plan.emplace_back(mappt(4.5, 2, 1), hpxy(1.5, 0.5));
plan.emplace_back(mappt(10.5, 2, 1), hpxy(1.5, -0.5));
plan.emplace_back(mappt(10.5, 4.5, 1), hpxy(-2, 0));
plan.emplace_back(mappt(6.5, 6.5, 1), hpxy(-1.5, -1.5));
plan.emplace_back(mappt(4.5, 10.5, 1), hpxy(1.5, -1.5));
plan.emplace_back(mappt(10.5, 9.5, 1), hpxy(1.5, 1.5));
/* plan.emplace_back(mappt(0.01, 3, 1), hpxy(0, -2));
plan.emplace_back(mappt(2, 3.99, 1), hpxy(4, 0));
plan.emplace_back(mappt(42, 3.99, 1), hpxy(4, 0));
*/
current = start;
timer = 0;
}
ld level::safe_alt(hyperpoint h, ld mul) {
ld maxv = 0;
for(int x: {-1, 0, 1})
for(int y: {-1, 0, 1}) {
hyperpoint c = sym_to_heis(point31(x*.5*scale, y*.5*scale, 0));
hyperpoint j = rgpushxto0(h) * c;
maxv = max(maxv, mul * (surface(j) - j[2]));
}
return maxv;
}
void level::draw_level(const shiftmatrix& V) {
if(false) for(int i=0; i<6; i++) {
auto &poly = queuepoly(V * rgpushxto0(start.where) * cpush(2, 0.3 + 0.3 * sin(ticks / 500.)), shMini[i], 0xFFFF00FF);
poly.tinf = &floor_texture_vertices[cgi.shFloor.id];
ensure_vertex_number(*poly.tinf, poly.cnt);
}
if(true) {
auto& poly = queuepoly(V, shCastle, 0xC02020FF);
poly.tinf = &castle_tinf;
castle_tinf.texture_id = castle_texture->textureid;
}
for(auto st: statues) queuepoly(V * st.T, *st.shape, st.color);
queuepoly(V, shField, 0xFFFF00FF);
auto& poly = queuepoly(V, shFloor, 0xFFFFFFFF); // 0xFFFFFFFF);
poly.tinf = &uniltinf;
uniltinf.texture_id = unil_texture->textureid;
}
}

View File

@ -0,0 +1,430 @@
namespace nilrider {
ld f_heisenberg0(hyperpoint h) { return 0; }
ld rot_plane(hyperpoint h) {
return h[0] * h[1] / 2;
}
ld f_rot_well(hyperpoint h) {
return h[0] * h[1] / 2 + h[0] * h[0] + h[1] * h[1];
}
ld long_x(hyperpoint h) {
return h[0] * h[1];
}
ld geodesics_0(hyperpoint h) {
ld r = hypot_d(2, h);
ld phi = atan2(h[1], h[0]);
ld z = (phi / 2 / M_PI) * (M_PI * r * r + 2 * M_PI);
return z + rot_plane(h);
}
ld geodesics_at_4(hyperpoint h) {
ld r = 4;
ld phi = atan2(h[1], h[0]);
ld z = (phi / 2 / M_PI) * (M_PI * r * r + 2 * M_PI);
return z + rot_plane(h);
}
map<char, color_t> bcols = {
{' ', 0xFF101010},
{'W', 0xFFFFFFFF},
{'g', 0xFF008000},
{'h', 0xFF20A020},
{'r', 0xFFFF4040},
{'u', 0xFF4040FF},
{'b', 0xFF804000},
{'l', 0xFF0000C0},
{'f', 0xFF603000},
{'F', 0xFF804000},
{'2', 0xFF404040},
{'4', 0xFF808080},
{'6', 0xFFC0C0C0},
};
map<char, array<string, 16> > submaps = {
{'o', {
"WWWWWWWWWWWWWWWW",
"W22222222222222W",
"W22222666622222W",
"W22266222266222W",
"W22622222222622W",
"W22622222222622W",
"W26222222222262W",
"W262222WW222262W",
"W262222WW222262W",
"W26222222222262W",
"W22622222222622W",
"W22622222222622W",
"W22266222266222W",
"W22222666622222W",
"W22222222222222W",
"WWWWWWWWWWWWWWWW"
}},
{'x', {
"WWWWWWWWWWWWWWWW",
"W22222222222222W",
"W22222222222222W",
"W22222222222222W",
"W22222222222222W",
"W22222222222222W",
"W22222622622222W",
"W222222rW222222W",
"W222222Wr222222W",
"W22222622622222W",
"W22222222222222W",
"W22222222222222W",
"W22222222222222W",
"W22222222222222W",
"W22222222222222W",
"WWWWWWWWWWWWWWWW"
}},
{'b', {
" ",
" rrr rrr rrr rrr",
" ",
"rr rrr rrr rrr r",
" ",
" rrr rrr rrr rrr",
" ",
"rr rrr rrr rrr r",
" ",
" rrr rrr rrr rrr",
" ",
"rr rrr rrr rrr r",
" ",
" rrr rrr rrr rrr",
" ",
"rr rrr rrr rrr r",
}},
{'f', {
"FfFfFfFfFfFfFfFf",
"fFfFfFfFfFfFfFfF",
"FfFfFfFfFfFfFfFf",
"fFfFfFfFfFfFfFfF",
"FfFfFfFfFfFfFfFf",
"fFfFfFfFfFfFfFfF",
"FfFfFfFfFfFfFfFf",
"fFfFfFfFfFfFfFfF",
"FfFfFfFfFfFfFfFf",
"fFfFfFfFfFfFfFfF",
"FfFfFfFfFfFfFfFf",
"fFfFfFfFfFfFfFfF",
"FfFfFfFfFfFfFfFf",
"fFfFfFfFfFfFfFfF",
"FfFfFfFfFfFfFfFf",
"fFfFfFfFfFfFfFfF",
}},
{'l', {
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
"llllllllllllllll",
}},
{'g', {
"ghghghghghghghgh",
"hghghghghghghghg",
"ghghghghghghghgh",
"hghghghghghghghg",
"ghghghghghghghgh",
"hghghghghghghghg",
"ghghghghghghghgh",
"hghghghghghghghg",
"ghghghghghghghgh",
"hghghghghghghghg",
"ghghghghghghghgh",
"hghghghghghghghg",
"ghghghghghghghgh",
"hghghghghghghghg",
"ghghghghghghghgh",
"hghghghghghghghg",
}},
{'G', {
"ghghghghghghghgh",
"hghghghghghWhghg",
"ghghrhghghWlWhgh",
"hghrWrhghghWhghg",
"ghghrhghghghghgh",
"hghghghghghghghg",
"ghghghghghghghgh",
"hghghghlhghghghg",
"ghghghlWlhghghgh",
"hghghghlhghghghg",
"ghghghghghghgrgh",
"hghglghghghgrWrg",
"ghglWlghghghgrgh",
"hghglghghghghghg",
"ghghghghghghghgh",
"hghghghghghghghg",
}},
{'r', {
"rrrrrrrrrrrrrrru",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"ubbbbbbbbbbbbbbu",
"urrrrrrrrrrrrrrr",
}},
{'*', {
"WWWWWW WW WWWWWW",
"W W",
"W W",
"W W",
"W W",
"W rr W",
" rr ",
"W r r W",
"W r r W",
" r r ",
"W r r W",
"W rrrrrrrr W",
"W W",
"W W",
"W W",
"WWWWWW WW WWWWWW",
}},
{'+', {
"gh WW gh",
"hg WW hg",
" WW ",
" ",
" ",
" WW ",
" WW ",
"WWW WWWWWW WWW",
"WWW WWWWWW WWW",
" WW ",
" WW ",
" ",
" ",
" WW ",
"gh WW gh",
"hg WW hg",
}},
{'-', {
"ghghghghghghghgh",
"hghghghghghghghg",
" ",
" ",
" ",
" ",
" ",
"WWW WWWWWW WWW",
"WWW WWWWWW WWW",
" ",
" ",
" ",
" ",
" ",
"ghghghghghghghgh",
"hghghghghghghghg",
}},
{'|', {
"gh WW gh",
"hg WW hg",
"gh WW gh",
"hg hg",
"gh gh",
"hg WW hg",
"gh WW gh",
"hg WW hg",
"gh WW gh",
"hg WW hg",
"gh WW gh",
"hg hg",
"gh gh",
"hg WW hg",
"gh WW gh",
"hg WW hg",
}},
};
level rotplane(
"Trying to be horizontal", 'r', 0,
"All the lines going through the center are horizontal.",
-7.5*dft_block, 7.5*dft_block, 8.5*dft_block, -8.5*dft_block,
{
"ggggggggggggggg!",
"ggggggfffgggggg!",
"ggggggfffgggggg!",
"gggg|ggggg|gggg!",
"ggg-+-----+-ggg!",
"gggg|ggggf|gggg!",
"ggGg|g+ggg|grgG!",
"gGgg|g|xgo|gggg!",
"ggGg|g|ggg|grgg!",
"gggg|g|ggg|gggg!",
"gg--+-+---+--gg!",
"gggg|ggggg|gggg!",
"gggggggGGgggggg!",
"ggggggggggggggg!",
"ggggggggggggggg!",
"!!!!!!!!!!!!!!!!"
},
6, 6,
rot_plane
);
level longtrack(
"A Long Track", 'l', 0,
"The main street is horizontal, as well as the lines orthogonal to it.",
0*dft_block, +2.5*dft_block, 64*dft_block, -1.5*dft_block,
{
"gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg",
"gggggggrgggggggrggggggggrGggggggggGGggggGGGgggggGGGGggggggggggGG",
"g+------------------------------------------------------------+G",
"gggggfffffggggggggggggggggggggggggggggggggggggggggggggggggggggGG"
},
0, 1,
long_x
);
level geodesical(
"Roads are Geodesics", 'g', nrlPolar,
"All the roads here are helical geodesics.",
-45*degree, 3*dft_block, 225*degree, 0,
// -8*dft_block, +8*dft_block, +8*dft_block, 0,
{
"ffffffffffffffff",
"----------------",
"----------------",
"----------------",
"----------------",
"----------------",
"----------------",
"bbbbbbbbbbbbbbbb",
},
0, 6,
geodesics_0
);
level geodesical4(
"Helical Geodesic", 's', nrlPolar,
"The main road here is a helical geodesic. Orthogonal lines are horizontal.",
-80*degree, 8.5*dft_block, 260*degree, 0.5*dft_block,
// -8*dft_block, +8*dft_block, +8*dft_block, 0,
{
"!!!!!!!!!!!!!!!!",
"ffffffffffffffff",
"gggggggggggggggg",
"ggGggggggggGgggg",
"+--------------+",
"gggggGggggGggggg",
"gggGgggggGgggggg",
"ffffffffffffffff",
},
0, 5,
geodesics_at_4
);
level heisenberg0(
"Heisenberg Zero", 'z', 0,
"This is the plane z=0 in the Heisenberg group model of Nil. The roads are x=0, y=0 axes.",
-7.5*dft_block, 7.5*dft_block, 8.5*dft_block, -8.5*dft_block,
{
"ggggggg|ggggggg!",
"grggggg|gggggrg!",
"ggggggg|ggggggg!",
"gggffgg|ggggggg!",
"gggffgg|ggfrggg!",
"ggggggg|gggggGg!",
"ggggggg|ggggggg!",
"-------+-------!",
"ggggggg|ggggggg!",
"gggGgog|ggggggg!",
"ggggggg|ggrgrgg!",
"gggGgGg|ggggggg!",
"ggggggg|ggggggg!",
"grggggg|gggggrg!",
"ggggggg|ggggggg!",
"!!!!!!!!!!!!!!!!"
},
8, 8,
f_heisenberg0
);
level rotwell(
"Deep Well", 'd', 0,
"Can you escape this well?",
-7.5*dft_block, 7.5*dft_block, 8.5*dft_block, -8.5*dft_block,
{
"ggggggggggggggg!",
"gogggggggggggog!",
"ggggg--*--ggggg!",
"gggg*ggggg*gggg!",
"ggg*ggGfGgg*ggg!",
"gg|ggfgggfgg|gg!",
"gg|gGgggggGg|gg!",
"gg*gfggxggfg*gg!",
"gg|gGgggggGg|gg!",
"gg|ggfgggfgg|gg!",
"ggg*ggGfGgg*ggg!",
"gggg*ggggg*gggg!",
"ggggg--*--ggggg!",
"gogggggggggggog!",
"ggggggggggggggg!",
"!!!!!!!!!!!!!!!!"
},
8, 8,
f_rot_well
);
level labyrinth(
"Labyrinth", 'l', 0,
"Go clockwise. The squares of this level have half of their usual length.",
-7.5*dft_block/2, 7.5*dft_block/2, 8.5*dft_block/2, -8.5*dft_block/2,
{
"ogggrfffffffffo!",
"g*ggrgggggggggg!",
"ggggrgggggggggg!",
"ggggrgggggggggg!",
"ggggrgggrrggggg!",
"ggggrgGGGrrgggg!",
"ggggrGgggGrgggg!",
"ggggrGgxgGrgggg!",
"ggggrGgggGrgggg!",
"ggggrrGGGrrgggg!",
"gggggrrrrrggggg!",
"ggggggggggggggg!",
"ggggggggggggggg!",
"ggggggggggggggg!",
"offfffffffffffo!",
"!!!!!!!!!!!!!!!!"
},
8, 8,
rot_plane
);
level *curlev = &rotplane;
vector<level*> all_levels = {
&rotplane, &longtrack, &geodesical, &geodesical4, &heisenberg0, &rotwell, &labyrinth
};
}

View File

@ -0,0 +1,290 @@
#include "nilrider.h"
#include "statues.cpp"
#include "timestamp.cpp"
#include "levels.cpp"
#include "level.cpp"
#include "planning.cpp"
namespace nilrider {
/** is the game paused? */
bool paused = false;
bool planning_mode = false;
bool view_simulation = false;
int simulation_start_tick;
void frame() {
if(planning_mode && !view_simulation) return;
shiftmatrix V = ggmatrix(cwt.at);
curlev->draw_level(V);
curlev->current.draw_unilcycle(V);
}
bool turn(int delta) {
if(planning_mode && !view_simulation) return false;
Uint8 *keystate = SDL_GetKeyState(NULL);
if(keystate[SDLK_RIGHT] && !paused) curlev->current.heading_angle -= delta / 1000.;
if(keystate[SDLK_LEFT] && !paused) curlev->current.heading_angle += delta / 1000.;
if(keystate[SDLK_UP] && !paused) min_gfx_slope -= delta / 1000.;
if(keystate[SDLK_DOWN] && !paused) min_gfx_slope += delta / 1000.;
curlev->current.heading_angle += mouseaim_x;
min_gfx_slope += mouseaim_y;
#if CAP_VR
if(vrhr::active()) {
curlev->current.heading_angle += vrhr::vraim_x * delta / 400;
min_gfx_slope -= vrhr::vraim_y * delta / 400;
}
#endif
if(min_gfx_slope < -90*degree) min_gfx_slope = -90*degree;
if(min_gfx_slope > +90*degree) min_gfx_slope = +90*degree;
if(!paused && !view_simulation) for(int i=0; i<delta; i++) {
curlev->history.push_back(curlev->current);
bool b = curlev->current.tick(curlev);
if(b) timer += 1. / tps;
else curlev->history.pop_back();
}
if(!paused) curlev->current.centerview(curlev);
return false;
}
void main_menu();
void run() {
cmode = sm::MAP;
clearMessages();
dialog::init();
if(view_simulation && !paused) {
int ttick = gmod(ticks - simulation_start_tick, isize(curlev->history));
timer = ttick * 1. / tps;
curlev->current = curlev->history[ttick];
curlev->current.centerview(curlev);
}
gamescreen(0);
if(planning_mode && !view_simulation) {
curlev->draw_planning_screen();
if(!holdmouse) {
auto t0 = SDL_GetTicks();
while(SDL_GetTicks() < t0 + 100) {
if(!curlev->simulate()) break;
}
}
}
curlev->current.draw_instruments(timer);
if(paused && !planning_mode) {
displayButton(current_display->xcenter, current_display->ycenter, mousing ? XLAT("paused -- click to unpause") : XLAT("paused -- press p to continue"), 'p', 8);
}
int x = vid.fsize;
auto show_button = [&] (char c, string s, color_t col = dialog::dialogcolor) {
if(displayButtonS(x, vid.yres - vid.fsize, s, col, 0, vid.fsize))
getcstat = c;
x += textwidth(vid.fsize, s) + vid.fsize;
};
if(planning_mode && !view_simulation) {
for(auto& b: buttons) show_button(b.first, b.second, planmode == b.first ? 0xFFD500 : dialog::dialogcolor);
show_button('s', "simulation");
}
if(planning_mode && view_simulation) {
show_button('s', "return");
show_button('p', "pause", paused ? 0xFF0000 : dialog::dialogcolor);
}
if(!planning_mode) {
show_button('p', "pause", paused ? 0xFF0000 : dialog::dialogcolor);
}
show_button('v', "menu");
dialog::add_key_action('v', [] {
paused = true;
pushScreen(main_menu);
});
dialog::add_key_action('p', [] {
paused = !paused;
if(view_simulation && !paused)
simulation_start_tick = ticks - timer * tps;
});
dialog::add_key_action('-', [] {
paused = false;
});
dialog::add_key_action('b', [] {
if(planning_mode)
simulation_start_tick += 500;
else {
for(int i=0; i<500; i++) if(!curlev->history.empty()) curlev->history.pop_back();
curlev->current = curlev->history.back();
timer = isize(curlev->history) * 1. / tps;
}
});
if(planning_mode) dialog::add_key_action('s', [] {
view_simulation = !view_simulation;
paused = false;
simulation_start_tick = ticks;
});
dialog::display();
keyhandler = [] (int sym, int uni) {
if(paused) handlePanning(sym, uni);
if(planning_mode && !view_simulation && curlev->handle_planning(sym, uni)) return;
dialog::handleNavigation(sym, uni);
};
}
int speedlimit = 0;
vector<string> speedlimit_names = {"none", "yellow", "green", "full"};
void pick_level() {
clearMessages();
dialog::init(XLAT("select the track"), 0xC0C0FFFF, 150, 100);
for(auto l: all_levels) {
dialog::addItem(l->name, l->hotkey);
dialog::add_action([l] {
curlev = l;
recompute_plan_transform = true;
l->init();
popScreen();
});
}
dialog::addBreak(100);
dialog::addBack();
dialog::display();
}
void pick_game() {
clearMessages();
dialog::init(XLAT("how do you want to play?"), 0xC0C0FFFF, 150, 100);
dialog::addSelItem("selected track", curlev->name, 't');
dialog::add_action_push(pick_level);
dialog::addInfo(curlev->longdesc);
dialog::addBreak(100);
add_edit(speedlimit);
add_edit(planning_mode);
dialog::addBreak(100);
dialog::addBack();
dialog::display();
}
void settings() {
dialog::init(XLAT("settings"), 0xC0C0FFFF, 150, 100);
dialog::addItem("RogueViz settings", 'r');
dialog::add_key_action('r', [] {
pushScreen(showSettings);
});
dialog::addBreak(100);
dialog::display();
dialog::addBack();
}
reaction_t on_quit = [] { exit(0); };
void main_menu() {
clearMessages();
dialog::init(XLAT("Nil Rider"), 0xC0C0FFFF, 150, 100);
dialog::addItem("continue", 'c');
dialog::add_action(popScreen);
if(!planning_mode) {
dialog::addItem("restart", 'r');
dialog::add_action([] {
curlev->current = curlev->start;
timer = 0;
paused = false;
popScreen();
});
dialog::addItem("view the replay", 'v');
dialog::add_action([] {
});
dialog::addItem("save the replay", 'e');
dialog::add_action([] {
});
}
else {
dialog::addItem("save this plan", 's');
dialog::add_action([] {
});
}
dialog::addItem("change track or game settings", 't');
dialog::add_action_push(pick_game);
dialog::addItem("change other settings", 'o');
dialog::add_action_push(settings);
dialog::addItem("quit", 'q');
dialog::add_action([] {
on_quit();
});
dialog::display();
}
bool on;
void initialize() {
check_cgi();
cgi.prepare_shapes();
init_statues();
curlev->init();
param_enum(planning_mode, "nil_planning", "nil_planning", false)
-> editable({{"manual", "control the unicycle manually"}, {"planning", "try to plan the optimal route!"}}, "game mode", 'p');
param_enum(speedlimit, "nil_speedlimit", "nil_speedlimit", 0)
-> editable({
{"no limit", "reach the goals as fast as you wan"},
{"yellow", "your speed must be in the yellow zone to collect"},
{"green", "your speed must be in the green zone to collect"},
{"full", "you must fully stop to collect"}
}, "speed limit", 's');
rv_hook(hooks_frame, 100, frame);
rv_hook(shmup::hooks_turn, 100, turn);
on = true;
on_cleanup_or_next([] { on = false; });
pushScreen(run);
}
auto celldemo = arg::add3("-unilcycle", initialize) + arg::add3("-unilplan", [] { planning_mode = true; }) + arg::add3("-viewsim", [] { view_simulation = true; })
+ arg::add3("-oqc", [] { on_quit = popScreenAll; })
+ arg::add3("-fullsim", [] {
/* for animations */
popScreenAll();
rv_hook(anims::hooks_anim, 100, [] {
int ttick = ticks % isize(curlev->history);
timer = ttick * 1. / tps;
curlev->current = curlev->history[ttick];
curlev->current.centerview(curlev);
anims::moved();
});
}) + arg::add3("-unillevel", [] {
arg::shift();
for(auto l: all_levels) if(appears(l->name, arg::args())) curlev = l;
if(on) curlev->init();
})
+ arg::add3("-simplemodel", [] {
nisot::geodesic_movement = false;
pmodel = mdPerspective;
pconf.rotational_nil = 0;
});
}

View File

@ -0,0 +1,130 @@
#include "../rogueviz.h"
namespace nilrider {
using namespace rogueviz;
struct level;
struct timestamp {
hyperpoint where; /**< the current position of the unicycle */
ld heading_angle; /**< the current heading angle */
ld vel; /**< the current velocity in units per second */
ld circpos; /**< controls the wheel graphics */
ld slope; /**< the current slope */
ld t; /**< planning spline parameter */
bool tick(level*);/**< one tick of the simulation -- returns false if the unicycle has stopped or crashed */
void centerview(level*);
void draw_unilcycle(const shiftmatrix&);
void draw_instruments(ld t);
ld energy_in_squares();
};
struct planpoint {
hyperpoint at;
hyperpoint vel;
planpoint(hyperpoint a, hyperpoint v): at(a), vel(v) {};
};
constexpr flagtype nrlPolar = Flag(1);
struct statue {
transmatrix T;
hpcshape *shape;
color_t color;
};
struct level {
string name;
char hotkey;
string longdesc;
flagtype flags;
ld minx, miny, maxx, maxy;
vector<string> map_tiles;
ld startx, starty;
ld scale;
std::function<ld(hyperpoint h)> surface;
bool initialized;
level(string name, char hotkey, flagtype flags, string longdesc, ld minx, ld miny, ld maxx, ld maxy, const vector<string>& mt, ld sx, ld sy, const std::function<ld(hyperpoint h)>& surf) :
name(name), hotkey(hotkey), longdesc(longdesc), flags(flags), minx(minx), miny(miny), maxx(maxx), maxy(maxy), map_tiles(mt), startx(sx), starty(sy), surface(surf) { initialized = false; }
ld real_minx, real_miny, real_maxx, real_maxy;
/* data */
hpcshape shFloor; /**< the 3D model of floor */
hpcshape shPlanFloor; /**< the 3D model of floor for planning */
hpcshape shField; /**< the 3D model of the 'field' */
hpcshape shCastle; /**< the 3D model of the 'castle' */
vector<statue> statues;
/** the texture data used for the ground */
texture::texture_data *unil_texture;
/** the texture used for the ground */
basic_textureinfo uniltinf;
/** the texture used for the ground */
basic_textureinfo castle_tinf;
/** starting timestamp */
timestamp start;
/** current timestamp */
timestamp current;
/** initialize textures and start */
void init();
vector<timestamp> history;
/** plan for the planning mode */
vector<planpoint> plan;
void init_plan();
bool simulate();
void draw_planning_screen();
void draw_level(const shiftmatrix& V);
shiftmatrix plan_transform;
hyperpoint get_spline(ld t);
hyperpoint mappt(ld x, ld y, int s);
ld safe_alt(hyperpoint h, ld mul = 1);
void compute_plan_transform();
bool handle_planning(int sym, int uni);
};
/** ticks per second */
inline const ld tps = 1000;
/** wheel radius */
inline ld whrad = 0.05;
/** epsilon used to measure slope */
inline ld slope_eps = 0.01;
/** gravity acceleration constant, in units per second squared */
inline ld gravity = 1 / 16.;
/** the distance of camera from the wheel */
inline ld whdist = 0.5;
/** minimum slope for rendering */
inline ld min_gfx_slope = +M_PI/2;
/** current slope for rendering */
inline ld gfx_slope = 0;
/** the timer */
inline ld timer = 0;
/** default block unit */
inline double dft_block = 1;
extern map<char, color_t> bcols;
extern map<char, array<string, 16> > submaps;
hyperpoint sym_to_heis(hyperpoint H);
}

View File

@ -0,0 +1,287 @@
namespace nilrider {
hyperpoint get_spline(ld t);
bool level::simulate() {
if(history.empty())
history.push_back(start);
auto at = history.back();
if(at.t >= isize(plan) - 1.001) return false;
ld goal_t;
if(1) {
int steps = 20;
ld min_t, max_t;
if(isize(history) == 1) {
steps = 60;
min_t = at.t;
max_t = at.t + 0.5;
}
else {
ld ldiff = history.back().t - history[history.size() - 2].t;
min_t = at.t;
max_t = min<ld>(at.t + ldiff + .1, isize(plan)-1);
}
auto f = [&] (ld t) {
hyperpoint h = get_spline(t);
auto copy = at;
copy.heading_angle = atan2(h[1] - at.where[1], h[0] - at.where[0]);
copy.tick(this);
return sqhypot_d(2, copy.where-h);
};
string seq = "";
for(int i=0; i<steps; i++) {
ld t1 = min_t * .6 + max_t * .4;
ld t2 = min_t * .4 + max_t * .6;
auto e1 = f(t1);
auto e2 = f(t2);
if(e1 < e2) max_t = t2, seq += "B";
else min_t = t1, seq += "A";
}
goal_t = (min_t + max_t) / 2;
}
hyperpoint h = get_spline(goal_t);
at.heading_angle = atan2(h[1] - at.where[1], h[0] - at.where[0]);
history.back() = at;
if(!at.tick(this)) return false;
at.t = goal_t;
history.push_back(at);
return true;
}
hyperpoint level::get_spline(ld t) {
int i = t;
if(i == isize(plan) - 1) return plan.back().at;
ld tf = t - i;
return plan[i].at * (1-tf) * (1-tf) * (1+2*tf) + plan[i+1].at * (tf*tf * (3-2*tf)) + plan[i].vel * tf * (1-tf) * (1-tf) - plan[i+1].vel * tf * tf * (1-tf);
}
hyperpoint mousept;
ld box;
ld closest_t;
char planmode = 'p';
vector<pair<char, string> > buttons = {
{'p', "pan"}, {'a', "add"}, {'m', "move"}, {'i', "insert"}, {'d', "delete"}
};
bool recompute_plan_transform = true;
void level::compute_plan_transform() {
dynamicval<eModel> pm(pmodel, mdDisk);
dynamicval<eGeometry> g(geometry, gEuclid);
dynamicval<bool> ga(vid.always3, false);
dynamicval<geometryinfo1> gi(ginf[gEuclid].g, giEuclid2);
auto& cd = current_display;
auto sId = shiftless(Id);
ld pix = 1 / (2 * cgi.hcrossf / cgi.crossf);
ld scale_x = (vid.xres - 2 * vid.fsize) / abs(real_maxx-real_minx);
ld scale_y = (vid.yres - 2 * vid.fsize) / abs(real_maxy-real_miny);
ld scale = min(scale_x, scale_y);
plan_transform = sId * atscreenpos(cd->xcenter, cd->ycenter, pix * scale) * eupush(-(real_minx+real_maxx)/2, (real_miny+real_maxy)/2) * MirrorY;
}
void level::draw_planning_screen() {
dynamicval<eGeometry> g(geometry, gEuclid);
dynamicval<eModel> pm(pmodel, mdDisk);
dynamicval<bool> ga(vid.always3, false);
dynamicval<geometryinfo1> gi(ginf[gEuclid].g, giEuclid2);
initquickqueue();
if(recompute_plan_transform) {
compute_plan_transform();
recompute_plan_transform = false;
}
auto& T = plan_transform;
auto scr_to_map = [&] (hyperpoint h) {
transmatrix mousef = inverse(unshift(T)) * atscreenpos(h[0], h[1], 1);
h = mousef * C0;
h /= h[2];
return h;
};
mousept = scr_to_map(hpxy(mousex, mousey));
box = scr_to_map(hpxy(mousex + 5, mousey))[0] - mousept[0];
/* draw the map */
auto& p = queuepolyat(T, shPlanFloor, 0xFFFFFFFF, PPR::FLOOR);
p.tinf = &uniltinf;
uniltinf.texture_id = unil_texture->textureid;
auto draw_sq = [&] (hyperpoint h, color_t col, PPR prio) {
curvepoint(hpxy(h[0]+box, h[1]+box));
curvepoint(hpxy(h[0]+box, h[1]-box));
curvepoint(hpxy(h[0]-box, h[1]-box));
curvepoint(hpxy(h[0]-box, h[1]+box));
curvepoint(hpxy(h[0]+box, h[1]+box));
queuecurve(T, 0xFF, col, prio);
};
auto draw_line = [&] (hyperpoint h1, hyperpoint h2, color_t col, PPR prio) {
curvepoint(hpxy(h1[0], h1[1]));
curvepoint(hpxy(h2[0], h2[1]));
queuecurve(T, col, 0, prio);
};
/* draw the plan */
for(auto& pp: plan) {
draw_sq(pp.at - pp.vel, 0xFF8080FF, PPR::ITEM);
draw_sq(pp.at + pp.vel, 0x80FF80FF, PPR::ITEM);
draw_sq(pp.at, 0xFFFF00FF, PPR::ITEM);
draw_line(pp.at - pp.vel, pp.at + pp.vel, 0x80, PPR::BFLOOR);
}
bool after = false;
if(history.empty()) history.push_back(start);
closest_t = history.back().t;
ld closest_dist = box * 2;
vid.linewidth *= 3;
int ps = isize(plan);
for(int t=0; t<=100*(ps-1); t++) {
ld tt = t / 100.;
if(tt > history.back().t && !after) {
queuecurve(T, 0xFFFFFFC0, 0, PPR::LIZEYE);
after = true;
}
hyperpoint h = get_spline(tt);
curvepoint(hpxy(h[0], h[1]));
ld dist = sqhypot_d(2, h - mousept);
if(dist < closest_dist) closest_dist = dist, closest_t = tt;
}
queuecurve(T, after ? 0xFF8080C0 : 0xFFFFFFC0, 0, PPR::LIZEYE);
vid.linewidth /= 3;
if(!history.empty()) {
int mint = 0, maxt = isize(history)-1;
while(mint < maxt) {
int t = (mint + maxt + 1) / 2;
if(history[t].t > closest_t) maxt = t-1;
else mint = t;
}
current = history[mint];
timer = mint * 1. / tps;
}
draw_sq(get_spline(closest_t), 0x8080FFFF, PPR::ITEM);
draw_sq(current.where, 0xFF8000FF, PPR::ITEM);
draw_sq(mousept, 0x8080FFFF, PPR::ITEM);
quickqueue();
glflush();
getcstat = '-';
}
hyperpoint mousept_drag;
int move_id = -1, move_dir = 0;
bool level::handle_planning(int sym, int uni) {
if(sym == PSEUDOKEY_WHEELUP || sym == SDLK_PAGEUP) {
dynamicval<eGeometry> g(geometry, gEuclid);
plan_transform.T = atscreenpos(mousex, mousey, 1.2) * inverse(atscreenpos(mousex, mousey, 1)) * plan_transform.T;
return true;
}
if(sym == PSEUDOKEY_WHEELDOWN || sym == SDLK_PAGEDOWN) {
dynamicval<eGeometry> g(geometry, gEuclid);
plan_transform.T = atscreenpos(mousex, mousey, 1) * inverse(atscreenpos(mousex, mousey, 1.2)) * plan_transform.T;
return true;
}
for(auto& b: buttons) if(uni == b.first) { planmode = uni; return true; }
auto clean_history_to = [&] (int i) {
while(history.size() > 1 && history.back().t > i) history.pop_back();
};
switch(planmode) {
case 'p':
if(uni == '-' && !holdmouse) {
mousept_drag = mousept;
holdmouse = true;
return true;
}
else if(uni == '-' && holdmouse) {
dynamicval<eGeometry> g(geometry, gEuclid);
plan_transform.T = plan_transform.T * eupush(mousept-mousept_drag);
return true;
}
return false;
case 'a':
if(uni == '-' && !holdmouse) {
plan.emplace_back(mousept, hpxy(0, 0));
holdmouse = true;
return true;
}
else if(uni == '-' && holdmouse) {
plan.back().vel = mousept - plan.back().at;
return true;
}
return false;
case 'm': case 'd': {
if(!holdmouse) {
ld len = box * 2;
move_id = -1;
auto check = [&] (hyperpoint h, int id, int dir) {
ld d = sqhypot_d(2, h - mousept);
if(d < len) { len = d; move_id = id; move_dir = dir; }
};
int next_id = 0;
for(auto p: plan) {
check(p.at, next_id, 0);
check(p.at + p.vel, next_id, 1);
check(p.at - p.vel, next_id, -1);
next_id++;
}
}
if(uni == '-' && planmode == 'd' && move_id > 0) {
plan.erase(plan.begin() + move_id);
clean_history_to(move_id - 1);
return true;
}
else if(uni == '-' && planmode == 'm' && (move_id + move_dir * move_dir > 0) && !holdmouse) {
holdmouse = true;
println(hlog, "moving ", tie(move_id, move_dir));
return true;
}
else if(uni == '-' && planmode == 'm' && holdmouse) {
println(hlog, "moving further ", tie(move_id, move_dir));
if(move_dir == 0) plan[move_id].at = mousept;
else plan[move_id].vel = move_dir * (mousept - plan[move_id].at);
println(hlog, "set to ", tie(plan[move_id].at, plan[move_id].vel));
clean_history_to(move_id - 1);
return true;
}
return false;
}
case 'i': {
if(uni == '-') {
planpoint pt(C0, C0);
pt.at = get_spline(closest_t);
pt.vel = (get_spline(closest_t + 1e-3) - pt.at) / 1e-3;
plan.insert(plan.begin() + int(ceil(closest_t)), pt);
clean_history_to(int(closest_t));
return true;
}
return false;
}
default:
return false;
}
}
}

View File

@ -0,0 +1,148 @@
namespace hr {
namespace bricks {
void draw_ro();
extern void build(bool in_pair);
struct brick {
euc::coord co;
color_t col;
int walls;
hyperpoint location;
hpcshape shRotWall[6];
};
extern vector<brick> bricks;
}
}
namespace nilrider {
hpcshape shMini[6];
void create_minitriangle() {
using namespace bricks;
hyperpoint ctr = Hypc;
for(auto& b: bricks::bricks) ctr += b.location;
ctr /= ctr[3];
transmatrix B = gpushxto0(ctr);
ld radius = 0;
ld sca = .18;
for(int f=0; f<6; f++) {
cgi.bshape(shMini[f], PPR::WALL);
shMini[f].flags |= POLY_TRIANGLES;
for(auto& b: bricks::bricks) {
transmatrix V = eupush(b.location);
int which = b.walls;
if(!((1<<f) & which)) continue;
auto& sh = b.shRotWall[f];
for(int i=sh.s; i<sh.e; i++) {
hyperpoint p = B * V * cgi.hpc[i];
p[0] *= sca;
p[1] *= sca;
p[2] *= sca*sca;
cgi.hpcpush(p);
radius = max(radius, sqhypot_d(2, p));
}
}
cgi.finishshape();
}
cgi.extra_vertices();
println(hlog, "radius = ", radius);
}
/** the texture data used for the castle walls */
texture::texture_data *castle_texture;
void create_castle() {
if(!castle_texture) {
castle_texture = new texture::texture_data;
auto& tex = *castle_texture;
tex.twidth = 16;
tex.theight = 16;
tex.stretched = false;
tex.strx = tex.tx;
tex.stry = tex.ty;
tex.base_x = 0;
tex.base_y = 0;
tex.whitetexture();
for(int y=0; y<16; y++)
for(int x=0; x<16; x++)
tex.get_texture_pixel(x, y) = bcols[submaps['b'][y][x]];
tex.loadTextureGL();
}
bricks::build(false);
create_minitriangle();
}
hpcshape shBall;
bool open_grid(int x, int y) {
return among(y&3, 1, 2) || among(x&3, 0, 3);
}
bool closed_grid(int x, int y) {
return among(y&3, 0, 3) || among(x&3, 0, 3);
}
template<class T1, class T2> void add_statue(const T1& grid, const T2& f) {
auto pt = [&] (int x, int y) {
ld x1 = x * M_PI / 16.;
ld y1 = y * M_PI / 32.;
cgi.hpcpush(f(x1,y1));
};
for(int y=-16; y<16; y++)
for(int x=-16; x<16; x++) if(grid(x,y)) {
pt(x, y);
pt(x, y+1);
pt(x+1, y);
pt(x+1, y);
pt(x, y+1);
pt(x+1, y+1);
}
}
hpcshape shGeostatue;
void init_statues() {
create_minitriangle();
create_castle();
cgi.bshape(shBall, PPR::WALL);
shBall.flags |= POLY_TRIANGLES;
add_statue(open_grid, [] (ld lon, ld lat) { return direct_exp(point3(cos(lat)*sin(lon)*.5, cos(lat)*cos(lon)*.5, sin(lat)*.5)); });
cgi.finishshape();
cgi.extra_vertices();
cgi.add_texture(shBall);
cgi.bshape(shGeostatue, PPR::WALL);
shGeostatue.flags |= POLY_TRIANGLES;
for(int i=0; i<8; i++) {
hyperpoint z = point31(0, 1e-6, 8);
hyperpoint ih = inverse_exp(shiftless(z));
ih = spin(i * 45 * degree) * ih;
add_statue(closed_grid, [&] (ld lon, ld lat) {
lat = lat * .75;
hyperpoint h = direct_exp(ih * (.5 + lat / M_PI));
return rgpushxto0(h) * sym_to_heis(point31(sin(lon)*.1, cos(lon)*.1, 0));
});
}
cgi.finishshape();
cgi.extra_vertices();
cgi.add_texture(shGeostatue);
for(ld z: vector<ld> {M_PI/2+1e-2, M_PI+1e-2, M_PI*2+1e-2, 7, 10})
for(hyperpoint h: {point31(0, 0, z), point31(1e-3, 0, z), point31(1e-6, 0, z), point31(0, 1e-6, z)}) {
hyperpoint i = inverse_exp(shiftless(h));
println(hlog, i, " @ ", hypot_d(3, i));
}
}
}

View File

@ -0,0 +1,244 @@
namespace nilrider {
ld timestamp::energy_in_squares() { return vel * vel / (2 * gravity); }
/** convert rotationally symmetric to Heisenberg model */
EX hyperpoint sym_to_heis(hyperpoint H) {
if(nil) {
H[2] += H[0] * H[1] / 2;
}
return H;
}
void timestamp::draw_unilcycle(const shiftmatrix& V) {
const int points = 60;
const int spoke_each = 5;
hyperpoint whpoint[points+1];
transmatrix Ta = cspin(0, 1, -heading_angle);
transmatrix Tb = cspin(0, 2, -slope);
hyperpoint base = Ta * Tb * point31(0, 0, whrad);
for(int a=0; a<points; a++) {
ld beta = 360 * degree * a / points + circpos;
whpoint[a] = base + Ta * point3(whrad*sin(beta),0,whrad*cos(beta));
}
whpoint[points] = whpoint[0];
hyperpoint hub[2];
const ld hublen = whrad / 2;
for(int a=0; a<2; a++) {
hub[a] = base + Ta * point3(0, hublen*(a?1:-1), 0);
}
for(int a=0; a<points; a+=spoke_each) for(int b=0; b<2; b++) {
curvepoint(hub[b]);
for(int b=0; b<=spoke_each; b++)
curvepoint(whpoint[a+b]);
curvepoint(hub[b]);
if(a&1)
queuecurve(V * rgpushxto0(where), 0xFFFFFFFF, 0xFFFF40FF, PPR::WALL);
else
queuecurve(V * rgpushxto0(where), 0xFFFFFFFF, 0xFF4040FF, PPR::WALL);
}
if(1) {
curvepoint(base + Ta * point3(hublen, 0, whrad+hublen));
curvepoint(base + Ta * point3(0, -hublen, whrad+hublen));
curvepoint(base + Ta * point3(0, +hublen, whrad+hublen));
curvepoint(base + Ta * point3(hublen, 0, whrad+hublen));
queuecurve(V * rgpushxto0(where), 0xFF, 0x303030FF, PPR::WALL);
for(auto& y: {hublen, -hublen}) {
curvepoint(base + Ta * point3(hublen * .1, -y, 0));
curvepoint(base + Ta * point3(hublen * -.1, -y, 0));
curvepoint(base + Ta * point3(hublen * -.1, 0, whrad + hublen / 2));
curvepoint(base + Ta * point3(hublen * .1, 0, whrad + hublen / 2));
curvepoint(base + Ta * point3(hublen * .1, -y, 0));
queuecurve(V * rgpushxto0(where), 0xFF, 0x303030FF, PPR::WALL);
curvepoint(base + Ta * point3(hublen * -.1, 0, whrad + hublen / 2));
curvepoint(base + Ta * point3(hublen * .1, 0, whrad + hublen / 2));
curvepoint(base + Ta * point3(hublen * .1, 0, whrad + hublen));
curvepoint(base + Ta * point3(hublen * -.1, 0, whrad + hublen));
curvepoint(base + Ta * point3(hublen * -.1, 0, whrad + hublen / 2));
queuecurve(V * rgpushxto0(where), 0xFF, 0x303030FF, PPR::WALL);
}
}
}
bool tick_debug = false;
bool timestamp::tick(level *lev) {
const ld eps = slope_eps;
hyperpoint wnext = where;
wnext[0] += cos(heading_angle) * eps;
wnext[1] += sin(heading_angle) * eps;
wnext[2] = lev->surface(wnext);
wnext = gpushxto0(where) * wnext;
slope = atan(wnext[2] / eps);
auto ovel = vel;
vel -= sin(slope) * gravity / tps;
if(vel < 0) {
vel = 0;
return false;
}
auto mvel = (vel + ovel) / 2;
where[0] += cos(heading_angle) * mvel * cos(slope) / tps;
where[1] += sin(heading_angle) * mvel * cos(slope) / tps;
where[2] = lev->surface(where);
circpos += mvel / whrad / tps;
return true;
}
void timestamp::centerview(level *lev) {
// static bool once = false; if(once) return; once = true;
auto w = where;
w[2] += 0.2 * lev->scale;
hyperpoint front = rgpushxto0(w) * sym_to_heis(hyperpoint(1e-3 * cos(heading_angle), 1e-3*sin(heading_angle), 0, 1));
hyperpoint up = w; up[2] += 1e-3;
set_view(w, front, up);
transmatrix T = View;
ld gfx_slope = binsearch(-90*degree, min(slope, min_gfx_slope), [&] (ld slope) {
View = T;
rotate_view(cspin(1, 2, slope));
for(int i=0; i<8; i++) {
shift_view(ztangent(whdist * lev->scale / 8.));
hyperpoint p = inverse(View) * C0;
ld room = p[2] - lev->surface(p);
if(room < .1 * lev->scale) return true;
}
return false;
});
View = T;
rotate_view(cspin(1, 2, gfx_slope));
shift_view(ztangent(whdist * lev->scale));
centerover = cwt.at;
playermoved = false;
}
void timestamp::draw_instruments(ld t) {
dynamicval<eGeometry> g(geometry, gEuclid);
dynamicval<eModel> pm(pmodel, mdDisk);
dynamicval<bool> ga(vid.always3, false);
dynamicval<geometryinfo1> gi(ginf[gEuclid].g, giEuclid2);
initquickqueue();
ld rad = 40;
ld cx = rad * 2;
ld cy = rad * 2;
auto sId = shiftless(Id);
ld pix = 1 / (2 * cgi.hcrossf / cgi.crossf);
// clinometer
cx += rad * 1.2;
for(int i=-90; i<=90; i++)
curvepoint(atscreenpos(cx+cos(i * degree)*rad, cy-sin(i*degree)*rad, 1) * C0);
curvepoint(atscreenpos(cx, cy+rad, 1) * C0);
queuecurve(sId, 0x000000FF, 0xFFFFFF80, PPR::ZERO);
curvepoint(hpxy(0, 0));
curvepoint(hpxy(rad, 0));
/* curvepoint(hpxy(rad/4, 0));
curvepoint(hpxy(0, rad));
curvepoint(hpxy(-rad/4, 0));
curvepoint(hpxy(rad/4, 0)); */
queuecurve(sId * atscreenpos(cx, cy, pix) * spin(min_gfx_slope), 0x40, 0x40, PPR::ZERO);
curvepoint(hpxy(rad/4, 0));
curvepoint(hpxy(0, rad));
curvepoint(hpxy(-rad/4, 0));
curvepoint(hpxy(rad/4, 0));
queuecurve(sId * atscreenpos(cx, cy, pix) * spin(90 * degree + slope), 0xFF, 0x40C040FF, PPR::ZERO);
// compass
cx -= rad * 1.2;
for(int i=0; i<360; i++)
curvepoint(atscreenpos(cx-cos(i * degree)*rad, cy-sin(i*degree)*rad, 1) * C0);
queuecurve(sId, 0x000000FF, 0xFFFFFF80, PPR::ZERO);
for(int d: {1}) {
// d == +1: direction arrow
// d == -1: compass
curvepoint(hpxy(rad/4, 0));
curvepoint(hpxy(0, rad));
curvepoint(hpxy(-rad/4, 0));
queuecurve(sId * atscreenpos(cx, cy, pix) * spin(d * (90*degree + heading_angle)), 0xFF, d > 0 ? 0x0000FFFF : 0xFF0000FF, PPR::ZERO);
curvepoint(hpxy(rad/4, 0));
curvepoint(hpxy(0, -rad));
curvepoint(hpxy(-rad/4, 0));
curvepoint(hpxy(rad/4, 0));
queuecurve(sId * atscreenpos(cx, cy, pix) * spin(d * (90*degree + heading_angle)), 0xFF, 0xFFFFFFFF, PPR::ZERO);
}
// speed meter
cx += rad * 3.4;
for(int i=0; i<360; i++)
curvepoint(atscreenpos(cx-cos(i * degree)*rad, cy-sin(i*degree)*rad, 1) * C0);
queuecurve(sId, 0x000000FF, 0xFFFFFF80, PPR::ZERO);
auto e_to_angle = [] (ld energy) {
return 135*degree - 3 * atan(energy/10);
};
vector<ld> short_lines = {2, 3, 4, 6, 7, 8, 9, 30, 40, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000};
for(auto h: short_lines) {
auto a = e_to_angle(h);
curvepoint(hpxy(-sin(a)*rad*.95, -cos(a)*rad*.95));
curvepoint(hpxy(-sin(a)*rad*.85, -cos(a)*rad*.85));
queuecurve(sId * atscreenpos(cx, cy, pix), 0xFF, 0, PPR::ZERO);
}
vector<ld> long_lines = {0, 1, 5, 10, 20, 50};
for(auto h: long_lines) {
auto a = e_to_angle(h);
curvepoint(hpxy(-sin(a)*rad*.95, -cos(a)*rad*.95));
curvepoint(hpxy(-sin(a)*rad*.75, -cos(a)*rad*.75));
queuecurve(sId * atscreenpos(cx, cy, pix), 0xFF, 0, PPR::ZERO);
displaystr(cx -sin(a)*rad*.65, cy -cos(a)*rad*.65, 0, 8, its(h), 0, 8);
}
curvepoint(hpxy(rad/4, 0));
curvepoint(hpxy(0, -rad));
curvepoint(hpxy(-rad/4, 0));
curvepoint(hpxy(rad/4, 0));
queuecurve(sId * atscreenpos(cx, cy, pix) * spin(e_to_angle(energy_in_squares())), 0xFF, 0xFF8080FF, PPR::ZERO);
quickqueue();
glflush();
string s = format("%d:%02d.%02d", int(t / 60), int(t) % 60, int(frac(t) * 100));
displaystr(vid.xres - vid.fsize, vid.fsize*2, 0, vid.fsize * 2, s, 0, 16);
}
}