1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-12-25 01:20:37 +00:00

Major refactoring, multisampling

This commit is contained in:
Zeno Rogue 2017-07-10 20:47:38 +02:00
parent c46ab39d1e
commit 069f7b0caf
31 changed files with 20872 additions and 6856 deletions

1147
basegraph.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1501,17 +1501,7 @@ void clearCellMemory() {
allmaps.clear(); allmaps.clear();
} }
void clearMemory() { auto cellhooks = addHook(clearmemory, 500, clearCellMemory);
extern void clearGameMemory();
clearGameMemory();
shmup::clearMemory();
cleargraphmemory();
#ifndef NOEDIT
mapeditor::clearModelCells();
#endif
clearCellMemory();
DEBMEM ( printf("ok\n"); )
}
int getHemisphere(cell *c, int which) { int getHemisphere(cell *c, int which) {
if(torus) return 0; if(torus) return 0;

View File

@ -2761,3 +2761,17 @@ namespace ca {
} }
} }
} }
auto ccm = addHook(clearmemory, 0, [] () {
offscreen.clear();
princess::clear();
mirrors.clear();
clearing::bpdata.clear();
tortoise::emap.clear();
tortoise::babymap.clear();
prairie::lasttreasure = NULL;
prairie::enter = NULL;
prairie::tchoices.clear();
prairie::beaststogen.clear();
});

