mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-27 22:39:53 +00:00
2498 lines
88 KiB
C++
2498 lines
88 KiB
C++
// Hyperbolic Rogue -- configuration
|
|
// Copyright (C) 2017-2018 Zeno Rogue, see 'hyper.cpp' for details
|
|
|
|
/** \file config.cpp
|
|
* \brief Configuration -- initial settings, saving/loading ini files, menus, etc.
|
|
*/
|
|
|
|
#include "hyper.h"
|
|
namespace hr {
|
|
|
|
#if HDR
|
|
struct supersaver {
|
|
string name;
|
|
virtual string save() = 0;
|
|
virtual void load(const string& s) = 0;
|
|
virtual bool dosave() = 0;
|
|
virtual void reset() = 0;
|
|
virtual ~supersaver() {};
|
|
};
|
|
|
|
typedef vector<shared_ptr<supersaver>> saverlist;
|
|
|
|
extern saverlist savers;
|
|
|
|
#if CAP_CONFIG
|
|
|
|
template<class T> struct dsaver : supersaver {
|
|
T& val;
|
|
T dft;
|
|
bool dosave() { return val != dft; }
|
|
void reset() { val = dft; }
|
|
dsaver(T& val) : val(val) { }
|
|
};
|
|
|
|
template<class T> struct saver : dsaver<T> {};
|
|
|
|
template<class T, class U, class V> void addsaver(T& i, U name, V dft) {
|
|
auto s = make_shared<saver<T>> (i);
|
|
s->dft = dft;
|
|
s->name = name;
|
|
savers.push_back(s);
|
|
}
|
|
|
|
template<class T> void addsaver(T& i, string name) {
|
|
addsaver(i, name, i);
|
|
}
|
|
|
|
template<class T> struct saverenum : supersaver {
|
|
T& val;
|
|
T dft;
|
|
bool dosave() { return val != dft; }
|
|
void reset() { val = dft; }
|
|
saverenum<T>(T& v) : val(v) { }
|
|
string save() { return its(int(val)); }
|
|
void load(const string& s) { val = (T) atoi(s.c_str()); }
|
|
};
|
|
|
|
template<class T, class U> void addsaverenum(T& i, U name, T dft) {
|
|
auto s = make_shared<saverenum<T>> (i);
|
|
s->dft = dft;
|
|
s->name = name;
|
|
savers.push_back(s);
|
|
}
|
|
|
|
template<class T, class U> void addsaverenum(T& i, U name) {
|
|
addsaverenum(i, name, i);
|
|
}
|
|
|
|
template<> struct saver<int> : dsaver<int> {
|
|
saver<int>(int& val) : dsaver<int>(val) { }
|
|
string save() { return its(val); }
|
|
void load(const string& s) { val = atoi(s.c_str()); }
|
|
};
|
|
|
|
template<> struct saver<char> : dsaver<char> {
|
|
saver<char>(char& val) : dsaver<char>(val) { }
|
|
string save() { return its(val); }
|
|
void load(const string& s) { val = atoi(s.c_str()); }
|
|
};
|
|
|
|
template<> struct saver<bool> : dsaver<bool> {
|
|
saver<bool>(bool& val) : dsaver<bool>(val) { }
|
|
string save() { return val ? "yes" : "no"; }
|
|
void load(const string& s) { val = isize(s) && s[0] == 'y'; }
|
|
};
|
|
|
|
template<> struct saver<unsigned> : dsaver<unsigned> {
|
|
saver<unsigned>(unsigned& val) : dsaver<unsigned>(val) { }
|
|
string save() { return itsh(val); }
|
|
void load(const string& s) { val = (unsigned) strtoll(s.c_str(), NULL, 16); }
|
|
};
|
|
|
|
template<> struct saver<string> : dsaver<string> {
|
|
saver<string>(string& val) : dsaver<string>(val) { }
|
|
string save() { return val; }
|
|
void load(const string& s) { val = s; }
|
|
};
|
|
|
|
template<> struct saver<ld> : dsaver<ld> {
|
|
saver<ld>(ld& val) : dsaver<ld>(val) { }
|
|
string save() { return fts(val, 10); }
|
|
void load(const string& s) {
|
|
if(s == "0.0000000000e+000") ; // ignore!
|
|
else val = atof(s.c_str());
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#endif
|
|
|
|
EX ld bounded_mine_percentage = 0.1;
|
|
EX int bounded_mine_quantity, bounded_mine_max;
|
|
|
|
EX const char *conffile = "hyperrogue.ini";
|
|
|
|
EX array<ld, gGUARD> sightranges;
|
|
|
|
EX 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];
|
|
|
|
EX charstyle& getcs(int id IS(multi::cpid)) {
|
|
if(multi::players>1 && id >= 0 && id < multi::players)
|
|
return multi::scs[id];
|
|
else
|
|
return vid.cs;
|
|
}
|
|
|
|
struct charstyle_old {
|
|
int charid;
|
|
color_t skincolor, haircolor, dresscolor, swordcolor, dresscolor2, uicolor;
|
|
bool lefthanded;
|
|
};
|
|
|
|
EX void hread(hstream& hs, charstyle& cs) {
|
|
// before 0xA61A there was no eyecolor
|
|
if(hs.get_vernum() < 0xA61A) {
|
|
charstyle_old cso;
|
|
hread_raw(hs, cso);
|
|
cs.charid = cso.charid;
|
|
cs.skincolor = cso.skincolor;
|
|
cs.haircolor = cso.haircolor;
|
|
cs.dresscolor = cso.dresscolor;
|
|
cs.swordcolor = cs.eyecolor = cso.swordcolor;
|
|
if(cs.charid < 4) cs.eyecolor = 0;
|
|
cs.dresscolor2 = cso.dresscolor2;
|
|
cs.uicolor = cso.uicolor;
|
|
cs.lefthanded = cso.lefthanded;
|
|
}
|
|
else hread_raw(hs, cs);
|
|
}
|
|
|
|
EX void hwrite(hstream& hs, const charstyle& cs) {
|
|
hwrite_raw(hs, cs);
|
|
}
|
|
|
|
// void hread(hstream& hs, charstyle& cs) { hread_raw(hs, cs); }
|
|
// void hwrite(hstream& hs, const charstyle& cs) { hwrite_raw(hs, cs); }
|
|
|
|
EX 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");
|
|
}
|
|
|
|
EX string csname(charstyle& cs) {
|
|
return csnameid(cs.charid);
|
|
}
|
|
|
|
EX int playergender() {
|
|
return (getcs().charid >= 0 && (getcs().charid&1)) ? GEN_F : GEN_M;
|
|
}
|
|
EX int princessgender() {
|
|
int g = playergender();
|
|
if(vid.samegender) return g;
|
|
return g == GEN_M ? GEN_F : GEN_M;
|
|
}
|
|
|
|
EX int default_language;
|
|
|
|
EX int lang() {
|
|
if(vid.language >= 0)
|
|
return vid.language;
|
|
return default_language;
|
|
}
|
|
|
|
EX 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
|
|
|
|
EX void addsaver(charstyle& cs, string s) {
|
|
addsaver(cs.charid, s + ".charid");
|
|
addsaver(cs.skincolor, s + ".skincolor");
|
|
addsaver(cs.eyecolor, s + ".eyecolor");
|
|
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, 0x804010FF, 0x00C000FF };
|
|
|
|
EX 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.eyecolor = 0x603000FF;
|
|
cs.lefthanded = false;
|
|
}
|
|
|
|
EX void savecolortable(colortable& ct, string name) {
|
|
for(int i=0; i<isize(ct); i++)
|
|
addsaver(ct[i], "color:" + name + ":" + its(i));
|
|
}
|
|
|
|
EX 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.radarrange, "radarrange", 2.5);
|
|
addsaver(vid.axes, "movement help", 1);
|
|
addsaver(vid.axes3, "movement help3", false);
|
|
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 | (ISWEB ? AA_MULTI : 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.use_wall_radar, "wallradar", true);
|
|
addsaver(vid.fixed_facing, "fixed facing", 0);
|
|
addsaver(vid.fixed_yz, "fixed YZ", true);
|
|
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(vid.depth, "3D depth", 1);
|
|
addsaver(vid.camera, "3D camera level", 1);
|
|
addsaver(vid.wall_height, "3D wall height", .3);
|
|
addsaver(vid.rock_wall_ratio, "3D rock-wall ratio", .9);
|
|
addsaver(vid.human_wall_ratio, "3D human-wall ratio", .7);
|
|
addsaver(vid.lake_top, "3D lake top", .25);
|
|
addsaver(vid.lake_bottom, "3D lake bottom", .9);
|
|
addsaver(vid.tc_depth, "3D TC depth", 1);
|
|
addsaver(vid.tc_camera, "3D TC camera", 2);
|
|
addsaver(vid.tc_alpha, "3D TC alpha", 3);
|
|
addsaver(vid.highdetail, "3D highdetail", 8);
|
|
addsaver(vid.middetail, "3D middetail", 8);
|
|
addsaver(vid.gp_autoscale_heights, "3D Goldberg autoscaling", true);
|
|
addsaver(vid.always3, "3D always", false);
|
|
|
|
addsaver(vid.eye, "eyelevel", 0);
|
|
addsaver(vid.auto_eye, "auto-eyelevel", false);
|
|
|
|
addsaver(memory_saving_mode, "memory_saving_mode", (ISMOBILE || ISPANDORA || ISWEB) ? 1 : 0);
|
|
addsaver(reserve_limit, "memory_reserve", 128);
|
|
addsaver(show_memory_warning, "show_memory_warning");
|
|
|
|
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", mdDisk);
|
|
addsaver(polygonal::SI, "polygon sides");
|
|
addsaver(polygonal::STAR, "polygon star factor");
|
|
addsaver(polygonal::deg, "polygonal degree");
|
|
addsaver(history::autobandhistory, "include history"); // check!
|
|
addsaver(history::lvspeed, "lineview speed");
|
|
addsaver(history::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(history::bandhalf, "band width");
|
|
addsaver(history::bandsegment, "band segment");
|
|
addsaver(models::rotation, "conformal rotation");
|
|
addsaver(models::rotation_xz, "conformal rotation_xz");
|
|
addsaver(models::rotation_xy2, "conformal rotation_2");
|
|
addsaver(models::do_rotate, "conformal rotation mode", 1);
|
|
addsaver(models::model_orientation, "model orientation", 0);
|
|
addsaver(models::model_orientation_yz, "model orientation-yz", 0);
|
|
addsaver(models::top_z, "topz", 5);
|
|
addsaver(models::model_transition, "model transition", 1);
|
|
addsaver(models::halfplane_scale, "halfplane scale", 1);
|
|
addsaver(history::autoband, "automatic band");
|
|
addsaver(history::autobandhistory, "automatic band history");
|
|
addsaver(history::dospiral, "do spiral");
|
|
addsaver(models::clip_min, "clip-min", -1);
|
|
addsaver(models::clip_max, "clip-max", +1);
|
|
|
|
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(crosshair_size, "size:crosshair");
|
|
addsaver(crosshair_color, "color:crosshair");
|
|
|
|
addsaver(backcolor, "color:background");
|
|
addsaver(forecolor, "color:foreground");
|
|
addsaver(bordcolor, "color:borders");
|
|
addsaver(ringcolor, "color:ring");
|
|
addsaver(vid.multiplier_ring, "mult:ring", 1);
|
|
addsaver(modelcolor, "color:model");
|
|
addsaver(periodcolor, "color:period");
|
|
addsaver(stdgridcolor, "color:stdgrid");
|
|
addsaver(vid.multiplier_grid, "mult:grid", 1);
|
|
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.fisheye_param, "fisheye parameter", 1);
|
|
addsaver(vid.stretch, "stretch", 1);
|
|
addsaver(vid.binary_width, "binary-tiling-width", 1);
|
|
addsaver(vid.collignon_parameter, "collignon-parameter", 1);
|
|
addsaver(vid.collignon_reflected, "collignon-reflect", false);
|
|
|
|
addsaver(vid.plevel_factor, "plevel_factor", 0.7);
|
|
|
|
addsaver(vid.creature_scale, "3d-creaturescale", 1);
|
|
addsaver(vid.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.smart_range_detail_3, "smart-range-detail", 30);
|
|
addsaver(vid.cells_drawn_limit, "limit on cells drawn", 10000);
|
|
addsaver(vid.cells_generated_limit, "limit on cells generated", 250);
|
|
|
|
#if CAP_SOLV
|
|
addsaver(solnihv::solrange_xy, "solrange-xy");
|
|
addsaver(solnihv::solrange_z, "solrange-z");
|
|
#endif
|
|
addsaver(slr::steps, "slr-steps");
|
|
addsaver(slr::range_xy, "slr-range-xy");
|
|
|
|
addsaver(vid.skiprope, "mobius", 0);
|
|
|
|
addsaver(models::formula, "formula");
|
|
addsaverenum(models::basic_model, "basic model");
|
|
addsaver(models::use_atan, "use_atan");
|
|
|
|
addsaver(arcm::current.symbol, "arcm-symbol", "4^5");
|
|
addsaverenum(hybrid::underlying, "product-underlying");
|
|
|
|
for(int i=0; i<isize(ginf); i++) {
|
|
if(ginf[i].flags & qELLIPTIC)
|
|
sightranges[i] = M_PI;
|
|
else if(ginf[i].cclass == gcSphere)
|
|
sightranges[i] = 2 * M_PI;
|
|
else if(ginf[i].cclass == gcEuclid)
|
|
sightranges[i] = 10;
|
|
else if(ginf[i].cclass == gcSL2)
|
|
sightranges[i] = 4.5;
|
|
else
|
|
sightranges[i] = 5;
|
|
sightranges[gArchimedean] = 10;
|
|
if(i < gBinary3) addsaver(sightranges[i], "sight-g" + its(i));
|
|
}
|
|
|
|
ld bonus = 0;
|
|
ld emul = 1;
|
|
|
|
addsaver(sightranges[gBinary3], "sight-binary3", 3.1 + bonus);
|
|
addsaver(sightranges[gCubeTiling], "sight-cubes", 10);
|
|
addsaver(sightranges[gCell120], "sight-120cell", 2 * M_PI);
|
|
addsaver(sightranges[gECell120], "sight-120cell-elliptic", M_PI);
|
|
addsaver(sightranges[gRhombic3], "sight-rhombic", 10.5 * emul);
|
|
addsaver(sightranges[gBitrunc3], "sight-bitrunc", 12 * emul);
|
|
addsaver(sightranges[gSpace534], "sight-534", 4 + bonus);
|
|
addsaver(sightranges[gSpace435], "sight-435", 3.8 + bonus);
|
|
|
|
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", 2.9 + bonus);
|
|
addsaver(sightranges[gHoroRec], "sight-hororec", 2.2 + bonus);
|
|
addsaver(sightranges[gHoroHex], "sight-horohex", 2.75 + bonus);
|
|
|
|
addsaver(sightranges[gKiteDart3], "sight-kd3", 2.25 + bonus);
|
|
|
|
addsaver(sightranges[gField435], "sight-field435", 4 + bonus);
|
|
addsaver(sightranges[gField534], "sight-field534", 3.8 + bonus);
|
|
addsaver(sightranges[gSol], "sight-sol");
|
|
addsaver(sightranges[gNil], "sight-nil", 6.5 + bonus);
|
|
addsaver(sightranges[gNIH], "sight-nih");
|
|
addsaver(sightranges[gSolN], "sight-solnih");
|
|
|
|
addsaver(sightranges[gCrystal344], "sight-crystal344", 5);
|
|
addsaver(sightranges[gSpace344], "sight-344", 4.5);
|
|
addsaver(sightranges[gSpace336], "sight-336", 4);
|
|
|
|
addsaver(vid.sloppy_3d, "sloppy3d", true);
|
|
|
|
addsaver(vid.texture_step, "wall-quality", 1);
|
|
|
|
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
|
|
|
|
addsaver(bounded_mine_percentage, "bounded_mine_percentage");
|
|
|
|
addsaver(nisot::geodesic_movement, "solv_geodesic_movement", true);
|
|
|
|
addsaver(s2xe::qrings, "s2xe-rings");
|
|
addsaver(rots::underlying_scale, "rots-underlying-scale");
|
|
|
|
#if CAP_SHMUP
|
|
multi::initConfig();
|
|
#endif
|
|
|
|
#if CAP_CONFIG
|
|
for(auto s: savers) s->reset();
|
|
#endif
|
|
}
|
|
|
|
EX 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;
|
|
}
|
|
|
|
EX 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;
|
|
}
|
|
|
|
EX 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;
|
|
}
|
|
|
|
EX 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;
|
|
}
|
|
|
|
EX void resetModes(char leave IS('c')) {
|
|
while(game_active || gamestack::pushed()) {
|
|
if(game_active) stop_game();
|
|
if(gamestack::pushed()) gamestack::pop();
|
|
}
|
|
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(leave == rg::heptagons ? eVariation::pure : eVariation::bitruncated);
|
|
|
|
start_game();
|
|
}
|
|
|
|
#if CAP_CONFIG
|
|
EX 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
|
|
EX void saveConfig() {
|
|
DEBB(DF_INIT, ("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;
|
|
if(vid.tc_depth > vid.tc_camera) pt_depth++;
|
|
if(vid.tc_depth < vid.tc_camera) pt_camera++;
|
|
if(vid.tc_depth > vid.tc_alpha ) pt_depth++;
|
|
if(vid.tc_depth < vid.tc_alpha ) pt_alpha ++;
|
|
if(vid.tc_alpha > vid.tc_camera) pt_alpha++;
|
|
if(vid.tc_alpha < vid.tc_camera) pt_camera++;
|
|
vid.tc_alpha = pt_alpha;
|
|
vid.tc_camera = pt_camera;
|
|
vid.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;
|
|
}
|
|
|
|
map<string, shared_ptr<supersaver> > allconfigs;
|
|
|
|
EX 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());
|
|
}
|
|
|
|
EX 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();
|
|
}
|
|
|
|
EX void loadConfig() {
|
|
|
|
DEBB(DF_INIT, ("load config"));
|
|
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;
|
|
#if CAP_LEGACY
|
|
loadOldConfig(f);
|
|
#endif
|
|
}
|
|
|
|
fclose(f);
|
|
DEBB(DF_INIT, ("Loaded configuration: %s\n", conffile));
|
|
}
|
|
|
|
geom3::apply_always3();
|
|
polygonal::solve();
|
|
check_cgi();
|
|
cgi.prepare_basics();
|
|
}
|
|
#endif
|
|
|
|
EX void add_cells_drawn(char c IS('C')) {
|
|
dialog::addSelItem(XLAT("cells drawn"), (noclipped ? its(cells_drawn) + " (" + its(noclipped) + ")" : its(cells_drawn)) + " / " + its(vid.cells_drawn_limit), 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();
|
|
});
|
|
if(WDIM == 3 || vid.use_smart_range == 2) {
|
|
dialog::addSelItem(XLAT("limit generated cells per frame"), its(vid.cells_generated_limit), 'L');
|
|
dialog::add_action([] () {
|
|
dialog::editNumber(vid.cells_generated_limit, 1, 1000, log(10), 25, XLAT("limit generated cells per frame"),
|
|
XLAT("In the 3D mode, lowering this value may help if the game lags while exploring new areas.")
|
|
);
|
|
});
|
|
}
|
|
}
|
|
|
|
string solhelp() {
|
|
#if CAP_SOLV
|
|
return XLAT(
|
|
"Solv (aka Sol) is a 3D space where directions work in different ways. It is described by the following metric:\n"
|
|
"ds² = (eᶻdx)² + (e⁻ᶻdy)² + dz²\n\n"
|
|
"You are currently displaying Solv in the perspective projection based on native geodesics. You can control how "
|
|
"the fog effects depends on the geodesic distance, and how far object in X/Y/Z coordinates are rendered."
|
|
);
|
|
#else
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
EX void edit_sightrange() {
|
|
if(vid.use_smart_range) {
|
|
ld& det = WDIM == 2 ? vid.smart_range_detail : vid.smart_range_detail_3;
|
|
dialog::editNumber(det, 1, 50, 1, WDIM == 2 ? 8 : 30, XLAT("minimum visible cell in pixels"), "");
|
|
}
|
|
else if(WDIM == 3) {
|
|
dialog::editNumber(sightranges[geometry], 0, 2 * M_PI, 0.5, M_PI, XLAT("3D sight range"),
|
|
(pmodel == mdGeodesic && sol) ? solhelp() : 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 2? will let you see things behind you as if they were in front of you, "
|
|
"and the sight range of ? (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."
|
|
)
|
|
);
|
|
}
|
|
else {
|
|
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() ? euclid ? 99 : gp::dist_2() * 5 : 0);
|
|
}
|
|
dialog::extra_options = [] () {
|
|
if(pmodel == mdGeodesic && sol) {
|
|
#if CAP_SOLV
|
|
dialog::addSelItem(XLAT("fog effect"), fts(sightranges[geometry]), 'R');
|
|
dialog::add_action([] {
|
|
auto xo = dialog::extra_options;
|
|
dialog::editNumber(sightranges[geometry], 0, 10, 0.5, M_PI, solhelp(), "");
|
|
dialog::extra_options = xo; popScreen();
|
|
});
|
|
dialog::addSelItem(XLAT("max difference in X/Y coordinates"), fts(solnihv::solrange_xy), 'X');
|
|
dialog::add_action([] {
|
|
auto xo = dialog::extra_options;
|
|
dialog::editNumber(solnihv::solrange_xy, 0.01, 200, 0.1, 50, XLAT("max difference in X/Y coordinates"), solhelp()), dialog::scaleLog();
|
|
dialog::extra_options = xo; popScreen();
|
|
});
|
|
dialog::addSelItem(XLAT("max difference in Z coordinate"), fts(solnihv::solrange_z), 'Z');
|
|
dialog::add_action([] {
|
|
auto xo = dialog::extra_options;
|
|
dialog::editNumber(solnihv::solrange_z, 0, 20, 0.1, 6, XLAT("max difference in Z coordinates"), solhelp());
|
|
dialog::extra_options = xo; popScreen();
|
|
});
|
|
#endif
|
|
}
|
|
else if(pmodel == mdGeodesic && sl2) {
|
|
dialog::addSelItem(XLAT("fog effect"), fts(sightranges[geometry]), 'R');
|
|
dialog::add_action([] {
|
|
auto xo = dialog::extra_options;
|
|
dialog::editNumber(sightranges[geometry], 0, 10, 0.5, M_PI, "", "");
|
|
dialog::extra_options = xo; popScreen();
|
|
});
|
|
dialog::addSelItem(XLAT("max difference in X/Y coordinates"), fts(slr::range_xy), 'X');
|
|
dialog::add_action([] {
|
|
auto xo = dialog::extra_options;
|
|
dialog::editNumber(slr::range_xy, 0, 10, 0.5, 4, XLAT("max difference in X/Y coordinates"), "");
|
|
dialog::extra_options = xo; popScreen();
|
|
});
|
|
dialog::addSelItem(XLAT("steps"), its(slr::steps), 'Z');
|
|
dialog::add_action([] {
|
|
auto xo = dialog::extra_options;
|
|
dialog::editNumber(slr::steps, 0, 50, 1, 10, "", "");
|
|
dialog::extra_options = xo; popScreen();
|
|
});
|
|
}
|
|
else {
|
|
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(WDIM == 2 && 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() && WDIM == 2) {
|
|
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);
|
|
}
|
|
if(WDIM == 3 && !vid.use_smart_range) {
|
|
dialog::addBoolItem_action(XLAT("sloppy range checking"), vid.sloppy_3d, 'S');
|
|
}
|
|
if(GDIM == 3 && !vid.use_smart_range) {
|
|
dialog::addSelItem(XLAT("limit generation"), fts(extra_generation_distance), 'E');
|
|
dialog::add_action([] {
|
|
auto xo = dialog::extra_options;
|
|
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."
|
|
);
|
|
dialog::extra_options = xo; popScreen();
|
|
});
|
|
}
|
|
}
|
|
add_cells_drawn('C');
|
|
if(GDIM == 3 && WDIM == 2 && pmodel == mdPerspective) {
|
|
dialog::addSelItem(XLAT("fog effect"), fts(sightranges[geometry]), 'R');
|
|
dialog::add_action([] {
|
|
auto xo = dialog::extra_options;
|
|
dialog::editNumber(sightranges[geometry], 0, 2 * M_PI, 0.5, M_PI, XLAT("fog effect"), "");
|
|
dialog::extra_options = xo; popScreen();
|
|
});
|
|
};
|
|
};
|
|
}
|
|
|
|
EX void menuitem_sightrange(char c IS('c')) {
|
|
if(vid.use_smart_range)
|
|
dialog::addSelItem(XLAT("minimum visible cell in pixels"), fts(WDIM == 3 ? vid.smart_range_detail_3 : vid.smart_range_detail), c);
|
|
#if CAP_SOLV
|
|
else if(pmodel == mdGeodesic && sol)
|
|
dialog::addSelItem(XLAT("3D sight range"), fts(solnihv::solrange_xy) + "x" + fts(solnihv::solrange_z), c);
|
|
#endif
|
|
else if(WDIM == 3)
|
|
dialog::addSelItem(XLAT("3D sight range"), fts(sightranges[geometry]), c);
|
|
else
|
|
dialog::addSelItem(XLAT("sight range"), its(sightrange_bonus), c);
|
|
dialog::add_action(edit_sightrange);
|
|
}
|
|
|
|
EX 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');
|
|
|
|
dialog::addBreak(50);
|
|
dialog::addBack();
|
|
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;
|
|
|
|
else 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"));
|
|
|
|
else if(xuni == 'm') dialog::editNumber(vid.mspeed, -5, 5, 1, 0,
|
|
XLAT("movement animation speed"),
|
|
XLAT("+5 = move instantly"));
|
|
|
|
else if(xuni == 'f') switchFullscreen();
|
|
|
|
#if CAP_GLORNOT
|
|
else if(xuni == 'o' && shiftmul > 0) switchGL();
|
|
#endif
|
|
|
|
else 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;
|
|
|
|
else 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; });
|
|
};
|
|
}
|
|
|
|
else 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."));
|
|
}
|
|
|
|
#if CAP_FRAMELIMIT
|
|
else if(xuni == 'l') {
|
|
dialog::editNumber(vid.framelimit, 5, 300, 10, 300, XLAT("framerate limit"), "");
|
|
dialog::bound_low(5);
|
|
}
|
|
#endif
|
|
|
|
else if(xuni =='p')
|
|
vid.backeffects = !vid.backeffects;
|
|
|
|
else if(doexiton(sym, uni)) popScreen();
|
|
};
|
|
}
|
|
|
|
EX 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
|
|
}
|
|
|
|
EX 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
|
|
}
|
|
|
|
EX void edit_whatever(char type, int index) {
|
|
if(type == 'f') {
|
|
dialog::editNumber(whatever[index], -10, 10, 1, 0, XLAT("whatever"),
|
|
"f:" + its(index));
|
|
}
|
|
else {
|
|
dialog::editNumber(whateveri[index], -10, 10, 1, 0, XLAT("whatever"),
|
|
"i:" + its(index));
|
|
}
|
|
dialog::extra_options = [type, index] {
|
|
dialog::addItem(XLAT("integer"), 'X');
|
|
dialog::add_action( [index] { popScreen(); edit_whatever('i', index); });
|
|
dialog::addItem(XLAT("float"), 'Y');
|
|
dialog::add_action( [index] { popScreen(); edit_whatever('f', index); });
|
|
for(int x=0; x<8; x++) {
|
|
dialog::addSelItem(its(x), type == 'i' ? its(whateveri[x]) : fts(whatever[x]), 'A' + x);
|
|
dialog::add_action([type,x] { popScreen(); edit_whatever(type, x); });
|
|
}
|
|
};
|
|
}
|
|
|
|
EX void configureOther() {
|
|
gamescreen(3);
|
|
|
|
dialog::init(XLAT("other settings"));
|
|
|
|
#if ISSTEAM
|
|
dialog::addBoolItem(XLAT("send scores to Steam leaderboards"), (vid.steamscore&1), 'x');
|
|
dialog::add_action([] {vid.steamscore = vid.steamscore^1; });
|
|
#endif
|
|
|
|
dialog::addBoolItem_action(XLAT("skip the start menu"), vid.skipstart, 'm');
|
|
|
|
dialog::addItem(XLAT("memory configuration"), 'y');
|
|
dialog::add_action_push(show_memory_menu);
|
|
|
|
// dialog::addBoolItem_action(XLAT("forget faraway cells"), memory_saving_mode, 'y');
|
|
|
|
#if CAP_AUDIO
|
|
if(CAP_AUDIO) {
|
|
dialog::addSelItem(XLAT("background music volume"), its(musicvolume), 'b');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(musicvolume, 0, 128, 10, 60, XLAT("background music volume"), "");
|
|
dialog::reaction = [] () {
|
|
#if CAP_SDLAUDIO
|
|
Mix_VolumeMusic(musicvolume);
|
|
#endif
|
|
#if ISANDROID
|
|
settingsChanged = true;
|
|
#endif
|
|
};
|
|
dialog::bound_low(0);
|
|
dialog::bound_up(MIX_MAX_VOLUME);
|
|
});
|
|
|
|
dialog::addSelItem(XLAT("sound effects volume"), its(effvolume), 'e');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(effvolume, 0, 128, 10, 60, XLAT("sound effects volume"), "");
|
|
dialog::reaction = [] () {
|
|
#if ISANDROID
|
|
settingsChanged = true;
|
|
#endif
|
|
};
|
|
dialog::bound_low(0);
|
|
dialog::bound_up(MIX_MAX_VOLUME);
|
|
});
|
|
}
|
|
#endif
|
|
|
|
menuitem_sightrange('r');
|
|
|
|
#ifdef WHATEVER
|
|
dialog::addSelItem(XLAT("whatever"), fts(whatever[0]), 'j');
|
|
dialog::add_action([] { edit_whatever('f', 0); });
|
|
#endif
|
|
|
|
dialog::addBreak(50);
|
|
dialog::addBack();
|
|
|
|
dialog::display();
|
|
}
|
|
|
|
EX void configureInterface() {
|
|
gamescreen(3);
|
|
dialog::init(XLAT("interface"));
|
|
|
|
#if CAP_TRANS
|
|
if(CAP_TRANS) {
|
|
dialog::addSelItem(XLAT("language"), XLAT("EN"), 'l');
|
|
dialog::add_action_push(selectLanguageScreen);
|
|
}
|
|
#endif
|
|
|
|
dialog::addSelItem(XLAT("player character"), numplayers() > 1 ? "" : csname(vid.cs), 'g');
|
|
dialog::add_action_push(showCustomizeChar);
|
|
if(getcstat == 'g') mouseovers = XLAT("Affects looks and grammar");
|
|
|
|
dialog::addSelItem(XLAT("message flash time"), its(vid.flashtime), 't');
|
|
dialog::add_action([] {
|
|
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);
|
|
});
|
|
|
|
dialog::addSelItem(XLAT("limit messages shown"), its(vid.msglimit), 'z');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(vid.msglimit, 0, 64, 1, 5, XLAT("limit messages shown"),
|
|
XLAT("Maximum number of messages on screen."));
|
|
dialog::bound_low(0);
|
|
});
|
|
|
|
const char* msgstyles[3] = {"centered", "left-aligned", "line-broken"};
|
|
|
|
dialog::addSelItem(XLAT("message style"), XLAT(msgstyles[vid.msgleft]), 'a');
|
|
dialog::add_action([] {
|
|
vid.msgleft = (1+vid.msgleft) % 3;
|
|
});
|
|
|
|
dialog::addSelItem(XLAT("font scale"), its(fontscale), 'b');
|
|
dialog::add_action([] {
|
|
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);
|
|
});
|
|
|
|
const char *glyphsortnames[6] = {
|
|
"first on top", "first on bottom",
|
|
"last on top", "last on bottom",
|
|
"by land", "by number"
|
|
};
|
|
dialog::addSelItem(XLAT("inventory/kill sorting"), XLAT(glyphsortnames[glyphsortorder]), 'k');
|
|
dialog::add_action([] {
|
|
glyphsortorder = eGlyphsortorder((glyphsortorder+6+(shiftmul>0?1:-1)) % gsoMAX);
|
|
});
|
|
|
|
const char *glyphmodenames[3] = {"letters", "auto", "images"};
|
|
dialog::addSelItem(XLAT("inventory/kill mode"), XLAT(glyphmodenames[vid.graphglyph]), 'd');
|
|
dialog::add_action([] {
|
|
vid.graphglyph = (1+vid.graphglyph)%3;
|
|
});
|
|
|
|
dialog::addSelItem(XLAT("draw crosshair"), crosshair_size > 0 ? fts(crosshair_size) : ONOFF(false), 'x');
|
|
dialog::add_action([] () {
|
|
dialog::editNumber(crosshair_size, 0, 100, 1, 10, XLAT("crosshair size"), XLAT(
|
|
"Display a targetting reticle in the center of the screen. Might be useful when exploring 3D modes, "
|
|
"as it precisely shows the direction we are going. However, the option is available in all modes."
|
|
));
|
|
dialog::bound_low(0);
|
|
dialog::extra_options = [] {
|
|
dialog::addColorItem(XLAT("crosshair color"), crosshair_color, 'X');
|
|
dialog::add_action([] { dialog::openColorDialog(crosshair_color); });
|
|
};
|
|
});
|
|
|
|
dialog::addBreak(50);
|
|
dialog::addBack();
|
|
|
|
dialog::display();
|
|
}
|
|
|
|
#if CAP_SDLJOY
|
|
EX 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
|
|
|
|
EX void projectionDialog() {
|
|
vid.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);
|
|
if(GDIM == 2) dialog::addHelp(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."));
|
|
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"; });
|
|
if(hyperbolic) {
|
|
dialog::addSelItem("inverted Poincaré model", "-1", 'I');
|
|
dialog::add_action([] () { *dialog::ne.editwhat = -1; vid.scale = 1; dialog::ne.s = "-1"; });
|
|
}
|
|
dialog::addItem(sphere ? "orthographic" : "Gans model", 'O');
|
|
dialog::add_action([] () { vid.alpha = vid.scale = 999; dialog::ne.s = dialog::disp(vid.alpha); });
|
|
dialog::addItem(sphere ? "towards orthographic" : "towards Gans model", 'T');
|
|
dialog::add_action([] () { double d = 1.1; vid.alpha *= d; vid.scale *= d; dialog::ne.s = dialog::disp(vid.alpha); });
|
|
};
|
|
}
|
|
|
|
EX void explain_detail() {
|
|
dialog::addHelp(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.",
|
|
fts(vid.highdetail), fts(vid.middetail)
|
|
));
|
|
}
|
|
|
|
EX void add_edit_fov(char key IS('f')) {
|
|
dialog::addSelItem(XLAT("field of view"), fts(vid.fov) + "°", key);
|
|
dialog::add_action([] {
|
|
dialog::editNumber(vid.fov, 1, 170, 1, 45, "field of view",
|
|
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);
|
|
});
|
|
}
|
|
|
|
bool supported_ods() {
|
|
if(!CAP_ODS) return false;
|
|
return rug::rugged || (hyperbolic && GDIM == 3);
|
|
}
|
|
|
|
EX void showStereo() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen(0);
|
|
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"), fts(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("desaturate 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) {
|
|
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) % 4);
|
|
if(vid.stereo_mode == sODS && !supported_ods()) vid.stereo_mode = sOFF;
|
|
}
|
|
|
|
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.")
|
|
), dialog::scaleSinh100();
|
|
|
|
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();
|
|
};
|
|
}
|
|
|
|
EX 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."
|
|
);
|
|
}
|
|
|
|
EX void add_edit_wall_quality(char c) {
|
|
dialog::addSelItem(XLAT("wall quality"), its(vid.texture_step), c);
|
|
dialog::add_action([] {
|
|
dialog::editNumber(vid.texture_step, 1, 16, 1, 1, XLAT("wall quality"),
|
|
XLAT(
|
|
"Controls the number of triangles used for wall surfaces. "
|
|
"Higher numbers reduce the performance. "
|
|
"This has a strong effect when the walls are curved indeed "
|
|
"(floors in 2D geometries, honeycombs based on horospheres, and projections other than native perspective), "
|
|
"but otherwise, usually it can be set to 1 without significant adverse effects other "
|
|
"than slightly incorrect texturing."
|
|
)
|
|
);
|
|
dialog::bound_low(1);
|
|
dialog::bound_up(128);
|
|
dialog::reaction = [] {
|
|
#if MAXMDIM >= 4
|
|
if(floor_textures) {
|
|
delete floor_textures;
|
|
floor_textures = NULL;
|
|
}
|
|
#endif
|
|
};
|
|
});
|
|
}
|
|
|
|
EX void show3D() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen(0);
|
|
dialog::init(XLAT("3D configuration"));
|
|
|
|
#if MAXMDIM >= 4
|
|
if(WDIM == 2) {
|
|
dialog::addBoolItem(XLAT("use the full 3D models"), vid.always3, 'U');
|
|
dialog::add_action(geom3::switch_always3);
|
|
}
|
|
#endif
|
|
if(vid.use_smart_range == 0 && GDIM == 2) {
|
|
dialog::addSelItem(XLAT("High detail range"), fts(vid.highdetail), 'n');
|
|
dialog::addSelItem(XLAT("Mid detail range"), fts(vid.middetail), 'm');
|
|
dialog::addBreak(50);
|
|
}
|
|
|
|
if(WDIM == 2) {
|
|
dialog::addSelItem(XLAT(GDIM == 2 ? "Camera level above the plane" : "Z shift"), fts(vid.camera), 'c');
|
|
if(GDIM == 3)
|
|
dialog::addSelItem(XLAT("Eye level"), fts(vid.eye), 'E');
|
|
|
|
dialog::addSelItem(XLAT("Ground level below the plane"), fts(vid.depth), 'g');
|
|
|
|
|
|
if(GDIM == 2)
|
|
dialog::addSelItem(XLAT("Projection at the ground level"), fts(vid.alpha), 'p');
|
|
else if(!in_perspective())
|
|
dialog::addSelItem(XLAT("Projection distance"), fts(vid.alpha), 'p');
|
|
|
|
dialog::addBreak(50);
|
|
dialog::addSelItem(XLAT("Height of walls"), fts(vid.wall_height), 'w');
|
|
|
|
dialog::addSelItem(XLAT("Rock-III to wall ratio"), fts(vid.rock_wall_ratio), 'r');
|
|
dialog::addSelItem(XLAT("Human to wall ratio"), fts(vid.human_wall_ratio), 'h');
|
|
dialog::addSelItem(XLAT("Level of water surface"), fts(vid.lake_top), 'l');
|
|
dialog::addSelItem(XLAT("Level of water bottom"), fts(vid.lake_bottom), 'k');
|
|
if(scale_used())
|
|
dialog::addSelItem(XLAT("Creature scale"), fts(vid.creature_scale), 'C');
|
|
}
|
|
else {
|
|
dialog::addSelItem(XLAT("Creature scale"), fts(vid.creature_scale), 'c');
|
|
dialog::addSelItem(XLAT("Height to width"), fts(vid.height_width), 'h');
|
|
menuitem_sightrange('s');
|
|
}
|
|
|
|
dialog::addBreak(50);
|
|
dialog::addSelItem(XLAT(GDIM == 3 && WDIM == 2 ? "Y shift" : "third person perspective"), fts(vid.yshift), 'y');
|
|
if(GDIM == 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");
|
|
});
|
|
}
|
|
dialog::addSelItem(XLAT("camera rotation"), fts(vid.camera_angle), 'x');
|
|
if(GDIM == 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((WDIM == 2 && GDIM == 3) || prod)
|
|
dialog::addBoolItem_action(XLAT("fixed Y/Z rotation"), vid.fixed_yz, 'Z');
|
|
|
|
if(true) {
|
|
dialog::addBreak(50);
|
|
dialog::addSelItem(XLAT("projection"), current_proj_name(), 'M');
|
|
}
|
|
#if MAXMDIM >= 4
|
|
if(GDIM == 3) add_edit_fov('f');
|
|
if(GDIM == 3) {
|
|
dialog::addSelItem(XLAT("radar size"), fts(vid.radarsize), 'r');
|
|
dialog::add_action([] () {
|
|
dialog::editNumber(vid.radarsize, 0, 360, 15, 90, "", XLAT("set to 0 to disable"));
|
|
dialog::extra_options = [] () { draw_radar(true); };
|
|
});
|
|
}
|
|
|
|
if(WDIM == 3 || (GDIM == 3 && euclid)) {
|
|
dialog::addSelItem(XLAT("radar range"), fts(vid.radarrange), 'R');
|
|
dialog::add_action([] () {
|
|
dialog::editNumber(vid.radarrange, 0, 10, 0.5, 2, "", XLAT(""));
|
|
dialog::extra_options = [] () { draw_radar(true); };
|
|
});
|
|
}
|
|
if(GDIM == 3) add_edit_wall_quality('W');
|
|
#endif
|
|
|
|
dialog::addBreak(50);
|
|
#if CAP_RUG
|
|
if(rug::rugged) {
|
|
dialog::addBoolItem_action(XLAT("3D monsters/walls on the surface"), rug::spatial_rug, 'S');
|
|
}
|
|
#endif
|
|
if(GDIM == 2) {
|
|
dialog::addBoolItem(XLAT("configure TPP automatically"), pmodel == mdDisk && vid.camera_angle, 'T');
|
|
dialog::add_action(geom3::switch_tpp);
|
|
}
|
|
|
|
#if MAXMDIM >=4
|
|
if(WDIM == 2) {
|
|
dialog::addBoolItem(XLAT("configure FPP automatically"), GDIM == 3, 'F');
|
|
dialog::add_action(geom3::switch_fpp);
|
|
}
|
|
#endif
|
|
|
|
if(0);
|
|
#if CAP_RUG
|
|
else if(rug::rugged && !rug::spatial_rug)
|
|
dialog::addBreak(100);
|
|
#endif
|
|
else if(GDIM == 2 && non_spatial_model())
|
|
dialog::addInfo(XLAT("no 3D effects available in this projection"), 0xC00000);
|
|
else if(GDIM == 2 && !spatial_graphics)
|
|
dialog::addInfo(XLAT("set 3D monsters or walls in basic config first"));
|
|
else if(geom3::invalid != "")
|
|
dialog::addInfo(XLAT("error: "+geom3::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' && GDIM == 2) {
|
|
dialog::editNumber(vid.highdetail, 0, 5, .5, 7, XLAT("High detail range"), "");
|
|
dialog::extra_options = explain_detail;
|
|
dialog::reaction = [] () {
|
|
if(vid.highdetail > vid.middetail) vid.middetail = vid.highdetail;
|
|
};
|
|
}
|
|
else if(uni == 'm' && GDIM == 2) {
|
|
dialog::editNumber(vid.middetail, 0, 5, .5, 7, XLAT("Mid detail range"), "");
|
|
dialog::extra_options = explain_detail;
|
|
dialog::reaction = [] () {
|
|
if(vid.highdetail > vid.middetail) vid.highdetail = vid.middetail;
|
|
};
|
|
}
|
|
else if(uni == 'c' && WDIM == 2)
|
|
vid.tc_camera = ticks,
|
|
dialog::editNumber(vid.camera, 0, 5, .1, 1, XLAT(GDIM == 2 ? "Camera level above the plane" : "Z shift"), ""),
|
|
dialog::extra_options = [] {
|
|
dialog::addHelp(GDIM == 2 ? 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).",
|
|
fts(vid.camera),
|
|
fts(vid.depth),
|
|
fts(atan(1/cosh(vid.camera))*2/degree),
|
|
fts(1/cosh(vid.camera))) : XLAT("Look from behind."));
|
|
if(GDIM == 3 && pmodel == mdPerspective) dialog::extra_options = [] () {
|
|
dialog::addBoolItem_action(XLAT("reduce if walls on the way"), vid.use_wall_radar, 'R');
|
|
};
|
|
};
|
|
else if(uni == 'g' && WDIM == 2)
|
|
vid.tc_depth = ticks,
|
|
dialog::editNumber(vid.depth, 0, 5, .1, 1, XLAT("Ground level below the plane"), ""),
|
|
dialog::extra_options = [] {
|
|
help = 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. ", fts(vid.depth), fts(cosh(vid.depth)));
|
|
if(GDIM == 2)
|
|
help += XLAT(
|
|
"(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.)"
|
|
);
|
|
if(GDIM == 3 && pmodel == mdPerspective && !euclid) {
|
|
ld current_camera_level = hdist0(tC0(radar_transform));
|
|
help += "\n\n";
|
|
if(abs(current_camera_level) < 1e-6)
|
|
help += XLAT(
|
|
"The camera is currently exactly on the plane P. "
|
|
"The horizon is seen as a straight line."
|
|
);
|
|
else help += XLAT(
|
|
"The camera is currently %1 units above the plane P. "
|
|
"This makes you see the floor level as in general perspective projection "
|
|
"with parameter %2.", fts(current_camera_level), fts(tan_auto(vid.depth) / tan_auto(current_camera_level)));
|
|
}
|
|
dialog::addHelp(help);
|
|
};
|
|
else if(uni == 'E' && WDIM == 2 && GDIM == 3)
|
|
vid.tc_depth = ticks,
|
|
dialog::editNumber(vid.eye, -5, 5, .1, 0, XLAT("eye level"), ""),
|
|
dialog::dialogflags |= sm::CENTER,
|
|
dialog::extra_options = [] {
|
|
|
|
dialog::addHelp(XLAT("In the FPP mode, the camera will be set at this altitude (before applying shifts)."));
|
|
|
|
dialog::addBoolItem(XLAT("auto-adjust to eyes on the player model"), vid.auto_eye, 'O');
|
|
dialog::reaction = [] { vid.auto_eye = false; };
|
|
dialog::add_action([] () {
|
|
vid.auto_eye = !vid.auto_eye;
|
|
geom3::do_auto_eye();
|
|
});
|
|
};
|
|
else if(uni == 'p' && WDIM == 2)
|
|
projectionDialog();
|
|
else if(uni == 'w' && WDIM == 2) {
|
|
dialog::editNumber(vid.wall_height, 0, 1, .1, .3, XLAT("Height of walls"), "");
|
|
dialog::extra_options = [] () {
|
|
dialog::addHelp(GDIM == 3 ? "" : 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.",
|
|
fts(actual_wall_height()), fts(factor_to_projection(cgi.WALL))));
|
|
dialog::addBoolItem(XLAT("auto-adjust in Goldberg grids"), vid.gp_autoscale_heights, 'O');
|
|
dialog::add_action([] () {
|
|
vid.gp_autoscale_heights = !vid.gp_autoscale_heights;
|
|
});
|
|
};
|
|
}
|
|
else if(uni == 'l' && WDIM == 2)
|
|
dialog::editNumber(vid.lake_top, 0, 1, .1, .25, XLAT("Level of water surface"), "");
|
|
else if(uni == 'k' && WDIM == 2)
|
|
dialog::editNumber(vid.lake_bottom, 0, 1, .1, .9, XLAT("Level of water bottom"), "");
|
|
else if(uni == 'r' && WDIM == 2)
|
|
dialog::editNumber(vid.rock_wall_ratio, 0, 1, .1, .9, XLAT("Rock-III to wall ratio"), ""),
|
|
dialog::extra_options = [] { dialog::addHelp(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.",
|
|
fts(vid.rock_wall_ratio), fts(vid.wall_height * vid.rock_wall_ratio),
|
|
fts(cosh(vid.depth - vid.wall_height * vid.rock_wall_ratio) / cosh(vid.depth))));
|
|
};
|
|
else if(uni == 'h' && WDIM == 2)
|
|
dialog::editNumber(vid.human_wall_ratio, 0, 1, .1, .7, XLAT("Human to wall ratio"), ""),
|
|
dialog::extra_options = [] { dialog::addHelp(XLAT(
|
|
"Humans are %1 "
|
|
"absolute units high. Your head travels %2 times the distance travelled by your "
|
|
"feet.",
|
|
fts(vid.wall_height * vid.human_wall_ratio),
|
|
fts(cosh(vid.depth - vid.wall_height * vid.human_wall_ratio) / cosh(vid.depth)))
|
|
);
|
|
};
|
|
else if(uni == 'h' && WDIM == 3)
|
|
dialog::editNumber(vid.height_width, 0, 1, .1, .7, XLAT("Height to width"), "");
|
|
else if(uni == 'c' && WDIM == 3)
|
|
dialog::editNumber(vid.creature_scale, 0, 1, .1, .7, XLAT("Creature scale"), "");
|
|
else if(uni == 'C' && WDIM == 2 && scale_used())
|
|
dialog::editNumber(vid.creature_scale, 0, 1, .1, .7, XLAT("Creature scale"), "");
|
|
|
|
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.")
|
|
);
|
|
if(WDIM == 3 && pmodel == mdPerspective) dialog::extra_options = [] () {
|
|
dialog::addBoolItem_action(XLAT("reduce if walls on the way"), vid.use_wall_radar, 'R');
|
|
};
|
|
}
|
|
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(models::model_menu);
|
|
else if(doexiton(sym, uni))
|
|
popScreen();
|
|
};
|
|
}
|
|
|
|
EX void switchcolor(unsigned int& c, unsigned int* cs) {
|
|
dialog::openColorDialog(c, cs);
|
|
}
|
|
|
|
double cc_footphase;
|
|
int lmousex, lmousey;
|
|
|
|
EX 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) multi::cpid = multi::cpid_edit % multi::players;
|
|
charstyle& cs = getcs();
|
|
|
|
dialog::addSelItem(XLAT("character"), csname(cs), 'g');
|
|
dialog::addColorItem(XLAT("skin color"), cs.skincolor, 's');
|
|
dialog::addColorItem(XLAT("eye color"), cs.eyecolor, 'e');
|
|
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(multi::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;
|
|
|
|
dynamicval<eModel> pm(pmodel, flat_model());
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
dynamicval<ld> va(vid.alpha, 1);
|
|
dynamicval<ld> vs(vid.scale, 1);
|
|
dynamicval<ld> vc(vid.camera_angle, 0);
|
|
|
|
initquickqueue();
|
|
transmatrix V = atscreenpos(vid.xres/2, firsty, scale);
|
|
|
|
double alpha = atan2(mousex - vid.xres/2, mousey - firsty) - M_PI/2;
|
|
V = V * spin(alpha);
|
|
drawMonsterType(moPlayer, NULL, V, 0, cc_footphase / scale, NOCOLOR);
|
|
quickqueue();
|
|
|
|
keyhandler = [] (int sym, int uni) {
|
|
dialog::handleNavigation(sym, uni);
|
|
|
|
if(shmup::on || multi::players) multi::cpid = multi::cpid_edit % multi::players;
|
|
charstyle& cs = getcs();
|
|
bool cat = cs.charid >= 4;
|
|
if(uni == 'a') { multi::cpid_edit++; multi::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, 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 == 'e') switchcolor(cs.eyecolor, eyecolors);
|
|
else if(uni == 'l') cs.lefthanded = !cs.lefthanded;
|
|
else if(doexiton(sym, uni)) popScreen();
|
|
};
|
|
}
|
|
|
|
EX 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);
|
|
}
|
|
}
|
|
|
|
EX void edit_color_table(colortable& ct, const reaction_t& r IS(reaction_t()), bool has_bit IS(false)) {
|
|
cmode = sm::SIDE;
|
|
gamescreen(0);
|
|
dialog::init(XLAT("colors & aura"));
|
|
|
|
for(int i=0; i<isize(ct); i++) {
|
|
dialog::addColorItem(its(i), ct[i] << 8, 'a'+i);
|
|
if(WDIM == 3 && has_bit && !(ct[i] & 0x1000000)) dialog::lastItem().value = XLAT("(no wall)");
|
|
dialog::add_action([i, &ct, r, has_bit] () {
|
|
if(WDIM == 3 && has_bit) {
|
|
ct[i] ^= 0x1000000;
|
|
if(!(ct[i] & 0x1000000)) return;
|
|
}
|
|
dialog::openColorDialog(ct[i]);
|
|
dialog::reaction = r;
|
|
dialog::colorAlpha = false;
|
|
dialog::dialogflags |= sm::SIDE;
|
|
});
|
|
}
|
|
|
|
dialog::addBack();
|
|
dialog::display();
|
|
}
|
|
|
|
EX void show_color_dialog() {
|
|
cmode = sm::SIDE | sm::DIALOG_STRICT_X;
|
|
getcstat = '-';
|
|
gamescreen(0);
|
|
dialog::init(XLAT("colors & 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::addSelItem(XLAT("boundary width multiplier"), fts(vid.multiplier_ring), 'R');
|
|
dialog::add_action([] () { dialog::editNumber(vid.multiplier_ring, 0, 10, 1, 1, XLAT("boundary width multiplier"), ""); });
|
|
|
|
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("grid width multiplier"), fts(vid.multiplier_grid), 'G');
|
|
dialog::add_action([] () { dialog::editNumber(vid.multiplier_grid, 0, 10, 1, 1, XLAT("grid width multiplier"), ""); });
|
|
|
|
dialog::addSelItem(XLAT("brightness behind the sphere"), fts(backbrightness), 'i');
|
|
dialog::add_action([] () { dialog::editNumber(backbrightness, 0, 1, .01, 0.25, XLAT("brightness behind the sphere"),
|
|
XLAT("In the orthogonal projection, objects on the other side of the sphere are drawn darker.")); 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_push([] { edit_color_table(colortables[patterns::whichCanvas], refresh_canvas, true); });
|
|
}
|
|
|
|
if(cwt.at->land == laMinefield) {
|
|
dialog::addItem(XLAT("minefield colors"), 'm');
|
|
dialog::add_action_push([] { edit_color_table(minecolors); });
|
|
}
|
|
|
|
if(viewdists) {
|
|
dialog::addItem(XLAT("distance colors"), 'd');
|
|
dialog::add_action_push([] () {edit_color_table(distcolors); });
|
|
}
|
|
|
|
#if CAP_CRYSTAL
|
|
if(cryst && 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
|
|
EX 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("resetting 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
|
|
EX 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
|
|
|
|
EX void configureMouse() {
|
|
gamescreen(1);
|
|
dialog::init(XLAT("mouse & touchscreen"));
|
|
|
|
dialog::addBoolItem_action(XLAT("reverse pointer control"), (vid.revcontrol), 'r');
|
|
|
|
dialog::addBoolItem_action(XLAT("draw circle around the target"), (vid.drawmousecircle), 'd');
|
|
|
|
if(GDIM == 3) {
|
|
dialog::addBoolItem_action(XLAT("highlight the cell forward"), vid.axes3, 'f');
|
|
}
|
|
|
|
#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
|
|
dialog::add_action([] {vid.shifttarget = vid.shifttarget^3; });
|
|
|
|
#if !ISMOBILE
|
|
dialog::addBoolItem_action(XLAT("quick mouse"), vid.quickmouse, 'M');
|
|
#endif
|
|
|
|
dialog::addSelItem(XLAT("move by clicking on compass"), its(vid.mobilecompasssize), 'C');
|
|
dialog::add_action([] {
|
|
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_ORIENTATION
|
|
if(GDIM == 2) {
|
|
dialog::addSelItem(XLAT("scrolling by device rotation"), ors::choices[ors::mode], '1');
|
|
dialog::add_action_push(ors::show);
|
|
}
|
|
#endif
|
|
|
|
dialog::addBack();
|
|
dialog::display();
|
|
}
|
|
|
|
EX void showSettings() {
|
|
gamescreen(1);
|
|
dialog::init(XLAT("settings"));
|
|
|
|
dialog::addItem(XLAT("interface"), 'i');
|
|
dialog::add_action_push(configureInterface);
|
|
|
|
dialog::addItem(XLAT("general graphics"), 'g');
|
|
dialog::add_action_push(showGraphConfig);
|
|
|
|
dialog::addItem(XLAT("3D configuration"), '9');
|
|
dialog::add_action_push(show3D);
|
|
|
|
dialog::addItem(XLAT("quick options"), 'q');
|
|
dialog::add_action_push(showGraphQuickKeys);
|
|
|
|
dialog::addItem(XLAT("models & projections"), 'p');
|
|
dialog::add_action_push(models::model_menu);
|
|
|
|
dialog::addItem(XLAT("colors & aura"), 'c');
|
|
dialog::add_action_push(show_color_dialog);
|
|
|
|
#if CAP_SHMUP
|
|
if(CAP_SHMUP && !ISMOBILE) {
|
|
dialog::addSelItem(XLAT("keyboard & joysticks"), "", 'k');
|
|
dialog::add_action(multi::configure);
|
|
}
|
|
#endif
|
|
|
|
dialog::addSelItem(XLAT("mouse & touchscreen"), "", 'm');
|
|
dialog::add_action_push(configureMouse);
|
|
|
|
dialog::addItem(XLAT("other settings"), 'o');
|
|
dialog::add_action_push(configureOther);
|
|
|
|
dialog::addBreak(100);
|
|
|
|
#if CAP_CONFIG
|
|
dialog::addItem(XLAT("save the current config"), 's');
|
|
dialog::add_action(saveConfig);
|
|
|
|
dialog::addItem(XLAT("reset all configuration"), 'R');
|
|
dialog::add_action_push(resetConfigMenu);
|
|
#endif
|
|
|
|
if(getcstat == 's') mouseovers = XLAT("Config file: %1", conffile);
|
|
|
|
dialog::addBack();
|
|
dialog::display();
|
|
}
|
|
|
|
#if CAP_COMMANDLINE
|
|
|
|
EX 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("-ringw")) {
|
|
PHASEFROM(2); shift_arg_formula(vid.multiplier_ring);
|
|
}
|
|
else if(argis("-stdgrid")) {
|
|
PHASEFROM(2); shift(); stdgridcolor = arghex();
|
|
}
|
|
else if(argis("-gridw")) {
|
|
PHASEFROM(2); shift_arg_formula(vid.multiplier_grid);
|
|
}
|
|
else if(argis("-period")) {
|
|
PHASEFROM(2); shift(); periodcolor = arghex();
|
|
}
|
|
else if(argis("-crosshair")) {
|
|
PHASEFROM(2); shift(); crosshair_color = arghex();
|
|
shift_arg_formula(crosshair_size);
|
|
}
|
|
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;
|
|
}
|
|
|
|
EX 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; }
|
|
else if(argis("-gridon")) { vid.grid = 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;
|
|
}
|
|
#if MAXMDIM >= 4
|
|
else if(argis("-switch-fpp")) {
|
|
PHASEFROM(2);
|
|
geom3::switch_fpp();
|
|
}
|
|
#endif
|
|
else if(argis("-switch-tpp")) {
|
|
PHASEFROM(2);
|
|
geom3::switch_tpp();
|
|
}
|
|
#if MAXMDIM >= 4
|
|
else if(argis("-switch-3d")) {
|
|
PHASEFROM(2);
|
|
geom3::switch_always3();
|
|
}
|
|
#endif
|
|
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("-fov")) {
|
|
PHASEFROM(2); shift_arg_formula(vid.fov);
|
|
}
|
|
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("-mrsv")) {
|
|
PHASEFROM(2); shift(); reserve_limit = argi(); apply_memory_reserve();
|
|
}
|
|
else if(argis("-yca")) {
|
|
PHASEFROM(2);
|
|
shift_arg_formula(vid.yshift);
|
|
shift_arg_formula(vid.camera_angle);
|
|
}
|
|
else if(argis("-pside")) {
|
|
PHASEFROM(2);
|
|
permaside = true;
|
|
}
|
|
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("-noshaders")) {
|
|
PHASE(1);
|
|
glhr::noshaders = true;
|
|
}
|
|
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:iface")) {
|
|
PHASEFROM(2); launch_dialog(configureInterface);
|
|
}
|
|
else if(argis("-d:graph")) {
|
|
PHASEFROM(2); launch_dialog(showGraphConfig);
|
|
}
|
|
else if(argis("-tstep")) {
|
|
PHASEFROM(2); shift(); vid.texture_step = argi();
|
|
}
|
|
else if(argis("-csc")) {
|
|
PHASEFROM(2); shift_arg_formula(vid.creature_scale);
|
|
}
|
|
else if(argis("-char")) {
|
|
auto& cs = vid.cs;
|
|
shift(); cs.charid = argi();
|
|
cs.lefthanded = cs.charid >= 10;
|
|
cs.charid %= 10;
|
|
}
|
|
else return 1;
|
|
return 0;
|
|
}
|
|
|
|
// mode changes:
|
|
|
|
EX 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
|
|
|
|
EX 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},
|
|
{"eye", vid.eye},
|
|
{"depth", vid.depth},
|
|
{"camera", vid.camera},
|
|
{"wall_height", vid.wall_height},
|
|
{"highdetail", vid.highdetail},
|
|
{"middetail", vid.middetail},
|
|
{"rock_wall_ratio", vid.rock_wall_ratio},
|
|
{"human_wall_ratio", vid.human_wall_ratio},
|
|
{"lake_top", vid.lake_top},
|
|
{"lake_bottom", vid.lake_bottom},
|
|
#if CAP_RUG
|
|
{"rug_model_distance", rug::model_distance},
|
|
#endif
|
|
{"star", polygonal::STAR},
|
|
{"lvspeed", history::lvspeed},
|
|
{"rotation", models::rotation},
|
|
{"mori", models::model_orientation},
|
|
{"mori_yz", models::model_orientation_yz},
|
|
{"clipmin", models::clip_min},
|
|
{"clipmax", models::clip_max},
|
|
{"topz", models::top_z},
|
|
{"mtrans", models::model_transition},
|
|
{"hp", models::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},
|
|
{"fisheye", vid.fisheye_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", models::spiral_angle},
|
|
{"spiralx", models::spiral_x},
|
|
{"spiraly", models::spiral_y},
|
|
#if CAP_CRYSTAL
|
|
{"cprob", crystal::compass_probability},
|
|
#endif
|
|
#if CAP_SHOT
|
|
{"gamma", shot::gamma},
|
|
{"fade", shot::fade},
|
|
{"mgrid", vid.multiplier_grid},
|
|
{"mring", vid.multiplier_ring},
|
|
{"collignon", vid.collignon_parameter},
|
|
#endif
|
|
};
|
|
|
|
}
|