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