1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-11-10 15:59:53 +00:00
hyperrogue/config.cpp
2019-09-12 22:38:39 +02:00

1973 lines
67 KiB
C++

// Hyperbolic Rogue -- configuration
// Copyright (C) 2017-2018 Zeno Rogue, see 'hyper.cpp' for details
namespace hr {
const char *conffile = "hyperrogue.ini";
videopar vid;
#define DEFAULT_WALLMODE (ISMOBILE ? 3 : 5)
#define DEFAULT_MONMODE (ISMOBILE ? 2 : 4)
#if ISANDROID
#define ANDROID_SETTINGS settingsChanged = true;
#else
#define ANDROID_SETTINGS ;
#endif
extern color_t floorcolors[landtypes];
charstyle& getcs(int id) {
if(multi::players>1 && id >= 0 && id < multi::players)
return multi::scs[id];
else
return vid.cs;
}
void hread(hstream& hs, charstyle& cs) { hread_raw(hs, cs); }
void hwrite(hstream& hs, const charstyle& cs) { hwrite_raw(hs, cs); }
string csnameid(int id) {
if(id == 0) return XLAT("male");
if(id == 1) return XLAT("female");
if(id == 2) return XLAT("Prince");
if(id == 3) return XLAT("Princess");
if(id == 4 || id == 5) return XLAT("cat");
if(id == 6 || id == 7) return XLAT("dog");
if(id == 8 || id == 9) return XLATN("Familiar");
return XLAT("none");
}
string csname(charstyle& cs) {
return csnameid(cs.charid);
}
int playergender() {
return (getcs().charid >= 0 && (getcs().charid&1)) ? GEN_F : GEN_M;
}
int princessgender() {
int g = playergender();
if(vid.samegender) return g;
return g == GEN_M ? GEN_F : GEN_M;
}
int default_language;
int lang() {
if(vid.language >= 0)
return vid.language;
return default_language;
}
bool autojoy = true;
#if CAP_CONFIG
saverlist savers;
#endif
#if !CAP_CONFIG
template<class T, class U, class V> void addsaver(T& i, U name, V dft) {
i = dft;
}
template<class T, class U> void addsaver(T& i, U name) {}
template<class T, class U> void addsaverenum(T& i, U name) {}
template<class T, class U> void addsaverenum(T& i, U name, T dft) {}
#endif
void addsaver(charstyle& cs, string s) {
addsaver(cs.charid, s + ".charid");
addsaver(cs.skincolor, s + ".skincolor");
addsaver(cs.haircolor, s + ".haircolor");
addsaver(cs.dresscolor, s + ".dresscolor");
addsaver(cs.swordcolor, s + ".swordcolor");
addsaver(cs.dresscolor2, s + ".dresscolor2");
addsaver(cs.uicolor, s + ".uicolor");
addsaver(cs.lefthanded, s + ".lefthanded");
}
// R:239, G:208, B:207
unsigned int skincolors[] = { 7, 0xD0D0D0FF, 0xEFD0C9FF, 0xC77A58FF, 0xA58869FF, 0x602010FF, 0xFFDCB1FF, 0xEDE4C8FF };
unsigned int haircolors[] = { 8, 0x686868FF, 0x8C684AFF, 0xF2E1AEFF, 0xB55239FF, 0xFFFFFFFF, 0x804000FF, 0x502810FF, 0x301800FF };
unsigned int dresscolors[] = { 6, 0xC00000FF, 0x00C000FF, 0x0000C0FF, 0xC0C000FF, 0xC0C0C0FF, 0x202020FF };
unsigned int dresscolors2[] = { 7, 0x8080FFC0, 0x80FF80C0, 0xFF8080C0, 0xFFFF80C0, 0xFF80FFC0, 0x80FFFFC0, 0xFFFFFF80 };
unsigned int swordcolors[] = { 6, 0xC0C0C0FF, 0xFFFFFFFF, 0xFFC0C0FF, 0xC0C0FFFF, 0x808080FF, 0x202020FF };
unsigned int eyecolors[] = { 4, 0x00C000FF, 0x0000C0FF, 0xC00000FF, 0xC0C000FF };
void initcs(charstyle &cs) {
cs.charid = 0;
cs.skincolor = 0xD0D0D0FF;
cs.haircolor = 0x686868FF;
cs.dresscolor = 0xC00000FF;
cs.swordcolor = 0xD0D0D0FF;
cs.dresscolor2= 0x8080FFC0;
cs.uicolor = 0xFF0000FF;
cs.lefthanded = false;
}
#if CAP_CONFIG
void loadcs(FILE *f, charstyle& cs, int xvernum) {
int gflags, err =
fscanf(f, "%d%d%x%x%x%x", &gflags, &vid.language, &cs.skincolor, &cs.haircolor, &cs.swordcolor, &cs.dresscolor);
if(err) cs.charid = gflags & 15;
if(err) vid.samegender = (gflags & 16) ? true : false;
if(cs.charid == 3) hr::ignore(fscanf(f, "%x", &cs.dresscolor2));
if(xvernum >= 8990) hr::ignore(fscanf(f, "%x", &cs.uicolor));
}
#endif
void savecolortable(colortable& ct, string name) {
for(int i=0; i<isize(ct); i++)
addsaver(ct[i], "color:" + name + ":" + its(i));
}
void initConfig() {
// basic config
addsaver(vid.flashtime, "flashtime", 8);
addsaver(vid.msgleft, "message style", 2);
addsaver(vid.msglimit, "message limit", 5);
addsaver(vid.timeformat, "message log time format", 0);
addsaver(fontscale, "fontscale", 100);
addsaver(vid.mobilecompasssize, "mobile compass size", 0); // ISMOBILE || ISPANDORA ? 30 : 0);
addsaver(vid.axes, "movement help", 1);
addsaver(vid.shifttarget, "shift-targetting", 2);
addsaver(vid.steamscore, "scores to Steam", 1);
initcs(vid.cs); addsaver(vid.cs, "single");
addsaver(vid.samegender, "princess choice", false);
addsaver(vid.language, "language", -1);
addsaver(vid.drawmousecircle, "mouse circle", ISMOBILE || ISPANDORA);
addsaver(vid.revcontrol, "reverse control", false);
addsaver(musicvolume, "music volume");
addsaver(effvolume, "sound effect volume");
addsaverenum(glyphsortorder, "glyph sort order");
// basic graphics
addsaver(vid.usingGL, "usingGL", true);
addsaver(vid.antialias, "antialias", AA_NOGL | AA_FONT | AA_LINES | AA_LINEWIDTH | AA_VERSION);
addsaver(vid.linewidth, "linewidth", 1);
addsaver(linepatterns::width, "pattern-linewidth", 1);
addsaver(vid.scale, "scale", 1);
addsaver(vid.xposition, "xposition", 0);
addsaver(vid.yposition, "yposition", 0);
addsaver(vid.alpha, "projection", 1);
addsaver(vid.sspeed, "scrollingspeed", 0);
addsaver(vid.mspeed, "movement speed", 1);
addsaver(vid.full, "fullscreen", false);
addsaver(vid.aurastr, "aura strength", ISMOBILE ? 0 : 128);
addsaver(vid.aurasmoothen, "aura smoothen", 5);
addsaver(vid.graphglyph, "graphical items/kills", 1);
addsaver(vid.particles, "extra effects", 1);
addsaver(vid.framelimit, "frame limit", 75);
addsaver(vid.xres, "xres");
addsaver(vid.yres, "yres");
addsaver(vid.fsize, "font size");
addsaver(vid.darkhepta, "mark heptagons", false);
// special graphics
addsaver(vid.ballangle, "ball angle", 20);
addsaver(vid.yshift, "Y shift", 0);
addsaver(vid.fixed_facing, "fixed facing", 0);
addsaver(vid.camera_angle, "camera angle", 0);
addsaver(vid.ballproj, "ballproj", 1);
addsaver(vid.monmode, "monster display mode", DEFAULT_MONMODE);
addsaver(vid.wallmode, "wall display mode", DEFAULT_WALLMODE);
addsaver(geom3::depth, "3D depth");
addsaver(geom3::camera, "3D camera level");
addsaver(geom3::wall_height, "3D wall height");
addsaver(geom3::rock_wall_ratio, "3D rock-wall ratio");
addsaver(geom3::human_wall_ratio, "3D human-wall ratio");
addsaver(geom3::lake_top, "3D lake top");
addsaver(geom3::lake_bottom, "3D lake bottom");
addsaver(geom3::tc_depth, "3D TC depth");
addsaver(geom3::tc_camera, "3D TC camera");
addsaver(geom3::tc_alpha, "3D TC alpha");
addsaver(geom3::highdetail, "3D highdetail");
addsaver(geom3::middetail, "3D middetail");
addsaver(geom3::gp_autoscale_heights, "3D Goldberg autoscaling");
addsaver(memory_saving_mode, "memory_saving_mode", (ISMOBILE || ISPANDORA || ISWEB) ? 1 : 0);
addsaver(rug::renderonce, "rug-renderonce");
addsaver(rug::rendernogl, "rug-rendernogl");
addsaver(rug::texturesize, "rug-texturesize");
#if CAP_RUG
addsaver(rug::model_distance, "rug-model-distance");
#endif
addsaverenum(pmodel, "used model");
addsaver(polygonal::SI, "polygon sides");
addsaver(polygonal::STAR, "polygon star factor");
addsaver(polygonal::deg, "polygonal degree");
addsaver(conformal::autobandhistory, "include history"); // check!
addsaver(conformal::lvspeed, "lineview speed");
addsaver(conformal::extra_line_steps, "lineview extension");
addsaver(polygonal::maxcoef, "polynomial degree");
for(int i=0; i<polygonal::MSI; i++) {
addsaver(polygonal::coefr[i], "polynomial "+its(i)+".real");
addsaver(polygonal::coefi[i], "polynomial "+its(i)+".imag");
}
addsaver(conformal::bandhalf, "band width");
addsaver(conformal::bandsegment, "band segment");
addsaver(conformal::rotation, "conformal rotation");
addsaver(conformal::do_rotate, "conformal rotation mode", 1);
addsaver(conformal::model_orientation, "model orientation", 0);
addsaver(conformal::top_z, "topz", 5);
addsaver(conformal::model_transition, "model transition", 1);
addsaver(conformal::halfplane_scale, "halfplane scale", 1);
addsaver(conformal::autoband, "automatic band");
addsaver(conformal::autobandhistory, "automatic band history");
addsaver(conformal::dospiral, "do spiral");
addsaver(vid.backeffects, "background particle effects", (ISMOBILE || ISPANDORA) ? false : true);
// control
addsaver(vid.joyvalue, "vid.joyvalue", 4800);
addsaver(vid.joyvalue2, "vid.joyvalue2", 5600);
addsaver(vid.joypanthreshold, "vid.joypanthreshold", 2500);
addsaver(vid.joypanspeed, "vid.joypanspeed", ISPANDORA ? 0.0001 : 0);
addsaver(autojoy, "autojoy");
vid.killreduction = 0;
addsaver(vid.skipstart, "skip the start menu", false);
addsaver(vid.quickmouse, "quick mouse", !ISPANDORA);
// colors
addsaver(backcolor, "color:background");
addsaver(forecolor, "color:foreground");
addsaver(bordcolor, "color:borders");
addsaver(ringcolor, "color:ring");
addsaver(modelcolor, "color:model");
addsaver(periodcolor, "color:period");
addsaver(dialog::dialogcolor, "color:dialog");
for(auto& p: colortables)
savecolortable(p.second, s0+"canvas"+p.first);
savecolortable(distcolors, "distance");
savecolortable(minecolors, "mines");
savecolortable(brownian::colors, "color:brown");
for(int i=0; i<motypes; i++)
addsaver(minf[i].color, "color:monster:" + its(i));
for(int i=0; i<ittypes; i++)
addsaver(iinf[i].color, "color:item:" + its(i));
for(int i=0; i<landtypes; i++)
addsaver(floorcolors[i], "color:land:" + its(i));
for(int i=0; i<walltypes; i++)
addsaver(winf[i].color, "color:wall:" + its(i));
// modes
addsaverenum(geometry, "mode-geometry");
addsaver(shmup::on, "mode-shmup", false);
addsaver(hardcore, "mode-hardcore", false);
addsaver(chaosmode, "mode-chaos");
addsaver(inv::on, "mode-Orb Strategy");
addsaverenum(variation, "mode-variation", eVariation::bitruncated);
addsaver(peace::on, "mode-peace");
addsaver(peace::otherpuzzles, "mode-peace-submode");
addsaverenum(specialland, "land for special modes");
addsaver(viewdists, "expansion mode");
addsaver(backbrightness, "brightness behind sphere");
addsaver(vid.ipd, "interpupilar-distance", 0.05);
addsaver(vid.lr_eyewidth, "eyewidth-lr", 0.5);
addsaver(vid.anaglyph_eyewidth, "eyewidth-anaglyph", 0.1);
addsaver(vid.fov, "field-of-vision", 90);
addsaverenum(vid.stereo_mode, "stereo-mode");
addsaver(vid.euclid_to_sphere, "euclid to sphere projection", 1.5);
addsaver(vid.twopoint_param, "twopoint parameter", 1);
addsaver(vid.stretch, "stretch", 1);
addsaver(vid.binary_width, "binary-tiling-width", 1);
addsaver(gp::param.first, "goldberg-x", gp::param.first);
addsaver(gp::param.second, "goldberg-y", gp::param.second);
addsaver(nohud, "no-hud", false);
addsaver(nofps, "no-fps", false);
addsaver(irr::density, "irregular-density", 2);
addsaver(irr::cellcount, "irregular-cellcount", 150);
addsaver(irr::quality, "irregular-quality", .2);
addsaver(irr::place_attempts, "irregular-place", 10);
addsaver(irr::rearrange_max_attempts, "irregular-rearrange-max", 50);
addsaver(irr::rearrange_less, "irregular-rearrangeless", 10);
addsaver(vid.linequality, "line quality", 0);
#if CAP_FILES && CAP_SHOT
addsaver(anims::animfile, "animation file format");
#endif
addsaver(anims::period, "animation period");
addsaver(anims::noframes, "animation frames");
addsaver(anims::cycle_length, "animation cycle length");
addsaver(anims::parabolic_length, "animation parabolic length");
addsaver(anims::rug_angle, "animation rug angle");
addsaver(anims::circle_radius, "animation circle radius");
addsaver(anims::circle_spins, "animation circle spins");
addsaver(crystal::compass_probability, "compass-probability");
addsaver(crystal::view_coordinates, "crystal-coordinates");
#if CAP_SHOT
addsaver(shot::shotx, "shotx");
addsaver(shot::shoty, "shoty");
addsaver(shot::make_svg, "shotsvg");
addsaver(shot::transparent, "shottransparent");
addsaver(shot::gamma, "shotgamma");
addsaver(shot::caption, "shotcaption");
addsaver(shot::fade, "shotfade");
#endif
#if CAP_TEXTURE
addsaver(texture::texture_aura, "texture-aura", false);
#endif
addsaver(vid.use_smart_range, "smart-range", 0);
addsaver(vid.smart_range_detail, "smart-range-detail", 8);
addsaver(vid.cells_drawn_limit, "limit on cells drawn", 10000);
addsaver(vid.skiprope, "mobius", 0);
addsaver(conformal::formula, "formula");
addsaverenum(conformal::basic_model, "basic model");
addsaver(conformal::use_atan, "use_atan");
addsaver(vid.consider_shader_projection, "shader-projection", true);
#if CAP_RACING
addsaver(racing::race_advance, "race_advance");
addsaver(racing::race_angle, "race_angle");
addsaver(racing::ghosts_to_show, "race_ghosts_to_show");
addsaver(racing::ghosts_to_save, "race_ghosts_to_save");
addsaver(racing::guiding, "race_guiding");
addsaver(racing::player_relative, "race_player_relative");
addsaver(racing::standard_centering, "race_standard_centering");
#endif
#if CAP_SHMUP
shmup::initConfig();
#endif
#if CAP_CONFIG
for(auto s: savers) s->reset();
#endif
}
bool inSpecialMode() {
return chaosmode || !BITRUNCATED || peace::on ||
#if CAP_TOUR
tour::on ||
#endif
yendor::on || tactic::on || randomPatternsMode ||
geometry != gNormal || pmodel != mdDisk || vid.alpha != 1 || vid.scale != 1 ||
rug::rugged || vid.monmode != DEFAULT_MONMODE ||
vid.wallmode != DEFAULT_WALLMODE;
}
bool have_current_settings() {
int modecount = 0;
if(inv::on) modecount++;
if(shmup::on) modecount += 10;
#if CAP_TOUR
if(tour::on) modecount += 10;
#endif
if(chaosmode) modecount += 10;
if(!BITRUNCATED) modecount += 10;
if(peace::on) modecount += 10;
if(yendor::on) modecount += 10;
if(tactic::on) modecount += 10;
if(randomPatternsMode) modecount += 10;
if(geometry != gNormal) modecount += 10;
if(modecount > 1)
return true;
return false;
}
bool have_current_graph_settings() {
if(vid.xposition || vid.yposition || vid.alpha != 1 || vid.scale != 1)
return true;
if(pmodel != mdDisk || vid.monmode != DEFAULT_MONMODE || vid.wallmode != DEFAULT_WALLMODE)
return true;
if(firstland != laIce || multi::players != 1 || rug::rugged)
return true;
return false;
}
void reset_graph_settings() {
pmodel = mdDisk; vid.alpha = 1; vid.scale = 1;
vid.xposition = vid.yposition = 0;
#if CAP_RUG
if(rug::rugged) rug::close();
#endif
vid.monmode = DEFAULT_MONMODE;
vid.wallmode = DEFAULT_WALLMODE;
}
void resetModes(char leave) {
popAllGames();
if(shmup::on != (leave == rg::shmup)) stop_game_and_switch_mode(rg::shmup);
if(inv::on != (leave == rg::inv)) stop_game_and_switch_mode(rg::inv);
if(chaosmode != (leave == rg::chaos)) stop_game_and_switch_mode(rg::chaos);
if(peace::on != (leave == rg::peace)) stop_game_and_switch_mode(rg::peace);
#if CAP_TOUR
if(tour::on != (leave == rg::tour)) stop_game_and_switch_mode(rg::tour);
#endif
if(yendor::on != (leave == rg::yendor)) stop_game_and_switch_mode(rg::yendor);
if(tactic::on != (leave == rg::tactic)) stop_game_and_switch_mode(rg::tactic);
if(randomPatternsMode != (leave == rg::randpattern)) stop_game_and_switch_mode(rg::randpattern);
if(multi::players != 1) {
stop_game_and_switch_mode(); multi::players = 1;
}
if(firstland != laIce || specialland != laIce) {
stop_game();
firstland = laIce; specialland = laIce; stop_game_and_switch_mode();
}
set_geometry(gNormal);
set_variation(eVariation::bitruncated);
start_game();
}
#if CAP_CONFIG
void resetConfig() {
dynamicval<int> rx(vid.xres, 0);
dynamicval<int> ry(vid.yres, 0);
dynamicval<int> rf(vid.fsize, 0);
dynamicval<bool> rfs(vid.full, false);
for(auto s: savers)
if(s->name.substr(0,5) != "mode-")
s->reset();
}
#endif
#if CAP_CONFIG
void saveConfig() {
DEBB(DF_INIT, (debugfile,"save config\n"));
FILE *f = fopen(conffile, "wt");
if(!f) {
addMessage(s0 + "Could not open the config file: " + conffile);
return;
}
{
int pt_depth = 0, pt_camera = 0, pt_alpha = 0;
using namespace geom3;
if(tc_depth > tc_camera) pt_depth++;
if(tc_depth < tc_camera) pt_camera++;
if(tc_depth > tc_alpha ) pt_depth++;
if(tc_depth < tc_alpha ) pt_alpha ++;
if(tc_alpha > tc_camera) pt_alpha++;
if(tc_alpha < tc_camera) pt_camera++;
tc_alpha = pt_alpha;
tc_camera = pt_camera;
tc_depth = pt_depth;
}
for(auto s: savers) if(s->dosave())
fprintf(f, "%s=%s\n", s->name.c_str(), s->save().c_str());
fclose(f);
#if ISMOBILE==0
addMessage(s0 + "Configuration saved to: " + conffile);
#else
addMessage(s0 + "Configuration saved");
#endif
}
void readf(FILE *f, ld& x) {
double fl = x;
hr::ignore(fscanf(f, "%lf", &fl));
x = fl;
}
void loadOldConfig(FILE *f) {
int gl=1, aa=1, bb=1, cc, dd;
int err;
float a, b, c, d;
err=fscanf(f, "%f%f%f%f\n", &a, &b, &c, &d);
if(err == 4) {
vid.scale = a; vid.alpha = c; vid.sspeed = d;
}
err=fscanf(f, "%d%d%d%d%d%d%d", &vid.wallmode, &vid.monmode, &vid.axes, &musicvolume, &vid.framelimit, &gl, &vid.antialias);
vid.usingGL = gl;
if(vid.antialias == 0) vid.antialias = AA_VERSION | AA_LINES | AA_LINEWIDTH;
if(vid.antialias == 1) vid.antialias = AA_NOGL | AA_VERSION | AA_LINES | AA_LINEWIDTH | AA_FONT;
double jps = vid.joypanspeed;
err=fscanf(f, "%d%d%d%lf%d%d", &vid.joyvalue, &vid.joyvalue2, &vid.joypanthreshold, &jps, &aa, &vid.flashtime);
vid.joypanspeed = jps;
autojoy = aa; aa = 0;
loadcs(f, vid.cs, 0);
aa=0; bb=0;
err=fscanf(f, "%d%d", &aa, &bb);
vid.darkhepta = aa; vid.shifttarget = bb;
aa = geometry; bb = specialland; cc = shmup::on; dd = hardcore;
err=fscanf(f, "%d%d%d%d", &aa, &bb, &cc, &dd);
geometry = eGeometry(aa); specialland = eLand(bb); shmup::on = cc; hardcore = dd;
shmup::loadConfig(f);
aa = rug::renderonce; bb = rug::rendernogl; dd = chaosmode;
int ee = vid.steamscore;
#if CAP_RUG
double rs = 2/rug::model_distance;
#else
double rs = 0;
#endif
err=fscanf(f, "%d%d%d%d%lf%d%d", &aa, &bb, &rug::texturesize, &cc, &rs, &ee, &dd);
rug::renderonce = aa; rug::rendernogl = bb;
chaosmode = dd; vid.steamscore = ee;
#if CAP_RUG
rug::model_distance = 2/rs;
#endif
aa=conformal::autobandhistory;
double ps = polygonal::STAR, lv = conformal::lvspeed;
int pmb = pmodel;
err=fscanf(f, "%d%d%lf%d%d%lf",
&pmb, &polygonal::SI, &ps, &polygonal::deg,
&aa, &lv);
pmodel = eModel(pmb);
conformal::autobandhistory = aa; polygonal::STAR = ps; conformal::lvspeed = lv;
aa=conformal::autoband; bb=conformal::autobandhistory; cc=conformal::dospiral;
int crot;
err=fscanf(f, "%d%d%d%d%d%d",
&conformal::bandhalf, &conformal::bandsegment, &crot,
&aa, &bb, &cc);
conformal::autoband = aa; conformal::autobandhistory = bb; conformal::dospiral = cc;
conformal::rotation = crot * 90;
err=fscanf(f, "%d", &polygonal::maxcoef);
if(polygonal::maxcoef < 0) polygonal::maxcoef = 0;
if(polygonal::maxcoef > polygonal::MSI) polygonal::maxcoef = polygonal::MSI;
for(int i=0; i<=polygonal::maxcoef; i++) {
double re=0, im=0;
err=fscanf(f, "%lf%lf", &re, &im);
polygonal::coefr[i] = re;
polygonal::coefi[i] = im;
}
aa=vid.revcontrol; bb=vid.drawmousecircle;
d = vid.mspeed;
int sr;
err=fscanf(f, "%d%d%d%f%d%d", &aa, &bb, &sr, &d, &effvolume, &vid.particles);
vid.mspeed = d;
vid.revcontrol = aa; vid.drawmousecircle = bb;
readf(f, geom3::depth); readf(f, geom3::camera); readf(f, geom3::wall_height);
readf(f, geom3::rock_wall_ratio); readf(f, geom3::human_wall_ratio);
readf(f, geom3::lake_top); readf(f, geom3::lake_bottom);
err=fscanf(f, "%d %d %d", &geom3::tc_depth, &geom3::tc_camera, &geom3::tc_alpha);
readf(f, geom3::highdetail);
geom3::middetail = 200; readf(f, geom3::middetail);
if(geom3::middetail == 200) {
if(ISMOBILE)
geom3::highdetail = 0, geom3::middetail = 3;
else
geom3::highdetail = geom3::middetail = 5;
}
int gso = glyphsortorder; err=fscanf(f, "%d", &gso); glyphsortorder = eGlyphsortorder(gso);
readf(f, vid.yshift); readf(f, vid.camera_angle); readf(f, vid.ballangle); readf(f, vid.ballproj);
jps = vid.linewidth;
err=fscanf(f, "%d%d%d%d%lf\n", &vid.mobilecompasssize, &vid.aurastr, &vid.aurasmoothen, &vid.graphglyph, &jps);
vid.linewidth = jps;
}
map<string, shared_ptr<supersaver> > allconfigs;
void parseline(const string& str) {
if(str[0] == '#') return;
for(int i=0; i<isize(str); i++) if(str[i] == '=') {
string cname = str.substr(0, i);
if(!allconfigs.count(cname)) {
printf("Warning: unknown config variable: %s\n", str.c_str());
return;
}
auto sav = allconfigs[cname];
sav->load(str.substr(i+1));
return;
}
printf("Warning: config line without equality sign: %s\n", str.c_str());
}
void loadNewConfig(FILE *f) {
for(auto& c: savers) allconfigs[c->name] = c;
string rd;
while(true) {
int c = fgetc(f);
if(c == -1) break;
if(c == 10 || c == 13) {
if(rd != "") parseline(rd);
rd = "";
}
else rd += c;
}
allconfigs.clear();
}
void loadConfig() {
DEBB(DF_INIT, (debugfile,"load config\n"));
vid.xres = 9999; vid.yres = 9999; vid.framelimit = 300;
FILE *f = fopen(conffile, "rt");
if(f) {
int err;
int fs;
err=fscanf(f, "%d%d%d%d", &vid.xres, &vid.yres, &fs, &vid.fsize);
if(!err)
loadNewConfig(f);
else {
vid.full = fs;
loadOldConfig(f);
}
fclose(f);
DEBB(DF_INIT, (debugfile,"Loaded configuration: %s\n", conffile));
}
polygonal::solve();
precalc();
}
#endif
void showAllConfig() {
dialog::addBreak(50);
dialog::addBack();
#if CAP_CONFIG
dialog::addItem(XLAT("save the current config"), 's');
if(getcstat == 's')
mouseovers = XLAT("Config file: %1", conffile);
#endif
}
void handleAllConfig(int sym, int uni) {
if(sym == SDLK_F1 || uni == 'h') gotoHelp(help);
else if(uni == ' ' || sym == SDLK_ESCAPE) popScreen();
#if CAP_CONFIG
else if(uni == 's') saveConfig();
#endif
}
void edit_sightrange() {
if(vid.use_smart_range == 0) {
dialog::editNumber(sightrange_bonus, -5, allowIncreasedSight() ? 3 : 0, 1, 0, XLAT("sight range"),
XLAT("Roughly 42% cells are on the edge of your sight range. Reducing "
"the sight range makes HyperRogue work faster, but also makes "
"the game effectively harder."));
dialog::reaction = doOvergenerate;
dialog::bound_low(1-getDistLimit());
dialog::bound_up(allowIncreasedSight() ? gp::dist_2() * 5 : 0);
}
else {
dialog::editNumber(vid.smart_range_detail, 1, 50, 1, 8, XLAT("minimum visible cell in pixels"), "");
}
dialog::extra_options = [] () {
dialog::addBoolItem(XLAT("draw range based on distance"), vid.use_smart_range == 0, 'D');
dialog::add_action([] () { vid.use_smart_range = 0; popScreen(); edit_sightrange(); });
if(allowIncreasedSight()) {
dialog::addBoolItem(XLAT("draw based on size in the projection (no generation)"), vid.use_smart_range == 1, 'N');
dialog::add_action([] () { vid.use_smart_range = 1; popScreen(); edit_sightrange(); });
}
if(allowChangeRange() && allowIncreasedSight()) {
dialog::addBoolItem(XLAT("draw based on size in the projection (generation)"), vid.use_smart_range == 2, 'G');
dialog::add_action([] () { vid.use_smart_range = 2; popScreen(); edit_sightrange(); });
}
if(vid.use_smart_range == 0 && allowChangeRange()) {
dialog::addSelItem(XLAT("generation range bonus"), its(genrange_bonus), 'O');
dialog::add_action([] () { genrange_bonus = sightrange_bonus; doOvergenerate(); });
dialog::addSelItem(XLAT("game range bonus"), its(gamerange_bonus), 'S');
dialog::add_action([] () { gamerange_bonus = sightrange_bonus; doOvergenerate(); });
}
if(!allowChangeRange() || !allowIncreasedSight()) {
dialog::addItem(XLAT("enable the cheat mode for additional options"), 'X');
dialog::add_action(enable_cheat);
}
dialog::addSelItem(XLAT("cells drawn"), its(cells_drawn), 'C');
dialog::add_action([] () {
popScreen();
dialog::editNumber(vid.cells_drawn_limit, 100, 1000000, log(10), 10000, XLAT("limit on cells drawn"),
XLAT("This limit exists to protect the engine from freezing when too many cells would be drawn according to the current options.")
);
dialog::scaleLog();
});
};
}
void menuitem_sightrange(char c) {
if(vid.use_smart_range)
dialog::addSelItem(XLAT("minimum visible cell in pixels"), fts(vid.smart_range_detail), c);
else
dialog::addSelItem(XLAT("sight range"), its(sightrange_bonus), c);
dialog::add_action(edit_sightrange);
}
void showGraphConfig() {
cmode = vid.xres > vid.yres * 1.4 ? sm::SIDE : sm::MAYDARK;
gamescreen(0);
dialog::init(XLAT("graphics configuration"));
#if CAP_GLORNOT
dialog::addBoolItem(XLAT("openGL mode"), vid.usingGL, 'o');
#endif
if(!vid.usingGL)
dialog::addBoolItem(XLAT("anti-aliasing"), vid.antialias & AA_NOGL, 'O');
if(vid.usingGL)
dialog::addSelItem(XLAT("anti-aliasing"),
(vid.antialias & AA_POLY) ? "polygons" :
(vid.antialias & AA_LINES) ? "lines" :
(vid.antialias & AA_MULTI) ? "multisampling" :
"NO", 'O');
if(vid.usingGL) {
dialog::addSelItem(XLAT("line width"), fts(vid.linewidth), 'w');
// dialog::addBoolItem(XLAT("finer lines at the boundary"), vid.antialias & AA_LINEWIDTH, 'b');
}
else
dialog::addBreak(100);
dialog::addSelItem(XLAT("line quality"), its(vid.linequality), 'L');
#if CAP_FRAMELIMIT
dialog::addSelItem(XLAT("framerate limit"), its(vid.framelimit), 'l');
if(getcstat == 'l')
mouseovers = XLAT("Reduce the framerate limit to conserve CPU energy");
#endif
#if !ISIOS && !ISWEB
dialog::addBoolItem(XLAT("fullscreen mode"), (vid.full), 'f');
#endif
dialog::addSelItem(XLAT("scrolling speed"), fts(vid.sspeed), 'a');
dialog::addSelItem(XLAT("movement animation speed"), fts(vid.mspeed), 'm');
dialog::addBoolItem(XLAT("extra graphical effects"), (vid.particles), 'u');
dialog::addBoolItem(XLAT("background particle effects"), (vid.backeffects), 'p');
#ifdef WHATEVER
dialog::addSelItem(XLAT("whatever"), fts(whatever), 'j');
#endif
const char *glyphsortnames[6] = {
"first on top", "first on bottom",
"last on top", "last on bottom",
"by land", "by number"
};
const char *glyphmodenames[3] = {"letters", "auto", "images"};
dialog::addSelItem(XLAT("inventory/kill sorting"), XLAT(glyphsortnames[glyphsortorder]), 'k');
dialog::addSelItem(XLAT("inventory/kill mode"), XLAT(glyphmodenames[vid.graphglyph]), 'd');
dialog::addSelItem(XLAT("font scale"), its(fontscale), 'b');
menuitem_sightrange();
dialog::addSelItem(XLAT("move by clicking on compass"), its(vid.mobilecompasssize), 'C');
dialog::addItem(XLAT("customize colors and aura"), 'c');
showAllConfig();
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni == 'O') uni = 'o', shiftmul = -1;
char xuni = uni | 96;
if((uni >= 32 && uni < 64) || uni == 'L' || uni == 'C') xuni = uni;
if(xuni == 'u') vid.particles = !vid.particles;
if(xuni == 'd') vid.graphglyph = (1+vid.graphglyph)%3;
if(xuni == 'c') pushScreen(show_color_dialog);
if(xuni == 'j') {
dialog::editNumber(whatever, -10, 10, 1, 0, XLAT("whatever"),
XLAT("Whatever."));
dialog::reaction = delayed_geo_reset;
}
if(xuni == 'a') dialog::editNumber(vid.sspeed, -5, 5, 1, 0,
XLAT("scrolling speed"),
XLAT("+5 = center instantly, -5 = do not center the map")
+ "\n\n" +
XLAT("press Space or Home to center on the PC"));
if(xuni == 'm') dialog::editNumber(vid.mspeed, -5, 5, 1, 0,
XLAT("movement animation speed"),
XLAT("+5 = move instantly"));
if(xuni == 'k') {
glyphsortorder = eGlyphsortorder((glyphsortorder+6+(shiftmul>0?1:-1)) % gsoMAX);
}
if(xuni == 'f') switchFullscreen();
#if CAP_GLORNOT
if(xuni == 'o' && shiftmul > 0) switchGL();
#endif
if(xuni == 'o' && shiftmul < 0) {
if(!vid.usingGL)
vid.antialias ^= AA_NOGL | AA_FONT;
else if(vid.antialias & AA_MULTI)
vid.antialias ^= AA_MULTI;
else if(vid.antialias & AA_POLY)
vid.antialias ^= AA_POLY | AA_LINES | AA_MULTI;
else if(vid.antialias & AA_LINES)
vid.antialias |= AA_POLY;
else
vid.antialias |= AA_LINES;
#if CAP_SDL
setvideomode();
#endif
}
// if(xuni == 'b') vid.antialias ^= AA_LINEWIDTH;
if(xuni == 'w' && vid.usingGL) {
dialog::editNumber(vid.linewidth, 0, 10, 0.1, 1, XLAT("line width"), "");
dialog::extra_options = [] () {
dialog::addBoolItem("finer lines at the boundary", vid.antialias & AA_LINEWIDTH, 'O');
dialog::add_action([] () { vid.antialias ^= AA_LINEWIDTH; });
};
}
if(xuni == 'L') {
dialog::editNumber(vid.linequality, -3, 5, 1, 1, XLAT("line quality"),
XLAT("Higher numbers make the curved lines smoother, but reduce the performance."));
dialog::reaction = delayed_geo_reset;
}
if(xuni == 'C') {
dialog::editNumber(vid.mobilecompasssize, 0, 100, 10, 20, XLAT("compass size"), XLAT("0 to disable"));
// we need to check the moves
dialog::reaction = checkmove;
dialog::bound_low(0);
}
#if CAP_FRAMELIMIT
if(xuni == 'l') {
dialog::editNumber(vid.framelimit, 5, 300, 10, 300, XLAT("framerate limit"), "");
dialog::bound_low(5);
}
#endif
if(xuni =='b') {
dialog::editNumber(fontscale, 25, 400, 10, 100, XLAT("font scale"), "");
const int minfontscale = ISMOBILE ? 50 : 25;
dialog::reaction = [] () { setfsize = true; do_setfsize(); };
dialog::bound_low(minfontscale);
}
if(xuni =='p')
vid.backeffects = !vid.backeffects;
handleAllConfig(sym, xuni);
};
}
void switchFullscreen() {
vid.full = !vid.full;
#if ISANDROID
addMessage(XLAT("Reenter HyperRogue to apply this setting"));
ANDROID_SETTINGS
#endif
#if CAP_SDL
if(true) {
vid.xres = vid.full ? vid.xscr : 9999;
vid.yres = vid.full ? vid.yscr : 9999;
extern bool setfsize;
setfsize = true;
}
setvideomode();
#endif
}
void switchGL() {
vid.usingGL = !vid.usingGL;
if(vid.usingGL) addMessage(XLAT("openGL mode enabled"));
if(!vid.usingGL) addMessage(XLAT("openGL mode disabled"));
ANDROID_SETTINGS;
#if CAP_SDL
setvideomode();
if(vid.usingGL) {
glhr::be_textured(); glhr::be_nontextured();
}
#endif
}
void resetConfigMenu();
void showBasicConfig() {
gamescreen(3);
const char *axmodes[5] = {"OFF", "auto", "light", "heavy", "arrows"};
dialog::init(XLAT("basic configuration"));
if(CAP_TRANS) dialog::addSelItem(XLAT("language"), XLAT("EN"), 'l');
dialog::addSelItem(XLAT("player character"), numplayers() > 1 ? "" : csname(vid.cs), 'g');
if(getcstat == 'g')
mouseovers = XLAT("Affects looks and grammar");
if(CAP_AUDIO) {
dialog::addSelItem(XLAT("background music volume"), its(musicvolume), 'b');
dialog::addSelItem(XLAT("sound effects volume"), its(effvolume), 'e');
}
// input:
dialog::addSelItem(XLAT("help for keyboard users"), XLAT(axmodes[vid.axes]), 'c');
dialog::addBoolItem(XLAT("reverse pointer control"), (vid.revcontrol), 'r');
dialog::addBoolItem(XLAT("draw circle around the target"), (vid.drawmousecircle), 'd');
dialog::addSelItem(XLAT("message flash time"), its(vid.flashtime), 't');
dialog::addSelItem(XLAT("limit messages shown"), its(vid.msglimit), 'z');
const char* msgstyles[3] = {"centered", "left-aligned", "line-broken"};
dialog::addSelItem(XLAT("message style"), XLAT(msgstyles[vid.msgleft]), 'a');
#if ISMOBILE
dialog::addBoolItem(XLAT("targetting ranged Orbs long-click only"), (vid.shifttarget&2), 'i');
#else
dialog::addBoolItem(XLAT("targetting ranged Orbs Shift+click only"), (vid.shifttarget&1), 'i');
#endif
#if ISSTEAM
dialog::addBoolItem(XLAT("send scores to Steam leaderboards"), (vid.steamscore&1), 'x');
#endif
dialog::addBoolItem(XLAT("skip the start menu"), vid.skipstart, 'm');
#if !ISMOBILE
dialog::addBoolItem(XLAT("quick mouse"), vid.quickmouse, 'M');
#endif
dialog::addBoolItem(XLAT("forget faraway cells"), memory_saving_mode, 'y');
dialog::addSelItem(XLAT("scrolling by device rotation"), ors::choices[ors::mode], '1');
if(CAP_SHMUP && !ISMOBILE)
dialog::addSelItem(XLAT("configure keys/joysticks"), "", 'p');
#if CAP_CONFIG
dialog::addItem(XLAT("reset all configuration"), 'R');
#endif
showAllConfig();
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
char xuni = uni | 96;
if(uni >= 32 && uni < 64) xuni = uni;
if(xuni == '1') pushScreen(ors::show);
if(uni == 'M') vid.quickmouse = !vid.quickmouse;
else if(xuni == 'm') vid.skipstart = !vid.skipstart;
if(xuni == 'y') memory_saving_mode = !memory_saving_mode;
if(xuni == 'c') { vid.axes += 60 + (shiftmul > 0 ? 1 : -1); vid.axes %= 5; }
#if CAP_AUDIO
if(CAP_AUDIO && xuni == 'b') {
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);
}
if(CAP_AUDIO && xuni == 'e') {
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);
}
#endif
if(CAP_TRANS && xuni == 'l')
pushScreen(selectLanguageScreen);
if(xuni == 'g') pushScreen(showCustomizeChar);
#if CAP_SHMUP
if(xuni == 'p')
shmup::configure();
#endif
if(uni == 'r') vid.revcontrol = !vid.revcontrol;
if(xuni == 'd') vid.drawmousecircle = !vid.drawmousecircle;
#if CAP_CONFIG
if(uni == 'R') pushScreen(resetConfigMenu);
#endif
#if ISSTEAM
if(xuni == 'x') vid.steamscore = vid.steamscore^1;
#endif
if(xuni == 't') {
dialog::editNumber(vid.flashtime, 0, 64, 1, 8, XLAT("message flash time"),
XLAT("How long should the messages stay on the screen."));
dialog::bound_low(0);
}
if(xuni == 'z') {
dialog::editNumber(vid.msglimit, 0, 64, 1, 5, XLAT("limit messages shown"),
XLAT("Maximum number of messages on screen."));
dialog::bound_low(0);
}
if(xuni == 'i') { vid.shifttarget = vid.shifttarget^3; }
if(xuni == 'a') { vid.msgleft = (1+vid.msgleft) % 3; }
handleAllConfig(sym, xuni);
};
}
#if CAP_SDLJOY
void showJoyConfig() {
gamescreen(4);
dialog::init(XLAT("joystick configuration"));
dialog::addSelItem(XLAT("first joystick position (movement)"), its(joyx)+","+its(joyy), 0);
dialog::addSelItem(XLAT("second joystick position (panning)"), its(panjoyx)+","+its(panjoyy), 0);
dialog::addSelItem(XLAT("joystick mode"), XLAT(autojoy ? "automatic" : "manual"), 'p');
if(getcstat == 'p') {
if(autojoy)
mouseovers = XLAT("joystick mode: automatic (release the joystick to move)");
if(!autojoy)
mouseovers = XLAT("joystick mode: manual (press a button to move)");
}
dialog::addSelItem(XLAT("first joystick: movement threshold"), its(vid.joyvalue), 'a');
dialog::addSelItem(XLAT("first joystick: execute movement threshold"), its(vid.joyvalue2), 'b');
dialog::addSelItem(XLAT("second joystick: pan threshold"), its(vid.joypanthreshold), 'c');
dialog::addSelItem(XLAT("second joystick: panning speed"), fts(vid.joypanspeed * 1000), 'd');
dialog::addBreak(50);
dialog::addBack();
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni == 'p') autojoy = !autojoy;
else if(uni == 'a') {
dialog::editNumber(vid.joyvalue, 0, 32768, 100, 4800, XLAT("first joystick: movement threshold"), "");
dialog::bound_low(0);
}
else if(uni == 'b') {
dialog::editNumber(vid.joyvalue2, 0, 32768, 100, 5600, XLAT("first joystick: execute movement threshold"), "");
dialog::bound_low(0);
}
else if(uni == 'c') {
dialog::editNumber(vid.joypanthreshold, 0, 32768, 100, 2500, XLAT("second joystick: pan threshold"), "");
dialog::bound_low(0);
}
else if(uni == 'd')
dialog::editNumber(vid.joypanspeed, 0, 1e-2, 1e-5, 1e-4, XLAT("second joystick: panning speed"), "");
else if(doexiton(sym, uni)) popScreen();
};
}
#endif
void projectionDialog() {
geom3::tc_alpha = ticks;
dialog::editNumber(vid.alpha, -5, 5, .1, 1,
XLAT("projection"),
XLAT("HyperRogue uses the Minkowski hyperboloid model internally. "
"Klein and Poincaré models can be obtained by perspective, "
"and the Gans model is obtained by orthogonal projection. "
// "This parameter specifies the distance from the hyperboloid center "
// "to the eye. "
"See also the conformal mode (in the special modes menu) "
"for more models."));
dialog::extra_options = [] () {
dialog::addBreak(100);
dialog::addSelItem(sphere ? "stereographic" : "Poincaré model", "1", 'P');
dialog::add_action([] () { *dialog::ne.editwhat = 1; vid.scale = 1; dialog::ne.s = "1"; });
dialog::addSelItem(sphere ? "gnomonic" : "Klein model", "0", 'K');
dialog::add_action([] () { *dialog::ne.editwhat = 0; vid.scale = 1; dialog::ne.s = "0"; });
dialog::addItem(sphere ? "towards orthographic" : "towards Gans model", 'O');
dialog::add_action([] () { double d = exp(shiftmul/10); vid.alpha *= d; vid.scale *= d; dialog::ne.s = dialog::disp(vid.alpha); });
};
}
string explain3D(ld *param) {
using namespace geom3;
if(param == &highdetail || param == &middetail)
return
XLAT(
"Objects at distance less than %1 absolute units "
"from the center will be displayed with high "
"detail, and at distance at least %2 with low detail.",
fts3(highdetail), fts3(middetail)
);
if(param == &camera)
return
XLAT(
"Camera is placed %1 absolute units above a plane P in a three-dimensional "
"world. Ground level is actually an equidistant surface, %2 absolute units "
"below the plane P. The plane P (as well as the ground level or any "
"other equidistant surface below it) is viewed at an angle of %3 "
"(the tangent of the angle between the point in "
"the center of your vision and a faraway location is 1/cosh(c) = %4).",
fts3(camera),
fts3(depth),
fts3(atan(1/cosh(camera))*2/degree),
fts3(1/cosh(camera)));
if(param == &depth)
return
XLAT(
"Ground level is actually an equidistant surface, "
"%1 absolute units below the plane P. "
"Theoretically, this value affects the world -- "
"for example, eagles could fly %2 times faster by "
"flying above the ground level, on the plane P -- "
"but the actual game mechanics are not affected. "
"(Distances reported by the vector graphics editor "
"are not about points on the ground level, but "
"about the matching points on the plane P -- "
"divide them by the factor above to get actual "
"distances.)"
,
fts3(depth), fts3(cosh(depth)));
// mention absolute units
if(param == &vid.alpha)
return
XLAT(
"If we are viewing an equidistant g absolute units below a plane, "
"from a point c absolute units above the plane, this corresponds "
"to viewing a Minkowski hyperboloid from a point "
"tanh(g)/tanh(c) units below the center. This in turn corresponds to "
"the Poincaré model for g=c, and Klein-Beltrami model for g=0.");
if(param == &wall_height)
return
XLAT(
"The height of walls, in absolute units. For the current values of g and c, "
"wall height of %1 absolute units corresponds to projection value of %2.",
fts3(actual_wall_height()), fts3(factor_to_projection(geom3::WALL)));
if(param == &rock_wall_ratio)
return
XLAT(
"The ratio of Rock III to walls is %1, so Rock III are %2 absolute units high. "
"Length of paths on the Rock III level is %3 of the corresponding length on the "
"ground level.",
fts3(rock_wall_ratio), fts3(wall_height * rock_wall_ratio),
fts3(cosh(depth - wall_height * rock_wall_ratio) / cosh(depth)));
if(param == &human_wall_ratio)
return
XLAT(
"Humans are %1 "
"absolute units high. Your head travels %2 times the distance travelled by your "
"feet.",
fts3(wall_height * human_wall_ratio),
fts3(cosh(depth - wall_height * human_wall_ratio) / cosh(depth)));
return "";
}
void showStereo() {
cmode = sm::SIDE | sm::MAYDARK;
gamescreen(0);
using namespace geom3;
dialog::init(XLAT("stereo vision config"));
string modenames[4] = { "OFF", "anaglyph", "side-by-side", "ODS" };
dialog::addSelItem(XLAT("stereo mode"), XLAT(modenames[vid.stereo_mode]), 'm');
dialog::addSelItem(XLAT("pupillary distance"), fts3(vid.ipd), 'e');
switch(vid.stereo_mode) {
case sAnaglyph:
dialog::addSelItem(XLAT("distance between images"), fts(vid.anaglyph_eyewidth), 'd');
break;
case sLR:
dialog::addSelItem(XLAT("distance between images"), fts(vid.lr_eyewidth), 'd');
break;
default:
dialog::addBreak(100);
break;
}
dialog::addSelItem(XLAT("field of view"), fts(vid.fov) + "°", 'f');
dialog::addBack();
dialog::display();
keyhandler = [] (int sym, int uni) {
using namespace geom3;
dialog::handleNavigation(sym, uni);
string help3 = XLAT(
"This allows you to view the world of HyperRogue in three dimensions. "
"Best used with the Hypersian Rug mode. When used in the disk model, "
"this lets you look at the Minkowski hyperboloid (which means the "
"depth of terrain features is actually reversed). It also works with non-disk models, "
"from the conformal menu."
) + " " + XLAT(
"Currently, red-cyan anaglyph glasses and mobile VR googles are supported."
) + "\n\n";
if(uni == 'm')
{ vid.stereo_mode = eStereo((1 + vid.stereo_mode) % (CAP_ODS ? 4 : 3)); return; }
else if(uni == 'e')
dialog::editNumber(vid.ipd, -10, 10, 0.01, 0, XLAT("pupillary distance"),
help3 +
XLAT("The distance between your eyes in the represented 3D object. This is given in absolute units.")
);
else if(uni == 'd' && vid.stereo_mode == sAnaglyph)
dialog::editNumber(vid.anaglyph_eyewidth, -1, 1, 0.01, 0, XLAT("distance between images"),
help3 +
XLAT("The distance between your eyes. 1 is the width of the screen."));
else if(uni == 'd' && vid.stereo_mode == sLR)
dialog::editNumber(vid.lr_eyewidth, -1, 1, 0.01, 0, XLAT("distance between images"),
help3 +
XLAT("The distance between your eyes. 1 is the width of the screen."));
else if(uni == 'f') {
dialog::editNumber(vid.fov, 1, 170, 1, 45, "field of view",
help3 + XLAT(
"Horizontal field of view, in angles. "
"This affects the Hypersian Rug mode (even when stereo is OFF) "
"and non-disk models.")
);
dialog::bound_low(1e-8);
}
else if(doexiton(sym, uni)) popScreen();
};
}
void config_camera_rotation() {
dialog::editNumber(vid.ballangle, 0, 90, 5, 0, XLAT("camera rotation in 3D models"),
"Rotate the camera in 3D models (ball model, hyperboloid, and hemisphere). "
"Note that hyperboloid and hemisphere models are also available in the "
"Hypersian Rug surfaces menu, but they are rendered differently there -- "
"by making a flat picture first, then mapping it to a surface. "
"This makes the output better in some ways, but 3D effects are lost. "
"Hypersian Rug model also allows more camera freedom."
);
}
void show3D() {
cmode = sm::SIDE | sm::A3 | sm::MAYDARK;
gamescreen(0);
using namespace geom3;
dialog::init(XLAT("3D configuration"));
if(vid.use_smart_range == 0) {
dialog::addSelItem(XLAT("High detail range"), fts(highdetail), 'n');
dialog::addSelItem(XLAT("Mid detail range"), fts(middetail), 'm');
dialog::addBreak(50);
}
dialog::addSelItem(XLAT("Camera level above the plane"), fts3(camera), 'c');
dialog::addSelItem(XLAT("Ground level below the plane"), fts3(depth), 'g');
dialog::addSelItem(XLAT("Projection at the ground level"), fts3(vid.alpha), 'a');
dialog::addBreak(50);
dialog::addSelItem(XLAT("Height of walls"), fts3(wall_height), 'w');
dialog::addSelItem(XLAT("Rock-III to wall ratio"), fts3(rock_wall_ratio), 'r');
dialog::addSelItem(XLAT("Human to wall ratio"), fts3(human_wall_ratio), 'h');
dialog::addSelItem(XLAT("Level of water surface"), fts3(lake_top), 'l');
dialog::addSelItem(XLAT("Level of water bottom"), fts3(lake_bottom), 'k');
dialog::addBreak(50);
dialog::addSelItem(XLAT("Y shift"), fts3(vid.yshift), 'y');
dialog::addSelItem(XLAT("camera rotation"), fts3(vid.camera_angle), 's');
dialog::addSelItem(XLAT("fixed facing"), vid.fixed_facing ? fts(vid.fixed_facing_dir) : XLAT("OFF"), 'f');
dialog::add_action([] () { vid.fixed_facing = !vid.fixed_facing;
if(vid.fixed_facing) {
dialog::editNumber(vid.fixed_facing_dir, 0, 360, 15, 90, "", "");
dialog::dialogflags |= sm::CENTER;
}
});
dialog::addBreak(50);
dialog::addSelItem(XLAT("model used"), conformal::get_model_name(pmodel), 'M');
dialog::addBreak(50);
if(rug::rugged) {
dialog::addBoolItem(XLAT("3D monsters/walls on the surface"), rug::spatial_rug, 'S');
dialog::add_action([] () { rug::spatial_rug = !rug::spatial_rug; });
}
dialog::addBoolItem(XLAT("configure TPP automatically"), pmodel == mdDisk && vid.camera_angle, 'T');
dialog::add_action([] () {
if(pmodel == mdDisk && vid.camera_angle) {
vid.yshift = 0;
vid.camera_angle = 0;
vid.xposition = 0;
vid.yposition = 0;
vid.scale = 1;
vid.fixed_facing = false;
}
else {
vid.yshift = -0.3;
vid.camera_angle = -45;
vid.scale = 18/16. * vid.xres / vid.yres / multi::players;
vid.xposition = 0;
vid.yposition = -0.9;
vid.fixed_facing = true;
vid.fixed_facing_dir = 90;
}
});
if(rug::rugged && !rug::spatial_rug)
dialog::addBreak(100);
else if(non_spatial_model())
dialog::addInfo(XLAT("no 3D effects available in this projection"), 0xC00000);
else if(!spatial_graphics)
dialog::addInfo(XLAT("set 3D monsters or walls in basic config first"));
else if(invalid != "")
dialog::addInfo(XLAT("error: "+invalid), 0xC00000);
else
dialog::addInfo(XLAT("parameters set correctly"));
dialog::addBreak(50);
dialog::addItem(XLAT("stereo vision config"), 'e');
dialog::addBack();
dialog::display();
keyhandler = [] (int sym, int uni) {
using namespace geom3;
dialog::handleNavigation(sym, uni);
if(uni == 'n') {
dialog::editNumber(geom3::highdetail, 0, 5, .5, 7, XLAT("High detail range"), "");
dialog::reaction = [] () {
if(highdetail > middetail) middetail = highdetail;
};
}
else if(uni == 'm') {
dialog::editNumber(geom3::middetail, 0, 5, .5, 7, XLAT("Mid detail range"), "");
dialog::reaction = [] () {
if(highdetail > middetail) highdetail = middetail;
};
}
else if(uni == 'c')
tc_camera = ticks,
dialog::editNumber(geom3::camera, 0, 5, .1, 1, XLAT("Camera level above the plane"), ""),
dialog::reaction = delayed_geo_reset;
else if(uni == 'g')
tc_depth = ticks,
dialog::editNumber(geom3::depth, 0, 5, .1, 1, XLAT("Ground level below the plane"), ""),
dialog::reaction = delayed_geo_reset;
else if(uni == 'a')
projectionDialog();
else if(uni == 'w') {
dialog::editNumber(geom3::wall_height, 0, 1, .1, .3, XLAT("Height of walls"), "");
dialog::reaction = delayed_geo_reset;
dialog::extra_options = [] () {
dialog::addBoolItem(XLAT("auto-adjust in Goldberg grids"), geom3::gp_autoscale_heights, 'O');
dialog::add_action([] () {
geom3::gp_autoscale_heights = !geom3::gp_autoscale_heights;
buildpolys();
#if CAP_GL
resetGL();
#endif
});
};
}
else if(uni == 'l')
dialog::editNumber(geom3::lake_top, 0, 1, .1, .25, XLAT("Level of water surface"), ""),
dialog::reaction = delayed_geo_reset;
else if(uni == 'k')
dialog::editNumber(geom3::lake_bottom, 0, 1, .1, .9, XLAT("Level of water bottom"), ""),
dialog::reaction = delayed_geo_reset;
else if(uni == 'r')
dialog::editNumber(geom3::rock_wall_ratio, 0, 1, .1, .9, XLAT("Rock-III to wall ratio"), ""),
dialog::reaction = delayed_geo_reset;
else if(uni == 'h')
dialog::editNumber(geom3::human_wall_ratio, 0, 1, .1, .7, XLAT("Human to wall ratio"), ""),
dialog::reaction = delayed_geo_reset;
else if(uni == 'e')
pushScreen(showStereo);
else if(uni == 'y')
dialog::editNumber(vid.yshift, 0, 1, .1, 0, XLAT("Y shift"),
XLAT("Don't center on the player character.")
);
else if(uni == 's')
dialog::editNumber(vid.camera_angle, -180, 180, 5, 0, XLAT("camera rotation"),
XLAT("Rotate the camera. Can be used to obtain a first person perspective, "
"or third person perspective when combined with Y shift.")
);
else if(uni == 'b')
config_camera_rotation();
else if(uni == 'M')
pushScreen(conformal::model_menu);
else if(doexiton(sym, uni))
popScreen();
};
}
void switchcolor(unsigned int& c, unsigned int* cs) {
dialog::openColorDialog(c, cs);
}
double cc_footphase;
int lmousex, lmousey;
void showCustomizeChar() {
cc_footphase += hypot(mousex - lmousex, mousey - lmousey);
lmousex = mousex; lmousey = mousey;
gamescreen(4);
dialog::init(XLAT("Customize character"));
if(shmup::on || multi::players) shmup::cpid = shmup::cpid_edit % shmup::players;
charstyle& cs = getcs();
dialog::addSelItem(XLAT("character"), csname(cs), 'g');
dialog::addColorItem(XLAT("skin color"), cs.skincolor, 's');
dialog::addColorItem(XLAT("weapon color"), cs.swordcolor, 'w');
dialog::addColorItem(XLAT("hair color"), cs.haircolor, 'h');
if(cs.charid >= 1) dialog::addColorItem(XLAT("dress color"), cs.dresscolor, 'd');
else dialog::addBreak(100);
if(cs.charid == 3) dialog::addColorItem(XLAT("dress color II"), cs.dresscolor2, 'f');
else dialog::addBreak(100);
dialog::addColorItem(XLAT("movement color"), cs.uicolor, 'u');
if(!shmup::on && multi::players == 1) dialog::addSelItem(XLAT("save whom"), XLAT1(minf[moPrincess].name), 'p');
if(numplayers() > 1) dialog::addSelItem(XLAT("player"), its(shmup::cpid+1), 'a');
dialog::addBoolItem(XLAT("left-handed"), cs.lefthanded, 'l');
dialog::addBreak(50);
dialog::addBack();
dialog::display();
int firsty = dialog::items[0].position / 2;
int scale = firsty - 2 * vid.fsize;
initquickqueue();
transmatrix V = atscreenpos(vid.xres/2, firsty, scale);
double alpha = atan2(mousex - vid.xres/2, mousey - firsty) - M_PI/2;
drawMonsterType(moPlayer, NULL, V * spin(alpha), 0, cc_footphase / scale);
quickqueue();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(shmup::on || multi::players) shmup::cpid = shmup::cpid_edit % shmup::players;
charstyle& cs = getcs();
bool cat = cs.charid >= 4;
if(uni == 'a') { shmup::cpid_edit++; shmup::cpid_edit %= 60; }
else if(uni == 'g') {
cs.charid++;
if(cs.charid == 2 && !princess::everSaved && !autocheat) cs.charid = 4;
cs.charid %= 10;
}
else if(uni == 'p') vid.samegender = !vid.samegender;
else if(uni == 's') switchcolor(cs.skincolor, cat ? haircolors : skincolors);
else if(uni == 'h') switchcolor(cs.haircolor, haircolors);
else if(uni == 'w') switchcolor(cs.swordcolor, cat ? eyecolors : swordcolors);
else if(uni == 'd') switchcolor(cs.dresscolor, cat ? haircolors : dresscolors);
else if(uni == 'f') switchcolor(cs.dresscolor2, dresscolors2);
else if(uni == 'u') switchcolor(cs.uicolor, eyecolors);
else if(uni == 'l') cs.lefthanded = !cs.lefthanded;
else if(doexiton(sym, uni)) popScreen();
};
}
void refresh_canvas() {
manual_celllister cl;
cl.add(cwt.at);
int at = 0;
while(at < isize(cl.lst)) {
cell *c2 = cl.lst[at];
c2->landparam = patterns::generateCanvas(c2);
at++;
forCellEx(c3, c2) cl.add(c3);
}
}
void edit_color_table(colortable& ct, const reaction_t& r = reaction_t()) {
cmode = sm::SIDE;
gamescreen(0);
dialog::init(XLAT("customize colors and aura"));
for(int i=0; i<isize(ct); i++) {
dialog::addColorItem(its(i), ct[i] << 8, 'a'+i);
dialog::add_action([i, &ct, r] () { dialog::openColorDialog(ct[i]); dialog::reaction = r; dialog::colorAlpha = false; dialog::dialogflags |= sm::SIDE; });
}
dialog::addBack();
dialog::display();
}
void show_color_dialog() {
cmode = sm::SIDE | sm::DIALOG_STRICT_X;
getcstat = '-';
gamescreen(0);
dialog::init(XLAT("customize colors and aura"));
dialog::addColorItem(XLAT("background"), backcolor << 8, 'b');
dialog::add_action([] () { dialog::openColorDialog(backcolor); dialog::colorAlpha = false; dialog::dialogflags |= sm::SIDE; });
dialog::addColorItem(XLAT("foreground"), forecolor << 8, 'f');
dialog::add_action([] () { dialog::openColorDialog(forecolor); dialog::colorAlpha = false; dialog::dialogflags |= sm::SIDE; });
dialog::addColorItem(XLAT("borders"), bordcolor << 8, 'o');
dialog::add_action([] () { dialog::openColorDialog(bordcolor); dialog::colorAlpha = false; dialog::dialogflags |= sm::SIDE; });
dialog::addColorItem(XLAT("projection boundary"), ringcolor, 'r');
dialog::add_action([] () { dialog::openColorDialog(ringcolor); dialog::dialogflags |= sm::SIDE; });
dialog::addColorItem(XLAT("projection background"), modelcolor, 'c');
dialog::add_action([] () { dialog::openColorDialog(modelcolor); dialog::dialogflags |= sm::SIDE; });
dialog::addSelItem(XLAT("brightness behind the sphere"), fts3(backbrightness), 'i');
dialog::add_action([] () { dialog::editNumber(backbrightness, 0, 1, .01, 0.25, XLAT("brightness behind the sphere"), "brightness behind the sphere"); dialog::bound_low(0); });
dialog::addColorItem(XLAT("projection period"), periodcolor, 'p');
dialog::add_action([] () { dialog::openColorDialog(periodcolor); dialog::dialogflags |= sm::SIDE; });
dialog::addColorItem(XLAT("dialogs"), dialog::dialogcolor << 8, 'd');
dialog::add_action([] () { dialog::openColorDialog(dialog::dialogcolor); dialog::colorAlpha = false; dialog::dialogflags |= sm::SIDE; });
dialog::addBreak(50);
if(specialland == laCanvas && colortables.count(patterns::whichCanvas)) {
dialog::addItem(XLAT("pattern colors"), 'P');
dialog::add_action([] () { pushScreen([] () { edit_color_table(colortables[patterns::whichCanvas], refresh_canvas); });});
}
if(cwt.at->land == laMinefield) {
dialog::addItem(XLAT("minefield colors"), 'm');
dialog::add_action([] () { pushScreen([] () { edit_color_table(minecolors); });});
}
if(viewdists) {
dialog::addItem(XLAT("distance colors"), 'd');
dialog::add_action([] () { pushScreen([] () { edit_color_table(distcolors); });});
}
if(geometry == gCrystal && cheater) {
dialog::addItem(XLAT("crystal coordinate colors"), 'C');
dialog::add_action([] () { crystal::view_coordinates = true; pushScreen([] () { edit_color_table(crystal::coordcolors); });});
}
dialog::addInfo(XLAT("colors of some game objects can be edited by clicking them."));
dialog::addBreak(50);
dialog::addSelItem(XLAT("aura brightness"), its(vid.aurastr), 'a');
dialog::add_action([] () { dialog::editNumber(vid.aurastr, 0, 256, 10, 128, XLAT("aura brightness"), ""); dialog::bound_low(0); });
dialog::addSelItem(XLAT("aura smoothening factor"), its(vid.aurasmoothen), 's');
dialog::add_action([] () { dialog::editNumber(vid.aurasmoothen, 1, 180, 1, 5, XLAT("aura smoothening factor"), ""); dialog::bound_low(1); });
dialog::addBreak(50);
dialog::addBack();
dialog::display();
keyhandler = [] (int sym, int uni) {
if(uni == '-') {
cell *c = mouseover;
if(!c) return;
else if(c == cwt.at) {
pushScreen(showCustomizeChar);
return;
}
else if(c->monst)
dialog::openColorDialog(minf[c->monst].color);
else if(c->item)
dialog::openColorDialog(iinf[c->item].color);
else if(c->wall)
dialog::openColorDialog(winf[c->wall == waMineMine ? waMineUnknown : c->wall].color);
else if(c->land == laBrownian)
dialog::openColorDialog(brownian::get_color_edit(c->landparam));
else
dialog::openColorDialog(floorcolors[c->land]);
dialog::colorAlpha = false;
dialog::dialogflags |= sm::SIDE;
return;
}
else dialog::handleNavigation(sym, uni);
if(doexiton(sym, uni)) popScreen();
};
}
#if CAP_CONFIG
void resetConfigMenu() {
dialog::init(XLAT("reset all configuration"));
dialog::addInfo("Are you sure?");
dialog::addItem("yes, and delete the config file", 'd');
dialog::addItem("yes", 'y');
dialog::addItem("cancel", 'n');
dialog::addItem("reset the special game modes", 'r');
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni == 'd') {
resetConfig();
unlink(conffile);
popScreen();
}
else if(uni == 'y') {
printf("reseting config\n");
resetConfig();
printf("config reset\n");
popScreen();
}
else if(uni == 'r')
resetModes();
else if(uni == 'n' || doexiton(sym, uni))
popScreen();
};
}
#endif
#if CAP_TRANS
void selectLanguageScreen() {
gamescreen(4);
dialog::init("select language"); // intentionally not translated
int v = vid.language;
dynamicval<int> d(vid.language, -1);
for(int i=0; i<NUMLAN-1 || i == v; i++) {
vid.language = i;
dialog::addSelItem(XLAT("EN"), its(100 * transcompleteness[i] / transcompleteness[0]) + "%", 'a'+i);
}
dialog::addBreak(50);
vid.language = -1;
dialog::addBoolItem(XLAT("default") + ": " + XLAT("EN"), v == -1, '0');
dialog::addBack();
dialog::addBreak(50);
vid.language = v;
if(lang() >= 1)
dialog::addHelp(XLAT("add credits for your translation here"));
else
dialog::addHelp(XLAT("original language"));
if(lang() != 0) {
string tw = "";
string s = XLAT("TRANSLATIONWARNING");
if(s != "" && s != "TRANSLATIONWARNING") tw += s;
s = XLAT("TRANSLATIONWARNING2");
if(s != "" && s != "TRANSLATIONWARNING2") { if(tw != "") tw += " "; tw += s; }
if(tw != "") {
dialog::addHelp(tw);
dialog::lastItem().color = 0xFF0000;
}
}
dialog::display();
keyhandler = [] (int sym, int uni) {
dialog::handleNavigation(sym, uni);
if(uni == '0') {
vid.language = -1;
ANDROID_SETTINGS;
}
else if(uni >= 'a' && uni < 'a'+NUMLAN) {
vid.language = uni - 'a';
ANDROID_SETTINGS;
}
else if(doexiton(sym, uni))
popScreen();
};
}
#endif
#if CAP_COMMANDLINE
int read_color_args() {
using namespace arg;
if(argis("-back")) {
PHASEFROM(2); shift(); backcolor = arghex();
}
else if(argis("-fillmodel")) {
PHASEFROM(2); shift(); modelcolor = arghex();
}
else if(argis("-ring")) {
PHASEFROM(2); shift(); ringcolor = arghex();
}
else if(argis("-period")) {
PHASEFROM(2); shift(); periodcolor = arghex();
}
else if(argis("-borders")) {
PHASEFROM(2); shift(); bordcolor = arghex();
}
else if(argis("-fore")) {
PHASEFROM(2); shift(); forecolor = arghex();
}
else if(argis("-dialog")) {
PHASEFROM(2); shift(); dialog::dialogcolor = arghex();
}
else if(argis("-d:color"))
launch_dialog(show_color_dialog);
else return 1;
return 0;
}
int read_config_args() {
using namespace arg;
if(argis("-c")) { PHASE(1); shift(); conffile = argcs(); }
// change the configuration from the command line
else if(argis("-aa")) { PHASEFROM(2); shift(); vid.antialias = argi(); }
else if(argis("-lw")) { PHASEFROM(2); shift_arg_formula(vid.linewidth); }
else if(argis("-wm")) { PHASEFROM(2); shift(); vid.wallmode = argi(); }
else if(argis("-mm")) { PHASEFROM(2); shift(); vid.monmode = argi(); }
else if(argis("-noshadow")) { noshadow = true; }
else if(argis("-bright")) { bright = true; }
// non-configurable options
else if(argis("-vsync_off")) {
vsync_off = true;
if(curphase == 3) setvideomode();
}
else if(argis("-aura")) {
PHASEFROM(2);
shift(); vid.aurastr = argi();
shift(); vid.aurasmoothen = argi();
}
else if(argis("-nofps")) {
PHASEFROM(2);
nofps = true;
}
else if(argis("-nohud")) {
PHASEFROM(2);
nohud = true;
}
else if(argis("-nomenu")) {
PHASEFROM(2);
nomenukey = true;
}
else if(argis("-nohelp")) {
PHASEFROM(2);
nohelp = true;
}
else if(argis("-dont_face_pc")) {
PHASEFROM(2);
dont_face_pc = true;
}
#if CAP_TRANS
else if(argis("-lang")) {
PHASEFROM(2); shift(); vid.language = argi();
}
#endif
else if(argis("-vlq")) {
PHASEFROM(2); shift(); vid.linequality = argi();
}
else if(argis("-r")) {
PHASEFROM(2);
shift();
int clWidth=0, clHeight=0, clFont=0;
sscanf(argcs(), "%dx%dx%d", &clWidth, &clHeight, &clFont);
if(clWidth) vid.xres = clWidth;
if(clHeight) vid.yres = clHeight;
if(clFont) vid.fsize = clFont;
}
else if(argis("-msm")) {
PHASEFROM(2); memory_saving_mode = true;
}
else if(argis("-yca")) {
PHASEFROM(2);
shift_arg_formula(vid.yshift);
shift_arg_formula(vid.camera_angle);
}
else if(argis("-xy")) {
PHASEFROM(2);
shift_arg_formula(vid.xposition);
shift_arg_formula(vid.yposition);
}
else if(argis("-fixdir")) {
PHASEFROM(2);
vid.fixed_facing = true;
shift_arg_formula(vid.fixed_facing_dir);
}
else if(argis("-fixdiroff")) {
PHASEFROM(2);
vid.fixed_facing = false;
}
else if(argis("-msmoff")) {
PHASEFROM(2); memory_saving_mode = false;
}
TOGGLE('o', vid.usingGL, switchGL())
TOGGLE('f', vid.full, switchFullscreen())
else if(argis("-d:sight")) {
PHASEFROM(2); launch_dialog(); edit_sightrange();
}
else if(argis("-d:char")) {
PHASEFROM(2); launch_dialog(showCustomizeChar);
}
else if(argis("-d:3")) {
PHASEFROM(2); launch_dialog(show3D);
}
else if(argis("-d:stereo")) {
PHASEFROM(2); launch_dialog(showStereo);
}
else if(argis("-d:basic")) {
PHASEFROM(2); launch_dialog(showBasicConfig);
}
else if(argis("-d:graph")) {
PHASEFROM(2); launch_dialog(showGraphConfig);
}
else return 1;
return 0;
}
// mode changes:
int read_gamemode_args() {
using namespace arg;
if(argis("-P")) {
PHASE(2); shift();
stop_game_and_switch_mode(rg::nothing);
multi::players = argi();
}
TOGGLE('C', chaosmode, stop_game_and_switch_mode(rg::chaos))
TOGGLE('S', shmup::on, stop_game_and_switch_mode(rg::shmup))
TOGGLE('H', hardcore, switchHardcore())
TOGGLE('R', randomPatternsMode, stop_game_and_switch_mode(rg::randpattern))
TOGGLE('i', inv::on, stop_game_and_switch_mode(rg::inv))
else return 1;
return 0;
}
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
unordered_map<string, ld&> params = {
{"linewidth", vid.linewidth},
{"patternlinewidth", linepatterns::width},
{"scale", vid.scale},
{"xposition", vid.xposition},
{"yposition", vid.yposition},
{"projection", vid.alpha},
{"sspeed", vid.sspeed},
{"mspeed", vid.mspeed},
{"ballangle", vid.ballangle},
{"yshift", vid.yshift},
{"cameraangle", vid.camera_angle},
{"depth", geom3::depth},
{"camera", geom3::camera},
{"wall_height", geom3::wall_height},
{"highdetail", geom3::highdetail},
{"middetail", geom3::middetail},
{"rock_wall_ratio", geom3::rock_wall_ratio},
{"human_wall_ratio", geom3::human_wall_ratio},
{"lake_top", geom3::lake_top},
{"lake_bottom", geom3::lake_bottom},
{"rug_model_distance", rug::model_distance},
{"star", polygonal::STAR},
{"lvspeed", conformal::lvspeed},
{"rotation", conformal::rotation},
{"mori", conformal::model_orientation},
{"topz", conformal::top_z},
{"mtrans", conformal::model_transition},
{"hp", conformal::halfplane_scale},
{"back", backbrightness},
{"ipd", vid.ipd},
{"lr", vid.lr_eyewidth},
{"anaglyph", vid.anaglyph_eyewidth},
{"fov", vid.fov},
{"ets", vid.euclid_to_sphere},
{"stretch", vid.stretch},
{"twopoint", vid.twopoint_param},
{"bwidth", vid.binary_width},
{"aperiod", anims::period},
{"acycle", anims::cycle_length},
{"aparabolic", anims::parabolic_length},
{"arugangle", anims::rug_angle},
{"acradius", anims::circle_radius},
{"acspins", anims::circle_spins},
{"mobius", vid.skiprope},
{"a", anims::a},
{"b", anims::b},
{"sang", conformal::spiral_angle},
{"spiralx", conformal::spiral_x},
{"spiraly", conformal::spiral_y},
{"cprob", crystal::compass_probability},
#if CAP_SHOT
{"gamma", shot::gamma},
{"fade", shot::fade},
#endif
};
}