1 // Hyperbolic Rogue -- the history mode
2 // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
3 
4 /** \file history.cpp
5  *  \brief Implementation of the history mode, including the long band and long spiral.
6  */
7 
8 #include "hyper.h"
9 namespace hr {
10 
11 #if CAP_SDL
12 namespace spiral {
13 
14   typedef long double ld;
15   typedef complex<long double> cxld;
16 
17   int shiftx, shifty, velx, vely;
18 
19   vector<pair<short, short> > quickmap;
20 
21   int CX, CY, SX, SY, Yshift;
22 
23   vector<SDL_Surface*> band;
24   SDL_Surface *out;
25 
26   bool displayhelp = true;
27 
bandpixel(int x,int y)28   color_t& bandpixel(int x, int y) {
29     int i = 0;
30     while(i < isize(band) && x >= band[i]->w)
31       x -= band[i]->w, i++;
32     return qpixel(band[i], x, y);
33     }
34 
precompute()35   void precompute() {
36 
37     CX = 0;
38     for(int i=0; i<isize(band); i++) CX += band[i]->w;
39     if(CX == 0) { printf("ERROR: no CX\n"); return; }
40     CY = band[0]->h;
41     SX = out->w;
42     SY = out->h;
43 
44     ld k = -2*M_PI*M_PI / log(2.6180339);
45 
46 //   cxld mnoznik = cxld(0, M_PI) / cxld(k, M_PI);
47 
48     cxld factor = cxld(0, -CY/2/M_PI/M_PI) * cxld(k, M_PI);
49 
50     Yshift = CY * k / M_PI;
51 
52     quickmap.clear();
53 
54     double xc = ((SX | 1) - 2) / 2.;
55     double yc = ((SY | 1) - 2) / 2.;
56 
57     for(int y=0; y<SY; y++)
58     for(int x=0; x<SX; x++) {
59       cxld z(x-xc, y-yc);
60       cxld z1 = log(z);
61 
62       z1 = z1 * factor;
63 
64       quickmap.push_back(make_pair(int(real(z1)) % CX, int(imag(z1))));
65       }
66     }
67 
draw()68   void draw() {
69     int c = 0;
70     for(int y=0; y<SY; y++) for(int x=0; x<SX; x++) {
71       pair<short,short> p = quickmap[c++];
72       int cx = p.first + shiftx;
73       int cy = p.second + + shifty;
74       int d = cy / CY;
75       cy -= d * CY; cx -= d * Yshift;
76       if(cy<0) cy += CY, cx += Yshift;
77       cx %= CX; if(cx<0) cx += CX;
78       qpixel(out, x, y) = bandpixel(cx, cy);
79       }
80     }
81 
loop(vector<SDL_Surface * > _band)82   void loop(vector<SDL_Surface*> _band) {
83 
84     band = _band;
85     out = s;
86     precompute();
87     if(CX == 0) return;
88     shiftx = shifty = 0;
89     velx=1; vely=1;
90     bool dosave = false;
91 
92     bool saveGL = vid.wantGL;
93     vid.wantGL = false;
94     apply_screen_settings();
95     out = s;
96 
97     while(true) {
98 
99       time_t timer;
100       timer = time(NULL);
101       char buf[128];
102       strftime(buf, 128, "spiral-%y%m%d-%H%M%S" IMAGEEXT, localtime(&timer));
103 
104       SDL_LockSurface(s);
105       draw();
106       if(dosave) { dosave = false; IMAGESAVE(s, buf); }
107       SDL_UnlockSurface(s);
108       if(displayhelp) {
109         displaystr(SX/2, vid.fsize*2, 0, vid.fsize, "arrows = navigate, ESC = return, h = hide help", forecolor, 8);
110         displaystr(SX/2, SY - vid.fsize*2, 0, vid.fsize, XLAT("s = save to " IMAGEEXT, buf), forecolor, 8);
111         }
112       present_surface();
113       shiftx += velx; shifty += vely;
114 
115       SDL_Event event;
116       while(SDL_PollEvent(&event)) switch (event.type) {
117 
118         #if !CAP_SDL2
119         case SDL_VIDEORESIZE: {
120           resize_screen_to(event.resize.w, event.resize.h);
121           precompute();
122           break;
123           }
124         #endif
125         #if CAP_SDL2
126         case SDL_WINDOWEVENT: {
127           if(event.window.event == SDL_WINDOWEVENT_RESIZED)
128           resize_screen_to(event.window.data1, event.window.data2);
129           precompute();
130           break;
131           }
132         #endif
133         case SDL_QUIT: case SDL_MOUSEBUTTONDOWN:
134           goto breakloop;
135 
136         case SDL_KEYDOWN: {
137           int sym = event.key.keysym.sym;
138           int uni = 0;
139           numlock_on = event.key.keysym.mod & KMOD_NUM;
140           if(DKEY == SDLK_RIGHT) velx++;
141           if(DKEY == SDLK_LEFT) velx--;
142           if(DKEY == SDLK_UP) vely++;
143           if(DKEY == SDLK_DOWN) vely--;
144           if(sym == SDLK_ESCAPE) goto breakloop;
145           if(sym == 'h') displayhelp = !displayhelp;
146           if(sym == 's') dosave = true;
147           }
148         }
149       }
150 
151     breakloop:
152     quickmap.clear();
153     vid.wantGL = saveGL;
154     apply_screen_settings();
155     }
156 
157   }
158 #endif
159 
160 EX namespace history {
161 
162   void handleKeyC(int sym, int uni);
163 
164   int lastprogress;
165 
progress_screen()166   EX void progress_screen() {
167     gamescreen(0);
168     mouseovers = "";
169     }
170 
progress(string str)171   EX void progress(string str) {
172 #if CAP_SDL
173     int tick = SDL_GetTicks();
174     if(tick > lastprogress + 250) {
175       lastprogress = tick;
176       msgs.clear();
177       addMessage(str);
178       drawscreen();
179       }
180 #endif
181     }
182 
183   EX bool on;
184   EX vector<shmup::monster*> v;
185   int llv;
186   EX double phase;
187 
188   EX vector<pair<cell*, eMonster> > killhistory;
189   EX vector<pair<cell*, eItem> > findhistory;
190   EX vector<cell*> movehistory;
191 
192   EX bool includeHistory;
193   EX ld lvspeed = 1;
194   EX int bandhalf = 200;
195   EX int bandsegment = 16000;
196 
197   EX int saved_ends;
198 
199   EX cell* first_center_at;
200   EX transmatrix first_center_view;
201 
save_end()202   EX void save_end() {
203     if(!allowIncreasedSight()) {
204       addMessage("Enable cheat mode or GAME OVER to use this");
205       return;
206       }
207     if(cheater) cheater++;
208     switch(saved_ends) {
209       case 0:
210         first_center_at = centerover;
211         first_center_view = View;
212         saved_ends = 1;
213         return;
214 
215       case 1: {
216         shmup::monster *m = new shmup::monster;
217         m->at = inverse(first_center_view);
218         m->base = first_center_at;
219         v.push_back(m);
220         create(first_center_at, centerover, inverse(unshift(ggmatrix(centerover))));
221         if(on) saved_ends = 2;
222         return;
223         }
224 
225       case 2:
226         on = false;
227         saved_ends = 0;
228         return;
229       }
230     }
231 
232   EX bool autoband = false;
233   EX bool autobandhistory = false;
234   EX bool dospiral = true;
235 
236   EX ld extra_line_steps = 0;
237 
238   EX vector<cell*> path_for_lineanimation;
239 
clear()240   EX void clear() {
241     on = false;
242     int N = isize(v);
243     for(int i=0; i<N; i++) delete v[i];
244     v.resize(0);
245     }
246 
smoothen_line()247   EX void smoothen_line() {
248     int Q = isize(v)-1;
249     // virtualRebase(v[0], false);
250     // virtualRebase(v[Q], false);
251 
252     for(int i=0; i<1000; i++) {
253       progress(XLAT("Preparing the line (%1/1000)...", its(i+1)));
254 
255       for(int j=1; j<Q; j++) if((j^i)&1) {
256 
257         // virtualRebase(v[j], false);
258 
259         hyperpoint prev = calc_relative_matrix(v[j-1]->base, v[j]->base, C0) *
260           v[j-1]->at * C0;
261 
262         hyperpoint next = calc_relative_matrix(v[j+1]->base, v[j]->base, C0) *
263           v[j+1]->at * C0;
264 
265         hyperpoint hmid = mid(prev, next);
266 
267         transmatrix at = rgpushxto0(hmid);
268 
269         v[j]->at = at * rspintox(inverse(at) * next);
270         fixmatrix(v[j]->at);
271         }
272       }
273 
274     hyperpoint next0 = calc_relative_matrix(v[1]->base, v[0]->base, C0) * v[1]->at * C0;
275     v[0]->at = v[0]->at * rspintox(inverse(v[0]->at) * next0);
276     }
277 
create(cell * start,cell * target,transmatrix last)278   EX void create(cell *start, cell *target, transmatrix last) {
279 
280     if(target == start && !(quotient && isize(path_for_lineanimation) > 1)) {
281       addMessage("Must go a distance from the starting point");
282       return;
283       }
284 
285     on = true;
286 
287     if(!quotient && !arb::in()) try {
288       auto p = build_shortest_path(start, target);
289       path_for_lineanimation = p;
290       }
291     catch(const hr_shortest_path_exception&) {
292       addMessage("Could not build a path");
293       return;
294       }
295 
296     for(cell *c: path_for_lineanimation) {
297       shmup::monster *m = new shmup::monster;
298       m->at = Id;
299       m->base = c;
300       v.push_back(m);
301       }
302 
303     v.back()->at = last;
304 
305     smoothen_line();
306 
307     llv = ticks;
308     phase = 0;
309     }
310 
create_playerpath()311   EX void create_playerpath() {
312     create(currentmap->gamestart(), cwt.at, Id);
313     }
314 
create_recenter_to_view(bool precise)315   EX void create_recenter_to_view(bool precise) {
316     cell *c = centerover ? centerover : cwt.at;
317     create(path_for_lineanimation[0], c, precise ? inverse(unshift(ggmatrix(c))) : Id);
318     }
319 
movetophase()320   EX void movetophase() {
321 
322     int ph = int(phase);
323     int siz = isize(v);
324     if(ph<0) ph = 0;
325     if(ph >= siz-1) ph = siz-2;
326 
327     cell *old = centerover;
328 
329     centerover = v[ph]->base;
330 
331     ld angle = 0;
332     if(WDIM == 3) {
333       hyperpoint h = inverse(models::rotmatrix()) * View * hpxy3(1,2,3);
334       angle = atan2(h[1], h[2]);
335       }
336 
337     View = inverse(v[ph]->at);
338 
339     if(arb::in() && v[ph]->base->master->emeraldval) View = Mirror * View;
340 
341     hyperpoint now = v[ph]->at * C0;
342 
343     hyperpoint next = calc_relative_matrix(v[ph+1]->base, v[ph]->base, C0) *
344       v[ph+1]->at * C0;
345 
346     View = xpush(-(phase-ph) * hdist(now, next)) * View;
347     if(WDIM == 2 || prod) {
348       View = models::rotmatrix() * View;
349       }
350     else {
351       if(celldistance(v[ph]->base, old) <= 2) {
352         hyperpoint h1 = View * currentmap->relative_matrix(old, centerover, C0) * hpxy3(1,2,3);
353         ld angle1 = atan2(h1[1], h1[2]);
354         View = cspin(2, 1, angle1 - angle) * View;
355         }
356       View = models::rotmatrix() * View;
357       }
358 
359     playermoved = false;
360     centerover = v[ph]->base;
361     compute_graphical_distance();
362     }
363 
apply()364   EX void apply() {
365     int t = ticks;
366     phase += (t-llv) * lvspeed / 400.;
367     llv = t;
368 
369     int siz = isize(v);
370 
371     while(phase > siz-1 + extra_line_steps) phase -= (siz + 2 * extra_line_steps-1);
372     while(phase < - extra_line_steps) phase += (siz + 2 * extra_line_steps-1);
373 
374     movetophase();
375     }
376 
measureLength()377   ld measureLength() {
378     ld r = bandhalf * pconf.scale;
379 
380     ld tpixels = 0;
381     int siz = isize(v);
382 
383     for(int j=0; j<siz-1; j++) {
384       hyperpoint next =
385         inverse(v[j]->at) *
386         calc_relative_matrix(v[j+1]->base, v[j]->base, C0) *
387         v[j+1]->at * C0;
388 
389       hyperpoint nextscr;
390       applymodel(shiftless(next), nextscr);
391       tpixels += nextscr[0] * r;
392 
393       if(j == 0 || j == siz-2)
394         tpixels += nextscr[0] * r * extra_line_steps;
395       }
396 
397     return tpixels;
398     }
399 
400   void restore();
401   void restoreBack();
402 
403 #if CAP_SHOT && CAP_SDL
404   string band_format_now = "bandmodel-$DATE-$ID" IMAGEEXT;
405   string band_format_auto = "bandmodel-$DATE-$ID" IMAGEEXT;
406 #endif
407 
408 #if CAP_SDL
createImage(const string & name_format,bool dospiral)409   EX void createImage(const string& name_format, bool dospiral) {
410     int segid = 1;
411     if(includeHistory) restore();
412 
413     int bandfull = 2*bandhalf;
414     ld len = measureLength();
415 
416     time_t timer;
417     timer = time(NULL);
418     char timebuf[128];
419     strftime(timebuf, 128, "%y%m%d-%H%M%S", localtime(&timer));
420 
421     vector<SDL_Surface*> bands;
422 
423     resetbuffer rbuf;
424 
425     if(1) {
426       // block for RAII
427       dynamicval<videopar> dv(vid, vid);
428       dynamicval<ld> dr(models::rotation, 0);
429       dynamicval<bool> di(inHighQual, true);
430 
431       renderbuffer glbuf(bandfull, bandfull, vid.usingGL);
432       vid.xres = vid.yres = bandfull;
433       glbuf.enable(); current_display->radius = bandhalf;
434       calcparam();
435 
436       ld xpos = 0;
437 
438       int seglen = min(int(len), bandsegment);
439 
440       SDL_Surface *band = SDL_CreateRGBSurface(SDL_SWSURFACE, seglen, bandfull,32,0,0,0,0);
441 
442       auto save_band_segment = [&] {
443         string fname = name_format;
444         replace_str(fname, "$DATE", timebuf);
445         replace_str(fname, "$ID", format("%03d", segid++));
446         IMAGESAVE(band, fname.c_str());
447 
448         if(dospiral)
449           bands.push_back(band);
450         else
451           SDL_FreeSurface(band);
452         };
453 
454       if(!band) {
455         addMessage("Could not create an image of that size.");
456         }
457       else {
458 
459         int siz = isize(v);
460 
461         int bonus = ceil(extra_line_steps);
462 
463         cell *last_base = NULL;
464         hyperpoint last_relative;
465 
466         for(int j=-bonus; j<siz+bonus; j++) {
467           /*
468           SDL_Surface *buffer = s;
469           s = sav;
470 
471           pushScreen(progress_screen);
472 
473           char buf[128];
474           sprintf(buf, "#%03d", segid);
475 
476           progress(s0 + buf + " ("+its(j+bonus)+"/"+its(siz+bonus+bonus-1)+")"); */
477 
478           // calcparam(); current_display->radius = bandhalf;
479           phase = j; movetophase();
480 
481           glbuf.clear(backcolor);
482           drawfullmap();
483 
484           if(last_base) {
485             shiftpoint last = ggmatrix(last_base) * last_relative;
486             hyperpoint hscr;
487             applymodel(last, hscr);
488             ld bwidth = -current_display->radius * hscr[0];
489             println(hlog, "bwidth = ", bwidth, "/", len, " : ", xpos, "..", xpos+bwidth);
490 
491             drawsegment:
492             SDL_Surface *gr = glbuf.render();
493 
494             for(int cy=0; cy<bandfull; cy++) for(int cx=0; cx<=bwidth+3; cx++)
495               qpixel(band, int(xpos+cx), cy) = qpixel(gr, int(bandhalf+cx-bwidth), cy);
496 
497             if(j == 1-bonus)
498               xpos = bwidth * (extra_line_steps - bonus);
499 
500             if(xpos+bwidth > bandsegment) {
501               save_band_segment();
502 
503               len -= bandsegment; xpos -= bandsegment;
504               seglen = min(int(len), bandsegment);
505               band = SDL_CreateRGBSurface(SDL_SWSURFACE, seglen, bandfull,32,0,0,0,0);
506               goto drawsegment;
507               }
508             xpos += bwidth;
509             }
510 
511           last_base = centerover;
512           last_relative = tC0(v[j]->at);
513           }
514         }
515 
516       save_band_segment();
517       }
518 
519     rbuf.reset();
520 
521     if(includeHistory) restoreBack();
522 
523     if(dospiral) {
524       spiral::loop(bands);
525       for(int i=0; i<isize(bands); i++) SDL_FreeSurface(bands[i]);
526       }
527     }
528 
open_filedialog_to_create_image(bool ds)529   EX void open_filedialog_to_create_image(bool ds) {
530     #if CAP_SHOT && CAP_SDL
531     dialog::openFileDialog(band_format_now, XLAT("rendered band ($ID=segment, $DATE=date)"), ".png", [ds] () {
532       createImage(band_format_now, ds);
533       return true;
534       });
535     #endif
536     }
537 #endif
538 
band_renderable_now()539   EX bool band_renderable_now() {
540     return on && (pmodel == mdBand || pmodel == mdBandEquidistant || pmodel == mdBandEquiarea) && !euclid && !sphere;
541     }
542 
history_menu()543   EX void history_menu() {
544     cmode = sm::SIDE | sm::MAYDARK;
545     gamescreen(0);
546 
547     dialog::init(XLAT("history mode"));
548 
549     dialog::addBoolItem(XLAT("include history"), (includeHistory), 'i');
550 
551     // bool notconformal0 = (pmodel >= 5 && pmodel <= 6) && !euclid;
552     // bool notconformal = notconformal0 || abs(pconf.alpha-1) > 1e-3;
553 
554     dialog::addSelItem(XLAT("projection"), current_proj_name(), 'm');
555 
556     dialog::addBoolItem(XLAT("animate from start to current player position"), (on), 'e');
557     dialog::addBoolItem(XLAT("animate from last recenter to current view"), (on), 'E');
558     dialog::addBoolItem(XLAT("animate from last recenter to precise current view"), (on), 'E'-64);
559 
560     if(saved_ends == 0)
561       dialog::addItem(XLAT("save the animation starting point"), '1');
562     else if(saved_ends == 1)
563       dialog::addItem(XLAT("animate from the starting point"), '1');
564     else
565       dialog::addItem(XLAT("reset animation"), '1');
566     dialog::add_action(save_end);
567 
568     if(on) dialog::addSelItem(XLAT("animation speed"), fts(lvspeed), 'a');
569     else dialog::addBreak(100);
570     dialog::addSelItem(XLAT("extend the ends"), fts(extra_line_steps), 'p');
571 
572 #if CAP_SDL
573     dialog::addBoolItem(XLAT("render bands automatically"), (autoband), 'o');
574     if(autoband)
575       dialog::addBoolItem(XLAT("include history when auto-rendering"), (autobandhistory), 'j');
576 
577     if(band_renderable_now() || autoband) {
578       dialog::addSelItem(XLAT("band width"), "2*"+its(bandhalf), 'd');
579       dialog::addSelItem(XLAT("length of a segment"), its(bandsegment), 's');
580       dialog::addBoolItem(XLAT("spiral on rendering"), (dospiral), 'g');
581       if(band_renderable_now())
582         dialog::addItem(XLAT("render now (length: %1)", fts(measureLength())), 'f');
583       }
584     else if(!on) ;
585     else if(!hyperbolic)
586       dialog::addInfo(XLAT("more options in hyperbolic geometry"));
587     else if(!among(pmodel, mdBand, mdBandEquiarea, mdBandEquidistant))
588       dialog::addInfo(XLAT("more options in band projections"));
589 
590 #endif
591 
592     dialog::addBack();
593     dialog::display();
594     mouseovers = XLAT("see http://www.roguetemple.com/z/hyper/models.php");
595     keyhandler = handleKeyC;
596     }
597 
handleKeyC(int sym,int uni)598   void handleKeyC(int sym, int uni) {
599     dialog::handleNavigation(sym, uni);
600 
601     if(uni == 'e' || uni == 'E' || uni == 'E'-64) {
602       if(on) clear();
603       else {
604         if(!allowIncreasedSight()) {
605           addMessage("Enable cheat mode or GAME OVER to use this");
606           return;
607           }
608         if(cheater) cheater++;
609         if(uni == 'E') create_recenter_to_view(false);
610         else if(uni == 'E'-64) create_recenter_to_view(true);
611         else create_playerpath();
612         }
613       }
614     else if(uni == 'o') {
615       autoband = !autoband;
616       #if CAP_SHOT && CAP_SDL
617       if(autoband)
618         dialog::openFileDialog(band_format_auto, XLAT("filename format to use ($ID=segment, $DATE=date)"), ".png", [] () { return true; });
619       #endif
620       }
621     else if(uni == 'm')
622       pushScreen(models::model_menu);
623     else if(uni == 'a')
624       dialog::editNumber(lvspeed, -5, 5, .1, 1, XLAT("animation speed"), "");
625     else if(uni == 'd') {
626       dialog::editNumber(bandhalf, 5, 1000, 5, 200, XLAT("band width"), "");
627       dialog::bound_low(5);
628       }
629     else if(uni == 's') {
630       dialog::editNumber(bandsegment, 500, 32000, 500, 16000, XLAT("band segment"), "");
631       dialog::bound_low(500);
632       }
633     else if(uni == 'p')
634       dialog::editNumber(extra_line_steps, 0, 5, 1, 1, XLAT("extend the ends"),
635         "0 = start at the game start, endat the end position; "
636         "larger numbers give extra space at the ends."
637         );
638     else if(uni == 'g') { dospiral = !dospiral; }
639     else if(uni == 'i') {
640       if(!allowIncreasedSight()) {
641         addMessage("Enable cheat mode or GAME OVER to use this");
642         return;
643         }
644       if(cheater) cheater++;
645       includeHistory = !includeHistory;
646       }
647 #if CAP_SDL
648     else if(uni == 'f' && band_renderable_now())
649       open_filedialog_to_create_image(dospiral);
650     else if(uni == 'j')
651       autobandhistory = !autobandhistory;
652 #endif
653     else if(doexiton(sym, uni)) popScreen();
654     }
655 
656   EX set<cell*> inmovehistory, inkillhistory, infindhistory;
657 
restore()658   EX void restore() {
659     inmovehistory.clear();
660     inkillhistory.clear();
661     infindhistory.clear();
662     for(int i=0; i<isize(movehistory); i++)
663       inmovehistory.insert(movehistory[i]);
664     int sk = isize(killhistory);
665     for(int i=0; i<sk; i++) {
666       eMonster m = killhistory[i].second;
667       killhistory[i].second = killhistory[i].first->monst;
668       killhistory[i].first->monst = m;
669       inkillhistory.insert(killhistory[i].first);
670       }
671     int si = isize(findhistory);
672     for(int i=0; i<si; i++) {
673       eItem m = findhistory[i].second;
674       findhistory[i].second = findhistory[i].first->item;
675       findhistory[i].first->item = m;
676       infindhistory.insert(findhistory[i].first);
677       }
678     }
679 
restoreBack()680   EX void restoreBack() {
681     int sk = isize(killhistory);
682     for(int i=sk-1; i>=0; i--) {
683       eMonster m = killhistory[i].second;
684       killhistory[i].second = killhistory[i].first->monst;
685       killhistory[i].first->monst = m;
686       }
687     int si = isize(findhistory);
688     for(int i=si-1; i>=0; i--) {
689       eItem m = findhistory[i].second;
690       findhistory[i].second = findhistory[i].first->item;
691       findhistory[i].first->item = m;
692       }
693     }
694 
renderAutoband()695   EX void renderAutoband() {
696 #if CAP_SDL && CAP_SHOT
697     if(!cwt.at || celldist(cwt.at) <= 7) return;
698     if(!autoband) return;
699     eModel spm = pmodel;
700     bool ih = includeHistory;
701     includeHistory = autobandhistory;
702     pmodel = mdBand;
703     create_playerpath();
704     createImage(band_format_auto, dospiral);
705     clear();
706     pmodel = spm;
707     includeHistory = ih;
708 #endif
709     }
710 
711   auto hookArg = arg::add3("-playerpath", history::create_playerpath);
712 
__anondd0d444f0402() 713   auto hooks = addHook(hooks_clearmemory, 0, [] () {
714     history::renderAutoband();
715     history::on = false;
716     history::killhistory.clear();
717     history::findhistory.clear();
718     history::movehistory.clear();
719     history::path_for_lineanimation.clear();
720     history::saved_ends = 0;
721     history::includeHistory = false;
722     }) + addHook(hooks_configfile, 0, [] {
723 
724     addsaver(autobandhistory, "include history"); // check!
725     param_f(lvspeed, "lvspeed", "lineview speed");
726     addsaver(extra_line_steps, "lineview extension");
727 
728     addsaver(bandhalf, "band width");
729     addsaver(bandsegment, "band segment");
730     addsaver(autoband, "automatic band");
731     addsaver(autobandhistory, "automatic band history");
732     addsaver(dospiral, "do spiral");
733 
734     #if CAP_SHOT && CAP_SDL
735     addsaver(band_format_auto, "band_format_auto");
736     addsaver(band_format_now, "band_format_now");
737     #endif
738     });
739 
740   }
741 
742 }
743