1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-11-15 17:54:48 +00:00
hyperrogue/config.cpp
2019-09-12 22:38:45 +02:00

2110 lines
72 KiB
C++

// Hyperbolic Rogue -- configuration
// Copyright (C) 2017-2018 Zeno Rogue, see 'hyper.cpp' for details
namespace hr {
const char *conffile = "hyperrogue.ini";
array<ld, gGUARD> sightranges;
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.radarsize, "radarsize size", 120);
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(stdgridcolor, "color:stdgrid");
addsaver(dialog::dialogcolor, "color:dialog");
for(auto& p: colortables)
savecolortable(p.second, s0+"canvas"+p.first);
savecolortable(distcolors, "distance");
savecolortable(minecolors, "mines");
#if CAP_COMPLEX2
savecolortable(brownian::colors, "color:brown");
#endif
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);
addsaver(vid.desaturate, "desaturate", 0);
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(geom3::creature_scale, "3d-creaturescale", 1);
addsaver(geom3::height_width, "3d-heightwidth", 1.5);
#if CAP_GP
addsaver(gp::param.first, "goldberg-x", gp::param.first);
addsaver(gp::param.second, "goldberg-y", gp::param.second);
#endif
addsaver(nohud, "no-hud", false);
addsaver(nofps, "no-fps", false);
#if CAP_IRR
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);
#endif
addsaver(vid.linequality, "line quality", 0);
#if CAP_FILES && CAP_SHOT && CAP_ANIMATIONS
addsaver(anims::animfile, "animation file format");
#endif
#if CAP_ANIMATIONS
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");
#endif
#if CAP_CRYSTAL
addsaver(crystal::compass_probability, "compass-probability");
addsaver(crystal::view_coordinates, "crystal-coordinates");
#endif
#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(sightranges[gBinary3], "sight-binary3", 3);
addsaver(sightranges[gCubeTiling], "sight-cubes", 7);
addsaver(sightranges[gCell120], "sight-120cell", 2 * M_PI);
addsaver(sightranges[gECell120], "sight-120cell-elliptic", M_PI);
addsaver(sightranges[gRhombic3], "sight-rhombic", 5.5);
addsaver(sightranges[gBitrunc3], "sight-bitrunc", 4.5);
addsaver(sightranges[gSpace534], "sight-534", 3);
addsaver(sightranges[gSpace435], "sight-435", 3);
addsaver(sightranges[gCell5], "sight-5cell", 2 * M_PI);
addsaver(sightranges[gCell8], "sight-8cell", 2 * M_PI);
addsaver(sightranges[gECell8], "sight-8cell-elliptic", M_PI);
addsaver(sightranges[gCell16], "sight-16cell", 2 * M_PI);
addsaver(sightranges[gECell16], "sight-16cell-elliptic", M_PI);
addsaver(sightranges[gCell24], "sight-24cell", 2 * M_PI);
addsaver(sightranges[gECell24], "sight-24cell-elliptic", M_PI);
addsaver(sightranges[gCell600], "sight-600cell", 2 * M_PI);
addsaver(sightranges[gECell600], "sight-600cell-elliptic", M_PI);
addsaver(sightranges[gHoroTris], "sight-horotris", 3);
addsaver(sightranges[gHoroRec], "sight-hororec", 2);
addsaver(smooth_scrolling, "smooth-scrolling", false);
addsaver(mouseaim_sensitivity, "mouseaim_sensitivity", 0.01);
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 add_cells_drawn(char c = 'C') {
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 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);
}
add_cells_drawn('C');
};
}
void menuitem_sightrange(char c) {
if(DIM == 3) {
dialog::addSelItem(XLAT("3D sight range"), fts(sightranges[geometry]), c);
dialog::add_action([] {
dialog::editNumber(sightranges[geometry], 0, 2 * M_PI, 0.5, M_PI, XLAT("sight range"),
XLAT(
"Sight range for 3D geometries is specified in the absolute units. This value also affects the fog effect.\n\n"
"In spherical geometries, the sight range of 2pi will let you see things behind you as if they were in front of you, "
"and the sight range of pi (or more) will let you see things on the antipodal point just as if they were close to you.\n\n"
"In hyperbolic geometries, the number of cells to render depends exponentially on the sight range. More cells to drawn "
"reduces the performance.\n\n"
"Sight range affects the gameplay, and monsters act iff they are visible. Monster generation takes this into account."
)
);
dialog::extra_options = [] {
add_cells_drawn('C');
dialog::addSelItem(XLAT("limit generation"), fts(extra_generation_distance), 'G');
dialog::add_action([] {
dialog::editNumber(extra_generation_distance, 0, 999, 0.5, 999, XLAT("limit generation"),
"Cells over this distance will not be generated, but they will be drawn if they are already generated and in the sight range."
);
});
};
});
}
else {
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 = 1.1; 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 add_edit_fov(char key = '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",
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);
dialog::bound_up(179);
});
}
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("desaturate colors"), its(vid.desaturate)+"%", 'c');
dialog::add_action([] {
dialog::editNumber(vid.desaturate, 0, 100, 10, 0, XLAT("remove standard colors"),
XLAT("Make the game colors less saturated. This is useful in the anaglyph mode.")
);
});
add_edit_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(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 && DIM == 2) {
dialog::addSelItem(XLAT("High detail range"), fts(highdetail), 'n');
dialog::addSelItem(XLAT("Mid detail range"), fts(middetail), 'm');
dialog::addBreak(50);
}
if(DIM == 2) {
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');
}
else {
dialog::addSelItem(XLAT("Creature scale"), fts3(creature_scale), 'c');
dialog::addSelItem(XLAT("Height to width"), fts3(height_width), 'h');
menuitem_sightrange('s');
}
dialog::addBreak(50);
dialog::addSelItem(XLAT(DIM == 2 ? "Y shift" : "third person perspective"), fts3(vid.yshift), 'y');
if(DIM == 3) {
dialog::addSelItem(XLAT("mouse aiming sensitivity"), fts(mouseaim_sensitivity), 'a');
dialog::add_action([] () {
dialog::editNumber(mouseaim_sensitivity, -1, 1, 0.002, 0.01, XLAT("mouse aiming sensitivity"), "set to 0 to disable");
});
}
if(DIM == 2) dialog::addSelItem(XLAT("camera rotation"), fts3(vid.camera_angle), 's');
if(DIM == 2) {
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;
}
});
}
if(DIM == 2) {
dialog::addBreak(50);
dialog::addSelItem(XLAT("model used"), conformal::get_model_name(pmodel), 'M');
}
if(DIM == 3) add_edit_fov('f');
if(DIM == 3) {
dialog::addSelItem(XLAT("radar size"), fts3(vid.radarsize), 'r');
dialog::add_action([] () {
dialog::editNumber(vid.radarsize, 0, 360, 15, 90, "", "");
});
}
dialog::addBreak(50);
#if CAP_RUG
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; });
}
#endif
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(0);
#if CAP_RUG
else if(rug::rugged && !rug::spatial_rug)
dialog::addBreak(100);
#endif
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' && DIM == 2) {
dialog::editNumber(geom3::highdetail, 0, 5, .5, 7, XLAT("High detail range"), "");
dialog::reaction = [] () {
if(highdetail > middetail) middetail = highdetail;
};
}
else if(uni == 'm' && DIM == 2) {
dialog::editNumber(geom3::middetail, 0, 5, .5, 7, XLAT("Mid detail range"), "");
dialog::reaction = [] () {
if(highdetail > middetail) highdetail = middetail;
};
}
else if(uni == 'c' && DIM == 2)
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' && DIM == 2)
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' && DIM == 2)
projectionDialog();
else if(uni == 'w' && DIM == 2) {
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' && DIM == 2)
dialog::editNumber(geom3::lake_top, 0, 1, .1, .25, XLAT("Level of water surface"), ""),
dialog::reaction = delayed_geo_reset;
else if(uni == 'k' && DIM == 2)
dialog::editNumber(geom3::lake_bottom, 0, 1, .1, .9, XLAT("Level of water bottom"), ""),
dialog::reaction = delayed_geo_reset;
else if(uni == 'r' && DIM == 2)
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' && DIM == 2)
dialog::editNumber(geom3::human_wall_ratio, 0, 1, .1, .7, XLAT("Human to wall ratio"), ""),
dialog::reaction = delayed_geo_reset;
else if(uni == 'h' && DIM == 3)
dialog::editNumber(geom3::height_width, 0, 1, .1, .7, XLAT("Height to width"), ""),
dialog::reaction = delayed_geo_reset;
else if(uni == 'c' && DIM == 3)
dialog::editNumber(geom3::creature_scale, 0, 1, .1, .7, XLAT("Creature scale"), ""),
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::addColorItem(XLAT("standard grid color"), stdgridcolor, 'g');
dialog::add_action([] () { vid.grid = true; dialog::openColorDialog(stdgridcolor); 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 CAP_CRYSTAL
if(geometry == gCrystal && cheater) {
dialog::addItem(XLAT("crystal coordinate colors"), 'C');
dialog::add_action([] () { crystal::view_coordinates = true; pushScreen([] () { edit_color_table(crystal::coordcolors); });});
}
#endif
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);
#if CAP_COMPLEX2
else if(c->land == laBrownian)
dialog::openColorDialog(brownian::get_color_edit(c->landparam));
#endif
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("-stdgrid")) {
PHASEFROM(2); shift(); stdgridcolor = 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;
}
else if(argis("-msens")) {
PHASEFROM(2); shift_arg_formula(mouseaim_sensitivity);
}
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},
#if CAP_RUG
{"rug_model_distance", rug::model_distance},
#endif
{"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},
#if CAP_ANIMATIONS
{"aperiod", anims::period},
{"acycle", anims::cycle_length},
{"aparabolic", anims::parabolic_length},
{"arugangle", anims::rug_angle},
{"acradius", anims::circle_radius},
{"acspins", anims::circle_spins},
{"a", anims::a},
{"b", anims::b},
#endif
{"mobius", vid.skiprope},
{"sang", conformal::spiral_angle},
{"spiralx", conformal::spiral_x},
{"spiraly", conformal::spiral_y},
#if CAP_CRYSTAL
{"cprob", crystal::compass_probability},
#endif
#if CAP_SHOT
{"gamma", shot::gamma},
{"fade", shot::fade},
#endif
};
}