namespace nilrider { const int steps_per_block = 16; const int texture_density = 64; void level::init_textures() { create_castle(); int tY = isize(map_tiles); int tX = isize(map_tiles[0]); transmatrix T = gpushxto0(new_levellines_for); for(int which: {0, 1, 2}) { bool stepped = which == 1; bool levels = which == 2; auto& target = stepped ? unil_texture_stepped : levels ? unil_texture_levels : unil_texture; if(target && !(levels && levellines_for != new_levellines_for)) continue; bool regen = !target; if(regen) target = new texture::texture_data; auto& tex = *target; if(regen) { tex.twidth = tex.tx = tX * texture_density; tex.theight = tex.ty = tY * texture_density; tex.stretched = false; tex.strx = tex.tx; tex.stry = tex.ty; tex.base_x = 0; tex.base_y = 0; tex.whitetexture(); } auto getpix = [&] (int x, int y) { int px = x / pixel_per_block; int py = y / pixel_per_block; if(px < 0 || py < 0 || px >= tX || py >= tY) return '!'; char bmch = map_tiles[py][px]; if(bmch == '!') return '!'; return submaps[bmch][y % pixel_per_block][x % pixel_per_block]; }; int subpixels = texture_density / pixel_per_block; int stepdiv = texture_density / steps_per_block; for(int y=0; y 0) col = gradient(col, 0xFFFF0000, 0, z - floor(z), 4); if(z < 0) col = gradient(col, 0xFF0000FF, 0, -z - floor(-z), 4); } } else { int mx = x % stepdiv; int my = y % stepdiv; int mx0, mx1, my0, my1; if(mx < stepdiv/2) { mx0 = mx1 = 2*mx; } else {mx0 = stepdiv-2; mx1=stepdiv; } if(my < stepdiv/2) { my0 = my1 = 2*my; } else {my0 = stepdiv-2; my1=stepdiv; } int ax = (x/stepdiv) * stepdiv; int ay = (y/stepdiv) * stepdiv; ld maxh = -HUGE_VAL; char c = '!'; for(int i=0; i<4; i++) { hyperpoint h = mappt(ax + ((mx0 != mx1) ? (i&1?3:1)*stepdiv/2 : mx0), ay + ((my0 != my1) ? (i&2?3:1)*stepdiv/2 : my0), texture_density); if(h[2] > maxh) { c = getpix(((i&1?mx1:mx0) + ax) / subpixels, ((i&2?my1:my0)+ ay) / subpixels); if(c != '!') maxh = h[2]; } } col = bcols[c]; if(mx0 != mx1) col = gradient(col, 0xFF000000, 0, .1, 1); if(my0 != my1) col = gradient(col, 0xFF000000, 0, .2, 1); } tex.get_texture_pixel(x, y) = col; tex.get_texture_pixel(x, y) |= 0xFF000000; if(!levels) tex.get_texture_pixel(x, y) ^= hrand(0x1000000) & 0xF0F0F; } tex.loadTextureGL(); } levellines_for = new_levellines_for; } void level::init_shapes() { check_cgi(); string s = "nillevel-" + name; if(cgi.ext.count(s)) return; cgi.ext[s] = nullptr; int tY = isize(map_tiles); int tX = isize(map_tiles[0]); for(int s=0; s<3; s++) { if(euclid && s != 1) continue; if(nil && s == 1) continue; cgi.bshape(s == 0 ? shFloor : s == 1 ? shPlanFloor : shStepFloor, PPR::WALL); shFloor.flags |= POLY_TRIANGLES; shPlanFloor.flags |= POLY_TRIANGLES; shStepFloor.flags |= POLY_TRIANGLES; int prec = 16; if(s == 2) prec *= 4; int cdiv = prec / steps_per_block; prec >>= reduce_quality; bool need_uniltinf = uniltinf.tvertices.empty(); auto pt = [&] (int x, int y, int qx, int qy) { if(need_uniltinf) uniltinf.tvertices.push_back(glhr::makevertex(x * 1. / tX / prec, y * 1. / tY / prec, 0)); if(s == 2) { ld ax = x, ay = y; if(qx) { if(x % cdiv == cdiv/2+1) qx = 0; } else { ax -= (x % cdiv); ax += .5; ax += (x % cdiv) * (cdiv/2-1.) / (cdiv/2); } if(qy) { if(y % cdiv == cdiv/2+1) qy = 0; } else { ay -= (y % cdiv); ay += .5; ay += (y % cdiv) * (cdiv/2-1.) / (cdiv/2); } uniltinf_stepped.tvertices.push_back(glhr::makevertex((ax+qx) / tX / prec, (ay+qy) / tY / prec, 0)); } hyperpoint h = mappt(x, y, prec); if(s == 2) { if(x % cdiv == cdiv/2+1) x += cdiv/2 - 1; if(y % cdiv == cdiv/2+1) y += cdiv/2 - 1; int gx = x/cdiv*cdiv + (x%cdiv) * 2; int gy = y/cdiv*cdiv + (y%cdiv) * 2; hyperpoint gh = mappt(gx, gy, prec); int ax = x/cdiv*cdiv + cdiv/2; int ay = y/cdiv*cdiv + cdiv/2; hyperpoint ah = mappt(ax, ay, prec); gh[0] = gh[0] - ah[0]; gh[1] = gh[1] - ah[1]; gh[2] = 0; gh = sym_to_heis(gh); h = rgpushxto0(ah) * gh; ld delta = 0; // make sure steps are below the actual level for(int z=0; z<4; z++) { int zx = x/cdiv*cdiv + ((z&1)?cdiv:0); int zy = y/cdiv*cdiv + ((z&2)?cdiv:0); hyperpoint zh = mappt(zx, zy, prec); hyperpoint uh; uh[0] = zh[0] - ah[0]; uh[1] = zh[1] - ah[1]; uh[2] = 0; uh[3] = 1; uh = sym_to_heis(uh); uh = rgpushxto0(ah) * uh; delta = max(delta, uh[2] - zh[2]); } h[2] -= delta; } 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; cgi.hpcpush(h); }; for(int y=0; y (cdiv/2)) qx+=2; if(y % cdiv == (cdiv/2)) qy++; if(y % cdiv > (cdiv/2)) qy+=2; if(qx + qy > 1) continue; } pt(x, y, qx, qy); pt(x, y+1, qx, qy); pt(x+1, y, qx, qy); pt(x+1, y+1, qx, qy); pt(x+1, y, qx, qy); pt(x, y+1, qx, qy); } 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; yscale); levellines_for = new_levellines_for = Hypc; if(1) { int tY = isize(map_tiles); int tX = isize(map_tiles[0]); for(int y=0; y lop1(loaded_or_planned, true); dynamicval lop2(planning_mode, false); if(c.tick(this) == b) break; start.heading_angle -= degree; } if(flags & nrlOrder) { sort(triangles.begin(), triangles.end(), [this] (triangledata a, triangledata b) { return atan2(spin(120._deg)*(a.where - start.where)) < atan2(spin(120._deg)*(b.where - start.where)); }); for(auto t: triangles) println(hlog, t.where); } init_plan(); } xy_float level::get_xy_f(hyperpoint h) { if(flags & nrlPolar) { tie(h[0], h[1]) = make_pair(atan2(h[0], h[1]), hypot(h[0], h[1])); ld bar = (minx + maxx) / 2; cyclefix(h[0], bar); } int tY = isize(map_tiles); int tX = isize(map_tiles[0]); ld rtx = ilerp(minx, maxx, h[0]) * tX; ld rty = ilerp(miny, maxy, h[1]) * tY; return {rtx, rty}; } char level::mapchar(xy_int p) { auto x = p.first; auto y = p.second; int tY = isize(map_tiles); int tX = isize(map_tiles[0]); if(x < 0 || y < 0 || x >= tX || y >= tY) return '!'; return map_tiles[y][x]; } /* 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; }; void level::init_plan() { plan.emplace_back(start.where, hpxy(cos(start.heading_angle + 90._deg) * 2, sin(start.heading_angle + 90._deg) * 2)); current = start; } ld level::safe_alt(hyperpoint h, ld mul, ld mulx) { ld maxv = 0; for(int x: {-1, 0, 1}) for(int y: {-1, 0, 1}) { hyperpoint c = sym_to_heis(point31(x*.5*scale*mulx, y*.5*scale*mulx, 0)); hyperpoint j = rgpushxto0(h) * c; maxv = max(maxv, mul * (surface(j) - j[2])); } return maxv; } bool stepped_display; int nilrider_tempo = 562; int nilrider_shift = 2633; void level::draw_level(const shiftmatrix& V) { int id = 0; init_statues(); curlev->init_shapes(); curlev->init_textures(); for(auto& t: triangles) { bool gotit = current.collected_triangles & Flag(id); id++; if(!gotit) { for(int i=0; i<6; i++) { ld tim = current.timer * 1000; tim -= nilrider_shift; tim /= nilrider_tempo; tim = abs(0.2 * sin(tim * M_PI)); auto &poly = queuepoly(V * rgpushxto0(t.where) * cpush(2, tim), shMini[i], t.colors[i]); 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); if(!stepped_display) { auto& poly = queuepoly(V, shFloor, 0xFFFFFFFF); // 0xFFFFFFFF); poly.tinf = &uniltinf; uniltinf.texture_id = unil_texture->textureid; } else { auto& poly = queuepoly(V, shStepFloor, 0xFFFFFFFF); // 0xFFFFFFFF); poly.tinf = &uniltinf_stepped; uniltinf_stepped.texture_id = unil_texture_stepped->textureid; } } void cleanup_texture(texture::texture_data*& d) { if(d) delete d; d = nullptr; } void cleanup_textures() { for(auto l: all_levels) { cleanup_texture(l->unil_texture); cleanup_texture(l->unil_texture_stepped); } println(hlog, "CLEANUP texture"); cleanup_texture(castle_texture); } }