1008
config.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
// Hyperbolic Rogue -- the conformal/history mode // Hyperbolic Rogue -- the conformal/history mode
// Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details // Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details
#include <complex>
namespace polygonal { namespace polygonal {
@ -11,8 +10,6 @@ namespace polygonal {
int deg = 20; int deg = 20;
#define MSI 120
ld matrix[MSI][MSI]; ld matrix[MSI][MSI];
ld ans[MSI]; ld ans[MSI];
@ -227,8 +224,15 @@ bool isbad(ld z) { return !isfinite(z) || fabs(z) > 1e6; }
namespace conformal { namespace conformal {
void handleKeyC(int sym, int uni);
int lastprogress; int lastprogress;
void progress_screen() {
gamescreen(0);
mouseovers = "";
}
void progress(string str) { void progress(string str) {
#ifndef NOSDL #ifndef NOSDL
int tick = SDL_GetTicks(); int tick = SDL_GetTicks();
@ -429,9 +433,9 @@ namespace conformal {
int siz = size(v); int siz = size(v);
for(int j=1; j<siz-1; j++) { for(int j=1; j<siz-1; j++) {
SDL_Surface *buffer = s; SDL_Surface *buffer = s;
emtype cm = cmode;
s = sav; s = sav;
cmode = emProgress;
pushScreen(progress_screen);
char buf[128]; char buf[128];
sprintf(buf, "#%03d", segid); sprintf(buf, "#%03d", segid);
@ -440,7 +444,6 @@ namespace conformal {
calcparam(); calcparam();
vid.radius = bandhalf; vid.radius = bandhalf;
cmode = cm;
s = buffer; s = buffer;
viewctr.h = v[j]->base->master; viewctr.h = v[j]->base->master;
viewctr.spin = 0; viewctr.spin = 0;
@ -462,6 +465,8 @@ namespace conformal {
int bwidth = x-bandhalf; int bwidth = x-bandhalf;
popScreen();
drawsegment: drawsegment:
for(int cy=0; cy<bandfull; cy++) for(int cx=0; cx<bwidth; cx++) for(int cy=0; cy<bandfull; cy++) for(int cx=0; cx<bwidth; cx++)
@ -572,10 +577,12 @@ namespace conformal {
dialog::addItem(XLAT("exit this menu"), 'q'); dialog::addItem(XLAT("exit this menu"), 'q');
dialog::display(); dialog::display();
mouseovers = XLAT("see http://www.roguetemple.com/z/hyper/conformal.php"); mouseovers = XLAT("see http://www.roguetemple.com/z/hyper/conformal.php");
keyhandler = handleKeyC;
} }
int ib = 0; int ib = 0;
ld compbuf; ld compbuf;
void applyIB() { void applyIB() {
using namespace polygonal; using namespace polygonal;
cld& tgt = coef[coefid]; cld& tgt = coef[coefid];
@ -583,7 +590,7 @@ namespace conformal {
if(ib == 2) tgt = cld(real(tgt), compbuf); if(ib == 2) tgt = cld(real(tgt), compbuf);
} }
void handleKey(int sym, int uni) { void handleKeyC(int sym, int uni) {
dialog::handleNavigation(sym, uni); dialog::handleNavigation(sym, uni);
ib = 0; ib = 0;
@ -624,7 +631,7 @@ namespace conformal {
dialog::sidedialog = true; dialog::sidedialog = true;
} }
else if(sym == 'n' && pmodel == mdPolygonal) { else if(sym == 'n' && pmodel == mdPolygonal) {
dialog::editNumber(polygonal::deg, 2, MSI-1, 1, 2, XLAT("degree of the approximation"), ""); dialog::editNumber(polygonal::deg, 2, polygonal::MSI-1, 1, 2, XLAT("degree of the approximation"), "");
dialog::sidedialog = true; dialog::sidedialog = true;
} }
else if(sym == 'x' && pmodel == mdPolynomial) { else if(sym == 'x' && pmodel == mdPolynomial) {
@ -642,7 +649,7 @@ namespace conformal {
ib = 2; ib = 2;
} }
else if(sym == 'n' && pmodel == mdPolynomial) else if(sym == 'n' && pmodel == mdPolynomial)
dialog::editNumber(polygonal::coefid, 0, MSI-1, 1, 0, XLAT("which coefficient"), ""); dialog::editNumber(polygonal::coefid, 0, polygonal::MSI-1, 1, 0, XLAT("which coefficient"), "");
else if(sym == 'r') rotation += (shiftmul > 0 ? 1:3); else if(sym == 'r') rotation += (shiftmul > 0 ? 1:3);
else if(sym == 'a') else if(sym == 'a')
dialog::editNumber(lvspeed, -5, 5, .1, 1, XLAT("animation speed"), ""); dialog::editNumber(lvspeed, -5, 5, .1, 1, XLAT("animation speed"), "");
@ -654,7 +661,6 @@ namespace conformal {
#ifndef NOSDL #ifndef NOSDL
else if(uni == 'f' && pmodel == mdBand && on) createImage(dospiral); else if(uni == 'f' && pmodel == mdBand && on) createImage(dospiral);
#endif #endif
else if(sym == 'q' || sym == SDLK_ESCAPE || sym == '0') { cmode = emNormal; }
else if(sym == 'i') { else if(sym == 'i') {
if(canmove && !cheater) { if(canmove && !cheater) {
addMessage("Enable cheat mode or GAME OVER to use this"); addMessage("Enable cheat mode or GAME OVER to use this");
@ -666,6 +672,7 @@ namespace conformal {
else if(sym == 'j') { else if(sym == 'j') {
autobandhistory = !autobandhistory; autobandhistory = !autobandhistory;
} }
else if(doexiton(sym, uni)) popScreen();
} }
void restore() { void restore() {
@ -718,5 +725,15 @@ namespace conformal {
pmodel = spm; pmodel = spm;
includeHistory = ih; includeHistory = ih;
#endif #endif
} }
auto hooks = addHook(clearmemory, 0, [] () {
conformal::renderAutoband();
conformal::on = false;
conformal::killhistory.clear();
conformal::findhistory.clear();
conformal::movehistory.clear();
conformal::includeHistory = false;
});
} }

778
control.cpp Normal file
View File

@ -0,0 +1,778 @@
int frames;
bool outoffocus = false;
int mousex, mousey, joyx, joyy, panjoyx, panjoyy;
hyperpoint mouseh, mouseoh;
bool leftclick, rightclick, targetclick, hiliteclick, anyshiftclick, wheelclick,
forcetarget, lshiftclick, lctrlclick;
bool gtouched;
int getcstat, lgetcstat; ld getcshift; bool inslider;
int andmode = 0;
// is the player using mouse? (used for auto-cross)
bool mousing = true;
// is the mouse button pressed?
bool mousepressed = false;
bool mousemoved = false;
bool actonrelease = false;
ld shiftmul = 1;
cell *mouseover, *mouseover2, *lmouseover, *centerover;
ld modist, modist2, centdist;
movedir mousedest, joydir;
int lastt;
#ifdef WEB
Uint8 *SDL_GetKeyState(void *v) { static Uint8 tab[1024]; return tab; }
#endif
bool quitsaves() { return (items[itOrbSafety] && havesave); }
bool needConfirmation() {
return canmove && (gold() >= 30 || tkills() >= 50) && !cheater && !quitsaves();
}
bool mouseout() {
if((getcstat != '-' && getcstat) || (lgetcstat && lgetcstat != '-')) return true;
return outofmap(mouseh);
}
bool mouseout2() {
if((getcstat && getcstat != '-') || (lgetcstat && lgetcstat != '-')) return true;
return outofmap(mouseh) || outofmap(mouseoh);
}
movedir vectodir(const hyperpoint& P) {
hyperpoint H = sphereflip * tC0(cwtV);
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
transmatrix Centered = sphereflip * cwtV;
if(!euclid)
Centered = gpushxto0(H) * Centered;
else if(R > 1e-9)
Centered = eupush(-H[0], -H[1]) * Centered;
ld binv = 99;
ld dirdist[7];
for(int i=0; i<cwt.c->type; i++) {
dirdist[i] = intval(Centered * xspinpush0(-i * 2 * M_PI /cwt.c->type, .5), P);
}
movedir res;
res.d = -1;
for(int i=0; i<cwt.c->type; i++) {
if(dirdist[i] < binv) {
binv = dirdist[i];
res.d = i;
res.subdir = dirdist[(i+1)%cwt.c->type] < dirdist[(i+cwt.c->type-1)%cwt.c->type] ? 1 : -1;
if(sphere) res.subdir = -res.subdir;
}
}
// if(euclid) bdir = (bdir + 3) % 6;
return res;
}
void movepckeydir(int d) {
DEBB(DF_GRAPH, (debugfile,"movepckeydir\n"));
// EUCLIDEAN
movedir md =
vectodir(spin(-d * M_PI/4) * tC0(pushone()));
movepcto(md);
}
void calcMousedest() {
if(mouseout()) return;
if(vid.revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
ld mousedist = intval(mouseh, tC0(shmup::ggmatrix(cwt.c)));
mousedest.d = -1;
cellwalker bcwt = cwt;
ld dists[7];
for(int i=0; i<cwt.c->type; i++)
dists[i] = intval(mouseh, tC0(shmup::ggmatrix(cwt.c->mov[i])));
/* printf("curcell = %Lf\n", mousedist);
for(int i=0; i<cwt.c->type; i++)
printf("d%d = %Lf\n", i, dists[i]); */
for(int i=0; i<cwt.c->type; i++) if(dists[i] < mousedist) {
mousedist = dists[i];
mousedest.d = fixdir(i - cwt.spin, cwt.c);
mousedest.subdir =
dists[(i+1)%cwt.c->type] < dists[(i+cwt.c->type-1)%cwt.c->type] ? 1 : -1;
if(cwt.mirrored)
mousedest.d = fixdir(-mousedest.d, cwt.c),
mousedest.subdir = -mousedest.subdir;
if(sphere) mousedest.subdir = -mousedest.subdir;
}
if(vid.revcontrol == true) { mouseh[0] = -mouseh[0]; mouseh[1] = -mouseh[1]; }
cwt = bcwt;
}
void mousemovement() {
calcMousedest();
movepcto(mousedest);
lmouseover = NULL;
}
#ifndef NOSDL
SDL_Joystick* sticks[8];
int numsticks;
void initJoysticks() {
DEBB(DF_INIT, (debugfile,"init joysticks\n"));
numsticks = SDL_NumJoysticks();
if(numsticks > 8) numsticks = 8;
for(int i=0; i<numsticks; i++) {
sticks[i] = SDL_JoystickOpen(i);
/* printf("axes = %d, balls = %d, buttons = %d, hats = %d\n",
SDL_JoystickNumAxes(sticks[i]),
SDL_JoystickNumBalls(sticks[i]),
SDL_JoystickNumButtons(sticks[i]),
SDL_JoystickNumHats(sticks[i])
); */
}
}
void closeJoysticks() {
DEBB(DF_INIT, (debugfile,"close joysticks\n"));
for(int i=0; i<numsticks; i++) {
SDL_JoystickClose(sticks[i]), sticks[i] = NULL;
}
numsticks = 0;
}
void checkjoy() {
DEBB(DF_GRAPH, (debugfile,"check joy\n"));
if(!DEFAULTCONTROL) return;
ld joyvalue1 = sqr(vid.joyvalue);
ld joyvalue2 = sqr(vid.joyvalue2);
ld jx = joyx;
ld jy = joyy;
ld sq = jx*jx+jy*jy;
static int laststate = 0;
int curstate = sq < joyvalue1 ? 0 : sq < joyvalue2 ? 1 : 2;
if(curstate != laststate) flashMessages(), laststate = curstate;
if(autojoy) {
if(sq < joyvalue1) { if(joydir.d >= 0) movepcto(joydir); joydir.d = -1; return; }
if(sq < joyvalue2 && joydir.d == -1) return;
}
else {
if(sq < joyvalue1) { joydir.d = -1; return; }
}
joydir = vectodir(hpxy(jx, jy));
}
void checkpanjoy(double t) {
if(shmup::on) return;
if(vid.joypanspeed < 1e-7) return;
if(sqr(panjoyx) + sqr(panjoyy) < sqr(vid.joypanthreshold))
return;
ld jx = panjoyx * t * vid.joypanspeed;
ld jy = panjoyy * t * vid.joypanspeed;
playermoved = false;
View = gpushxto0(hpxy(jx, jy)) * View;
}
#endif
bool quitmainloop = false;
bool doexiton(int sym, int uni) {
if(sym == SDLK_ESCAPE) return true;
if(sym == SDLK_F10) return true;
if(uni != 0) return true;
return false;
}
void handleKeyQuit(int sym, int uni) {
dialog::handleNavigation(sym, uni);
// ignore the camera movement keys
#ifndef NORUG
if(rug::rugged && (sym == SDLK_UP || sym == SDLK_DOWN || sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN ||
sym == SDLK_RIGHT || sym == SDLK_LEFT))
sym = 0;
#endif
if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER || sym == SDLK_F10) quitmainloop = true;
else if(uni == 'r' || sym == SDLK_F5) {
restartGame(), popScreen();
msgs.clear();
}
else if(sym == SDLK_UP || sym == SDLK_KP8 || sym == PSEUDOKEY_WHEELUP) msgscroll++;
else if(sym == SDLK_DOWN || sym == SDLK_KP2 || sym == PSEUDOKEY_WHEELDOWN) msgscroll--;
else if(sym == SDLK_PAGEUP || sym == SDLK_KP9) msgscroll+=5;
else if(sym == SDLK_PAGEDOWN || sym == SDLK_KP3) msgscroll-=5;
else if(uni == 'v') popScreenAll(), pushScreen(showMainMenu);
else if(sym == SDLK_F3 || (sym == ' ' || sym == SDLK_HOME))
fullcenter();
else if(uni == 'o' && DEFAULTNOR(sym)) setAppropriateOverview();
#ifdef INV
else if(uni == 'i' && DEFAULTNOR(sym) && inv::on)
pushScreen(inv::show);
#endif
#ifndef NOSAVE
else if(uni == 't') {
if(!canmove) restartGame();
loadScores();
msgs.clear();
}
#endif
else if(doexiton(sym, uni) && !didsomething) {
popScreen();
msgscroll = 0;
msgs.clear();
if(!canmove) {
addMessage(XLAT("GAME OVER"));
addMessage(timeline());
}
}
}
bool didsomething;
#ifdef MOBILE
typedef int eventtype;
#else
typedef SDL_Event eventtype;
#endif
void handlePanning(int sym, int uni) {
if(rug::rugged) return;
#ifndef PANDORA
if(sym == SDLK_RIGHT) {
if(conformal::on)
conformal::lvspeed += 0.1 * shiftmul;
else
View = xpush(-0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
if(sym == SDLK_LEFT) {
if(conformal::on)
conformal::lvspeed -= 0.1 * shiftmul;
else
View = xpush(+0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
if(sym == SDLK_UP) {
if(conformal::on)
conformal::lvspeed += 0.1 * shiftmul;
else
View = ypush(+0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
if(sym == SDLK_DOWN) {
if(conformal::on)
conformal::lvspeed -= 0.1 * shiftmul;
else
View = ypush(-0.2*shiftmul) * View, playermoved = false, didsomething = true;
}
#endif
if(sym == SDLK_PAGEUP) {
if(conformal::on)
conformal::rotation++;
else
View = spin(M_PI/S21*shiftmul) * View, didsomething = true;
}
if(sym == SDLK_PAGEDOWN) {
if(conformal::on)
conformal::rotation++;
else
View = spin(-M_PI/S21*shiftmul) * View, didsomething = true;
}
if(sym == SDLK_PAGEUP || sym == SDLK_PAGEDOWN)
if(isGravityLand(cwt.c->land)) playermoved = false;
if(sym == PSEUDOKEY_WHEELUP) {
ld jx = (mousex - vid.xcenter - .0) / vid.radius / 10;
ld jy = (mousey - vid.ycenter - .0) / vid.radius / 10;
playermoved = false;
View = gpushxto0(hpxy(jx, jy)) * View;
sym = 1;
}
}
void handleKeyNormal(int sym, int uni) {
if(cheater) {
if(applyCheat(uni, mouseover))
sym = 0;
}
if(DEFAULTNOR(sym)) handlePanning(sym, uni);
if(!(uni >= 'A' && uni <= 'Z') && DEFAULTCONTROL) {
if(sym == 'l' || sym == 'd' || sym == SDLK_KP6) movepckeydir(0);
if(sym == 'n' || sym == 'c' || sym == SDLK_KP3) movepckeydir(1);
if(sym == 'j' || sym == 'x' || sym == SDLK_KP2) movepckeydir(2);
if(sym == 'b' || sym == 'z' || sym == SDLK_KP1) movepckeydir(3);
if(sym == 'h' || sym == 'a' || sym == SDLK_KP4) movepckeydir(4);
if(sym == 'y' || sym == 'q' || sym == SDLK_KP7) movepckeydir(5);
if(sym == 'k' || sym == 'w' || sym == SDLK_KP8) movepckeydir(6);
if(sym == 'u' || sym == 'e' || sym == SDLK_KP9) movepckeydir(7);
}
#ifdef PANDORA
if(DEFAULTCONTROL) {
if(sym == SDLK_RIGHT) movepckeydir(0);
if(sym == SDLK_LEFT) movepckeydir(4);
if(sym == SDLK_DOWN) movepckeydir(2 + (leftclick?1:0) - (rightclick?1:0));
if(sym == SDLK_UP) movepckeydir(6 - (leftclick?1:0) + (rightclick?1:0));
}
#endif
if(uni == sym && DEFAULTNOR(sym)) {
gmodekeys(sym, uni);
if(sym == '8') {
backcolor = backcolor ^ 0xFFFFFF;
bordcolor = bordcolor ^ 0xFFFFFF;
forecolor = forecolor ^ 0xFFFFFF;
printf("back = %x\n", backcolor);
}
if(sym == '9') {
pmodel = eModel(8 - pmodel);
// vid.yshift = 1 - vid.yshift;
// vid.drawmousecircle = true;
}
if(sym == 'm' && canmove && cmode2 == smNormal && (centerover == cwt.c ? mouseover : centerover))
performMarkCommand(mouseover);
}
if(DEFAULTCONTROL) {
if(sym == '.' || sym == 's') movepcto(-1, 1);
if((sym == SDLK_DELETE || sym == SDLK_KP_PERIOD || sym == 'g') && uni != 'G' && uni != 'G'-64)
movepcto(MD_DROP, 1);
if(sym == 't' && uni != 'T' && uni != 'T'-64 && canmove && cmode2 == smNormal) {
if(playermoved && items[itStrongWind]) {
cell *c = whirlwind::jumpDestination(cwt.c);
if(c) centerover = c;
}
targetRangedOrb(centerover, roKeyboard);
sym = 0; uni = 0;
}
}
if(sym == SDLK_KP5 && DEFAULTCONTROL) movepcto(-1, 1);
// if(sym == SDLK_F4) restartGameSwitchEuclid();
if(sym == SDLK_F5) {
if(needConfirmation())
pushScreen(showMission);
else restartGame();
}
if(sym == SDLK_ESCAPE)
showMissionScreen();
if(sym == SDLK_F10) {
if(needConfirmation()) pushScreen(showMission);
else quitmainloop = true;
}
if(!canmove) {
if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER) quitmainloop = true;
else if(uni == 'r') restartGame();
#ifndef NOSAVE
else if(uni == 't') {
restartGame();
loadScores();
}
#endif
#ifndef NORUG
else if(rug::rugged) ;
#endif
else if(sym == SDLK_UP || sym == SDLK_KP8) msgscroll++;
else if(sym == SDLK_DOWN || sym == SDLK_KP2) msgscroll--;
else if(sym == SDLK_PAGEUP || sym == SDLK_KP9) msgscroll+=5;
else if(sym == SDLK_PAGEDOWN || sym == SDLK_KP3) msgscroll-=5;
}
if(uni == 'o' && DEFAULTNOR(sym)) setAppropriateOverview();
#ifdef INV
if(uni == 'i' && DEFAULTNOR(sym) && inv::on)
pushScreen(inv::show);
#endif
if((sym == SDLK_HOME || sym == SDLK_F3 || sym == ' ') && DEFAULTNOR(sym))
fullcenter();
if(sym == 'v' && DEFAULTNOR(sym))
pushScreen(showMainMenu);
if(sym == '-' || sym == PSEUDOKEY_WHEELDOWN) {
actonrelease = false;
shmup::cpid = 0;
if(mouseover &&
targetclick && (!shmup::on || numplayers() == 1) && targetRangedOrb(mouseover, forcetarget ? roMouseForce : roMouse)) {
}
else if(forcetarget)
;
else if(!DEFAULTCONTROL) {
if(!shmup::on)
multi::mousemovement(mouseover);
}
else mousemovement();
}
if(sym == SDLK_F1) gotoHelp(help);
#ifdef ROGUEVIZ
rogueviz::processKey(sym, uni);
#endif
}
void handlekey(int sym, int uni) {
if(callhandlers(false, hooks_handleKey, sym, uni)) return;
keyhandler(sym, uni);
}
#ifdef NOSDL
void mainloopiter() { printf("(compiled without SDL -- no action)\n"); quitmainloop = true; }
#else
// Warning: a very long function! todo: refactor
int cframelimit = 1000;
void mainloopiter() {
DEBB(DF_GRAPH, (debugfile,"main loop\n"));
#ifndef GFX
#ifndef GL
vid.wallmode = 0;
vid.monmode = 0;
#endif
#endif
optimizeview();
if(conformal::on) conformal::apply();
ticks = SDL_GetTicks();
int timetowait = lastt + 1000 / cframelimit - ticks;
cframelimit = vid.framelimit;
if(outoffocus && cframelimit > 10) cframelimit = 10;
if(DOSHMUP && cmode2 == smNormal)
timetowait = 0, shmup::turn(ticks - lastt);
if(!DOSHMUP && (multi::alwaysuse || multi::players > 1) && cmode2 == smNormal)
timetowait = 0, multi::handleMulti(ticks - lastt);
if(vid.sspeed >= 5 && gmatrix.count(cwt.c) && !elliptic) {
cwtV = gmatrix[cwt.c] * ddspin(cwt.c, cwt.spin);
if(cwt.mirrored) playerV = playerV * Mirror;
}
#ifdef WEB
if(playermoved && vid.sspeed > -4.99 && !outoffocus) {
centerpc((ticks - lastt) / 1000.0 * exp(vid.sspeed));
}
if(!outoffocus) drawscreen();
#else
if(timetowait > 0)
SDL_Delay(timetowait);
else {
if(cmode2 == smNormal) {
if(playermoved && vid.sspeed > -4.99 && !outoffocus)
centerpc((ticks - lastt) / 1000.0 * exp(vid.sspeed));
if(panjoyx || panjoyy)
checkpanjoy((ticks - lastt) / 1000.0);
}
tortoise::updateVals(ticks - lastt);
frames++;
if(!outoffocus) {
drawscreen();
}
lastt = ticks;
}
#endif
Uint8 *keystate = SDL_GetKeyState(NULL);
rightclick = keystate[SDLK_RCTRL];
leftclick = keystate[SDLK_RSHIFT];
lctrlclick = keystate[SDLK_LCTRL];
lshiftclick = keystate[SDLK_LSHIFT];
forcetarget = (keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]);
hiliteclick = keystate[SDLK_LALT] | keystate[SDLK_RALT];
anyshiftclick = keystate[SDLK_LSHIFT] | keystate[SDLK_RSHIFT];
wheelclick = false;
getcshift = 1;
if(keystate[SDLK_LSHIFT] || keystate[SDLK_RSHIFT]) getcshift = -1;
if(keystate[SDLK_LCTRL] || keystate[SDLK_RCTRL]) getcshift /= 10;
if(keystate[SDLK_LALT] || keystate[SDLK_RALT]) getcshift *= 10;
didsomething = false;
if(vid.shifttarget&1) {
leftclick = false;
targetclick = keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT];
}
else {
leftclick = keystate[SDLK_RSHIFT];
targetclick = true;
}
#ifdef SDLAUDIO
if(audio) handlemusic();
#endif
SDL_Event ev;
DEBB(DF_GRAPH, (debugfile,"polling for events\n"));
achievement_pump();
while(SDL_PollEvent(&ev)) {
DEBB(DF_GRAPH, (debugfile,"got event type #%d\n", ev.type));
int sym = 0;
int uni = 0;
shiftmul = 1;
/* if(ev.type == SDL_JOYDEVICEADDED || ev.type == SDL_JOYDEVICEREMOVED) {
joyx = joyy = 0;
panjoyx = panjoyy = 0;
closeJoysticks();
initJoysticks();
}*/
if(ev.type == SDL_ACTIVEEVENT) {
if(ev.active.state & SDL_APPINPUTFOCUS) {
if(ev.active.gain) {
outoffocus = false;
}
else {
outoffocus = true;
}
}
}
if(ev.type == SDL_VIDEORESIZE) {
vid.xres = ev.resize.w;
vid.yres = ev.resize.h;
vid.killreduction = 0;
extern bool setfsize;
setfsize = true;
setvideomode();
#ifdef GL
if(vid.usingGL) glViewport(0, 0, vid.xres, vid.yres);
#endif
}
if(ev.type == SDL_VIDEOEXPOSE) {
drawscreen();
}
if(ev.type == SDL_JOYAXISMOTION) {
if(ev.jaxis.which == 0) {
if(ev.jaxis.axis == 0)
joyx = ev.jaxis.value;
else if(ev.jaxis.axis == 1)
joyy = ev.jaxis.value;
else if(ev.jaxis.axis == 3)
panjoyx = ev.jaxis.value;
else if(ev.jaxis.axis == 4)
panjoyy = ev.jaxis.value;
checkjoy();
// printf("panjoy = %d,%d\n", panjoyx, panjoyy);
}
else {
if(ev.jaxis.axis == 0)
panjoyx = ev.jaxis.value;
else
panjoyy = ev.jaxis.value;
}
}
if(ev.type == SDL_JOYBUTTONDOWN && cmode2 == smShmupConfig && vid.scfg.setwhat) {
int joyid = ev.jbutton.which;
int button = ev.jbutton.button;
if(joyid < 8 && button < 32)
vid.scfg.joyaction[joyid][button] = vid.scfg.setwhat;
vid.scfg.setwhat = 0;
}
else if(ev.type == SDL_JOYHATMOTION && cmode2 == smShmupConfig && vid.scfg.setwhat) {
int joyid = ev.jhat.which;
int hat = ev.jhat.hat;
int dir = 4;
if(ev.jhat.value == SDL_HAT_UP) dir = 0;
if(ev.jhat.value == SDL_HAT_RIGHT) dir = 1;
if(ev.jhat.value == SDL_HAT_DOWN) dir = 2;
if(ev.jhat.value == SDL_HAT_LEFT) dir = 3;
if(joyid < 8 && hat < 4 && dir < 4) {
vid.scfg.hataction[joyid][hat][dir] = vid.scfg.setwhat;
vid.scfg.setwhat = 0;
}
}
else if(ev.type == SDL_JOYBUTTONDOWN && DEFAULTCONTROL) {
flashMessages();
movepcto(joydir);
checkjoy();
}
if(ev.type == SDL_KEYDOWN) {
flashMessages();
mousing = false;
sym = ev.key.keysym.sym;
uni = ev.key.keysym.unicode;
if(ev.key.keysym.mod & (KMOD_LSHIFT | KMOD_RSHIFT)) shiftmul = -1;
if(ev.key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) shiftmul /= 10;
if(sym == SDLK_RETURN && (ev.key.keysym.mod & (KMOD_LALT | KMOD_RALT))) {
sym = 0; uni = 0;
switchFullscreen();
}
}
dialog::handleZooming(ev);
if(sym == SDLK_F1 && cmode2 == smNormal && playermoved)
help = "@";
bool rollchange =
cmode2 == smOverview && getcstat >= 2000 && cheater;
if(ev.type == SDL_MOUSEBUTTONDOWN) {
flashMessages();
mousepressed = true;
mousing = true;
actonrelease = true;
if(ev.button.button==SDL_BUTTON_WHEELDOWN) {
sym = uni = PSEUDOKEY_WHEELDOWN;
}
if(ev.button.button==SDL_BUTTON_WHEELUP) {
sym = uni = PSEUDOKEY_WHEELUP;
}
else if(ev.button.button == SDL_BUTTON_RIGHT) {
sym = 1; didsomething = true;
}
else if(ev.button.button == SDL_BUTTON_MIDDLE) {
sym = 2; didsomething = true;
}
}
if(ev.type == SDL_MOUSEBUTTONUP) {
mousepressed = false;
mousing = true;
if(ev.button.button==SDL_BUTTON_RIGHT || leftclick)
sym = SDLK_F1;
else if(ev.button.button==SDL_BUTTON_MIDDLE || rightclick)
sym = 1, didsomething = true;
else if(ev.button.button == SDL_BUTTON_LEFT && actonrelease) {
sym = getcstat, uni = getcstat, shiftmul = getcshift;
}
else if(ev.button.button == SDL_BUTTON_WHEELUP && rollchange) {
sym = getcstat, uni = getcstat, shiftmul = getcshift, wheelclick = true;
}
else if(ev.button.button == SDL_BUTTON_WHEELDOWN && rollchange) {
sym = getcstat, uni = getcstat, shiftmul = -getcshift, wheelclick = true;
}
}
if(ev.type == SDL_MOUSEMOTION) {
mouseoh = mouseh;
mousing = true;
mousemoved = true;
mousex = ev.motion.x;
mousey = ev.motion.y;
#ifndef NORUG
if(rug::rugged)
mouseh = rug::gethyper(mousex, mousey);
else
#endif
mouseh = gethyper(mousex, mousey);
if((rightclick || (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_MMASK)) && !mouseout2() &&
mouseh[2] < 50 && mouseoh[2] < 50) {
panning(mouseoh, mouseh);
}
#ifdef SIMULATE_JOYSTICK
// pretend that both joysticks are present
stick = panstick = (SDL_Joystick*) (&vid);
panjoyx = 20 * (mousex - vid.xcenter);
panjoyy = 20 * (mousey - vid.ycenter);
checkjoy();
#endif
if(mousepressed && inslider) {
sym = getcstat, uni = getcstat, shiftmul = getcshift;
}
}
if(ev.type == SDL_QUIT) {
if(needConfirmation() && cmode2 != smMission) showMissionScreen();
else quitmainloop = true;
}
handlekey(sym, uni);
}
}
#endif
void mainloop() {
lastt = 0;
#ifdef WEB
initweb();
emscripten_set_main_loop(mainloopiter, 0, true);
#else
while(!quitmainloop) mainloopiter();
#endif
}
#ifdef MOBILE
void displayabutton(int px, int py, string s, int col) {
// TMP
int siz = vid.yres > vid.xres ? vid.fsize*2 : vid.fsize * 3/2;
int vrx = min(vid.radius, vid.xres/2 - 40);
int vry = min(vid.radius, min(vid.ycenter, vid.yres - vid.ycenter) - 20);
int x = vid.xcenter + px * vrx;
int y = vid.ycenter + py * (vry - siz/2);
int vrr = int(hypot(vrx, vry) * sqrt(2.));
if(gtouched && !mouseover
&& abs(mousex - vid.xcenter) < vrr
&& abs(mousey - vid.ycenter) < vrr
&& hypot(mousex-vid.xcenter, mousey-vid.ycenter) > vrr
&& px == (mousex > vid.xcenter ? 1 : -1)
&& py == (mousey > vid.ycenter ? 1 : -1)
) col = 0xFF0000;
if(displayfr(x, y, 0, siz, s, col, 8+8*px))
buttonclicked = true;
}
#endif

View File

@ -386,7 +386,53 @@ namespace dialog {
int colorp = 0; int colorp = 0;
void drawColorDialog(int color) { int *colorPointer;
bool handleKeyColor(int sym, int uni) {
int& color = *colorPointer;
if(uni >= 'A' && uni <= 'D') {
int x = (mousex - vid.xres/4) * 510 / vid.xres;
if(x < 0) x = 0;
if(x > 255) x = 255;
unsigned char* pts = (unsigned char*) &color;
pts[uni - 'A'] = x;
}
else if(uni == ' ') {
bool inHistory = false;
for(int i=0; i<10; i++) if(colorhistory[i] == (unsigned) color)
inHistory = true;
if(!inHistory) { colorhistory[lch] = color; lch++; lch %= 10; }
popScreen();
}
else if(uni >= '0' && uni <= '9') {
color = colorhistory[uni - '0'];
}
else if(palette && uni >= 'a' && uni < 'a'+(int) palette[0]) {
color = palette[1 + uni - 'a'];
}
else if(sym == SDLK_DOWN || sym == SDLK_KP2) {
colorp = (colorp-1) & 3;
}
else if(sym == SDLK_UP || sym == SDLK_KP8) {
colorp = (colorp+1) & 3;
}
else if(sym == SDLK_LEFT || sym == SDLK_KP4) {
unsigned char* pts = (unsigned char*) &color;
pts[colorp] -= abs(shiftmul) < .6 ? 1 : 17;
}
else if(sym == SDLK_RIGHT || sym == SDLK_KP6) {
unsigned char* pts = (unsigned char*) &color;
pts[colorp] += abs(shiftmul) < .6 ? 1 : 17;
}
else if(doexiton(sym, uni))
popScreen();
return false;
}
void drawColorDialog() {
int color = *colorPointer;
int ash = 8; int ash = 8;
for(int j=0; j<10; j++) { for(int j=0; j<10; j++) {
@ -427,60 +473,13 @@ namespace dialog {
} }
displayColorButton(vid.xres/2, vid.yres/2+vid.fsize * 6, XLAT("select this color") + " : " + itsh(color), ' ', 8, 0, color >> ash); displayColorButton(vid.xres/2, vid.yres/2+vid.fsize * 6, XLAT("select this color") + " : " + itsh(color), ' ', 8, 0, color >> ash);
keyhandler = handleKeyColor;
} }
// 0: nothing happened, 1: color accepted, 2: break
int handleKeyColor(int sym, int uni, int& color) {
if(uni >= 'A' && uni <= 'D') {
int x = (mousex - vid.xres/4) * 510 / vid.xres;
if(x < 0) x = 0;
if(x > 255) x = 255;
unsigned char* pts = (unsigned char*) &color;
pts[uni - 'A'] = x;
}
else if(uni == ' ') {
bool inHistory = false;
for(int i=0; i<10; i++) if(colorhistory[i] == (unsigned) color)
inHistory = true;
if(!inHistory) { colorhistory[lch] = color; lch++; lch %= 10; }
return 1;
}
else if(uni >= '0' && uni <= '9') {
color = colorhistory[uni - '0'];
}
else if(palette && uni >= 'a' && uni < 'a'+(int) palette[0]) {
color = palette[1 + uni - 'a'];
}
else if(sym == SDLK_DOWN || sym == SDLK_KP2) {
colorp = (colorp-1) & 3;
}
else if(sym == SDLK_UP || sym == SDLK_KP8) {
colorp = (colorp+1) & 3;
}
else if(sym == SDLK_LEFT || sym == SDLK_KP4) {
unsigned char* pts = (unsigned char*) &color;
pts[colorp] -= abs(shiftmul) < .6 ? 1 : 17;
}
else if(sym == SDLK_RIGHT || sym == SDLK_KP6) {
unsigned char* pts = (unsigned char*) &color;
pts[colorp] += abs(shiftmul) < .6 ? 1 : 17;
}
else if(uni || sym == SDLK_F10) return 2;
return 0;
}
int *colorPointer;
emtype lastmode;
void openColorDialog(int& col, unsigned int *pal) { void openColorDialog(int& col, unsigned int *pal) {
colorPointer = &col; palette = pal; colorPointer = &col; palette = pal;
lastmode = cmode; cmode = emColor; pushScreen(drawColorDialog);
}
void handleColor(int sym, int uni) {
int ret = handleKeyColor(sym, uni, *colorPointer);
if(ret) cmode = lastmode;
} }
struct numberEditor { struct numberEditor {
@ -511,28 +510,6 @@ namespace dialog {
ne.positive = true; ne.positive = true;
} }
void editNumber(ld& x, ld vmin, ld vmax, ld step, ld dft, string title, string help) {
ne.editwhat = &x;
ne.s = fts(x);
ne.vmin = vmin;
ne.vmax = vmax;
ne.step = step;
ne.dft = dft;
ne.title = title;
ne.help = help;
lastmode = cmode; cmode = emNumber;
ne.scale = ne.inverse_scale = identity;
ne.intval = NULL;
ne.positive = false;
sidedialog = false;
}
void editNumber(int& x, int vmin, int vmax, int step, int dft, string title, string help) {
editNumber(ne.intbuf, vmin, vmax, step, dft, title, help);
ne.intbuf = x; ne.intval = &x; ne.s = its(x);
sidedialog = true;
}
string disp(ld x) { if(ne.intval) return its((int) (x+.5)); else return fts(x); } string disp(ld x) { if(ne.intval) return its((int) (x+.5)); else return fts(x); }
void affect(char kind) { void affect(char kind) {
@ -603,14 +580,14 @@ namespace dialog {
if(ne.intval == &polygonal::coefid && polygonal::coefid < 0) if(ne.intval == &polygonal::coefid && polygonal::coefid < 0)
*ne.editwhat = *ne.intval = 0, affect('v'); *ne.editwhat = *ne.intval = 0, affect('v');
if(ne.intval == &polygonal::coefid && polygonal::coefid >= MSI) if(ne.intval == &polygonal::coefid && polygonal::coefid >= polygonal::MSI)
*ne.editwhat = *ne.intval = MSI-1, affect('v'); *ne.editwhat = *ne.intval = polygonal::MSI-1, affect('v');
if(ne.intval == &polygonal::deg && polygonal::deg < 0) if(ne.intval == &polygonal::deg && polygonal::deg < 0)
*ne.editwhat = *ne.intval = MSI-1, affect('v'); *ne.editwhat = *ne.intval = polygonal::MSI-1, affect('v');
if(ne.intval == &polygonal::deg && polygonal::deg >= MSI) if(ne.intval == &polygonal::deg && polygonal::deg >= polygonal::MSI)
*ne.editwhat = *ne.intval = MSI-1, affect('v'); *ne.editwhat = *ne.intval = polygonal::MSI-1, affect('v');
if(ne.intval == &polygonal::SI) polygonal::solve(); if(ne.intval == &polygonal::SI) polygonal::solve();
if(ne.editwhat == &polygonal::STAR) polygonal::solve(); if(ne.editwhat == &polygonal::STAR) polygonal::solve();
@ -623,13 +600,15 @@ namespace dialog {
if(ne.editwhat == &geom3::middetail && geom3::highdetail > geom3::middetail) if(ne.editwhat == &geom3::middetail && geom3::highdetail > geom3::middetail)
geom3::highdetail = geom3::middetail; geom3::highdetail = geom3::middetail;
if(lastmode == em3D) buildpolys(); buildpolys();
#ifdef GL #ifdef GL
if(lastmode == em3D) resetGL(); resetGL();
#endif #endif
} }
void drawNumberDialog() { void drawNumberDialog() {
gamescreen(sidedialog ? 0 : 2);
cmode2 = smNumber;
init(ne.title); init(ne.title);
addInfo(ne.s); addInfo(ne.s);
addSlider(ne.scale(ne.vmin), ne.scale(*ne.editwhat), ne.scale(ne.vmax), 500); addSlider(ne.scale(ne.vmin), ne.scale(*ne.editwhat), ne.scale(ne.vmax), 500);
@ -644,7 +623,7 @@ namespace dialog {
addBreak(100); addBreak(100);
if(lastmode == em3D) ne.help = explain3D(ne.editwhat); ne.help = explain3D(ne.editwhat);
if(ne.help != "") { if(ne.help != "") {
addHelp(ne.help); addHelp(ne.help);
@ -671,69 +650,67 @@ namespace dialog {
addBoolItem("finer lines at the boundary", vid.antialias & AA_LINEWIDTH, 'o'); addBoolItem("finer lines at the boundary", vid.antialias & AA_LINEWIDTH, 'o');
display(); display();
}
void handleNumber(int sym, int uni) { keyhandler = [] (int sym, int uni) {
handleNavigation(sym, uni); handleNavigation(sym, uni);
if((uni >= '0' && uni <= '9') || (uni == '.' && !ne.intval) || (uni == '-' && !ne.positive)) { if((uni >= '0' && uni <= '9') || (uni == '.' && !ne.intval) || (uni == '-' && !ne.positive)) {
ne.s += uni; ne.s += uni;
affect('s'); affect('s');
} }
else if(uni == '\b' || uni == '\t') { else if(uni == '\b' || uni == '\t') {
ne.s = ne.s. substr(0, size(ne.s)-1); ne.s = ne.s. substr(0, size(ne.s)-1);
sscanf(ne.s.c_str(), LDF, ne.editwhat); sscanf(ne.s.c_str(), LDF, ne.editwhat);
affect('s'); affect('s');
} }
#ifndef MOBILE #ifndef MOBILE
else if(sym == SDLK_RIGHT || sym == SDLK_KP6) { else if(sym == SDLK_RIGHT || sym == SDLK_KP6) {
if(ne.intval && abs(shiftmul) < .6) if(ne.intval && abs(shiftmul) < .6)
(*ne.editwhat)++; (*ne.editwhat)++;
else else
*ne.editwhat = ne.inverse_scale(ne.scale(*ne.editwhat) + shiftmul * ne.step); *ne.editwhat = ne.inverse_scale(ne.scale(*ne.editwhat) + shiftmul * ne.step);
affect('v'); affect('v');
} }
else if(sym == SDLK_LEFT || sym == SDLK_KP4) { else if(sym == SDLK_LEFT || sym == SDLK_KP4) {
if(ne.intval && abs(shiftmul) < .6) if(ne.intval && abs(shiftmul) < .6)
(*ne.editwhat)--; (*ne.editwhat)--;
else else
*ne.editwhat = ne.inverse_scale(ne.scale(*ne.editwhat) - shiftmul * ne.step); *ne.editwhat = ne.inverse_scale(ne.scale(*ne.editwhat) - shiftmul * ne.step);
affect('v'); affect('v');
} }
#endif #endif
else if(sym == SDLK_HOME) { else if(sym == SDLK_HOME) {
*ne.editwhat = ne.dft; *ne.editwhat = ne.dft;
affect('v'); affect('v');
} }
else if(uni == 500) { else if(uni == 500) {
int sl, sr; int sl, sr;
if(sidescreen) if(sidescreen)
sl = vid.yres + vid.fsize*2, sr = vid.xres - vid.fsize*2; sl = vid.yres + vid.fsize*2, sr = vid.xres - vid.fsize*2;
else else
sl = vid.xres/4, sr = vid.xres*3/4; sl = vid.xres/4, sr = vid.xres*3/4;
ld d = (mousex - sl + .0) / (sr-sl); ld d = (mousex - sl + .0) / (sr-sl);
*ne.editwhat = *ne.editwhat =
ne.inverse_scale(d * (ne.scale(ne.vmax) - ne.scale(ne.vmin)) + ne.scale(ne.vmin)); ne.inverse_scale(d * (ne.scale(ne.vmax) - ne.scale(ne.vmin)) + ne.scale(ne.vmin));
affect('v'); affect('v');
} }
else if(uni == 'o' && ne.editwhat == &ne.intbuf && ne.intval == &sightrange && cheater) else if(uni == 'o' && ne.editwhat == &ne.intbuf && ne.intval == &sightrange && cheater)
overgenerate = !overgenerate; overgenerate = !overgenerate;
else if(uni == 'o' && ne.editwhat == &vid.linewidth) else if(uni == 'o' && ne.editwhat == &vid.linewidth)
vid.antialias ^= AA_LINEWIDTH; vid.antialias ^= AA_LINEWIDTH;
else if(uni == 'p' && ne.editwhat == &vid.alpha) { else if(uni == 'p' && ne.editwhat == &vid.alpha) {
*ne.editwhat = 1; vid.scale = 1; ne.s = "1"; *ne.editwhat = 1; vid.scale = 1; ne.s = "1";
} }
else if(uni == 'k' && ne.editwhat == &vid.alpha) { else if(uni == 'k' && ne.editwhat == &vid.alpha) {
*ne.editwhat = 0; vid.scale = 1; ne.s = "0"; *ne.editwhat = 0; vid.scale = 1; ne.s = "0";
} }
else if((uni == 'i' || uni == 'I' || uni == 'o' || uni == 'O') && ne.editwhat == &vid.alpha) { else if((uni == 'i' || uni == 'I' || uni == 'o' || uni == 'O') && ne.editwhat == &vid.alpha) {
double d = exp(shiftmul/10); double d = exp(shiftmul/10);
vid.alpha *= d; vid.alpha *= d;
vid.scale *= d; vid.scale *= d;
ne.s = fts(vid.alpha); ne.s = fts(vid.alpha);
} }
else if(doexiton(sym, uni)) { else if(doexiton(sym, uni)) popScreen();
cmode = lastmode; };
}
} }
int nlpage = 1; int nlpage = 1;
@ -785,4 +762,26 @@ namespace dialog {
return true; return true;
} }
void editNumber(ld& x, ld vmin, ld vmax, ld step, ld dft, string title, string help) {
ne.editwhat = &x;
ne.s = fts(x);
ne.vmin = vmin;
ne.vmax = vmax;
ne.step = step;
ne.dft = dft;
ne.title = title;
ne.help = help;
ne.scale = ne.inverse_scale = identity;
ne.intval = NULL;
ne.positive = false;
sidedialog = false;
pushScreen(drawNumberDialog);
}
void editNumber(int& x, int vmin, int vmax, int step, int dft, string title, string help) {
editNumber(ne.intbuf, vmin, vmax, step, dft, title, help);
ne.intbuf = x; ne.intval = &x; ne.s = its(x);
sidedialog = true;
}
}; };

View File

@ -5415,8 +5415,12 @@ void checkmove() {
if(movepcto(1, 1, true)) if(movepcto(1, 1, true))
canmove = legalmoves[cwt.spin] = true; canmove = legalmoves[cwt.spin] = true;
if(kills[moPlayer]) canmove = false; if(kills[moPlayer]) canmove = false;
if(!canmove)
if(!canmove) {
achievement_final(true); achievement_final(true);
if(cmode2 == smNormal) showMissionScreen();
}
if(canmove && timerstopped) { if(canmove && timerstopped) {
timerstart = time(NULL); timerstart = time(NULL);
timerstopped = false; timerstopped = false;

4755
graph.cpp

File diff suppressed because it is too large Load Diff

816
help.cpp Normal file
View File

@ -0,0 +1,816 @@
string help;
#ifndef NOLAMBDAS
function<void()> help_delegate;
#endif
string buildHelpText() {
DEBB(DF_GRAPH, (debugfile,"buildHelpText\n"));
#ifdef ROGUEVIZ
if(rogueviz::on) return rogueviz::makehelp();
#endif
string h;
h += XLAT("Welcome to HyperRogue");
#ifdef ANDROID
h += XLAT(" for Android");
#endif
#ifdef IOS
h += XLAT(" for iOS");
#endif
h += XLAT("! (version %1)\n\n", VER);
h += XLAT(
"You have been trapped in a strange, non-Euclidean world. Collect as much treasure as possible "
"before being caught by monsters. The more treasure you collect, the more "
"monsters come to hunt you, as long as you are in the same land type. The "
"Orbs of Yendor are the ultimate treasure; get at least one of them to win the game!"
);
h += XLAT(" (press ESC for some hints about it).");
h += "\n\n";
h += XLAT(
"You can fight most monsters by moving into their location. "
"The monster could also kill you by moving into your location, but the game "
"automatically cancels all moves which result in that.\n\n"
);
h += XLAT(
"There are many lands in HyperRogue. Collect 10 treasure "
"in the given land type to complete it; this enables you to "
"find the magical Orbs of this land, and in some cases "
"get access to new lands. At 25 treasures "
"this type of Orbs starts appearing in other lands as well. Press 'o' to "
"get the details of all the Lands.\n\n");
h += "\n\n";
#ifdef MOBILE
h += XLAT(
"Usually, you move by touching somewhere on the map; you can also touch one "
"of the four buttons on the map corners to change this (to scroll the map "
"or get information about map objects). You can also touch the "
"numbers displayed to get their meanings.\n"
);
#else
h += XLAT(
"Move with mouse, num pad, qweadzxc, or hjklyubn. Wait by pressing 's' or '.'. Spin the world with arrows, PageUp/Down, and Home/Space. "
"To save the game you need an Orb of Safety. Press 'v' for the main menu (configuration, special modes, etc.), ESC for the quest status.\n\n"
);
h += XLAT(
"You can right click any element to get more information about it.\n\n"
);
h += XLAT("(You can also use right Shift)\n\n");
#endif
h += XLAT("See more on the website: ")
+ "http//roguetemple.com/z/hyper/\n\n";
#ifdef TOUR
h += XLAT("Try the Tutorial to help with understanding the "
"geometry of HyperRogue (menu -> special modes).\n\n");
#endif
h += XLAT("Still confused? Read the FAQ on the HyperRogue website!\n\n");
return h;
}
string buildCredits() {
string h;
h += XLAT("game design, programming, texts and graphics by Zeno Rogue <zeno@attnam.com>\n\n");
if(lang() != 0)
h += XLAT("add credits for your translation here");
#ifndef NOLICENSE
h += XLAT(
"released under GNU General Public License version 2 and thus "
"comes with absolutely no warranty; see COPYING for details\n\n"
);
#endif
h += XLAT(
"special thanks to the following people for their bug reports, feature requests, porting, and other help:\n\n%1\n\n",
"Konstantin Stupnik, ortoslon, chrysn, Adam Borowski, Damyan Ivanov, Ryan Farnsley, mcobit, Darren Grey, tricosahedron, Maciej Chojecki, Marek Čtrnáct, "
"wonderfullizardofoz, Piotr MigdaĹ, tehora, Michael Heerdegen, Sprite Guard, zelda0x181e, Vipul, snowyowl0, Patashu, phenomist, Alan Malloy, Tom Fryers, Sinquetica, _monad, CtrlAltDestroy, jruderman"
);
#ifdef EXTRALICENSE
h += EXTRALICENSE;
#endif
#ifndef MOBILE
h += XLAT(
"\n\nSee sounds/credits.txt for credits for sound effects"
);
#endif
if(musiclicense != "") h += musiclicense;
return h;
}
string pushtext(stringpar p) {
string s = XLAT(
"\n\nNote: when pushing %the1 off a heptagonal cell, you can control the pushing direction "
"by clicking left or right half of the heptagon.", p);
#ifndef MOBILE
s += XLAT(" With the keyboard, you can rotate the view for a similar effect (Page Up/Down).");
#endif
return s;
}
string princedesc() {
if(princessgender() == GEN_M)
return XLAT("Apparently a prince is kept locked somewhere, but you won't ever find him in this hyperbolic palace. ");
else
return XLAT("Apparently a princess is kept locked somewhere, but you won't ever find her in this hyperbolic palace. ");
}
string helptitle(string s, int col) {
return "@" + its(col) + "\t" + s + "\n";
}
string princessReviveHelp() {
string h = "\n\n" +
XLAT("Killed %1 can be revived with Orb of the Love, after you collect 20 more $$$.", moPrincess);
if(princess::reviveAt)
h += "\n\n" +
XLAT("%The1 will be revivable at %2 $$$", moPrincess, its(princess::reviveAt));
return h;
}
void describeOrb(string& help, const orbinfo& oi) {
eOrbLandRelation olr = getOLR(oi.orb, cwt.c->land);
eItem tr = treasureType(oi.l);
help += "\n\n" + XLAT(olrDescriptions[olr], cwt.c->land, tr, treasureType(cwt.c->land));
int t = items[tr] * landMultiplier(oi.l);
if(t >= 25)
if(olr == olrPrize25 || olr == olrPrize3 || olr == olrGuest || olr == olrMonster || olr == olrAlways) {
help += XLAT("\nSpawn rate (as prize Orb): %1%/%2\n",
its(int(.5 + 100 * orbprizefun(t))),
its(oi.gchance));
}
if(t >= 10)
if(olr == olrHub) {
help += XLAT("\nSpawn rate (in Hubs): %1%/%2\n",
its(int(.5 + 100 * orbcrossfun(t))),
its(oi.gchance));
}
}
string generateHelpForItem(eItem it) {
string help = helptitle(XLATN(iinf[it].name), iinf[it].color);
help += XLAT(iinf[it].help);
if(it == itSavedPrincess || it == itOrbLove)
help += princessReviveHelp();
if(it == itTrollEgg)
help += XLAT("\n\nAfter the Trolls leave, you have 750 turns to collect %the1, or it gets stolen.", it);
if(it == itIvory || it == itAmethyst || it == itLotus || it == itMutant) {
help += XLAT(
"\n\nEasy %1 might disappear when you collect more of its kind.", it);
if(it != itMutant) help += XLAT(
" You need to go deep to collect lots of them.");
}
#ifdef MOBILE
if(it == itOrbSafety)
help += XLAT("This might be very useful for devices with limited memory.");
#else
if(it == itOrbSafety)
help += XLAT("Thus, it is potentially useful for extremely long games, which would eat all the memory on your system otherwise.\n");
#endif
if(isRangedOrb(it)) {
help += XLAT("\nThis is a ranged Orb. ");
#ifdef ISMOBILE
if(vid.shifttarget&2)
help += XLAT("\nRanged Orbs can be targeted by long touching the desired location.");
else
help += XLAT("\nRanged Orbs can be targeted by touching the desired location.");
#else
if(vid.shifttarget&1)
help += XLAT("\nRanged Orbs can be targeted by shift-clicking the desired location. "
else
help += XLAT("\nRanged Orbs can be targeted by clicking the desired location. ");
help += "You can also scroll to the desired location and then press 't'.");
#endif
help += XLAT("\nYou can never target cells which are adjacent to the player character, or ones out of the sight range.");
}
#ifdef MOBILE
if(it == itGreenStone)
help += XLAT("You can touch the Dead Orb in your inventory to drop it.");
#else
if(it == itGreenStone)
help += XLAT("You can press 'g' or click them in the list to drop a Dead Orb.");
#endif
if(it == itOrbLightning || it == itOrbFlash)
help += XLAT("\n\nThis Orb is triggered on your first attack or illegal move.");
if(it == itOrbShield)
help += XLAT("\n\nThis Orb protects you from attacks, scents, and insulates you "
"from electricity. It does not let you go through deadly terrain, but "
"if you are attacked with fire, it lets you stay in place in it.");
if(it == itOrbEmpathy) {
int cnt = 0;
for(int i=0; i<ittypes; i++) {
eItem it2 = eItem(i);
if(isEmpathyOrb(it2)) {
help += XLAT(cnt ? ", %1" : " %1", it2);
cnt++;
}
}
}
if(itemclass(it) == IC_ORB || it == itGreenStone || it == itOrbYendor) {
for(int i=0; i<ORBLINES; i++) {
const orbinfo& oi(orbinfos[i]);
if(oi.orb == it) describeOrb(help, oi);
}
}
if(itemclass(it) == IC_TREASURE) {
for(int i=0; i<ORBLINES; i++) {
const orbinfo& oi(orbinfos[i]);
if(treasureType(oi.l) == it) {
if(oi.gchance > 0) {
help += XLAT("\n\nOrb unlocked: %1", oi.orb);
describeOrb(help, oi);
}
else if(oi.l == cwt.c->land) {
help += XLAT("\n\nSecondary orb: %1", oi.orb);
describeOrb(help, oi);
}
}
}
}
return help;
}
void addMinefieldExplanation(string& s) {
s += XLAT(
"\n\nOnce you collect 10 Bomberbird Eggs, "
"stepping on a cell with no adjacent mines also reveals the adjacent cells. "
"Collecting even more Eggs will increase the radius. Additionally, collecting "
"25 Bomberbird Eggs will reveal adjacent cells even in your future games."
);
s += "\n\n";
#ifdef MOBILE
s += XLAT("Known mines may be marked by pressing 'm'. Your allies won't step on marked mines.");
#else
s += XLAT("Known mines may be marked by touching while in drag mode. Your allies won't step on marked mines.");
#endif
}
string generateHelpForWall(eWall w) {
string s = helptitle(XLATN(winf[w].name), winf[w].color);
s += XLAT(winf[w].help);
if(w == waMineMine || w == waMineUnknown || w == waMineOpen)
addMinefieldExplanation(s);
if(isThumper(w)) s += pushtext(w);
if((w == waClosePlate || w == waOpenPlate) && purehepta)
s += "\n\n(For the heptagonal mode, the radius has been reduced to 2 for closing plates.)";
return s;
}
void buteol(string& s, int current, int req) {
int siz = size(s);
if(s[siz-1] == '\n') s.resize(siz-1);
char buf[100]; sprintf(buf, " (%d/%d)", current, req);
s += buf; s += "\n";
}
string generateHelpForMonster(eMonster m) {
string s = helptitle(XLATN(minf[m].name), minf[m].color);
if(m == moPlayer) {
#ifdef TOUR
if(tour::on)
return s+XLAT(
"A tourist from another world. They mutter something about the 'tutorial', "
"and claim that they are here just to learn, and to leave without any treasures. "
"Do not kill them!"
);
#endif
s += XLAT(
"This monster has come from another world, presumably to steal our treasures. "
"Not as fast as an Eagle, not as resilient as the guards from the Palace, "
"and not as huge as the Mutant Ivy from the Clearing; however, "
"they are very dangerous because of their intelligence, "
"and access to magical powers.\n\n");
if(cheater)
s += XLAT("Actually, their powers appear god-like...\n\n");
else if(!hardcore)
s += XLAT(
"Rogues will never make moves which result in their immediate death. "
"Even when cornered, they are able to instantly teleport back to their "
"home world at any moment, taking the treasures forever... but "
"at least they will not steal anything further!\n\n"
);
if(!euclid)
s += XLAT(
"Despite this intelligence, Rogues appear extremely surprised "
"by the most basic facts about geometry. They must come from "
"some really strange world.\n\n"
);
if(shmup::on)
s += XLAT("In the Shoot'em Up mode, you are armed with thrown Knives.");
return s;
}
s += XLAT(minf[m].help);
if(m == moPalace || m == moSkeleton)
s += pushtext(m);
if(m == moTroll) s += XLAT(trollhelp2);
if(isMonsterPart(m))
s += XLAT("\n\nThis is a part of a monster. It does not count for your total kills.", m);
if(isFriendly(m))
s += XLAT("\n\nThis is a friendly being. It does not count for your total kills.", m);
if(m == moTortoise)
s += XLAT("\n\nTortoises are not monsters! They are just annoyed. They do not count for your total kills.", m);
if(isGhost(m))
s += XLAT("\n\nA Ghost never moves to a cell which is adjacent to another Ghost of the same kind.", m);
if(m == moBat || m == moEagle)
s += XLAT("\n\nFast flying creatures may attack or go against gravity only in their first move.", m);
return s;
}
string generateHelpForLand(eLand l) {
string s = helptitle(XLATN(linf[l].name), linf[l].color);
if(l == laPalace) s += princedesc();
s += XLAT(linf[l].help);
if(l == laMinefield) addMinefieldExplanation(s);
s += "\n\n";
if(l == laIce || l == laCaves || l == laDesert || l == laMotion || l == laJungle ||
l == laCrossroads || l == laAlchemist)
s += XLAT("Always available.\n");
#define ACCONLY(z) s += XLAT("Accessible only from %the1.\n", z);
#define ACCONLY2(z,x) s += XLAT("Accessible only from %the1 or %the2.\n", z, x);
#define ACCONLYF(z) s += XLAT("Accessible only from %the1 (until finished).\n", z);
#define TREQ(z) { s += XLAT("Treasure required: %1 $$$.\n", its(z)); buteol(s, gold(), z); }
#define TREQ2(z,x) { s += XLAT("Treasure required: %1 x %2.\n", its(z), x); buteol(s, items[x], z); }
if(l == laMirror || l == laMinefield || l == laPalace ||
l == laOcean || l == laLivefjord || l == laZebra || l == laWarpCoast || l == laWarpSea ||
l == laReptile || l == laIvoryTower)
TREQ(R30)
if(isCoastal(l))
s += XLAT("Coastal region -- connects inland and aquatic regions.\n");
if(isPureSealand(l))
s += XLAT("Aquatic region -- accessible only from coastal regions and other aquatic regions.\n");
if(l == laWhirlpool) ACCONLY(laOcean)
if(l == laRlyeh) ACCONLYF(laOcean)
if(l == laTemple) ACCONLY(laRlyeh)
if(l == laClearing) ACCONLY(laOvergrown)
if(l == laHaunted) ACCONLY(laGraveyard)
if(l == laPrincessQuest) ACCONLY(laPalace)
if(l == laMountain) ACCONLY(laJungle)
if(l == laCamelot) ACCONLY2(laCrossroads, laCrossroads3)
if(l == laDryForest || l == laWineyard || l == laDeadCaves || l == laHive || l == laRedRock ||
l == laOvergrown || l == laStorms || l == laWhirlwind || l == laRose ||
l == laCrossroads2 || l == laRlyeh)
TREQ(R60)
if(l == laReptile) TREQ2(U10, itElixir)
if(l == laEndorian) TREQ2(U10, itIvory)
if(l == laKraken) TREQ2(U10, itFjord)
if(l == laBurial) TREQ2(U10, itKraken)
if(l == laDungeon) TREQ2(U5, itIvory)
if(l == laDungeon) TREQ2(U5, itPalace)
if(l == laMountain) TREQ2(U5, itIvory)
if(l == laMountain) TREQ2(U5, itRuby)
if(l == laPrairie) TREQ(R90)
if(l == laBull) TREQ(R90)
if(l == laCrossroads4) TREQ(R200)
if(l == laCrossroads5) TREQ(R300)
if(l == laGraveyard || l == laHive) {
s += XLAT("Kills required: %1.\n", "100");
buteol(s, tkills(), R100);
}
if(l == laDragon) {
s += XLAT("Different kills required: %1.\n", "20");
buteol(s, killtypes(), R20);
}
if(l == laTortoise) ACCONLY(laDragon)
if(l == laTortoise) s += XLAT("Find a %1 in %the2.", itBabyTortoise, laDragon);
if(l == laHell || l == laCrossroads3) {
s += XLAT("Finished lands required: %1 (collect %2 treasure)\n", "9", its(R10));
buteol(s, orbsUnlocked(), 9);
}
if(l == laCocytus || l == laPower) TREQ2(U10, itHell)
if(l == laRedRock) TREQ2(U10, itSpice)
if(l == laOvergrown) TREQ2(U10, itRuby)
if(l == laClearing) TREQ2(U5, itMutant)
if(l == laCocytus) TREQ2(U10, itDiamond)
if(l == laDeadCaves) TREQ2(U10, itGold)
if(l == laTemple) TREQ2(U5, itStatue)
if(l == laHaunted) TREQ2(U10, itBone)
if(l == laCamelot) TREQ2(U5, itEmerald)
if(l == laEmerald) {
TREQ2(5, itFernFlower) TREQ2(5, itGold)
s += XLAT("Alternatively: kill a %1 in %the2.\n", moVizier, laPalace);
buteol(s, kills[moVizier], 1);
}
#define KILLREQ(who, where) { s += XLAT("Kills required: %1 (%2).\n", who, where); buteol(s, kills[who], 1); }
if(l == laPrincessQuest)
KILLREQ(moVizier, laPalace);
if(l == laElementalWall) {
KILLREQ(moFireElemental, laDragon);
KILLREQ(moEarthElemental, laDeadCaves);
KILLREQ(moWaterElemental, laLivefjord);
KILLREQ(moAirElemental, laWhirlwind);
}
if(l == laTrollheim) {
KILLREQ(moTroll, laCaves);
KILLREQ(moFjordTroll, laLivefjord);
KILLREQ(moDarkTroll, laDeadCaves);
KILLREQ(moStormTroll, laStorms);
KILLREQ(moForestTroll, laOvergrown);
KILLREQ(moRedTroll, laRedRock);
}
if(l == laZebra) TREQ2(U10, itFeather)
if(l == laCamelot || l == laPrincessQuest)
s += XLAT("Completing the quest in this land is not necessary for the Hyperstone Quest.");
int rl = isRandland(l);
if(rl == 2)
s += XLAT("Variants of %the1 are always available in the Random Pattern Mode.", l);
else if(rl == 1)
s += XLAT(
"Variants of %the1 are available in the Random Pattern Mode after "
"getting a highscore of at least 10 %2.", l, treasureType(l));
if(l == laPrincessQuest) {
s += XLAT("Unavailable in the shmup mode.\n");
s += XLAT("Unavailable in the multiplayer mode.\n");
}
if(noChaos(l))
s += XLAT("Unavailable in the Chaos mode.\n");
if(l == laWildWest)
s += XLAT("Bonus land, available only in some special modes.\n");
if(l == laWhirlpool)
s += XLAT("Orbs of Safety always appear here, and may be used to escape.\n");
/* if(isHaunted(l) || l == laDungeon)
s += XLAT("You may be unable to leave %the1 if you are not careful!\n", l); */
if(l == laStorms) {
if(elec::lightningfast == 0) s += XLAT("\nSpecial conduct (still valid)\n");
else s += XLAT("\nSpecial conduct failed:\n");
s += XLAT(
"Avoid escaping from a discharge (\"That was close\").");
}
if(isHaunted(l)) {
if(survivalist) s += XLAT("\nSpecial conduct (still valid)\n");
else s += XLAT("\nSpecial conduct failed:\n");
s += XLAT(
"Avoid chopping trees, using Orbs, and non-graveyard monsters in the Haunted Woods."
);
}
#ifndef ISMOBILE
if(l == laCA)
s += XLAT("\n\nHint: use 'm' to toggle cells quickly");
#endif
return s;
}
bool instat;
string turnstring(int i) {
if(i == 1) return XLAT("1 turn");
else return XLAT("%1 turns", its(i));
}
void describeMouseover() {
DEBB(DF_GRAPH, (debugfile,"describeMouseover\n"));
cell *c = mousing ? mouseover : playermoved ? NULL : centerover;
string out = mouseovers;
if(!c || instat || getcstat) { }
else if(c->wall != waInvisibleFloor) {
out = XLAT1(linf[c->land].name);
help = generateHelpForLand(c->land);
// Celsius
// if(c->land == laIce) out = "Icy Lands (" + fts(60 * (c->heat - .4)) + " C)";
if(c->land == laIce || c->land == laCocytus)
out += " (" + fts(heat::celsius(c)) + " °C)";
if(c->land == laDryForest && c->landparam)
out += " (" + its(c->landparam)+"/10)";
if(c->land == laOcean && chaosmode)
out += " (" + its(c->CHAOSPARAM)+"S"+its(c->SEADIST)+"L"+its(c->LANDDIST)+")";
else if(c->land == laOcean && c->landparam <= 25) {
if(shmup::on)
out += " (" + its(c->landparam)+")";
else {
bool b = c->landparam >= tide[(turncount-1) % tidalsize];
int t = 1;
for(; t < 1000 && b == (c->landparam >= tide[(turncount+t-1) % tidalsize]); t++) ;
if(b)
out += " (" + turnstring(t) + XLAT(" to surface") + ")";
else
out += " (" + turnstring(t) + XLAT(" to submerge") + ")";
}
}
if(c->land == laTortoise && tortoise::seek()) out += " " + tortoise::measure(getBits(c));
/* if(c->land == laGraveyard || c->land == laHauntedBorder || c->land == laHaunted)
out += " (" + its(c->landparam)+")"; */
if(buggyGeneration) {
char buf[20]; sprintf(buf, " H=%d M=%d", c->landparam, c->mpdist); out += buf;
}
// if(c->land == laBarrier)
// out += "(" + string(linf[c->barleft].name) + " / " + string(linf[c->barright].name) + ")";
// out += "(" + its(c->bardir) + ":" + string(linf[c->barleft].name) + " / " + string(linf[c->barright].name) + ")";
// out += " MD"+its(c->mpdist);
// out += " WP:" + its(c->wparam);
// out += " rose:" + its(rosemap[c]/4) + "." + its(rosemap[c]%4);
// out += " MP:" + its(c->mpdist);
// out += " cda:" + its(celldistAlt(c));
/* out += " DP=" + its(celldistance(c, cwt.c));
out += " DO=" + its(celldist(c));
out += " PD=" + its(c->pathdist); */
if(false) {
out += " LP:" + itsh(c->landparam)+"/"+its(turncount);
out += " CD:" + its(celldist(c));
out += " D:" + its(c->mpdist);
char zz[64]; sprintf(zz, " P%p", c); out += zz;
// out += " rv" + its(rosedist(c));
// if(rosemap.count(c))
// out += " rv " + its(rosemap[c]/8) + "." + its(rosemap[c]%8);
// out += " ai" + its(c->aitmp);
if(euclid) {
for(int i=0; i<4; i++) out += " " + its(getEuclidCdata(c->master)->val[i]);
out += " " + itsh(getBits(c));
}
else {
for(int i=0; i<4; i++) out += " " + its(getHeptagonCdata(c->master)->val[i]);
// out += " " + itsh(getHeptagonCdata(c->master)->bits);
out += " " + fts(tortoise::getScent(getBits(c)));
}
// itsh(getHeptagonCdata(c->master)->bits);
// out += " barleft: " + s0 + dnameof(c->barleft);
// out += " barright: " + s0 + dnameof(c->barright);
}
// char zz[64]; sprintf(zz, " P%p", c); out += zz;
/* whirlwind::calcdirs(c);
for(int i=0; i<whirlwind::qdirs; i++)
out += " " + its(whirlwind::dfrom[i]) + ":" + its(whirlwind::dto[i]); */
// out += " : " + its(whirlwinddir(c));
if(randomPatternsMode)
out += " " + describeRPM(c->land);
if(euclid && cheater) {
if(torus) {
out += " ("+its(decodeId(c->master))+")";
}
else {
eucoord x, y;
decodeMaster(c->master, x, y);
out += " ("+its(short(x))+","+its(short(y))+")";
}
}
// char zz[64]; sprintf(zz, " P%d", princess::dist(c)); out += zz;
// out += " MD"+its(c->mpdist);
// out += " H "+its(c->heat);
// if(c->type != 6) out += " Z"+its(c->master->zebraval);
// out += " H"+its(c->heat);
/* // Hive debug
if(c->land == laHive) {
out += " [" + its(c->tmp) + " H" + its(int(c->heat));
if(c->tmp >= 0 && c->tmp < size(buginfo) && buginfo[c->tmp].where == c) {
buginfo_t b(buginfo[c->tmp]);
for(int k=0; k<3; k++) out += ":" + its(b.dist[k]);
for(int k=0; k<3; k++)
for(int i=0; i<size(bugqueue[k]); i++)
if(bugqueue[k][i] == c->tmp)
out += " B"+its(k)+":"+its(i);
}
out += "]";
} */
if(c->wall &&
!((c->wall == waFloorA || c->wall == waFloorB || c->wall == waFloorC || c->wall == waFloorD) && c->item)) {
out += ", "; out += XLAT1(winf[c->wall].name);
if(c->wall == waRose) out += " (" + its(7-rosephase) + ")";
if((c->wall == waBigTree || c->wall == waSmallTree) && c->land != laDryForest)
help =
"Trees in this forest can be chopped down. Big trees take two turns to chop down.";
else if(c->wall != waSea && c->wall != waPalace)
if(!((c->wall == waCavefloor || c->wall == waCavewall) && c->land == laEmerald))
help = generateHelpForWall(c->wall);
}
if(isActivable(c)) out += XLAT(" (touch to activate)");
if(hasTimeout(c)) out += " [" + turnstring(c->wparam) + "]";
if(isReptile(c->wall))
out += " [" + turnstring((unsigned char) c->wparam) + "]";
if(c->monst) {
out += ", "; out += XLAT1(minf[c->monst].name);
if(hasHitpoints(c->monst))
out += " (" + its(c->hitpoints)+" HP)";
if(isMutantIvy(c))
out += " (" + its((c->stuntime - mutantphase) & 15) + "*)";
else if(c->stuntime)
out += " (" + its(c->stuntime) + "*)";
if(c->monst == moTortoise && tortoise::seek())
out += " " + tortoise::measure(tortoise::getb(c));
help = generateHelpForMonster(c->monst);
}
if(c->item && !itemHiddenFromSight(c)) {
out += ", ";
out += XLAT1(iinf[c->item].name);
if(c->item == itBarrow) out += " (x" + its(c->landparam) + ")";
if(c->item == itBabyTortoise && tortoise::seek())
out += " " + tortoise::measure(tortoise::babymap[c]);
if(!c->monst) help = generateHelpForItem(c->item);
}
if(isPlayerOn(c) && !shmup::on) out += XLAT(", you"), help = generateHelpForMonster(moPlayer);
shmup::addShmupHelp(out);
if(rosedist(c) == 1)
out += ", wave of scent (front)";
if(rosedist(c) == 2)
out += ", wave of scent (back)";
if(sword::at(c)) out += ", Energy Sword";
if(rosedist(c) || c->land == laRose || c->wall == waRose)
help += s0 + "\n\n" + rosedesc;
if(isWarped(c) && !isWarped(c->land))
out += ", warped";
if(isWarped(c))
help += s0 + "\n\n" + warpdesc;
}
/*
else if(cmode == emGraphConfig) {
if(getcstat == 'a' && vid.sspeed > -4.99)
out = XLAT("+5 = center instantly, -5 = do not center the map");
else if(getcstat == 'a')
out = XLAT("press Space or Home to center on the PC");
else if(getcstat == 'w')
out = XLAT("also hold Alt during the game to toggle high contrast");
else if(getcstat == 'f')
out = XLAT("Reduce the framerate limit to conserve CPU energy");
}
else if(cmode == emBasicConfig) {
if(getcstat == 'c')
out = XLAT("The axes help with keyboard movement");
else if(getcstat == 'g')
out = XLAT("Affects looks and grammar");
#ifndef MOBILE
else if(getcstat == 's')
out = XLAT("Config file: %1", conffile);
#endif
else out = "";
}
else if(cmode == emDisplayMode) {
if(getcstat == 'p') {
if(autojoy)
out = XLAT("joystick mode: automatic (release the joystick to move)");
if(!autojoy)
out = XLAT("joystick mode: manual (press a button to move)");
}
}
else if(cmode == emChangeMode) {
if(getcstat == 'h')
out = XLAT("One wrong move and it is game over!");
}
*/
mouseovers = out;
#ifdef ROGUEVIZ
rogueviz::describe(c);
#endif
int col = linf[cwt.c->land].color;
if(cwt.c->land == laRedRock) col = 0xC00000;
#ifndef MOBILE
displayfr(vid.xres/2, vid.fsize, 2, vid.fsize, out, col, 8);
#endif
if(mousey < vid.fsize * 3/2) getcstat = SDLK_F1;
}
void showHelp() {
cmode2 = smHelp;
getcstat = SDLK_ESCAPE;
if(help == "HELPFUN") {
help_delegate();
return;
}
if(help == "@") help = buildHelpText();
string help2;
if(help[0] == '@') {
int iv = help.find("\t");
int id = help.find("\n");
dialog::init(help.substr(iv+1, id-iv-1), atoi(help.c_str()+1), 120, 100);
dialog::addHelp(help.substr(id+1));
}
else {
dialog::init("help", forecolor, 120, 100);
dialog::addHelp(help);
}
if(help == buildHelpText()) dialog::addItem("credits", 'c');
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(sym == SDLK_F1 && help != "@")
help = "@";
else if(uni == 'c')
help = buildCredits();
else if(doexiton(sym, uni))
popScreen();
};
}
void gotoHelp(const string& h) {
help = h;
pushScreen(showHelp);
}

460
hud.cpp Normal file
View File

@ -0,0 +1,460 @@
purehookset hooks_stats;
int monsterclass(eMonster m) {
if(isFriendly(m) || m == moTortoise) return 1;
else if(isMonsterPart(m)) return 2;
else return 0;
}
int glyphclass(int i) {
if(i < ittypes) {
eItem it = eItem(i);
return itemclass(it) == IC_TREASURE ? 0 : 1;
}
else {
eMonster m = eMonster(i-ittypes);
return monsterclass(m) == 0 ? 2 : 3;
}
}
int subclass(int i) {
if(i < ittypes)
return itemclass(eItem(i));
else
return monsterclass(eMonster(i-ittypes));
}
#define GLYPH_MARKTODO 1
#define GLYPH_MARKOVER 2
#define GLYPH_LOCAL 4
#define GLYPH_IMPORTANT 8
#define GLYPH_NONUMBER 16
#define GLYPH_DEMON 32
#define GLYPH_RUNOUT 64
#define GLYPH_INPORTRAIT 128
#define GLYPH_LOCAL2 256
#define GLYPH_TARGET 512
#define GLYPH_INSQUARE 1024
eGlyphsortorder glyphsortorder;
int zero = 0;
int& ikmerge(int i) {
if(i < ittypes) return items[i];
else if(i == ittypes) return zero;
else return kills[i-ittypes];
}
const int glyphs = ittypes + motypes;
int gfirsttime[glyphs], glasttime[glyphs], gcopy[glyphs], ikland[glyphs];
int glyphorder[glyphs];
void updatesort() {
for(int i=0; i<glyphs; i++) {
if(ikmerge(i) && gfirsttime[i] == 0)
gfirsttime[i] = ticks;
if(ikmerge(i) != gcopy[i])
gcopy[i] = items[i], glasttime[i] = ticks;
}
}
void preparesort() {
for(int i=0; i<glyphs; i++) glyphorder[i] = i;
for(int i=0; i<LAND_OVERX; i++) {
eLand l = land_over[i];
ikland[treasureType(l)] = i+1;
for(int mi=0; mi<motypes; mi++)
if(isNative(l, eMonster(mi)))
ikland[mi+ittypes] = i+1;
}
glyphsortorder = gsoLand; updatesort();
glyphsortorder = gsoFirstTop;
}
int glyphsortkey = 0;
int glyphcorner(int i) {
if(i < ittypes)
return itemclass(eItem(i)) == IC_ORB ? 2 : 0;
else
return 1;
}
bool glyphsort(int i, int j) {
if(subclass(i) != subclass(j))
return subclass(i) < subclass(j);
if(glyphsortorder == gsoFirstTop)
return gfirsttime[i] < gfirsttime[j];
if(glyphsortorder == gsoFirstBottom)
return gfirsttime[i] > gfirsttime[j];
if(glyphsortorder == gsoLastTop)
return glasttime[i] > glasttime[j];
if(glyphsortorder == gsoLastBottom)
return glasttime[i] < glasttime[j];
if(glyphsortorder == gsoValue)
return ikmerge(i) > ikmerge(j);
if(glyphsortorder == gsoLand)
return ikland[i] < ikland[j];
return 0;
}
int glyphflags(int gid) {
int f = 0;
if(gid < ittypes) {
eItem i = eItem(gid);
if(itemclass(i) == IC_NAI) f |= GLYPH_NONUMBER;
if(isElementalShard(i)) {
f |= GLYPH_LOCAL | GLYPH_INSQUARE;
if(i == localshardof(cwt.c->land)) f |= GLYPH_LOCAL2;
}
if(i == treasureType(cwt.c->land))
f |= GLYPH_LOCAL | GLYPH_LOCAL2 | GLYPH_IMPORTANT | GLYPH_INSQUARE;
if(i == itHolyGrail) {
if(items[i] >= 3) f |= GLYPH_MARKOVER;
}
else if(itemclass(i) == IC_TREASURE) {
if(items[i] >= 25 && items[i] < 100) f |= GLYPH_MARKOVER;
else if(items[i] < 10) f |= GLYPH_MARKTODO;
}
else {
f |= GLYPH_IMPORTANT | GLYPH_INSQUARE;
if(itemclass(i) == IC_ORB && items[i] < 10) f |= GLYPH_RUNOUT;
}
if(i == orbToTarget) f |= GLYPH_TARGET;
f |= GLYPH_INPORTRAIT;
}
else {
eMonster m = eMonster(gid-ittypes);
if(m == moLesser) f |= GLYPH_IMPORTANT | GLYPH_DEMON | GLYPH_INPORTRAIT | GLYPH_INSQUARE;
int isnat = isNative(cwt.c->land, m);
if(isnat) f |= GLYPH_LOCAL | GLYPH_IMPORTANT | GLYPH_INPORTRAIT | GLYPH_INSQUARE;
if(isnat == 2) f |= GLYPH_LOCAL2;
if(m == monsterToSummon) f |= GLYPH_TARGET;
}
return f;
}
bool graphglyph() {
return vid.graphglyph == 2 || (vid.graphglyph == 1 && vid.monmode);
}
bool displayglyph(int cx, int cy, int buttonsize, char glyph, int color, int qty, int flags, int id) {
bool b =
mousex >= cx && mousex < cx+buttonsize && mousey >= cy-buttonsize/2 && mousey <= cy-buttonsize/2+buttonsize;
int glsize = buttonsize;
if(glyph == '%' || glyph == 'M' || glyph == 'W') glsize = glsize*4/5;
if(graphglyph()) {
initquickqueue();
if(id >= ittypes) {
eMonster m = eMonster(id - ittypes);
int bsize = buttonsize;
if(m == moKrakenH) bsize /= 3;
if(m == moKrakenT || m == moDragonTail) bsize /= 2;
if(m == moSlime) bsize = (2*bsize+1)/3;
transmatrix V = atscreenpos(cx+buttonsize/2, cy, bsize);
int mcol = color;
mcol -= (color & 0xFCFCFC) >> 2;
drawMonsterType(m, NULL, V, mcol, 0);
}
else {
eItem it = eItem(id);
int bsize = buttonsize;
if(glyph =='*') bsize *= 2;
if(glyph == '$') bsize = (bsize*5+2)/3;
if(glyph == 'o') bsize = (bsize*3+1)/2;
if(glyph == 't') bsize = bsize*5/2;
if(it == itWarning) bsize *= 2;
if(it == itBombEgg || it == itTrollEgg || it == itDodeca) bsize = bsize*3/2;
transmatrix V = atscreenpos(cx+buttonsize/2, cy, bsize);
int icol = color;
icol -= (color & 0xFCFCFC) >> 2;
int ic = itemclass(it);
drawItemType(it, NULL, V, icol, (ic == IC_ORB || ic == IC_NAI) ? ticks*2 : ((glyph == 't' && qty%5) || it == itOrbYendor) ? ticks/2 : 0, false);
}
quickqueue();
}
else if(glyph == '*')
displaychr(cx + buttonsize/2, cy+buttonsize/4, 0, glsize*3/2, glyph, darkenedby(color, b?0:1));
else
displaychr(cx + buttonsize/2, cy, 0, glsize, glyph, darkenedby(color, b?0:1));
string fl = "";
string str = its(qty);
if(flags & GLYPH_TARGET) fl += "!";
if(flags & GLYPH_LOCAL2) fl += "+";
else if(flags & GLYPH_LOCAL) fl += "-";
if(flags & GLYPH_DEMON) fl += "X";
if(flags & GLYPH_MARKOVER) str += "!";
if(fl != "")
displaystr(cx + buttonsize, cy-buttonsize/2 + buttonsize/4, 0, buttonsize/2, fl, darkenedby(color, 0), 16);
if(flags & GLYPH_NONUMBER) str = "";
int bsize =
(qty < 10 && (flags & (GLYPH_MARKTODO | GLYPH_RUNOUT))) ? buttonsize*3/4 :
qty < 100 ? buttonsize / 2 :
buttonsize / 3;
if(str != "")
displayfr(cx + buttonsize, cy + buttonsize/2 - bsize/2, 1, bsize, str, color, 16);
return b;
}
void displayglyph2(int cx, int cy, int buttonsize, int i) {
char glyph = i < ittypes ? iinf[i].glyph : minf[i-ittypes].glyph;
int color = i < ittypes ? iinf[i].color : minf[i-ittypes].color;
int imp = glyphflags(i);
if(displayglyph(cx, cy, buttonsize, glyph, color, ikmerge(i), imp, i)) {
instat = true;
getcstat = SDLK_F1;
if(i < ittypes) {
eItem it = eItem(i);
int t = itemclass(it);
if(t == IC_TREASURE)
mouseovers = XLAT("treasure collected: %1", it);
if(t == IC_OTHER)
mouseovers = XLAT("objects found: %1", it);
if(t == IC_NAI)
mouseovers = XLAT("%1", it);
if(t == IC_ORB)
mouseovers = XLAT("orb power: %1", eItem(i));
if(it == itGreenStone) {
mouseovers += XLAT(" (click to drop)");
getcstat = 'g';
}
if(imp & GLYPH_LOCAL) mouseovers += XLAT(" (local treasure)");
help = generateHelpForItem(it);
}
else {
eMonster m = eMonster(i-ittypes);
if(isMonsterPart(m))
mouseovers = s0 + XLAT("parts destroyed: %1", m);
else if(isFriendly(m) && isNonliving(m))
mouseovers = s0 + XLAT("friends destroyed: %1", m);
else if(isFriendly(m))
mouseovers = s0 + XLAT("friends killed: %1", m);
else if(isNonliving(m))
mouseovers = s0 + XLAT("monsters destroyed: %1", m);
else if(m == moTortoise)
mouseovers = s0 + XLAT("animals killed: %1", m);
else
mouseovers = s0 + XLAT("monsters killed: %1", m);
if(imp & GLYPH_LOCAL2) mouseovers += XLAT(" (killing increases treasure spawn)");
else if(imp & GLYPH_LOCAL) mouseovers += XLAT(" (appears here)");
help = generateHelpForMonster(m);
}
}
}
bool nohud, nomenukey;
hookset<bool()> *hooks_prestats;
void drawStats() {
callhandlers(false, hooks_prestats);
#ifdef ROGUEVIZ
if(rogueviz::on || nohud) return;
#endif
if(viewdists && sidescreen) {
distcolors[0] = forecolor;
dialog::init("");
int qty[64];
vector<cell*>& ac = currentmap->allcells();
for(int i=0; i<64; i++) qty[i] = 0;
for(int i=0; i<size(ac); i++) {
int d = celldistance(ac[i], cwt.c);
if(d >= 0 && d < 64) qty[d]++;
}
if(geometry == gNormal)
for(int i=purehepta?6:8; i<=15; i++)
qty[i] =
purehepta ?
3*qty[i-1] - qty[i-2]
: qty[i-1] + qty[i-2] + qty[i-3] - qty[i-4];
if(geometry == gEuclid)
for(int i=8; i<=15; i++) qty[i] = 6*i;
for(int i=0; i<64; i++) if(qty[i])
dialog::addInfo(its(qty[i]), distcolors[i&7]);
if(geometry == gNormal && !purehepta) {
dialog::addBreak(200);
dialog::addHelp("a(d+4) = a(d+3) + a(d+2) + a(d+1) - a(d)");
dialog::addInfo("a(d) ~ 1.72208ᵈ", forecolor);
}
if(geometry == gNormal && purehepta) {
dialog::addBreak(200);
dialog::addHelp("a(d+2) = 3a(d+1) - a(d+2)");
dialog::addInfo("a(d) ~ 2.61803ᵈ", forecolor);
}
if(geometry == gEuclid) {
dialog::addBreak(300);
dialog::addInfo("a(d) = 6d", forecolor);
}
dialog::display();
}
if(sidescreen) return;
if(vid.xres > vid.yres * 85/100 && vid.yres > vid.xres * 85/100) {
int bycorner[4];
for(int u=0; u<4; u++) bycorner[u] = 0;
for(int i=0; i<glyphs; i++) if(ikmerge(i) && (glyphflags(i) & GLYPH_INSQUARE))
bycorner[glyphcorner(i)]++;
updatesort();
stable_sort(glyphorder, glyphorder+glyphs, glyphsort);
int rad = min(vid.xres, vid.yres) / 2;
for(int cor=0; cor<3; cor++) {
for(int a=5; a<41; a++) {
int s = min(vid.xres, vid.yres) / a;
int spots = 0;
for(int u=vid.fsize; u<vid.xres/2-s; u += s)
for(int v=vid.fsize; v<vid.yres/2-s; v += s)
if(hypot(vid.xres/2-u-s, vid.yres/2-v-s) > rad) {
spots++;
}
if(spots >= bycorner[cor] && spots >= 3) {
int next = 0;
vector<int> glyphstoshow;
for(int i=0; i<glyphs; i++) {
int g = glyphorder[i];
if(ikmerge(g) && (glyphflags(g) & GLYPH_INSQUARE) && glyphcorner(g) == cor)
glyphstoshow.push_back(g);
}
for(int u=vid.fsize; u<vid.xres/2-s; u += s)
for(int v=vid.fsize; v<vid.yres/2-s; v += s)
if(hypot(vid.xres/2-u-s, vid.yres/2-v-s) > rad) {
if(next >= size(glyphstoshow)) break;
int cx = u;
int cy = v + s/2;
if(cor&1) cx = vid.xres-1-s-cx;
if(cor&2) cy = vid.yres-1-cy;
displayglyph2(cx, cy, s, glyphstoshow[next++]);
}
break;
}
}
}
return;
}
instat = false;
bool portrait = vid.xres < vid.yres;
int colspace = portrait ? (vid.yres - vid.xres - vid.fsize*3) : (vid.xres - vid.yres - 16) / 2;
int rowspace = portrait ? vid.xres - 16 : vid.yres - vid.fsize * 4;
int colid[4], rowid[4];
int maxbyclass[4];
for(int z=0; z<4; z++) maxbyclass[z] = 0;
for(int i=0; i<glyphs; i++) if(ikmerge(i))
if(!portrait || (glyphflags(i) | GLYPH_INPORTRAIT))
maxbyclass[glyphclass(i)]++;
int buttonsize;
int columns, rows;
bool imponly = false;
int minsize = vid.fsize * (portrait ? 4 : 2);
rows = 0;
while((buttonsize = minsize - vid.killreduction)) {
columns = colspace / buttonsize;
rows = rowspace / buttonsize;
int coltaken = 0;
for(int z=0; z<4; z++) {
if(z == 2 && !portrait) {
if(coltaken > columns) { vid.killreduction++; continue; }
coltaken = 0;
}
colid[z] = coltaken, rowid[z] = 0,
coltaken += (maxbyclass[z] + rows-1) / rows;
}
if(coltaken > columns) { vid.killreduction++; continue; }
break;
}
if(buttonsize <= vid.fsize*3/4) {
imponly = true; buttonsize = minsize;
rows = rowspace / buttonsize; if(!rows) return;
colid[0] = 0; colid[2] = portrait ? 1 : 0;
}
updatesort();
stable_sort(glyphorder, glyphorder+glyphs, glyphsort);
for(int i0=0; i0<glyphs; i0++) {
int i = glyphorder[i0];
if(!ikmerge(i)) continue;
int z = glyphclass(i);
int imp = glyphflags(i);
if(imponly) { z &=~1; if(!(imp & GLYPH_IMPORTANT)) continue; }
int cx, cy;
if(portrait)
cx = 8 + buttonsize * rowid[z], cy = vid.fsize*2 + buttonsize * (colid[z]) + buttonsize/2;
else
cx = 8 + buttonsize * (colid[z]), cy = vid.fsize * 3 + buttonsize * rowid[z];
if(!portrait && z < 2) cx = vid.xres - cx - buttonsize;
rowid[z]++; if(rowid[z] >= rows) rowid[z] = 0, colid[z]++;
displayglyph2(cx, cy, buttonsize, i);
}
string s0;
if(displayButtonS(vid.xres - 8, vid.fsize, "score: " + its(gold()), forecolor, 16, vid.fsize)) {
mouseovers = XLAT("Your total wealth"),
instat = true,
getcstat = SDLK_F1,
help = helptitle(XLAT("Your total wealth"), 0xFFD500) +
XLAT(
"The total value of the treasure you have collected.\n\n"
"Every world type contains a specific type of treasure, worth 1 $$$; "
"your goal is to collect as much treasure as possible, but every treasure you find "
"causes more enemies to hunt you in its native land.\n\n"
"Orbs of Yendor are worth 50 $$$ each.\n\n"
);
}
if(displayButtonS(8, vid.fsize, "kills: " + its(tkills()), forecolor, 0, vid.fsize)) {
instat = true,
getcstat = SDLK_F1,
mouseovers = XLAT("Your total kills")+": " + its(tkills()),
help = helptitle(XLAT("Your total kills") + ": " + its(tkills()), 0x404040) +
XLAT(
"In most lands, more treasures are generated with each enemy native to this land you kill. "
"Moreover, 100 kills is a requirement to enter the Graveyard and the Hive.\n\n"
"Friendly creatures and parts of monsters (such as the Ivy) do appear in the list, "
"but are not counted in the total kill count.");
}
if(displayButtonS(4, vid.yres - 4 - vid.fsize/2, s0+VER+ " fps: " + its(calcfps()), 0x202020, 0, vid.fsize/2)) {
mouseovers = XLAT("frames per second"),
getcstat = SDLK_F1,
instat = true,
help =
helptitle(XLAT("frames per second"), 0xFF4040) +
XLAT(
"The higher the number, the smoother the animations in the game. "
"If you find that animations are not smooth enough, you can try "
"to change the options "
) +
#ifdef IOS
XLAT(
"(in the MENU). You can reduce the sight range, this should make "
"the animations smoother.");
#else
XLAT(
"(press v) and change the wall/monster mode to ASCII, or change "
"the resolution.");
#endif
}
achievement_display();
callhooks(hooks_stats);
}

View File

@ -387,11 +387,11 @@ else if(args()[0] == '-' && args()[1] == x && args()[2] == '0') { if(curphase ==
return 0; return 0;
} }
hookset<bool(int argc, char** argv)> *hooks_main;
#ifndef NOMAIN #ifndef NOMAIN
int main(int argc, char **argv) { int main(int argc, char **argv) {
#ifdef EXTRA_MAIN if(callhandlers(false, hooks_main, argc, argv)) return 0;
if(extra::main(argc, argv)) return 0;
#endif
#ifndef WEB #ifndef WEB
#ifdef LINUX #ifdef LINUX
moreStack(); moreStack();
@ -409,25 +409,25 @@ int main(int argc, char **argv) {
#endif #endif
#ifdef USE_COMMANDLINE #ifdef USE_COMMANDLINE
purehookset hooks_config;
hookset<int()> *hooks_args;
namespace arg { namespace arg {
int argc; char **argv; int argc; char **argv;
auto ah = addHook(hooks_args, 0, readCommon);
void read(int phase) { void read(int phase) {
curphase = phase; curphase = phase;
#ifdef EXTRA_CONFIG callhooks(hooks_config);
extra::config();
#endif
while(argc) { while(argc) {
int r; for(auto& h: *hooks_args) {
r = readCommon(); if(r == 2) return; if(r == 0) { lshift(); continue; } int r = h.second(); if(r == 2) return; if(r == 0) { lshift(); goto cont; }
#ifdef EXTRA_ARG }
r = extra::arg(); if(r == 2) return; if(r == 0) { lshift(); continue; }
#endif
#ifdef ROGUEVIZ
r = rogueviz::readArgs(); if(r == 2) return; if(r == 0) { lshift(); continue; }
#endif
printf("Unknown option: %s\n", args()); printf("Unknown option: %s\n", args());
exit(3); exit(3);
cont: ;
} }
} }
} }

223
hyper.h
View File

@ -151,6 +151,7 @@ void loadcs(FILE *f, charstyle& cs, int vernum);
namespace multi { namespace multi {
extern bool shmupcfg;
void recall(); void recall();
extern cell *origpos[MAXPLAYER], *origtarget[MAXPLAYER]; extern cell *origpos[MAXPLAYER], *origtarget[MAXPLAYER];
extern int players; extern int players;
@ -179,6 +180,10 @@ namespace multi {
int deadzoneval[MAXJOY][MAXAXE]; int deadzoneval[MAXJOY][MAXAXE];
}; };
void saveConfig(FILE *f);
void loadConfig(FILE *f);
void initConfig();
charstyle scs[MAXPLAYER]; charstyle scs[MAXPLAYER];
bool playerActive(int p); bool playerActive(int p);
@ -186,9 +191,11 @@ namespace multi {
cell *multiPlayerTarget(int i); cell *multiPlayerTarget(int i);
void checklastmove(); void checklastmove();
void leaveGame(int i); void leaveGame(int i);
void showShmupConfig();
} }
namespace shmup { namespace shmup {
using namespace multi;
void recall(); void recall();
extern bool on; extern bool on;
extern bool safety; extern bool safety;
@ -217,6 +224,7 @@ namespace shmup {
void virtualRebase(cell*& base, transmatrix& at, bool tohex); void virtualRebase(cell*& base, transmatrix& at, bool tohex);
void virtualRebase(shmup::monster *m, bool tohex); void virtualRebase(shmup::monster *m, bool tohex);
void fixStorage(); void fixStorage();
void addShmupHelp(string& out);
} }
// graph // graph
@ -228,8 +236,6 @@ void showMissionScreen();
void restartGraph(); void restartGraph();
void resetmusic(); void resetmusic();
void cleargraphmemory();
void drawFlash(cell* c); void drawFlash(cell* c);
void drawBigFlash(cell* c); void drawBigFlash(cell* c);
void drawParticle(cell *c, int col, int maxspeed = 100); void drawParticle(cell *c, int col, int maxspeed = 100);
@ -319,6 +325,7 @@ struct videopar {
int flashtime; int flashtime;
int wallmode, monmode, axes; int wallmode, monmode, axes;
bool revcontrol;
// for OpenGL // for OpenGL
float scrdist; float scrdist;
@ -331,6 +338,7 @@ struct videopar {
#define AA_POLY 8 #define AA_POLY 8
#define AA_LINEWIDTH 16 #define AA_LINEWIDTH 16
#define AA_FONT 32 #define AA_FONT 32
#define AA_MULTI 64
ld linewidth; ld linewidth;
int joyvalue, joyvalue2, joypanthreshold; int joyvalue, joyvalue2, joypanthreshold;
@ -351,30 +359,11 @@ struct videopar {
extern videopar vid; extern videopar vid;
enum emtype {emNormal, emHelp, extern vector< function<void()> > screens;
emMenu,
emBasicConfig, emGraphConfig, emDisplayMode,
emChangeMode, emCustomizeChar,
emQuit, emDraw, emScores, emPickEuclidean,
emPickScores,
emShmupConfig,
emMapEditor,
emPatternPicker,
emOverview,
emNetgen,
emYendor, emTactic, emRugConfig,
emConformal,
emProgress,
emCheatMenu, emLeader,
emJoyConfig,
emColor, emNumber,
em3D, emRogueviz,
emLinepattern,
emPeace, emInventory,
emSlideshows
};
extern emtype cmode, lastmode; template<class T> void pushScreen(T& x) { screens.push_back(x); }
inline void popScreen() { screens.pop_back(); }
inline void popScreenAll() { while(size(screens)>1) popScreen(); }
extern transmatrix View; // current rotation, relative to viewctr extern transmatrix View; // current rotation, relative to viewctr
extern transmatrix cwtV; // player-relative view extern transmatrix cwtV; // player-relative view
@ -390,26 +379,36 @@ namespace mapeditor {
extern char whichCanvas; extern char whichCanvas;
extern int displaycodes; extern int displaycodes;
int generateCanvas(cell *c); int generateCanvas(cell *c);
void clearModelCells();
void applyModelcell(cell *c); void applyModelcell(cell *c);
int realpattern(cell *c); int realpattern(cell *c);
int patterndir(cell *c, char w = whichPattern); int patterndir(cell *c, char w = whichPattern);
int subpattern(cell *c); int subpattern(cell *c);
extern cell *drawcell; extern cell *drawcell;
void initdraw(cell *c); void initdraw(cell *c);
void showMapEditor();
void showDrawEditor();
} }
#ifndef NORUG #ifndef NORUG
namespace rug { namespace rug {
extern bool rugged; extern bool rugged;
extern bool renderonce;
extern bool rendernogl;
extern int texturesize;
extern double scale;
void show();
void init(); void init();
void close(); void close();
void actDraw(); void actDraw();
void select();
void buildVertexInfo(cell *c, transmatrix V); void buildVertexInfo(cell *c, transmatrix V);
} }
#endif #endif
#define HASLINEVIEW #define HASLINEVIEW
#include <complex>
typedef complex<ld> cld;
namespace conformal { namespace conformal {
extern bool on; extern bool on;
extern vector<pair<cell*, eMonster> > killhistory; extern vector<pair<cell*, eMonster> > killhistory;
@ -417,10 +416,15 @@ namespace conformal {
extern vector<cell*> movehistory; extern vector<cell*> movehistory;
extern bool includeHistory; extern bool includeHistory;
extern int rotation; extern int rotation;
extern bool autoband;
extern bool autobandhistory;
extern bool dospiral;
extern ld lvspeed;
extern int bandsegment;
extern int bandhalf;
void create(); void create();
void clear(); void clear();
void handleKey();
void show(); void show();
void apply(); void apply();
void movetophase(); void movetophase();
@ -428,11 +432,16 @@ namespace conformal {
extern vector<shmup::monster*> v; extern vector<shmup::monster*> v;
extern double phase; extern double phase;
void applyIB();
} }
namespace polygonal { namespace polygonal {
static const int MSI = 120;
extern int SI; extern int SI;
extern ld STAR; extern ld STAR;
extern int deg;
extern complex<ld> coef[MSI];
extern int maxcoef, coefid;
void solve(); void solve();
pair<ld, ld> compute(ld x, ld y); pair<ld, ld> compute(ld x, ld y);
} }
@ -805,12 +814,15 @@ namespace dialog {
int position; int position;
}; };
extern vector<item> items;
item& lastItem(); item& lastItem();
extern unsigned int *palette;
void addSelItem(string body, string value, int key); void addSelItem(string body, string value, int key);
void addBoolItem(string body, bool value, int key); void addBoolItem(string body, bool value, int key);
void addColorItem(string body, int value, int key); void addColorItem(string body, int value, int key);
void openColorDialog(int& col, unsigned int *pal); void openColorDialog(int& col, unsigned int *pal = palette);
void addHelp(string body); void addHelp(string body);
void addInfo(string body, int color = 0xC0C0C0); void addInfo(string body, int color = 0xC0C0C0);
void addItem(string body, int key); void addItem(string body, int key);
@ -821,9 +833,6 @@ namespace dialog {
void init(string title, int color = 0xE8E8E8, int scale = 150, int brk = 60); void init(string title, int color = 0xE8E8E8, int scale = 150, int brk = 60);
void display(); void display();
void drawColorDialog(int color);
int handleKeyColor(int sym, int uni, int& color);
void editNumber(ld& x, ld vmin, ld vmax, ld step, ld dft, string title, string help); void editNumber(ld& x, ld vmin, ld vmax, ld step, ld dft, string title, string help);
void editNumber(int& x, int vmin, int vmax, int step, int dft, string title, string help); void editNumber(int& x, int vmin, int vmax, int step, int dft, string title, string help);
void scaleLog(); void scaleLog();
@ -1002,6 +1011,7 @@ extern eGlyphsortorder glyphsortorder;
namespace rogueviz { namespace rogueviz {
extern bool on; extern bool on;
string describe(shmup::monster *m); string describe(shmup::monster *m);
void describe(cell *c);
void activate(shmup::monster *m); void activate(shmup::monster *m);
void drawVertex(const transmatrix &V, cell *c, shmup::monster *m); void drawVertex(const transmatrix &V, cell *c, shmup::monster *m);
bool virt(shmup::monster *m); bool virt(shmup::monster *m);
@ -1011,6 +1021,8 @@ namespace rogueviz {
int readArgs(); int readArgs();
void close(); void close();
void mark(cell *c); void mark(cell *c);
void showMenu();
string makehelp();
} }
#endif #endif
@ -1149,11 +1161,10 @@ namespace tour {
extern string slidecommand; extern string slidecommand;
extern int currentslide; extern int currentslide;
bool handleKeyTour(int sym, int uni);
enum presmode { enum presmode {
pmStartAll = 0, pmStartAll = 0,
pmStart = 1, pmFrame = 2, pmStop = 3, pmKey = 4, pmRestart = 5, pmStart = 1, pmFrame = 2, pmStop = 3, pmKey = 4, pmRestart = 5,
pmAfterFrame = 6,
pmGeometry = 11, pmGeometryReset = 13, pmGeometryStart = 15 pmGeometry = 11, pmGeometryReset = 13, pmGeometryStart = 15
}; };
@ -1192,7 +1203,6 @@ namespace tour {
namespace ss { namespace ss {
void showMenu(); void showMenu();
void handleKey(int sym, int uni);
void list(slide*); void list(slide*);
} }
@ -1218,6 +1228,8 @@ extern bool dronemode;
extern ld whatever; extern ld whatever;
enum screenmode { smMenu, smNormal, smMission, smHelp, smMap, smDraw, smNumber, smShmupConfig, smOverview };
namespace linepatterns { namespace linepatterns {
enum ePattern { enum ePattern {
@ -1242,6 +1254,7 @@ namespace linepatterns {
void clearAll(); void clearAll();
void setColor(ePattern id, int col); void setColor(ePattern id, int col);
void drawAll(); void drawAll();
void showMenu();
}; };
transmatrix ddspin(cell *c, int d, int bonus = 0); transmatrix ddspin(cell *c, int d, int bonus = 0);
@ -1290,3 +1303,145 @@ bool graphglyph();
extern bool hiliteclick; extern bool hiliteclick;
extern int antialiaslines; extern int antialiaslines;
extern int ringcolor; extern int ringcolor;
#include <functional>
template<class T> class hookset : public map<int, function<T>> {};
typedef hookset<void()> *purehookset;
template<class T, class U> int addHook(hookset<T>*& m, int prio, const U& hook) {
if(!m) m = new hookset<T> ();
(*m)[prio] = hook;
return 0;
}
extern purehookset hooks_frame, hooks_stats, clearmemory;
template<class T, class... U> void callhooks(hookset<T> *h, U... args) {
if(h) for(auto& p: *h) p.second(args...);
}
template<class T, class V, class... U> V callhandlers(V zero, hookset<T> *h, U... args) {
if(h) for(auto& p: *h) {
auto z = p.second(args...);
if(z != zero) return z;
}
return zero;
}
extern hookset<bool(int sym, int uni)> *hooks_handleKey;
extern hookset<void(cell *c, const transmatrix& V)> *hooks_drawcell;
extern hookset<bool(int argc, char** argv)> *hooks_main;
extern hookset<int()> *hooks_args;
extern hookset<eLand(eLand)> *hooks_nextland;
// hooks to extend HyperRogue with an external program
// start compilation from another file which defines EXTRA_..., includes
// hyper.cpp, then defines the necessary functions
extern ld shiftmul;
void initcs(charstyle &cs);
charstyle& getcs();
struct msginfo {
int stamp;
char flashout;
char spamtype;
int quantity;
string msg;
};
extern vector<msginfo> msgs;
void flashMessages();
extern int lightat, safetyat;
int watercolor(int phase);
bool doHighlight();
string buildHelpText();
string buildCredits();
void setAppropriateOverview();
bool quitsaves();
extern bool sidescreen;
static const char* COLORBAR = "###";
int textwidth(int siz, const string &str);
#define GLERR(call) glError(call, __FILE__, __LINE__)
extern bool gtouched, mousepressed, mousemoved, actonrelease;
extern bool inslider;
#ifdef ROGUEVIZ
#define DOSHMUP (shmup::on || rogueviz::on)
#else
#define DOSHMUP shmup::on
#endif
extern bool outoffocus;
extern int frames;
extern transmatrix playerV;
extern bool didsomething;
extern void drawStats();
extern int calcfps();
extern int distcolors[8];
extern eItem orbToTarget;
extern eMonster monsterToSummon;
void panning(hyperpoint hf, hyperpoint ht);
extern transmatrix sphereflip;
void initConfig();
void loadConfig();
extern bool auraNOGL;
#ifndef NOSDL
extern void initJoysticks();
extern int joyx, joyy, panjoyx, panjoyy;
extern bool autojoy;
extern movedir joydir;
extern SDL_Joystick* sticks[8];
extern int numsticks;
void closeJoysticks();
#endif
void preparesort();
#ifdef MOBILE
#define SHMUPTITLE "shoot'em up mode"
#else
#define SHMUPTITLE "shoot'em up and multiplayer"
#endif
bool dodrawcell(cell *c);
void drawcell(cell *c, transmatrix V, int spinv, bool mirrored);
extern double downspin;
extern int frameid;
extern bool leftclick;
void clearMemory();
extern function <void(int sym, int uni)> keyhandler;
void gmodekeys(int sym, int uni);
void switchGL();
void switchFullscreen();
extern screenmode cmode2;
void gotoHelp(const string& h);
void showCustomizeChar();
void showScores();
void showPickScores();
void showCheatMenu();
void showDisplayMode();
void showChangeMode();
void showEuclideanMenu();
void show3D();
void gameoverscreen();
void showJoyConfig();
void gamescreen(int darken);
void showMission();
void handleKeyQuit(int sym, int uni);
void handlePanning(int sym, int uni);

View File

@ -474,3 +474,5 @@ transmatrix mzscale(const transmatrix& t, double fac) {
return res; return res;
} }
transmatrix pushone() { return euclid ? eupush(1, 0) : xpush(sphere?.5 : 1); }

561
hypgraph.cpp Normal file
View File

@ -0,0 +1,561 @@
ld ghx, ghy, ghgx, ghgy;
hyperpoint ghpm = C0;
void ghcheck(hyperpoint &ret, const hyperpoint &H) {
if(hypot(ret[0]-ghx, ret[1]-ghy) < hypot(ghgx-ghx, ghgy-ghy)) {
ghpm = H; ghgx = ret[0]; ghgy = ret[1];
}
}
void camrotate(ld& hx, ld& hy) {
ld cam = vid.camera_angle * M_PI / 180;
GLfloat cc = cos(cam);
GLfloat ss = sin(cam);
ld ux = hx, uy = hy * cc + ss, uz = cc - ss * hy;
hx = ux / uz, hy = uy / uz;
}
hyperpoint gethyper(ld x, ld y) {
ld hx = (x - vid.xcenter) / vid.radius;
ld hy = (y - vid.ycenter) / vid.radius;
if(pmodel) {
ghx = hx, ghy = hy;
return ghpm;
}
if(euclid)
return hpxy(hx * (EUCSCALE + vid.alphax), hy * (EUCSCALE + vid.alphax));
if(vid.camera_angle) camrotate(hx, hy);
ld hr = hx*hx+hy*hy;
if(hr > .9999 && !sphere) return Hypc;
// hz*hz-(hx/(hz+alpha))^2 - (hy/(hz+alpha))^2 =
// hz*hz-hr*(hz+alpha)^2 == 1
// hz*hz - hr*hr*hz*Hz
ld A, B, C;
ld curv = sphere ? 1 : -1;
A = 1+curv*hr;
B = 2*hr*vid.alphax*-curv;
C = 1 - curv*hr*vid.alphax*vid.alphax;
// Az^2 - Bz = C
B /= A; C /= A;
// z^2 - Bz = C
// z^2 - Bz + (B^2/4) = C + (B^2/4)
// z = (B/2) + sqrt(C + B^2/4)
ld rootsign = 1;
if(sphere && vid.alphax > 1) rootsign = -1;
ld hz = B / 2 + rootsign * sqrt(C + B*B/4);
hyperpoint H;
H[0] = hx * (hz+vid.alphax);
H[1] = hy * (hz+vid.alphax);
H[2] = hz;
return H;
}
void ballmodel(hyperpoint& ret, double alpha, double d, double zl) {
hyperpoint H = ypush(geom3::camera) * xpush(d) * ypush(zl) * C0;
ld tzh = vid.ballproj + H[2];
ld ax = H[0] / tzh;
ld ay = H[1] / tzh;
ld ball = vid.ballangle * M_PI / 180;
ld ca = cos(alpha), sa = sin(alpha);
ld cb = cos(ball), sb = sin(ball);
ret[0] = ax * ca;
ret[1] = ay * cb + ax * sa * sb;
ret[2] = - ax * sa * cb - ay * sb;
}
void applymodel(hyperpoint H, hyperpoint& ret) {
ld tz = euclid ? (EUCSCALE+vid.alphax) : vid.alphax+H[2];
if(tz < 1e-3 && tz > -1e-3) tz = 1000;
if(pmodel == mdUnchanged) {
for(int i=0; i<3; i++) ret[i] = H[i] / vid.radius;
return;
}
if(pmodel == mdBall) {
ld zlev = zlevel(H);
using namespace hyperpoint_vec;
H = H / zlev;
ld zl = geom3::depth-geom3::factor_to_lev(zlev);
double alpha = atan2(H[1], H[0]);
double d = hdist0(H);
ballmodel(ret, alpha, d, zl);
ghcheck(ret,H);
return;
}
if(pmodel == mdHyperboloid) {
ld ball = vid.ballangle * M_PI / 180;
ld cb = cos(ball), sb = sin(ball);
ret[0] = H[0] / 3;
ret[1] = (1 - H[2]) / 3 * cb + H[1] / 3 * sb;
ret[2] = H[1] / 3 * cb - (1 - H[2]) / 3 * sb;
ghcheck(ret,H);
return;
}
if(pmodel == mdDisk) {
if(!vid.camera_angle) {
ret[0] = H[0] / tz;
ret[1] = H[1] / tz;
ret[2] = (1 - vid.beta / tz);
}
else {
ld tx = H[0];
ld ty = H[1];
ld cam = vid.camera_angle * M_PI / 180;
GLfloat cc = cos(cam);
GLfloat ss = sin(cam);
ld ux = tx, uy = ty * cc - ss * tz, uz = tz * cc + ss * ty;
ret[0] = ux / uz;
ret[1] = uy / uz;
ret[2] = 1 - vid.beta / uz;
}
return;
}
ld zlev = 1;
if(wmspatial || mmspatial) {
zlev = zlevel(H);
using namespace hyperpoint_vec;
H = H / zlev;
}
if(pmodel == mdEquidistant || pmodel == mdEquiarea) {
ld rad = sqrt(H[0] * H[0] + H[1] * H[1]);
ld d = hdist0(H);
if(pmodel == 6 && sphere)
d = sqrt(2*(1 - cos(d))) * 1.25; // /1.5 to make it fit on the screen better
else if(pmodel == 6)
d = sqrt(2*(cosh(d) - 1)) / 1.5;
ret[0] = d * H[0] / rad / 4;
ret[1] = d * H[1] / rad / 4;
ret[2] = 0;
if(zlev != 1 && vid.goteyes)
ret[2] = geom3::factor_to_lev(zlev);
ghcheck(ret,H);
return;
}
tz = H[2]+vid.alphax;
if(pmodel == mdPolygonal || pmodel == mdPolynomial) {
pair<long double, long double> p = polygonal::compute(H[0]/tz, H[1]/tz);
ret[0] = p.first;
ret[1] = p.second;
ret[2] = 0;
ghcheck(ret,H);
return;
}
// Poincare to half-plane
ld x0, y0;
x0 = H[0] / tz;
y0 = H[1] / tz;
y0 += 1;
double rad = x0*x0 + y0*y0;
y0 /= rad;
x0 /= rad;
y0 -= .5;
if(pmodel == mdHalfplane) {
ret[0] = x0;
if(wmspatial || mmspatial) y0 *= zlev;
ret[1] = 1 - y0;
ret[2] = 0;
if(zlev != 1 && vid.goteyes)
ret[2] = y0 * geom3::factor_to_lev(zlev);
ghcheck(ret,H);
return;
}
// center
x0 *= 2; y0 *= 2;
// half-plane to band
double tau = (log((x0+1)*(x0+1) + y0*y0) - log((x0-1)*(x0-1) + y0*y0)) / 2;
double u=(1-x0*x0-y0*y0);
u = (1 - x0*x0 - y0*y0 + sqrt(u*u+4*y0*y0));
double yv = 2*y0 / u;
double sigma = 2 * atan(yv * zlev) - M_PI/2;
x0 = tau; y0 = sigma;
/* if(zlev != 1) {
double alp = (y0 * y0) / (1-y0*y0);
double gx = alp + sqrt(alp*alp-1);
double gy = y0 * (gx+1);
double yr = zlev * gy / (zlev * gx + 1);
printf("zlev = %10.5lf y0 = %20.10lf yr = %20.10lf\n", double(zlev), (double)y0, yr);
y0 = yr;
} */
ret[0] = x0/M_PI*2;
ret[1] = -y0/M_PI*2;
ret[2] = 0;
if(zlev != 1 && vid.goteyes)
ret[2] = geom3::factor_to_lev(zlev) / (1 + yv * yv);
ghcheck(ret,H);
}
// game-related graphics
transmatrix View; // current rotation, relative to viewctr
transmatrix cwtV; // player-relative view
transmatrix sphereflip; // on the sphere, flip
heptspin viewctr; // heptagon and rotation where the view is centered at
bool playerfound; // has player been found in the last drawing?
double eurad = 0.52;
double q3 = sqrt(double(3));
bool outofmap(hyperpoint h) {
if(euclid)
return false; // h[0] * h[0] + h[1] * h[1] > 15 * eurad;
else if(sphere)
return h[2] < .1 && h[2] > -.1 && h[1] > -.1 && h[1] < .1 && h[0] > -.1 && h[0] < .1;
else
return h[2] < .5;
}
hyperpoint mirrorif(const hyperpoint& V, bool b) {
if(b) return Mirror*V;
else return V;
}
// -1 if away, 0 if not away
int away(const transmatrix& V2) {
return intval(C0, V2 * xpush0(1)) > intval(C0, tC0(V2));
}
/* double zgrad(double f1, double f2, int nom, int den) {
using namespace geom3;
ld fo1 = factor_to_lev(f1);
ld fo2 = factor_to_lev(f2);
return lev_to_factor(fo1 + (fo2-fo1) * nom / den);
} */
double zgrad0(double l1, double l2, int nom, int den) {
using namespace geom3;
return lev_to_factor(l1 + (l2-l1) * nom / den);
}
bool behindsphere(const hyperpoint& h) {
if(!sphere) return false;
if(vid.alpha > 1) {
if(h[2] > -1/vid.alpha) return true;
}
if(vid.alpha <= 1) {
if(h[2] < -.8) return true;
}
return false;
}
bool behindsphere(const transmatrix& V) {
return behindsphere(tC0(V));
}
bool confusingGeometry() {
return elliptic || quotient == 1 || torus;
}
void drawrec(const heptspin& hs, int lev, hstate s, const transmatrix& V) {
// shmup::calc_relative_matrix(cwt.c, hs.h);
cell *c = hs.h->c7;
transmatrix V10;
const transmatrix& V1 = hs.mirrored ? (V10 = V * Mirror) : V;
if(dodrawcell(c)) {
reclevel = maxreclevel - lev;
drawcell(c, (hs.spin || purehepta) ? V1 * spin(hs.spin*2*M_PI/S7 + (purehepta ? M_PI:0)) : V1, hs.spin,
hs.mirrored);
}
if(lev <= 0) return;
if(!purehepta) for(int d=0; d<S7; d++) {
int ds = fixrot(hs.spin + d);
reclevel = maxreclevel - lev + 1;
// createMov(c, ds);
if(c->mov[ds] && c->spn(ds) == 0 && dodrawcell(c->mov[ds])) {
drawcell(c->mov[ds], V1 * hexmove[d], 0, hs.mirrored ^ c->mirror(ds));
}
}
if(lev <= 1) return;
for(int d=0; d<S7; d++) {
hstate s2 = transition(s, d);
if(s2 == hsError) continue;
heptspin hs2 = hsstep(hsspin(hs, d), 0);
drawrec(hs2, lev-2, s2, V * heptmove[d]);
}
}
int mindx=-7, mindy=-7, maxdx=7, maxdy=7;
transmatrix eumove(int x, int y) {
transmatrix Mat = Id;
Mat[2][2] = 1;
Mat[0][2] += (x + y * .5) * eurad;
// Mat[2][0] += (x + y * .5) * eurad;
Mat[1][2] += y * q3 /2 * eurad;
// Mat[2][1] += y * q3 /2 * eurad;
while(Mat[0][2] <= -16384 * eurad) Mat[0][2] += 32768 * eurad;
while(Mat[0][2] >= 16384 * eurad) Mat[0][2] -= 32768 * eurad;
while(Mat[1][2] <= -16384 * q3 * eurad) Mat[1][2] += 32768 * q3 * eurad;
while(Mat[1][2] >= 16384 * q3 * eurad) Mat[1][2] -= 32768 * q3 * eurad;
return Mat;
}
transmatrix eumovedir(int d) {
d = fix6(d);
switch(d) {
case 0: return eumove(1,0);
case 1: return eumove(0,1);
case 2: return eumove(-1,1);
case 3: return eumove(-1,0);
case 4: return eumove(0,-1);
case 5: return eumove(1,-1);
}
return eumove(0,0);
}
void drawEuclidean() {
DEBB(DF_GRAPH, (debugfile,"drawEuclidean\n"));
eucoord px=0, py=0;
if(!centerover) centerover = cwt.c;
// printf("centerover = %p player = %p [%d,%d]-[%d,%d]\n", lcenterover, cwt.c,
// mindx, mindy, maxdx, maxdy);
int pid;
const bool b = torus;
if(b)
pid = decodeId(centerover->master);
else
decodeMaster(centerover->master, px, py);
int minsx = mindx-1, maxsx=maxdx+1, minsy=mindy-1, maxsy=maxdy+1;
mindx=maxdx=mindy=maxdy=0;
for(int dx=minsx; dx<=maxsx; dx++)
for(int dy=minsy; dy<=maxsy; dy++) {
reclevel = eudist(dx, dy);
cell *c;
transmatrix Mat;
if(b) {
reclevel = eudist(dx, dy);
c = getTorusId(pid+torusconfig::dx*dx+torusconfig::dy*dy);
Mat = eumove(dx,dy);
}
else {
eucoord x = dx+px;
eucoord y = dy+py;
c = euclideanAt(x,y);
Mat = eumove(x, y);
}
if(!c) continue;
Mat = View * Mat;
// Mat[0][0] = -1;
// Mat[1][1] = -1;
// 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(dodrawcell(c)) {
drawcell(c, Mat, 0, false);
}
}
}
void spinEdge(ld aspd) {
if(downspin > aspd) downspin = aspd;
if(downspin < -aspd) downspin = -aspd;
View = spin(downspin) * View;
}
void centerpc(ld aspd) {
if(vid.sspeed >= 4.99) aspd = 1000;
DEBB(DF_GRAPH, (debugfile,"center pc\n"));
hyperpoint H = ypush(-vid.yshift) * sphereflip * tC0(cwtV);
if(H[0] == 0 && H[1] == 0) return; // either already centered or direction unknown
ld R = hdist0(H); // = sqrt(H[0] * H[0] + H[1] * H[1]);
if(R < 1e-9) {
/* if(playerfoundL && playerfoundR) {
} */
spinEdge(aspd);
fixmatrix(View);
return;
}
if(euclid) {
// Euclidean
aspd *= (2+3*R*R);
if(aspd > R) aspd = R;
View[0][2] -= cwtV[0][2] * aspd / R;
View[1][2] -= cwtV[1][2] * aspd / R;
}
else {
aspd *= (1+R+(shmup::on?1:0));
if(R < aspd) {
View = gpushxto0(H) * View;
}
else
View = rspintox(H) * xpush(-aspd) * spintox(H) * View;
fixmatrix(View);
spinEdge(aspd);
}
}
void optimizeview() {
DEBB(DF_GRAPH, (debugfile,"optimize view\n"));
int turn = 0;
ld best = INF;
transmatrix TB = Id;
for(int i=-1; i<S7; i++) {
ld trot = -i * M_PI * 2 / (S7+.0);
transmatrix T = i < 0 ? Id : spin(trot) * xpush(tessf) * pispin;
hyperpoint H = View * tC0(T);
if(H[2] < best) best = H[2], turn = i, TB = T;
}
if(turn >= 0) {
View = View * TB;
fixmatrix(View);
viewctr = hsspin(viewctr, turn);
viewctr = hsstep(viewctr, 0);
}
}
void addball(ld a, ld b, ld c) {
hyperpoint h;
ballmodel(h, a, b, c);
for(int i=0; i<3; i++) h[i] *= vid.radius;
curvepoint(h);
}
void ballgeometry() {
queuereset(vid.usingGL ? mdDisk : mdUnchanged, PPR_CIRCLE);
for(int i=0; i<60; i++)
addball(i * M_PI/30, 10, 0);
for(double d=10; d>=-10; d-=.2)
addball(0, d, 0);
for(double d=-10; d<=10; d+=.2)
addball(0, d, geom3::depth);
addball(0, 0, -geom3::camera);
addball(0, 0, geom3::depth);
addball(0, 0, -geom3::camera);
addball(0, -10, 0);
addball(0, 0, -geom3::camera);
queuecurve(darkena(0xFF, 0, 0x80), 0, PPR_CIRCLE);
queuereset(pmodel, PPR_CIRCLE);
}
void resetview() {
DEBB(DF_GRAPH, (debugfile,"reset view\n"));
View = Id;
// EUCLIDEAN
if(!euclid)
viewctr.h = cwt.c->master,
viewctr.spin = cwt.spin;
else centerover = cwt.c;
// SDL_LockSurface(s);
// SDL_UnlockSurface(s);
}
void panning(hyperpoint hf, hyperpoint ht) {
View =
rgpushxto0(hf) * rgpushxto0(gpushxto0(hf) * ht) * gpushxto0(hf) * View;
playermoved = false;
}
void fullcenter() {
if(playerfound && false) centerpc(INF);
else {
bfs();
resetview();
drawthemap();
centerpc(INF);
}
playermoved = true;
}
transmatrix screenpos(ld x, ld y) {
transmatrix V = Id;
V[0][2] += (x - vid.xcenter) / vid.radius * (1+vid.alphax);
V[1][2] += (y - vid.ycenter) / vid.radius * (1+vid.alphax);
return V;
}
transmatrix atscreenpos(ld x, ld y, ld size) {
transmatrix V = Id;
V[0][2] += (x - vid.xcenter);
V[1][2] += (y - vid.ycenter);
V[0][0] = size * 2 * hcrossf / crossf;
V[1][1] = size * 2 * hcrossf / crossf;
V[2][2] = vid.scrdist;
if(euclid) V[2][2] /= EUCSCALE;
return V;
}

View File

@ -1,6 +1,6 @@
#define VER "9.4n1" #define VER "9.4n2"
#define VERNUM 9415 #define VERNUM 9416
#define VERNUM_HEX 0x9415 #define VERNUM_HEX 0x9416
#define GEN_M 0 #define GEN_M 0
#define GEN_F 1 #define GEN_F 1
@ -276,6 +276,23 @@ const char *loadlevel = NULL;
#ifdef EXTRA #ifdef EXTRA
#include "extra/extra.cpp" #include "extra/extra.cpp"
#endif #endif
#include "basegraph.cpp"
#include "help.cpp"
#include "config.cpp"
#include "scores.cpp"
#include "menus.cpp"
#ifdef FIXEDSIZE
#include "nofont.cpp"
#endif
#include "shmup.cpp"
#ifdef ROGUEVIZ
#include "rogueviz.cpp"
#endif
#include "conformal.cpp"
#include "rug.cpp"
#include "control.cpp"
#include "hud.cpp"
#include "hypgraph.cpp"
#include "graph.cpp" #include "graph.cpp"
#include "sound.cpp" #include "sound.cpp"
#include "achievement.cpp" #include "achievement.cpp"
@ -299,7 +316,6 @@ void initAll() {
eLand f = firstland; eLand f = firstland;
// initlanguage(); // initlanguage();
cmode = emNormal;
initgraph(); initgraph();
#ifndef NOSAVE #ifndef NOSAVE
loadsave(); loadsave();
@ -396,7 +412,7 @@ bool useRangedOrb;
void handleclick(MOBPAR_FORMAL) { void handleclick(MOBPAR_FORMAL) {
if(!shmup::on && andmode == 0 && cmode == emNormal && canmove && !useRangedOrb && vid.mobilecompasssize > 0) { if(!shmup::on && andmode == 0 && size(screens) == 1 && canmove && !useRangedOrb && vid.mobilecompasssize > 0) {
using namespace shmupballs; using namespace shmupballs;
int dx = mousex - xmove; int dx = mousex - xmove;
int dy = mousey - yb; int dy = mousey - yb;
@ -404,7 +420,7 @@ void handleclick(MOBPAR_FORMAL) {
if(h < rad) { if(h < rad) {
if(h < rad*SKIPFAC) movepcto(MD_WAIT); if(h < rad*SKIPFAC) movepcto(MD_WAIT);
else { else {
double d = revcontrol ? -1 : 1; double d = vid.revcontrol ? -1 : 1;
mouseh = hpxy(dx * d / rad, dy * d / rad); mouseh = hpxy(dx * d / rad, dy * d / rad);
mousemovement(); mousemovement();
} }
@ -415,7 +431,7 @@ void handleclick(MOBPAR_FORMAL) {
if(buttonclicked || mouseout()) { if(buttonclicked || mouseout()) {
if(andmode == 0 && getcstat == 'g' && !shmup::on && (cmode == emNormal || cmode == emQuit)) { if(andmode == 0 && getcstat == 'g' && !shmup::on && size(screens) == 1) {
movepcto(MD_DROP); movepcto(MD_DROP);
getcstat = 0; getcstat = 0;
} }
@ -424,7 +440,7 @@ void handleclick(MOBPAR_FORMAL) {
int px = mousex < vid.xcenter ? 0 : 1; int px = mousex < vid.xcenter ? 0 : 1;
int py = mousey < vid.ycenter ? 0 : 1; int py = mousey < vid.ycenter ? 0 : 1;
if(cmode == (canmove ? emNormal : emQuit)) { if(size(screens) == 1) {
if(px == 0 && py == 1) { if(px == 0 && py == 1) {
if(andmode == 0 && shmup::on) ; if(andmode == 0 && shmup::on) ;
else andmode = 10; else andmode = 10;
@ -456,7 +472,7 @@ void handleclick(MOBPAR_FORMAL) {
} }
} }
if(andmode == 0 && cmode == (canmove ? emNormal : emQuit) && !mouseout()) { if(andmode == 0 && size(screens) == 1 && !mouseout()) {
bool forcetarget = longclick; bool forcetarget = longclick;
@ -499,7 +515,7 @@ void mobile_draw(MOBPAR_FORMAL) {
if(playermoved && vid.sspeed > -4.99) if(playermoved && vid.sspeed > -4.99)
centerpc(tdiff / 1000.0 * exp(vid.sspeed)); centerpc(tdiff / 1000.0 * exp(vid.sspeed));
if(shmup::on && (andmode == 0 || andmode == 10) && cmode == emNormal) if(shmup::on && (andmode == 0 || andmode == 10) && size(screens) == 1)
shmup::turn(tdiff); shmup::turn(tdiff);
safety = false; safety = false;
@ -520,7 +536,7 @@ void mobile_draw(MOBPAR_FORMAL) {
if(hypot(mousex - xmove, mousey - yb) < rad) targetclick = false; if(hypot(mousex - xmove, mousey - yb) < rad) targetclick = false;
} }
if(cmode == emNormal) { if(size(screens) == 1) {
lmouseover = (gtouched && lclicked) ? mouseover : NULL; lmouseover = (gtouched && lclicked) ? mouseover : NULL;
if(!shmup::on && !useRangedOrb && vid.mobilecompasssize) { if(!shmup::on && !useRangedOrb && vid.mobilecompasssize) {
using namespace shmupballs; using namespace shmupballs;
@ -530,7 +546,7 @@ void mobile_draw(MOBPAR_FORMAL) {
if(h < rad) { if(h < rad) {
if(h < rad*SKIPFAC) { lmouseover = cwt.c; mousedest.d = -1; } if(h < rad*SKIPFAC) { lmouseover = cwt.c; mousedest.d = -1; }
else { else {
double d = revcontrol ? -1 : 1; double d = vid.revcontrol ? -1 : 1;
mouseh = hpxy(dx * d / rad, dy * d / rad); mouseh = hpxy(dx * d / rad, dy * d / rad);
calcMousedest(); calcMousedest();
} }
@ -556,6 +572,8 @@ void mobile_draw(MOBPAR_FORMAL) {
shiftmul = getcshift; shiftmul = getcshift;
calcMousedest(); calcMousedest();
inmenu = size(screens) > 1;
if(lclicked && !clicked && !inmenu) handleclick(MOBPAR_ACTUAL); if(lclicked && !clicked && !inmenu) handleclick(MOBPAR_ACTUAL);
if(inmenu && !clicked && !lclicked) inmenu = false; if(inmenu && !clicked && !lclicked) inmenu = false;
@ -581,29 +599,20 @@ void mobile_draw(MOBPAR_FORMAL) {
#ifdef ANDROIDSHARE #ifdef ANDROIDSHARE
if(getcstat == 's'-96 && keyreact) { if(getcstat == 's'-96 && keyreact) {
cmode = canmove ? emQuit : emNormal; popScreenAll().
shareScore(MOBPAR_ACTUAL); shareScore(MOBPAR_ACTUAL);
cmode = emNormal;
} }
#endif #endif
if(andmode == 2 && cmode != emNormal) andmode = 12; if(andmode == 2 && size(screens) != 1) andmode = 12;
if((cmode == emQuit && !canmove && keyreact && lclicked && !clicked) && !buttonclicked) { if((cmode == emQuit && !canmove && keyreact && lclicked && !clicked) && !buttonclicked) {
cmode = emNormal; printf("back to quit\n"); popScreenAll(); printf("back to quit\n");
} }
else if(cmode == emScores) handleScoreKeys(0, 0); else if(cmode == emScores) handleScoreKeys(0, 0);
else if(getcstat && keyreact) { else if(getcstat && keyreact) {
if(cmode == emMenu && getcstat == 'q') openURL();
if(cmode == (canmove ? emQuit : emNormal)) else { extra ex; handlekey(getcstat, getcstat, ex); }
handleQuit(getcstat, getcstat);
else {
if(cmode != emNormal && cmode != emQuit) inmenu = true;
if(cmode == emMenu && getcstat == 'q') openURL();
else { extra ex; handlekey(getcstat, getcstat, ex); }
}
} }
#ifdef IOS #ifdef IOS
@ -621,7 +630,7 @@ void mobile_draw(MOBPAR_FORMAL) {
if(andmode == 1 && lclicked && !clicked && !inmenu && mouseover) if(andmode == 1 && lclicked && !clicked && !inmenu && mouseover)
performMarkCommand(mouseover); performMarkCommand(mouseover);
if(clicked && andmode == 2 && (mouseover != lmouseover || mouseovers != lmouseovers) && cmode == emNormal) { if(clicked && andmode == 2 && (mouseover != lmouseover || mouseovers != lmouseovers) && !inmenu) {
addMessage(mouseovers); addMessage(mouseovers);
lmouseovers = mouseovers; lmouseovers = mouseovers;
} }
@ -630,7 +639,7 @@ void mobile_draw(MOBPAR_FORMAL) {
if(andmode == 20 && clicked != lclicked) andmode = 10; if(andmode == 20 && clicked != lclicked) andmode = 10;
if(andmode == 2 && lclicked && !clicked) { if(andmode == 2 && lclicked && !clicked) {
if(cmode == emNormal) if(!inmenu)
showHelp(MOBPAR_ACTUAL, help); showHelp(MOBPAR_ACTUAL, help);
else if(cmode != emScores && cmode != emPickScores) else if(cmode != emScores && cmode != emPickScores)
cmode = emNormal; cmode = emNormal;
@ -664,35 +673,3 @@ void mobile_draw(MOBPAR_FORMAL) {
#ifdef NOAUDIO #ifdef NOAUDIO
void playSound(cell*, const string &s, int vol) { printf("play sound: %s vol %d\n", s.c_str(), vol); } void playSound(cell*, const string &s, int vol) { printf("play sound: %s vol %d\n", s.c_str(), vol); }
#endif #endif
// optional hooks
// you may include hyper.cpp from another file and define EXTRA_... to change some things
namespace extra {
// on drawing cells
void drawcell(cell *c, const transmatrix& V);
// on each frame
void frame();
// on stats drawing
void stats();
// return true if key is handled
bool handleKey(int sym, int uni);
// return true to exit immediately
bool main(int argc, char **argv);
// extra configuration, called together with reading arguments
void config();
// read command line arguments
int arg();
// change land distribution
eLand getNext(eLand old);
// change musics
bool changeMusic(eLand id);
}

View File

@ -68,9 +68,9 @@ void loadsamples(const char *fname) {
while(true) { while(true) {
sample s; sample s;
alloc(s.val); alloc(s.val);
for(int i=0; i<cols; i++)
if(fscanf(f, "%lf", &s.val[i]) != 1) { fclose(f); return; }
if(feof(f)) break; if(feof(f)) break;
for(int i=0; i<cols; i++)
if(fscanf(f, "%lf", &s.val[i]) != 1) { break; }
fgetc(f); fgetc(f);
while(true) { while(true) {
int c = fgetc(f); int c = fgetc(f);

View File

@ -1596,26 +1596,12 @@ bool createOnSea(eLand old) {
(old == laOcean && (chaosmode ? hrand(2) : !generatingEquidistant)); (old == laOcean && (chaosmode ? hrand(2) : !generatingEquidistant));
} }
hookset<eLand(eLand)> *hooks_nextland;
eLand getNewLand(eLand old) { eLand getNewLand(eLand old) {
#ifdef EXTRA_NEWLAND eLand l = callhandlers(laNone, hooks_nextland, old);
if(true) { if(l) return l;
eLand l = extra::getNext(old);
if(l) return l;
}
#endif
#ifdef TOUR
if(tour::on) {
eLand l = tour::getNext(old);
if(l) return l;
}
if(peace::on) {
eLand l = peace::getNext(old);
if(l) return l;
}
#endif
if(cheatdest != old) if(!isCyclic(cheatdest) && !isTechnicalLand(cheatdest)) return cheatdest; if(cheatdest != old) if(!isCyclic(cheatdest) && !isTechnicalLand(cheatdest)) return cheatdest;

14103
language-data.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -26,9 +26,8 @@ namespace mapeditor {
#ifndef NOEDIT #ifndef NOEDIT
map<int, cell*> modelcell; map<int, cell*> modelcell;
void clearModelCells() { void handleKeyMap(int sym, int uni);
modelcell.clear(); bool handleKeyFile(int sym, int uni);
}
void applyModelcell(cell *c) { void applyModelcell(cell *c) {
if(mapeditor::whichPattern == 'H') return; if(mapeditor::whichPattern == 'H') return;
@ -157,7 +156,6 @@ namespace mapstream {
clearMemory(); clearMemory();
initcells(); initcells();
cleargraphmemory();
if(shmup::on) shmup::init(); if(shmup::on) shmup::init();
while(true) { while(true) {
@ -425,8 +423,6 @@ namespace mapeditor {
return true; return true;
} }
int subscreen; //0=normal, 1=config, 2=patterns, 3=predesigned
cell *drawcell; cell *drawcell;
#ifndef NOEDIT #ifndef NOEDIT
@ -557,11 +553,15 @@ namespace mapeditor {
return f1.first < f2.first; return f1.first < f2.first;
} }
string filecaption, cfileext;
string *cfileptr;
void drawFileDialog() { void drawFileDialog() {
displayfr(vid.xres/2, 30 + vid.fsize, 2, vid.fsize, displayfr(vid.xres/2, 30 + vid.fsize, 2, vid.fsize,
XLAT(cmode == emDraw ? "pics to save/load:" : "level to save/load:"), forecolor, 8); filecaption, forecolor, 8);
string& cfile = *cfileptr;
string cfile = cmode == emDraw ? picfile : levelfile;
displayfr(vid.xres/2, 34 + vid.fsize * 2, 2, vid.fsize, displayfr(vid.xres/2, 34 + vid.fsize * 2, 2, vid.fsize,
cfile, editext ? 0xFF00FF : 0xFFFF00, 8); cfile, editext ? 0xFF00FF : 0xFFFF00, 8);
@ -589,7 +589,7 @@ namespace mapeditor {
while ((dir = readdir(d)) != NULL) { while ((dir = readdir(d)) != NULL) {
string s = dir->d_name; string s = dir->d_name;
if(s != ".." && s[0] == '.') ; if(s != ".." && s[0] == '.') ;
else if(size(s) > 4 && s.substr(size(s)-4) == (cmode == emDraw ? ".pic" : ".lev")) else if(size(s) > 4 && s.substr(size(s)-4) == cfileext)
v.push_back(make_pair(s, CFILE)); v.push_back(make_pair(s, CFILE));
else if(dir->d_type & DT_DIR) else if(dir->d_type & DT_DIR)
v.push_back(make_pair(s+"/", CDIR)); v.push_back(make_pair(s+"/", CDIR));
@ -608,6 +608,8 @@ namespace mapeditor {
displayColorButton(x, y, v[i].first, 1000 + i, 0, 0, v[i].second, 0xFFFF00); displayColorButton(x, y, v[i].first, 1000 + i, 0, 0, v[i].second, 0xFFFF00);
} }
keyhandler = handleKeyFile;
} }
void displayFunctionKeys() { void displayFunctionKeys() {
@ -630,142 +632,190 @@ namespace mapeditor {
v.push_back(make_pair(s, i)); v.push_back(make_pair(s, i));
} }
void showPrePattern() {
dialog::init("predesigned patterns");
dialog::addItem(XLAT("Gameboard"), 'g');
dialog::addItem(XLAT("random colors"), 'r');
dialog::addItem(XLAT("rainbow landscape"), 'l');
dialog::addItem(XLAT("dark rainbow landscape"), 'd');
dialog::addItem(XLAT("football"), 'F');
dialog::addSelItem(XLAT("emerald pattern"), "emerald", 'e');
dialog::addSelItem(XLAT("four elements"), "palace", 'b');
dialog::addSelItem(XLAT("eight domains"), "palace", 'a');
dialog::addSelItem(XLAT("zebra pattern"), "zebra", 'z');
dialog::addSelItem(XLAT("four triangles"), "zebra", 't');
dialog::addSelItem(XLAT("three stripes"), "zebra", 'x');
dialog::addSelItem(XLAT("random black-and-white"), "current", 'w');
dialog::addSelItem(XLAT("field pattern C"), "field", 'C');
dialog::addSelItem(XLAT("field pattern D"), "field", 'D');
dialog::addSelItem(XLAT("field pattern N"), "field", 'N');
dialog::addSelItem(XLAT("field pattern S"), "field", 'S');
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if((uni >= 'a' && uni <= 'z') || (uni >= 'A' && uni <= 'Z')) {
whichCanvas = uni;
subcanvas = rand();
firstland = laCanvas; randomPatternsMode = false;
restartGame();
}
else if(doexiton(sym, uni)) popScreen();
};
}
void showPattern() {
dialog::init();
dialog::addBoolItem(XLAT(euclid ? "three colors" : "Emerald Pattern"), (whichPattern == 'f'), 'f');
dialog::addBoolItem(XLAT("Palace Pattern"), (whichPattern == 'p'), 'p');
dialog::addBoolItem(XLAT(euclid ? "three colors rotated" : "Zebra Pattern"), (whichPattern == 'z'), 'z');
dialog::addBoolItem(XLAT("field pattern"), (whichPattern == 'F'), 'F');
if(whichPattern == 'f') symRotation = true;
if(whichPattern == 'F') ;
else if(!euclid) {
dialog::addBoolItem(XLAT("rotational symmetry"), (symRotation), '0');
dialog::addBoolItem(XLAT("symmetry 0-1"), (sym01), '1');
dialog::addBoolItem(XLAT("symmetry 0-2"), (sym02), '2');
dialog::addBoolItem(XLAT("symmetry 0-3"), (sym03), '3');
}
else
dialog::addBoolItem(XLAT("edit all three colors"), (symRotation), '0');
dialog::addBoolItem(XLAT("display pattern codes (full)"), (displaycodes == 1), 'd');
dialog::addBoolItem(XLAT("display pattern codes (simplified)"), (displaycodes == 2), 's');
dialog::addBoolItem(XLAT("display only hexagons"), (whichShape == '6'), '6');
dialog::addBoolItem(XLAT("display only heptagons"), (whichShape == '7'), '7');
dialog::addBoolItem(XLAT("display the triheptagonal grid"), (whichShape == '8'), '8');
dialog::addItem(XLAT("line patterns"), 'l');
dialog::addItem(XLAT("predesigned patterns"), 'r');
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni == 'f' || uni == 'p' || uni == 'z' || uni == 'H' || uni == 'F') {
if(whichPattern == uni) whichPattern = 0;
else whichPattern = uni;
modelcell.clear();
}
else if(uni == '0') symRotation = !symRotation;
else if(uni == '1') sym01 = !sym01;
else if(uni == '2') sym02 = !sym02;
else if(uni == '3') sym03 = !sym03;
else if(uni == '6' || uni == '7' || uni == '8') {
if(whichShape == uni) whichShape = 0;
else whichShape = uni;
}
else if(uni == '3') sym03 = !sym03;
else if(uni == 'd') displaycodes = displaycodes == 1 ? 0 : 1;
else if(uni == 's') displaycodes = displaycodes == 2 ? 0 : 2;
else if(uni == 'l')
pushScreen(linepatterns::showMenu);
else if(uni == 'r') pushScreen(showPrePattern);
else if(doexiton(sym, uni)) popScreen();
};
}
void showList() {
v.clear();
if(painttype == 4) painttype = 0;
switch(painttype) {
case 0:
for(int i=0; i<motypes; i++) {
eMonster m = eMonster(i);
if(
m == moTongue || m == moPlayer || m == moFireball || m == moBullet ||
m == moFlailBullet || m == moShadow || m == moAirball ||
m == moWolfMoved || m == moGolemMoved ||
m == moTameBomberbirdMoved || m == moKnightMoved ||
m == moDeadBug || m == moLightningBolt || m == moDeadBird ||
m == moMouseMoved || m == moPrincessMoved || m == moPrincessArmedMoved) ;
else vpush(i, minf[i].name);
}
break;
case 1:
for(int i=0; i<ittypes; i++) vpush(i, iinf[i].name);
break;
case 2:
for(int i=0; i<landtypes; i++) vpush(i, linf[i].name);
break;
case 3:
for(int i=0; i<walltypes; i++) if(i != waChasmD) vpush(i, winf[i].name);
break;
}
// sort(v.begin(), v.end());
if(infix != "") mouseovers = infix;
int q = v.size();
int percolumn = vid.yres / (vid.fsize+5) - 4;
int columns = 1 + (q-1) / percolumn;
for(int i=0; i<q; i++) {
int x = 16 + (vid.xres * (i/percolumn)) / columns;
int y = (vid.fsize+5) * (i % percolumn) + vid.fsize*2;
int actkey = 1000 + i;
string vv = v[i].first;
if(i < 9) { vv += ": "; vv += ('1' + i); }
displayButton(x, y, vv, actkey, 0);
}
keyhandler = [] (int sym, int uni) {
if(uni >= '1' && uni <= '9') uni = 1000 + uni - '1';
if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER || sym == '-' || sym == SDLK_KP_MINUS) uni = 1000;
for(int z=0; z<size(v); z++) if(1000 + z == uni) {
paintwhat = v[z].second;
paintwhat_str = v[z].first;
mousepressed = false;
popScreen();
return;
}
if(editInfix(uni)) ;
else if(doexiton(sym, uni)) popScreen();
};
}
void showMapEditor() { void showMapEditor() {
cmode2 = smMap;
gamescreen(0);
if(choosefile) { drawFileDialog(); return; } int fs = vid.fsize + 5;
if(subscreen == 2) { getcstat = '-';
dialog::init();
dialog::addBoolItem(XLAT(euclid ? "three colors" : "Emerald Pattern"), (whichPattern == 'f'), 'f'); displayfr(8, 8 + fs, 2, vid.fsize, paintwhat_str, forecolor, 0);
dialog::addBoolItem(XLAT("Palace Pattern"), (whichPattern == 'p'), 'p'); displayfr(8, 8+fs*2, 2, vid.fsize, XLAT("use at your own risk!"), 0x800000, 0);
dialog::addBoolItem(XLAT(euclid ? "three colors rotated" : "Zebra Pattern"), (whichPattern == 'z'), 'z');
dialog::addBoolItem(XLAT("field pattern"), (whichPattern == 'F'), 'F');
if(whichPattern == 'f') symRotation = true; displayButton(8, 8+fs*4, XLAT("0-9 = radius (%1)", its(radius)), ('0' + (radius+1)%10), 0);
if(whichPattern == 'F') ; displayButton(8, 8+fs*5, XLAT("b = boundary"), 'b', 0);
else if(!euclid) { displayButton(8, 8+fs*6, XLAT("m = monsters"), 'm', 0);
dialog::addBoolItem(XLAT("rotational symmetry"), (symRotation), '0'); displayButton(8, 8+fs*7, XLAT("w = walls"), 'w', 0);
dialog::addBoolItem(XLAT("symmetry 0-1"), (sym01), '1'); displayButton(8, 8+fs*8, XLAT("i = items"), 'i', 0);
dialog::addBoolItem(XLAT("symmetry 0-2"), (sym02), '2'); displayButton(8, 8+fs*9, XLAT("l = lands"), 'l', 0);
dialog::addBoolItem(XLAT("symmetry 0-3"), (sym03), '3'); displayfr(8, 8+fs*10, 2, vid.fsize, XLAT("c = copy"), 0xC0C0C0, 0);
} displayButton(8, 8+fs*11, XLAT("u = undo"), 'u', 0);
else if(painttype == 4)
dialog::addBoolItem(XLAT("edit all three colors"), (symRotation), '0'); displayButton(8, 8+fs*12, XLAT("f = flip %1", ONOFF(copyflip)), 'u', 0);
displayButton(8, 8+fs*13, XLAT("r = regular"), 'r', 0);
displayButton(8, 8+fs*14, XLAT("p = paint"), 'p', 0);
dialog::addBoolItem(XLAT("display pattern codes (full)"), (displaycodes == 1), 'd'); displayFunctionKeys();
dialog::addBoolItem(XLAT("display pattern codes (simplified)"), (displaycodes == 2), 's');
dialog::addBoolItem(XLAT("display only hexagons"), (whichShape == '6'), '6'); keyhandler = handleKeyMap;
dialog::addBoolItem(XLAT("display only heptagons"), (whichShape == '7'), '7');
dialog::addBoolItem(XLAT("display the triheptagonal grid"), (whichShape == '8'), '8');
dialog::addItem(XLAT("line patterns"), 'l');
dialog::addItem(XLAT("predesigned patterns"), 'r');
dialog::display();
}
else if(subscreen == 3) {
dialog::init("predesigned patterns");
dialog::addItem(XLAT("Gameboard"), 'g');
dialog::addItem(XLAT("random colors"), 'r');
dialog::addItem(XLAT("rainbow landscape"), 'l');
dialog::addItem(XLAT("dark rainbow landscape"), 'd');
dialog::addItem(XLAT("football"), 'F');
dialog::addSelItem(XLAT("emerald pattern"), "emerald", 'e');
dialog::addSelItem(XLAT("four elements"), "palace", 'b');
dialog::addSelItem(XLAT("eight domains"), "palace", 'a');
dialog::addSelItem(XLAT("zebra pattern"), "zebra", 'z');
dialog::addSelItem(XLAT("four triangles"), "zebra", 't');
dialog::addSelItem(XLAT("three stripes"), "zebra", 'x');
dialog::addSelItem(XLAT("random black-and-white"), "current", 'w');
dialog::addSelItem(XLAT("field pattern C"), "field", 'C');
dialog::addSelItem(XLAT("field pattern D"), "field", 'D');
dialog::addSelItem(XLAT("field pattern N"), "field", 'N');
dialog::addSelItem(XLAT("field pattern S"), "field", 'S');
dialog::display();
}
else if(subscreen == 1 && painttype == 6)
dialog::drawColorDialog(paintwhat);
else if(subscreen == 1) {
v.clear();
if(painttype == 4) painttype = 0;
switch(painttype) {
case 0:
for(int i=0; i<motypes; i++) {
eMonster m = eMonster(i);
if(
m == moTongue || m == moPlayer || m == moFireball || m == moBullet ||
m == moFlailBullet || m == moShadow || m == moAirball ||
m == moWolfMoved || m == moGolemMoved ||
m == moTameBomberbirdMoved || m == moKnightMoved ||
m == moDeadBug || m == moLightningBolt || m == moDeadBird ||
m == moMouseMoved || m == moPrincessMoved || m == moPrincessArmedMoved) ;
else vpush(i, minf[i].name);
}
break;
case 1:
for(int i=0; i<ittypes; i++) vpush(i, iinf[i].name);
break;
case 2:
for(int i=0; i<landtypes; i++) vpush(i, linf[i].name);
break;
case 3:
for(int i=0; i<walltypes; i++) if(i != waChasmD) vpush(i, winf[i].name);
break;
}
// sort(v.begin(), v.end());
if(infix != "") mouseovers = infix;
int q = v.size();
int percolumn = vid.yres / (vid.fsize+5) - 4;
int columns = 1 + (q-1) / percolumn;
for(int i=0; i<q; i++) {
int x = 16 + (vid.xres * (i/percolumn)) / columns;
int y = (vid.fsize+5) * (i % percolumn) + vid.fsize*2;
int actkey = 1000 + i;
string vv = v[i].first;
if(i < 9) { vv += ": "; vv += ('1' + i); }
displayButton(x, y, vv, actkey, 0);
}
}
else {
/* displayfr(vid.xres/2, vid.ycenter - vid.radius * 3/4 - vid.fsize*3/2, 2,
vid.fsize,
XLAT("MAP EDITOR: ") + paintwhat_str,
0xFFFFFF, 8); */
int fs = vid.fsize + 5;
getcstat = '-';
displayfr(8, 8 + fs, 2, vid.fsize, paintwhat_str, forecolor, 0);
displayfr(8, 8+fs*2, 2, vid.fsize, XLAT("use at your own risk!"), 0x800000, 0);
displayButton(8, 8+fs*4, XLAT("0-9 = radius (%1)", its(radius)), ('0' + (radius+1)%10), 0);
displayButton(8, 8+fs*5, XLAT("b = boundary"), 'b', 0);
displayButton(8, 8+fs*6, XLAT("m = monsters"), 'm', 0);
displayButton(8, 8+fs*7, XLAT("w = walls"), 'w', 0);
displayButton(8, 8+fs*8, XLAT("i = items"), 'i', 0);
displayButton(8, 8+fs*9, XLAT("l = lands"), 'l', 0);
displayfr(8, 8+fs*10, 2, vid.fsize, XLAT("c = copy"), 0xC0C0C0, 0);
displayButton(8, 8+fs*11, XLAT("u = undo"), 'u', 0);
if(painttype == 4)
displayButton(8, 8+fs*12, XLAT("f = flip %1", ONOFF(copyflip)), 'u', 0);
displayButton(8, 8+fs*13, XLAT("r = regular"), 'r', 0);
displayButton(8, 8+fs*14, XLAT("p = paint"), 'p', 0);
displayFunctionKeys();
}
} }
int spillinc() { int spillinc() {
@ -1032,16 +1082,16 @@ namespace mapeditor {
} }
} }
bool handleKeyFile(int uni, int& sym) { bool handleKeyFile(int uni, int sym) {
string& s(cmode == emDraw ? picfile : levelfile); string& s(*cfileptr);
int i = size(s) - (editext?0:4); int i = size(s) - (editext?0:4);
if(uni > 2000) sym = uni - 2000; if(uni > 2000) sym = uni - 2000;
if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER || sym == SDLK_ESCAPE) { if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER || sym == SDLK_ESCAPE) {
choosefile = false; popScreen();
return true; return true;
} }
else if(sym == SDLK_F2 || sym == SDLK_F3) { else if(sym == SDLK_F2 || sym == SDLK_F3) {
choosefile = false; popScreen();
return false; return false;
} }
else if(sym == SDLK_F4) { else if(sym == SDLK_F4) {
@ -1075,132 +1125,80 @@ namespace mapeditor {
return true; return true;
} }
void handleKey(int sym, int uni) { void handleKeyMap(int sym, int uni) {
if(choosefile && handleKeyFile(sym, uni)) ; handlePanning(sym, uni);
else if(subscreen == 1 && painttype == 6) {
// left-clicks are coded with '-', and right-clicks are coded with sym F1
if(uni == '-') undoLock();
if(mousepressed && mouseover && sym != SDLK_F1)
allInPattern(mouseover, radius, neighborId(mouseover, mouseover2));
if(mouseover) for(int i=0; i<mouseover->type; i++) createMov(mouseover, i);
if(uni == 'u') applyUndo();
else if(uni == 'v' || sym == SDLK_F10 || sym == SDLK_ESCAPE) popScreen();
else if(uni >= '0' && uni <= '9') radius = uni - '0';
else if(uni == 'm') pushScreen(showList), painttype = 0, infix = "";
else if(uni == 'i') pushScreen(showList), painttype = 1, infix = "";
else if(uni == 'l') pushScreen(showList), painttype = 2, infix = "";
else if(uni == 'w') pushScreen(showList), painttype = 3, infix = "";
else if(uni == 'r') pushScreen(showPattern);
else if(uni == 't' && mouseover) {
cwt.c = mouseover; playermoved = true;
cwt.spin = neighborId(mouseover, mouseover2);
}
else if(uni == 'b') painttype = 5, paintwhat_str = XLAT("boundary");
else if(uni == 'p') {
painttype = 6;
paintwhat_str = "paint"; paintwhat_str = "paint";
int v = dialog::handleKeyColor(sym, uni, paintwhat); dialog::openColorDialog(paintwhat = (painttype ==6 ? paintwhat : 0x808080));
if(v == 1) subscreen = 0;
if(v == 2) cmode = emNormal;
} }
else if(subscreen == 1) { else if(sym == SDLK_F2) {
if(uni >= '1' && uni <= '9') uni = 1000 + uni - '1'; if(mapstream::saveMap(levelfile.c_str()))
if(sym == SDLK_RETURN || sym == SDLK_KP_ENTER || sym == '-' || sym == SDLK_KP_MINUS) uni = 1000; addMessage(XLAT("Map saved to %1", levelfile));
for(int z=0; z<size(v); z++) if(1000 + z == uni) { else
paintwhat = v[z].second; addMessage(XLAT("Failed to save map to %1", levelfile));
paintwhat_str = v[z].first;
subscreen = 0;
mousepressed = false;
}
if(editInfix(uni)) ;
else if(subscreen == 1 && uni != 0) cmode = emNormal;
} }
else if(subscreen == 3) { else if(sym == SDLK_F5) {
dialog::handleNavigation(sym, uni); restartGame();
if((uni >= 'a' && uni <= 'z') || (uni >= 'A' && uni <= 'Z')) {
whichCanvas = uni;
subcanvas = rand();
firstland = laCanvas; randomPatternsMode = false;
restartGame(); subscreen = 0;
}
else if(uni != 0) subscreen = 0;
} }
else if(subscreen == 2) { else if(sym == SDLK_F3) {
dialog::handleNavigation(sym, uni); if(mapstream::loadMap(levelfile.c_str()))
if(uni == 'f' || uni == 'p' || uni == 'z' || uni == 'H' || uni == 'F') { addMessage(XLAT("Map loaded from %1", levelfile));
if(whichPattern == uni) whichPattern = 0; else
else whichPattern = uni; addMessage(XLAT("Failed to load map from %1", levelfile));
clearModelCells();
}
else if(uni == '0') symRotation = !symRotation;
else if(uni == '1') sym01 = !sym01;
else if(uni == '2') sym02 = !sym02;
else if(uni == '3') sym03 = !sym03;
else if(uni == '6' || uni == '7' || uni == '8') {
if(whichShape == uni) whichShape = 0;
else whichShape = uni;
}
else if(uni == '3') sym03 = !sym03;
else if(uni == 'd') displaycodes = displaycodes == 1 ? 0 : 1;
else if(uni == 's') displaycodes = displaycodes == 2 ? 0 : 2;
else if(uni == 'l')
cmode = emLinepattern;
else if(uni == 'r')
subscreen = 3;
else if(uni != 0) subscreen = 0;
} }
else { else if(sym == SDLK_F4) {
// left-clicks are coded with '-', and right-clicks are coded with sym F1 cfileptr = &levelfile;
if(uni == '-') undoLock(); filecaption = XLAT("level to save/load:");
if(mousepressed && mouseover && sym != SDLK_F1) cfileext = ".lev";
allInPattern(mouseover, radius, neighborId(mouseover, mouseover2)); pushScreen(drawFileDialog);
}
if(mouseover) for(int i=0; i<mouseover->type; i++) createMov(mouseover, i); else if(sym == SDLK_F6) {
if(uni == 'u') applyUndo(); saveHighQualityShot();
else if(uni == 'v' || sym == SDLK_F10 || sym == SDLK_ESCAPE) cmode = emNormal; }
else if(uni >= '0' && uni <= '9') radius = uni - '0'; else if(sym == SDLK_F8) {
else if(uni == 'm') subscreen = 1, painttype = 0, infix = ""; svg::render();
else if(uni == 'i') subscreen = 1, painttype = 1, infix = ""; }
else if(uni == 'l') subscreen = 1, painttype = 2, infix = ""; else if(sym == SDLK_F7) {
else if(uni == 'w') subscreen = 1, painttype = 3, infix = ""; drawplayer = !drawplayer;
else if(uni == 'r') subscreen = 2; }
else if(uni == 't' && mouseover) { else if(uni == 'c') {
cwt.c = mouseover; playermoved = true; if(mouseover && mouseover2)
cwt.spin = neighborId(mouseover, mouseover2); copydir = neighborId(mouseover, mouseover2);
} if(copydir<0) copydir = 0;
else if(uni == 'b') painttype = 5, paintwhat_str = XLAT("boundary"); copyflip = (uni == 'f');
else if(uni == 'p') copywhat = mouseover, painttype = 4;
subscreen = 1, paintwhat = (painttype ==6 ? paintwhat : 0x808080), painttype = 6; paintwhat_str = XLAT("copying");
else if(sym == SDLK_F2) { }
if(mapstream::saveMap(levelfile.c_str())) else if(uni == 'f') {
addMessage(XLAT("Map saved to %1", levelfile)); copyflip = !copyflip;
else }
addMessage(XLAT("Failed to save map to %1", levelfile)); else if(uni == 'h' || sym == SDLK_F1)
} gotoHelp(mehelptext());
else if(sym == SDLK_F5) { else if(uni == ' ') {
restartGame(); popScreen();
} pushScreen(showDrawEditor);
else if(sym == SDLK_F3) { initdraw(mouseover ? mouseover : cwt.c);
if(mapstream::loadMap(levelfile.c_str()))
addMessage(XLAT("Map loaded from %1", levelfile));
else
addMessage(XLAT("Failed to load map from %1", levelfile));
}
else if(sym == SDLK_F4)
choosefile = true;
else if(sym == SDLK_F6) {
saveHighQualityShot();
}
else if(sym == SDLK_F8) {
svg::render();
}
else if(sym == SDLK_F7) {
drawplayer = !drawplayer;
}
else if(uni == 'c') {
if(mouseover && mouseover2)
copydir = neighborId(mouseover, mouseover2);
if(copydir<0) copydir = 0;
copyflip = (uni == 'f');
copywhat = mouseover, painttype = 4;
paintwhat_str = XLAT("copying");
}
else if(uni == 'f') {
copyflip = !copyflip;
}
else if(uni == 'h' || sym == SDLK_F1) {
lastmode = cmode;
cmode = emHelp;
help = mehelptext();
}
else if(uni == ' ') {
cmode = emDraw;
initdraw(mouseover ? mouseover : cwt.c);
}
} }
} }
@ -1216,7 +1214,7 @@ namespace mapeditor {
"the screen for more keys."; "the screen for more keys.";
string drawhelptext() { string drawhelptext() {
return XLAT(mapeditor::drawhelp); return XLAT(drawhelp);
} }
int dslayer; int dslayer;
@ -1235,83 +1233,57 @@ namespace mapeditor {
} }
void drawGhosts(cell *c, const transmatrix& V, int ct) { void drawGhosts(cell *c, const transmatrix& V, int ct) {
/* if(cmode == emDraw && cwt.c->type == 6 && ct == 6) for(int a=0; a<dsCur->rots; a++) {
transmatrix V2 = V * spin(M_PI + 2*M_PI*a/dsCur->rots);
if(mouseout()) break;
hyperpoint P2 = V2 * inverse(cwtV) * mouseh;
int xc, yc, sc;
getcoord(P2, xc, yc, sc);
displaychr(xc, yc, sc, 10, 'x', 0xFF);
if(crad > 0 && c->cpdist <= 3) {
lalpha = 0x80;
transmatrix movtocc = V2 * inverse(cwtV) * rgpushxto0(ccenter);
for(int d=0; d<84; d++)
drawline(movtocc * ddi(d+1, crad) * C0, movtocc * ddi(d, crad) * C0, 0xC00000);
lalpha = 0xFF;
}
} */
} }
hyperpoint ccenter = C0; hyperpoint ccenter = C0;
hyperpoint coldcenter = C0; hyperpoint coldcenter = C0;
void drawGrid() { void drawGrid() {
if(cmode == emDraw && !inHighQual) { for(int d=0; d<84; d++) {
transmatrix d2 = drawtrans * rgpushxto0(ccenter);
for(int d=0; d<84; d++) { int lalpha;
transmatrix d2 = drawtrans * rgpushxto0(ccenter); if(d % (84/drawcell->type) == 0)
int lalpha; lalpha = 0x40;
if(d % (84/drawcell->type) == 0) else
lalpha = 0x40; lalpha = 0x20;
else int col = darkena(0xC0C0C0, 0, lalpha);
lalpha = 0x20; queueline(d2 * C0, d2 * spin(M_PI*d/42)* xpush(1) * C0, col);
int col = darkena(0xC0C0C0, 0, lalpha); for(int u=2; u<=20; u++) {
queueline(d2 * C0, d2 * spin(M_PI*d/42)* xpush(1) * C0, col); if(u % 5 == 0) lalpha = 0x40;
for(int u=2; u<=20; u++) { else lalpha = 0x20;
if(u % 5 == 0) lalpha = 0x40; queueline(
else lalpha = 0x20; d2 * spin(M_PI*d/42)* xpush(u/20.) * C0,
queueline( d2 * spin(M_PI*(d+1)/42)* xpush(u/20.) * C0,
d2 * spin(M_PI*d/42)* xpush(u/20.) * C0, darkena(0xC0C0C0, 0, lalpha));
d2 * spin(M_PI*(d+1)/42)* xpush(u/20.) * C0,
darkena(0xC0C0C0, 0, lalpha));
}
} }
queueline(drawtrans*ccenter, drawtrans*coldcenter, darkena(0xC0C0C0, 0, 0x20)); }
queueline(drawtrans*ccenter, drawtrans*coldcenter, darkena(0xC0C0C0, 0, 0x20));
int sg = drawcellShapeGroup(); int sg = drawcellShapeGroup();
if(0) for(int i=0; i<USERSHAPEIDS; i++) if(editingShape(sg, i) && usershapes[sg][i]) { if(0) for(int i=0; i<USERSHAPEIDS; i++) if(editingShape(sg, i) && usershapes[sg][i]) {
usershapelayer &ds(usershapes[sg][i]->d[mapeditor::dslayer]); usershapelayer &ds(usershapes[sg][i]->d[mapeditor::dslayer]);
for(int a=0; a<size(ds.list); a++) { for(int a=0; a<size(ds.list); a++) {
hyperpoint P2 = drawtrans * ds.list[a]; hyperpoint P2 = drawtrans * ds.list[a];
queuechr(P2, 10, 'x', queuechr(P2, 10, 'x',
darkena(a == 0 ? 0x00FF00 : darkena(a == 0 ? 0x00FF00 :
a == size(ds.list)-1 ? 0xFF0000 : a == size(ds.list)-1 ? 0xFF0000 :
0xFFFF00, 0, 0xFF)); 0xFFFF00, 0, 0xFF));
}
} }
} }
} }
void drawHandleKey(int sym, int uni);
void showDrawEditor() { void showDrawEditor() {
cmode2 = smDraw;
drawGrid();
if(!mouseout()) getcstat = '-'; if(!mouseout()) getcstat = '-';
if(coloring) {
dialog::drawColorDialog(colortouse);
return;
}
if(choosefile) { drawFileDialog(); return; }
int sg = drawcellShapeGroup(); int sg = drawcellShapeGroup();
string line1, line2; string line1, line2;
@ -1395,6 +1367,8 @@ namespace mapeditor {
} }
displayFunctionKeys(); displayFunctionKeys();
keyhandler = drawHandleKey;
} }
bool rebuildPolys = false; bool rebuildPolys = false;
@ -1589,17 +1563,10 @@ namespace mapeditor {
void drawHandleKey(int sym, int uni) { void drawHandleKey(int sym, int uni) {
if(choosefile && handleKeyFile(sym, uni)) return; handlePanning(sym, uni);
if(uni == SETMOUSEKEY) mousekey = newmousekey; if(uni == SETMOUSEKEY) mousekey = newmousekey;
if(coloring) {
int v = dialog::handleKeyColor(sym, uni, colortouse);
if(v == 2) { coloring = false; return; }
else if(v == 1) { coloring = false; uni = COLORKEY; }
else return;
}
dslayer %= USERLAYERS; dslayer %= USERLAYERS;
hyperpoint mh = inverse(drawtrans) * mouseh; hyperpoint mh = inverse(drawtrans) * mouseh;
@ -1638,11 +1605,21 @@ namespace mapeditor {
if(uni == 'z') vid.scale *= 2; if(uni == 'z') vid.scale *= 2;
if(uni == 'o') vid.scale /= 2; if(uni == 'o') vid.scale /= 2;
if(uni == ' ' && cheater) cmode = emMapEditor; if(uni == ' ' && cheater) {
popScreen();
pushScreen(showMapEditor);
}
if(uni == 'p') coloring = true; if(uni == 'p')
dialog::openColorDialog(colortouse);
if(sym == SDLK_F4) choosefile = true; if(sym == SDLK_F4) {
filecaption = XLAT("pics to save/load:");
cfileptr = &picfile;
cfileext = ".pic";
pushScreen(drawFileDialog);
return;
}
if(sym == SDLK_F2) { if(sym == SDLK_F2) {
FILE *f = fopen(picfile.c_str(), "wt"); FILE *f = fopen(picfile.c_str(), "wt");
@ -1728,20 +1705,28 @@ namespace mapeditor {
if(usershapes[i][j]) delete usershapes[i][j]; if(usershapes[i][j]) delete usershapes[i][j];
} }
if(sym == SDLK_ESCAPE) cmode = emNormal; if(sym == SDLK_ESCAPE) popScreen();
if(sym == SDLK_F1) { if(sym == SDLK_F1) {
lastmode = cmode; gotoHelp(drawhelptext());
cmode = emHelp;
sym = 0;
help = drawhelptext();
} }
if(sym == SDLK_F10) cmode = emNormal; if(sym == SDLK_F10) popScreen();
if(rebuildPolys) if(rebuildPolys)
buildpolys(), rebuildPolys = false; buildpolys(), rebuildPolys = false;
} }
auto hooks = addHook(clearmemory, 0, [] () {
if(mapeditor::painttype == 4)
mapeditor::painttype = 0, mapeditor::paintwhat = 0,
mapeditor::paintwhat_str = "clear monster";
mapeditor::copywhat = NULL;
mapeditor::undo.clear();
if(!cheater) mapeditor::displaycodes = 0;
if(!cheater) mapeditor::whichShape = 0;
modelcell.clear();
});
#endif #endif
int canvasback = linf[laCanvas].color >> 2; int canvasback = linf[laCanvas].color >> 2;
@ -1897,7 +1882,7 @@ namespace mapeditor {
} }
#ifndef NOEDIT #ifndef NOEDIT
if(cmode == emDraw && mapeditor::editingShape(group, id)) { if(cmode2 == smDraw && mapeditor::editingShape(group, id)) {
/* for(int a=0; a<size(ds.list); a++) { /* for(int a=0; a<size(ds.list); a++) {
hyperpoint P2 = V * ds.list[a]; hyperpoint P2 = V * ds.list[a];
@ -2264,12 +2249,13 @@ lessalphaif(col, behindsphere(V), behindsphere(gmatrix[c2]))
dialog::addInfo("change the alpha parameter to show the lines"); dialog::addInfo("change the alpha parameter to show the lines");
dialog::display(); dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni >= 'a' && uni < 'a' + numpat)
dialog::openColorDialog(patterns[uni - 'a'].color, NULL);
else if(doexiton(sym,uni)) popScreen();
};
} }
void handleMenu(int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni >= 'a' && uni < 'a' + numpat)
dialog::openColorDialog(patterns[uni - 'a'].color, NULL);
else if(doexiton(sym,uni)) cmode = emNormal;
}
}; };

1729
menus.cpp

File diff suppressed because it is too large Load Diff

View File

@ -659,48 +659,48 @@ namespace netgen {
dialog::display(); dialog::display();
} }
}
void handleKey(int sym, int uni) { keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni); dialog::handleNavigation(sym, uni);
if(!loaded) {
loadData();
if(!loaded) { if(!loaded) {
addMessage(XLAT("Failed to load the file 'papermodeldata.txt'")); loadData();
cmode = emNormal; if(!loaded) {
addMessage(XLAT("Failed to load the file 'papermodeldata.txt'"));
popScreen();
return;
}
if(!created) {
View = Id;
if(centerover) viewctr.h = centerover->master;
else viewctr.h = cwt.c->master;
playermoved = false;
dataFromHR();
designNet();
created = 1;
return;
}
}
if(mode == 2 && uni != 0) {
mode = 0;
return; return;
} }
if(!created) { if(uni == 's') {
View = Id; View = Id;
if(centerover) viewctr.h = centerover->master; if(centerover) viewctr.h = centerover->master;
else viewctr.h = cwt.c->master; else viewctr.h = cwt.c->master;
playermoved = false; playermoved = false;
dataFromHR();
designNet();
created = 1;
return;
} }
} else if(uni == 'c') {
createPapermodel();
if(mode == 2 && uni != 0) { addMessage(XLAT("The paper model created as papermodel-*.bmp"));
mode = 0; }
return; else if(uni == 'd') designNet();
} else if(uni == 't') mode = 2;
if(uni == 's') { else if(doexiton(sym, uni))
View = Id; popScreen();
if(centerover) viewctr.h = centerover->master; };
else viewctr.h = cwt.c->master;
playermoved = false;
}
else if(uni == 'c') {
createPapermodel();
addMessage(XLAT("The paper model created as papermodel-*.bmp"));
}
else if(uni == 'd') designNet();
else if(uni == 't') mode = 2;
else if(doexiton(sym, uni))
cmode = emNormal;
} }
} }

View File

@ -955,9 +955,9 @@ string describe(shmup::monster *m) {
sort(alledges.begin(), alledges.end(), edgecmp); sort(alledges.begin(), alledges.end(), edgecmp);
help = "Edges: "; ::help = "Edges: ";
if(vd.info) help = (*vd.info) + "\n" + help; if(vd.info) ::help = (*vd.info) + "\n" + help;
for(int j=0; j<size(alledges); j++) { for(int j=0; j<size(alledges); j++) {
edgeinfo *ei = alledges[j]; edgeinfo *ei = alledges[j];
@ -1741,37 +1741,37 @@ void showMenu() {
dialog::addItem(XLAT("exit menu"), 'v'); dialog::addItem(XLAT("exit menu"), 'v');
dialog::display(); dialog::display();
}
void handleMenu(int sym, int uni) { keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni); dialog::handleNavigation(sym, uni);
if(uni == 't') if(uni == 't')
dialog::editNumber(sag::temperature, sag::lowtemp, sag::hightemp, 1, 0, XLAT("temperature"), ""); dialog::editNumber(sag::temperature, sag::lowtemp, sag::hightemp, 1, 0, XLAT("temperature"), "");
else if(uni == 'm') { else if(uni == 'm') {
sag::sagmode = sag::eSagmode( (1+sag::sagmode) % 3 ); sag::sagmode = sag::eSagmode( (1+sag::sagmode) % 3 );
} }
else if(uni == 'l') showlabels = !showlabels; else if(uni == 'l') showlabels = !showlabels;
else if(uni == 'v') rog3 = !rog3; else if(uni == 'v') rog3 = !rog3;
else if(uni == 'x') specialmark = !specialmark; else if(uni == 'x') specialmark = !specialmark;
else if(uni == 'b') backcolor ^= 0xFFFFFF, bordcolor ^= 0xFFFFFF, forecolor ^= 0xFFFFFF; else if(uni == 'b') backcolor ^= 0xFFFFFF, bordcolor ^= 0xFFFFFF, forecolor ^= 0xFFFFFF;
else if(uni == 'g') { else if(uni == 'g') {
dialog::editNumber(ggamma, 0, 5, .01, 0.5, XLAT("gamma value for edges"), ""); dialog::editNumber(ggamma, 0, 5, .01, 0.5, XLAT("gamma value for edges"), "");
dialog::sidedialog = true; dialog::sidedialog = true;
} }
else if(uni == 'z') { else if(uni == 'z') {
for(int i=0; i<size(named)-1; i++) if(named[i] == cwt.c) for(int i=0; i<size(named)-1; i++) if(named[i] == cwt.c)
swap(named[i], named[i+1]); swap(named[i], named[i+1]);
if(!size(named) || named[size(named)-1] != cwt.c) named.push_back(cwt.c); if(!size(named) || named[size(named)-1] != cwt.c) named.push_back(cwt.c);
printf("named = %d\n", size(named)); printf("named = %d\n", size(named));
cmode = emNormal; popScreen();
} }
else if(kind == kKohonen && kohonen::handleMenu(sym, uni)) ; else if(kind == kKohonen && kohonen::handleMenu(sym, uni)) ;
else if(doexiton(sym, uni)) cmode = emNormal; else if(doexiton(sym, uni)) popScreen();
};
} }
void processKey(int sym, int uni) { } void processKey(int sym, int uni) { }
string help() { string makehelp() {
string ret = string ret =
"This is RogueViz, a visualization engine based on HyperRogue.\n\nUse WASD to move, v for menu.\n\n" "This is RogueViz, a visualization engine based on HyperRogue.\n\nUse WASD to move, v for menu.\n\n"
"Read more about RogueViz on : http://roguetemple.com/z/hyper/rogueviz.php\n\n"; "Read more about RogueViz on : http://roguetemple.com/z/hyper/rogueviz.php\n\n";
@ -1822,15 +1822,16 @@ template<class T, class T1> function<void(presmode)> roguevizslide_action(char c
return [c,t,act] (presmode mode) { return [c,t,act] (presmode mode) {
mapeditor::canvasback = 0x101010; mapeditor::canvasback = 0x101010;
setCanvas(mode, c); setCanvas(mode, c);
if(mode == 1 || mode == pmGeometryStart) t(); if(mode == pmStart || mode == pmGeometryStart) t();
if(mode == 3 || mode == pmGeometry || mode == pmGeometryReset) { act(mode);
if(mode == pmStop || mode == pmGeometry || mode == pmGeometryReset) {
rogueviz::close(); rogueviz::close();
shmup::clearMonsters(); shmup::clearMonsters();
if(mode == pmGeometryReset) t(); if(mode == pmGeometryReset) t();
} }
act(mode);
}; };
} }
@ -1983,4 +1984,11 @@ slide rvslides[] = {
} }
auto hooks =
addHook(hooks_frame, 0, drawExtra) +
addHook(hooks_args, 100, readArgs) +
addHook(clearmemory, 0, clear) +
addHook(hooks_config, 0, [] () { ss::list(rogueviz::rvtour::rvslides); });
}; };

61
rug.cpp
View File

@ -667,50 +667,45 @@ void show() {
dialog::addBoolItem(XLAT("render texture without OpenGL"), (rendernogl), 'g'); dialog::addBoolItem(XLAT("render texture without OpenGL"), (rendernogl), 'g');
dialog::addSelItem(XLAT("texture size"), its(texturesize)+"x"+its(texturesize), 's'); dialog::addSelItem(XLAT("texture size"), its(texturesize)+"x"+its(texturesize), 's');
dialog::display(); dialog::display();
} keyhandler = [] (int sym, int uni) {
#ifdef PANDORA
rendernogl = true;
#endif
dialog::handleNavigation(sym, uni);
void handleKey(int sym, int uni) { if(uni == 'h') gotoHelp(
#ifdef PANDORA
rendernogl = true;
#endif
dialog::handleNavigation(sym, uni);
if(uni == 'h') {
lastmode = cmode;
cmode = emHelp;
help =
"In this mode, HyperRogue is played on a 3D model of a part of the hyperbolic plane, " "In this mode, HyperRogue is played on a 3D model of a part of the hyperbolic plane, "
"similar to one you get from the 'paper model creator' or by hyperbolic crocheting.\n\n" "similar to one you get from the 'paper model creator' or by hyperbolic crocheting.\n\n"
"This requires some OpenGL extensions and may crash or not work correctly -- enabling " "This requires some OpenGL extensions and may crash or not work correctly -- enabling "
"the 'render texture without OpenGL' options may be helpful in this case. Also the 'render once' option " "the 'render texture without OpenGL' options may be helpful in this case. Also the 'render once' option "
"will make the rendering faster, but the surface will be rendered only once, so " "will make the rendering faster, but the surface will be rendered only once, so "
"you won't be able to play a game on it.\n\n" "you won't be able to play a game on it.\n\n"
"Use arrow keys to rotate, Page Up/Down to zoom."; "Use arrow keys to rotate, Page Up/Down to zoom."
} );
else if(uni == 'u') { else if(uni == 'u') {
if(sphere) restartGame('E'); if(sphere) restartGame('E');
if(euclid) restartGame('e'); if(euclid) restartGame('e');
rug::init(); rug::init();
cmode = emNormal; popScreen();
} }
else if(uni == 'o') else if(uni == 'o')
renderonce = !renderonce; renderonce = !renderonce;
#ifndef PANDORA #ifndef PANDORA
else if(uni == 'g') else if(uni == 'g')
rendernogl = !rendernogl; rendernogl = !rendernogl;
#endif #endif
else if(uni == 's') { else if(uni == 's') {
texturesize *= 2; texturesize *= 2;
if(texturesize == 8192) texturesize = 128; if(texturesize == 8192) texturesize = 128;
dialog::scaleLog(); dialog::scaleLog();
} }
else if(doexiton(sym, uni)) else if(doexiton(sym, uni)) popScreen();
cmode = emChangeMode; };
} }
void select() { void select() {
if(rug::rugged) rug::close(); if(rug::rugged) rug::close();
else cmode = emRugConfig; else pushScreen(rug::show);
} }
} }

288
scores.cpp Normal file
View File

@ -0,0 +1,288 @@
#ifndef NOSAVE
vector<score> scores;
int scoresort = 2;
int scoredisplay = 1;
int scorefrom = 0;
int scoremode = 0;
bool scorerev = false;
bool scorecompare(const score& s1, const score &s2) {
return s1.box[scoresort] > s2.box[scoresort];
}
bool fakescore() {
return fakebox[scoredisplay];
}
string displayfor(score* S) {
// printf("S=%p, scoredisplay = %d\n", S, scoredisplay);
if(S == NULL) {
return XLATN(boxname[scoredisplay]);
}
if(scoredisplay == 0) {
char buf[10];
snprintf(buf, 10, "%d:%02d", S->box[0]/60, S->box[0]%60);
return buf;
}
if(scoredisplay == 1) {
time_t tim = S->box[1];
char buf[128]; strftime(buf, 128, "%c", localtime(&tim));
return buf;
}
return its(S->box[scoredisplay]);
}
void loadScores() {
scores.clear();
FILE *f = fopen(scorefile, "rt");
if(!f) {
printf("Could not open the score file '%s'!\n", scorefile);
addMessage(s0 + "Could not open the score file: " + scorefile);
return;
}
while(!feof(f)) {
char buf[120];
if(fgets(buf, 120, f) == NULL) break;
if(buf[0] == 'H' && buf[1] == 'y') {
score sc; bool ok = true;
{if(fscanf(f, "%s", buf) <= 0) break;} sc.ver = buf;
for(int i=0; i<MAXBOX; i++) {
if(fscanf(f, "%d", &sc.box[i]) <= 0) { boxid = i; break; }
}
for(int i=boxid; i<MAXBOX; i++) sc.box[i] = 0;
if(sc.ver >= "4.4") {
sc.box[0] = sc.box[65];
// the first executable on Steam included a corruption
if(sc.box[65] > 1420000000 && sc.box[65] < 1430000000) {
sc.box[0] = sc.box[65] - sc.box[1];
sc.box[65] = sc.box[0];
}
// do not include saves
if(sc.box[65 + 4 + itOrbSafety - itOrbLightning]) ok = false;
}
else
sc.box[0] = sc.box[1] - sc.box[0]; // could not save then
if(ok && boxid > 20) scores.push_back(sc);
}
}
fclose(f);
addMessage(its(size(scores))+" games have been recorded in "+scorefile);
pushScreen(showScores);
boxid = 0; applyBoxes();
scoresort = 2; reverse(scores.begin(), scores.end());
scoremode = 0;
if(shmup::on) scoremode = 1;
else if(hardcore) scoremode = 2;
scorefrom = 0;
stable_sort(scores.begin(), scores.end(), scorecompare);
#ifdef MOBILE
extern int andmode;
andmode = 2;
#endif
}
vector<pair<string, int> > pickscore_options;
void sortScores() {
if(scorerev) reverse(scores.begin(), scores.end());
else {
scorerev = true;
scoresort = scoredisplay;
stable_sort(scores.begin(), scores.end(), scorecompare);
}
}
void shiftScoreDisplay(int delta) {
scoredisplay = (scoredisplay + POSSCORE + delta) % POSSCORE, scorerev = false;
if(fakescore()) shiftScoreDisplay(delta);
}
void showScores() {
int y = vid.fsize * 7/2;
int bx = vid.fsize;
getcstat = 1;
string modes =
scoremode == 0 ? XLAT(", m - mode: normal") :
scoremode == 1 ? XLAT(", m - mode: shoot'em up") :
scoremode == 2 ? XLAT(", m - mode: hardcore only") :
"?";
if(euclid) modes += XLAT(" (E:%1)", euclidland);
mouseovers = XLAT("t/left/right - change display, up/down - scroll, s - sort by") + modes;
displaystr(bx*4, vid.fsize*2, 0, vid.fsize, "#", forecolor, 16);
displaystr(bx*8, vid.fsize*2, 0, vid.fsize, "$$$", forecolor, 16);
displaystr(bx*12, vid.fsize*2, 0, vid.fsize, XLAT("kills"), forecolor, 16);
displaystr(bx*18, vid.fsize*2, 0, vid.fsize, XLAT("time"), forecolor, 16);
displaystr(bx*22, vid.fsize*2, 0, vid.fsize, XLAT("ver"), forecolor, 16);
displaystr(bx*23, vid.fsize*2, 0, vid.fsize, displayfor(NULL), forecolor, 0);
if(scorefrom < 0) scorefrom = 0;
int id = 0;
int omit = scorefrom;
int rank = 0;
while(y < (ISMOBILE ? vid.yres - 5*vid.fsize : vid.yres - 2 * vid.fsize)) {
if(id >= size(scores)) break;
score& S(scores[id]);
bool wrongtype = false;
wrongtype |= (euclid && (!S.box[116] || S.box[120] != euclidland));
wrongtype |= (!euclid && S.box[116]);
wrongtype |= (scoremode == 1 && !S.box[119]);
wrongtype |= (scoremode != 1 && S.box[119]);
wrongtype |= (scoremode == 2 && (!S.box[117] || S.box[118] >= PUREHARDCORE_LEVEL));
if(wrongtype) { id++; continue; }
if(omit) { omit--; rank++; id++; continue; }
char buf[16];
rank++; sprintf(buf, "%d", rank);
displaystr(bx*4, y, 0, vid.fsize, buf, 0xC0C0C0, 16);
sprintf(buf, "%d", S.box[2]);
displaystr(bx*8, y, 0, vid.fsize, buf, 0xC0C0C0, 16);
sprintf(buf, "%d", S.box[3]);
displaystr(bx*12, y, 0, vid.fsize, buf, 0xC0C0C0, 16);
sprintf(buf, "%d:%02d", S.box[0]/60, S.box[0] % 60);
displaystr(bx*18, y, 0, vid.fsize, buf, 0xC0C0C0, 16);
displaystr(bx*22, y, 0, vid.fsize, S.ver, 0xC0C0C0, 16);
displaystr(bx*23, y, 0, vid.fsize, displayfor(&S), 0xC0C0C0, 0);
y += vid.fsize*5/4; id++;
}
#ifdef MOBILE
buttonclicked = false;
displayabutton(-1, +1, XLAT("SORT"), BTON);
displayabutton( 0, +1, XLAT("PICK"), BTON);
displayabutton(+1, +1, XLAT("PLAY"), BTON);
#endif
keyhandler = [] (int sym, int uni) {
#ifndef MOBILE
if(sym == SDLK_LEFT || sym == SDLK_KP4 || sym == 'h' || sym == 'a')
shiftScoreDisplay(-1);
else if(sym == SDLK_RIGHT || sym == SDLK_KP6 || sym == 'l' || sym == 'd')
shiftScoreDisplay(1);
else if(sym == 't') { mapeditor::infix = ""; pushScreen(showPickScores); }
else if(sym == SDLK_UP || sym == 'k' || sym == 'w')
scorefrom -= 5;
else if(sym == SDLK_DOWN || sym == 'j' || sym == 'x')
scorefrom += 5;
else if(sym == PSEUDOKEY_WHEELUP)
scorefrom--;
else if(sym == PSEUDOKEY_WHEELDOWN)
scorefrom++;
else if(sym == 's') sortScores();
else if(sym == 'm') { scoremode++; scoremode %= 3; }
else if(doexiton(sym, uni)) popScreen();
#else
static int scoredragx, scoredragy;
extern bool clicked, lclicked;
extern int andmode;
if(andmode) {
if(!clicked && !lclicked) {
andmode = 0;
scoredragx = mousex;
scoredragy = mousey;
}
}
else {
if(clicked && !lclicked)
scoredragx = mousex, scoredragy = mousey;
else if(lclicked && !clicked) {
if(mousey > vid.ycenter - 2 * vid.fsize) {
if(mousex < vid.xcenter*2/3) sortScores();
else if(mousex < vid.xcenter*4/3)
cmode = emPickScores;
else andmode = 0, popScreen();
}
}
else if(clicked && lclicked) {
// if(mousex > scoredragx + 80) scoredragx += 80, shiftScoreDisplay(1);
// if(mousex < scoredragx - 80) scoredragx -= 80, shiftScoreDisplay(-1);
while(mousey > scoredragy + vid.fsize) scoredragy += vid.fsize, scorefrom--;
while(mousey < scoredragy - vid.fsize) scoredragy -= vid.fsize, scorefrom++;
}
}
#endif
};
}
bool monsterpage = false;
void showPickScores() {
getcstat = '0';
int d = scoredisplay;
pickscore_options.clear();
scorerev = false;
for(int i=0; i<POSSCORE; i++) {
scoredisplay = i;
if(!fakescore()) {
string s = displayfor(NULL);
if(mapeditor::hasInfix(s))
if(monsbox[scoredisplay] == monsterpage)
pickscore_options.push_back(make_pair(s, i));
}
}
sort(pickscore_options.begin(), pickscore_options.end());
int q = (int) pickscore_options.size();
int percolumn = vid.yres / (vid.fsize+3) - 4;
int columns = 1 + (q-1) / percolumn;
for(int i=0; i<q; i++) {
int x = 16 + (vid.xres * (i/percolumn)) / columns;
int y = (vid.fsize+3) * (i % percolumn) + vid.fsize*2;
scoredisplay = pickscore_options[i].second;
if(q <= 9)
pickscore_options[i].first = pickscore_options[i].first + " [" + its(i+1) + "]";
if(!fakescore())
displayButton(x, y, pickscore_options[i].first, 1000+i, 0);
}
displayButton(vid.xres/2, vid.yres - vid.fsize*2, "kills", 'm', 8);
scoredisplay = d;
mouseovers = mapeditor::infix;
keyhandler = [] (int sym, int uni) {
extern int andmode;
andmode = 2;
if(uni == 'm') monsterpage = !monsterpage; else
if(uni >= '1' && uni <= '9') uni = uni + 1000 - '1';
else if(uni >= 1000 && uni < 1000 + size(pickscore_options)) {
scoredisplay = pickscore_options[uni - 1000].second;
popScreen();
}
else if(mapeditor::editInfix(uni)) ;
else if(doexiton(sym, uni)) popScreen();
};
}
#endif

View File

@ -4,12 +4,6 @@
// implementation of the shoot'em up mode // implementation of the shoot'em up mode
#ifdef MOBILE
#define SHMUPTITLE "shoot'em up mode"
#else
#define SHMUPTITLE "shoot'em up and multiplayer"
#endif
extern int mousex, mousey; extern int mousex, mousey;
extern bool clicked; extern bool clicked;
@ -215,6 +209,8 @@ bool shmupcfg;
bool configdead; bool configdead;
void handleConfig(int sym, int uni);
void showShmupConfig() { void showShmupConfig() {
#ifndef NOSDL #ifndef NOSDL
@ -362,6 +358,8 @@ void showShmupConfig() {
dialog::display(); dialog::display();
} }
keyhandler = handleConfig;
#endif #endif
} }
@ -380,7 +378,7 @@ void handleConfig(int sym, int uni) {
else if(uni == '7') vid.scfg.subconfig = 8; else if(uni == '7') vid.scfg.subconfig = 8;
else if(uni == 'j') vid.scfg.subconfig = SCJOY; else if(uni == 'j') vid.scfg.subconfig = SCJOY;
else if(uni == 'a') multi::alwaysuse = !multi::alwaysuse; else if(uni == 'a') multi::alwaysuse = !multi::alwaysuse;
else if(uni == 'b') cmode = emJoyConfig; else if(uni == 'b') pushScreen(showJoyConfig);
else if(uni == 'r') else if(uni == 'r')
for(int i=0; i<MAXPLAYER; i++) for(int i=0; i<MAXPLAYER; i++)
kills[i] = deaths[i] = treasures[i] = 0; kills[i] = deaths[i] = treasures[i] = 0;
@ -396,20 +394,7 @@ void handleConfig(int sym, int uni) {
if(vid.scfg.players <= 0) vid.scfg.players += MAXPLAYER; if(vid.scfg.players <= 0) vid.scfg.players += MAXPLAYER;
} }
else if(sym == SDLK_F1 || uni == '?' || uni == 'h') { else if(sym == SDLK_F1 || uni == '?' || uni == 'h') {
lastmode = cmode; gotoHelp("");
cmode = emHelp;
/* help =
"In the shmup (shoot'em up) mode, you can play a hyperbolic shoot'em up "
"game. The game is based on the usual turn-based grid-based HyperRogue, "
"but there are some changes. You fight by throwing knives, and you "
"have three extra lives. There are no friendly monsters, so Orbs of "
"Life and Friendship give you extra lives instead. Some other rules have been "
"adapted too.\n\n"
"It is possible for two players to play the shmup mode cooperatively "
"(locally). When playing together, lives, orbs, and treasures are shared, "
"knives recharge slower, orbs drain faster, and player characters are not "
"allowed to separate."; */
help = help =
XLAT( XLAT(
@ -438,8 +423,8 @@ help += XLATN(iinf[itOrbSafety].name); help += "\n\n";
help += XLAT("This menu can be also used to configure keys.\n\n"); help += XLAT("This menu can be also used to configure keys.\n\n");
} }
else if(uni || sym == SDLK_F10) { else if(doexiton(sym, uni)) {
cmode = emNormal; popScreen();
if(shmup::on != shmupcfg) { restartGame('s'); resetScores(); } if(shmup::on != shmupcfg) { restartGame('s'); resetScores(); }
else if(vid.scfg.players != players) { restartGame(); resetScores(); } else if(vid.scfg.players != players) { restartGame(); resetScores(); }
} }
@ -3335,8 +3320,23 @@ void virtualRebase(shmup::monster *m, bool tohex) {
virtualRebase(m->base, m->at, tohex); virtualRebase(m->base, m->at, tohex);
} }
} void addShmupHelp(string& out) {
if(shmup::mousetarget && intval(mouseh, tC0(shmup::mousetarget->pat)) < .1) {
out += ", ";
#ifdef ROGUEVIZ #ifdef ROGUEVIZ
#include "rogueviz.cpp" if(shmup::mousetarget->type == moRogueviz) {
help = XLAT(minf[shmup::mousetarget->type].help);
out += rogueviz::describe(shmup::mousetarget);
}
else
#endif #endif
{
out += XLAT1(minf[shmup::mousetarget->type].name);
help = generateHelpForMonster(shmup::mousetarget->type);
}
}
}
auto hooks = addHook(clearmemory, 0, shmup::clearMemory);
}

View File

@ -65,13 +65,13 @@ int musfadeval = 2000;
eLand cid = laNone; eLand cid = laNone;
hookset<bool(eLand)> *hooks_music;
void handlemusic() { void handlemusic() {
DEBB(DF_GRAPH, (debugfile,"handle music\n")); DEBB(DF_GRAPH, (debugfile,"handle music\n"));
if(audio && musicvolume) { if(audio && musicvolume) {
eLand id = getCurrentLandForMusic(); eLand id = getCurrentLandForMusic();
#ifdef EXTRA_MUSIC if(callhandlers(false, hooks_music, id)) return;
if(extra::changemusic(id)) return;
#endif
if(outoffocus) id = eLand(0); if(outoffocus) id = eLand(0);
if(musfname[id] == "LAST") id = cid; if(musfname[id] == "LAST") id = cid;
if(!loaded[id]) { if(!loaded[id]) {

View File

@ -925,6 +925,7 @@ namespace gamestack {
}; };
void restartGame(char switchWhat, bool push) { void restartGame(char switchWhat, bool push) {
popScreenAll();
DEBB(DF_INIT, (debugfile,"restartGame\n")); DEBB(DF_INIT, (debugfile,"restartGame\n"));
if(push) if(push)
@ -1061,41 +1062,6 @@ void restartGame(char switchWhat, bool push) {
resetmusic(); resetmusic();
} }
void clearGameMemory() {
#ifdef HASLINEVIEW
conformal::renderAutoband();
conformal::on = false;
#endif
DEBB(DF_INIT, (debugfile,"clearGameMemory\n"));
pathq.clear();
dcal.clear();
yendor::yii = NOYENDOR; yendor::yi.clear();
clearshadow();
offscreen.clear();
princess::clear();
buggycells.clear();
mirrors.clear();
clearing::bpdata.clear();
tortoise::emap.clear();
tortoise::babymap.clear();
seenSevenMines = false;
#ifdef HASLINEVIEW
conformal::killhistory.clear();
conformal::findhistory.clear();
conformal::movehistory.clear();
conformal::includeHistory = false;
#endif
recallCell = NULL;
prairie::lasttreasure = NULL;
prairie::enter = NULL;
prairie::tchoices.clear();
prairie::beaststogen.clear();
butterflies.clear();
#ifdef ROGUEVIZ
rogueviz::close();
#endif
}
static int orbid = 0; static int orbid = 0;
eItem nextOrb() { eItem nextOrb() {
@ -1222,7 +1188,6 @@ bool applyCheat(char u, cell *c = NULL) {
items[itOrbShield] += 1; items[itOrbShield] += 1;
cheater++; addMessage(XLAT("Orb power gained!")); cheater++; addMessage(XLAT("Orb power gained!"));
canmove = true; canmove = true;
if(cmode == emQuit) cmode = emNormal;
} }
return true; return true;
} }
@ -1235,12 +1200,12 @@ bool applyCheat(char u, cell *c = NULL) {
#ifndef NOEDIT #ifndef NOEDIT
if(u == 'A') { if(u == 'A') {
lastexplore = turncount; lastexplore = turncount;
cmode = emMapEditor; pushScreen(mapeditor::showMapEditor);
return true; return true;
} }
if(u == 'A'-64) { if(u == 'A'-64) {
mapeditor::drawcell = mouseover ? mouseover : cwt.c; mapeditor::drawcell = mouseover ? mouseover : cwt.c;
cmode = emDraw; pushScreen(mapeditor::showDrawEditor);
return true; return true;
} }
#endif #endif
@ -1366,7 +1331,7 @@ bool applyCheat(char u, cell *c = NULL) {
return true; return true;
} }
if(u == 'W'-64) { if(u == 'W'-64) {
cmode = emLinepattern; pushScreen(linepatterns::showMenu);
return true; return true;
} }
if(u == 'G'-64) { if(u == 'G'-64) {
@ -1423,3 +1388,19 @@ bool applyCheat(char u, cell *c = NULL) {
return false; return false;
} }
purehookset clearmemory;
void clearMemory() {
callhooks(clearmemory);
}
auto cgm = addHook(clearmemory, 40, [] () {
pathq.clear();
dcal.clear();
clearshadow();
seenSevenMines = false;
recallCell = NULL;
butterflies.clear();
buggycells.clear();
});

View File

@ -72,21 +72,20 @@ void presentation(presmode mode) {
} }
void slidehelp() { void slidehelp() {
if(texts && slides[currentslide].help[0]) { if(texts && slides[currentslide].help[0])
help = gotoHelp(
helptitle(XLAT(slides[currentslide].name), 0xFF8000) + help =
XLAT(slides[currentslide].help); helptitle(XLAT(slides[currentslide].name), 0xFF8000) +
if(cmode != emHelp) XLAT(slides[currentslide].help)
lastmode = cmode; );
cmode = emHelp;
}
} }
bool handleKeyTour(int sym, int uni) { bool handleKeyTour(int sym, int uni) {
bool normode = (cmode == emHelp || cmode == emNormal || cmode == emQuit); if(!tour::on) return false;
if(!normode) return false; if(!cmode2) return false;
int flags = slides[currentslide].flags; int flags = slides[currentslide].flags;
if((sym == SDLK_RETURN || sym == SDLK_KP_ENTER) && (cmode != emHelp || (flags & QUICKSKIP))) { if((sym == SDLK_RETURN || sym == SDLK_KP_ENTER) && (cmode2 != smHelp || (flags & QUICKSKIP))) {
if(cmode2) popScreen();
if(geometry || purehepta) { if(geometry || purehepta) {
restartGame(0, false); restartGame(0, false);
if(!(flags & QUICKGEO)) return true; if(!(flags & QUICKGEO)) return true;
@ -106,7 +105,7 @@ bool handleKeyTour(int sym, int uni) {
if(currentslide == 0) { slidehelp(); return true; } if(currentslide == 0) { slidehelp(); return true; }
presentation(pmStop); presentation(pmStop);
currentslide--; currentslide--;
if(cmode == emHelp) slidehelp(); if(cmode2 == smHelp) popScreen(), slidehelp();
presentation(pmStart); presentation(pmStart);
return true; return true;
} }
@ -164,7 +163,7 @@ bool handleKeyTour(int sym, int uni) {
return true; return true;
} }
if(sym == '4') { if(sym == '4') {
cmode = emNormal; popScreenAll();
if(items[itOrbTeleport]) goto give_aether; if(items[itOrbTeleport]) goto give_aether;
items[itOrbTeleport] = 1; items[itOrbTeleport] = 1;
checkmove(); checkmove();
@ -214,15 +213,15 @@ bool handleKeyTour(int sym, int uni) {
return true; return true;
} }
if(sym == '9') { if(sym == '9') {
cmode = emSlideshows; pushScreen(ss::showMenu);
return true; return true;
} }
return false; return false;
} }
void checkGoodLand(eLand l) { void checkGoodLand(eLand l) {
if(!showland(l) && texts) { if(!showland(l) && texts)
help = XLAT( gotoHelp(XLAT(
"This tutorial is different than most other game tutorials -- " "This tutorial is different than most other game tutorials -- "
"you are not forced to do anything, and you can go wherever you want.\n\n" "you are not forced to do anything, and you can go wherever you want.\n\n"
"However, %the1 is not what we are talking about now. " "However, %the1 is not what we are talking about now. "
@ -230,9 +229,7 @@ void checkGoodLand(eLand l) {
"get lost there.\n\n" "get lost there.\n\n"
"Remember that you can get to the next slide by pressing Enter.", "Remember that you can get to the next slide by pressing Enter.",
l l
); ));
cmode = emHelp;
}
} }
namespace ss { namespace ss {
@ -260,42 +257,38 @@ namespace ss {
if(size(slideshows) > 1) dialog::addItem(XLAT("change slideshow"), '1'); if(size(slideshows) > 1) dialog::addItem(XLAT("change slideshow"), '1');
dialog::addItem(XLAT("exit menu"), '0'); dialog::addItem(XLAT("exit menu"), '0');
dialog::display(); dialog::display();
} keyhandler = [] (int sym, int uni) {
if(uni >= 'a' && uni < 'a' + sssize) {
void handleKey(int sym, int uni) { if(geometry || purehepta) {
if(uni >= 'a' && uni < 'a' + sssize) { restartGame(0, false);
if(geometry || purehepta) { presentation(pmGeometryReset);
restartGame(0, false); }
presentation(pmGeometryReset); if(slides != wts) {
while(tour::on) restartGame('T', false);
slides = wts;
tour::start();
}
presentation(pmStop);
currentslide = uni - 'a';
popScreenAll();
presentation(pmStart);
slidehelp();
} }
if(slides != wts) { else if(uni == '1') {
while(tour::on) restartGame('T', false); list(wts);
slides = wts; for(int i=0; i<size(slideshows)-1; i++) if(slideshows[i] == wts) {
tour::start(); wts = slideshows[i+1]; return;
}
wts = slideshows[0];
} }
presentation(pmStop); else if(doexiton(sym, uni)) { wts = NULL; popScreen(); }
currentslide = uni - 'a'; };
cmode = emNormal;
presentation(pmStart);
slidehelp();
}
else if(uni == '1') {
list(wts);
for(int i=0; i<size(slideshows)-1; i++) if(slideshows[i] == wts) {
wts = slideshows[i+1]; return;
}
wts = slideshows[0];
}
else if(doexiton(sym, uni)) { wts = NULL; cmode = emNormal; }
} }
} }
void start() { void start() {
ss::list(default_slides); ss::list(default_slides);
#ifdef ROGUEVIZ
ss::list(rogueviz::rvtour::rvslides);
#endif
currentslide = 0; currentslide = 0;
vid.scale = 1; vid.scale = 1;
vid.alpha = 1; vid.alpha = 1;
@ -760,4 +753,8 @@ slide default_slides[] = {
slide *slides = default_slides; slide *slides = default_slides;
auto a1 = addHook(hooks_frame, 100, [] () { if(tour::on) tour::presentation(tour::pmFrame); });
auto a2 = addHook(hooks_handleKey, 100, handleKeyTour);
auto a3 = addHook(hooks_nextland, 100, [] (eLand l) { return tour::on ? getNext(l) : laNone; });
} }

View File

@ -23,6 +23,7 @@ typedef long double ld;
#define ASINH asinhl #define ASINH asinhl
#endif #endif
long double sqr(long double x) { return x*x; }
template<class T> int size(const T& x) {return int(x.size()); } template<class T> int size(const T& x) {return int(x.size()); }
string its(int i) { char buf[64]; sprintf(buf, "%d", i); return buf; } string its(int i) { char buf[64]; sprintf(buf, "%d", i); return buf; }
string fts(float x) { char buf[64]; sprintf(buf, "%4.2f", x); return buf; } string fts(float x) { char buf[64]; sprintf(buf, "%4.2f", x); return buf; }

View File

@ -412,6 +412,31 @@ namespace yendor {
}; };
vector<scoredata> scoreboard; vector<scoredata> scoreboard;
const char *chelp =
"There are many possible solutions to the Yendor Quest. In the Yendor "
"Challenge, you will try many of them!\n\n"
"Each challenge takes part in a specific land, and you have to use what "
"you have available.\n\n"
"You need to obtain an Orb of Yendor in the normal game to activate "
"this challenge, and (ever) collect 10 treasures in one or two lands "
"to activate a specific level.\n\n"
"After you complete each challenge, you can try it again, on a harder "
"difficulty level.\n\n"
"All the solutions showcased in the Yendor Challenge work in the normal "
"play too. However, passages to other lands, and (sometimes) some land features "
"are disabled in the Yendor "
"Challenge, so that you have to use the expected method. Also, "
"the generation rules are changed slightly for the Palace "
"and Minefield while you are looking for the Orb of Yendor, "
"to make the challenge more balanced "
"(but these changes are also active during the normal Yendor Quest).\n\n"
"You get 1000 points for each challenge won, and 1 extra point for "
"each extra difficulty level.";
void showMenu() { void showMenu() {
int s = vid.fsize; int s = vid.fsize;
vid.fsize = vid.fsize * 4/5; vid.fsize = vid.fsize * 4/5;
@ -475,55 +500,26 @@ namespace yendor {
yendor::uploadScore(); yendor::uploadScore();
vid.fsize = s; vid.fsize = s;
}
const char *chelp = keyhandler = [] (int sym, int uni) {
"There are many possible solutions to the Yendor Quest. In the Yendor " dialog::handleNavigation(sym, uni);
"Challenge, you will try many of them!\n\n" if(uni >= 'a' && uni < 'a'+YENDORLEVELS-1) {
"Each challenge takes part in a specific land, and you have to use what " challenge = uni-'a' + 1;
"you have available.\n\n" if(levelUnlocked(challenge) || autocheat) {
restartGame(yendor::on ? 0 : 'y');
"You need to obtain an Orb of Yendor in the normal game to activate " popScreen();
"this challenge, and (ever) collect 10 treasures in one or two lands " }
"to activate a specific level.\n\n" else
addMessage("Collect 10 treasures in various lands to unlock the challenges there");
"After you complete each challenge, you can try it again, on a harder "
"difficulty level.\n\n"
"All the solutions showcased in the Yendor Challenge work in the normal "
"play too. However, passages to other lands, and (sometimes) some land features "
"are disabled in the Yendor "
"Challenge, so that you have to use the expected method. Also, "
"the generation rules are changed slightly for the Palace "
"and Minefield while you are looking for the Orb of Yendor, "
"to make the challenge more balanced "
"(but these changes are also active during the normal Yendor Quest).\n\n"
"You get 1000 points for each challenge won, and 1 extra point for "
"each extra difficulty level.";
void handleKey(int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni >= 'a' && uni < 'a'+YENDORLEVELS-1) {
challenge = uni-'a' + 1;
if(levelUnlocked(challenge) || autocheat) {
restartGame(yendor::on ? 0 : 'y');
cmode = emNormal;
} }
else else if(uni == '0') {
addMessage("Collect 10 treasures in various lands to unlock the challenges there"); if(yendor::on) restartGame('y');
} popScreen();
else if(uni == '0') { }
if(yendor::on) restartGame('y'); else if(uni == '1') easy = !easy;
cmode = emNormal; else if(uni == '2' || sym == SDLK_F1) gotoHelp(chelp);
} else if(doexiton(sym, uni)) popScreen();
else if(uni == '1') easy = !easy; };
else if(uni == '2' || sym == SDLK_F1) {
lastmode = cmode;
cmode = emHelp;
help = chelp;
}
else if(doexiton(sym, uni)) cmode = emNormal;
} }
void collected(cell* c2) { void collected(cell* c2) {
@ -565,6 +561,10 @@ namespace yendor {
achievement_collection(itOrbYendor, pg, gold()); achievement_collection(itOrbYendor, pg, gold());
achievement_victory(false); achievement_victory(false);
} }
auto hooks = addHook(clearmemory, 0, [] () {
yendor::yii = NOYENDOR; yendor::yi.clear();
});
}; };
#define MAXTAC 20 #define MAXTAC 20
@ -738,23 +738,20 @@ namespace tactic {
} }
displayScore(scorehere, xr * 50); displayScore(scorehere, xr * 50);
} }
}
void handleKey(int sym, int uni) { keyhandler = [] (int sym, int uni) {
if(uni >= 1000 && uni < 1000 + LAND_TAC) { if(uni >= 1000 && uni < 1000 + LAND_TAC) {
firstland = euclidland = getLandById(uni - 1000); firstland = euclidland = getLandById(uni - 1000);
restartGame(tactic::on ? 0 : 't'); restartGame(tactic::on ? 0 : 't');
cmode = emNormal; popScreen();
} }
else if(uni == '0') { else if(uni == '0') {
cmode = emNormal; popScreen();
firstland = laIce; firstland = laIce;
if(tactic::on) restartGame('t'); if(tactic::on) restartGame('t');
} }
else if(sym == SDLK_F1) {
lastmode = cmode; else if(sym == SDLK_F1) gotoHelp(
cmode = emHelp;
help =
"In the pure tactics mode, you concentrate on a specific land. " "In the pure tactics mode, you concentrate on a specific land. "
"Your goal to obtain as high score as possible, without using " "Your goal to obtain as high score as possible, without using "
"features of the other lands. You can then compare your score " "features of the other lands. You can then compare your score "
@ -775,12 +772,13 @@ namespace tactic {
"The rate of treasure spawn is static in this mode. It is not " "The rate of treasure spawn is static in this mode. It is not "
"increased by killing monsters.\n\n" "increased by killing monsters.\n\n"
"Good luck, and have fun!"; "Good luck, and have fun!"
);
} else if(dialog::handlePageButtons(uni)) ;
else if(dialog::handlePageButtons(uni)) ; else if(doexiton(sym, uni)) popScreen();
else if(doexiton(sym, uni)) cmode = emNormal; };
} }
}; };
int modecodetable[42][6] = { int modecodetable[42][6] = {
@ -931,6 +929,7 @@ namespace peace {
int qty; int qty;
eLand getNext(eLand last) { eLand getNext(eLand last) {
if(!peace::on) return laNone;
if(isElemental(last) && hrand(100) < 90) if(isElemental(last) && hrand(100) < 90)
return laNone; return laNone;
else if(createOnSea(last)) else if(createOnSea(last))
@ -949,23 +948,6 @@ namespace peace {
return false; return false;
} }
void showMenu() {
dialog::init(XLAT(otherpuzzles ? "hyperbolic puzzles" : "memory game"), 0x40A040, 150, 100);
levellist = otherpuzzles ? explorelevels : simonlevels;
for(qty = 0; levellist[qty]; qty++)
dialog::addItem(XLAT1(linf[levellist[qty]].name), 'a'+qty);
dialog::addBreak(100);
dialog::addItem(XLAT(otherpuzzles ? "memory game" : "other hyperbolic puzzles"), '1');
dialog::addBoolItem(XLAT("display hints"), hint, '2');
dialog::addItem(XLAT("Help"), SDLK_F1);
dialog::addItem(XLAT("Return to the normal game"), '0');
dialog::display();
}
const char *chelp = NODESCYET; const char *chelp = NODESCYET;
namespace simon { namespace simon {
@ -1043,26 +1025,41 @@ namespace peace {
} }
} }
void handleKey(int sym, int uni) { void showMenu() {
dialog::handleNavigation(sym, uni); dialog::init(XLAT(otherpuzzles ? "hyperbolic puzzles" : "memory game"), 0x40A040, 150, 100);
if(uni == '1') otherpuzzles = !otherpuzzles; levellist = otherpuzzles ? explorelevels : simonlevels;
else if(uni >= 'a' && uni < 'a' + qty) {
whichland = levellist[uni - 'a']; for(qty = 0; levellist[qty]; qty++)
restartGame(peace::on ? 0 : 'P'); dialog::addItem(XLAT1(linf[levellist[qty]].name), 'a'+qty);
cmode = emNormal;
} dialog::addBreak(100);
else if(uni == '2') { hint = !hint; cmode = emNormal; } dialog::addItem(XLAT(otherpuzzles ? "memory game" : "other hyperbolic puzzles"), '1');
else if(uni == '0') { dialog::addBoolItem(XLAT("display hints"), hint, '2');
firstland = laIce; dialog::addItem(XLAT("Help"), SDLK_F1);
if(peace::on) restartGame('P'); dialog::addItem(XLAT("Return to the normal game"), '0');
cmode = emNormal;
} dialog::display();
else if(uni == 'h' || sym == SDLK_F1) {
lastmode = cmode; keyhandler = [] (int sym, int uni) {
cmode = emHelp; dialog::handleNavigation(sym, uni);
help = chelp;
} if(uni == '1') otherpuzzles = !otherpuzzles;
else if(doexiton(sym, uni)) cmode = emNormal; else if(uni >= 'a' && uni < 'a' + qty) {
whichland = levellist[uni - 'a'];
restartGame(peace::on ? 0 : 'P');
popScreen();
}
else if(uni == '2') { hint = !hint; popScreen(); }
else if(uni == '0') {
firstland = laIce;
if(peace::on) restartGame('P');
popScreen();
}
else if(uni == 'h' || sym == SDLK_F1) gotoHelp(chelp);
else if(doexiton(sym, uni)) popScreen();
};
} }
auto aNext = addHook(hooks_nextland, 100, getNext);
}; };