1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-11-23 21:07:17 +00:00

Merge branch 'master' into fix_hyperroid

This commit is contained in:
Zeno Rogue 2020-11-06 15:40:59 +01:00 committed by GitHub
commit 28383c94e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
79 changed files with 5882 additions and 3186 deletions

View File

@ -89,8 +89,6 @@ struct archimedean_tiling {
};
#endif
#if CAP_ARCM
#if HDR
static const int sfPH = 1;
static const int sfLINE = 2;
@ -99,6 +97,8 @@ static const int sfTHREE = 8;
static const int sfSEMILINE = 16;
#endif
#if CAP_ARCM
EX archimedean_tiling current;
EX archimedean_tiling fake_current;
@ -841,8 +841,8 @@ void connectHeptagons(heptspin hi, heptspin hs) {
/** T and X are supposed to be equal -- move T so that it is closer to X */
void fixup_matrix(transmatrix& T, const transmatrix& X, ld step) {
for(int i=0; i<MDIM; i++)
for(int j=0; j<MDIM; j++)
for(int i=0; i<MXDIM; i++)
for(int j=0; j<MXDIM; j++)
T[i][j] = (T[i][j] * (1-step) + X[i][j] * step);
/*
@ -1450,10 +1450,10 @@ EX int valence() {
return total / isize(current.faces);
}
#endif
EX map<gp::loc, cdata>& get_cdata() { return ((arcm::hrmap_archimedean*) (currentmap))->eucdata; }
#endif
EX }
}

View File

@ -136,8 +136,8 @@ EX transmatrix adjmatrix(int i) {
}
struct hrmap_asonov : hrmap {
unordered_map<coord, heptagon*> at;
unordered_map<heptagon*, coord> coords;
map<coord, heptagon*> at;
map<heptagon*, coord> coords;
heptagon *getOrigin() override { return get_at(coord(0,0,0)); }

View File

@ -372,7 +372,7 @@ EX bool attackJustStuns(cell *c2, flagtype f, eMonster attacker) {
return true;
else if((f & AF_SWORD) && c2->monst == moSkeleton)
return false;
else if(f & (AF_CRUSH | AF_MAGIC | AF_FALL | AF_EAT | AF_GUN))
else if(f & (AF_CRUSH | AF_MAGIC | AF_FALL | AF_EAT | AF_GUN | AF_PSI))
return false;
else
return isStunnable(c2->monst) && c2->hitpoints > 1;
@ -1183,19 +1183,19 @@ EX void killThePlayerAt(eMonster m, cell *c, flagtype flags) {
}
#if HDR
template<class T> void do_swords(cell *mf, cell *mt, eMonster who, const T& f) {
template<class T> void do_swords(movei mi, eMonster who, const T& f) {
for(int bb=0; bb<2; bb++) if(who == moPlayer && sword::orbcount(bb)) {
cell *sf = sword::pos(mf, sword::dir[multi::cpid], bb);
cell *st = sword::pos(mt, sword::shift(mf, mt, sword::dir[multi::cpid]), bb);
cell *sf = sword::pos(mi.s, sword::dir[multi::cpid], bb);
cell *st = sword::pos(mi.t, sword::shift(mi, sword::dir[multi::cpid]), bb);
f(st, bb);
if(sf != st && !isNeighbor(sf,st)) {
// also attack the in-transit cell
if(S3 == 3) {
forCellEx(sb, sf) if(isNeighbor(sb, st) && sb != mf && sb != mt) f(sb, bb);
forCellEx(sb, sf) if(isNeighbor(sb, st) && sb != mi.s && sb != mi.t) f(sb, bb);
}
else {
forCellEx(sb, mf) if(isNeighbor(sb, st) && sb != mt) f(sb, bb);
forCellEx(sb, mt) if(isNeighbor(sb, sf) && sb != mf) f(sb, bb);
forCellEx(sb, mi.s) if(isNeighbor(sb, st) && sb != mi.t) f(sb, bb);
forCellEx(sb, mi.t) if(isNeighbor(sb, sf) && sb != mi.s) f(sb, bb);
}
}
}
@ -1204,14 +1204,16 @@ template<class T> void do_swords(cell *mf, cell *mt, eMonster who, const T& f) {
int lastdouble = -3;
EX void stabbingAttack(cell *mf, cell *mt, eMonster who, int bonuskill IS(0)) {
EX void stabbingAttack(movei mi, eMonster who, int bonuskill IS(0)) {
int numsh = 0, numflail = 0, numlance = 0, numslash = 0, numbb[2];
numbb[0] = numbb[1] = 0;
int backdir = neighborId(mt, mf);
cell *mf = mi.s;
cell *mt = mi.t;
int backdir = mi.rev_dir();
do_swords(mf, mt, who, [&] (cell *c, int bb) { if(swordAttack(mt, who, c, bb)) numbb[bb]++, numslash++; });
do_swords(mi, who, [&] (cell *c, int bb) { if(swordAttack(mt, who, c, bb)) numbb[bb]++, numslash++; });
for(int bb=0; bb<2; bb++) achievement_count("SLASH", numbb[bb], 0);

View File

@ -22,7 +22,7 @@ struct display_data {
/** The view relative to the player character. */
shiftmatrix player_matrix;
/** On-screen coordinates for all the visible cells. */
unordered_map<cell*, shiftmatrix> cellmatrices, old_cellmatrices;
map<cell*, shiftmatrix> cellmatrices, old_cellmatrices;
/** Position of the current map view, relative to the screen (0 to 1). */
ld xmin, ymin, xmax, ymax;
/** Position of the current map view, in pixels. */
@ -50,7 +50,7 @@ struct display_data {
/** Which copy of the player cell? */
transmatrix which_copy;
/** On-screen coordinates for all the visible cells. */
unordered_map<cell*, vector<shiftmatrix>> all_drawn_copies;
map<cell*, vector<shiftmatrix>> all_drawn_copies;
};
#define View (::hr::current_display->view_matrix)
@ -79,7 +79,14 @@ int utfsize(char c) {
}
EX int get_sightrange() { return getDistLimit() + sightrange_bonus; }
EX int get_sightrange_ambush() { return max(get_sightrange(), ambush::distance); }
EX int get_sightrange_ambush() {
#if CAP_COMPLEX2
return max(get_sightrange(), ambush::distance);
#else
return get_sightrange();
#endif
}
bool display_data::in_anaglyph() { return vid.stereo_mode == sAnaglyph; }
bool display_data::stereo_active() { return vid.stereo_mode != sOFF; }
@ -603,7 +610,9 @@ EX void resetGL() {
matched_programs.clear();
glhr::current_glprogram = nullptr;
ray::reset_raycaster();
#if CAP_RUG
if(rug::glbuf) rug::close_glbuf();
#endif
}
#endif
@ -1142,8 +1151,7 @@ EX void initgraph() {
}
#if ISWEB
vid.xscr = vid.xres = 1280;
vid.yscr = vid.yres = 900;
get_canvas_size();
#else
const SDL_VideoInfo *inf = SDL_GetVideoInfo();
vid.xscr = vid.xres = inf->current_w;
@ -1161,7 +1169,9 @@ EX void initgraph() {
#if CAP_CONFIG
loadConfig();
#endif
#if CAP_ARCM
arcm::current.parse();
#endif
if(hybri) geometry = hybrid::underlying;
#if CAP_COMMANDLINE

View File

@ -21,6 +21,7 @@ EX int newRoundTableRadius() {
return 28 + 2 * items[itHolyGrail];
}
#if CAP_COMPLEX2
EX int getAnthraxData(cell *c, bool b) {
int d = celldistAlt(c);
int rad = 28 + 3 * camelot::anthraxBonus;
@ -36,10 +37,13 @@ EX int getAnthraxData(cell *c, bool b) {
if(b) return rad;
return d;
}
#endif
EX int roundTableRadius(cell *c) {
if(eubinary) return 28;
#if CAP_COMPLEX2
if(tactic::on) return getAnthraxData(c, true);
#endif
if(!c->master->alt) return 28;
return c->master->alt->alt->emeraldval & GRAIL_RADIUS_MASK;
}
@ -55,7 +59,9 @@ EX int celldistAltRelative(cell *c) {
if(sphere || quotient) {
return celldist(c) - 3;
}
#if CAP_COMPLEX2
if(tactic::on) return getAnthraxData(c, false);
#endif
return celldistAlt(c) - roundTableRadius(c);
}
@ -1805,12 +1811,14 @@ EX void moreBigStuff(cell *c) {
if(c->master->emeraldval % 2)
c->wall = waColumn;
}
#if CAP_BT
else if(geometry == gHoroTris || geometry == gHoroRec) {
if(c->c.spin(bt::updir()) != 0) c->wall = waColumn;
}
else if(geometry == gKiteDart3) {
if(kite::getshape(c->master) == kite::pKite) c->wall = waColumn;
}
#endif
else if(in_s2xe()) {
auto d = hybrid::get_where(c);
if(!PIU(pseudohept(d.first))) c->wall = waColumn;

View File

@ -19,6 +19,10 @@ EX namespace bt {
#endif
}
#if !CAP_BT
EX int updir() { return 0; }
#endif
#if CAP_BT
#if HDR
enum bindir {
@ -1031,7 +1035,6 @@ EX int celldistance3(heptagon *c1, heptagon *c2) {
}
EX int celldistance3(cell *c1, cell *c2) { return celldistance3(c1->master, c2->master); }
#endif
EX hyperpoint get_horopoint(ld y, ld x) {
return xpush(-y) * bt::parabolic(x) * C0;
@ -1091,6 +1094,7 @@ EX hyperpoint get_corner_horo_coordinates(cell *c, int i) {
auto hooksw = addHook(hooks_swapdim, 100, [] {
if(bt::in()) build_tmatrix();
});
#endif
}

View File

@ -179,8 +179,11 @@ EX heptagon* hyperbolic_origin() {
h.cdata = NULL;
h.alt = NULL;
h.distance = 0;
#if CAP_IRR
if(IRREGULAR) irr::link_start(origin);
else h.c7 = newCell(odegree, origin);
else
#endif
h.c7 = newCell(odegree, origin);
return origin;
}
@ -292,10 +295,10 @@ EX void initcells() {
hrmap* res = callhandlers((hrmap*)nullptr, hooks_newmap);
if(res) currentmap = res;
else if(INVERSE) currentmap = gp::new_inverse();
else if(fake::in()) currentmap = fake::new_map();
else if(asonov::in()) currentmap = asonov::new_map();
else if(nonisotropic || hybri) currentmap = nisot::new_map();
else if(INVERSE) currentmap = gp::new_inverse();
else if(fake::in()) currentmap = fake::new_map();
#if CAP_CRYSTAL
else if(cryst) currentmap = crystal::new_map();
#endif
@ -405,7 +408,7 @@ EX void clearfrom(heptagon *at) {
}
int edges = at->degree();
if(bt::in() && WDIM == 2) edges = at->c7->type;
for(int i=0; i<edges; i++) if(at->move(i)) {
for(int i=0; i<edges; i++) if(at->move(i) && at->move(i) != at) {
if(at->move(i)->alt != &deletion_marker)
q.push(at->move(i));
unlink_cdata(at->move(i));
@ -554,7 +557,9 @@ EX int celldistAlt(cell *c) {
/** direction upwards in the tree */
EX int updir(heptagon *h) {
#if CAP_BT
if(bt::in()) return bt::updir();
#endif
#if MAXMDIM >= 4
if(WDIM == 3 && reg3::in_rule()) {
for(int i=0; i<S7; i++) if(h->move(i) && h->move(i)->distance < h->distance)
@ -815,10 +820,12 @@ cdata *getHeptagonCdata(heptagon *h) {
if(sphere || quotient) h = currentmap->gamestart()->master;
bool starting = h->s == hsOrigin;
#if CAP_BT
if(bt::in()) {
if(bt::mapside(h) == 0) starting = true;
for(int i=0; i<h->type; i++) if(bt::mapside(h->cmove(i)) == 0) starting = true;
}
#endif
if(starting) {
h->cdata = new cdata(orig_cdata);
@ -866,7 +873,12 @@ cdata *getHeptagonCdata(heptagon *h) {
cdata *getEuclidCdata(gp::loc h) {
int x = h.first, y = h.second;
#if CAP_ARCM
auto& data = arcm::in() ? arcm::get_cdata() : euc::get_cdata();
#else
auto& data = euc::get_cdata();
#endif
// hrmap_euclidean* euc = dynamic_cast<hrmap_euclidean*> (currentmap);
if(data.count(h)) return &(data[h]);
@ -918,6 +930,7 @@ int ld_to_int(ld x) {
return int(x + 1000000.5) - 1000000;
}
#if CAP_ARCM
EX gp::loc pseudocoords(cell *c) {
transmatrix T = arcm::archimedean_gmatrix[c->master].second;
return {ld_to_int(T[0][LDIM]), ld_to_int((spin(60*degree) * T)[0][LDIM])};
@ -935,19 +948,22 @@ EX cdata *arcmCdata(cell *c) {
dynamicval<hrmap*> cm(currentmap, arcm::current_altmap);
return getHeptagonCdata(h2);
}
#endif
EX int getCdata(cell *c, int j) {
if(fake::in()) return FPIU(getCdata(c, j));
if(INVERSE) {
if(hybri) { c = hybrid::get_where(c).first; return PIU(getBits(c)); }
else if(INVERSE) {
cell *c1 = gp::get_mapped(c);
return UIU(getCdata(c1, j));
}
if(hybri) { c = hybrid::get_where(c).first; return PIU(getBits(c)); }
else if(euc::in()) return getEuclidCdata(euc2_coordinates(c))->val[j];
#if CAP_ARCM
else if(arcm::in() && euclid)
return getEuclidCdata(pseudocoords(c))->val[j];
else if(arcm::in() && hyperbolic)
return arcmCdata(c)->val[j]*3;
#endif
else if(!geometry_supports_cdata()) return 0;
else if(ctof(c)) return getHeptagonCdata(c->master)->val[j]*3;
else {
@ -961,16 +977,18 @@ EX int getCdata(cell *c, int j) {
EX int getBits(cell *c) {
if(fake::in()) return FPIU(getBits(c));
if(INVERSE) {
if(hybri) { c = hybrid::get_where(c).first; return PIU(getBits(c)); }
else if(INVERSE) {
cell *c1 = gp::get_mapped(c);
return UIU(getBits(c1));
}
if(hybri) { c = hybrid::get_where(c).first; return PIU(getBits(c)); }
else if(euc::in()) return getEuclidCdata(euc2_coordinates(c))->bits;
#if CAP_ARCM
else if(arcm::in() && euclid)
return getEuclidCdata(pseudocoords(c))->bits;
else if(arcm::in() && (hyperbolic || sl2))
return arcmCdata(c)->bits;
#endif
else if(!geometry_supports_cdata()) return 0;
else if(c == c->master->c7) return getHeptagonCdata(c->master)->bits;
else {
@ -1108,7 +1126,7 @@ EX int celldistance(cell *c1, cell *c2) {
if(hybri) return hybrid::celldistance(c1, c2);
#if CAP_FIELD
if(geometry == gFieldQuotient) {
if(geometry == gFieldQuotient && (PURE || BITRUNCATED)) {
int d = fieldpattern::field_celldistance(c1, c2);
if(d != DISTANCE_UNKNOWN) return d;
}

View File

@ -534,11 +534,13 @@ void celldrawer::setcolors() {
break;
case waMineUnknown: case waMineMine:
#if CAP_COMPLEX2
if(mine::marked_safe(c))
fcol = wcol = gradient(wcol, 0x40FF40, 0, 0.2, 1);
else if(mine::marked_mine(c))
fcol = wcol = gradient(wcol, 0xFF4040, -1, sintick(100), 1);
// fallthrough
#endif
case waMineOpen:
if(wmblack || wmascii) {
@ -1352,7 +1354,9 @@ void celldrawer::draw_features() {
}
case waTerraWarrior:
#if CAP_COMPLEX2
drawTerraWarrior(V, terracotta::randterra ? (c->landparam & 7) : (5 - (c->landparam & 7)), 7, 0);
#endif
break;
case waBoat: case waStrandedBoat:

View File

@ -4139,3 +4139,33 @@ graphics/UI bugfixes:
- fixed the issues with Orb of Yendor
- fixed a bug in S2xE, and choosing cells in sphere
- fixed Friendly Ghosts
2020-11-02 00:08 Update 11.4:
* Panini perspective projection (allows wider vision -- configurable in 3d config -> FOV)
* Changed the default texture_step to 4
* An option to hide the flat projection in hyperboloid
* improved plain floor shapes in arbitrary tessellations
* fixed the Asonov cat geometry
* Five new projections: Poor Man (hyperbolic only), Panini, retroazimuthal: Craig, Hammer, Littrow (retro-Hammer buggy on sphere)
* Various fixes related to product spaces, especially product+inverse
* Fixed the navigation keys in dialogs
* fixed duals for Euclidean tessellations
Also, improvements in the Web version -- Backspace not Esc to exit dialogs, auto-resizing, raytracer is available.
Not yet in HyperRogue online, but see: https://zenorogue.itch.io/bringris
2020-11-05 18:53 Update 11.4a:
- fixed the aura when camera angle changed
- fixed selecting large regions for copying with mouse
- messages at 30 treasures etc. should no longer appear in PTM
- fixed a bug which allowed changing the generation/game range outside of cheat mode
- fixed drawing of creatures in kill list (visible e.g. for dogs)
- the RGB/RGBA hex value color is now shown with leading 0s
- Orb of the Mind now actually kills reptiles
- Orb of Chaos is now forbidden in the Princess Quest
- highlight mode setting should be saved now
- improved the safe move checking (fixes some minor bugs especially in multiplayer)
- allies use the new safe move checking now (they now know whether attacking hedgehog warriors/pikemen is safe for them and do it if yes)
- golems and bomberbirds now pathfind correctly

View File

@ -39,28 +39,47 @@ EX bool hasSafeOrb(cell *c) {
}
#if HDR
struct stalemate1 {
eMonster who;
cell *moveto;
cell *pushto;
cell *comefrom;
struct player_move_info {
movei mi;
cell *swordlast[2], *swordtransit[2], *swordnext[2];
stalemate1(eMonster w, cell *mt, cell *pt, cell *cf) : who(w), moveto(mt), pushto(pt), comefrom(cf) {}
player_move_info(movei mi);
};
#endif
EX vector<player_move_info> pmi;
EX vector<cell*> pushes;
player_move_info::player_move_info(movei _mi) : mi(_mi) {
for(int b=0; b<2; b++) swordlast[b] = sword::pos(multi::cpid, b);
dynamicval<sword::sworddir> x7(sword::dir[multi::cpid], sword::shift(mi, sword::dir[multi::cpid]));
for(int b=0; b<2; b++) {
swordnext[b] = sword::pos(multi::cpid, b);
swordtransit[b] = NULL;
if(swordnext[b] && swordnext[b] != swordlast[b] && !isNeighbor(swordlast[b], swordnext[b])) {
forCellEx(c2, swordnext[b])
if(c2 != mi.t && c2 != mi.s && isNeighbor(c2, S3==3 ? swordlast[b] : mi.t))
swordtransit[b] = c2;
if(S3 == 4)
forCellEx(c2, mi.s)
if(c2 != mi.s && isNeighbor(c2, swordlast[b]))
swordtransit[b] = c2;
}
}
}
EX bool krakensafe(cell *c) {
return items[itOrbFish] || items[itOrbAether] ||
(c->item == itOrbFish && c->wall == waBoat) ||
(c->item == itOrbAether && c->wall == waBoat);
}
EX bool monstersnear(stalemate1& sm) {
EX bool monstersnear(cell *c, eMonster who) {
cell *c = sm.moveto;
bool eaten = false;
if(hardcore && sm.who == moPlayer) return false;
if(hardcore && who == moPlayer) return false;
int res = 0;
bool fast = false;
@ -76,9 +95,9 @@ EX bool monstersnear(stalemate1& sm) {
who_kills_me = moCrusher; res++;
}
if(sm.who == moPlayer || items[itOrbEmpathy]) {
if(who == moPlayer || items[itOrbEmpathy]) {
fast = (items[itOrbSpeed] && (items[itOrbSpeed] & 1));
if(sm.who == moPlayer && sm.moveto->item == itOrbSpeed && !items[itOrbSpeed]) fast = true;
if(who == moPlayer && c->item == itOrbSpeed && !items[itOrbSpeed]) fast = true;
}
if(havewhat&HF_OUTLAW) {
@ -103,7 +122,7 @@ EX bool monstersnear(stalemate1& sm) {
if(!logical_adjacent(c3, c3->monst, c2) || !logical_adjacent(c2, c3->monst, c) || (c3->monst == moWitchSpeed && c2->land != laPower))
continue;
if(elec::affected(c3)) continue;
if(c3->stuntime > (sm.who == moPlayer ? 0 : 1)) continue;
if(c3->stuntime > (who == moPlayer ? 0 : 1)) continue;
// speedwitches can only attack not-fastened monsters,
// others can only attack if the move is not fastened
if(c3->monst == moWitchSpeed && items[itOrbSpeed]) continue;
@ -120,19 +139,19 @@ EX bool monstersnear(stalemate1& sm) {
// consider normal monsters
if(c2 &&
isArmedEnemy(c2, sm.who) &&
(c2->monst != moLancer || isUnarmed(sm.who) || !logical_adjacent(c, sm.who, c2))) {
isArmedEnemy(c2, who) &&
(c2->monst != moLancer || isUnarmed(who) || !logical_adjacent(c, who, c2))) {
eMonster m = c2->monst;
if(elec::affected(c2)) continue;
if(fast && c2->monst != moWitchSpeed) continue;
// Krakens just destroy boats
if(c2->monst == moKrakenT && onboat(sm)) {
if(c2->monst == moKrakenT && c->wall == waBoat) {
if(krakensafe(c)) continue;
else if(warningprotection(XLAT("This move appears dangerous -- are you sure?")) && res == 0) m = moWarning;
else continue;
}
// they cannot attack through vines
if(!canAttack(c2, c2->monst, c, sm.who, AF_NEXTTURN)) continue;
if(!canAttack(c2, c2->monst, c, who, AF_NEXTTURN)) continue;
if(c2->monst == moWorm || c2->monst == moTentacle || c2->monst == moHexSnake) {
if(passable_for(c2->monst, c, c2, 0))
eaten = true;
@ -142,18 +161,16 @@ EX bool monstersnear(stalemate1& sm) {
}
}
if(sm.who == moPlayer && res && (markOrb2(itOrbShield) || markOrb2(itOrbShell)) && !eaten)
if(who == moPlayer && res && (markOrb2(itOrbShield) || markOrb2(itOrbShell)) && !eaten)
res = 0;
if(sm.who == moPlayer && res && markOrb2(itOrbDomination) && c->monst)
if(who == moPlayer && res && markOrb2(itOrbDomination) && c->monst)
res = 0;
return !!res;
}
EX bool monstersnear2();
EX bool monstersnear2() {
EX bool monstersnear_aux() {
changes.value_set(passive_switch, (gold() & 1) ? moSwitch1 : moSwitch2);
multi::cpid++;
bool b = false;
@ -162,22 +179,33 @@ EX bool monstersnear2() {
if(multi::cpid == multi::players || multi::players == 1 || multi::checkonly) {
if(shmup::delayed_safety) return false;
dynamicval<eMonster> sw(passive_switch, passive_switch);
for(int i=0; i<isize(stalemate::moves); i++)
for(int j=0; j<isize(stalemate::moves); j++) if(i != j) {
if(swordConflict(stalemate::moves[i], stalemate::moves[j])) {
for(int i=0; i<isize(pmi); i++)
for(int j=0; j<isize(pmi); j++) if(i != j) {
if(swordConflict(pmi[i], pmi[j])) {
b = true;
who_kills_me = moEnergySword;
}
if(multi::player[i].at == multi::player[j].at)
if(pmi[i].mi.t == pmi[j].mi.t)
{ b = true; who_kills_me = moFireball; }
if(celldistance(multi::player[i].at, multi::player[j].at) > 8)
if(celldistance(pmi[i].mi.t, pmi[j].mi.t) > 8)
{ b = true; who_kills_me = moAirball; }
}
for(int i=0; !b && i<isize(stalemate::moves); i++)
b = monstersnear(stalemate::moves[i]);
for(auto& pushto: pushes)
for(auto& mi: pmi)
if(pushto == mi.mi.t) {
b = true; who_kills_me = moTongue;
}
for(int i=0; i<isize(pushes); i++)
for(int j=0; j<i; j++)
if(pushes[i] == pushes[j]) {
b = true; who_kills_me = moCrushball;
}
for(int i=0; !b && i<isize(pmi); i++)
b = monstersnear(pmi[i].mi.t, moPlayer);
}
else b = !multimove();
multi::cpid--;
@ -185,94 +213,17 @@ EX bool monstersnear2() {
return b;
}
EX bool monstersnear(cell *c, eMonster who, cell *pushto, cell *comefrom) {
if(peace::on) return 0; // you are safe
stalemate1 sm(who, c, pushto, comefrom);
if(who == moPlayer) for(int b=0; b<2; b++) sm.swordlast[b] = sword::pos(multi::cpid, b);
cell *none = NULL;
cell **wcw = &cwt.at;
if(who != moPlayer) wcw = &none;
else if(multi::players > 1) wcw = &multi::player[multi::cpid].at;
dynamicval<cell*> x5(*wcw, c);
dynamicval<bool> x6(stalemate::nextturn, true);
dynamicval<sword::sworddir> x7(sword::dir[multi::cpid],
who == moPlayer ? sword::shift(comefrom, c, sword::dir[multi::cpid]) :
sword::dir[multi::cpid]);
for(int b=0; b<2; b++) {
if(who == moPlayer) {
sm.swordnext[b] = sword::pos(multi::cpid, b);
sm.swordtransit[b] = NULL;
if(sm.swordnext[b] && sm.swordnext[b] != sm.swordlast[b] && !isNeighbor(sm.swordlast[b], sm.swordnext[b])) {
forCellEx(c2, sm.swordnext[b])
if(c2 != c && c2 != comefrom && isNeighbor(c2, S3==3 ? sm.swordlast[b] : *wcw))
sm.swordtransit[b] = c2;
if(S3 == 4)
forCellEx(c2, c)
if(c2 != comefrom && isNeighbor(c2, sm.swordlast[b]))
sm.swordtransit[b] = c2;
}
}
else {
sm.swordnext[b] = sm.swordtransit[b] = NULL;
}
}
stalemate::moves.push_back(sm);
// dynamicval<eMonster> x7(stalemate::who, who);
bool b;
if(who == moPlayer && c->wall == waBigStatue) {
eWall w = comefrom->wall;
c->wall = waNone;
if(doesnotFall(comefrom)) comefrom->wall = waBigStatue;
b = monstersnear2();
comefrom->wall = w;
c->wall = waBigStatue;
}
else if(who == moPlayer && isPushable(c->wall)) {
eWall w = c->wall;
c->wall = waNone;
b = monstersnear2();
c->wall = w;
}
else {
b = monstersnear2();
}
stalemate::moves.pop_back();
/** like monstersnear but add the potential moves of other players into account */
EX bool monstersnear_add_pmi(player_move_info pmi0) {
pmi.push_back(pmi0);
bool b = monstersnear_aux();
pmi.pop_back();
return b;
}
EX namespace stalemate {
EX vector<stalemate1> moves;
EX bool nextturn;
EX bool isMoveto(cell *c) {
for(int i=0; i<isize(moves); i++) if(moves[i].moveto == c) return true;
return false;
}
EX bool isPushto(cell *c) {
for(int i=0; i<isize(moves); i++) if(moves[i].pushto == c) return true;
return false;
}
EX }
EX bool onboat(stalemate1& sm) {
cell *c = sm.moveto;
cell *cf = sm.comefrom;
return (c->wall == waBoat) || (cf->wall == waBoat && c->wall == waSea);
}
EX bool multimove() {
if(multi::cpid == 0) lastkills = tkills();
if(!multi::playerActive(multi::cpid)) return !monstersnear2();
if(!multi::playerActive(multi::cpid)) return !monstersnear_aux();
cellwalker bcwt = cwt;
cwt = multi::player[multi::cpid];
bool b = movepcto(multi::whereto[multi::cpid]);
@ -293,11 +244,11 @@ EX namespace multi {
EX bool aftermove;
EX }
EX bool swordConflict(const stalemate1& sm1, const stalemate1& sm2) {
EX bool swordConflict(const player_move_info& sm1, const player_move_info& sm2) {
if(items[itOrbSword] || items[itOrbSword2])
for(int b=0; b<2; b++)
if(sm1.comefrom == sm2.swordlast[b] || sm1.comefrom == sm2.swordtransit[b] || sm1.comefrom == sm2.swordnext[b])
if(sm1.moveto == sm2.swordlast[b] || sm1.moveto == sm2.swordtransit[b] || sm1.moveto == sm2.swordnext[b])
if(sm1.mi.s == sm2.swordlast[b] || sm1.mi.s == sm2.swordtransit[b] || sm1.mi.s == sm2.swordnext[b])
if(sm1.mi.t == sm2.swordlast[b] || sm1.mi.t == sm2.swordtransit[b] || sm1.mi.t == sm2.swordnext[b])
return true;
return false;
}

View File

@ -980,6 +980,8 @@ enum eModel : int {
// 32..38
mdWerner, mdAitoff, mdHammer, mdLoximuthal, mdMiller, mdGallStereographic, mdWinkelTripel,
// 39..
mdPoorMan, mdPanini, mdRetroCraig, mdRetroLittrow, mdRetroHammer,
// 44..
mdGUARD, mdPixel, mdHyperboloidFlat, mdPolynomial, mdManual
};
#endif
@ -1031,6 +1033,11 @@ EX vector<modelinfo> mdinf = {
{X3("Miller projection"), mf::euc_boring | mf::band, DEFAULTS}, // scale latitude 4/5 -> Mercator -> 5/4
{X3("Gall stereographic"), mf::euc_boring | mf::band, DEFAULTS}, // like central cylindrical but stereographic
{X3("Winkel tripel"), mf::euc_boring | mf::broken, DEFAULTS}, // mean of equirec and Aitoff
{X3("Poor man's square"), mf::euc_boring, DEFAULTS}, //
{X3("Panini projection"), mf::euc_boring, DEFAULTS}, //
{X3("Craig retroazimuthal"), mf::euc_boring | mf::broken, DEFAULTS}, // retroazimuthal cylindrical
{X3("Littrow retroazimuthal"), mf::euc_boring | mf::broken, DEFAULTS}, // retroazimuthal conformal
{X3("Hammer retroazimuthal"), mf::euc_boring, DEFAULTS}, // retroazimuthal equidistant
{X3("guard"), 0, DEFAULTS},
{X3("polynomial"), mf::conformal, DEFAULTS},
};

View File

@ -2800,12 +2800,18 @@ EX namespace sword {
EX void determine_sword_angles() {
sword_angles = 2;
if(SWORDDIM == 3) sword_angles = 1;
#if CAP_IRR
else if(IRREGULAR) sword_angles = 840;
#endif
#if CAP_BT
else if(bt::in()) sword_angles = 42;
#endif
#if CAP_ARCM
else if(arcm::in()) {
if(!PURE) possible_divisor((BITRUNCATED ? 2 : 1) * isize(arcm::current.faces));
if(!DUAL) for(int f: arcm::current.faces) possible_divisor(f);
}
#endif
else {
possible_divisor(S7);
if(BITRUNCATED) possible_divisor(S3);
@ -2861,10 +2867,13 @@ EX namespace sword {
}
// from c1 to c2
EX sworddir shift(cell *c1, cell *c2, sworddir d) {
if(!c1 || !c2) return d;
int s1 = neighborId(c1, c2);
int s2 = neighborId(c2, c1);
EX sworddir shift(movei mi, sworddir d) {
cell *c1 = mi.s;
cell *c2 = mi.t;
if(!mi.proper()) return d;
int s1 = mi.d;
int s2 = mi.rev_dir();
neighborId(c2, c1);
if(s1 < 0 || s2 < 0) return d;
if(SWORDDIM == 2) {
int sub = (hybri) ? 2 : 0;
@ -3347,7 +3356,7 @@ EX namespace ca {
EX eWall wlive = waFloorA;
EX unordered_set<cell*> changed;
EX set<cell*> changed;
EX void list_adj(cell *c) {
changed.insert(c);

View File

@ -22,6 +22,8 @@ struct supersaver {
virtual bool dosave() = 0;
virtual void reset() = 0;
virtual ~supersaver() {};
virtual bool affects(void* v) { return false; }
virtual void set_default() = 0;
};
typedef vector<shared_ptr<supersaver>> saverlist;
@ -36,6 +38,8 @@ template<class T> struct dsaver : supersaver {
bool dosave() { return val != dft; }
void reset() { val = dft; }
dsaver(T& val) : val(val) { }
bool affects(void* v) { return v == &val; }
void set_default() { dft = val; }
};
template<class T> struct saver : dsaver<T> {};
@ -51,6 +55,18 @@ template<class T> void addsaver(T& i, string name) {
addsaver(i, name, i);
}
template<class T> void removesaver(T& val) {
for(int i=0; i<isize(savers); i++)
if(savers[i]->affects(&val))
savers.erase(savers.begin() + i);
}
template<class T> void set_saver_default(T& val) {
for(auto sav: savers)
if(sav->affects(&val))
sav->set_default();
}
template<class T> struct saverenum : supersaver {
T& val;
T dft;
@ -59,6 +75,8 @@ template<class T> struct saverenum : supersaver {
saverenum<T>(T& v) : val(v) { }
string save() { return its(int(val)); }
void load(const string& s) { val = (T) atoi(s.c_str()); }
virtual bool affects(void* v) { return v == &val; }
virtual void set_default() { dft = val; }
};
template<class T, class U> void addsaverenum(T& i, U name, T dft) {
@ -344,6 +362,7 @@ EX void initConfig() {
addsaver(pconf.ballproj, "ballproj", 1);
addsaver(vid.monmode, "monster display mode", DEFAULT_MONMODE);
addsaver(vid.wallmode, "wall display mode", DEFAULT_WALLMODE);
addsaver(vid.highlightmode, "highlightmode");
addsaver(vid.depth, "3D depth", 1);
addsaver(vid.camera, "3D camera level", 1);
@ -473,7 +492,9 @@ EX void initConfig() {
addsaver(shmup::on, "mode-shmup", false);
addsaver(hardcore, "mode-hardcore", false);
addsaver(chaosmode, "mode-chaos");
#if CAP_INV
addsaver(inv::on, "mode-Orb Strategy");
#endif
addsaverenum(variation, "mode-variation", eVariation::bitruncated);
addsaver(peace::on, "mode-peace");
addsaver(peace::otherpuzzles, "mode-peace-submode");
@ -495,6 +516,7 @@ EX void initConfig() {
addsaver(vid.binary_width, "binary-tiling-width", 1);
addsaver(pconf.collignon_parameter, "collignon-parameter", 1);
addsaver(pconf.collignon_reflected, "collignon-reflect", false);
addsaver(pconf.show_hyperboloid_flat, "hyperboloid-flat", true);
addsaver(pconf.aitoff_parameter, "aitoff-parameter");
addsaver(pconf.miller_parameter, "miller-parameter");
@ -577,7 +599,9 @@ EX void initConfig() {
addsaverenum(pconf.basic_model, "basic model");
addsaver(pconf.use_atan, "use_atan");
#if CAP_ARCM
addsaver(arcm::current.symbol, "arcm-symbol", "4^5");
#endif
addsaverenum(hybrid::underlying, "product-underlying");
for(int i=0; i<isize(ginf); i++) {
@ -637,7 +661,7 @@ EX void initConfig() {
addsaver(vid.sloppy_3d, "sloppy3d", true);
addsaver(vid.texture_step, "wall-quality", 1);
addsaver(vid.texture_step, "wall-quality", 4);
addsaver(smooth_scrolling, "smooth-scrolling", false);
addsaver(mouseaim_sensitivity, "mouseaim_sensitivity", 0.01);
@ -690,6 +714,8 @@ EX void initConfig() {
addsaver(camera_speed, "camera-speed", 1);
addsaver(camera_rot_speed, "camera-rot-speed", 1);
addsaver(panini_alpha, "panini_alpha", 0);
callhooks(hooks_configfile);
#if CAP_CONFIG
@ -1061,6 +1087,43 @@ EX void menuitem_sightrange(char c IS('c')) {
dialog::add_action(edit_sightrange);
}
EX void menuitem_sfx_volume() {
dialog::addSelItem(XLAT("sound effects volume"), its(effvolume), 'e');
dialog::add_action([] {
dialog::editNumber(effvolume, 0, 128, 10, 60, XLAT("sound effects volume"), "");
dialog::numberdark = dialog::DONT_SHOW;
dialog::reaction = [] () {
#if ISANDROID
settingsChanged = true;
#endif
};
dialog::bound_low(0);
dialog::bound_up(MIX_MAX_VOLUME);
});
}
EX void menuitem_music_volume() {
if (!audio) return;
dialog::addSelItem(XLAT("background music volume"), its(musicvolume), 'b');
dialog::add_action([] {
dialog::editNumber(musicvolume, 0, 128, 10, 60, XLAT("background music volume"), "");
dialog::numberdark = dialog::DONT_SHOW;
dialog::reaction = [] () {
#if CAP_SDLAUDIO
Mix_VolumeMusic(musicvolume);
#endif
#if ISANDROID
settingsChanged = true;
#endif
};
dialog::bound_low(0);
dialog::bound_up(MIX_MAX_VOLUME);
dialog::extra_options = [] {
dialog::addBoolItem_action(XLAT("play music when out of focus"), music_out_of_focus, 'A');
};
});
}
EX void showSpecialEffects() {
cmode = vid.xres > vid.yres * 1.4 ? sm::SIDE : sm::MAYDARK;
gamescreen(0);
@ -1312,37 +1375,8 @@ EX void configureOther() {
// dialog::addBoolItem_action(XLAT("forget faraway cells"), memory_saving_mode, 'y');
#if CAP_AUDIO
dialog::addSelItem(XLAT("background music volume"), its(musicvolume), 'b');
dialog::add_action([] {
dialog::editNumber(musicvolume, 0, 128, 10, 60, XLAT("background music volume"), "");
dialog::reaction = [] () {
#if CAP_SDLAUDIO
Mix_VolumeMusic(musicvolume);
#endif
#if ISANDROID
settingsChanged = true;
#endif
};
dialog::bound_low(0);
dialog::bound_up(MIX_MAX_VOLUME);
dialog::extra_options = [] {
#if CAP_SDLAUDIO
dialog::addBoolItem_action(XLAT("play music when out of focus"), music_out_of_focus, 'A');
#endif
};
});
dialog::addSelItem(XLAT("sound effects volume"), its(effvolume), 'e');
dialog::add_action([] {
dialog::editNumber(effvolume, 0, 128, 10, 60, XLAT("sound effects volume"), "");
dialog::reaction = [] () {
#if ISANDROID
settingsChanged = true;
#endif
};
dialog::bound_low(0);
dialog::bound_up(MIX_MAX_VOLUME);
});
menuitem_music_volume();
menuitem_sfx_volume();
#endif
menuitem_sightrange('r');
@ -1530,17 +1564,49 @@ EX void explain_detail() {
));
}
EX void add_edit_fov(char key IS('f')) {
dialog::addSelItem(XLAT("field of view"), fts(vid.fov) + "°", key);
dialog::add_action([] {
dialog::editNumber(vid.fov, 1, 170, 1, 45, "field of view",
EX ld max_fov_angle() {
if(panini_alpha >= 1 || panini_alpha <= -1) return 360;
return acos(-panini_alpha) * 2 / degree;
}
EX void add_edit_fov(char key IS('f'), bool pop IS(false)) {
string sfov = fts(vid.fov) + "°";
if(panini_alpha) {
sfov += " / " + fts(max_fov_angle()) + "°";
}
dialog::addSelItem(XLAT("field of view"), sfov, key);
dialog::add_action([=] {
if(pop) popScreen();
dialog::editNumber(vid.fov, 1, max_fov_angle(), 1, 45, "field of view",
XLAT(
"Horizontal field of view, in angles. "
"This affects the Hypersian Rug mode (even when stereo is OFF) "
"and non-disk models.")
"and non-disk models.") + "\n\n" +
XLAT(
"Must be less than %1°. Panini projection can be used to get higher values.",
fts(max_fov_angle())
)
);
dialog::bound_low(1e-8);
dialog::bound_up(179);
dialog::bound_up(max_fov_angle() - 0.01);
dialog::extra_options = [] {
dialog::addSelItem(XLAT("Panini projection"), fts(panini_alpha), 'P');
dialog::add_action([] {
popScreen();
dialog::editNumber(panini_alpha, 0, 1, 0.1, 0, "Panini parameter",
XLAT(
"The Panini projection is an alternative perspective projection "
"which allows very wide field-of-view values. HyperRogue uses "
"a quick implementation, so parameter values too close to 1 may "
"be buggy; try e.g. 0.9 instead.")
);
dialog::reaction = ray::reset_raycaster;
dialog::extra_options = [] {
add_edit_fov('F', true);
};
});
};
});
}
@ -2654,7 +2720,7 @@ EX int read_gamemode_args() {
auto ah_config = addHook(hooks_args, 0, read_config_args) + addHook(hooks_args, 0, read_gamemode_args) + addHook(hooks_args, 0, read_color_args);
#endif
EX unordered_map<string, ld&> params = {
EX map<string, ld&> params = {
{"linewidth", vid.linewidth},
{"patternlinewidth", linepatterns::width},
{"scale", pconf.scale},

View File

@ -480,11 +480,13 @@ EX void handleKeyNormal(int sym, int uni) {
}
#endif
#if CAP_COMPLEX2
if(DEFAULTNOR(sym)) {
gmodekeys(sym, uni);
if(uni == 'm' && canmove && (centerover == cwt.at ? mouseover : centerover))
mine::performMarkCommand(mouseover);
}
#endif
if(DEFAULTCONTROL) {
if(sym == '.' || sym == 's') movepcto(-1, 1);

View File

@ -9,7 +9,6 @@
namespace hr {
EX namespace crystal {
#if CAP_CRYSTAL
#if HDR
static const int MAXDIM = 7;
@ -34,6 +33,8 @@ struct ldcoord : public array<ld, MAXDIM> {
static const ldcoord ldc0 = {};
#endif
#if CAP_CRYSTAL
/** Crystal can be bitruncated either by changing variation to bitruncated.
* In case of the 4D Crystal, the standard HyperRogue bitruncation becomes
* confused by having both the original and new vertices of degree 8.
@ -470,7 +471,7 @@ struct hrmap_crystal : hrmap_standard {
map<coord, heptagon*> heptagon_at;
map<int, eLand> landmemo;
map<coord, eLand> landmemo4;
unordered_map<cell*, unordered_map<cell*, int>> distmemo;
map<cell*, map<cell*, int>> distmemo;
map<cell*, ldcoord> sgc;
cell *camelot_center;
ldcoord camelot_coord;

View File

@ -721,12 +721,14 @@ int read_cheat_args() {
inv::compute();
}
#endif
#if CAP_COMPLEX2
else if(argis("-ambush")) {
// make all ambushes use the given number of dogs
// example: hyper -W Hunt -IP Shield 1 -ambush 60
PHASE(3) cheat();
shift(); ambush::fixed_size = argi();
}
#endif
else if(argis("-testdistances")) {
PHASE(3); shift(); test_distances(argi());
}
@ -875,6 +877,11 @@ int read_cheat_args() {
cheat();
gen_wandering = false;
}
else if(argis("-hroll")) {
shift();
int i = argi();
while(i>0) i--, hrand(10);
}
else if(argis("-W")) {
PHASEFROM(2);
shift();

View File

@ -64,7 +64,10 @@ EX namespace dialog {
EX color_t dialogcolor = 0xC0C0C0;
EX void addBack() {
addItem(XLAT("go back"), ISWEB ? SDLK_BACKSPACE : SDLK_ESCAPE);
addItem(XLAT("go back"),
(cmode & sm::NUMBER) ? SDLK_RETURN :
ISWEB ? SDLK_BACKSPACE :
SDLK_ESCAPE);
}
EX void addHelp() {
@ -523,20 +526,37 @@ EX namespace dialog {
return it.type == diItem || it.type == diBigItem;
}
EX void handle_actions(int &sym, int &uni) {
if(key_actions.count(uni)) {
key_actions[uni]();
sym = uni = 0;
return;
}
if(key_actions.count(sym)) {
key_actions[sym]();
sym = uni = 0;
return;
}
}
EX void handleNavigation(int &sym, int &uni) {
if(uni == '\n' || uni == '\r' || DIRECTIONKEY == SDLK_KP5)
if(uni == '\n' || uni == '\r' || DIRECTIONKEY == SDLK_KP5) {
for(int i=0; i<isize(items); i++)
if(isitem(items[i]))
if(items[i].body == highlight_text) {
uni = sym = items[i].key;
handle_actions(sym, uni);
return;
}
}
if(DKEY == SDLK_PAGEDOWN) {
uni = sym = 0;
for(int i=0; i<isize(items); i++)
if(isitem(items[i]))
highlight_text = items[i].body;
}
if(DKEY == SDLK_PAGEUP) {
uni = sym = 0;
for(int i=0; i<isize(items); i++)
if(isitem(items[i])) {
highlight_text = items[i].body;
@ -544,11 +564,11 @@ EX namespace dialog {
}
}
if(DKEY == SDLK_UP) {
uni = sym = 0;
string last = "";
for(int i=0; i<isize(items); i++)
if(isitem(items[i]))
last = items[i].body;
uni = sym = 0;
for(int i=0; i<isize(items); i++)
if(isitem(items[i])) {
if(items[i].body == highlight_text) {
@ -559,6 +579,7 @@ EX namespace dialog {
highlight_text = last;
}
if(DKEY == SDLK_DOWN) {
uni = sym = 0;
int state = 0;
for(int i=0; i<isize(items); i++)
if(isitem(items[i])) {
@ -570,18 +591,8 @@ EX namespace dialog {
highlight_text = items[i].body;
break;
}
uni = sym = 0;
}
if(key_actions.count(uni)) {
key_actions[uni]();
sym = uni = 0;
return;
}
if(key_actions.count(sym)) {
key_actions[sym]();
sym = uni = 0;
return;
}
handle_actions(sym, uni);
}
color_t colorhistory[10] = {
@ -697,7 +708,7 @@ EX namespace dialog {
getcstat = 'A' + i, inslider = true;
}
displayColorButton(dcenter, vid.yres/2+vid.fsize * 6, XLAT("select this color") + " : " + itsh(color), ' ', 8, 0, color >> (colorAlpha ? ash : 0));
displayColorButton(dcenter, vid.yres/2+vid.fsize * 6, XLAT("select this color") + " : " + format(colorAlpha ? "%08X" : "%06X", color), ' ', 8, 0, color >> (colorAlpha ? ash : 0));
if(extra_options) extra_options();

View File

@ -93,9 +93,11 @@ void launch(int seed, int elimit, int hlimit) {
cl1 = cl.lst;
for(cell *c: cl.lst) {
c->wall = waNone, c->land = laCanvas;
#if CAP_ARCM
int id = arcm::current.tilegroup[arcm::id_of(c->master)];
color_t yellows[5] = { 0x80C080, 0x80C0C0, 0x8080C0, 0xC080C0, 0xC0C080 };
c->landparam = yellows[id];
#endif
}
println(hlog, "c1 size = ", isize(cl.lst));
}

View File

@ -293,6 +293,8 @@ EX bool two_sided_model() {
if(pmodel == mdHyperboloid) return !euclid;
// if(pmodel == mdHemisphere) return true;
if(pmodel == mdDisk) return sphere;
if(pmodel == mdRetroLittrow) return sphere;
if(pmodel == mdRetroHammer) return sphere;
if(pmodel == mdHemisphere) return true;
if(pmodel == mdRotatedHyperboles) return true;
if(pmodel == mdSpiral && pconf.spiral_cone < 360) return true;
@ -305,6 +307,12 @@ EX int get_side(const hyperpoint& H) {
double horizon = curnorm / pconf.alpha;
return (H[2] <= -horizon) ? -1 : 1;
}
if(pmodel == mdRetroLittrow && sphere) {
return H[2] >= 0 ? 1 : -1;
}
if(pmodel == mdRetroHammer && sphere) {
return H[2] >= 0 ? 1 : -1;
}
if(pmodel == mdRotatedHyperboles)
return H[1] > 0 ? -1 : 1;
if(pmodel == mdHyperboloid && hyperbolic)
@ -2111,7 +2119,7 @@ EX void sort_drawqueue() {
int siz = isize(ptds);
#if MINIMIZE_GL_CALLS
unordered_map<color_t, vector<unique_ptr<drawqueueitem>>> subqueue;
map<color_t, vector<unique_ptr<drawqueueitem>>> subqueue;
for(auto& p: ptds) subqueue[(p->prio == PPR::CIRCLE || p->prio == PPR::OUTCIRCLE) ? 0 : p->outline_group()].push_back(move(p));
ptds.clear();
for(auto& p: subqueue) for(auto& r: p.second) ptds.push_back(move(r));
@ -2156,7 +2164,7 @@ EX void reverse_side_priorities() {
// on the sphere, parts on the back are drawn first
EX void draw_backside() {
DEBBI(DF_GRAPH, ("draw_backside"));
if(pmodel == mdHyperboloid && hyperbolic) {
if(pmodel == mdHyperboloid && hyperbolic && pconf.show_hyperboloid_flat) {
dynamicval<eModel> dv (pmodel, mdHyperboloidFlat);
for(auto& ptd: ptds)
if(!among(ptd->prio, PPR::MOBILE_ARROW, PPR::OUTCIRCLE, PPR::CIRCLE))

View File

@ -129,9 +129,10 @@ EX void compute_graphical_distance() {
}
}
EX void computePathdist(eMonster param) {
EX void computePathdist(eMonster param, bool include_allies IS(true)) {
for(cell *c: targets)
if(include_allies || isPlayerOn(c))
onpath(c, isPlayerOn(c) ? 0 : 1, hrand(c->type));
int qtarg = isize(targets);
@ -158,8 +159,11 @@ EX void computePathdist(eMonster param) {
// printf("i=%d cd=%d\n", i, c->move(i)->cpdist);
cell *c2 = c->move(i);
flagtype f = P_MONSTER | P_REVDIR;
if(param == moTameBomberbird) f |= P_FLYING;
if(c2 && c2->pathdist == PINFD &&
passable(c2, (qb<qtarg) && !nonAdjacent(c,c2) && !thruVine(c,c2) ?NULL:c, P_MONSTER | P_REVDIR)) {
passable(c2, (qb<qtarg) && !nonAdjacent(c,c2) && !thruVine(c,c2) ?NULL:c, f)) {
if(qb >= qtarg) {
if(param == moTortoise && nogoSlow(c, c2)) continue;
@ -186,9 +190,9 @@ struct pathdata {
pathlock--;
clear_pathdata();
}
pathdata(eMonster m) {
pathdata(eMonster m, bool include_allies IS(true)) {
checklock();
computePathdist(m);
computePathdist(m, include_allies);
}
pathdata(int i) {
checklock();

View File

@ -100,7 +100,7 @@ EX namespace euc {
/** ? */
intmatrix inverse_axes;
/** for canonicalization on tori */
unordered_map<coord, int> hash;
map<coord, int> hash;
vector<coord> seq;
int index;
@ -150,11 +150,13 @@ EX namespace euc {
tmatrix[i] = eumove(shifttable[i]);
camelot_center = NULL;
build_torus3(geometry);
#if CAP_IRR
if(!valid_irr_torus()) {
addMessage(XLAT("Error: period mismatch"));
eu_input = irr::base_config;
build_torus3(geometry);
}
#endif
}
heptagon *getOrigin() override {
@ -168,6 +170,7 @@ EX namespace euc {
auto h = tailored_alloc<heptagon> (S7);
if(!IRREGULAR)
h->c7 = newCell(S7, h);
#if CAP_IRR
else {
coord m0 = shifttable[0];
transmatrix dummy;
@ -180,6 +183,7 @@ EX namespace euc {
break;
}
}
#endif
h->distance = 0;
h->cdata = NULL;
h->alt = NULL;
@ -534,6 +538,7 @@ EX namespace euc {
}
EX bool valid_irr_torus() {
#if CAP_IRR
if(!IRREGULAR) return true;
if(eu.twisted) return false;
for(int i=0; i<2; i++) {
@ -547,6 +552,7 @@ EX namespace euc {
eu.canonicalize(x0, dm0, dummy, mirr);
if(x0 != euzero || dm0 != eutester) return false;
}
#endif
return true;
}

View File

@ -81,7 +81,9 @@ EX namespace fake {
transmatrix S1, S2;
ld dist;
in_underlying([c, d, &S1, &S2, &dist] {
#if CAP_ARCM
dynamicval<bool> u(arcm::use_gmatrix, false);
#endif
transmatrix T = currentmap->adj(c, d);
S1 = rspintox(tC0(T));
transmatrix T1 = spintox(tC0(T)) * T;
@ -89,12 +91,16 @@ EX namespace fake {
S2 = xpush(-dist) * T1;
});
#if CAP_ARCM
if(arcm::in()) {
int t = arcm::id_of(c->master);
int t2 = arcm::id_of(c->move(d)->master);
auto& cof = arcm::current_or_fake();
cgi.adjcheck = cof.inradius[t/2] + cof.inradius[t2/2];
}
#else
if(0) ;
#endif
else if(WDIM == 2) {
@ -400,7 +406,9 @@ EX ld around;
/** @brief the value of 'around' which makes the tiling Euclidean */
EX ld compute_euclidean() {
#if CAP_ARCM
if(arcm::in()) return arcm::current.N * 2 / arcm::current.euclidean_angle_sum;
#endif
if(WDIM == 2) return 4 / (S7-2.) + 2;
if(underlying == gRhombic3) return 3;
@ -411,8 +419,10 @@ EX ld compute_euclidean() {
}
EX ld around_orig() {
#if CAP_ARCM
if(arcm::in())
return arcm::current.N;
#endif
if(WDIM == 2)
return S3;
if(underlying == gRhombic3)

View File

@ -424,6 +424,48 @@ void geometry_information::generate_floorshapes_for(int id, cell *c, int siid, i
}
}
else if(arb::in()) {
vector<hyperpoint> actual;
for(int j=0; j<cor; j++)
actual.push_back(get_corner_position(c, j));
ld min_dist = 1e3;
for(int j=0; j<cor; j++)
for(int k=0; k<j; k++) {
ld dist = hdist(actual[j], actual[k]);
if(dist > 1e-6 && dist < min_dist)
min_dist = dist;
}
ld dist = min_dist * (1 - 3 / sca);
ld area = 0;
for(int j=0; j<cor; j++) {
hyperpoint current = kleinize(actual[j]);
hyperpoint last = kleinize(actual[j?j-1:cor-1]);
area += current[0] * last[1] - last[0] * current[1];
}
if(area < 0) dist = -dist;
for(int j=0; j<cor; j++) {
hyperpoint last = actual[j?j-1:cor-1];
hyperpoint current = actual[j];
hyperpoint next = actual[j<cor-1?j+1:0];
auto T = gpushxto0(current);
last = T * last;
next = T * next;
hyperpoint a = rspintox(last) * ypush0(dist);
hyperpoint b = rspintox(last) * xpush(hdist0(last)) * ypush0(dist);
hyperpoint c = rspintox(next) * ypush0(-dist);
hyperpoint d = rspintox(next) * xpush(hdist0(next)) * ypush0(-dist);
hyperpoint h = linecross(a, b, c, d);
cornerlist.push_back(rgpushxto0(current) * h);
}
}
else {
for(int j=0; j<cor; j++)
cornerlist.push_back(get_corner_position(c, j, sca));
@ -771,7 +813,9 @@ void geometry_information::generate_floorshapes() {
}
else {
cell model;
static hrmap_standard stdmap;
dynamicval<hrmap*> c(currentmap, &stdmap);
// cell model;
model.type = S6; generate_floorshapes_for(0, &model, 0, 0);
model.type = S7; generate_floorshapes_for(1, &model, bt::in() ? 0 : 1, 0);
}
@ -886,24 +930,30 @@ EX int shvid(cell *c) {
}
else if(GOLDBERG_INV)
return gp::get_plainshape_id(c);
#if CAP_IRR
else if(IRREGULAR)
return irr::cellindex[c];
#endif
#if CAP_ARCM
else if(arcm::in()) {
auto& ac = arcm::current;
int id = arcm::id_of(c->master);
if(ac.regular && id>=2 && id < 2*ac.N) id &= 1;
return id;
}
#endif
else if(arb::in())
return arb::id_of(c->master);
else if(geosupport_football() == 2)
return pseudohept(c);
#if CAP_BT
else if(geometry == gBinaryTiling)
return c->type-6;
else if(kite::in())
return kite::getshape(c->master);
else if(geometry == gBinary4 || geometry == gTernary)
return c->master->zebraval;
#endif
else if(inforder::mixed()) {
int t = c->type;
static vector<bool> computed;

View File

@ -182,7 +182,7 @@ EX bool activateRecall() {
killFriendlyIvy();
movecost(cwt.at, recallCell.at, 3);
playerMoveEffects(cwt.at, recallCell.at);
playerMoveEffects(movei(cwt.at, recallCell.at, TELEPORT));
mirror::destroyAll();
sword::reset();

View File

@ -300,6 +300,7 @@ void set_or_configure_geometry(eGeometry g) {
addMessage(XLAT("Only works with (semi-)regular tilings"));
return;
}
#if CAP_ARCM
if(arcm::in()) {
int steps, single_step;
if(!arcm::current.get_step_values(steps, single_step)) {
@ -307,6 +308,7 @@ void set_or_configure_geometry(eGeometry g) {
return;
}
}
#endif
}
}
dual::may_split_or_do([g] { set_geometry(g); });
@ -384,7 +386,9 @@ void ge_select_tiling() {
EX string current_proj_name() {
bool h = hyperbolic || sn::in();
if(vpconf.model != mdDisk)
if(vpconf.model == mdPanini && vpconf.alpha == 1)
return XLAT("stereographic Panini");
else if(vpconf.model != mdDisk)
return models::get_model_name(vpconf.model);
else if(h && vpconf.alpha == 1)
return XLAT("Poincaré model");
@ -540,8 +544,10 @@ EX void select_quotient_screen() {
}
else if(g == gFieldQuotient)
pushScreen(showQuotientConfig);
#if CAP_CRYSTAL
else if(g == gCrystal)
pushScreen(crystal::show);
#endif
else {
dual::may_split_or_do([g] { set_geometry(g); });
start_game();
@ -707,7 +713,9 @@ EX void showEuclideanMenu() {
worldsize = euc::eu.det;
if(BITRUNCATED) worldsize *= (a4 ? 2 : 3);
if(GOLDBERG) worldsize *= cgi.gpdata->area;
#if CAP_IRR
if(IRREGULAR) worldsize *= isize(irr::cells) / isize(irr::cells_of_heptagon);
#endif
}
else
worldsize = denom ? nom / denom : 0;
@ -794,15 +802,19 @@ EX void showEuclideanMenu() {
dialog::add_action(select_quotient);
#if CAP_ARCM
if(arcm::in()) {
dialog::addItem(XLAT("advanced parameters"), '4');
dialog::add_action_push(arcm::show);
}
#endif
#if CAP_CRYSTAL
if(cryst) {
dialog::addItem(XLAT("advanced parameters"), '4');
dialog::add_action_push(crystal::show);
}
#endif
if(fake::available()) {
dialog::addItem(XLAT("fake curvature"), '4');

View File

@ -673,12 +673,17 @@ void geometry_information::prepare_basics() {
plevel = vid.plevel_factor * scalefactor;
single_step = 1;
if(hybri && !prod) {
#if CAP_ARCM
if(hybrid::underlying == gArchimedean)
arcm::current.get_step_values(psl_steps, single_step);
#else
if(0) ;
#endif
else {
single_step = S3 * S7 - 2 * S7 - 2 * S3;
psl_steps = 2 * S7;
if(BITRUNCATED) psl_steps *= S3;
if(inv) psl_steps = 2 * S3;
if(single_step < 0) single_step = -single_step;
}
DEBB(DF_GEOM | DF_POLY, ("steps = ", psl_steps, " / ", single_step));
@ -913,7 +918,7 @@ EX void apply_always3() {
#if MAXMDIM >= 4
EX void switch_always3() {
if(dual::split(switch_always3)) return;
#if CAP_GL
#if CAP_GL && CAP_RUG
if(rug::rugged) rug::close();
#endif
vid.always3 = !vid.always3;
@ -946,7 +951,7 @@ EX void switch_always3() {
EX void switch_fpp() {
#if MAXMDIM >= 4
#if CAP_GL
#if CAP_GL && CAP_RUG
if(rug::rugged) rug::close();
#endif
if(dual::split(switch_fpp)) return;
@ -1026,7 +1031,9 @@ EX string cgi_string() {
if(GOLDBERG_INV) V("GP", its(gp::param.first) + "," + its(gp::param.second));
if(IRREGULAR) V("IRR", its(irr::irrid));
#if CAP_ARCM
if(arcm::in()) V("ARCM", arcm::current.symbol);
#endif
if(arb::in()) V("ARB", its(arb::current.order));
@ -1034,7 +1041,10 @@ EX string cgi_string() {
if(bt::in() || GDIM == 3) V("WQ", its(vid.texture_step));
if(hybri) V("U", its(int(hybrid::underlying)));
if(hybri) {
V("U", PIU(cgi_string()));
// its(int(hybrid::underlying)));
}
if(prod) V("PL", fts(vid.plevel_factor));

View File

@ -12,14 +12,14 @@ shiftmatrix &ggmatrix(cell *c);
EX void fixelliptic(transmatrix& at) {
if(elliptic && at[LDIM][LDIM] < 0) {
for(int i=0; i<MDIM; i++) for(int j=0; j<MDIM; j++)
for(int i=0; i<MXDIM; i++) for(int j=0; j<MXDIM; j++)
at[i][j] = -at[i][j];
}
}
EX void fixelliptic(hyperpoint& h) {
if(elliptic && h[LDIM] < 0)
for(int i=0; i<MDIM; i++) h[i] = -h[i];
for(int i=0; i<MXDIM; i++) h[i] = -h[i];
}
/** find relative_matrix via recursing the tree structure */
@ -398,6 +398,7 @@ ld hrmap_standard::spin_angle(cell *c, int d) {
ld hexshift = 0;
if(c == c->master->c7 && (S7 % 2 == 0) && BITRUNCATED) hexshift = cgi.hexshift + 2*M_PI/c->type;
else if(cgi.hexshift && c == c->master->c7) hexshift = cgi.hexshift;
#if CAP_IRR
if(IRREGULAR) {
auto id = irr::cellindex[c];
auto& vs = irr::cells[id];
@ -405,7 +406,7 @@ ld hrmap_standard::spin_angle(cell *c, int d) {
auto& p = vs.jpoints[vs.neid[d]];
return -atan2(p[1], p[0]) - hexshift;
}
else
#endif
return M_PI - d * 2 * M_PI / c->type - hexshift;
}
@ -594,6 +595,7 @@ EX bool approx_nearcorner = false;
EX hyperpoint nearcorner(cell *c, int i) {
if(GOLDBERG_INV) {
i = gmod(i, c->type);
cellwalker cw(c, i);
cw += wstep;
transmatrix cwm = currentmap->adj(c, i);

View File

@ -154,7 +154,7 @@ void display(const glmatrix& m) {
printf("\n");
}
glmatrix operator * (glmatrix m1, glmatrix m2) {
EX glmatrix operator * (glmatrix m1, glmatrix m2) {
glmatrix res;
for(int i=0; i<4; i++)
for(int j=0; j<4; j++) {
@ -694,6 +694,10 @@ template<class T> void bindbuffer(T& v) {
#define PTR(attrib, q, field) \
glVertexAttribPointer(attrib, q, GL_FLOAT, GL_FALSE, sizeof(v[0]), (void*) ((char*) &v[0].field - (char*) &v[0]));
EX void bindbuffer_vertex(vector<glvertex>& v) {
bindbuffer(v);
}
#endif
EX void vertices(const vector<glvertex>& v, int vshift IS(0)) {

View File

@ -806,6 +806,7 @@ EX namespace gp {
dialog::addBreak(100);
#if CAP_IRR
if(irr::supports(geometry)) {
dialog::addBoolItem(XLAT("irregular"), IRREGULAR, 'i');
dialog::add_action(dialog::add_confirmation([=] () {
@ -817,6 +818,7 @@ EX namespace gp {
if(!IRREGULAR) irr::visual_creator();
}));
}
#endif
dialog::addBreak(100);
int style = 0;
@ -1126,6 +1128,8 @@ EX namespace gp {
}
transmatrix relative_matrix(cell *c2, cell *c1, const hyperpoint& hint) override {
c1 = mapping[c1];
c2 = mapping[c2];
return in_underlying([&] { return currentmap->relative_matrix(c2, c1, hint); });
}

View File

@ -22,6 +22,8 @@ EX bool spatial_graphics;
EX bool wmspatial, wmescher, wmplain, wmblack, wmascii, wmascii3;
EX bool mmspatial, mmhigh, mmmon, mmitem;
EX ld panini_alpha = 0;
EX int detaillevel = 0;
EX bool first_cell_to_draw = true;
@ -1020,7 +1022,11 @@ EX void drawTerraWarrior(const shiftmatrix& V, int t, int hp, double footphase)
EX void drawPlayer(eMonster m, cell *where, const shiftmatrix& V, color_t col, double footphase, bool stop IS(false)) {
charstyle& cs = getcs();
#if CAP_COMPLEX2
auto& knighted = camelot::knighted;
#else
const bool knighted = false;
#endif
if(mapeditor::drawplayer && !mapeditor::drawUserShape(V, mapeditor::sgPlayer, cs.charid, cs.skincolor, where)) {
@ -1264,8 +1270,10 @@ void drawMimic(eMonster m, cell *where, const shiftmatrix& V, color_t col, doubl
else if(!where || shmup::curtime >= shmup::getPlayer()->nextshot)
queuepoly(VBODY * VBS, cgi.shPKnife, darkena(col, 0, 0XC0));
#if CAP_COMPLEX2
if(camelot::knighted)
queuepoly(VBODY3 * VBS, cgi.shKnightCloak, darkena(col, 1, 0xC0));
#endif
queuepoly(VHEAD1, (cs.charid&1) ? cgi.shFemaleHair : cgi.shPHead, darkena(col, 1, 0XC0));
queuepoly(VHEAD, cgi.shPFace, darkena(col, 0, 0XC0));
@ -3070,8 +3078,22 @@ EX void drawaura() {
else rad0 *= m;
}
cx[r][z][0] = rad0 * c;
cx[r][z][1] = rad0 * s * pconf.stretch;
ld x = rad0 * c;
ld y = rad0 * s;
if(pconf.camera_angle) {
ld z = rad0;
ld cam = pconf.camera_angle * degree;
GLfloat cc = cos(cam);
GLfloat ss = sin(cam);
tie(y, z) = make_pair(y * cc - z * ss, z * cc + y * ss);
x *= rad0 / z;
y *= rad0 / z;
}
cx[r][z][0] = x;
cx[r][z][1] = y * pconf.stretch;
for(int u=0; u<3; u++)
cx[r][z][u+2] = bak[u] + (aurac[rm][u] / (aurac[rm][3]+.1) - bak[u]) * cmul[z];
@ -3410,7 +3432,11 @@ EX bool placeSidewall(cell *c, int i, int sidepar, const shiftmatrix& V, color_t
#endif
bool openorsafe(cell *c) {
#if CAP_COMPLEX2
return c->wall == waMineOpen || mine::marked_safe(c);
#else
return false;
#endif
}
#define Dark(x) darkena(x,0,0xFF)
@ -3693,7 +3719,7 @@ EX bool frustum_culling = true;
void make_clipping_planes() {
#if MAXMDIM >= 4
clipping_planes.clear();
if(!frustum_culling || PIU(sphere) || experimental || vid.stereo_mode == sODS) return;
if(!frustum_culling || PIU(sphere) || experimental || vid.stereo_mode == sODS || panini_alpha) return;
auto add_clipping_plane = [] (ld x1, ld y1, ld x2, ld y2) {
ld z1 = 1, z2 = 1;
hyperpoint sx = point3(y1 * z2 - y2 * z1, z1 * x2 - z2 * x1, x1 * y2 - x2 * y1);
@ -3788,7 +3814,9 @@ EX void gridline(const shiftmatrix& V, const hyperpoint h1, const hyperpoint h2,
EX int wall_offset(cell *c) {
if(hybri || WDIM == 2) return hybrid::wall_offset(c);
#if CAP_BT
if(kite::in() && kite::getshape(c->master) == kite::pKite) return 10;
#endif
return 0;
}
@ -4455,7 +4483,6 @@ EX bool allowChangeRange() {
if(tour::on) return true;
#endif
if(racing::on) return true;
if(sightrange_bonus >= 0) return true;
if(arcm::in() || arb::in()) return true;
if(WDIM == 3) return true;
return false;
@ -4866,7 +4893,8 @@ EX void calcparam() {
cd->xcenter += cd->scrsize * pconf.xposition;
cd->ycenter += cd->scrsize * pconf.yposition;
cd->tanfov = tan(vid.fov * degree / 2);
ld fov = vid.fov * degree / 2;
cd->tanfov = sin(fov) / (cos(fov) + panini_alpha);
callhooks(hooks_calcparam);
reset_projection();
@ -4966,13 +4994,22 @@ EX void gamescreen(int _darken) {
return;
}
auto cdc = *current_display;
auto gx = vid.xres;
auto gy = vid.yres;
if(dual::split([=] () {
*current_display = cdc;
vid.xres = gx;
vid.yres = gy;
dual::in_subscreen([=] () { gamescreen(_darken); });
})) {
calcparam();
return;
}
calcparam();
if((cmode & sm::MAYDARK) && !current_display->sidescreen) {
_darken += 2;
}

View File

@ -232,7 +232,7 @@ EX void buildCredits() {
"Kojiguchi Kazuki, baconcow, Alan, SurelyYouJest, hotdogPi, DivisionByZero, xXxWeedGokuxXx, jpystynen, Dmitry Marakasov, Alexandre Moine, Arthur O'Dwyer, "
"Triple_Agent_AAA, bluetailedgnat, Allalinor, Shitford, KittyTac, Christopher King, KosGD, TravelDemon, Bubbles, rdococ, frozenlake, MagmaMcFry, "
"Snakebird Priestess, roaringdragon2, Stopping Dog, bengineer8, Sir Light IJIJ, ShadeBlade, Saplou, shnourok, Ralith, madasa, 6% remaining, Chimera245, Remik Pi, alien foxcat thing, "
"Piotr Grochowski"
"Piotr Grochowski, Ann, still-flow"
);
#ifdef EXTRALICENSE
help += EXTRALICENSE;
@ -928,8 +928,12 @@ EX void describeMouseover() {
if(isReptile(c->wall))
out += " [" + turnstring((unsigned char) c->wparam) + "]";
#if CAP_COMPLEX2
if(c->monst == moKnight)
out += XLAT(", %1 the Knight", camelot::knight_name(c));
#else
if(0) ;
#endif
else if(c->monst) {
out += ", "; out += XLAT1(minf[c->monst].name);
@ -950,10 +954,12 @@ EX void describeMouseover() {
out += ", ";
out += XLAT1(iinf[c->item].name);
if(c->item == itBarrow) out += " (x" + its(c->landparam) + ")";
#if CAP_COMPLEX2
if(c->land == laHunting) {
int i = ambush::size(c, c->item);
if(i) out += " (" + XLAT("ambush:") + " " + its(i) + ")";
}
#endif
if(c->item == itBabyTortoise && tortoise::seek())
out += " " + tortoise::measure(tortoise::babymap[c]);
if(!c->monst) set_help_to(c->item);
@ -1000,12 +1006,14 @@ EX void describeMouseover() {
callhooks(hooks_mouseover, c);
if(mousey < vid.fsize * 3/2 && getcstat == '-' && !instat) getcstat = SDLK_F1;
#if CAP_TOUR
if(tour::on && !tour::texts) {
if(tour::slides[tour::currentslide].flags & tour::NOTITLE)
mouseovers = "";
else
mouseovers = XLAT(tour::slides[tour::currentslide].name);
}
#endif
}
EX void showHelp() {

View File

@ -279,6 +279,12 @@ heptagon *hrmap_standard::create_step(heptagon *h, int d) {
else
buildHeptagon(h, d, transition(h->s, d));
}
else if(S3 > 4 && quotient) {
/* this branch may be used for some >4-valent quotient spaces outside of standard HyperRogue */
/* this is wrong, but we don't care in quotient */
h->move(d) = h;
// buildHeptagon(h, d, transition(h->s, d));
}
else if(d == 1) {
addSpin(h, d, h->move(0), h->c.spin(0)-1, -1);
}

View File

@ -228,6 +228,7 @@ bool displayglyph(int cx, int cy, int buttonsize, char glyph, color_t color, int
glyphphase[id] * 2;
drawItemType(it, NULL, shiftless(V), icol, t, false);
}
sortquickqueue();
quickqueue();
}
else if(glyph == '*')

18
hyper.h
View File

@ -13,8 +13,8 @@
#define _HYPER_H_
// version numbers
#define VER "11."
#define VERNUM_HEX 0xA83B
#define VER "11.4a"
#define VERNUM_HEX 0xA841
#include "sysconfig.h"
@ -48,7 +48,6 @@ template<class T, class V, class... U> bool among(T x, V y, U... u) { return x==
using std::vector;
using std::map;
using std::array;
using std::unordered_map;
using std::sort;
using std::multimap;
using std::set;
@ -242,6 +241,7 @@ struct projection_configuration {
ld model_orientation, halfplane_scale, model_orientation_yz;
ld collignon_parameter;
ld aitoff_parameter, miller_parameter, loximuthal_parameter, winkel_parameter;
bool show_hyperboloid_flat;
bool collignon_reflected;
string formula;
eModel basic_model;
@ -272,6 +272,7 @@ struct projection_configuration {
miller_parameter = .8;
loximuthal_parameter = 0;
winkel_parameter = .5;
show_hyperboloid_flat = true;
}
};
@ -383,16 +384,23 @@ struct videopar {
extern videopar vid;
/** \brief How many dimensional is the gameplay. In the FPP mode of a 2D geometry, WDIM is 2 */
#define WDIM cginf.g.gameplay_dimension
/** \brief How many dimensional is the graphical representation. In the FPP mode of a 2D geometry, MDIM is 3 */
#define GDIM cginf.g.graphical_dimension
/** \brief How many dimensions of the matrix representation are used. It is usually 3 in 2D geometries (not FPP) and in product geometries, 4 in 3D geometries */
#define MDIM (MAXMDIM == 3 ? 3 : cginf.g.homogeneous_dimension)
/** \brief What dimension of matrices is used in loops (the 'extra' dimensions have values 0 or 1 as in Id)
* Even if MDIM==3, it may be faster to keep 4x4 matrices and perform computations using them (rather than having another condition due to the variable loop size).
* The experiments on my computer show it to be the case, but the effect is not significant, and it may be different on another computer.
*/
#define MXDIM (CAP_MDIM_FIXED ? MAXMDIM : MDIM)
/** \brief The 'homogeneous' dimension index */
#define LDIM (MDIM-1)
#define cclass g.kind
#define self (*this)
// #define MODFIXER (2*10090080*17)
#define BUGCOLORS 3
#define big_unlock (inv::on && !chaosmode)

View File

@ -59,22 +59,22 @@ struct hyperpoint : array<ld, MAXMDIM> {
#endif
inline hyperpoint& operator *= (ld d) {
for(int i=0; i<MDIM; i++) self[i] *= d;
for(int i=0; i<MXDIM; i++) self[i] *= d;
return self;
}
inline hyperpoint& operator /= (ld d) {
for(int i=0; i<MDIM; i++) self[i] /= d;
for(int i=0; i<MXDIM; i++) self[i] /= d;
return self;
}
inline hyperpoint& operator += (const hyperpoint h2) {
for(int i=0; i<MDIM; i++) self[i] += h2[i];
for(int i=0; i<MXDIM; i++) self[i] += h2[i];
return self;
}
inline hyperpoint& operator -= (const hyperpoint h2) {
for(int i=0; i<MDIM; i++) self[i] -= h2[i];
for(int i=0; i<MXDIM; i++) self[i] -= h2[i];
return self;
}
@ -99,7 +99,7 @@ struct hyperpoint : array<ld, MAXMDIM> {
// inner product
inline friend ld operator | (hyperpoint h1, hyperpoint h2) {
ld sum = 0;
for(int i=0; i<MDIM; i++) sum += h1[i] * h2[i];
for(int i=0; i<MXDIM; i++) sum += h1[i] * h2[i];
return sum;
}
};
@ -118,18 +118,18 @@ struct transmatrix {
inline friend hyperpoint operator * (const transmatrix& T, const hyperpoint& H) {
hyperpoint z;
for(int i=0; i<MDIM; i++) {
for(int i=0; i<MXDIM; i++) {
z[i] = 0;
for(int j=0; j<MDIM; j++) z[i] += T[i][j] * H[j];
for(int j=0; j<MXDIM; j++) z[i] += T[i][j] * H[j];
}
return z;
}
inline friend transmatrix operator * (const transmatrix& T, const transmatrix& U) {
transmatrix R;
for(int i=0; i<MDIM; i++) for(int j=0; j<MDIM; j++) {
for(int i=0; i<MXDIM; i++) for(int j=0; j<MXDIM; j++) {
R[i][j] = 0;
for(int k=0; k<MDIM; k++)
for(int k=0; k<MXDIM; k++)
R[i][j] += T[i][k] * U[k][j];
}
return R;
@ -490,14 +490,14 @@ EX ld hypot_auto(ld x, ld y) {
EX hyperpoint normalize(hyperpoint H) {
if(prod) return H;
ld Z = zlevel(H);
for(int c=0; c<MDIM; c++) H[c] /= Z;
for(int c=0; c<MXDIM; c++) H[c] /= Z;
return H;
}
/** like normalize but makes (ultra)ideal points material */
EX hyperpoint ultra_normalize(hyperpoint H) {
if(material(H) <= 0) {
H[MDIM-1] = hypot_d(MDIM-1, H) + 1e-6;
H[LDIM] = hypot_d(LDIM, H) + 1e-6;
}
return normalize(H);
}
@ -531,7 +531,7 @@ EX hyperpoint midz(const hyperpoint& H1, const hyperpoint& H2) {
ld Z = 2;
if(!euclid) Z = zlevel(H3) * 2 / (zlevel(H1) + zlevel(H2));
for(int c=0; c<MDIM; c++) H3[c] /= Z;
for(int c=0; c<MXDIM; c++) H3[c] /= Z;
return H3;
}
@ -611,8 +611,8 @@ EX transmatrix cpush(int cid, ld alpha) {
EX transmatrix xpush(ld alpha) { return cpush(0, alpha); }
EX bool eqmatrix(transmatrix A, transmatrix B, ld eps IS(.01)) {
for(int i=0; i<MDIM; i++)
for(int j=0; j<MDIM; j++)
for(int i=0; i<MXDIM; i++)
for(int j=0; j<MXDIM; j++)
if(std::abs(A[i][j] - B[i][j]) > eps)
return false;
return true;
@ -702,7 +702,7 @@ EX transmatrix parabolic13(ld u, ld v) {
}
EX hyperpoint parabolic10(hyperpoint h) {
if(euclid) { h[MDIM] = 1; return h; }
if(euclid) { h[LDIM] = 1; return h; }
else if(MDIM == 4) return hyperpoint(sinh(h[0]), h[1]/exp(h[0]), h[2]/exp(h[0]), cosh(h[0]));
else return hyperpoint(sinh(h[0]), h[1]/exp(h[0]), cosh(h[0]), 0);
}
@ -768,18 +768,19 @@ EX transmatrix pushxto0(const hyperpoint& H) {
/** set the i-th column of T to H */
EX void set_column(transmatrix& T, int i, const hyperpoint& H) {
for(int j=0; j<MDIM; j++)
for(int j=0; j<MXDIM; j++)
T[j][i] = H[j];
}
/** build a matrix using the given vectors as columns */
EX transmatrix build_matrix(hyperpoint h1, hyperpoint h2, hyperpoint h3, hyperpoint h4) {
transmatrix T;
for(int i=0; i<MDIM; i++)
for(int i=0; i<MXDIM; i++) {
T[i][0] = h1[i],
T[i][1] = h2[i],
T[i][2] = h3[i];
if(MAXMDIM == 4) for(int i=0; i<MDIM; i++) T[i][3] = h4[i];
if(MAXMDIM == 4) T[i][3] = h4[i];
}
return T;
}
@ -868,11 +869,11 @@ EX void fixmatrix_euclid(transmatrix& T) {
EX void orthonormalize(transmatrix& T) {
for(int x=0; x<MDIM; x++) for(int y=0; y<=x; y++) {
ld dp = 0;
for(int z=0; z<MDIM; z++) dp += T[z][x] * T[z][y] * sig(z);
for(int z=0; z<MXDIM; z++) dp += T[z][x] * T[z][y] * sig(z);
if(y == x) dp = 1 - sqrt(sig(x)/dp);
for(int z=0; z<MDIM; z++) T[z][x] -= dp * T[z][y];
for(int z=0; z<MXDIM; z++) T[z][x] -= dp * T[z][y];
}
}
@ -893,20 +894,20 @@ EX ld det3(const transmatrix& T) {
/** determinant */
EX ld det(const transmatrix& T) {
if(GDIM == 2)
if(MDIM == 3)
return det3(T);
else {
ld det = 1;
transmatrix M = T;
for(int a=0; a<MDIM; a++) {
for(int b=a; b<=GDIM; b++)
for(int b=a; b<MDIM; b++)
if(M[b][a]) {
if(b != a)
for(int c=a; c<MDIM; c++) tie(M[b][c], M[a][c]) = make_pair(-M[a][c], M[b][c]);
break;
}
if(!M[a][a]) return 0;
for(int b=a+1; b<=GDIM; b++) {
for(int b=a+1; b<MDIM; b++) {
ld co = -M[b][a] / M[a][a];
for(int c=a; c<MDIM; c++) M[b][c] += M[a][c] * co;
}
@ -986,7 +987,7 @@ EX transmatrix ortho_inverse(transmatrix T) {
/** \brief inverse of an orthogonal matrix in Minkowski space */
EX transmatrix pseudo_ortho_inverse(transmatrix T) {
for(int i=1; i<MDIM; i++)
for(int i=1; i<MXDIM; i++)
for(int j=0; j<i; j++)
swap(T[i][j], T[j][i]);
for(int i=0; i<MDIM-1; i++)
@ -1127,7 +1128,7 @@ EX hyperpoint mscale(const hyperpoint& t, double fac) {
if(GDIM == 3 && !prod) return cpush(2, fac) * t;
if(prod) fac = exp(fac);
hyperpoint res;
for(int i=0; i<MDIM; i++)
for(int i=0; i<MXDIM; i++)
res[i] = t[i] * fac;
return res;
}
@ -1143,8 +1144,12 @@ EX transmatrix mscale(const transmatrix& t, double fac) {
}
if(prod) fac = exp(fac);
transmatrix res;
for(int i=0; i<MDIM; i++) for(int j=0; j<MDIM; j++)
for(int i=0; i<MXDIM; i++) {
for(int j=0; j<MDIM; j++)
res[i][j] = t[i][j] * fac;
for(int j=MDIM; j<MXDIM; j++)
res[i][j] = t[i][j];
}
return res;
}
@ -1154,17 +1159,25 @@ EX shiftmatrix mscale(const shiftmatrix& t, double fac) {
EX transmatrix xyscale(const transmatrix& t, double fac) {
transmatrix res;
for(int i=0; i<MDIM; i++) for(int j=0; j<GDIM; j++)
for(int i=0; i<MXDIM; i++) {
for(int j=0; j<GDIM; j++)
res[i][j] = t[i][j] * fac;
for(int j=GDIM; j<MXDIM; j++)
res[i][j] = t[i][j];
}
return res;
}
EX transmatrix xyzscale(const transmatrix& t, double fac, double facz) {
transmatrix res;
for(int i=0; i<MDIM; i++) for(int j=0; j<GDIM; j++)
for(int i=0; i<MXDIM; i++) {
for(int j=0; j<GDIM; j++)
res[i][j] = t[i][j] * fac;
for(int i=0; i<MDIM; i++)
res[i][LDIM] = t[i][LDIM] * facz;
res[i][LDIM] =
t[i][LDIM] * facz;
for(int j=LDIM+1; j<MXDIM; j++)
res[i][j] = t[i][j];
}
return res;
}
@ -1181,7 +1194,7 @@ EX transmatrix mzscale(const transmatrix& t, double fac) {
transmatrix res = t * inverse(tcentered) * ypush(-fac) * tcentered;
fac *= .2;
fac += 1;
for(int i=0; i<MDIM; i++) for(int j=0; j<MDIM; j++)
for(int i=0; i<MXDIM; i++) for(int j=0; j<MXDIM; j++)
res[i][j] = res[i][j] * fac;
return res;
}
@ -1300,8 +1313,8 @@ EX ld ortho_error(transmatrix T) {
EX transmatrix transpose(transmatrix T) {
transmatrix result;
for(int i=0; i<MDIM; i++)
for(int j=0; j<MDIM; j++)
for(int i=0; i<MXDIM; i++)
for(int j=0; j<MXDIM; j++)
result[j][i] = T[i][j];
return result;
}
@ -1340,7 +1353,7 @@ inline hyperpoint zpush0(ld x) { return cpush0(2, x); }
/** T * C0, optimized */
inline hyperpoint tC0(const transmatrix &T) {
hyperpoint z;
for(int i=0; i<MDIM; i++) z[i] = T[i][LDIM];
for(int i=0; i<MXDIM; i++) z[i] = T[i][LDIM];
return z;
}
@ -1485,4 +1498,24 @@ EX hyperpoint lerp(hyperpoint a0, hyperpoint a1, ld x) {
return a0 + (a1-a0) * x;
}
EX hyperpoint linecross(hyperpoint a, hyperpoint b, hyperpoint c, hyperpoint d) {
a /= a[LDIM];
b /= b[LDIM];
c /= c[LDIM];
d /= d[LDIM];
ld bax = b[0] - a[0];
ld dcx = d[0] - c[0];
ld cax = c[0] - a[0];
ld bay = b[1] - a[1];
ld dcy = d[1] - c[1];
ld cay = c[1] - a[1];
hyperpoint res;
res[0] = (cay * dcx * bax + a[0] * bay * dcx - c[0] * dcy * bax) / (bay * dcx - dcy * bax);
res[1] = (cax * dcy * bay + a[1] * bax * dcy - c[1] * dcx * bay) / (bax * dcy - dcx * bay);
res[2] = 1;
return normalize(res);
}
}

View File

@ -2,21 +2,16 @@
// This is the main file when the online version of HyperRogue is compiled with Emscripten.
// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
#ifndef ISWEB
#define ISWEB 1
#define ISMINI 0
#define CAP_AUDIO 0
#define CAP_SDLGFX 0
#define CAP_PNG 0
#define CAP_TOUR 1
#define CAP_SDLTTF 0
#define CAP_SHMUP 0
#define CAP_RUG 1
#define CAP_INV 0
#define CAP_URL 1
#define GLES_ONLY
#define CAP_COMPLEX2 1
#define SCU
#endif
#if CAP_ROGUEVIZ
#if CAP_ROGUEVIZ || defined(MAXMDIM)
#define MAXMDIM 4
#else
#define MAXMDIM 3
@ -45,6 +40,7 @@ namespace hr {
void emscripten_get_commandline();
void loadCompressedChar(int &otwidth, int &otheight, int *tpix);
void get_canvas_size();
const char *wheresounds;
@ -53,7 +49,11 @@ namespace hr {
void offer_download(std::string sfilename, std::string smimetype);
}
#ifdef SCU
#include "hyper.cpp"
#else
#include "hyper.h"
#endif
namespace hr {
@ -72,7 +72,7 @@ string get_value(string name) {
return res;
}
void offer_download(string sfilename, string smimetype) {
EX void offer_download(string sfilename, string smimetype) {
EM_ASM({
var name = UTF8ToString($0, $1);
@ -105,7 +105,7 @@ extern "C" {
}
}
void offer_choose_file(reaction_t r) {
EX void offer_choose_file(reaction_t r) {
on_use_file = r;
EM_ASM({
fileElem.click();
@ -221,9 +221,39 @@ EM_BOOL fsc_callback(int eventType, const EmscriptenFullscreenChangeEvent *fulls
return true;
}
void initweb() {
EX void get_canvas_size() {
vid.xscr = vid.xres = EM_ASM_INT({
var d = document.getElementById("this_wide");
if(!d) return window.innerWidth;
return d.clientWidth;
});
if(vid.full) vid.yscr = vid.yres = EM_ASM_INT({
var d = document.getElementById("this_wide");
if(!d) return window.innerWidth;
return d.clientHeight;
});
else {
vid.xscr = vid.xres = vid.xres - 32;
vid.yscr = vid.yres = EM_ASM_INT({
return window.innerHeight;
}) - 32;
vid.yscr = vid.yres = min(vid.yscr, vid.xscr * 9 / 16);
vid.xscr = vid.xres = min(vid.xscr, vid.yscr * 16 / 9);
}
println(hlog, "X = ", vid.xscr, " Y = ", vid.yscr);
}
EM_BOOL resize_callback(int eventType, const EmscriptenUiEvent *resizeEvent, void *userData) {
if(vid.full) return true;
get_canvas_size();
setvideomode();
return true;
}
EX void initweb() {
// toggleanim(false);
emscripten_set_fullscreenchange_callback(0, NULL, false, fsc_callback);
emscripten_set_resize_callback(0, NULL, false, resize_callback);
printf("showstartmenu = %d\n", showstartmenu);
if(showstartmenu) pushScreen(showDemo);
}
@ -241,7 +271,7 @@ transmatrix getOrientation() {
}
#endif
void emscripten_get_commandline() {
EX void emscripten_get_commandline() {
#ifdef EMSCRIPTEN_FIXED_ARG
string s = EMSCRIPTEN_FIXED_ARG;
#else

View File

@ -1074,6 +1074,76 @@ EX void apply_other_model(shiftpoint H_orig, hyperpoint& ret, eModel md) {
}
}
case mdRetroCraig: {
makeband(H_orig, ret, [] (ld& x, ld& y) {
if(x)
y = x / sin_auto(x) * (sin_auto(y) * cos_auto(x) - tan_auto(pconf.loximuthal_parameter) * cos_auto(y));
else
y = sin_auto(y) - tan_auto(pconf.loximuthal_parameter) * cos_auto(y);
});
break;
}
case mdRetroLittrow: {
makeband(H_orig, ret, [] (ld& x, ld& y) {
tie(x, y) = make_pair(
sin_auto(x) / cos_auto(y),
cos_auto(x) * tan_auto(y)
);
});
break;
}
case mdRetroHammer: {
ld d = hdist(H, ypush0(pconf.loximuthal_parameter));
makeband(H_orig, ret, [d,H] (ld& x, ld& y) {
if(x == 0 && y == 0) return;
if(x)
y = x / sin_auto(x) * (sin_auto(y) * cos_auto(x) - tan_auto(pconf.loximuthal_parameter) * cos_auto(y));
else
y = sin_auto(y) - tan_auto(pconf.loximuthal_parameter) * cos_auto(y);
ld scale = d / hypot(x, y);
if(H[2] < 0) scale = -scale;
x *= scale;
y *= scale;
});
break;
}
case mdPanini: {
ld proh = sqrt(H[2]*H[2] + curvature() * H[0] * H[0]);
H /= proh;
H /= (H[2] + pconf.alpha);
ret = H;
ret[2] = 0; ret[3] = 1;
break;
}
case mdPoorMan: {
find_zlev(H);
H = space_to_perspective(H);
models::apply_orientation_yz(H[1], H[2]);
models::apply_orientation(H[0], H[1]);
ld u = H[0], v = H[1];
if(abs(u) > 1e-3 && abs(v) > 1e-3) {
ld r2 = u*u+v*v;
ld scale = sqrt((-r2+sqrt(r2*(r2+4*u*u*v*v*(r2-2))))/(2*(r2-2))) / u / v;
if(u*v<0) scale = -scale;
H = scale * H;
}
ret = H;
ret[2] = 0;
ret[3] = 1;
models::apply_orientation(ret[1], ret[0]);
models::apply_orientation_yz(ret[2], ret[1]);
break;
}
case mdGUARD: case mdManual: break;
}
@ -1462,6 +1532,10 @@ void hrmap::draw_at(cell *at, const shiftmatrix& where) {
}
void hrmap_standard::draw_at(cell *at, const shiftmatrix& where) {
if(S3 > 4) {
hrmap::draw_at(at, where);
return;
}
drawn_cells.clear();
drawn_cells.emplace_back(at->master, hsOrigin, where * master_relative(at, true));
for(int i=0; i<isize(drawn_cells); i++) {
@ -1926,6 +2000,7 @@ EX void draw_model_elements() {
}
case mdHyperboloid: {
if(!pconf.show_hyperboloid_flat) return;
if(hyperbolic) {
#if CAP_QUEUE
curvepoint(point3(0,0,1));

View File

@ -313,6 +313,8 @@ EX void dropGreenStone(cell *c) {
return;
}
if(items[itGreenStone] && c->item == itNone) {
changes.ccell(c);
changes.value_keep(items[itGreenStone]);
items[itGreenStone]--;
if(false) {
c->item = itNone;
@ -484,6 +486,7 @@ EX void gainItem(eItem it) {
#define IF(x) if(g < (x) && g2 >= x && !peace::on)
if(in_full_game()) {
IF(R60/4)
addMessage(XLAT("Collect treasure to access more different lands..."));
IF(R30)
@ -501,6 +504,7 @@ EX void gainItem(eItem it) {
addMessage(XLAT("And the Orbs of Yendor await!"));
}
}
}
EX string itemcounter(int qty) {
string s = ""; s += " ("; s += its(qty); s += ")"; return s;

View File

@ -2431,7 +2431,9 @@ EX void giantLandSwitch(cell *c, int d, cell *from) {
break;
case laBrownian:
#if CAP_COMPLEX2
brownian::build(c, d);
#endif
break;
case laMirrored:
@ -2807,6 +2809,7 @@ EX void setdist(cell *c, int d, cell *from) {
if(d >= BARLEV) {
#if CAP_BT
if(bt::in() && WDIM == 3 && !c->land && !sn::in()) {
ld z = vid.binary_width;
cell *cseek = c;
@ -2816,6 +2819,7 @@ EX void setdist(cell *c, int d, cell *from) {
while(z < 3.999 && step < 10) cseek = cseek->cmove(bt::updir()), z *= scale;
if(cseek->master->emeraldval) setland(c, eLand(cseek->master->emeraldval));
}
#endif
if(!c->land && from && (WDIM == 3 || !among(from->land, laBarrier, laElementalWall, laHauntedWall, laOceanWall)) && !quotient && !(chaosmode > 1)) {
if(!hasbardir(c)) setland(c, from->land);

View File

@ -62,8 +62,11 @@ struct fullnoun {
};
#if !CAP_TRANS
#define NUMEXTRA 11
const char* natchars[NUMEXTRA] = {"°","é","á", "²", "½", "Θ", "δ", "π", "ϕ", "", ""};
#if HDR
#define NUMEXTRA 12
extern const char* natchars[NUMEXTRA];
#endif
const char* natchars[NUMEXTRA] = {"°","é","á", "²", "½", "Θ", "δ", "π", "ϕ", "", "", ""};
#endif
#if CAP_TRANS

View File

@ -459,6 +459,7 @@ constexpr int FALL = 98;
constexpr int NO_SPACE = 97;
constexpr int TELEPORT = 96;
constexpr int JUMP = 95;
constexpr int STAY = 94;
namespace whirlwind { cell *jumpDestination(cell*); }

View File

@ -1095,8 +1095,8 @@ EX namespace mapeditor {
displayButton(8, vid.yres-8-fs*2, XLAT("ESC = return to the game"), SDLK_ESCAPE, 0);
}
EX unordered_set<cell*> affected;
EX unordered_set<int> affected_id;
EX set<cell*> affected;
EX set<int> affected_id;
EX void showMapEditor() {
cmode = sm::MAP;
@ -1276,6 +1276,7 @@ EX namespace mapeditor {
void list_spill(cellwalker tgt, cellwalker src, manual_celllister& cl) {
spill_list.clear();
spill_list.emplace_back(tgt, src);
if(painttype == 7) return;
int crad = 0, nextstepat = 0;
for(int i=0; i<isize(spill_list); i++) {
if(i == nextstepat) {

View File

@ -200,12 +200,15 @@ EX namespace models {
if(GDIM == 2 && pm == mdEquivolume) return false;
if(GDIM == 3 && among(pm, mdBall, mdHyperboloid, mdFormula, mdPolygonal, mdRotatedHyperboles, mdSpiral, mdHemisphere)) return false;
if(pm == mdCentralInversion && !euclid) return false;
if(pm == mdPoorMan) return hyperbolic;
if(pm == mdRetroHammer) return hyperbolic;
return true;
}
EX bool has_orientation(eModel m) {
if(m == mdHorocyclic)
return hyperbolic;
if((m == mdPerspective || m == mdGeodesic) && panini_alpha) return true;
return
among(m, mdHalfplane, mdPolynomial, mdPolygonal, mdTwoPoint, mdJoukowsky, mdJoukowskyInverted, mdSpiral, mdSimulatedPerspective, mdTwoHybrid, mdHorocyclic, mdAxial, mdAntiAxial, mdQuadrant,
mdWerner, mdAitoff, mdHammer, mdLoximuthal, mdWinkelTripel) || mdBandAny();
@ -442,7 +445,9 @@ EX namespace models {
dialog::addBreak(50);
}
if(among(vpmodel, mdDisk, mdBall, mdHyperboloid, mdRotatedHyperboles)) {
if(among(vpmodel, mdDisk, mdBall, mdHyperboloid, mdRotatedHyperboles, mdPanini)) {
dynamicval<eModel> v(vpconf.model, vpconf.model);
if(vpmodel == mdHyperboloid) vpconf.model = mdDisk;
dialog::addSelItem(XLAT("projection distance"), fts(vpconf.alpha) + " (" + current_proj_name() + ")", 'p');
dialog::add_action(projectionDialog);
}
@ -614,6 +619,10 @@ EX namespace models {
});
}
if(vpmodel == mdHyperboloid) {
dialog::addBoolItem_action(XLAT("show flat"), pconf.show_hyperboloid_flat, 'b');
}
if(vpmodel == mdCollignon) {
dialog::addSelItem(XLAT("parameter"), fts(vpconf.collignon_parameter) + (vpconf.collignon_reflected ? " (r)" : ""), 'b');
dialog::add_action([](){
@ -636,13 +645,17 @@ EX namespace models {
});
}
if(vpmodel == mdLoximuthal) {
if(among(vpmodel, mdLoximuthal, mdRetroHammer, mdRetroCraig)) {
dialog::addSelItem(XLAT("parameter"), fts(vpconf.loximuthal_parameter), 'b');
dialog::add_action([](){
dialog::add_action([vpmodel](){
dialog::editNumber(vpconf.loximuthal_parameter, -M_PI/2, M_PI/2, .1, 0, XLAT("parameter"),
(vpmodel == mdLoximuthal ?
"This model is similar to azimuthal equidistant, but based on loxodromes (lines of constant geographic direction) rather than geodesics. "
"The loximuthal projection maps (the shortest) loxodromes to straight lines of the same length, going through the starting point. "
"This setting changes the latitude of the starting point."
"This setting changes the latitude of the starting point." :
"In retroazimuthal projections, a point is drawn at such a point that the azimuth *from* that point to the chosen central point is correct. "
"For example, if you should move east, the point is drawn to the right. This parameter is the latitude of the central point.")
+ string(hyperbolic ? "\n\n(In hyperbolic geometry directions are assigned according to the Lobachevsky coordinates.)" : "")
);
});
}
@ -827,7 +840,7 @@ EX namespace models {
PHASEFROM(2);
if(pmodel == mdCollignon) shift_arg_formula(vpconf.collignon_parameter);
else if(pmodel == mdMiller) shift_arg_formula(vpconf.miller_parameter);
else if(pmodel == mdLoximuthal) shift_arg_formula(vpconf.loximuthal_parameter);
else if(among(pmodel, mdLoximuthal, mdRetroCraig, mdRetroHammer)) shift_arg_formula(vpconf.loximuthal_parameter);
else if(among(pmodel, mdAitoff, mdHammer, mdWinkelTripel)) shift_arg_formula(vpconf.aitoff_parameter);
if(pmodel == mdWinkelTripel) shift_arg_formula(vpconf.winkel_parameter);
}
@ -856,6 +869,10 @@ EX namespace models {
PHASEFROM(2);
shift_arg_formula(vpconf.skiprope);
}
else if(argis("-palpha")) {
PHASEFROM(2);
shift_arg_formula(panini_alpha, reset_all_shaders);
}
else if(argis("-zoom")) {
PHASEFROM(2); shift_arg_formula(vpconf.scale);
}

View File

@ -318,10 +318,12 @@ EX eItem wanderingTreasure(cell *c) {
/** generate the wandering monsters */
EX void wandering() {
#if CAP_COMPLEX2
if(mine::in_minesweeper()) {
mine::count_status();
return;
}
#endif
if(!canmove) return;
if(!gen_wandering) return;
if(racing::on) return;

View File

@ -108,6 +108,8 @@ EX void moveMonster(const movei& mi) {
auto& cf = mi.s;
auto& ct = mi.t;
eMonster m = cf->monst;
changes.ccell(cf);
changes.ccell(ct);
bool fri = isFriendly(cf);
if(isDragon(m)) {
printf("called for Dragon\n");
@ -117,6 +119,7 @@ EX void moveMonster(const movei& mi) {
// the following line is necessary because otherwise plates disappear only inside the sight range
if(cellUnstable(cf) && !ignoresPlates(m)) {
fallingFloorAnimation(cf);
changes.ccell(cf);
cf->wall = waChasm;
}
moveEffect(mi, m);
@ -124,6 +127,7 @@ EX void moveMonster(const movei& mi) {
(m == moShark || m == moCShark || m == moGreaterShark))
achievement_gain_once("MOATSHARK");
if(m == moTentacleGhost) {
changes.ccell(cf);
cf->monst = moTentacletail;
m = moGhost;
}
@ -156,10 +160,11 @@ EX void moveMonster(const movei& mi) {
}
}
if(fri || isBug(m) || items[itOrbDiscord]) stabbingAttack(cf, ct, m);
if(fri || isBug(m) || items[itOrbDiscord]) stabbingAttack(mi, m);
if(mi.d == JUMP && m == moVaulter) {
cell *cm = common_neighbor(cf, ct);
changes.ccell(cm);
if(cm->wall == waShrub) cm->wall = waNone;
if(cm->wall == waSmallTree) cm->wall = waNone;
if(cm->wall == waBigTree) cm->wall = waSmallTree;
@ -1518,7 +1523,10 @@ EX int stayvalue(eMonster m, cell *c) {
}
/** for an ally m at c, evaluate moving to c2 */
EX int movevalue(eMonster m, cell *c, cell *c2, flagtype flags) {
EX int movevalue(eMonster m, cell *c, int dir, flagtype flags) {
auto mi = movei(c, dir);
auto& c2 = mi.t;
int val = 0;
if(isPlayerOn(c2)) val = -3000;
@ -1537,7 +1545,6 @@ EX int movevalue(eMonster m, cell *c, cell *c2, flagtype flags) {
isInactiveEnemy(c2,m) ? 1000 :
-500;
else if(monstersnear(c2, m, NULL, c)) val = 50; // linked with mouse suicide!
else if(passable_for(m, c2, c, 0)) {
#if CAP_COMPLEX2
if(mine::marked_mine(c2) && !ignoresPlates(m))
@ -1545,12 +1552,22 @@ EX int movevalue(eMonster m, cell *c, cell *c2, flagtype flags) {
else
#endif
val = 4000;
int tk = tkills();
changes.init(true);
moveMonster(mi);
int tk2 = tkills();
bool b = monstersnear(mi.t, m);
changes.rollback();
if(b) val = 50;
else if(tk2 > tk) val += 1000 + 200 * (tk2 - tk);
}
else if(passable_for(m, c2, c, P_DEADLY)) val = -1100;
else val = -1750;
if(c->monst == moGolem )
val -= c2->cpdist;
if(c->monst == moGolem ) {
val -= c2->pathdist;
}
else if(c->monst == moFriendlyGhost )
val += c2->cpdist - 40;
else if(c->monst == moMouse) {
@ -1599,9 +1616,9 @@ EX int movevalue(eMonster m, cell *c, cell *c2, flagtype flags) {
val -= 5;
}
if(c->monst == moTameBomberbird) {
int d = c2->cpdist;
if(d == 1 && c->cpdist > 1) d = 5;
if(d == 2 && c->cpdist > 2) d = 4;
int d = c2->pathdist;
if(d == 1 && c->pathdist > 1) d = 5;
if(d == 2 && c->pathdist > 2) d = 4;
val -= d;
}
if(c->monst == moKnight && (eubinary || c2->master->alt)) {
@ -1616,11 +1633,11 @@ EX int movevalue(eMonster m, cell *c, cell *c2, flagtype flags) {
EX void movegolems(flagtype flags) {
if(items[itOrbEmpathy] && items[itOrbSlaying])
flags |= AF_CRUSH;
pathdata pd(moMouse);
int qg = 0;
for(int i=0; i<isize(golems); i++) {
cell *c = golems[i];
eMonster m = c->monst;
pathdata pd(m, false);
if(c->stuntime) continue;
if(m == moGolem || m == moKnight || m == moTameBomberbird || m == moPrincess ||
m == moPrincessArmed || m == moMouse || m == moFriendlyGhost) {
@ -1636,8 +1653,7 @@ EX void movegolems(flagtype flags) {
DEBB(DF_TURN, ("moveval"));
for(int k=0; k<c->type; k++) if(c->move(k)) {
cell *c2 = c->move(k);
int val = movevalue(m, c, c2, flags);
int val = movevalue(m, c, k, flags);
if(val > bestv) bestv = val, bdirs.clear();
if(val == bestv) bdirs.push_back(k);
@ -1646,7 +1662,7 @@ EX void movegolems(flagtype flags) {
if(m == moTameBomberbird) {
cell *c2 = whirlwind::jumpDestination(c);
if(c2 && !c2->monst) {
int val = movevalue(m, c, c2, flags);
int val = movevalue(m, c, STRONGWIND, flags);
// printf("val = %d bestv = %d\n",
if(val > bestv) bestv = val, bdirs.clear();
if(val == bestv) bdirs.push_back(STRONGWIND);

View File

@ -221,22 +221,15 @@ struct key_configurer {
int sc;
vector<string>& shmupcmdtable;
string caption;
int setwhat;
key_configurer(int sc, vector<string>& sct) : sc(sc), shmupcmdtable(sct), setwhat(0) {}
key_configurer(int sc, vector<string>& sct, const string& caption) : sc(sc), shmupcmdtable(sct), caption(caption), setwhat(0) {
}
void operator() () {
dialog::init(
XLAT(sc == 1 ? "configure player 1" :
sc == 2 ? "configure player 2" :
sc == 3 ? "configure panning" :
sc == 4 ? "configure player 3" :
sc == 5 ? "configure player 4" :
sc == 6 ? "configure player 5" :
sc == 7 ? "configure player 6" :
sc == 8 ? "configure player 7" : ""
));
dialog::init(caption);
getcstat = ' ';
@ -302,7 +295,21 @@ struct key_configurer {
}
};
EX reaction_t get_key_configurer(int sc, vector<string>& sct) { return key_configurer(sc, sct); }
EX reaction_t get_key_configurer(int sc, vector<string>& sct, string caption) {
return key_configurer(sc, sct, caption);
}
EX reaction_t get_key_configurer(int sc, vector<string>& sct) {
return key_configurer(sc, sct, XLAT(sc == 1 ? "configure player 1" :
sc == 2 ? "configure player 2" :
sc == 3 ? "configure panning" :
sc == 4 ? "configure player 3" :
sc == 5 ? "configure player 4" :
sc == 6 ? "configure player 5" :
sc == 7 ? "configure player 6" :
sc == 8 ? "configure player 7" : ""
));
}
#if CAP_SDLJOY
struct joy_configurer {
@ -456,14 +463,14 @@ struct shmup_configurer {
if(0) ;
#if CAP_SDL
else if(uni == '1') pushScreen(key_configurer(1, cmdlist));
else if(uni == '2') pushScreen(key_configurer(2, cmdlist));
else if(uni == 'p') pushScreen(key_configurer(3, GDIM == 3 ? pancmds3 : pancmds));
else if(uni == '3') pushScreen(key_configurer(4, cmdlist));
else if(uni == '4') pushScreen(key_configurer(5, cmdlist));
else if(uni == '5') pushScreen(key_configurer(6, cmdlist));
else if(uni == '6') pushScreen(key_configurer(7, cmdlist));
else if(uni == '7') pushScreen(key_configurer(8, cmdlist));
else if(uni == '1') pushScreen(get_key_configurer(1, cmdlist));
else if(uni == '2') pushScreen(get_key_configurer(2, cmdlist));
else if(uni == 'p') pushScreen(get_key_configurer(3, GDIM == 3 ? pancmds3 : pancmds));
else if(uni == '3') pushScreen(get_key_configurer(4, cmdlist));
else if(uni == '4') pushScreen(get_key_configurer(5, cmdlist));
else if(uni == '5') pushScreen(get_key_configurer(6, cmdlist));
else if(uni == '6') pushScreen(get_key_configurer(7, cmdlist));
else if(uni == '7') pushScreen(get_key_configurer(8, cmdlist));
#if CAP_SDLJOY
else if(uni == 'j') pushScreen(joy_configurer(players));
#endif
@ -638,6 +645,7 @@ EX void initConfig() {
multi::scs[5].uicolor = 0x00C0C0FF;
multi::scs[6].uicolor = 0xC0C0C0FF;
#if CAP_CONFIG
addsaver(multi::players, "mode-number of players");
addsaver(alwaysuse, "use configured keys");
// unfortunately we cannot use key names here because SDL is not yet initialized
@ -656,6 +664,7 @@ EX void initConfig() {
}
}
for(int i=0; i<7; i++) addsaver(multi::scs[i], "player"+its(i));
#endif
}
EX void handleInput(int delta) {

View File

@ -295,11 +295,17 @@ EX namespace dual {
}
else {
variation = eVariation::pure;
#if CAP_ARCM
geometry = s == 0 ? gEuclidSquare : gArchimedean;
#else
geometry = gEuclidSquare;
#endif
}
firstland = specialland = laCrossroads4;
#if CAP_ARCM
if(geometry == gArchimedean)
arcm::current.parse("4,4,4,4,4");
#endif
check_cgi();
cgi.require_basics();
dgd[s].storegame();

View File

@ -60,6 +60,19 @@ void set_mingw64() {
setvbuf(stdout, NULL, _IONBF, 0); // MinGW is quirky with output buffering
}
void set_web() {
preprocessor = "/usr/lib/emscripten/em++ -E";
compiler = "/usr/lib/emscripten/em++ -c";
default_standard = standard = " -std=c++17";
opts = "-DISWEB=1";
linker =
"/usr/lib/emscripten/em++ -s USE_ZLIB=1 -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=0 -s TOTAL_MEMORY=512MB "
"-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"FS\",\"ccall\"]' "
"-s EXPORTED_FUNCTIONS=\"['_main', '_use_file']\" "
"-s DISABLE_EXCEPTION_CATCHING=0 -o mhyper.html";
libs = "";
}
vector<string> modules;
time_t get_file_time(const string s) {
@ -123,6 +136,12 @@ int main(int argc, char **argv) {
obj_dir += "/linux";
setdir += "../";
}
else if(s == "-web") {
set_web();
modules.push_back("hyperweb");
obj_dir += "/web";
setdir += "../";
}
else if(s.substr(0, 2) == "-f") {
opts += " " + s;
obj_dir += "/";

View File

@ -127,11 +127,11 @@ unsigned char fonttable[] = {
unsigned char *ftv = fonttable;
void resetTabFont() {
EX void resetTabFont() {
ftv = fonttable;
}
void loadCompressedChar(int &otwidth, int &otheight, unsigned char *tpix) {
EX void loadCompressedChar(int &otwidth, int &otheight, unsigned char *tpix) {
if(*ftv == 255) {
fprintf(stderr, "There is something wrong with the font table\n");
exit(1);

View File

@ -236,8 +236,8 @@ EX namespace sn {
struct hrmap_solnih : hrmap {
hrmap *binary_map;
hrmap *ternary_map; /* nih only */
unordered_map<pair<heptagon*, heptagon*>, heptagon*> at;
unordered_map<heptagon*, pair<heptagon*, heptagon*>> coords;
map<pair<heptagon*, heptagon*>, heptagon*> at;
map<heptagon*, pair<heptagon*, heptagon*>> coords;
heptagon *origin;
@ -872,8 +872,8 @@ EX namespace nilv {
}
struct hrmap_nil : hrmap {
unordered_map<mvec, heptagon*> at;
unordered_map<heptagon*, mvec> coords;
map<mvec, heptagon*> at;
map<heptagon*, mvec> coords;
heptagon *getOrigin() override { return get_at(mvec_zero); }
@ -1231,6 +1231,8 @@ EX namespace hybrid {
dynamicval<hrmap*> gpm(pmap, this);
dynamicval<eGeometry> gag(actual_geometry, geometry);
dynamicval<eGeometry> g(geometry, underlying);
dynamicval<int> gss(underlying_cgip->single_step, cgi.single_step);
dynamicval<int> gsp(underlying_cgip->psl_steps, cgi.psl_steps);
dynamicval<geometry_information*> gc(cgip, underlying_cgip);
dynamicval<hrmap*> gu(currentmap, underlying_map);
return t();
@ -1308,6 +1310,8 @@ EX namespace hybrid {
if(!hybri) return f();
dynamicval<eGeometry> g(geometry, underlying);
dynamicval<eGeometry> gag(actual_geometry, geometry);
dynamicval<int> gss(underlying_cgip->single_step, cgi.single_step);
dynamicval<int> gsp(underlying_cgip->psl_steps, cgi.psl_steps);
dynamicval<geometry_information*> gc(cgip, underlying_cgip);
dynamicval<hrmap*> gpm(pmap, currentmap);
dynamicval<hrmap*> gm(currentmap, get_umap());
@ -1345,13 +1349,17 @@ EX namespace hybrid {
}
EX int wall_offset(cell *c) {
if(GOLDBERG) {
if(GOLDBERG || INVERSE) {
/* a bit slow... */
cell *c1 = WDIM == 2 ? c : get_where(c).first;
gp::draw_li = WDIM == 2 ? gp::get_local_info(c1) : PIU(gp::get_local_info(c1));
}
auto ugeometry = hybri ? hybrid::underlying : geometry;
#if CAP_ARCM
int id = ugeometry == gArchimedean ? arcm::id_of(c->master) + 20 * arcm::parent_index_of(c->master) : shvid(c);
#else
int id = shvid(c);
#endif
if(isize(cgi.walloffsets) <= id) cgi.walloffsets.resize(id+1, {-1, nullptr});
auto &wop = cgi.walloffsets[id];
int &wo = wop.first;
@ -2057,14 +2065,19 @@ EX namespace rots {
return spin(beta) * uxpush(distance/2) * spin(-beta+alpha);
}
std::unordered_map<int, transmatrix> saved_matrices_ray;
std::map<int, transmatrix> saved_matrices_ray;
EX transmatrix ray_iadj(cell *c1, int i) {
if(i == c1->type-1) return uzpush(-cgi.plevel) * spin(-2*cgi.plevel);
if(i == c1->type-2) return uzpush(+cgi.plevel) * spin(+2*cgi.plevel);
cell *c2 = c1->cmove(i);
#if CAP_ARCM
int id1 = hybrid::underlying == gArchimedean ? arcm::id_of(c1->master) + 20 * arcm::parent_index_of(c1->master) : shvid(c1);
int id2 = hybrid::underlying == gArchimedean ? arcm::id_of(c2->master) + 20 * arcm::parent_index_of(c2->master) : shvid(c2);
#else
int id1 = shvid(c1);
int id2 = shvid(c2);
#endif
int j = c1->c.spin(i);
int id = id1 + (id2 << 10) + (i << 20) + (j << 26);
auto &M = saved_matrices_ray[id];
@ -2084,14 +2097,19 @@ EX namespace rots {
struct hrmap_rotation_space : hybrid::hrmap_hybrid {
std::unordered_map<int, transmatrix> saved_matrices;
std::map<int, transmatrix> saved_matrices;
transmatrix adj(cell *c1, int i) override {
if(i == c1->type-2) return uzpush(-cgi.plevel) * spin(-2*cgi.plevel);
if(i == c1->type-1) return uzpush(+cgi.plevel) * spin(+2*cgi.plevel);
cell *c2 = c1->cmove(i);
#if CAP_ARCM
int id1 = hybrid::underlying == gArchimedean ? arcm::id_of(c1->master) + 20 * arcm::parent_index_of(c1->master) : shvid(c1);
int id2 = hybrid::underlying == gArchimedean ? arcm::id_of(c2->master) + 20 * arcm::parent_index_of(c2->master) : shvid(c2);
#else
int id1 = shvid(c1);
int id2 = shvid(c2);
#endif
int j = c1->c.spin(i);
int id = id1 + (id2 << 10) + (i << 20) + (j << 26);
auto &M = saved_matrices[id];

View File

@ -309,7 +309,7 @@ EX eOrbLandRelation getOLR(eItem it, eLand l) {
return olrUseless;
if(l == laPrincessQuest)
if(among(it, itOrbAether, itOrbFlash, itOrbTeleport, itOrbSummon, itOrbFreedom, itOrbFriend, itOrbPhasing))
if(among(it, itOrbAether, itOrbFlash, itOrbTeleport, itOrbSummon, itOrbFreedom, itOrbFriend, itOrbPhasing, itOrbChaos))
return olrForbidden;
if(l == laTemple)

View File

@ -88,7 +88,9 @@ EX bool reduceOrbPower(eItem it, int cap) {
return true;
}
if(items[it] > cap && timerghost) items[it] = cap;
#if CAP_COMPLEX2
mine::auto_teleport_charges();
#endif
return false;
}
@ -566,7 +568,7 @@ EX void teleportTo(cell *dest) {
killFriendlyIvy();
drainOrb(itOrbTeleport);
movecost(cwt.at, dest, 3);
playerMoveEffects(cwt.at, dest);
playerMoveEffects(movei(cwt.at, dest, TELEPORT));
afterplayermoved();
bfs();
}
@ -578,7 +580,7 @@ EX void teleportTo(cell *dest) {
killFriendlyIvy();
cell *from = cwt.at;
movecost(from, dest, 1);
playerMoveEffects(cwt.at, dest);
playerMoveEffects(movei(cwt.at, dest, TELEPORT));
current_display->which_copy = unshift(ggmatrix(dest));
cwt.at = dest; cwt.spin = hrand(dest->type); flipplayer = !!(hrand(2));
drainOrb(itOrbTeleport);
@ -596,7 +598,9 @@ EX void teleportTo(cell *dest) {
checkmoveO();
movecost(from, dest, 2);
#if CAP_COMPLEX2
mine::auto_teleport_charges();
#endif
}
EX bool jumpTo(orbAction a, cell *dest, eItem byWhat, int bonuskill IS(0), eMonster dashmon IS(moNone)) {
@ -634,8 +638,9 @@ EX bool jumpTo(orbAction a, cell *dest, eItem byWhat, int bonuskill IS(0), eMons
}
sword::reset();
stabbingAttack(c1, dest, moPlayer, bonuskill);
playerMoveEffects(c1, dest);
auto mi = movei(c1, dest, JUMP);
stabbingAttack(mi, moPlayer, bonuskill);
playerMoveEffects(mi);
if(itemclass(byWhat) == IC_ORB)
apply_impact(dest);
@ -654,7 +659,7 @@ EX bool jumpTo(orbAction a, cell *dest, eItem byWhat, int bonuskill IS(0), eMons
mirror::destroyAll();
if(monstersnearO(a, dest, moPlayer, NULL, c1)) {
if(monstersnearO(a, dest)) {
changes.rollback();
return false;
}
@ -935,7 +940,7 @@ bool gun_attack(orbAction a, cell *dest) {
attackMonster(dest, AF_GUN, moNone);
apply_impact(dest);
if(monstersnearO(a, cwt.at, moPlayer, NULL, cwt.at)) {
if(monstersnearO(a, cwt.at)) {
changes.rollback();
return false;
}
@ -1064,13 +1069,13 @@ void useOrbOfDragon(cell *c) {
checkmoveO();
}
EX bool monstersnearO(orbAction a, cell *c, eMonster who, cell *pushto, cell *comefrom) {
EX bool monstersnearO(orbAction a, cell *c) {
// printf("[a = %d] ", a);
if(shmup::on) return false;
if(a == roCheck && multi::players > 1)
return true;
else if(a == roMultiCheck) return false;
else return monstersnear(c, who, pushto, comefrom);
else return monstersnear(c, moPlayer);
}
EX bool isCheck(orbAction a) { return a == roCheck || a == roMultiCheck; }
@ -1237,12 +1242,12 @@ EX eItem targetRangedOrb(cell *c, orbAction a) {
if(c->monst) {
if(!canAttack(cf, moFriendlyIvy, c, c->monst, 0)) continue;
if(monstersnear(cwt.at, moPlayer, NULL, cwt.at)) continue;
if(monstersnear(cwt.at, moPlayer)) continue;
}
else {
if(!passable(c, cf, P_ISPLAYER | P_MONSTER)) continue;
if(strictlyAgainstGravity(c, cf, false, MF_IVY)) continue;
if(monstersnear(cwt.at, moPlayer, c, cwt.at)) continue;
if(monstersnear(cwt.at, moPlayer)) continue;
}
dirs.push_back(d);
}

View File

@ -422,7 +422,7 @@ EX int fieldval_uniq(cell *c) {
}
else if(euc::in(2)) {
auto p = euc2_coordinates(c);
if(bounded) return p.first + (p.second << 16);
if(bounded) return p.first + p.second * (1 << 16);
return gmod(p.first - 22 * p.second, 3*127);
}
else if(euc::in(3)) {
@ -1682,11 +1682,13 @@ EX namespace patterns {
ep.extra_params["ey"] = y;
if(S7 == 6) ep.extra_params["ez"] = -x-y;
}
#if CAP_CRYSTAL
if(cryst) {
crystal::ldcoord co = crystal::get_ldcoord(c);
for(int i=0; i<crystal::MAXDIM; i++)
ep.extra_params["x"+its(i)] = co[i];
}
#endif
if(asonov::in()) {
auto co = asonov::get_coord(c->master);
ep.extra_params["ax"] = szgmod(co[0], asonov::period_xy);

View File

@ -358,7 +358,7 @@ bool pcmove::swing() {
mirror::act(origd, mirror::SPINMULTI | mirror::ATTACK);
if(monstersnear(cwt.at, moPlayer, nullptr, cwt.at)) {
if(monstersnear_add_pmi(movei(cwt.at, STAY))) {
if(vmsg())
wouldkill("You would be killed by %the1!");
return false;
@ -510,6 +510,13 @@ struct changes_t {
void at_rollback(reaction_t act) {
if(on) rollbacks.emplace_back(act);
}
void push_push(cell *tgt) {
pushes.push_back(tgt);
auto v = [] { pushes.pop_back(); };
rollbacks.push_back(v);
commits.push_back(v);
}
};
#endif
@ -601,7 +608,7 @@ bool pcmove::actual_move() {
return false;
}
if(items[itOrbDomination] > ORBBASE && isMountable(c2->monst) && !monstersnear2() && fmsMove) {
if(items[itOrbDomination] > ORBBASE && isMountable(c2->monst) && fmsMove) {
if(checkonly) { nextmovetype = lmMove; return true; }
if(!isMountable(cwt.at->monst)) dragon::target = NULL;
movecost(cwt.at, c2, 3);
@ -629,6 +636,7 @@ bool pcmove::actual_move() {
addMessage(XLAT("You push %the1.", c2->wall));
lastmovetype = lmPush; lastmove = cwt.at;
pushThumper(mip);
changes.push_push(mip.t);
return perform_actual_move();
}
@ -646,8 +654,6 @@ bool pcmove::actual_move() {
if(!c2->monst && cwt.at->wall == waBoat && cwt.at->item != itOrbYendor && boatGoesThrough(c2) && markOrb(itOrbWater) && !nonAdjacentPlayer(c2, cwt.at) && fmsMove) {
if(havePushConflict(cwt.at, checkonly)) return false;
if(c2->item && !cwt.at->item) moveItem(c2, cwt.at, false), boatmove = true;
placeWater(c2, cwt.at);
moveBoat(mi);
@ -663,7 +669,6 @@ bool pcmove::actual_move() {
bool pcmove::boat_move() {
cell *& c2 = mi.t;
if(havePushConflict(cwt.at, checkonly)) return false;
if(againstWind(c2, cwt.at)) {
if(vmsg()) addMessage(XLAT(airdist(c2) < 3 ? "The Air Elemental blows you away!" : "You cannot go against the wind!"));
@ -744,8 +749,6 @@ bool pcmove::after_escape() {
return false;
}
if(havePushConflict(cwt.at, checkonly)) return false;
changes.ccell(c2);
changes.ccell(cwt.at);
@ -756,6 +759,7 @@ bool pcmove::after_escape() {
nextmovetype = lmMove;
addMessage(XLAT("You push %the1 behind you!", waBigStatue));
animateMovement(mi.rev(), LAYER_BOAT);
changes.push_push(cwt.at);
return perform_actual_move();
}
@ -849,7 +853,7 @@ bool pcmove::move_if_okay() {
if(switchplace_prevent(cwt.at, c2, checkonly))
return false;
if(!checkonly && warningprotection_hit(do_we_stab_a_friend(cwt.at, c2, moPlayer)))
if(!checkonly && warningprotection_hit(do_we_stab_a_friend(mi, moPlayer)))
return false;
nextmovetype = lmMove;
@ -899,7 +903,7 @@ bool pcmove::attack() {
if(!ca) {
if(forcedmovetype == fmAttack) {
if(monstersnear(cwt.at,moPlayer,NULL,cwt.at)) {
if(monstersnear_add_pmi(movei(cwt.at, STAY))) {
if(vmsg()) wouldkill("%The1 would get you!");
return false;
}
@ -922,10 +926,9 @@ bool pcmove::attack() {
else
mip.t = c2;
if(mip.t) changes.ccell(mip.t);
changes.push_push(mip.t);
}
if(havePushConflict(mip.t, checkonly)) return false;
if(!(isWatery(cwt.at) && c2->monst == moWaterElemental) && checkNeedMove(checkonly, true))
return false;
@ -982,7 +985,7 @@ bool pcmove::attack() {
lastmovetype = lmAttack; lastmove = c2;
swordAttackStatic();
if(monstersnear(cwt.at, moPlayer, nullptr, cwt.at)) {
if(monstersnear_add_pmi(movei(cwt.at, STAY))) {
if(vmsg()) wouldkill("You would be killed by %the1!");
return false;
}
@ -1090,18 +1093,18 @@ bool pcmove::perform_move_or_jump() {
lastmovetype = lmMove; lastmove = cwt.at;
apply_chaos();
stabbingAttack(cwt.at, mi.t, moPlayer);
cell *c1 = cwt.at;
stabbingAttack(mi, moPlayer);
changes.value_keep(cwt);
cwt += wstep;
mirror::act(origd, mirror::SPINMULTI | mirror::ATTACK | mirror::GO);
playerMoveEffects(c1, mi.t);
auto pmi = player_move_info(mi);
playerMoveEffects(mi);
if(mi.t->monst == moFriendlyIvy) changes.ccell(mi.t), mi.t->monst = moNone;
if(monstersnear(cwt.at, moPlayer, nullptr, c1)) {
if(monstersnear_add_pmi(pmi)) {
if(vmsg()) wouldkill("%The1 would kill you there!");
return false;
}
@ -1135,20 +1138,22 @@ bool pcmove::stay() {
return false;
swordAttackStatic();
nextmovetype = lmSkip;
if(monstersnear(cwt.at, moPlayer, nullptr, cwt.at)) {
mi = movei(cwt.at, STAY);
if(last_gravity_state && !gravity_state)
playerMoveEffects(mi);
if(d == -2)
dropGreenStone(cwt.at);
if(monstersnear_add_pmi(mi)) {
if(vmsg()) wouldkill("%The1 would get you!");
return false;
}
if(checkonly) return true;
if(changes.on) changes.commit();
if(d == -2)
dropGreenStone(cwt.at);
if(cellUnstable(cwt.at) && !markOrb(itOrbAether))
doesFallSound(cwt.at);
if(last_gravity_state && !gravity_state)
playerMoveEffects(cwt.at, cwt.at);
return after_move();
}
@ -1246,12 +1251,14 @@ EX bool playerInPower() {
return false;
}
EX void playerMoveEffects(cell *c1, cell *c2) {
EX void playerMoveEffects(movei mi) {
cell *c1 = mi.s;
cell *c2 = mi.t;
if(peace::on) items[itOrbSword] = c2->land == laBurial ? 100 : 0;
changes.value_keep(sword::dir[multi::cpid]);
sword::dir[multi::cpid] = sword::shift(c1, c2, sword::dir[multi::cpid]);
sword::dir[multi::cpid] = sword::shift(mi, sword::dir[multi::cpid]);
destroyWeakBranch(c1, c2, moPlayer);
@ -1457,20 +1464,20 @@ EX void sideAttack(cell *mf, int dir, eMonster who, int bonuskill) {
}
}
EX eMonster do_we_stab_a_friend(cell *mf, cell *mt, eMonster who) {
EX eMonster do_we_stab_a_friend(movei mi, eMonster who) {
eMonster m = moNone;
do_swords(mf, mt, who, [&] (cell *c, int bb) {
if(!peace::on && canAttack(mt, who, c, c->monst, AF_SWORD) && c->monst && isFriendly(c)) m = c->monst;
do_swords(mi, who, [&] (cell *c, int bb) {
if(!peace::on && canAttack(mi.t, who, c, c->monst, AF_SWORD) && c->monst && isFriendly(c)) m = c->monst;
});
for(int t=0; t<mf->type; t++) {
cell *c = mf->move(t);
for(int t=0; t<mi.s->type; t++) {
cell *c = mi.s->move(t);
if(!c) continue;
bool stabthere = false;
if(logical_adjacent(mt, who, c)) stabthere = true;
if(logical_adjacent(mi.t, who, c)) stabthere = true;
if(stabthere && canAttack(mt,who,c,c->monst,AF_STAB) && isFriendly(c))
if(stabthere && canAttack(mi.t,who,c,c->monst,AF_STAB) && isFriendly(c))
return c->monst;
}
@ -1484,27 +1491,14 @@ EX void wouldkill(const char *msg) {
addMessage(XLAT("Cannot move into the current location of another player!"));
else if(who_kills_me == moAirball)
addMessage(XLAT("Players cannot get that far away!"));
else if(who_kills_me == moTongue)
addMessage(XLAT("Cannot push into another player!"));
else if(who_kills_me == moCrushball)
addMessage(XLAT("Cannot push into the same location!"));
else
addMessage(XLAT(msg, who_kills_me));
}
EX bool havePushConflict(cell *pushto, bool checkonly) {
if(pushto && multi::activePlayers() > 1) {
for(int i=0; i<multi::players; i++) if(i != multi::cpid && multi::playerActive(i))
if(multi::origpos[i] == pushto || multi::origtarget[i] == pushto) {
addMessage(XLAT("Cannot push into another player!"));
return true;
}
for(int i=0; i<isize(stalemate::moves); i++) {
if(pushto == stalemate::moves[i].pushto) {
addMessage(XLAT("Cannot push into the same location!"));
return true;
}
}
}
return false;
}
EX void movecost(cell* from, cell *to, int phase) {
if(from->land == laPower && to->land != laPower && (phase & 1)) {
int n=0;

View File

@ -718,24 +718,26 @@ void geometry_information::procedural_shapes() {
vector<ld> equal_weights(1000, 1);
#if !(CAP_BT && MAXMDIM >= 4)
#if MAXMDIM < 4
void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector<ld> weights) { }
void geometry_information::reserve_wall3d(int i) { }
void geometry_information::create_wall3d() { }
void geometry_information::compute_cornerbonus() { }
#endif
#if CAP_BT && MAXMDIM >= 4
#if MAXMDIM >= 4
// Make a wall
hyperpoint ray_kleinize(hyperpoint h, int id, ld pz) {
if(geometry == gNil && among(id, 2, 5)) h[2] = 0;
#if CAP_BT
if(hyperbolic && bt::in()) {
// ld co = vid.binary_width / log(2) / 4;
// hyperpoint res = point31(h[2]*log(2)/2, h[0]*co, h[1]*co);
return deparabolic10(bt::parabolic3(h[0], h[1]) * xpush0(log(2)/2*h[2]));
}
#endif
if(prod) {
return point3(h[0]/h[2], h[1]/h[2], pz);
}
@ -756,7 +758,9 @@ void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector
reverse(vertices.begin(), vertices.end()),
reverse(weights.begin(), weights.end());
#if CAP_BT
ld yy = log(2) / 2;
#endif
bshape(shWall3D[id], PPR::WALL);
last->flags |= POLY_TRIANGLES | POLY_PRINTABLE;
@ -815,8 +819,13 @@ void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector
hpcpush(h); return;
}
if(sn::in() || !bt::in()) { hpcpush(ultra_normalize(h)); return; }
#if CAP_BT
if(bt::in()) {
hyperpoint res = bt::parabolic3(h[0], h[1]) * xpush0(yy*h[2]);
hpcpush(res);
}
#endif
hpcpush(h);
});
}
@ -833,9 +842,14 @@ void geometry_information::make_wall(int id, vector<hyperpoint> vertices, vector
if(nil)
h = nilv::on_geodesic(vertices[a], vertices[(a+1)%n], y * 1. / STEP);
if(sn::in() || !bt::in()) { hpcpush(ultra_normalize(h)); continue; }
#if CAP_BT
if(bt::in()) {
hyperpoint res = bt::parabolic3(h[0], h[1]) * xpush0(yy*h[2]);
hpcpush(res);
}
#endif
hpcpush(h);
}
hpcpush(hpc[last->s]);
}
@ -875,6 +889,8 @@ void geometry_information::reserve_wall3d(int i) {
void geometry_information::create_wall3d() {
if(WDIM == 2) return;
reserve_wall3d(kite::in() ? 22 : hybri ? 0 : S7);
#if CAP_BT
if(GDIM == 3 && bt::in() && geometry == gBinary3) {
hyperpoint h00 = point3(-1,-1,-1);
hyperpoint h01 = point3(-1,0,-1);
@ -970,6 +986,7 @@ void geometry_information::create_wall3d() {
make_wall(12, {point3(3*h,r3,z), point3(0,2*r3,z), point3(-3*h,r3,z)});
make_wall(13, {point3(3*h,r3,z), point3(3*h,-r3,z), point3(0,-2*r3,z), point3(-3*h,-r3,z), point3(-3*h,r3,z)});
}
#endif
if(prod) {
walloffsets.clear();
@ -1060,6 +1077,7 @@ void geometry_information::create_wall3d() {
}
}
#if CAP_BT
if(kite::in()) {
auto kv = kite::make_walls();
for(auto& v: kv.first) for(auto& h: v) {
@ -1068,6 +1086,7 @@ void geometry_information::create_wall3d() {
}
for(int i=0; i<isize(kv.first); i++) make_wall(i, kv.first[i], kv.second[i]);
}
#endif
wallstart.push_back(isize(raywall));
compute_cornerbonus();

View File

@ -321,7 +321,11 @@ EX void showMission() {
);
keyhandler = handleKeyQuit;
#if CAP_COMPLEX2
bool sweeper = mine::in_minesweeper();
#else
const bool sweeper = false;
#endif
if(!peace::on && !racing::on && !sweeper)
dialog::addInfo(XLAT("Your score: %1", its(gold())));
@ -335,6 +339,7 @@ EX void showMission() {
dialog::addInfo(XLAT("Orbs of Yendor found: %1", its(items[itOrbYendor])), iinf[itOrbYendor].color);
dialog::addInfo(XLAT("CONGRATULATIONS!"), iinf[itOrbYendor].color);
}
#if CAP_COMPLEX2
else if(mine::in_minesweeper()) {
int to_uncover = kills[moBomberbird];
if(to_uncover) {
@ -346,6 +351,7 @@ EX void showMission() {
dialog::addInfo(XLAT("You won in %1", getgametime_s(mine::victory_time)));
}
}
#endif
else {
if(0)
;

View File

@ -32,9 +32,9 @@ EX namespace quotientspace {
int rv(int x) { return (rvadd+x*rvdir) % S7; }
constexpr int symmask = (1<<30);
#if HDR
constexpr int symmask = (1<<30);
struct code {
vector<int> connections;

View File

@ -755,7 +755,7 @@ EX transmatrix track_matrix(int at, int dir) {
transmatrix res = unshift(ggmatrix(racing::track[at]));
while(true) {
if(at+dir < 0 || at+dir >= isize(racing::track)) return res;
for(int x=0; x<MDIM; x++) for(int y=0; y<MDIM; y++)
for(int x=0; x<MXDIM; x++) for(int y=0; y<MXDIM; y++)
if(abs(res[y][x]) > 10000) return res;
cell *cur = racing::track[at];
at += dir;

View File

@ -27,7 +27,11 @@ EX ld exp_start = 1;
EX ld exp_decay_exp = 4;
EX ld exp_decay_poly = 10;
#ifdef GLES_ONLY
const int gms_limit = 16; /* enough for Bringris -- need to do better */
#else
const int gms_limit = 110;
#endif
EX ld maxstep_sol = .05;
EX ld maxstep_nil = .1;
@ -39,7 +43,8 @@ static const int NO_LIMIT = 999999;
EX ld hard_limit = NO_LIMIT;
EX int max_iter_sol = 600, max_iter_iso = 60;
EX int max_iter_sol = 600;
EX int max_iter_iso = 60;
EX int max_cells = 2048;
EX bool rays_generate = true;
@ -170,6 +175,8 @@ int deg, irays;
#ifdef GLES_ONLY
void add(string& tgt, string type, string name, int min_index, int max_index) {
if(min_index >= max_index) ;
else
if(min_index + 1 == max_index)
tgt += "{ return " + name + "[" + its(min_index) + "]; }";
else {
@ -193,6 +200,11 @@ string build_getter(string type, string name, int index) {
#define GET(array, index) array "[" index "]"
#endif
void replace_str(string& s, string a, string b) {
while(s.find(a) != string::npos)
s.replace(s.find(a), isize(a), b);
}
EX hookset<void(string&, string&)> hooks_rayshader;
EX hookset<bool(shared_ptr<raycaster>)> hooks_rayset;
@ -216,6 +228,7 @@ void enable_raycaster() {
bool use_reflect = reflect_val && !nil && !levellines;
bool bi = arcm::in() || kite::in() || arb::in() || !PURE;
bi = false;
string vsh =
"attribute mediump vec4 aPosition;\n"
@ -269,11 +282,13 @@ void enable_raycaster() {
int flat1 = 0, flat2 = deg;
if(prod || rotspace) flat2 -= 2;
#if CAP_BT
if(hyperbolic && bt::in()) {
fsh += "uniform mediump float uBLevel;\n";
flat1 = bt::dirs_outer();
flat2 -= bt::dirs_inner();
}
#endif
if(hyperbolic) fsh +=
@ -355,14 +370,34 @@ void enable_raycaster() {
" mediump mat4 vw = uStart * xzspin(-lambda) * xpush(eye) * yzspin(phi);\n"
" mediump vec4 at0 = vec4(0., 0., 1., 0.);\n";
else fmain +=
else {
fmain +=
" mediump mat4 vw = uStart;\n"
" mediump vec4 at0 = at;\n"
" gl_FragColor = vec4(0,0,0,1);\n"
" mediump float left = 1.;\n"
" at0.y = -at.y;\n"
" at0.w = 0.;\n"
" at0.w = 0.;\n";
if(panini_alpha) fmain +=
"mediump float hr = at0.x*at0.x;\n"
"mediump float alpha = " + to_glsl(panini_alpha) + ";\n"
"mediump float A = 1. + hr;\n"
"mediump float B = -2.*hr*alpha;\n"
"mediump float C = 1. - hr*alpha*alpha;\n"
"B /= A; C /= A;\n"
"mediump float hz = B / 2. + sqrt(C + B*B/4.);\n"
"if(abs(hz) > 1e-3) {"
"at0.xyz *= hz+alpha;\n"
"at0.z = hz;\n}"
" else at0.z = 0.;\n"
"\n"
;
fmain +=
" at0.xyz = at0.xyz / length(at0.xyz);\n";
}
if(hyperbolic) fsh += " mediump float len(mediump vec4 x) { return x[3]; }\n";
else if(sphere && rotspace) fsh += " mediump float len(mediump vec4 x) { return 1.+x.x*x.x+x.y*x.y-x.z*x.z-x.w*x.w; }\n";
@ -454,7 +489,7 @@ void enable_raycaster() {
fmain += "for(int i="+its(flat1)+"; i<"+(prod ? "sides-2" : WDIM == 2 ? "sides" : its(flat2))+"; i++) {\n";
fmain += "int woi = walloffset+i;\n";
// fmain += "int woi = walloffset+i;\n";
if(in_h2xe()) fmain +=
" mediump float v = ((position - uM[woi] * position)[2] / (uM[woi] * tangent - tangent)[2]);\n"
@ -496,6 +531,8 @@ void enable_raycaster() {
" mediump vec4 next_position = position + d * tangent;\n"
" if(dot(next_position, tangent) < dot(uM[woi]*next_position, uM[woi]*tangent)) continue;\n";
replace_str(fmain, "[woi]", "[walloffset+i]");
fmain +=
" if(d < dist) { dist = d; which = i; }\n"
"}\n";
@ -964,6 +1001,11 @@ void enable_raycaster() {
"tangent -= dot(vec4(-position.xyz, position.w), tangent) * position;\n"
"tangent /= sqrt(dot(tangent.xyz, tangent.xyz) - tangent.w*tangent.w);\n";
if(in_h2xe()) fmain +=
"position /= sqrt(position.z*position.z - dot(position.xy, position.xy));\n"
"tangent -= dot(vec3(-position.xy, position.z), tangent.xyz) * position;\n"
"tangent /= sqrt(dot(tangent.xy, tangent.xy) - tangent.z*tangent.z);\n";
if(hyperbolic && bt::in()) {
fmain +=
"if(which == 20) {\n"
@ -1054,11 +1096,12 @@ void enable_raycaster() {
" if(col.w == 1.) {\n";
if(hyperbolic) fmain +=
" mediump float z = at0.z * sinh(go);\n"
" mediump float w = 1.;\n";
" mediump vec4 t = at0 * sinh(go);\n";
else fmain +=
" mediump float z = at0.z * go;\n"
" mediump float w = 1.;\n";
" mediump vec4 t = at0 * go;\n";
fmain +=
" t.w = 1.;\n";
if(levellines) {
if(hyperbolic)
@ -1068,9 +1111,12 @@ void enable_raycaster() {
fsh += "uniform mediump float uLevelLines;\n";
}
if(panini_alpha)
fmain += panini_shader();
#ifndef GLES_ONLY
fmain +=
" gl_FragDepth = (" + to_glsl(-vnear-vfar)+"+w*" + to_glsl(2*vnear*vfar)+"/z)/" + to_glsl(vnear-vfar)+";\n"
" gl_FragDepth = (" + to_glsl(-vnear-vfar)+"+t.w*" + to_glsl(2*vnear*vfar)+"/t.z)/" + to_glsl(vnear-vfar)+";\n"
" gl_FragDepth = (gl_FragDepth + 1.) / 2.;\n";
#endif
@ -1195,12 +1241,20 @@ void bind_array(vector<array<float, 4>>& v, GLint t, GLuint& tx, int id) {
if(tx == 0) glGenTextures(1, &tx);
glActiveTexture(GL_TEXTURE0 + id);
GLERR("activeTexture");
glBindTexture(GL_TEXTURE_2D, tx);
GLERR("bindTexture");
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLERR("texParameteri");
#ifdef GLES_ONLY
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, length, isize(v)/length, 0, GL_RGBA, GL_FLOAT, &v[0]);
#else
glTexImage2D(GL_TEXTURE_2D, 0, 0x8814 /* GL_RGBA32F */, length, isize(v)/length, 0, GL_RGBA, GL_FLOAT, &v[0]);
#endif
GLERR("bind_array");
}
@ -1533,8 +1587,11 @@ EX void cast() {
}
if(o->uPLevel != -1)
glUniform1f(o->uPLevel, cgi.plevel / 2);
#if CAP_BT
if(o->uBLevel != -1)
glUniform1f(o->uBLevel, log(bt::expansion()) / 2);
#endif
if(o->uLinearSightRange != -1)
glUniform1f(o->uLinearSightRange, sightranges[geometry]);
@ -1560,7 +1617,13 @@ EX void cast() {
}
#if CAP_VERTEXBUFFER
glhr::bindbuffer_vertex(screen);
glVertexAttribPointer(hr::aPosition, 4, GL_FLOAT, GL_FALSE, sizeof(glvertex), 0);
#else
glVertexAttribPointer(hr::aPosition, 4, GL_FLOAT, GL_FALSE, sizeof(glvertex), &screen[0]);
#endif
if(ray::comparison_mode)
glhr::set_depthtest(false);
else {
@ -1572,6 +1635,7 @@ EX void cast() {
glActiveTexture(GL_TEXTURE0 + 0);
glBindTexture(GL_TEXTURE_2D, floor_textures->renderedTexture);
GLERR("bind");
glDrawArrays(GL_TRIANGLES, 0, 6);
GLERR("finish");
}

View File

@ -13,13 +13,6 @@
namespace hr {
#if MAXMDIM >= 4
namespace binary {
void build_tmatrix();
void virtualRebaseSimple(heptagon*& base, transmatrix& at);
int celldistance3(heptagon *c1, heptagon *c2);
hyperpoint deparabolic3(hyperpoint h);
}
/** \brief regular three-dimensional tessellations */
EX namespace reg3 {
@ -608,8 +601,8 @@ EX namespace reg3 {
hrmap *binary_map;
hrmap_quotient3 *quotient_map;
unordered_map<heptagon*, pair<heptagon*, transmatrix>> reg_gmatrix;
unordered_map<heptagon*, vector<pair<heptagon*, transmatrix> > > altmap;
map<heptagon*, pair<heptagon*, transmatrix>> reg_gmatrix;
map<heptagon*, vector<pair<heptagon*, transmatrix> > > altmap;
vector<cell*> spherecells;
@ -641,10 +634,13 @@ EX namespace reg3 {
quotient_map = nullptr;
#if CAP_FIELD
#if CAP_CRYSTAL
if(geometry == gSpace344) {
quotient_map = new hrmap_from_crystal;
}
else if(geometry == gSpace535) {
else
#endif
if(geometry == gSpace535) {
quotient_map = new seifert_weber::hrmap_seifert_cover;
}
else if(hyperbolic) {
@ -653,6 +649,7 @@ EX namespace reg3 {
#endif
h.zebraval = quotient_map ? quotient_map->allh[0]->zebraval : 0;
#if CAP_BT
if(hyperbolic) {
dynamicval<eGeometry> g(geometry, gBinary3);
bt::build_tmatrix();
@ -667,6 +664,7 @@ EX namespace reg3 {
binary_map = bt::new_alt_map(alt);
T = xpush(.01241) * spin(1.4117) * xpush(0.1241) * cspin(0, 2, 1.1249) * xpush(0.07) * Id;
}
#endif
reg_gmatrix[origin] = make_pair(alt, T);
altmap[alt].emplace_back(origin, T);
@ -730,12 +728,14 @@ EX namespace reg3 {
println(hlog, "FAIL");
exit(3);
}
#if CAP_BT
if(steps) {
dynamicval<eGeometry> g(geometry, gBinary3);
dynamicval<hrmap*> cm(currentmap, binary_map);
for(int i=0; i<alt->type; i++)
verify_neighbors(alt->cmove(i), steps-1, currentmap->iadj(alt, i) * hT);
}
#endif
}
heptagon *create_step(heptagon *parent, int d) override {
@ -748,11 +748,13 @@ EX namespace reg3 {
transmatrix T = p1.second * cgi.adjmoves[d];
#endif
transmatrix T1 = T;
#if CAP_BT
if(hyperbolic) {
dynamicval<eGeometry> g(geometry, gBinary3);
dynamicval<hrmap*> cm(currentmap, binary_map);
binary_map->virtualRebase(alt, T);
}
#endif
fixmatrix(T);
auto hT = tC0(T);
@ -852,10 +854,12 @@ EX namespace reg3 {
}
~hrmap_reg3() {
#if CAP_BT
if(binary_map) {
dynamicval<eGeometry> g(geometry, gBinary3);
delete binary_map;
}
#endif
if(quotient_map) delete quotient_map;
clearfrom(origin);
}
@ -901,11 +905,13 @@ EX namespace reg3 {
auto p1 = reg_gmatrix[h1];
auto p2 = reg_gmatrix[h2];
transmatrix T = Id;
#if CAP_BT
if(hyperbolic) {
dynamicval<eGeometry> g(geometry, gBinary3);
dynamicval<hrmap*> cm(currentmap, binary_map);
T = binary_map->relative_matrix(p2.first, p1.first, hint);
}
#endif
T = inverse(p1.second) * T * p2.second;
if(elliptic && T[LDIM][LDIM] < 0) T = centralsym * T;
return T;
@ -1343,7 +1349,11 @@ EX int celldistance(cell *c1, cell *c2) {
return clueless_celldistance(c1, c2);
dynamicval<eGeometry> g(geometry, gBinary3);
#if CAP_BT
return 20 + bt::celldistance3(r->reg_gmatrix[c1->master].first, r->reg_gmatrix[c2->master].first);
#else
return 20;
#endif
}
EX bool pseudohept(cell *c) {
@ -1429,7 +1439,7 @@ ld adistance(cell *c) {
return regmap()->reg_gmatrix[c->master].first->distance * log(2) - h[0];
}
unordered_map<pair<cell*, cell*>, int> memo;
map<pair<cell*, cell*>, int> memo;
bool cdd;

View File

@ -85,7 +85,9 @@ renderbuffer::renderbuffer(int x, int y, bool gl) : x(x), y(y) {
# if CAP_GL
if(gl) {
GLERR("renderbuffer init");
resetbuffer rb;
GLERR("after resetbuffer");
tx = next_p2(x);
ty = next_p2(y);
@ -119,7 +121,7 @@ renderbuffer::renderbuffer(int x, int y, bool gl) : x(x), y(y) {
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tx, ty);
bool has_depth = true;
if(glGetError() != GL_NO_ERROR) {
printf("Could not create: GL_DEPTH24_STENCIL8");
println(hlog, "Could not create: GL_DEPTH24_STENCIL8");
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, tx, ty);
has_depth = false;
}
@ -243,7 +245,9 @@ resetbuffer::resetbuffer() {
#if CAP_GL
drawFboId = 0, readFboId = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFboId);
GLERR("getInteger a");
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &readFboId);
GLERR("getInteger b");
#endif
#if CAP_SDL
sreset = s;

1802
rogueviz/bringris.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,7 @@ struct jmatrix : array<array<int, 7>, 7> {
};
vector<jmatrix> jms;
std::unordered_map<jmatrix, int> ids;
std::map<jmatrix, int> ids;
jmatrix J, Z, id;

View File

@ -74,7 +74,7 @@ void make() {
for(int i=0; i<magmav; i++) vertices.push_back(vertices[i]);
for(int i=0; i<magmav; i++) vertices.push_back(vertices[i]);
unordered_map<pair<int, int>, int> counts;
map<pair<int, int>, int> counts;
int big = v - 2;

View File

@ -11,7 +11,7 @@
#endif
#define main nconf_main
#undef unordered_map
#undef map
#undef self
#include "nconf.cpp"
#undef main

View File

@ -546,7 +546,7 @@ void queuedisk(const shiftmatrix& V, const colorpair& cp, bool legend, const str
}
}
unordered_map<pair<edgeinfo*, int>, int> drawn_edges;
map<pair<edgeinfo*, int>, int> drawn_edges;
map<pair<cell*, cell*>, transmatrix> relmatrices;

View File

@ -360,7 +360,7 @@ void starbattle_puzzle() {
}
keyhandler = [] (int sym, int uni) {
handlePanning(sym, uni);
if(!dialog_shown) handlePanning(sym, uni);
dialog::handleNavigation(sym, uni);
if(among(sym, '-', SDLK_F1) && !holdmouse) push_stop();

179
rogueviz/subquotient.cpp Normal file
View File

@ -0,0 +1,179 @@
#include "../hyper.h"
#include <iostream>
#include <fstream>
#include <thread>
namespace hr {
namespace subquotient {
eGeometry gSubquotient(eGeometry(-1));
vector<int> connections;
void create_subquotient(int qty = -1, int id = 0) {
start_game();
auto ac = currentmap->allcells();
auto ca = currentmap->gamestart();
for(auto cb: ac) for(int i=0; i<cb->type; i++) for(int m=0; m<2; m++) {
vector<cell*> visited;
map<cell*, cellwalker> vmap;
auto visit = [&] (cell *da, cellwalker db) {
if(vmap.count(da)) {
// println(hlog, da, " -> ", db, " [old]");
return;
}
// println(hlog, da, " -> ", db, " [new]");
vmap[da] = db;
visited.emplace_back(da);
};
visit(ca, cellwalker(cb, i, m));
for(int i=0; i<isize(visited); i++) {
for(int j=0; j<visited[i]->type; j++) {
cellwalker wa(visited[i], 0);
cellwalker wb(vmap[visited[i]]);
wa += j;
wb += j;
wa += wstep;
wb += wstep;
int r = wa.spin;
wa -= r;
wb -= r;
// println(hlog, wa, " -> ", wb);
setdist(wa.at, 7, nullptr);
wa.at->item = itGold;
visit(wa.at, wb);
}
}
int vertex = 0, edge = 0, badcycle = 0;
map<int, int> by_cycle;
for(auto swb: vmap) {
auto& s = swb.first;
auto& wb = swb.second;
if(s == wb.at) { vertex++; continue; }
bool is_edge = false;
for(int j=0; j<s->type; j++) if(s->move(j) == wb.at && (wb+j).peek() == s)
is_edge = true;
if(is_edge) { edge++; continue; }
int cs = 0;
cell *sx = s;
auto cw = cellwalker(s, 0);
vector<cell*> lst;
do {
int sp = cw.spin;
bool mirr = cw.mirrored;
if(cw.mirrored) sp = -sp;
cw -= sp;
lst.push_back(sx);
cw = vmap[sx];
if(mirr) cw += wmirror;
sx = cw.at;
cw += sp;
cs++;
if(cs >= 100) break;
}
while(sx != s);
if(cw.spin) badcycle++;
by_cycle[cs]++;
}
if(vertex || edge || badcycle || m == 0) continue;
vector<pair<int, int>> bcp;
for(auto b: by_cycle) bcp.push_back(b);
if(qty == -1)
println(hlog, "m=", m, " vertex/edge = ", tie(vertex, edge), " badcycle = ", badcycle, " by_cycle = ", bcp);
if(by_cycle[qty] == isize(vmap)) {
if(id > 0) {id--; continue; }
map<cell*, int> ids;
int next_id = 0;
vector<cell*> by_id;
set<cell*> visited;
for(auto s: ac) if(!visited.count(s)) {
by_id.push_back(s);
ids[s] = next_id;
auto sx = s;
do {
visited.insert(sx);
sx = vmap[sx].at;
}
while(sx != s);
next_id++;
}
println(hlog, "ids = ", next_id);
connections.clear();
if(int(gSubquotient) == -1) {
ginf.push_back(ginf[geometry]);
gSubquotient = eGeometry(isize(ginf) - 1);
}
ginf[gSubquotient] = ginf[geometry];
/* we need to be 'pure', unrectified may not work */
if(UNRECTIFIED) swap(ginf[gSubquotient].sides, ginf[gSubquotient].vertex);
for(int i=0; i<next_id; i++) {
cell *s = by_id[i];
for(int j=0; j<s->type; j++) {
cellwalker cw(s, j);
cw += wstep;
int res;
while(!ids.count(cw.at)) {
int sp = cw.spin;
bool flip = cw.mirrored;
if(flip) sp = -sp;
cw -= sp;
if(cw.spin) println(hlog, "bad spin");
cw = vmap[cw.at];
if(flip) cw += wmirror;
cw += sp;
}
res = ids[cw.at] * s->type + cw.spin;
if(cw.mirrored) res |= quotientspace::symmask;
connections.push_back(res);
}
}
stop_game();
set_geometry(gSubquotient);
variation = eVariation::pure;
println(hlog, "variation = ", int(variation));
start_game();
println(hlog, "started");
return;
}
}
}
int readArgs() {
using namespace arg;
if(0) ;
else if(argis("-subquotient")) { start_game(); shift(); create_subquotient(argi()); }
else return 1;
return 0;
}
auto fundamentalhook = addHook(hooks_args, 100, readArgs)
+ addHook(hooks_newmap, 0, [] {
if(geometry == gSubquotient)
return (hrmap*) new quotientspace::hrmap_quotient(connections);
return (hrmap*) nullptr;
});
}
}

View File

@ -631,7 +631,7 @@ bool force(rugpoint& m1, rugpoint& m2, double rd, bool is_anticusp=false, double
transmatrix iT = rgpushxto0(m1.native);
for(int i=0; i<MDIM; i++) if(std::isnan(m1.native[i])) {
for(int i=0; i<MXDIM; i++) if(std::isnan(m1.native[i])) {
addMessage("Failed!");
println(hlog, "m1 = ", m1.native);
throw rug_exception();
@ -1850,11 +1850,15 @@ EX namespace rug {
EX bool rugged = false;
EX bool renderonce = false;
EX bool rendernogl = true;
EX bool mouse_control_rug = false;
EX int texturesize = 512;
EX ld scale = 1.0f;
EX bool rug_control() { return false; }
EX bool in_crystal() { return false; }
EX void reset_view() { }
#if HDR
struct using_rugview {};
#endif
EX }
#endif

View File

@ -1815,6 +1815,7 @@ void no_init() { }
startanim null_animation { "", no_init, [] { gamescreen(2); }};
#if CAP_STARTANIM
startanim joukowsky { "Joukowsky transform", no_init, [] {
dynamicval<eModel> dm(pmodel, mdJoukowskyInverted);
dynamicval<ld> dt(pconf.model_orientation, ticks / 25.);
@ -1874,6 +1875,7 @@ startanim spin_around { "spinning around", no_init, [] {
dynamicval<transmatrix> dv(View, spin(-cos_auto(circle_radius)*alpha) * xpush(circle_radius) * spin(alpha) * View);
gamescreen(2);
}};
#endif
reaction_t add_to_frame;

View File

@ -60,6 +60,23 @@ glhr::glmatrix model_orientation_gl() {
return s;
}
EX void reset_all_shaders() {
ray::reset_raycaster();
compiled_programs.clear();
matched_programs.clear();
}
EX string panini_shader() {
return
"t.w += 1.; t *= 2. / t.w; t.w -= 1.;\n"
"float s = t.z;\n"
"float l = length(t.xyz);\n"
"t /= max(length(t.xz), 1e-2);\n"
"t.z += " + glhr::to_glsl(panini_alpha) + ";\n"
"t *= l;\n"
"t.w = 1.;\n";
}
shared_ptr<glhr::GLprogram> write_shader(flagtype shader_flags) {
string varying, vsh, fsh, vmain = "void main() {\n", fmain = "void main() {\n";
@ -332,6 +349,14 @@ shared_ptr<glhr::GLprogram> write_shader(flagtype shader_flags) {
}
if(shader_flags & GF_LEVELS) vmain += "vPos = t;\n";
if(treset) vmain += "t[3] = 1.0;\n";
if(WDIM == 3 && panini_alpha) {
vmain += "t = uPP * t;", vsh += "uniform mediump mat4 uPP;";
/* panini */
vmain += panini_shader();
shader_flags |= SF_ORIENT;
}
vmain += "gl_Position = uP * t;\n";
}
@ -485,6 +510,7 @@ void display_data::set_projection(int ed, ld shift) {
for(int i=0; i<3; i++) NLP[3][i] = NLP[i][3] = 0;
NLP[3][3] = 1;
}
if(!(shader_flags & SF_ORIENT))
glhr::projection_multiply(glhr::tmtogl_transpose(NLP));
}
if(ed) {
@ -520,6 +546,9 @@ void display_data::set_projection(int ed, ld shift) {
if(get_shader_flags() & SF_USE_ALPHA)
pp[3][2] = GLfloat(pconf.alpha);
if(nisot::local_perspective_used())
pp = glhr::tmtogl_transpose(NLP) * pp;
if(get_shader_flags() & SF_ORIENT) {
if(GDIM == 3) for(int a=0; a<4; a++)
models::apply_orientation_yz(pp[a][1], pp[a][2]);
@ -547,6 +576,10 @@ void display_data::set_projection(int ed, ld shift) {
glhr::projection_multiply(glhr::translate(shift, 0, 0));
}
if(in_h2xe() || in_s2xe()) {
glhr::projection_multiply(glhr::translate(0, 0, shift));
}
if(selected->shader_flags & SF_HALFPLANE) {
glhr::projection_multiply(glhr::translate(0, 1, 0));
glhr::projection_multiply(glhr::scale(-1, 1, 1));
@ -608,7 +641,7 @@ EX void glapplymatrix(const transmatrix& V) {
GLfloat mat[16];
int id = 0;
if(MDIM == 3) {
if(MXDIM == 3) {
for(int y=0; y<3; y++) {
for(int x=0; x<3; x++) mat[id++] = V[x][y];
mat[id++] = 0;

View File

@ -1167,8 +1167,10 @@ void movePlayer(monster *m, int delta) {
cwt.at = c2; afterplayermoved();
if(c2->item && c2->land == laAlchemist) c2->wall = m->base->wall;
#if CAP_COMPLEX2
if(m->base->wall == waRoundTable)
camelot::roundTableMessage(c2);
#endif
if(c2->wall == waCloud || c2->wall == waMirror) {
visibleFor(500);
cellwalker cw(c2, 0, false);
@ -1189,7 +1191,9 @@ void movePlayer(monster *m, int delta) {
items[itOrbLife] = 0;
m->dead = true;
}
#if CAP_COMPLEX2
mine::uncover_full(c2);
#endif
if(isWatery(c2) && isWatery(m->base) && m->inBoat)
moveItem(m->base, c2, true);
@ -2523,7 +2527,7 @@ EX void turn(int delta) {
if(doall)
for(cell *c: currentmap->allcells()) activateMonstersAt(c);
else
for(unordered_map<cell*, shiftmatrix>::iterator it = gmatrix.begin(); it != gmatrix.end(); it++)
for(map<cell*, shiftmatrix>::iterator it = gmatrix.begin(); it != gmatrix.end(); it++)
activateMonstersAt(it->first);
/* printf("size: gmatrix = %ld, active = %ld, monstersAt = %ld, delta = %d\n",
@ -2635,7 +2639,9 @@ EX void turn(int delta) {
#if CAP_INV
if(inv::on) inv::compute();
#endif
#if CAP_COMPLEX2
terracotta::check();
#endif
heat::processfires();
if(havewhat&HF_WHIRLPOOL) whirlpool::move();
if(havewhat&HF_WHIRLWIND) whirlwind::move();

View File

@ -55,10 +55,7 @@ void dqi_sky::draw() {
int sk = get_skybrightness();
unordered_map<cell*, pair<color_t, color_t>> colors;
#ifdef USE_UNORDERED_MAP
colors.reserve(isize(sky));
#endif
map<cell*, pair<color_t, color_t>> colors;
for(sky_item& si: sky) colors[si.c] =
make_pair(darkena(gradient(0, si.color, 0, sk, 255), 0, 0xFF),
darkena(si.skycolor, 0, 0xFF)
@ -238,6 +235,7 @@ void celldrawer::draw_ceiling() {
break;
case laVariant: {
#if CAP_COMPLEX2
int b = getBits(c);
col = 0x404040;
for(int a=0; a<21; a++)
@ -245,6 +243,7 @@ void celldrawer::draw_ceiling() {
col += variant::features[a].color_change;
col = col & 0x00FF00;
skycol = col;
#endif
break;
}

View File

@ -161,7 +161,9 @@ struct hrmap_spherical : hrmap_standard {
transmatrix relative_matrix(cell *c2, cell *c1, const hyperpoint& hint) {
if(!gmatrix0.count(c2) || !gmatrix0.count(c1)) {
#if !ISWEB
printf("building gmatrix0 (size=%d)\n", isize(gmatrix0));
#endif
#if CAP_GP
auto bak = gp::draw_li;
#endif

View File

@ -185,6 +185,10 @@
#define MAXMDIM 4
#endif
#ifndef CAP_MDIM_FIXED
#define CAP_MDIM_FIXED 0
#endif
#ifndef CAP_TEXTURE
#define CAP_TEXTURE (CAP_GL && (CAP_PNG || CAP_SDL_IMG) && !ISMINI)
#endif
@ -206,7 +210,7 @@
#endif
#ifndef CAP_TOUR
#define CAP_TOUR (!ISWEB && !ISMINI)
#define CAP_TOUR (!ISMINI)
#endif
#ifndef CAP_ROGUEVIZ
@ -285,7 +289,7 @@
#endif
#ifndef CAP_SHMUP
#define CAP_SHMUP 1
#define CAP_SHMUP (!ISWEB)
#endif
#ifndef CAP_BITFIELD
@ -403,6 +407,10 @@ extern "C" {
#define CAP_GLEW (CAP_GL && !ISMOBILE && !ISMAC && !ISLINUX && !ISWEB)
#endif
#if ISWEB
#define GLES_ONLY
#endif
#if CAP_GL
#if CAP_GLEW
#include <GL/glew.h>
@ -462,6 +470,11 @@ typedef unsigned GLuint;
#include <zlib.h>
#endif
#if ISWEB
#include <emscripten.h>
#include <emscripten/html5.h>
#endif
#if CAP_GMP
#include <gmpxx.h>
#endif
@ -478,14 +491,6 @@ typedef unsigned GLuint;
#endif
#endif
#ifdef USE_UNORDERED_MAP
#include <unordered_map>
#include <unordered_set>
#else
#define unordered_map map
#define unordered_set set
#endif
#include <stdint.h>
#if ISWINDOWS
@ -573,7 +578,7 @@ union SDL_Event;
#endif
#ifndef CAP_RAY
#define CAP_RAY (MAXMDIM >= 4 && !ISWEB && !ISMOBILE && CAP_GL)
#define CAP_RAY (MAXMDIM >= 4 && CAP_GL)
#endif
#ifndef CAP_MEMORY_RESERVE

View File

@ -194,7 +194,9 @@ EX void initgame() {
cwt.at = currentmap->gamestart(); cwt.spin = 0; cwt.mirrored = false;
cwt.at->land = firstland;
#if CAP_COMPLEX2
if(firstland == laBrownian) brownian::init(cwt.at);
#endif
chaosAchieved = false;
@ -214,7 +216,9 @@ EX void initgame() {
}
if((tactic::on || yendor::on || peace::on) && isCyclic(firstland)) {
#if CAP_COMPLEX2
camelot::anthraxBonus = items[itHolyGrail];
#endif
cwt.at->move(0)->land = firstland;
if(firstland == laWhirlpool) cwt.at->move(0)->wall = waSea;
@ -366,7 +370,9 @@ EX void initgame() {
#if CAP_INV
if(inv::on) inv::init();
#endif
#if CAP_COMPLEX2
mine::auto_teleport_charges();
#endif
if(!use_special_land) {
if(firstland != (princess::challenge ? laPalace : laIce)) cheater++;
}
@ -1265,7 +1271,9 @@ EX void stop_game() {
princess::reviveAt = 0;
princess::forceVizier = false;
princess::forceMouse = false;
#if CAP_COMPLEX2
camelot::knighted = 0;
#endif
// items[itGreenStone] = 100;
clearMemory();
game_active = false;
@ -1287,7 +1295,6 @@ EX void set_geometry(eGeometry target) {
bool was_default = pmodel == default_model();
callhooks(hooks_on_geometry_change);
if(geometry != target) {
int old_DIM = GDIM;
stop_game();
ors::reset();
if(among(target, gProduct, gRotSpace)) {
@ -1318,14 +1325,11 @@ EX void set_geometry(eGeometry target) {
if(bt::in() || WDIM == 3 || kite::in() || arb::in()) if(!hybri) variation = eVariation::pure;
#endif
if(S3 >= OINF) variation = eVariation::pure;
if(INVERSE) variation = gp::variation_for(gp::param);
if(INVERSE && !hybri) variation = gp::variation_for(gp::param);
if(ginf[target].default_variation == eVariation::pure && geometry != gArchimedean)
variation = eVariation::pure;
if(was_default) pmodel = default_model();
if(nonisotropic && old_DIM == 2 && vid.texture_step < 4) vid.texture_step = 4;
if(WDIM == 2 && (cgflags & qIDEAL) && vid.always3 && vid.texture_step < 32) vid.texture_step = 32;
if(prod) { pmodel = mdPerspective; if(vid.texture_step < 4) vid.texture_step = 4; }
if(WDIM == 3 && (cgflags & qIDEAL) && vid.texture_step < 4) vid.texture_step = 4;
if(sl2) nisot::geodesic_movement = true;
if(rotspace) {
@ -1495,7 +1499,9 @@ EX void start_game() {
ignored_memory_warning = false;
check_cgi();
cgi.require_basics();
#if CAP_ARCM
arcm::current.compute_geometry();
#endif
initcells();
expansion.reset();

View File

@ -7,6 +7,13 @@
#include "hyper.h"
namespace hr {
#if !CAP_TOUR
EX namespace tour {
EX always_false on;
EX }
#endif
#if CAP_TOUR
/** \brief Variables and function related to Guided Tour and other presentations. */

View File

@ -263,6 +263,7 @@ cld exp_parser::parse(int prio) {
force_eat(")");
res = edge_of_triangle_with_angles(M_PI/2, M_PI/a, M_PI/b);
}
#if CAP_ARCM
else if(eat("arcmedge(")) {
vector<int> vals;
vals.push_back(iparse(0));
@ -280,6 +281,7 @@ cld exp_parser::parse(int prio) {
if(extra_params.count("distunit"))
res /= extra_params["distunit"];
}
#endif
else if(eat("regangle(")) {
cld edgelen = parse(0);
if(extra_params.count("distunit")) {
@ -373,6 +375,7 @@ cld exp_parser::parse(int prio) {
else if(number == "psl_steps") res = cgi.psl_steps;
else if(number == "single_step") res = cgi.single_step;
else if(number == "step") res = hdist0(tC0(currentmap->adj(cwt.at, 0)));
else if(number == "edgelen") res = hdist(get_corner_position(cwt.at, 0), get_corner_position(cwt.at, 1));
else if(number == "mousey") res = mousey;
else if(number == "random") res = randd();
else if(number == "mousez") res = cld(mousex - current_display->xcenter, mousey - current_display->ycenter) / cld(current_display->radius, 0);

View File

@ -1063,7 +1063,11 @@ void save_mode_data(hstream& f) {
f.write<char>(chaosmode);
f.write<char>(shmup::on);
f.write<char>(inv::on);
#if CAP_TOUR
f.write<char>(tour::on);
#else
f.write<char>(false);
#endif
f.write<char>(peace::on);
f.write<char>(peace::otherpuzzles);
f.write<char>(peace::explore_other);