From 1ba1b141f32e04f2ea62e5308ae542ef5b29486d Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Thu, 1 Nov 2018 18:59:25 +0100 Subject: [PATCH] smart display range --- config.cpp | 89 ++++++++++++++++++++++++++++++++++++--------------- conformal.cpp | 2 ++ graph.cpp | 10 ++++-- hyper.h | 26 ++++++++++++++- hypgraph.cpp | 77 +++++++++++++++++++++++++++++++++++--------- menus.cpp | 45 ++++++++++++++------------ system.cpp | 6 +++- tour.cpp | 10 ++++-- 8 files changed, 196 insertions(+), 69 deletions(-) diff --git a/config.cpp b/config.cpp index b4c3d26f..38ecac64 100644 --- a/config.cpp +++ b/config.cpp @@ -290,6 +290,10 @@ void initConfig() { addsaver(texture::texture_aura, "texture-aura", false); #endif + addsaver(vid.use_smart_range, "smart-range", 0); + addsaver(vid.smart_range_detail, "smart-range-detail", 8); + addsaver(vid.cells_drawn_limit, "limit on cells drawn", 10000); + #if CAP_SHMUP shmup::initConfig(); #endif @@ -555,6 +559,59 @@ void loadNewConfig(FILE *f) { } allconfigs.clear(); } + +void edit_sightrange() { + if(vid.use_smart_range == 0) { + dialog::editNumber(sightrange_bonus, -5, allowIncreasedSight() ? 3 : 0, 1, 0, XLAT("sight range"), + XLAT("Roughly 42% cells are on the edge of your sight range. Reducing " + "the sight range makes HyperRogue work faster, but also makes " + "the game effectively harder.")); + dialog::reaction = [] () { + doOvergenerate(); + }; + } + else { + dialog::editNumber(vid.smart_range_detail, 1, 50, 1, 8, XLAT("minimum visible cell in pixels"), ""); + } + dialog::extra_options = [] () { + dialog::addBoolItem(XLAT("draw range based on distance"), vid.use_smart_range == 0, 'd'); + dialog::add_action([] () { vid.use_smart_range = 0; popScreen(); edit_sightrange(); }); + if(allowIncreasedSight()) { + dialog::addBoolItem(XLAT("draw based on size in the projection (no generation)"), vid.use_smart_range == 1, 'n'); + dialog::add_action([] () { vid.use_smart_range = 1; popScreen(); edit_sightrange(); }); + } + if(allowChangeRange()) { + dialog::addBoolItem(XLAT("draw based on size in the projection (generation)"), vid.use_smart_range == 2, 'g'); + dialog::add_action([] () { vid.use_smart_range = 2; popScreen(); edit_sightrange(); }); + } + if(vid.use_smart_range == 0 && allowChangeRange()) { + dialog::addSelItem(XLAT("generation range bonus"), its(genrange_bonus), 'o'); + dialog::add_action([] () { genrange_bonus = sightrange_bonus; doOvergenerate(); }); + dialog::addSelItem(XLAT("game range bonus"), its(gamerange_bonus), 'O'); + dialog::add_action([] () { gamerange_bonus = sightrange_bonus; doOvergenerate(); }); + } + if(!allowChangeRange() || !allowIncreasedSight()) { + dialog::addItem(XLAT("enable the cheat mode for additional options"), 'C'); + dialog::add_action(enable_cheat); + } + dialog::addSelItem(XLAT("cells drawn"), its(cells_drawn), 'c'); + dialog::add_action([] () { + popScreen(); + dialog::editNumber(vid.cells_drawn_limit, 100, 1000000, log(10), 10000, XLAT("limit on cells drawn"), + XLAT("This limit exists to protect the engine from freezing when too many cells would be drawn according to the current options.") + ); + dialog::scaleLog(); + }); + }; + } + +void menuitem_sightrange(char c = 'r') { + if(vid.use_smart_range) + dialog::addSelItem(XLAT("minimum visible cell in pixels"), fts(vid.smart_range_detail), c); + else + dialog::addSelItem(XLAT("sight range"), its(sightrange_bonus), c); + dialog::add_action(edit_sightrange); + } void loadConfig() { @@ -663,8 +720,8 @@ void showGraphConfig() { dialog::addSelItem(XLAT("font scale"), its(fontscale), 'b'); - dialog::addSelItem(XLAT("sight range"), its(sightrange_bonus), 'r'); - + menuitem_sightrange(); + dialog::addSelItem(XLAT("compass size"), its(vid.mobilecompasssize), 'c'); dialog::addSelItem(XLAT("aura brightness"), its(vid.aurastr), 'z'); @@ -701,26 +758,6 @@ void showGraphConfig() { XLAT("movement animation speed"), XLAT("+5 = move instantly")); - if(xuni == 'r') { - dialog::editNumber(sightrange_bonus, -5, allowIncreasedSight() ? 3 : 0, 1, 0, XLAT("sight range"), - XLAT("Roughly 42% cells are on the edge of your sight range. Reducing " - "the sight range makes HyperRogue work faster, but also makes " - "the game effectively harder.")); - dialog::reaction = [] () { - doOvergenerate(); - }; - dialog::extra_options = [] () { - if(allowChangeRange()) { - dialog::addSelItem(XLAT("generation range bonus"), its(genrange_bonus), 'o'); - dialog::add_action([] () { genrange_bonus = sightrange_bonus; doOvergenerate(); }); - dialog::addSelItem(XLAT("game range bonus"), its(gamerange_bonus), 'O'); - dialog::add_action([] () { gamerange_bonus = sightrange_bonus; doOvergenerate(); }); - } - if(!allowChangeRange() || !allowIncreasedSight()) - dialog::addInfo(XLAT("note: enable the cheat mode for additional options")); - }; - } - if(xuni == 'k') { glyphsortorder = eGlyphsortorder((glyphsortorder+6+(shiftmul>0?1:-1)) % gsoMAX); } @@ -1177,10 +1214,12 @@ void show3D() { using namespace geom3; dialog::init(XLAT("3D configuration")); - dialog::addSelItem(XLAT("High detail range"), fts(highdetail), 'n'); - dialog::addSelItem(XLAT("Mid detail range"), fts(middetail), 'm'); + if(vid.use_smart_range == 0) { + dialog::addSelItem(XLAT("High detail range"), fts(highdetail), 'n'); + dialog::addSelItem(XLAT("Mid detail range"), fts(middetail), 'm'); + dialog::addBreak(50); + } - dialog::addBreak(50); dialog::addSelItem(XLAT("Camera level above the plane"), fts3(camera), 'c'); dialog::addSelItem(XLAT("Ground level below the plane"), fts3(depth), 'g'); diff --git a/conformal.cpp b/conformal.cpp index 31c050b3..9d7a95f1 100644 --- a/conformal.cpp +++ b/conformal.cpp @@ -682,6 +682,8 @@ namespace conformal { } dialog::addSelItem(XLAT("vertical stretch"), fts3(vid.stretch), 's'); + + menuitem_sightrange('R'); dialog::addBreak(100); dialog::addItem(XLAT("history mode"), 'a'); diff --git a/graph.cpp b/graph.cpp index 56351224..6402c275 100644 --- a/graph.cpp +++ b/graph.cpp @@ -3353,7 +3353,7 @@ void pushdown(cell *c, int& q, const transmatrix &V, double down, bool rezoom, b bool dodrawcell(cell *c) { // do not display out of range cells, unless on torus - if(c->pathdist == PINFD && geometry != gTorus) + if(c->pathdist == PINFD && geometry != gTorus && vid.use_smart_range == 0) return false; // do not display not fully generated cells, unless a cheater if(c->mpdist > 7 && !cheater && !autocheat) return false; @@ -3535,6 +3535,8 @@ int colorhash(color_t i) { void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { + cells_drawn++; + #if CAP_TEXTURE if(texture::saving) { texture::config.apply(c, V, 0xFFFFFFFF); @@ -3564,7 +3566,8 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { if(callhandlers(false, hooks_drawcell, c, V)) return; ld dist0 = hdist0(tC0(V)) - 1e-6; - if(dist0 < geom3::highdetail) detaillevel = 2; + if(vid.use_smart_range) detaillevel = 2; + else if(dist0 < geom3::highdetail) detaillevel = 2; else if(dist0 < geom3::middetail) detaillevel = 1; else detaillevel = 0; @@ -5245,6 +5248,7 @@ void drawthemap() { callhooks(hooks_drawmap); frameid++; + cells_drawn = 0; wavephase = (-(ticks / 100)) & 7; @@ -5316,7 +5320,7 @@ void drawthemap() { else if(archimedean) arcm::draw(); else - drawrec(viewctr, hsOrigin, cview()); + drawrec(viewctr, hsOrigin, cview(), 0); drawWormSegments(); drawBlizzards(); drawArrowTraps(); diff --git a/hyper.h b/hyper.h index 2c3b72e5..dde1b073 100644 --- a/hyper.h +++ b/hyper.h @@ -1047,6 +1047,10 @@ struct videopar { bool skipstart; // skip the start menu int quickmouse; // quick mouse on the map int timeformat; // time format used in the message log + + int use_smart_range; // 0 = distance-based, 1 = model-based, 2 = model-based and generate + ld smart_range_detail;// minimum visible cell for modes 1 and 2 + int cells_drawn_limit; }; extern videopar vid; @@ -3798,7 +3802,7 @@ void queuestr(const hyperpoint& h, int size, const string& chr, color_t col, int void queuechr(const transmatrix& V, double size, char chr, color_t col, int frame = 0); extern bool just_gmatrix; -void drawrec(const heptspin& hs, hstate s, const transmatrix& V); +void drawrec(const heptspin& hs, hstate s, const transmatrix& V, int reclev = 0); bool haveLeaderboard(int id); int get_currentscore(int id); @@ -4272,5 +4276,25 @@ struct exp_parser { } }; +#ifdef CAP_COMPLEX2 +namespace brownian { + const int level = 5; + void init(cell *c); + void build(cell *c, int d); + void explosion(cell *c, int x); + }; +#else +namespace brownian { + inline void dissolve_brownian(cell*, int) {}; + inline void build(cell *c, int d) {} + inline void init(cell *c) {} + } +#endif + +#define ONEMPTY if(d == 7 && passable(c, NULL, 0) && !safety) + +void enable_cheat(); + +extern int cells_drawn; } diff --git a/hypgraph.cpp b/hypgraph.cpp index 6e281a37..810501f5 100644 --- a/hypgraph.cpp +++ b/hypgraph.cpp @@ -626,9 +626,34 @@ transmatrix applyspin(const heptspin& hs, const transmatrix& V) { return hs.spin ? V * spin(hs.spin*2*M_PI/S7) : V; } +bool in_smart_range(const transmatrix& T) { + hyperpoint h1, h2, h3; + applymodel(tC0(T), h1); + ld x = vid.xcenter + vid.radius * h1[0]; + ld y = vid.ycenter + vid.radius * h1[1] * vid.stretch; + if(x > vid.xres * 2) return false; + if(x < -vid.xres) return false; + if(y > vid.yres * 2) return false; + if(y < -vid.yres) return false; + ld epsilon = 0.01; + applymodel(T * xpush0(epsilon), h2); + ld x1 = vid.radius * abs(h2[0] - h1[0]) / epsilon; + ld y1 = vid.radius * abs(h2[1] - h1[1]) * vid.stretch / epsilon; + applymodel(T * ypush(epsilon) * C0, h3); + ld x2 = vid.radius * abs(h3[0] - h1[0]) / epsilon; + ld y2 = vid.radius * abs(h3[1] - h1[1]) * vid.stretch / epsilon; + ld scale = sqrt(hypot(x1, y1) * hypot(x2, y2)) * scalefactor * hcrossf7; + return + scale > vid.smart_range_detail && + x - 2 * max(x1, x2) < vid.xres && + x + 2 * max(x1, x2) > 0 && + y - 2 * max(y1, y2) < vid.yres && + y + 2 * max(y1, y2) > 0; + } + // in hyperbolic quotient geometries, relying on pathdist is not sufficient bool in_qrange(const transmatrix& V) { - if(!quotient || !hyperbolic) return true; + if(!quotient || !hyperbolic || vid.use_smart_range) return true; return V[2][2] < cosh(crossf * get_sightrange_ambush()); } @@ -688,7 +713,7 @@ void drawrec(cell *c, const transmatrix& V) { } } -void drawrec(const heptspin& hs, hstate s, const transmatrix& V) { +void drawrec(const heptspin& hs, hstate s, const transmatrix& V, int reclev) { // calc_relative_matrix(cwt.c, hs.at); @@ -699,6 +724,14 @@ void drawrec(const heptspin& hs, hstate s, const transmatrix& V) { bool draw = c->pathdist < PINFD; + if(cells_drawn > vid.cells_drawn_limit || reclev >= 100 || std::isinf(V[2][2]) || std::isnan(V[2][2])) + draw = false; + else if(vid.use_smart_range) { + draw = reclev < 2 ? true : in_smart_range(V); + if(draw && vid.use_smart_range == 2) + setdist(c, 7, NULL); + } + if(GOLDBERG) { gp::drawrec(c, actualV(hs, V1)); } @@ -734,7 +767,7 @@ void drawrec(const heptspin& hs, hstate s, const transmatrix& V) { hstate s2 = transition(s, d); if(s2 == hsError) continue; heptspin hs2 = hs + d + wstep; - drawrec(hs2, s2, V * heptmove[d]); + drawrec(hs2, s2, V * heptmove[d], reclev+1); } } @@ -838,19 +871,31 @@ void drawEuclidean() { // Mat[2][0] = x*x/10; // Mat[2][1] = y*y/10; // Mat = Mat * xpush(x-30) * ypush(y-30); - - int cx, cy, shift; - getcoord0(tC0(Mat), cx, cy, shift); - if(cx >= 0 && cy >= 0 && cx < vid.xres && cy < vid.yres) { - if(dx < mindx) mindx = dx; - if(dy < mindy) mindy = dy; - if(dx > maxdx) maxdx = dx; - if(dy > maxdy) maxdy = dy; - } - if(cx >= -cellrad && cy >= -cellrad && cx < vid.xres+cellrad && cy < vid.yres+cellrad) - if(dodrawcell(cw.at)) { - drawcell(cw.at, cw.mirrored ? Mat * Mirror : Mat, cw.spin, cw.mirrored); + + if(vid.use_smart_range) { + if(in_smart_range(Mat)) { + if(dx < mindx) mindx = dx; + if(dy < mindy) mindy = dy; + if(dx > maxdx) maxdx = dx; + if(dy > maxdy) maxdy = dy; + if(dodrawcell(cw.at)) + drawcell(cw.at, cw.mirrored ? Mat * Mirror : Mat, cw.spin, cw.mirrored); } + } + else { + int cx, cy, shift; + getcoord0(tC0(Mat), cx, cy, shift); + if(cx >= 0 && cy >= 0 && cx < vid.xres && cy < vid.yres) { + if(dx < mindx) mindx = dx; + if(dy < mindy) mindy = dy; + if(dx > maxdx) maxdx = dx; + if(dy > maxdy) maxdy = dy; + } + if(cx >= -cellrad && cy >= -cellrad && cx < vid.xres+cellrad && cy < vid.yres+cellrad) + if(dodrawcell(cw.at)) { + drawcell(cw.at, cw.mirrored ? Mat * Mirror : Mat, cw.spin, cw.mirrored); + } + } } } @@ -997,6 +1042,8 @@ void panning(hyperpoint hf, hyperpoint ht) { playermoved = false; } +int cells_drawn; + void fullcenter() { if(playerfound && false) centerpc(INF); else { diff --git a/menus.cpp b/menus.cpp index 750e584d..92ecbce0 100644 --- a/menus.cpp +++ b/menus.cpp @@ -406,6 +406,28 @@ void showDisplayMode() { }; } +void enable_cheat() { + if(tactic::on && gold()) { + addMessage(XLAT("Not available in the pure tactics mode!")); + } + else if(daily::on) { + addMessage(XLAT("Not available in the daily challenge!")); + } + else if(!cheater) { + cheater++; + addMessage(XLAT("You activate your demonic powers!")); +#if ISMOBILE==0 + addMessage(XLAT("Shift+F, Shift+O, Shift+T, Shift+L, Shift+U, etc.")); +#endif + popScreen(); + } + else { + popScreen(); + firstland = princess::challenge ? laPalace : laIce; + restart_game(); + } + } + // -- game modes -- void switchHardcore() { @@ -484,27 +506,8 @@ void showChangeMode() { pushScreen(daily::showMenu); #endif - else if(uni == 'c') { - if(tactic::on && gold()) { - addMessage(XLAT("Not available in the pure tactics mode!")); - } - else if(daily::on) { - addMessage(XLAT("Not available in the daily challenge!")); - } - else if(!cheater) { - cheater++; - addMessage(XLAT("You activate your demonic powers!")); - #if ISMOBILE==0 - addMessage(XLAT("Shift+F, Shift+O, Shift+T, Shift+L, Shift+U, etc.")); - #endif - popScreen(); - } - else { - popScreen(); - firstland = princess::challenge ? laPalace : laIce; - restart_game(); - } - } + else if(uni == 'c') + enable_cheat(); else if(xuni == 'e') runGeometryExperiments(); diff --git a/system.cpp b/system.cpp index 7dd9ef29..7ce5ece8 100644 --- a/system.cpp +++ b/system.cpp @@ -293,7 +293,11 @@ void initgame() { if(quotient || sphere) for(cell *c: currentmap->allcells()) setdist(c, 8, NULL); - if(!cheater) gamerange_bonus = genrange_bonus = 0; + if(!cheater) { + gamerange_bonus = genrange_bonus = 0; + if(vid.use_smart_range == 2) vid.use_smart_range = 1; + } + if(!allowIncreasedSight()) vid.use_smart_range = 0; } bool havesave = true; diff --git a/tour.cpp b/tour.cpp index 1f17c6c0..dca2344c 100644 --- a/tour.cpp +++ b/tour.cpp @@ -588,11 +588,12 @@ slide default_slides[] = { "The next slide shows another model, called the Poincaré upper half-plane model. In this model, " "horocycles centered at one specific ideal point are drawn as straight lines.", [] (presmode mode) { + static int smart; if(mode == 1) - pmodel = mdHalfplane; + pmodel = mdHalfplane, smart = vid.use_smart_range, vid.use_smart_range = 2; if(mode == 2) conformal::rotation = cwt.at->land == laDungeon ? 0 : 2; - if(mode == 3) pmodel = mdDisk, conformal::rotation = 0; + if(mode == 3) pmodel = mdDisk, conformal::rotation = 0, vid.use_smart_range = smart; } }, {"Curvature", 29, LEGAL_ANY, @@ -739,13 +740,16 @@ slide default_slides[] = { "If you want, press '5' to see it rendered as a spiral, although it takes lots of time and " "memory.", [] (presmode mode) { - if(mode == 1) pmodel = mdBand, conformal::create(), conformal::rotation = 0; + static int smart; + if(mode == 1) pmodel = mdBand, conformal::create(), conformal::rotation = 0, + smart = vid.use_smart_range, vid.use_smart_range = 2; if(mode == 3) { conformal::clear(), pmodel = mdDisk; resetview(); drawthemap(); centerpc(INF); conformal::includeHistory = false; + vid.use_smart_range = smart; } #if CAP_SDL slidecommand = "render spiral";