2019-08-15 13:02:33 +00:00
|
|
|
// Hyperbolic Rogue -- the history mode
|
|
|
|
// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
|
|
|
|
|
|
|
|
/** \file history.cpp
|
|
|
|
* \brief Implementation of the history mode, including the long band and long spiral.
|
|
|
|
*/
|
|
|
|
|
2019-09-05 07:15:40 +00:00
|
|
|
#include "hyper.h"
|
2019-08-15 13:02:33 +00:00
|
|
|
namespace hr {
|
|
|
|
|
|
|
|
#if CAP_SDL
|
|
|
|
namespace spiral {
|
|
|
|
|
|
|
|
typedef long double ld;
|
|
|
|
typedef complex<long double> cxld;
|
|
|
|
|
|
|
|
int shiftx, shifty, velx, vely;
|
|
|
|
|
|
|
|
vector<pair<short, short> > quickmap;
|
|
|
|
|
|
|
|
int CX, CY, SX, SY, Yshift;
|
|
|
|
|
|
|
|
vector<SDL_Surface*> band;
|
|
|
|
SDL_Surface *out;
|
|
|
|
|
|
|
|
bool displayhelp = true;
|
|
|
|
|
|
|
|
color_t& bandpixel(int x, int y) {
|
|
|
|
int i = 0;
|
|
|
|
while(i < isize(band) && x >= band[i]->w)
|
|
|
|
x -= band[i]->w, i++;
|
|
|
|
return qpixel(band[i], x, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
void precompute() {
|
|
|
|
|
|
|
|
CX = 0;
|
|
|
|
for(int i=0; i<isize(band); i++) CX += band[i]->w;
|
|
|
|
if(CX == 0) { printf("ERROR: no CX\n"); return; }
|
|
|
|
CY = band[0]->h;
|
|
|
|
SX = out->w;
|
|
|
|
SY = out->h;
|
2022-11-12 21:38:45 +00:00
|
|
|
|
|
|
|
ld prec = 2*M_PI*M_PI;
|
2019-08-15 13:02:33 +00:00
|
|
|
|
2022-11-12 21:38:45 +00:00
|
|
|
ld k = -prec / log(2.6180339);
|
2019-08-15 13:02:33 +00:00
|
|
|
|
2022-11-12 21:38:45 +00:00
|
|
|
cxld factor = cxld(0, -CY/prec) * cxld(k, M_PI);
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
Yshift = CY * k / M_PI;
|
|
|
|
|
|
|
|
quickmap.clear();
|
|
|
|
|
|
|
|
double xc = ((SX | 1) - 2) / 2.;
|
|
|
|
double yc = ((SY | 1) - 2) / 2.;
|
|
|
|
|
|
|
|
for(int y=0; y<SY; y++)
|
|
|
|
for(int x=0; x<SX; x++) {
|
|
|
|
cxld z(x-xc, y-yc);
|
|
|
|
cxld z1 = log(z);
|
|
|
|
|
|
|
|
z1 = z1 * factor;
|
|
|
|
|
|
|
|
quickmap.push_back(make_pair(int(real(z1)) % CX, int(imag(z1))));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw() {
|
|
|
|
int c = 0;
|
|
|
|
for(int y=0; y<SY; y++) for(int x=0; x<SX; x++) {
|
|
|
|
pair<short,short> p = quickmap[c++];
|
|
|
|
int cx = p.first + shiftx;
|
|
|
|
int cy = p.second + + shifty;
|
|
|
|
int d = cy / CY;
|
|
|
|
cy -= d * CY; cx -= d * Yshift;
|
|
|
|
if(cy<0) cy += CY, cx += Yshift;
|
|
|
|
cx %= CX; if(cx<0) cx += CX;
|
|
|
|
qpixel(out, x, y) = bandpixel(cx, cy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void loop(vector<SDL_Surface*> _band) {
|
|
|
|
|
2022-04-25 22:37:34 +00:00
|
|
|
renderbuffer rb(vid.xres, vid.yres, false);
|
|
|
|
|
|
|
|
if(vid.usingGL) {
|
|
|
|
rb.make_surface();
|
|
|
|
out = rb.srf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
out = s;
|
|
|
|
|
2019-08-15 13:02:33 +00:00
|
|
|
band = _band;
|
|
|
|
precompute();
|
|
|
|
if(CX == 0) return;
|
|
|
|
shiftx = shifty = 0;
|
|
|
|
velx=1; vely=1;
|
|
|
|
bool dosave = false;
|
2022-04-25 22:37:34 +00:00
|
|
|
|
2019-08-15 13:02:33 +00:00
|
|
|
while(true) {
|
|
|
|
|
|
|
|
time_t timer;
|
|
|
|
timer = time(NULL);
|
|
|
|
char buf[128];
|
|
|
|
strftime(buf, 128, "spiral-%y%m%d-%H%M%S" IMAGEEXT, localtime(&timer));
|
|
|
|
|
2022-04-25 22:37:34 +00:00
|
|
|
SDL_LockSurface(out);
|
2019-08-15 13:02:33 +00:00
|
|
|
draw();
|
2022-04-25 22:37:34 +00:00
|
|
|
if(vid.usingGL) {
|
2022-04-25 22:46:02 +00:00
|
|
|
calcparam();
|
2022-04-25 22:37:34 +00:00
|
|
|
setGLProjection();
|
|
|
|
current_display->next_shader_flags = GF_TEXTURE;
|
|
|
|
dynamicval<eModel> m(pmodel, mdPixel);
|
|
|
|
glhr::color2(0xFFFFFFFF);
|
|
|
|
glhr::set_depthtest(false);
|
|
|
|
glhr::current_vertices = NULL;
|
|
|
|
current_display->set_all(0,0);
|
|
|
|
vector<glhr::textured_vertex> rtver(4);
|
|
|
|
for(int i=0; i<4; i++) {
|
|
|
|
int cx[4] = {0, 1, 1, 0};
|
|
|
|
int cy[4] = {0, 0, 1, 1};
|
|
|
|
rtver[i].texture[0] = cx[i] * rb.x * 1. / rb.tx;
|
|
|
|
rtver[i].texture[1] = 1 - cy[i] * rb.y * 1. / rb.ty;
|
|
|
|
rtver[i].coords[0] = vid.xres * cx[i] - current_display->xcenter;
|
|
|
|
rtver[i].coords[1] = vid.yres * cy[i] - current_display->ycenter;
|
|
|
|
rtver[i].coords[2] = 0;
|
|
|
|
rtver[i].coords[3] = 1;
|
|
|
|
}
|
|
|
|
glhr::prepare(rtver);
|
|
|
|
rb.use_as_texture();
|
|
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
}
|
|
|
|
if(dosave) { dosave = false; IMAGESAVE(out, buf); }
|
|
|
|
SDL_UnlockSurface(out);
|
2019-08-15 13:02:33 +00:00
|
|
|
if(displayhelp) {
|
|
|
|
displaystr(SX/2, vid.fsize*2, 0, vid.fsize, "arrows = navigate, ESC = return, h = hide help", forecolor, 8);
|
|
|
|
displaystr(SX/2, SY - vid.fsize*2, 0, vid.fsize, XLAT("s = save to " IMAGEEXT, buf), forecolor, 8);
|
2022-04-25 22:37:34 +00:00
|
|
|
glflush();
|
2019-08-15 13:02:33 +00:00
|
|
|
}
|
2022-04-25 22:37:34 +00:00
|
|
|
present_screen();
|
2019-08-15 13:02:33 +00:00
|
|
|
shiftx += velx; shifty += vely;
|
|
|
|
|
|
|
|
SDL_Event event;
|
|
|
|
while(SDL_PollEvent(&event)) switch (event.type) {
|
|
|
|
|
2021-03-06 13:53:25 +00:00
|
|
|
#if !CAP_SDL2
|
2019-08-15 13:02:33 +00:00
|
|
|
case SDL_VIDEORESIZE: {
|
|
|
|
resize_screen_to(event.resize.w, event.resize.h);
|
|
|
|
precompute();
|
|
|
|
break;
|
|
|
|
}
|
2021-03-06 13:53:25 +00:00
|
|
|
#endif
|
|
|
|
#if CAP_SDL2
|
|
|
|
case SDL_WINDOWEVENT: {
|
|
|
|
if(event.window.event == SDL_WINDOWEVENT_RESIZED)
|
|
|
|
resize_screen_to(event.window.data1, event.window.data2);
|
|
|
|
precompute();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2019-08-15 13:02:33 +00:00
|
|
|
case SDL_QUIT: case SDL_MOUSEBUTTONDOWN:
|
|
|
|
goto breakloop;
|
|
|
|
|
|
|
|
case SDL_KEYDOWN: {
|
|
|
|
int sym = event.key.keysym.sym;
|
2021-03-06 13:53:25 +00:00
|
|
|
int uni = 0;
|
2019-08-15 13:02:33 +00:00
|
|
|
numlock_on = event.key.keysym.mod & KMOD_NUM;
|
|
|
|
if(DKEY == SDLK_RIGHT) velx++;
|
|
|
|
if(DKEY == SDLK_LEFT) velx--;
|
|
|
|
if(DKEY == SDLK_UP) vely++;
|
|
|
|
if(DKEY == SDLK_DOWN) vely--;
|
|
|
|
if(sym == SDLK_ESCAPE) goto breakloop;
|
2021-03-06 13:53:25 +00:00
|
|
|
if(sym == 'h') displayhelp = !displayhelp;
|
|
|
|
if(sym == 's') dosave = true;
|
2019-08-15 13:02:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
breakloop:
|
|
|
|
quickmap.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
EX namespace history {
|
|
|
|
|
|
|
|
void handleKeyC(int sym, int uni);
|
|
|
|
|
|
|
|
int lastprogress;
|
|
|
|
|
|
|
|
EX void progress_screen() {
|
2022-07-05 09:51:06 +00:00
|
|
|
gamescreen();
|
2019-08-15 13:02:33 +00:00
|
|
|
mouseovers = "";
|
|
|
|
}
|
|
|
|
|
2022-06-08 16:01:23 +00:00
|
|
|
EX int progress_each = 250;
|
|
|
|
|
2019-08-15 13:02:33 +00:00
|
|
|
EX void progress(string str) {
|
|
|
|
#if CAP_SDL
|
|
|
|
int tick = SDL_GetTicks();
|
2022-06-08 16:01:23 +00:00
|
|
|
if(tick > lastprogress + progress_each) {
|
2019-08-15 13:02:33 +00:00
|
|
|
lastprogress = tick;
|
|
|
|
msgs.clear();
|
|
|
|
addMessage(str);
|
|
|
|
drawscreen();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
EX bool on;
|
|
|
|
EX vector<shmup::monster*> v;
|
|
|
|
int llv;
|
|
|
|
EX double phase;
|
|
|
|
|
|
|
|
EX vector<pair<cell*, eMonster> > killhistory;
|
|
|
|
EX vector<pair<cell*, eItem> > findhistory;
|
|
|
|
EX vector<cell*> movehistory;
|
|
|
|
|
|
|
|
EX bool includeHistory;
|
|
|
|
EX ld lvspeed = 1;
|
|
|
|
EX int bandhalf = 200;
|
|
|
|
EX int bandsegment = 16000;
|
2020-04-01 09:26:19 +00:00
|
|
|
|
|
|
|
EX int saved_ends;
|
|
|
|
|
|
|
|
EX cell* first_center_at;
|
|
|
|
EX transmatrix first_center_view;
|
|
|
|
|
|
|
|
EX void save_end() {
|
2020-04-02 10:59:30 +00:00
|
|
|
if(!allowIncreasedSight()) {
|
2020-04-01 09:26:19 +00:00
|
|
|
addMessage("Enable cheat mode or GAME OVER to use this");
|
|
|
|
return;
|
|
|
|
}
|
2020-04-02 10:59:30 +00:00
|
|
|
if(cheater) cheater++;
|
2020-04-01 09:26:19 +00:00
|
|
|
switch(saved_ends) {
|
|
|
|
case 0:
|
|
|
|
first_center_at = centerover;
|
|
|
|
first_center_view = View;
|
|
|
|
saved_ends = 1;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 1: {
|
|
|
|
shmup::monster *m = new shmup::monster;
|
|
|
|
m->at = inverse(first_center_view);
|
|
|
|
m->base = first_center_at;
|
|
|
|
v.push_back(m);
|
2020-07-27 16:49:04 +00:00
|
|
|
create(first_center_at, centerover, inverse(unshift(ggmatrix(centerover))));
|
2020-04-01 09:26:19 +00:00
|
|
|
if(on) saved_ends = 2;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
on = false;
|
|
|
|
saved_ends = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
EX bool autoband = false;
|
|
|
|
EX bool autobandhistory = false;
|
|
|
|
EX bool dospiral = true;
|
|
|
|
|
|
|
|
EX ld extra_line_steps = 0;
|
|
|
|
|
|
|
|
EX vector<cell*> path_for_lineanimation;
|
|
|
|
|
|
|
|
EX void clear() {
|
|
|
|
on = false;
|
|
|
|
int N = isize(v);
|
|
|
|
for(int i=0; i<N; i++) delete v[i];
|
|
|
|
v.resize(0);
|
|
|
|
}
|
2020-04-01 09:26:19 +00:00
|
|
|
|
|
|
|
EX void smoothen_line() {
|
|
|
|
int Q = isize(v)-1;
|
|
|
|
// virtualRebase(v[0], false);
|
|
|
|
// virtualRebase(v[Q], false);
|
|
|
|
|
|
|
|
for(int i=0; i<1000; i++) {
|
|
|
|
progress(XLAT("Preparing the line (%1/1000)...", its(i+1)));
|
|
|
|
|
|
|
|
for(int j=1; j<Q; j++) if((j^i)&1) {
|
|
|
|
|
|
|
|
// virtualRebase(v[j], false);
|
|
|
|
|
|
|
|
hyperpoint prev = calc_relative_matrix(v[j-1]->base, v[j]->base, C0) *
|
|
|
|
v[j-1]->at * C0;
|
|
|
|
|
|
|
|
hyperpoint next = calc_relative_matrix(v[j+1]->base, v[j]->base, C0) *
|
|
|
|
v[j+1]->at * C0;
|
|
|
|
|
|
|
|
hyperpoint hmid = mid(prev, next);
|
|
|
|
|
|
|
|
transmatrix at = rgpushxto0(hmid);
|
|
|
|
|
|
|
|
v[j]->at = at * rspintox(inverse(at) * next);
|
|
|
|
fixmatrix(v[j]->at);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hyperpoint next0 = calc_relative_matrix(v[1]->base, v[0]->base, C0) * v[1]->at * C0;
|
|
|
|
v[0]->at = v[0]->at * rspintox(inverse(v[0]->at) * next0);
|
|
|
|
}
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
EX void create(cell *start, cell *target, transmatrix last) {
|
|
|
|
|
|
|
|
if(target == start && !(quotient && isize(path_for_lineanimation) > 1)) {
|
|
|
|
addMessage("Must go a distance from the starting point");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
on = true;
|
|
|
|
|
2020-01-15 17:19:59 +00:00
|
|
|
if(!quotient && !arb::in()) try {
|
|
|
|
auto p = build_shortest_path(start, target);
|
|
|
|
path_for_lineanimation = p;
|
2019-08-15 13:02:33 +00:00
|
|
|
}
|
2021-03-21 21:56:38 +00:00
|
|
|
catch(const hr_shortest_path_exception&) {
|
2020-01-15 17:19:59 +00:00
|
|
|
addMessage("Could not build a path");
|
2019-08-15 13:02:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(cell *c: path_for_lineanimation) {
|
|
|
|
shmup::monster *m = new shmup::monster;
|
|
|
|
m->at = Id;
|
|
|
|
m->base = c;
|
|
|
|
v.push_back(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
v.back()->at = last;
|
|
|
|
|
2020-04-01 09:26:19 +00:00
|
|
|
smoothen_line();
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
llv = ticks;
|
|
|
|
phase = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
EX void create_playerpath() {
|
|
|
|
create(currentmap->gamestart(), cwt.at, Id);
|
|
|
|
}
|
|
|
|
|
|
|
|
EX void create_recenter_to_view(bool precise) {
|
2019-11-13 23:26:50 +00:00
|
|
|
cell *c = centerover ? centerover : cwt.at;
|
2020-07-27 16:49:04 +00:00
|
|
|
create(path_for_lineanimation[0], c, precise ? inverse(unshift(ggmatrix(c))) : Id);
|
2019-08-15 13:02:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
EX void movetophase() {
|
|
|
|
|
|
|
|
int ph = int(phase);
|
|
|
|
int siz = isize(v);
|
|
|
|
if(ph<0) ph = 0;
|
|
|
|
if(ph >= siz-1) ph = siz-2;
|
|
|
|
|
2019-11-13 23:26:50 +00:00
|
|
|
cell *old = centerover;
|
2019-08-15 13:02:33 +00:00
|
|
|
|
2019-11-13 23:26:50 +00:00
|
|
|
centerover = v[ph]->base;
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
ld angle = 0;
|
|
|
|
if(WDIM == 3) {
|
|
|
|
hyperpoint h = inverse(models::rotmatrix()) * View * hpxy3(1,2,3);
|
|
|
|
angle = atan2(h[1], h[2]);
|
|
|
|
}
|
|
|
|
|
2019-12-27 00:19:14 +00:00
|
|
|
View = inverse(v[ph]->at);
|
2020-01-15 17:19:59 +00:00
|
|
|
|
|
|
|
if(arb::in() && v[ph]->base->master->emeraldval) View = Mirror * View;
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
hyperpoint now = v[ph]->at * C0;
|
|
|
|
|
|
|
|
hyperpoint next = calc_relative_matrix(v[ph+1]->base, v[ph]->base, C0) *
|
|
|
|
v[ph+1]->at * C0;
|
|
|
|
|
|
|
|
View = xpush(-(phase-ph) * hdist(now, next)) * View;
|
2019-08-19 12:29:25 +00:00
|
|
|
if(WDIM == 2 || prod) {
|
2019-08-15 13:02:33 +00:00
|
|
|
View = models::rotmatrix() * View;
|
|
|
|
}
|
|
|
|
else {
|
2019-11-13 23:26:50 +00:00
|
|
|
if(celldistance(v[ph]->base, old) <= 2) {
|
|
|
|
hyperpoint h1 = View * currentmap->relative_matrix(old, centerover, C0) * hpxy3(1,2,3);
|
2019-08-15 13:02:33 +00:00
|
|
|
ld angle1 = atan2(h1[1], h1[2]);
|
|
|
|
View = cspin(2, 1, angle1 - angle) * View;
|
|
|
|
}
|
|
|
|
View = models::rotmatrix() * View;
|
|
|
|
}
|
|
|
|
|
|
|
|
playermoved = false;
|
2019-11-13 23:26:50 +00:00
|
|
|
centerover = v[ph]->base;
|
2019-08-15 13:02:33 +00:00
|
|
|
compute_graphical_distance();
|
|
|
|
}
|
|
|
|
|
|
|
|
EX void apply() {
|
|
|
|
int t = ticks;
|
|
|
|
phase += (t-llv) * lvspeed / 400.;
|
|
|
|
llv = t;
|
|
|
|
|
|
|
|
int siz = isize(v);
|
|
|
|
|
|
|
|
while(phase > siz-1 + extra_line_steps) phase -= (siz + 2 * extra_line_steps-1);
|
|
|
|
while(phase < - extra_line_steps) phase += (siz + 2 * extra_line_steps-1);
|
|
|
|
|
|
|
|
movetophase();
|
|
|
|
}
|
|
|
|
|
|
|
|
ld measureLength() {
|
2020-04-16 22:53:58 +00:00
|
|
|
ld r = bandhalf * pconf.scale;
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
ld tpixels = 0;
|
|
|
|
int siz = isize(v);
|
|
|
|
|
|
|
|
for(int j=0; j<siz-1; j++) {
|
|
|
|
hyperpoint next =
|
|
|
|
inverse(v[j]->at) *
|
|
|
|
calc_relative_matrix(v[j+1]->base, v[j]->base, C0) *
|
|
|
|
v[j+1]->at * C0;
|
|
|
|
|
|
|
|
hyperpoint nextscr;
|
2020-07-27 16:49:04 +00:00
|
|
|
applymodel(shiftless(next), nextscr);
|
2019-08-15 13:02:33 +00:00
|
|
|
tpixels += nextscr[0] * r;
|
|
|
|
|
|
|
|
if(j == 0 || j == siz-2)
|
|
|
|
tpixels += nextscr[0] * r * extra_line_steps;
|
|
|
|
}
|
|
|
|
|
|
|
|
return tpixels;
|
|
|
|
}
|
|
|
|
|
|
|
|
void restore();
|
|
|
|
void restoreBack();
|
2021-05-21 23:24:28 +00:00
|
|
|
|
|
|
|
#if CAP_SHOT && CAP_SDL
|
2021-02-04 16:13:33 +00:00
|
|
|
string band_format_now = "bandmodel-$DATE-$ID" IMAGEEXT;
|
|
|
|
string band_format_auto = "bandmodel-$DATE-$ID" IMAGEEXT;
|
2021-05-21 23:24:28 +00:00
|
|
|
#endif
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
#if CAP_SDL
|
2021-02-04 16:13:33 +00:00
|
|
|
EX void createImage(const string& name_format, bool dospiral) {
|
2019-08-15 13:02:33 +00:00
|
|
|
int segid = 1;
|
|
|
|
if(includeHistory) restore();
|
|
|
|
|
|
|
|
int bandfull = 2*bandhalf;
|
|
|
|
ld len = measureLength();
|
|
|
|
|
|
|
|
time_t timer;
|
|
|
|
timer = time(NULL);
|
|
|
|
char timebuf[128];
|
|
|
|
strftime(timebuf, 128, "%y%m%d-%H%M%S", localtime(&timer));
|
|
|
|
|
|
|
|
vector<SDL_Surface*> bands;
|
|
|
|
|
|
|
|
resetbuffer rbuf;
|
|
|
|
|
|
|
|
if(1) {
|
|
|
|
// block for RAII
|
|
|
|
dynamicval<videopar> dv(vid, vid);
|
|
|
|
dynamicval<ld> dr(models::rotation, 0);
|
|
|
|
dynamicval<bool> di(inHighQual, true);
|
|
|
|
|
|
|
|
renderbuffer glbuf(bandfull, bandfull, vid.usingGL);
|
|
|
|
vid.xres = vid.yres = bandfull;
|
|
|
|
glbuf.enable(); current_display->radius = bandhalf;
|
|
|
|
calcparam();
|
|
|
|
|
|
|
|
ld xpos = 0;
|
|
|
|
|
|
|
|
int seglen = min(int(len), bandsegment);
|
|
|
|
|
|
|
|
SDL_Surface *band = SDL_CreateRGBSurface(SDL_SWSURFACE, seglen, bandfull,32,0,0,0,0);
|
|
|
|
|
2021-02-04 16:13:33 +00:00
|
|
|
auto save_band_segment = [&] {
|
|
|
|
string fname = name_format;
|
|
|
|
replace_str(fname, "$DATE", timebuf);
|
|
|
|
replace_str(fname, "$ID", format("%03d", segid++));
|
|
|
|
IMAGESAVE(band, fname.c_str());
|
|
|
|
|
|
|
|
if(dospiral)
|
|
|
|
bands.push_back(band);
|
|
|
|
else
|
|
|
|
SDL_FreeSurface(band);
|
|
|
|
};
|
|
|
|
|
2019-08-15 13:02:33 +00:00
|
|
|
if(!band) {
|
|
|
|
addMessage("Could not create an image of that size.");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
int siz = isize(v);
|
|
|
|
|
|
|
|
int bonus = ceil(extra_line_steps);
|
|
|
|
|
|
|
|
cell *last_base = NULL;
|
|
|
|
hyperpoint last_relative;
|
|
|
|
|
|
|
|
for(int j=-bonus; j<siz+bonus; j++) {
|
|
|
|
/*
|
|
|
|
SDL_Surface *buffer = s;
|
|
|
|
s = sav;
|
|
|
|
|
|
|
|
pushScreen(progress_screen);
|
|
|
|
|
|
|
|
char buf[128];
|
|
|
|
sprintf(buf, "#%03d", segid);
|
|
|
|
|
|
|
|
progress(s0 + buf + " ("+its(j+bonus)+"/"+its(siz+bonus+bonus-1)+")"); */
|
|
|
|
|
|
|
|
// calcparam(); current_display->radius = bandhalf;
|
|
|
|
phase = j; movetophase();
|
|
|
|
|
|
|
|
glbuf.clear(backcolor);
|
|
|
|
drawfullmap();
|
|
|
|
|
|
|
|
if(last_base) {
|
2020-07-27 16:49:04 +00:00
|
|
|
shiftpoint last = ggmatrix(last_base) * last_relative;
|
2019-08-15 13:02:33 +00:00
|
|
|
hyperpoint hscr;
|
|
|
|
applymodel(last, hscr);
|
|
|
|
ld bwidth = -current_display->radius * hscr[0];
|
2019-12-27 00:19:37 +00:00
|
|
|
println(hlog, "bwidth = ", bwidth, "/", len, " : ", xpos, "..", xpos+bwidth);
|
|
|
|
|
2019-08-15 13:02:33 +00:00
|
|
|
drawsegment:
|
|
|
|
SDL_Surface *gr = glbuf.render();
|
|
|
|
|
|
|
|
for(int cy=0; cy<bandfull; cy++) for(int cx=0; cx<=bwidth+3; cx++)
|
|
|
|
qpixel(band, int(xpos+cx), cy) = qpixel(gr, int(bandhalf+cx-bwidth), cy);
|
|
|
|
|
|
|
|
if(j == 1-bonus)
|
|
|
|
xpos = bwidth * (extra_line_steps - bonus);
|
|
|
|
|
|
|
|
if(xpos+bwidth > bandsegment) {
|
2021-02-04 16:13:33 +00:00
|
|
|
save_band_segment();
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
len -= bandsegment; xpos -= bandsegment;
|
|
|
|
seglen = min(int(len), bandsegment);
|
|
|
|
band = SDL_CreateRGBSurface(SDL_SWSURFACE, seglen, bandfull,32,0,0,0,0);
|
|
|
|
goto drawsegment;
|
|
|
|
}
|
|
|
|
xpos += bwidth;
|
|
|
|
}
|
|
|
|
|
2019-11-13 23:26:50 +00:00
|
|
|
last_base = centerover;
|
2019-12-27 00:19:37 +00:00
|
|
|
last_relative = tC0(v[j]->at);
|
2019-08-15 13:02:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-04 16:13:33 +00:00
|
|
|
save_band_segment();
|
2019-08-15 13:02:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rbuf.reset();
|
|
|
|
|
|
|
|
if(includeHistory) restoreBack();
|
|
|
|
|
|
|
|
if(dospiral) {
|
|
|
|
spiral::loop(bands);
|
|
|
|
for(int i=0; i<isize(bands); i++) SDL_FreeSurface(bands[i]);
|
|
|
|
}
|
|
|
|
}
|
2021-02-04 16:13:33 +00:00
|
|
|
|
|
|
|
EX void open_filedialog_to_create_image(bool ds) {
|
2021-06-09 02:33:55 +00:00
|
|
|
#if CAP_SHOT && CAP_SDL
|
2021-02-04 16:13:33 +00:00
|
|
|
dialog::openFileDialog(band_format_now, XLAT("rendered band ($ID=segment, $DATE=date)"), ".png", [ds] () {
|
|
|
|
createImage(band_format_now, ds);
|
|
|
|
return true;
|
|
|
|
});
|
2021-06-09 02:33:55 +00:00
|
|
|
#endif
|
2021-02-04 16:13:33 +00:00
|
|
|
}
|
2019-08-15 13:02:33 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
EX bool band_renderable_now() {
|
|
|
|
return on && (pmodel == mdBand || pmodel == mdBandEquidistant || pmodel == mdBandEquiarea) && !euclid && !sphere;
|
|
|
|
}
|
|
|
|
|
|
|
|
EX void history_menu() {
|
|
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
2022-07-05 09:51:06 +00:00
|
|
|
gamescreen();
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
dialog::init(XLAT("history mode"));
|
|
|
|
|
|
|
|
dialog::addBoolItem(XLAT("include history"), (includeHistory), 'i');
|
|
|
|
|
|
|
|
// bool notconformal0 = (pmodel >= 5 && pmodel <= 6) && !euclid;
|
2020-04-16 22:53:58 +00:00
|
|
|
// bool notconformal = notconformal0 || abs(pconf.alpha-1) > 1e-3;
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
dialog::addSelItem(XLAT("projection"), current_proj_name(), 'm');
|
|
|
|
|
|
|
|
dialog::addBoolItem(XLAT("animate from start to current player position"), (on), 'e');
|
|
|
|
dialog::addBoolItem(XLAT("animate from last recenter to current view"), (on), 'E');
|
|
|
|
dialog::addBoolItem(XLAT("animate from last recenter to precise current view"), (on), 'E'-64);
|
2020-04-01 09:26:19 +00:00
|
|
|
|
|
|
|
if(saved_ends == 0)
|
|
|
|
dialog::addItem(XLAT("save the animation starting point"), '1');
|
|
|
|
else if(saved_ends == 1)
|
|
|
|
dialog::addItem(XLAT("animate from the starting point"), '1');
|
|
|
|
else
|
|
|
|
dialog::addItem(XLAT("reset animation"), '1');
|
|
|
|
dialog::add_action(save_end);
|
|
|
|
|
2019-08-15 13:02:33 +00:00
|
|
|
if(on) dialog::addSelItem(XLAT("animation speed"), fts(lvspeed), 'a');
|
|
|
|
else dialog::addBreak(100);
|
|
|
|
dialog::addSelItem(XLAT("extend the ends"), fts(extra_line_steps), 'p');
|
|
|
|
|
|
|
|
#if CAP_SDL
|
|
|
|
dialog::addBoolItem(XLAT("render bands automatically"), (autoband), 'o');
|
|
|
|
if(autoband)
|
|
|
|
dialog::addBoolItem(XLAT("include history when auto-rendering"), (autobandhistory), 'j');
|
|
|
|
|
|
|
|
if(band_renderable_now() || autoband) {
|
|
|
|
dialog::addSelItem(XLAT("band width"), "2*"+its(bandhalf), 'd');
|
|
|
|
dialog::addSelItem(XLAT("length of a segment"), its(bandsegment), 's');
|
|
|
|
dialog::addBoolItem(XLAT("spiral on rendering"), (dospiral), 'g');
|
|
|
|
if(band_renderable_now())
|
|
|
|
dialog::addItem(XLAT("render now (length: %1)", fts(measureLength())), 'f');
|
|
|
|
}
|
2021-02-04 16:13:33 +00:00
|
|
|
else if(!on) ;
|
|
|
|
else if(!hyperbolic)
|
|
|
|
dialog::addInfo(XLAT("more options in hyperbolic geometry"));
|
|
|
|
else if(!among(pmodel, mdBand, mdBandEquiarea, mdBandEquidistant))
|
|
|
|
dialog::addInfo(XLAT("more options in band projections"));
|
|
|
|
|
2019-08-15 13:02:33 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
dialog::addBack();
|
|
|
|
dialog::display();
|
|
|
|
mouseovers = XLAT("see http://www.roguetemple.com/z/hyper/models.php");
|
|
|
|
keyhandler = handleKeyC;
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleKeyC(int sym, int uni) {
|
|
|
|
dialog::handleNavigation(sym, uni);
|
|
|
|
|
|
|
|
if(uni == 'e' || uni == 'E' || uni == 'E'-64) {
|
|
|
|
if(on) clear();
|
|
|
|
else {
|
2020-04-02 10:59:30 +00:00
|
|
|
if(!allowIncreasedSight()) {
|
2019-08-15 13:02:33 +00:00
|
|
|
addMessage("Enable cheat mode or GAME OVER to use this");
|
|
|
|
return;
|
|
|
|
}
|
2020-04-02 10:59:30 +00:00
|
|
|
if(cheater) cheater++;
|
2019-08-15 13:02:33 +00:00
|
|
|
if(uni == 'E') create_recenter_to_view(false);
|
|
|
|
else if(uni == 'E'-64) create_recenter_to_view(true);
|
|
|
|
else create_playerpath();
|
|
|
|
}
|
|
|
|
}
|
2021-02-04 16:13:33 +00:00
|
|
|
else if(uni == 'o') {
|
2019-08-15 13:02:33 +00:00
|
|
|
autoband = !autoband;
|
2021-05-21 23:24:28 +00:00
|
|
|
#if CAP_SHOT && CAP_SDL
|
2021-02-04 16:13:33 +00:00
|
|
|
if(autoband)
|
|
|
|
dialog::openFileDialog(band_format_auto, XLAT("filename format to use ($ID=segment, $DATE=date)"), ".png", [] () { return true; });
|
2021-05-21 23:24:28 +00:00
|
|
|
#endif
|
2021-02-04 16:13:33 +00:00
|
|
|
}
|
2019-08-15 13:02:33 +00:00
|
|
|
else if(uni == 'm')
|
|
|
|
pushScreen(models::model_menu);
|
|
|
|
else if(uni == 'a')
|
|
|
|
dialog::editNumber(lvspeed, -5, 5, .1, 1, XLAT("animation speed"), "");
|
|
|
|
else if(uni == 'd') {
|
|
|
|
dialog::editNumber(bandhalf, 5, 1000, 5, 200, XLAT("band width"), "");
|
|
|
|
dialog::bound_low(5);
|
|
|
|
}
|
|
|
|
else if(uni == 's') {
|
|
|
|
dialog::editNumber(bandsegment, 500, 32000, 500, 16000, XLAT("band segment"), "");
|
|
|
|
dialog::bound_low(500);
|
|
|
|
}
|
|
|
|
else if(uni == 'p')
|
|
|
|
dialog::editNumber(extra_line_steps, 0, 5, 1, 1, XLAT("extend the ends"),
|
|
|
|
"0 = start at the game start, endat the end position; "
|
|
|
|
"larger numbers give extra space at the ends."
|
|
|
|
);
|
|
|
|
else if(uni == 'g') { dospiral = !dospiral; }
|
|
|
|
else if(uni == 'i') {
|
2020-04-02 10:59:30 +00:00
|
|
|
if(!allowIncreasedSight()) {
|
2019-08-15 13:02:33 +00:00
|
|
|
addMessage("Enable cheat mode or GAME OVER to use this");
|
|
|
|
return;
|
|
|
|
}
|
2020-04-02 10:59:30 +00:00
|
|
|
if(cheater) cheater++;
|
2019-08-15 13:02:33 +00:00
|
|
|
includeHistory = !includeHistory;
|
|
|
|
}
|
|
|
|
#if CAP_SDL
|
2021-02-04 16:13:33 +00:00
|
|
|
else if(uni == 'f' && band_renderable_now())
|
|
|
|
open_filedialog_to_create_image(dospiral);
|
|
|
|
else if(uni == 'j')
|
2019-08-15 13:02:33 +00:00
|
|
|
autobandhistory = !autobandhistory;
|
2021-02-04 16:13:33 +00:00
|
|
|
#endif
|
2019-08-15 13:02:33 +00:00
|
|
|
else if(doexiton(sym, uni)) popScreen();
|
|
|
|
}
|
|
|
|
|
2019-09-06 06:17:02 +00:00
|
|
|
EX set<cell*> inmovehistory, inkillhistory, infindhistory;
|
2019-08-15 13:02:33 +00:00
|
|
|
|
|
|
|
EX void restore() {
|
|
|
|
inmovehistory.clear();
|
|
|
|
inkillhistory.clear();
|
|
|
|
infindhistory.clear();
|
|
|
|
for(int i=0; i<isize(movehistory); i++)
|
|
|
|
inmovehistory.insert(movehistory[i]);
|
|
|
|
int sk = isize(killhistory);
|
|
|
|
for(int i=0; i<sk; i++) {
|
|
|
|
eMonster m = killhistory[i].second;
|
|
|
|
killhistory[i].second = killhistory[i].first->monst;
|
|
|
|
killhistory[i].first->monst = m;
|
|
|
|
inkillhistory.insert(killhistory[i].first);
|
|
|
|
}
|
|
|
|
int si = isize(findhistory);
|
|
|
|
for(int i=0; i<si; i++) {
|
|
|
|
eItem m = findhistory[i].second;
|
|
|
|
findhistory[i].second = findhistory[i].first->item;
|
|
|
|
findhistory[i].first->item = m;
|
|
|
|
infindhistory.insert(findhistory[i].first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EX void restoreBack() {
|
|
|
|
int sk = isize(killhistory);
|
|
|
|
for(int i=sk-1; i>=0; i--) {
|
|
|
|
eMonster m = killhistory[i].second;
|
|
|
|
killhistory[i].second = killhistory[i].first->monst;
|
|
|
|
killhistory[i].first->monst = m;
|
|
|
|
}
|
|
|
|
int si = isize(findhistory);
|
|
|
|
for(int i=si-1; i>=0; i--) {
|
|
|
|
eItem m = findhistory[i].second;
|
|
|
|
findhistory[i].second = findhistory[i].first->item;
|
|
|
|
findhistory[i].first->item = m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EX void renderAutoband() {
|
2021-06-09 02:33:55 +00:00
|
|
|
#if CAP_SDL && CAP_SHOT
|
2019-08-15 13:02:33 +00:00
|
|
|
if(!cwt.at || celldist(cwt.at) <= 7) return;
|
|
|
|
if(!autoband) return;
|
|
|
|
eModel spm = pmodel;
|
|
|
|
bool ih = includeHistory;
|
|
|
|
includeHistory = autobandhistory;
|
|
|
|
pmodel = mdBand;
|
|
|
|
create_playerpath();
|
2021-02-04 16:13:33 +00:00
|
|
|
createImage(band_format_auto, dospiral);
|
2019-08-15 13:02:33 +00:00
|
|
|
clear();
|
|
|
|
pmodel = spm;
|
|
|
|
includeHistory = ih;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-03-31 00:58:03 +00:00
|
|
|
auto hookArg = arg::add3("-playerpath", history::create_playerpath);
|
2019-08-15 13:02:33 +00:00
|
|
|
|
2020-04-11 18:40:12 +00:00
|
|
|
auto hooks = addHook(hooks_clearmemory, 0, [] () {
|
2019-08-15 13:02:33 +00:00
|
|
|
history::renderAutoband();
|
|
|
|
history::on = false;
|
|
|
|
history::killhistory.clear();
|
|
|
|
history::findhistory.clear();
|
|
|
|
history::movehistory.clear();
|
|
|
|
history::path_for_lineanimation.clear();
|
2020-04-01 09:26:19 +00:00
|
|
|
history::saved_ends = 0;
|
2019-08-15 13:02:33 +00:00
|
|
|
history::includeHistory = false;
|
2021-02-04 13:58:39 +00:00
|
|
|
}) + addHook(hooks_configfile, 0, [] {
|
2021-01-31 17:45:17 +00:00
|
|
|
|
|
|
|
addsaver(autobandhistory, "include history"); // check!
|
2021-02-01 00:45:10 +00:00
|
|
|
param_f(lvspeed, "lvspeed", "lineview speed");
|
2021-01-31 17:45:17 +00:00
|
|
|
addsaver(extra_line_steps, "lineview extension");
|
|
|
|
|
|
|
|
addsaver(bandhalf, "band width");
|
|
|
|
addsaver(bandsegment, "band segment");
|
|
|
|
addsaver(autoband, "automatic band");
|
|
|
|
addsaver(autobandhistory, "automatic band history");
|
|
|
|
addsaver(dospiral, "do spiral");
|
2021-02-04 16:13:33 +00:00
|
|
|
|
2021-05-21 23:24:28 +00:00
|
|
|
#if CAP_SHOT && CAP_SDL
|
2021-02-04 16:13:33 +00:00
|
|
|
addsaver(band_format_auto, "band_format_auto");
|
|
|
|
addsaver(band_format_now, "band_format_now");
|
2021-05-21 23:24:28 +00:00
|
|
|
#endif
|
2019-08-15 13:02:33 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|