mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-12-26 18:10:35 +00:00
4211 lines
143 KiB
C++
4211 lines
143 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
|
|
enum eCentering { face, edge, vertex };
|
|
#endif
|
|
|
|
EX eCentering centering;
|
|
|
|
EX function<bool()> auto_restrict;
|
|
|
|
EX void add_to_changed(struct setting *f);
|
|
|
|
EX bool return_false() { return false; }
|
|
|
|
#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 bool affects(void* v) { return false; }
|
|
virtual void set_default() = 0;
|
|
virtual ~supersaver() = default;
|
|
virtual void swap_with(supersaver*) = 0;
|
|
virtual void clone(struct local_parameter_set& lps, void *value) = 0;
|
|
};
|
|
|
|
typedef vector<shared_ptr<supersaver>> saverlist;
|
|
|
|
extern saverlist savers;
|
|
|
|
struct setting {
|
|
function<bool()> restrict;
|
|
string parameter_name;
|
|
string config_name;
|
|
string menu_item_name;
|
|
string help_text;
|
|
reaction_t reaction;
|
|
char default_key;
|
|
bool is_editable;
|
|
supersaver *saver;
|
|
virtual bool available() { if(restrict) return restrict(); return true; }
|
|
virtual bool affects(void *v) { return false; }
|
|
virtual supersaver *make_saver() { return nullptr; }
|
|
virtual void register_saver() { saver = make_saver(); }
|
|
void show_edit_option() { show_edit_option(default_key); }
|
|
virtual void show_edit_option(int key) {
|
|
println(hlog, "default called!"); }
|
|
virtual string search_key() {
|
|
return parameter_name + "|" + config_name + "|" + menu_item_name + "|" + help_text;
|
|
}
|
|
explicit setting() { restrict = auto_restrict; is_editable = false; }
|
|
virtual void check_change() { }
|
|
reaction_t sets;
|
|
setting *set_sets(const reaction_t& s) { sets = s; return this; }
|
|
setting *set_extra(const reaction_t& r);
|
|
setting *set_reaction(const reaction_t& r);
|
|
virtual ~setting() = default;
|
|
virtual void load_from(const string& s) {
|
|
if(saver) { saver->load(s); return; }
|
|
println(hlog, "cannot load this parameter");
|
|
exit(1);
|
|
}
|
|
virtual bool load_from_animation(const string& s) {
|
|
load_from(s); return false;
|
|
}
|
|
virtual void load_as_animation(const string& s) {
|
|
load_from(s);
|
|
}
|
|
virtual bool anim_unchanged() { return true; }
|
|
virtual void anim_restore() { }
|
|
virtual cld get_cld() { throw hr_exception("parameter has no complex value"); }
|
|
virtual void set_cld_raw(cld x) { throw hr_exception("parameter has no complex value"); }
|
|
virtual void set_cld(cld value) {
|
|
auto bak = get_cld();
|
|
set_cld_raw(value);
|
|
if(value != bak && reaction) reaction();
|
|
}
|
|
};
|
|
#endif
|
|
|
|
setting *setting::set_extra(const reaction_t& r) {
|
|
auto s = sets; set_sets([s, r] { if(s) s(); dialog::get_di().extra_options = r; }); return this;
|
|
}
|
|
|
|
setting *setting::set_reaction(const reaction_t& r) {
|
|
reaction = r; return this;
|
|
}
|
|
|
|
EX map<string, std::unique_ptr<setting>> params;
|
|
|
|
EX void show_edit_option_enum(char* value, const string& name, const vector<pair<string, string>>& options, char key, setting *s);
|
|
|
|
#if HDR
|
|
struct list_setting : setting {
|
|
virtual int get_value() = 0;
|
|
virtual void set_value(int i) = 0;
|
|
vector<pair<string, string> > options;
|
|
list_setting* editable(const vector<pair<string, string> >& o, string menu_item_name, char key) {
|
|
is_editable = true;
|
|
options = o;
|
|
this->menu_item_name = menu_item_name;
|
|
default_key = key;
|
|
return this;
|
|
}
|
|
void show_edit_option(int key) override;
|
|
};
|
|
|
|
namespace anims {
|
|
extern void animate_setting(setting*, string);
|
|
}
|
|
|
|
template<class T> struct enum_setting : list_setting {
|
|
T *value, last_value, dft, anim_value;
|
|
int get_value() override { return (int) *value; }
|
|
void set_value(int i) override { *value = (T) i; }
|
|
bool affects(void* v) override { return v == value; }
|
|
supersaver *make_saver() override;
|
|
virtual void load_from_raw(const string& s) {
|
|
int N = isize(options);
|
|
for(int i=0; i<N; i++) if(appears(options[i].first, s)) {
|
|
*value = (T) i;
|
|
return;
|
|
}
|
|
*value = (T) parseint(s);
|
|
}
|
|
virtual void check_change() {
|
|
if(*value != last_value) {
|
|
last_value = *value;
|
|
add_to_changed(this);
|
|
}
|
|
}
|
|
|
|
void load_from(const string& s) override {
|
|
auto bak = *value;
|
|
load_from_raw(s);
|
|
if(*value != bak && reaction) reaction();
|
|
}
|
|
bool load_from_animation(const string& s) override {
|
|
if(anim_value != *value) return false;
|
|
load_from(s);
|
|
anim_value = *value;
|
|
return true;
|
|
}
|
|
void load_as_animation(const string& s) override {
|
|
load_from(s);
|
|
anim_value = *value;
|
|
anims::animate_setting(this, s);
|
|
}
|
|
};
|
|
|
|
/** transmatrix with equality, so we can construct val_setting<matrix_eq> */
|
|
struct matrix_eq : transmatrix {
|
|
bool operator == (const transmatrix& t) const {
|
|
for(int i=0; i<MAXMDIM; i++) for(int j=0; j<MAXMDIM; j++) if(self[i][j] != t[i][j]) return false;
|
|
return true;
|
|
}
|
|
bool operator != (const transmatrix& t) const {
|
|
return ! (self == t);
|
|
}
|
|
};
|
|
|
|
template<class T> struct val_setting : public setting {
|
|
T *value, last_value, anim_value, dft;
|
|
|
|
bool affects(void *v) override { return v == value; }
|
|
virtual void check_change() {
|
|
if(*value != last_value) {
|
|
last_value = *value;
|
|
add_to_changed(this);
|
|
}
|
|
}
|
|
|
|
virtual bool anim_unchanged() { return *value == anim_value; }
|
|
virtual void anim_restore() { *value = anim_value; if(reaction) reaction(); }
|
|
|
|
virtual void load_from_raw(const string& s) { throw hr_exception("load_from_raw not defined"); }
|
|
|
|
void load_from(const string& s) override {
|
|
auto bak = *value;
|
|
load_from_raw(s);
|
|
if(*value != bak && reaction) reaction();
|
|
}
|
|
bool load_from_animation(const string& s) override {
|
|
if(anim_value != *value) return false;
|
|
load_from(s);
|
|
anim_value = *value;
|
|
return true;
|
|
}
|
|
void load_as_animation(const string& s) override {
|
|
load_from(s);
|
|
anim_value = *value;
|
|
anims::animate_setting(this, s);
|
|
}
|
|
};
|
|
|
|
struct float_setting : public val_setting<ld> {
|
|
ld min_value, max_value, step;
|
|
string unit;
|
|
float_setting *editable(ld min_value, ld max_value, ld step, string menu_item_name, string help_text, char key) {
|
|
is_editable = true;
|
|
this->min_value = min_value;
|
|
this->max_value = max_value;
|
|
this->menu_item_name = menu_item_name;
|
|
this->help_text = help_text;
|
|
this->step = step;
|
|
default_key = key;
|
|
return this;
|
|
}
|
|
function<void(float_setting*)> modify_me;
|
|
float_setting *modif(const function<void(float_setting*)>& r) { modify_me = r; return this; }
|
|
supersaver *make_saver() override;
|
|
void show_edit_option(int key) override;
|
|
void load_from_raw(const string& s) override { *value = parseld(s); }
|
|
virtual cld get_cld() override { return *value; }
|
|
void set_cld_raw(cld x) override { *value = real(x); }
|
|
};
|
|
|
|
struct float_setting_dft : public float_setting {
|
|
void show_edit_option(int key) override;
|
|
function<ld()> get_hint;
|
|
float_setting_dft* set_hint(const function<ld()>& f) { get_hint = f; return this; }
|
|
};
|
|
|
|
struct int_setting : public val_setting<int> {
|
|
int min_value, max_value;
|
|
ld step;
|
|
supersaver *make_saver() override;
|
|
function<void(int_setting*)> modify_me;
|
|
int_setting *modif(const function<void(int_setting*)>& r) { modify_me = r; return this; }
|
|
void show_edit_option(int key) override;
|
|
int_setting *editable(int min_value, int max_value, ld step, string menu_item_name, string help_text, char key) {
|
|
this->is_editable = true;
|
|
this->min_value = min_value;
|
|
this->max_value = max_value;
|
|
this->menu_item_name = menu_item_name;
|
|
this->help_text = help_text;
|
|
this->step = step;
|
|
default_key = key;
|
|
return this;
|
|
}
|
|
|
|
virtual cld get_cld() override { return *value; }
|
|
|
|
void load_from_raw(const string& s) override { *value = parseint(s); }
|
|
void set_cld_raw(cld x) override { *value = (int)(real(x) + .5); }
|
|
|
|
virtual void check_change() {
|
|
if(*value != last_value) {
|
|
last_value = *value;
|
|
add_to_changed(this);
|
|
}
|
|
}
|
|
};
|
|
|
|
struct color_setting : public val_setting<color_t> {
|
|
bool has_alpha;
|
|
supersaver *make_saver() override;
|
|
void show_edit_option(int key) override;
|
|
color_setting *editable(string menu_item_name, string help_text, char key) {
|
|
this->is_editable = true;
|
|
this->menu_item_name = menu_item_name;
|
|
this->help_text = help_text;
|
|
default_key = key;
|
|
return this;
|
|
}
|
|
|
|
void load_from_raw(const string& s) override { sscanf(s.c_str(), "%x", value); }
|
|
};
|
|
|
|
struct matrix_setting : public val_setting<matrix_eq> {
|
|
int dim;
|
|
supersaver *make_saver() override;
|
|
void show_edit_option(int key) override;
|
|
matrix_setting *editable(string menu_item_name, string help_text, char key) {
|
|
this->is_editable = true;
|
|
this->menu_item_name = menu_item_name;
|
|
this->help_text = help_text;
|
|
default_key = key;
|
|
return this;
|
|
}
|
|
|
|
void load_from_raw(const string& s) override { (transmatrix&)*value = parsematrix(s); }
|
|
};
|
|
|
|
struct char_setting : public val_setting<char> {
|
|
supersaver *make_saver() override;
|
|
void show_edit_option(int key) override;
|
|
|
|
void load_from_raw(const string& s) override {
|
|
if(s == "\\0") *value = 0;
|
|
else sscanf(s.c_str(), "%c", value);
|
|
}
|
|
};
|
|
|
|
struct bool_setting : public val_setting<bool> {
|
|
supersaver *make_saver() override;
|
|
reaction_t switcher;
|
|
bool_setting* editable(string cap, char key ) {
|
|
is_editable = true;
|
|
menu_item_name = cap; default_key = key; return this;
|
|
}
|
|
void show_edit_option(int key) override;
|
|
|
|
void load_from_raw(const string& s) override { *value = parseint(s); }
|
|
|
|
virtual cld get_cld() override { return *value; }
|
|
};
|
|
|
|
struct custom_setting : public setting {
|
|
cld last_value;
|
|
function<void(char)> custom_viewer;
|
|
function<cld()> custom_value;
|
|
function<bool(void*)> custom_affect;
|
|
void show_edit_option(int key) override { custom_viewer(key); }
|
|
supersaver *make_saver() { throw hr_exception("make_saver for custom_setting"); }
|
|
bool affects(void *v) override { return custom_affect(v); }
|
|
virtual void check_change() {
|
|
if(custom_value() != last_value) {
|
|
last_value = custom_value();
|
|
add_to_changed(this);
|
|
}
|
|
}
|
|
};
|
|
|
|
struct local_parameter_set {
|
|
string label;
|
|
local_parameter_set* extends;
|
|
vector<pair<supersaver*, supersaver*>> swaps;
|
|
void pswitch();
|
|
local_parameter_set(string l, local_parameter_set *ext = nullptr) : label(l), extends(ext) {}
|
|
};
|
|
|
|
#if CAP_CONFIG
|
|
|
|
template<class T> struct dsaver : supersaver {
|
|
T& val;
|
|
T dft;
|
|
bool dosave() override { return val != dft; }
|
|
void reset() override { val = dft; }
|
|
explicit dsaver(T& val) : val(val) { }
|
|
bool affects(void* v) override { return v == &val; }
|
|
void set_default() override { dft = val; }
|
|
};
|
|
|
|
template<class T> struct saver : dsaver<T> {};
|
|
|
|
template<class T, class U, class V> supersaver* addsaver(T& i, U name, V dft) {
|
|
auto s = make_shared<saver<T>> (i);
|
|
s->dft = dft;
|
|
s->name = name;
|
|
savers.push_back(s);
|
|
return &*s;
|
|
}
|
|
|
|
template<class T> supersaver* addsaver(T& i, string name) {
|
|
return addsaver(i, name, i);
|
|
}
|
|
|
|
template<class T> void removesaver(T& val) {
|
|
for(int i=0; i<isize(savers); i++)
|
|
if(savers[i]->affects(&val))
|
|
savers.erase(savers.begin() + i);
|
|
}
|
|
|
|
template<class T> void set_saver_default(T& val) {
|
|
for(auto sav: savers)
|
|
if(sav->affects(&val))
|
|
sav->set_default();
|
|
}
|
|
|
|
template<class T, class U> supersaver *addsaverenum(T& i, U name);
|
|
|
|
template<class T> struct saverenum : supersaver {
|
|
T& val;
|
|
T dft;
|
|
explicit saverenum(T& v) : val(v) { }
|
|
bool dosave() override { return val != dft; }
|
|
void reset() override { val = dft; }
|
|
string save() override { return its(int(val)); }
|
|
void load(const string& s) override { val = (T) atoi(s.c_str()); }
|
|
bool affects(void* v) override { return v == &val; }
|
|
void set_default() override { dft = val; }
|
|
void clone(struct local_parameter_set& lps, void *value) override { addsaverenum(*(T*) value, lps.label + name); }
|
|
void swap_with(supersaver *s) { swap(val, ((saverenum<T>*)s)->val); }
|
|
};
|
|
|
|
template<class T, class U> supersaver *addsaverenum(T& i, U name, T dft) {
|
|
auto s = make_shared<saverenum<T>> (i);
|
|
s->dft = dft;
|
|
s->name = name;
|
|
savers.push_back(s);
|
|
return &*s;
|
|
}
|
|
|
|
template<class T, class U> supersaver *addsaverenum(T& i, U name) {
|
|
return addsaverenum(i, name, i);
|
|
}
|
|
|
|
template<> struct saver<int> : dsaver<int> {
|
|
explicit saver(int& val) : dsaver<int>(val) { }
|
|
string save() override { return its(val); }
|
|
void load(const string& s) override { val = atoi(s.c_str()); }
|
|
virtual void clone(struct local_parameter_set& lps, void *value) override { addsaver(*(int*) value, lps.label + name); }
|
|
virtual void swap_with(supersaver *s) { swap(val, ((saver<int>*)s)->val); }
|
|
};
|
|
|
|
template<> struct saver<char> : dsaver<char> {
|
|
explicit saver(char& val) : dsaver<char>(val) { }
|
|
string save() override { return its(val); }
|
|
void load(const string& s) override { val = atoi(s.c_str()); }
|
|
virtual void clone(struct local_parameter_set& lps, void *value) override { addsaver(*(char*) value, lps.label + name); }
|
|
virtual void swap_with(supersaver *s) { swap(val, ((saver<char>*)s)->val); }
|
|
};
|
|
|
|
template<> struct saver<bool> : dsaver<bool> {
|
|
explicit saver(bool& val) : dsaver<bool>(val) { }
|
|
string save() override { return val ? "yes" : "no"; }
|
|
void load(const string& s) override { val = isize(s) && s[0] == 'y'; }
|
|
virtual void clone(struct local_parameter_set& lps, void *value) override { addsaver(*(bool*) value, lps.label + name); }
|
|
virtual void swap_with(supersaver *s) { swap(val, ((saver<bool>*)s)->val); }
|
|
};
|
|
|
|
template<> struct saver<unsigned> : dsaver<unsigned> {
|
|
explicit saver(unsigned& val) : dsaver<unsigned>(val) { }
|
|
string save() override { return itsh(val); }
|
|
void load(const string& s) override { val = (unsigned) strtoll(s.c_str(), NULL, 16); }
|
|
virtual void clone(struct local_parameter_set& lps, void *value) override { addsaver(*(unsigned*) value, lps.label + name); }
|
|
virtual void swap_with(supersaver *s) { swap(val, ((saver<unsigned>*)s)->val); }
|
|
};
|
|
|
|
template<> struct saver<string> : dsaver<string> {
|
|
explicit saver(string& val) : dsaver<string>(val) { }
|
|
string save() override { return val; }
|
|
void load(const string& s) override { val = s; }
|
|
virtual void clone(struct local_parameter_set& lps, void *value) override { addsaver(*(string*) value, lps.label + name); }
|
|
virtual void swap_with(supersaver *s) { swap(val, ((saver<string>*)s)->val); }
|
|
};
|
|
|
|
template<> struct saver<matrix_eq> : supersaver {
|
|
|
|
matrix_eq& val;
|
|
matrix_eq dft;
|
|
|
|
explicit saver(matrix_eq& _val) : val(_val) { }
|
|
|
|
void reset() override { val = dft; }
|
|
bool affects(void* v) override { return v == &val; }
|
|
void set_default() override { dft = val; }
|
|
bool dosave() override { return !eqmatrix(val, dft); }
|
|
|
|
string save() override {
|
|
shstream ss;
|
|
for(int a=0; a<4; a++) for(int b=0; b<4; b++) print(ss, val[a][b], " ");
|
|
return ss.s;
|
|
}
|
|
void load(const string& s) override {
|
|
shstream ss;
|
|
ss.s = s;
|
|
for(int a=0; a<4; a++) for(int b=0; b<4; b++) scan(ss, val[a][b]);
|
|
}
|
|
virtual void clone(struct local_parameter_set& lps, void *value) override { addsaver(*(matrix_eq*) value, lps.label + name); }
|
|
virtual void swap_with(supersaver *s) override { swap(val, ((saver<matrix_eq>*)s)->val); }
|
|
};
|
|
|
|
template<> struct saver<ld> : dsaver<ld> {
|
|
explicit saver(ld& val) : dsaver<ld>(val) { }
|
|
string save() override { return fts(val, 10); }
|
|
void load(const string& s) override {
|
|
if(s == "0.0000000000e+000") ; // ignore!
|
|
else val = atof(s.c_str());
|
|
}
|
|
virtual void clone(struct local_parameter_set& lps, void *value) override { addsaver(*(ld*) value, lps.label + name); }
|
|
virtual void swap_with(supersaver *s) { swap(val, ((saver<ld>*)s)->val); }
|
|
};
|
|
#endif
|
|
#endif
|
|
|
|
supersaver *float_setting::make_saver() {
|
|
#if CAP_CONFIG
|
|
return addsaver(*value, config_name, dft);
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
supersaver *int_setting::make_saver() {
|
|
#if CAP_CONFIG
|
|
return addsaver(*value, config_name, dft);
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
supersaver* color_setting::make_saver() {
|
|
#if CAP_CONFIG
|
|
return addsaver(*value, config_name, dft);
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
supersaver* matrix_setting::make_saver() {
|
|
#if CAP_CONFIG
|
|
return addsaver(*value, config_name, dft);
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
supersaver *char_setting::make_saver() {
|
|
#if CAP_CONFIG
|
|
return addsaver(*value, config_name, dft);
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
supersaver *bool_setting::make_saver() {
|
|
#if CAP_CONFIG
|
|
return addsaver(*value, config_name, dft);
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
void non_editable() {
|
|
dialog::addHelp("Warning: editing this value through this menu may not work correctly");
|
|
}
|
|
|
|
void float_setting::show_edit_option(int key) {
|
|
if(modify_me) modify_me(this);
|
|
dialog::addSelItem(XLAT(menu_item_name), fts(*value) + unit, key);
|
|
if(*value == use_the_default_value) dialog::lastItem().value = XLAT("default");
|
|
dialog::add_action([this] () {
|
|
add_to_changed(this);
|
|
dialog::editNumber(*value, min_value, max_value, step, dft, XLAT(menu_item_name), help_text);
|
|
if(sets) sets();
|
|
if(reaction) dialog::get_di().reaction = reaction;
|
|
if(!is_editable) dialog::get_di().extra_options = non_editable;
|
|
});
|
|
}
|
|
|
|
void float_setting_dft::show_edit_option(int key) {
|
|
if(modify_me) modify_me(this);
|
|
dialog::addSelItem(XLAT(menu_item_name), fts(*value) + unit, key);
|
|
if(*value == use_the_default_value) dialog::lastItem().value = XLAT("default: ") + fts(get_hint());
|
|
dialog::add_action([this] () {
|
|
add_to_changed(this);
|
|
if(*value == use_the_default_value) *value = get_hint();
|
|
dialog::editNumber(*value, min_value, max_value, step, dft, XLAT(menu_item_name), help_text);
|
|
if(sets) sets();
|
|
if(reaction) dialog::get_di().reaction = reaction;
|
|
if(!is_editable) dialog::get_di().extra_options = non_editable;
|
|
auto eo = dialog::get_di().extra_options;
|
|
dialog::get_di().extra_options = [eo, this] {
|
|
dialog::addSelItem(XLAT("use the default value"), "", 'D');
|
|
dialog::add_action([this] { *value = use_the_default_value; });
|
|
if(eo) eo();
|
|
};
|
|
});
|
|
}
|
|
|
|
void int_setting::show_edit_option(int key) {
|
|
if(modify_me) modify_me(this);
|
|
dialog::addSelItem(XLAT(menu_item_name), its(*value), key);
|
|
dialog::add_action([this] () {
|
|
add_to_changed(this);
|
|
dialog::editNumber(*value, min_value, max_value, step, dft, XLAT(menu_item_name), help_text);
|
|
if(sets) sets();
|
|
if(reaction) dialog::get_di().reaction = reaction;
|
|
if(!is_editable) dialog::get_di().extra_options = non_editable;
|
|
});
|
|
}
|
|
|
|
void bool_setting::show_edit_option(int key) {
|
|
dialog::addBoolItem(XLAT(menu_item_name), *value, key);
|
|
dialog::add_action([this] () {
|
|
add_to_changed(this);
|
|
switcher(); if(sets) sets();
|
|
if(reaction) reaction();
|
|
});
|
|
}
|
|
|
|
void color_setting::show_edit_option(int key) {
|
|
dialog::addColorItem(XLAT(menu_item_name), has_alpha ? *value : addalpha(*value), key);
|
|
dialog::add_action([this] () {
|
|
dialog::openColorDialog(*value);
|
|
dialog::colorAlpha = has_alpha;
|
|
dialog::get_di().dialogflags |= sm::SIDE;
|
|
});
|
|
}
|
|
|
|
void matrix_setting::show_edit_option(int key) {
|
|
dialog::addMatrixItem(XLAT(menu_item_name), *value, key);
|
|
dialog::add_action([this] () {
|
|
dialog::editMatrix(*value, XLAT(menu_item_name), help_text, dim);
|
|
if(sets) sets();
|
|
if(reaction) dialog::get_di().reaction = reaction;
|
|
});
|
|
}
|
|
|
|
void char_setting::show_edit_option(int key) {
|
|
string s = s0; s += value;
|
|
dialog::addSelItem(XLAT(menu_item_name), s, key);
|
|
}
|
|
|
|
EX float_setting *param_f(ld& val, const string p, const string s, ld dft) {
|
|
unique_ptr<float_setting> u ( new float_setting );
|
|
u->parameter_name = p;
|
|
u->config_name = s;
|
|
u->menu_item_name = s;
|
|
u->value = &val;
|
|
u->last_value = dft;
|
|
if(dft == 0) {
|
|
u->min_value = -100;
|
|
u->max_value = +100;
|
|
}
|
|
else {
|
|
u->min_value = 0;
|
|
u->max_value = 2 * dft;
|
|
}
|
|
u->step = dft / 10;
|
|
u->dft = dft;
|
|
val = dft;
|
|
u->register_saver();
|
|
auto f = &*u;
|
|
params[u->parameter_name] = std::move(u);
|
|
return f;
|
|
}
|
|
|
|
EX float_setting_dft *param_fd(ld& val, const string s, ld dft IS(use_the_default_value) ) {
|
|
unique_ptr<float_setting_dft> u ( new float_setting_dft );
|
|
u->parameter_name = s;
|
|
u->config_name = s;
|
|
u->menu_item_name = s;
|
|
u->value = &val;
|
|
u->last_value = dft;
|
|
u->min_value = -100;
|
|
u->max_value = +100;
|
|
u->step = 1;
|
|
u->dft = dft;
|
|
val = dft;
|
|
u->register_saver();
|
|
auto f = &*u;
|
|
params[u->parameter_name] = std::move(u);
|
|
return f;
|
|
}
|
|
|
|
EX string param_esc(string s) {
|
|
string out;
|
|
for(char c: s)
|
|
if(c == ' ' || c == '-' || c == ':')
|
|
out += '_';
|
|
else
|
|
out += c;
|
|
return out;
|
|
}
|
|
|
|
EX int_setting *param_i(int& val, const string s, int dft) {
|
|
unique_ptr<int_setting> u ( new int_setting );
|
|
u->parameter_name = param_esc(s);
|
|
u->config_name = s;
|
|
u->menu_item_name = s;
|
|
u->value = &val;
|
|
u->last_value = dft;
|
|
u->dft = dft;
|
|
val = dft;
|
|
if(dft == 0) {
|
|
u->min_value = -100;
|
|
u->max_value = +100;
|
|
}
|
|
else {
|
|
u->min_value = 0;
|
|
u->max_value = 2 * dft;
|
|
}
|
|
u->register_saver();
|
|
auto f = &*u;
|
|
params[u->parameter_name] = std::move(u);
|
|
return f;
|
|
}
|
|
|
|
EX int_setting *param_i(int& val, const string s) { return param_i(val, s, val); }
|
|
|
|
EX bool_setting *param_b(bool& val, const string s, bool dft) {
|
|
unique_ptr<bool_setting> u ( new bool_setting );
|
|
u->parameter_name = param_esc(s);
|
|
u->config_name = s;
|
|
u->menu_item_name = s;
|
|
u->value = &val;
|
|
u->last_value = dft;
|
|
u->dft = dft;
|
|
u->switcher = [&val] { val = !val; };
|
|
val = dft;
|
|
u->register_saver();
|
|
auto f = &*u;
|
|
params[u->parameter_name] = std::move(u);
|
|
return f;
|
|
}
|
|
|
|
EX color_setting *param_color(color_t& val, const string s, bool has_alpha, color_t dft) {
|
|
unique_ptr<color_setting> u ( new color_setting );
|
|
u->parameter_name = param_esc(s);
|
|
u->config_name = s;
|
|
u->menu_item_name = s;
|
|
u->value = &val;
|
|
u->last_value = dft;
|
|
u->dft = dft;
|
|
u->has_alpha = has_alpha;
|
|
val = dft;
|
|
u->register_saver();
|
|
auto f = &*u;
|
|
params[u->parameter_name] = std::move(u);
|
|
return f;
|
|
}
|
|
|
|
EX matrix_setting *param_matrix(transmatrix& val0, const string s, int dim) {
|
|
matrix_eq& val = (matrix_eq&) val0;
|
|
unique_ptr<matrix_setting> u ( new matrix_setting );
|
|
u->parameter_name = param_esc(s);
|
|
u->config_name = s;
|
|
u->menu_item_name = s;
|
|
u->value = &val;
|
|
u->last_value = val;
|
|
u->dft = val;
|
|
u->dim = dim;
|
|
u->register_saver();
|
|
auto f = &*u;
|
|
params[u->parameter_name] = std::move(u);
|
|
return f;
|
|
}
|
|
|
|
EX char_setting *param_char(char& val, const string s, char dft) {
|
|
unique_ptr<char_setting> u ( new char_setting );
|
|
u->parameter_name = param_esc(s);
|
|
u->config_name = s;
|
|
u->menu_item_name = s;
|
|
u->value = &val;
|
|
u->last_value = dft;
|
|
u->dft = dft;
|
|
val = dft;
|
|
u->register_saver();
|
|
auto f = &*u;
|
|
params[u->parameter_name] = std::move(u);
|
|
return f;
|
|
}
|
|
|
|
EX color_setting *param_color(color_t& val, const string s, bool has_alpha) { return param_color(val, s, has_alpha, val); }
|
|
|
|
EX bool_setting *param_b(bool& val, const string s) { return param_b(val, s, val); }
|
|
|
|
#if HDR
|
|
template<class T> supersaver* enum_setting<T>::make_saver() {
|
|
#if CAP_CONFIG
|
|
return addsaverenum(*value, config_name, dft);
|
|
#endif
|
|
return nullptr;
|
|
}
|
|
|
|
template<class T> enum_setting<T> *param_enum(T& val, const string p, const string s, T dft) {
|
|
unique_ptr<enum_setting<T>> u ( new enum_setting<T> );
|
|
u->parameter_name = p;
|
|
u->config_name = s;
|
|
u->menu_item_name = s;
|
|
u->value = &val;
|
|
u->dft = dft;
|
|
val = dft;
|
|
u->last_value = dft;
|
|
u->register_saver();
|
|
auto f = &*u;
|
|
params[p] = std::move(u);
|
|
return f;
|
|
}
|
|
#endif
|
|
|
|
EX float_setting* param_f(ld& val, const string s) {
|
|
return param_f(val, param_esc(s), s, val);
|
|
}
|
|
|
|
EX float_setting* param_f(ld& val, const string p, const string s) {
|
|
return param_f(val, p, s, val);
|
|
}
|
|
|
|
EX float_setting* param_f(ld& val, const string s, ld dft) {
|
|
return param_f(val, param_esc(s), s, dft);
|
|
}
|
|
|
|
#if HDR
|
|
template<class T>
|
|
custom_setting* param_custom(T& val, const string& s, function<void(char)> menuitem, char key) {
|
|
unique_ptr<custom_setting> u ( new custom_setting );
|
|
u->parameter_name = param_esc(s);
|
|
u->config_name = s;
|
|
u->menu_item_name = s;
|
|
u->last_value = (int) val;
|
|
u->custom_viewer = menuitem;
|
|
u->custom_value = [&val] () { return (int) val; };
|
|
u->custom_affect = [&val] (void *v) { return &val == v; };
|
|
u->default_key = key;
|
|
u->is_editable = true;
|
|
auto f = &*u;
|
|
params[s] = std::move(u);
|
|
return f;
|
|
}
|
|
#endif
|
|
|
|
EX ld bounded_mine_percentage = 0.1;
|
|
EX int bounded_mine_quantity, bounded_mine_max;
|
|
|
|
EX const char *conffile = "hyperrogue.ini";
|
|
|
|
/* extra space if more geometries are added */
|
|
EX array<ld, gGUARD+64> sightranges;
|
|
|
|
EX bool logfog;
|
|
|
|
EX videopar vid;
|
|
|
|
#define DEFAULT_WALLMODE (ISMOBILE ? 3 : 5)
|
|
#define DEFAULT_MONMODE (ISMOBILE ? 2 : 4)
|
|
|
|
EX void android_settings_changed() {
|
|
#if ISANDROID
|
|
settingsChanged = true;
|
|
#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
|
|
#if HDR
|
|
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) { i = dft; }
|
|
#endif
|
|
#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 purehookset hooks_configfile;
|
|
|
|
EX void initConfig() {
|
|
|
|
// basic config
|
|
param_i(vid.flashtime, "flashtime", 8);
|
|
param_enum(vid.msgleft, "message_style", "message style", 2)
|
|
-> editable({{"centered", ""}, {"left-aligned", ""}, {"line-broken", ""}}, "message style", 'a');
|
|
|
|
param_i(vid.msglimit, "message limit", 5);
|
|
param_i(vid.timeformat, "message log time format", 0);
|
|
|
|
param_b(resizable, "resizable", true)
|
|
-> editable("resizable window", 'r');
|
|
|
|
param_b(no_find_player, "no_find_player");
|
|
param_b(game_keys_scroll, "game_keys_scroll")
|
|
-> editable("pure exploration (game keys scroll)", 'P');
|
|
param_b(reg3::cubes_reg3, "cubes_reg3");
|
|
param_f(linepatterns::tree_starter, "tree_starter")
|
|
-> editable(0, 1, 0.05, "tree-drawing parameter", "How much of edges to draw for tree patterns (to show how the tree edges are oriented).", 't');
|
|
|
|
param_char(patterns::whichCanvas, "whichCanvas", 0);
|
|
param_i(patterns::rwalls, "randomwalls");
|
|
|
|
param_b(vid.grid, "grid");
|
|
param_b(models::desitter_projections, "desitter_projections", false);
|
|
param_b(nonisotropic_weird_transforms, "nonisotropic_weird_transforms", false);
|
|
|
|
param_b(arb::apeirogon_consistent_coloring, "apeirogon_consistent_coloring", true)
|
|
-> editable("apeirogon_consistent_coloring", 'c');
|
|
param_b(arb::apeirogon_hide_grid_edges, "apeirogon_hide_grid_edges", true)
|
|
-> editable("apeirogon_hide_grid_edges", 'h');
|
|
param_b(arb::apeirogon_simplified_display, "apeirogon_simplified_display", false)
|
|
-> editable("simplified display of apeirogons", 'f');
|
|
param_b(arb::convert::minimize_on_convert, "tes_minimize_on_convert", false)
|
|
-> editable("consider all symmetries when converting", 'm');
|
|
param_b(arb::convert::reverse_order, "tes_reverse_order", false)
|
|
-> editable("tes reverse order on convert", 'r');
|
|
|
|
param_b(display_yasc_codes, "yasc", false)
|
|
-> editable("YASC codes", 'Y')
|
|
-> set_reaction([] {
|
|
addMessage(XLAT("YASC codes: Sides-Entity-Restrict-Threat-Wall"));
|
|
});
|
|
|
|
param_b(vid.relative_font, "relative_font", true)
|
|
-> editable("set relative font size", 'r')
|
|
-> set_reaction(compute_fsize);
|
|
param_i(vid.fontscale, "fontscale", 100)
|
|
-> editable(25, 400, 10, "font scale", "", 'b')
|
|
-> set_reaction(compute_fsize)
|
|
-> set_sets([] { dialog::bound_low(0); });
|
|
|
|
param_i(vid.abs_fsize, "fsize", 12)
|
|
-> editable(1, 72, 1, "font size", "", 'b')
|
|
-> set_reaction(compute_fsize)
|
|
-> set_sets([] { dialog::bound_low(0); });
|
|
|
|
param_i(vid.mobilecompasssize, "mobile compass size", 0); // ISMOBILE || ISPANDORA ? 30 : 0);
|
|
param_i(vid.radarsize, "radarsize size", 120);
|
|
param_f(vid.radarrange, "radarrange", 2.5);
|
|
param_i(vid.axes, "movement help", 1);
|
|
param_b(vid.axes3, "movement help3", true);
|
|
param_i(vid.shifttarget, "shift-targetting", 2);
|
|
addsaver(vid.steamscore, "scores to Steam", 1);
|
|
initcs(vid.cs); addsaver(vid.cs, "single");
|
|
param_b(vid.samegender, "princess choice", false);
|
|
addsaver(vid.language, "language", -1);
|
|
param_b(vid.drawmousecircle, "mouse circle", ISMOBILE || ISPANDORA);
|
|
param_b(vid.revcontrol, "reverse control", false);
|
|
#if CAP_AUDIO
|
|
param_i(musicvolume, "music volume")
|
|
->editable(0, 128, 10, "background music volume", "", 'b')
|
|
->set_sets(sets_music_volume);
|
|
#endif
|
|
#if CAP_SDLAUDIO
|
|
addsaver(music_out_of_focus, "music out of focus", false);
|
|
#endif
|
|
#if CAP_AUDIO
|
|
param_i(effvolume, "sound effect volume")
|
|
->editable(0, 128, 10, "sound effects volume", "", 'e')
|
|
->set_sets(sets_sfx_volume);
|
|
#endif
|
|
|
|
param_enum(vid.faraway_highlight, "faraway_highlight", "highlight faraway monsters", tlNoThreat)
|
|
->editable({{"off", ""}, {"spam", ""}, {"normal monsters", ""}, {"high-threat monsters only", ""}}, "highlight faraway monsters", 'h');
|
|
|
|
param_i(vid.faraway_highlight_color, "faraway_highlight_color", 50)
|
|
-> editable(0, 100, 10, "faraway highlight color", "0 = monster color, 100 = red-light oscillation", 'c');
|
|
|
|
param_enum(glyphsortorder, "glyph_sort", "glyph sort order", glyphsortorder)
|
|
->editable({
|
|
{"first on top", ""},
|
|
{"first on bottom", ""},
|
|
{"last on top", ""},
|
|
{"last on bottom", ""},
|
|
{"by land", ""},
|
|
{"by number", ""}
|
|
}, "inventory/kill sorting", 'k');
|
|
|
|
param_enum(vid.orbmode, "orb_mode", "orb display mode", 2)
|
|
->editable({
|
|
{"plain", ""},
|
|
{"types", ""},
|
|
{"icons", ""},
|
|
}, "orb display mode", 'o');
|
|
|
|
param_b(less_in_landscape, "less_in_landscape", false)
|
|
->editable("less items/kills in landscape", 'L')
|
|
-> set_sets([] { dialog::get_di().reaction_final = [] { println(hlog, "Reset"); vid.killreduction = 0; }; });
|
|
|
|
param_b(less_in_portrait, "less_in_portrait", false)
|
|
->editable("less items/kills in portrait", 'P')
|
|
-> set_sets([] { dialog::get_di().reaction_final = [] { println(hlog, "Reset"); vid.killreduction = 0; }; });
|
|
|
|
// basic graphics
|
|
|
|
param_b(vid.wantGL, "usingGL", true)
|
|
->editable("openGL mode", 'o');
|
|
|
|
addsaver(vid.want_antialias, "antialias", AA_NOGL | AA_FONT | (ISWEB ? AA_MULTI : AA_LINES) | AA_VERSION);
|
|
param_b(vid.fineline, "fineline", true);
|
|
param_f(vid.linewidth, "linewidth", 1);
|
|
addsaver(precise_width, "precisewidth", .5);
|
|
param_i(perfect_linewidth, "perfect_linewidth", 1);
|
|
param_f(linepatterns::width, "lpwidth", "pattern-linewidth", 1);
|
|
addsaver(fat_edges, "fat-edges");
|
|
param_f(vid.sspeed, "sspeed", "scrollingspeed", 0);
|
|
param_f(vid.mspeed, "mspeed", "movement speed", 1);
|
|
param_f(vid.ispeed, "ispeed", "idle speed", 1);
|
|
addsaver(vid.aurastr, "aura strength", ISMOBILE ? 0 : 128);
|
|
addsaver(vid.aurasmoothen, "aura smoothen", 5);
|
|
param_enum(vid.graphglyph, "graphglyph", "graphical items/kills", 1)
|
|
-> editable({{"letters", ""}, {"auto", ""}, {"images", ""}}, "inventory/kill mode", 'd');
|
|
|
|
param_i(min_cells_drawn, "min_cells_drawn");
|
|
|
|
param_i(menu_darkening, "menu_darkening", 2)
|
|
-> editable(0, 8, 1, "menu map darkening", "A larger number means darker game map in the background. Set to 8 to disable the background.", 'd')
|
|
-> set_sets([] { dialog::bound_low(0); dialog::bound_up(8); dialog::get_di().dialogflags |= sm::DARKEN; });
|
|
param_b(centered_menus, "centered_menus", false)
|
|
-> editable("centered menus in widescreen", 'c');
|
|
|
|
param_b(startanims::enabled, "startanim", true)
|
|
-> editable("start animations", 's');
|
|
|
|
addsaver(vid.flasheffects, "flasheffects", 1);
|
|
|
|
param_f(vid.binary_width, "bwidth", "binary-tiling-width", 1);
|
|
param_custom(vid.binary_width, "binary tiling width", menuitem_binary_width, 'v');
|
|
|
|
param_b(fake::multiple_special_draw, "fake_multiple", true);
|
|
|
|
param_f(hat::hat_param, "hat_param", "hat_param", 1)
|
|
-> editable(0, 2, 0.1, "hat/spectre/turtle parameter",
|
|
"Apeirodic hat tiling based on: https://arxiv.org/pdf/2303.10798.pdf\n\n"
|
|
"This controls the parameter discussed in Section 6. Parameter p is Tile(p, (2-p)√3), scaled so that the area is the same for every p.\n\n"
|
|
"Aperiodic spectre tiling based on: https://arxiv.org/abs/2305.17743\n\n"
|
|
"In the spectre tiling, set the parameter to 'spectre' value to make all tiles have the same shape."
|
|
,
|
|
'v'
|
|
)
|
|
-> set_extra([] {
|
|
dialog::addSelItem(XLAT("chevron (periodic)"), "0", 'C');
|
|
dialog::add_action([] { dialog::get_ne().s = "0"; dialog::get_ne().apply_edit(); });
|
|
dialog::addSelItem(XLAT("hat"), "1", 'H');
|
|
dialog::add_action([] { dialog::get_ne().s = "1"; dialog::get_ne().apply_edit(); });
|
|
dialog::addSelItem(XLAT("spectre"), "3-√3", 'T');
|
|
dialog::add_action([] { dialog::get_ne().s = "3 - sqrt(3)"; dialog::get_ne().apply_edit(); });
|
|
dialog::addSelItem(XLAT("turtle"), "1.5", 'T');
|
|
dialog::add_action([] { dialog::get_ne().s = "1.5"; dialog::get_ne().apply_edit(); });
|
|
dialog::addSelItem(XLAT("comma (periodic)"), "2", ',');
|
|
dialog::add_action([] { dialog::get_ne().s = "2"; dialog::get_ne().apply_edit(); });
|
|
})
|
|
-> set_reaction(hat::reshape);
|
|
|
|
param_f(hat::hat_param_imag, "hat_param_imag", "hat_param_imag", 0)
|
|
-> editable(0, 2, 0.1, "hat parameter (imaginary)",
|
|
"Imaginary part of the hat parameter. This corresponds to the usual interpretation of complex numbers in Euclidean planar geometry: rather than shortened or lengthened, the edges are moved in the other dimension.", 'v'
|
|
)
|
|
-> set_reaction(hat::reshape);
|
|
|
|
addsaver(vid.particles, "extra effects", 1);
|
|
param_i(vid.framelimit, "frame limit", 999);
|
|
|
|
#if !ISMOBWEB
|
|
param_b(vid.want_vsync, "vsync", true)
|
|
->editable("vsync", 'v');
|
|
#endif
|
|
|
|
param_b(vid.want_fullscreen, "fullscreen", false)
|
|
->editable("fullscreen mode", 'f');
|
|
param_b(vid.change_fullscr, "fullscreen_change", false)
|
|
->editable("use specific fullscreen resolution", 'g');
|
|
param_b(vid.relative_window_size, "window_relative", true)
|
|
->editable("specify relative window size", 'g');
|
|
|
|
param_custom(vid.xres, "xres", [] (char ch) {}, 0)->restrict = return_false;
|
|
param_custom(vid.yres, "yres", [] (char ch) {}, 0)->restrict = return_false;
|
|
|
|
param_i(vid.fullscreen_x, "fullscreen_x", 1280)
|
|
-> editable(640, 3840, 640, "fullscreen resolution to use (X)", "", 'x')
|
|
-> set_sets([] { dialog::bound_low(640); dialog::get_di().reaction_final = do_request_resolution_change; });
|
|
|
|
param_i(vid.fullscreen_y, "fullscreen_y", 1024)
|
|
-> editable(480, 2160, 480, "fullscreen resolution to use (Y)", "", 'x')
|
|
-> set_sets([] { dialog::bound_low(480); dialog::get_di().reaction_final = do_request_resolution_change; });
|
|
|
|
param_i(vid.window_x, "window_x", 1280)
|
|
-> editable(160, 3840, 160, "window resolution to use (X)", "", 'x')
|
|
-> set_sets([] { dialog::bound_low(160); dialog::get_di().reaction_final = do_request_resolution_change; });
|
|
|
|
param_i(vid.window_y, "window_y", 1024)
|
|
-> editable(120, 2160, 120, "window resolution to use (Y)", "", 'x')
|
|
-> set_sets([] { dialog::bound_low(120); dialog::get_di().reaction_final = do_request_resolution_change; });
|
|
|
|
param_f(vid.window_rel_x, "window_rel_x", .9)
|
|
-> editable(.1, 1, .1, "screen size percentage to use (X)", "", 'x')
|
|
-> set_sets([] { dialog::bound_low(.1); dialog::get_di().reaction_final = do_request_resolution_change; });
|
|
|
|
param_f(vid.window_rel_y, "window_rel_y", .9)
|
|
-> editable(.1, 1, .1, "screen size percentage to use (Y)", "", 'x')
|
|
-> set_sets([] { dialog::bound_low(.1); dialog::get_di().reaction_final = do_request_resolution_change; });
|
|
|
|
param_b(vid.darkhepta, "mark heptagons", false);
|
|
|
|
param_b(logfog, "logfog", false);
|
|
|
|
for(auto& lp: linepatterns::patterns) {
|
|
addsaver(lp->color, "lpcolor-" + lp->lpname);
|
|
addsaver(lp->multiplier, "lpwidth-" + lp->lpname);
|
|
}
|
|
|
|
// special graphics
|
|
|
|
addsaver(vid.monmode, "monster display mode", DEFAULT_MONMODE);
|
|
addsaver(vid.wallmode, "wall display mode", DEFAULT_WALLMODE);
|
|
addsaver(vid.highlightmode, "highlightmode");
|
|
|
|
addsaver(vid.always3, "3D always", false);
|
|
|
|
param_f(geom3::euclid_embed_scale, "euclid_embed_scale", "euclid_embed_scale")
|
|
-> editable(0, 2, 0.05, "Euclidean embedding scale", "How to scale the Euclidean map, relatively to the 3D absolute unit.", 'X')
|
|
-> set_sets([] { dialog::bound_low(0.05); })
|
|
-> set_reaction(geom3::apply_settings_light);
|
|
|
|
param_f(geom3::euclid_embed_scale_y, "euclid_embed_scale_y", "euclid_embed_scale_y")
|
|
-> editable(0, 2, 0.05, "Euclidean embedding scale Y/X", "This scaling factor affects only the Y coordinate.", 'Y')
|
|
-> set_sets([] { dialog::bound_low(0.05); })
|
|
-> set_reaction(geom3::apply_settings_light);
|
|
|
|
param_f(geom3::euclid_embed_rotate, "euclid_embed_rotate", "euclid_embed_rotate")
|
|
-> editable(0, 360, 15, "Euclidean embedding rotation", "How to rotate the Euclidean embedding, in degrees.", 'F')
|
|
-> set_reaction(geom3::apply_settings_light);
|
|
|
|
param_enum(embedded_shift_method_choice, "embedded_shift_method", "embedded_shift_method", smcBoth)
|
|
-> editable({
|
|
{"geodesic", "always move on geodesics"},
|
|
{"keep levels", "keep the vertical angle of the camera"},
|
|
{"mixed", "on geodesics when moving camera manually, keep level when auto-centering"}
|
|
}, "view shift for embedded planes", 'H');
|
|
|
|
param_b(geom3::auto_configure, "auto_configure_3d", "auto_configure_3d")
|
|
-> editable("set 3D settings automatically", 'A');
|
|
|
|
param_b(geom3::inverted_embedding, "inverted_3d", false)
|
|
-> editable("invert convex/concave", 'I')
|
|
-> set_reaction(geom3::apply_settings_full);
|
|
|
|
param_b(geom3::flat_embedding, "flat_3d", false)
|
|
-> editable("flat, not equidistant", 'F')
|
|
-> set_reaction(geom3::apply_settings_full);
|
|
|
|
param_enum(geom3::spatial_embedding, "spatial_embedding", "spatial_embedding", geom3::seDefault)
|
|
->editable(geom3::spatial_embedding_options, "3D embedding method", 'E')
|
|
->set_reaction(geom3::apply_settings_full);
|
|
|
|
param_b(memory_saving_mode, "memory_saving_mode", (ISMOBILE || ISPANDORA || ISWEB) ? 1 : 0);
|
|
param_i(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
|
|
param_f(rug::model_distance, "rug_model_distance", "rug-model-distance");
|
|
#endif
|
|
|
|
param_b(vid.backeffects, "background particle effects", (ISMOBILE || ISPANDORA) ? false : true);
|
|
// control
|
|
|
|
param_i(vid.joyvalue, "vid.joyvalue", 4800);
|
|
param_i(vid.joyvalue2, "vid.joyvalue2", 5600);
|
|
param_i(vid.joysmooth, "vid.joysmooth", 200);
|
|
param_i(vid.joypanthreshold, "vid.joypanthreshold", 2500);
|
|
param_f(vid.joypanspeed, "vid.joypanspeed", ISPANDORA ? 0.0001 : 0);
|
|
addsaver(autojoy, "autojoy");
|
|
|
|
vid.killreduction = 0;
|
|
|
|
param_b(vid.skipstart, "skip the start menu", false);
|
|
param_b(vid.quickmouse, "quick mouse", !ISPANDORA);
|
|
|
|
// colors
|
|
|
|
param_f(crosshair_size, "size:crosshair");
|
|
addsaver(crosshair_color, "color:crosshair");
|
|
|
|
param_b(mapeditor::drawplayer, "drawplayer");
|
|
param_color((color_t&) patterns::canvasback, "color:canvasback", false);
|
|
|
|
param_color(backcolor, "color:background", false);
|
|
param_color(forecolor, "color:foreground", false);
|
|
param_color(bordcolor, "color:borders", false);
|
|
param_color(ringcolor, "color:ring", true);
|
|
param_f(vid.multiplier_ring, "mring", "mult:ring", 1);
|
|
param_color(modelcolor, "color:model", true);
|
|
param_color(periodcolor, "color:period", true);
|
|
param_color(stdgridcolor, "color:stdgrid", true);
|
|
param_f(vid.multiplier_grid, "mgrid", "mult:grid", 1);
|
|
param_color(dialog::dialogcolor, "color:dialog", false);
|
|
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);
|
|
addsaverenum(land_structure, "mode-chaos");
|
|
#if CAP_INV
|
|
addsaver(inv::on, "mode-Orb Strategy");
|
|
#endif
|
|
addsaverenum(variation, "mode-variation", eVariation::bitruncated);
|
|
addsaver(peace::on, "mode-peace");
|
|
addsaver(peace::otherpuzzles, "mode-peace-submode");
|
|
addsaverenum(specialland, "land for special modes");
|
|
|
|
addsaver(viewdists, "expansion mode");
|
|
param_f(backbrightness, "back", "brightness behind sphere");
|
|
param_b(auto_extend, "expansion_auto_extend")
|
|
-> editable("extend automatically", 'E');
|
|
|
|
param_f(vid.ipd, "ipd", "interpupilar-distance", 0.05);
|
|
param_f(vid.lr_eyewidth, "lr", "eyewidth-lr", 0.5);
|
|
param_f(vid.anaglyph_eyewidth, "anaglyph", "eyewidth-anaglyph", 0.1);
|
|
param_f(vid.fov, "fov", "field-of-vision", 90);
|
|
addsaver(vid.desaturate, "desaturate", 0);
|
|
|
|
param_enum(vid.stereo_mode, "stereo_mode", "stereo-mode", vid.stereo_mode)
|
|
->editable({{"OFF", ""}, {"anaglyph", ""}, {"side-by-side", ""}
|
|
#if CAP_ODS
|
|
, {"ODS", ""}
|
|
#endif
|
|
}, "stereo mode", 'm');
|
|
|
|
param_f(vid.plevel_factor, "plevel_factor", 0.7);
|
|
|
|
#if CAP_GP
|
|
addsaver(gp::param.first, "goldberg-x", gp::param.first);
|
|
addsaver(gp::param.second, "goldberg-y", gp::param.second);
|
|
#endif
|
|
|
|
param_b(nohud, "no-hud", false);
|
|
param_b(nomap, "nomap", false);
|
|
param_b(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
|
|
|
|
param_i(vid.linequality, "line quality", 0);
|
|
|
|
#if CAP_FILES && CAP_SHOT && CAP_ANIMATIONS
|
|
addsaver(anims::animfile, "animation file format");
|
|
#endif
|
|
|
|
#if CAP_RUG
|
|
addsaver(rug::move_on_touch, "rug move on touch");
|
|
#endif
|
|
|
|
#if CAP_CRYSTAL
|
|
param_f(crystal::compass_probability, "cprob", "compass-probability");
|
|
addsaver(crystal::view_coordinates, "crystal-coordinates");
|
|
#endif
|
|
|
|
#if CAP_TEXTURE
|
|
param_b(texture::texture_aura, "texture-aura", false);
|
|
#endif
|
|
|
|
addsaver(vid.use_smart_range, "smart-range", 0);
|
|
param_f(vid.smart_range_detail, "smart-range-detail", 8)
|
|
->editable(1, 50, 1, "minimum visible cell in pixels", "", 'd');
|
|
|
|
param_f(vid.smart_range_detail_3, "smart-range-detail-3", 30)
|
|
->editable(1, 50, 1, "minimum visible cell in pixels", "", 'd');
|
|
|
|
param_b(vid.smart_area_based, "smart-area-based", false);
|
|
param_i(vid.cells_drawn_limit, "limit on cells drawn", 10000);
|
|
param_i(vid.cells_generated_limit, "limit on cells generated", 250);
|
|
|
|
param_enum(diskshape, "disk_shape", "disk_shape", dshTiles)
|
|
->editable({{"distance in tiles", ""}, {"distance in vertices", ""}, {"geometric distance", ""}
|
|
}, "disk shape", 'S')
|
|
->set_reaction([] { if(game_active) { stop_game(); start_game(); } });
|
|
|
|
param_i(req_disksize, "disk_size")
|
|
->editable(10, 100000, 10, "disk size", "Play on a disk. Enables the special game rules for small bounded spaces (especially relevant for e.g. Minefield and Halloween). The number given is the number of tiles to use; it is not used exactly, actually the smallest disk above this size is used. Set to 0 to disable.", 'd')
|
|
->set_sets([] { dialog::bound_low(0); })
|
|
->set_reaction([] { if(game_active) { stop_game(); start_game(); } })
|
|
->set_extra([] {
|
|
add_edit(diskshape);
|
|
});
|
|
|
|
#if CAP_SOLV
|
|
addsaver(sn::solrange_xy, "solrange-xy");
|
|
addsaver(sn::solrange_z, "solrange-z");
|
|
#endif
|
|
param_i(slr::shader_iterations, "slr-steps");
|
|
param_f(slr::range_xy, "slr-range-xy");
|
|
param_f(slr::range_z, "slr-range-z");
|
|
|
|
param_f(arcm::euclidean_edge_length, "arcm-euclid-length");
|
|
|
|
#if CAP_ARCM
|
|
addsaver(arcm::current.symbol, "arcm-symbol", "4^5");
|
|
#endif
|
|
addsaverenum(hybrid::underlying, "product-underlying");
|
|
|
|
for(int i=0; i<isize(ginf); i++) {
|
|
if(ginf[i].flags & qELLIPTIC)
|
|
sightranges[i] = M_PI;
|
|
else if(ginf[i].cclass == gcSphere)
|
|
sightranges[i] = TAU;
|
|
else if(ginf[i].cclass == gcEuclid)
|
|
sightranges[i] = 10;
|
|
else if(ginf[i].cclass == gcSL2)
|
|
sightranges[i] = 4.5;
|
|
else if(ginf[i].cclass == gcHyperbolic && ginf[i].g.gameplay_dimension == 2)
|
|
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;
|
|
|
|
param_b(dialog::onscreen_keyboard, "onscreen_keyboard")
|
|
->editable("onscreen keyboard", 'k');
|
|
|
|
param_b(context_fog, "coolfog");
|
|
|
|
addsaver(sightranges[gBinary3], "sight-binary3", 3.1 + bonus);
|
|
addsaver(sightranges[gCubeTiling], "sight-cubes", 10);
|
|
addsaver(sightranges[gCell120], "sight-120cell", TAU);
|
|
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", TAU);
|
|
addsaver(sightranges[gCell8], "sight-8cell", TAU);
|
|
addsaver(sightranges[gECell8], "sight-8cell-elliptic", M_PI);
|
|
addsaver(sightranges[gCell16], "sight-16cell", TAU);
|
|
addsaver(sightranges[gECell16], "sight-16cell-elliptic", M_PI);
|
|
addsaver(sightranges[gCell24], "sight-24cell", TAU);
|
|
addsaver(sightranges[gECell24], "sight-24cell-elliptic", M_PI);
|
|
addsaver(sightranges[gCell600], "sight-600cell", TAU);
|
|
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", 2.5); /* assume raycasting */
|
|
addsaver(sightranges[gSpace344], "sight-344", 4.5);
|
|
addsaver(sightranges[gSpace336], "sight-336", 4);
|
|
|
|
param_b(vid.sloppy_3d, "sloppy3d", true);
|
|
|
|
param_i(vid.texture_step, "wall-quality", 4);
|
|
|
|
param_b(smooth_scrolling, "smooth-scrolling", false);
|
|
addsaver(mouseaim_sensitivity, "mouseaim_sensitivity", 0.01);
|
|
|
|
param_b(vid.consider_shader_projection, "shader-projection", true);
|
|
param_b(semidirect_rendering, "semidirect_rendering", false)
|
|
->editable("semidirect_rendering (perspective on GPU)", 'k');
|
|
|
|
param_i(forced_center_down, "forced_center_down")
|
|
-> editable(0, 100, 10, "forced center down", "make the center not the actual screen center", 'd');
|
|
|
|
param_b(tortoise::shading_enabled, "tortoise_shading", true);
|
|
|
|
param_f(bounded_mine_percentage, "bounded_mine_freq")
|
|
-> editable(0, 1, 0.01, "fraction of mine in bounded minefield", "", '%')
|
|
-> set_reaction([] {
|
|
if(game_active) { stop_game(); start_game(); }
|
|
});
|
|
|
|
param_enum(nisot::geodesic_movement, "solv_geodesic_movement", "solv_geodesic_movement", true)
|
|
-> editable({{"Lie group", "light, camera, and objects move in lines of constant direction, in the Lie group sense"}, {"geodesics", "light, camera, and objects always take the shortest path"}}, "straight lines", 'G')
|
|
-> set_reaction([] {
|
|
if(pmodel == mdLiePerspective && nisot::geodesic_movement) pmodel = hyperbolic ? mdPerspective : mdGeodesic;
|
|
if(among(pmodel, mdGeodesic, mdPerspective) && !nisot::geodesic_movement) pmodel = mdLiePerspective;
|
|
});
|
|
|
|
addsaver(s2xe::qrings, "s2xe-rings");
|
|
addsaver(rots::underlying_scale, "rots-underlying-scale");
|
|
|
|
param_b(vid.bubbles_special, "bubbles-special", 1);
|
|
param_b(vid.bubbles_threshold, "bubbles-threshold", 1);
|
|
param_b(vid.bubbles_all, "bubbles-all", 0);
|
|
|
|
#if CAP_SHMUP
|
|
multi::initConfig();
|
|
#endif
|
|
|
|
addsaver(asonov::period_xy, "asonov:period_xy");
|
|
addsaver(asonov::period_z, "asonov:period_z");
|
|
addsaver(nilv::nilperiod[0], "nilperiod_x");
|
|
addsaver(nilv::nilperiod[1], "nilperiod_y");
|
|
addsaver(nilv::nilperiod[2], "nilperiod_z");
|
|
|
|
param_enum(neon_mode, "neon_mode", "neon_mode", neon_mode)
|
|
->editable(
|
|
{{"OFF", ""}, {"standard", ""}, {"no boundary mode", ""}, {"neon mode II", ""}, {"illustration mode", ""}},
|
|
"neon mode", 'M'
|
|
);
|
|
|
|
addsaverenum(neon_nofill, "neon_nofill");
|
|
param_b(noshadow, "noshadow");
|
|
param_b(bright, "bright");
|
|
param_b(cblind, "cblind");
|
|
|
|
addsaver(berger_limit, "berger_limit");
|
|
|
|
addsaverenum(centering, "centering");
|
|
|
|
param_f(camera_speed, "camspd", "camera-speed", 1);
|
|
param_f(camera_rot_speed, "camrot", "camera-rot-speed", 1);
|
|
param_f(third_person_rotation, "third_person_rotation", 0);
|
|
|
|
param_f(panini_alpha, "panini_alpha", 0)
|
|
->set_reaction(reset_all_shaders);
|
|
param_f(stereo_alpha, "stereo_alpha", 0)
|
|
->set_reaction(reset_all_shaders);
|
|
|
|
callhooks(hooks_configfile);
|
|
|
|
#if CAP_SHOT
|
|
param_f(levellines, "levellines", 0);
|
|
#endif
|
|
|
|
#if CAP_CONFIG
|
|
for(auto s: savers) s->reset();
|
|
#endif
|
|
|
|
param_custom(sightrange_bonus, "sightrange_bonus", menuitem_sightrange_bonus, 'r');
|
|
param_custom(vid.use_smart_range, "sightrange_style", menuitem_sightrange_style, 's');
|
|
|
|
param_custom(gp::param.first, "Goldberg x", menuitem_change_variation, 0);
|
|
param_custom(gp::param.second, "Goldberg y", menuitem_change_variation, 0);
|
|
param_custom(variation, "variation", menuitem_change_variation, 'v')
|
|
->help_text = "variation|dual|bitruncated";
|
|
param_custom(geometry, "geometry", menuitem_change_geometry, 0)
|
|
->help_text = "hyperbolic|spherical|Euclidean";
|
|
|
|
param_i(stamplen, "stamplen");
|
|
param_f(anims::period, "animperiod");
|
|
}
|
|
|
|
EX bool inSpecialMode() {
|
|
return !ls::nice_walls() || ineligible_starting_land || !BITRUNCATED || peace::on ||
|
|
#if CAP_TOUR
|
|
tour::on ||
|
|
#endif
|
|
yendor::on || tactic::on || randomPatternsMode ||
|
|
geometry != gNormal || pmodel != mdDisk || pconf.alpha != 1 || pconf.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(!ls::nice_walls()) 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(pconf.xposition || pconf.yposition || pconf.alpha != 1 || pconf.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; pconf.alpha = 1; pconf.scale = 1;
|
|
pconf.xposition = pconf.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);
|
|
|
|
/* we do this twice to make sure that stop_game_and_switch_mode switches to the correct land_structure */
|
|
for(int i=0; i<2; i++) {
|
|
if(leave == rg::chaos && !ls::std_chaos()) stop_game_and_switch_mode(rg::chaos);
|
|
if(leave != rg::chaos && !ls::nice_walls()) stop_game_and_switch_mode(rg::chaos);
|
|
}
|
|
|
|
if((!!dual::state) != (leave == rg::dualmode)) stop_game_and_switch_mode(rg::dualmode);
|
|
|
|
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
|
|
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 = 999;
|
|
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 != 4)
|
|
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.require_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([] () {
|
|
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 menuitem_sightrange_bonus(char c) {
|
|
dialog::addSelItem(XLAT("sight range bonus"), its(sightrange_bonus), c);
|
|
dialog::add_action([]{
|
|
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::get_di().reaction = doOvergenerate;
|
|
dialog::bound_low(1-getDistLimit());
|
|
dialog::bound_up(allowIncreasedSight() ? euclid ? 99 : gp::dist_2() * 5 : 0);
|
|
});
|
|
}
|
|
|
|
EX void edit_sightrange() {
|
|
#if CAP_RUG
|
|
USING_NATIVE_GEOMETRY_IN_RUG;
|
|
#endif
|
|
cmode = sm::SIDE;
|
|
gamescreen();
|
|
dialog::init("sight range settings");
|
|
add_edit(vid.use_smart_range);
|
|
if(vid.use_smart_range)
|
|
add_edit(WDIM == 2 ? vid.smart_range_detail : vid.smart_range_detail_3);
|
|
else {
|
|
if(WDIM == 2) {
|
|
add_edit(sightrange_bonus);
|
|
if(GDIM == 3) {
|
|
dialog::addSelItem(XLAT("3D sight range for the fog effect"), fts(sightranges[geometry]), 'r');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(sightranges[geometry], 0, TAU, 0.5, M_PI, XLAT("fog effect"), "");
|
|
});
|
|
}
|
|
}
|
|
if(WDIM == 3) {
|
|
dialog::addSelItem(XLAT("3D sight range"), fts(sightranges[geometry]), 'r');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(sightranges[geometry], 0, TAU, 0.5, M_PI, XLAT("3D 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 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."
|
|
)
|
|
);
|
|
});
|
|
}
|
|
}
|
|
#if CAP_SOLV
|
|
if(models::is_perspective(pmodel) && sol) {
|
|
dialog::addSelItem(XLAT("max difference in X/Y coordinates"), fts(sn::solrange_xy), 'x');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(sn::solrange_xy, 0.01, 200, 0.1, 50, XLAT("max difference in X/Y coordinates"), solhelp()), dialog::scaleLog();
|
|
});
|
|
dialog::addSelItem(XLAT("max difference in Z coordinate"), fts(sn::solrange_z), 'z');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(sn::solrange_z, 0, 20, 0.1, 6, XLAT("max difference in Z coordinates"), solhelp());
|
|
});
|
|
}
|
|
#endif
|
|
if(models::is_perspective(pmodel) && sl2) {
|
|
dialog::addSelItem(XLAT("max difference in X/Y coordinates"), fts(slr::range_xy), 'x');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(slr::range_xy, 0, 10, 0.5, 4, XLAT("max difference in X/Y coordinates"), "");
|
|
});
|
|
dialog::addSelItem(XLAT("max difference in Z coordinate"), fts(slr::range_z), 'x');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(slr::range_xy, 0, 10, 0.5, 4, XLAT("max difference in Z coordinate"), "");
|
|
});
|
|
dialog::addSelItem(XLAT("shader_iterations"), its(slr::shader_iterations), 'z');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(slr::shader_iterations, 0, 50, 1, 10, "", "");
|
|
});
|
|
}
|
|
if(vid.use_smart_range && WDIM == 2) {
|
|
dialog::addBoolItem_action(XLAT("area-based range"), vid.smart_area_based, 'a');
|
|
}
|
|
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(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([] {
|
|
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."
|
|
);
|
|
});
|
|
}
|
|
add_cells_drawn('c');
|
|
dialog::display();
|
|
}
|
|
|
|
EX void menuitem_sightrange_style(char c IS('c')) {
|
|
dialog::addSelItem(XLAT("draw range based on"),
|
|
vid.use_smart_range == 0 ? XLAT("distance") :
|
|
vid.use_smart_range == 1 ? XLAT("size (no gen)") :
|
|
XLAT("size"),
|
|
c
|
|
);
|
|
dialog::add_action_push([] {
|
|
dialog::init(XLAT("draw range based on"));
|
|
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(!allowChangeRange() || !allowIncreasedSight()) {
|
|
dialog::addItem(XLAT("enable the cheat mode for additional options"), 'X');
|
|
dialog::add_action(enable_cheat);
|
|
}
|
|
dialog::display();
|
|
});
|
|
}
|
|
|
|
EX void menuitem_sightrange(char c IS('c')) {
|
|
#if CAP_SOLV
|
|
if(pmodel == mdGeodesic && sol)
|
|
dialog::addSelItem(XLAT("sight range settings"), fts(sn::solrange_xy) + "x" + fts(sn::solrange_z), c);
|
|
else
|
|
#endif
|
|
if(vid.use_smart_range)
|
|
dialog::addSelItem(XLAT("sight range settings"), fts(WDIM == 3 ? vid.smart_range_detail_3 : vid.smart_range_detail) + " px", c);
|
|
else if(WDIM == 3)
|
|
dialog::addSelItem(XLAT("sight range settings"), fts(sightranges[geometry]) + "au", c);
|
|
else
|
|
dialog::addSelItem(XLAT("sight range settings"), format("%+d", sightrange_bonus), c);
|
|
dialog::add_action_push(edit_sightrange);
|
|
}
|
|
|
|
EX void sets_sfx_volume() {
|
|
#if CAP_AUDIO
|
|
dialog::get_di().dialogflags = sm::NOSCR;
|
|
#if ISANDROID
|
|
dialog::get_di().reaction = [] () {
|
|
settingsChanged = true;
|
|
};
|
|
#endif
|
|
dialog::bound_low(0);
|
|
dialog::bound_up(MIX_MAX_VOLUME);
|
|
#endif
|
|
}
|
|
|
|
EX void sets_music_volume() {
|
|
#if CAP_AUDIO
|
|
dialog::get_di().dialogflags = sm::NOSCR;
|
|
dialog::get_di().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_SDLAUDIO
|
|
dialog::get_di().extra_options = [] {
|
|
dialog::addBoolItem_action(XLAT("play music when out of focus"), music_out_of_focus, 'A');
|
|
};
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
EX void showSpecialEffects() {
|
|
cmode = vid.xres > vid.yres * 1.4 ? sm::SIDE : sm::MAYDARK;
|
|
gamescreen();
|
|
dialog::init(XLAT("extra graphical effects"));
|
|
|
|
dialog::addBoolItem_action(XLAT("particles on attack"), (vid.particles), 'p');
|
|
dialog::addBoolItem_action(XLAT("floating bubbles: special"), vid.bubbles_special, 's');
|
|
dialog::addBoolItem_action(XLAT("floating bubbles: treasure thresholds"), vid.bubbles_threshold, 't');
|
|
dialog::addBoolItem_action(XLAT("floating bubbles: all treasures"), vid.bubbles_all, 'a');
|
|
dialog::addBoolItem_action(XLAT("background particle effects"), (vid.backeffects), 'b');
|
|
|
|
dialog::addBreak(50);
|
|
dialog::addBack();
|
|
dialog::display();
|
|
}
|
|
|
|
EX void show_vector_settings() {
|
|
cmode = vid.xres > vid.yres * 1.4 ? sm::SIDE : sm::MAYDARK;
|
|
gamescreen();
|
|
dialog::init(XLAT("vector settings"));
|
|
|
|
dialog::addSelItem(XLAT("line width"), fts(vid.linewidth), 'w');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(vid.linewidth, 0, 10, 0.1, 1, XLAT("line width"),
|
|
vid.usingGL ? "" : XLAT("Line width setting is only taken into account in OpenGL."));
|
|
});
|
|
|
|
dialog::addSelItem(XLAT("line quality"), its(vid.linequality), 'l');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(vid.linequality, -3, 5, 1, 1, XLAT("line quality"),
|
|
XLAT("Higher numbers make the curved lines smoother, but reduce the performance."));
|
|
});
|
|
|
|
dialog::addBoolItem("perfect width", perfect_linewidth == 2, 'p');
|
|
if(perfect_linewidth == 1)
|
|
dialog::lastItem().value = XLAT("shots only");
|
|
dialog::add_action([] { perfect_linewidth = (1 + perfect_linewidth) % 3; });
|
|
|
|
dialog::addBoolItem_action("finer lines at the boundary", vid.fineline, 'O');
|
|
|
|
if(vid.fineline) {
|
|
dialog::addSelItem("variable width", fts(precise_width), 'm');
|
|
dialog::add_action([] () {
|
|
dialog::editNumber(precise_width, 0, 2, 0.1, 0.5,
|
|
XLAT("variable width"), XLAT("lines longer than this value will be split into shorter lines, with width computed separately for each of them.")
|
|
);
|
|
});
|
|
}
|
|
else dialog::addBreak(100);
|
|
|
|
add_edit(neon_mode);
|
|
dialog::addBreak(100);
|
|
dialog::addInfo(XLAT("hint: press Alt while testing modes"));
|
|
dialog::addBreak(100);
|
|
dialog::addBoolItem_action(XLAT("disable shadows"), noshadow, 'f');
|
|
dialog::addBoolItem_action(XLAT("bright mode"), bright, 'g');
|
|
dialog::addBoolItem_action(XLAT("colorblind simulation"), cblind, 'h');
|
|
|
|
dialog::addBoolItem_action(XLAT("no fill in neon mode"), neon_nofill, 'n');
|
|
|
|
dialog::addBreak(50);
|
|
dialog::addBack();
|
|
dialog::display();
|
|
}
|
|
|
|
EX void showGraphConfig() {
|
|
cmode = vid.xres > vid.yres * 1.4 ? sm::SIDE : sm::MAYDARK;
|
|
gamescreen();
|
|
|
|
dialog::init(XLAT("graphics configuration"));
|
|
|
|
#if !ISIOS && !ISWEB
|
|
add_edit(vid.want_fullscreen);
|
|
|
|
#if !ISANDROID && !ISFAKEMOBILE
|
|
if(vid.want_fullscreen) {
|
|
add_edit(vid.change_fullscr);
|
|
if(vid.change_fullscr)
|
|
add_edit(vid.fullscreen_x), add_edit(vid.fullscreen_y);
|
|
else
|
|
dialog::addBreak(200);
|
|
}
|
|
else {
|
|
add_edit(vid.relative_window_size);
|
|
if(vid.relative_window_size)
|
|
add_edit(vid.window_rel_x), add_edit(vid.window_rel_y);
|
|
else
|
|
add_edit(vid.window_x), add_edit(vid.window_y);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if CAP_GLORNOT
|
|
add_edit(vid.wantGL);
|
|
#endif
|
|
|
|
#if !ISIOS && !ISANDROID && !ISFAKEMOBILE
|
|
if(!vid.usingGL) {
|
|
dialog::addBoolItem(XLAT("anti-aliasing"), vid.want_antialias & AA_NOGL, 'O');
|
|
dialog::add_action([] {
|
|
if(!vid.usingGL)
|
|
vid.want_antialias ^= AA_NOGL | AA_FONT;
|
|
});
|
|
}
|
|
else {
|
|
dialog::addSelItem(XLAT("anti-aliasing"),
|
|
(vid.want_antialias & AA_POLY) ? "polygons" :
|
|
(vid.want_antialias & AA_LINES) ? "lines" :
|
|
(vid.want_antialias & AA_MULTI) ? "multisampling" :
|
|
"NO", 'O');
|
|
dialog::add_action([] {
|
|
if(vid.want_antialias & AA_MULTI)
|
|
vid.want_antialias ^= AA_MULTI;
|
|
else if(vid.want_antialias & AA_POLY)
|
|
vid.want_antialias ^= AA_POLY | AA_LINES | AA_MULTI;
|
|
else if(vid.want_antialias & AA_LINES)
|
|
vid.want_antialias |= AA_POLY;
|
|
else
|
|
vid.want_antialias |= AA_LINES;
|
|
});
|
|
}
|
|
#endif
|
|
|
|
#if !ISIOS && !ISANDROID && !ISFAKEMOBILE
|
|
if(vid.usingGL) {
|
|
if(vrhr::active())
|
|
dialog::addInfo(XLAT("(vsync disabled in VR)"));
|
|
else
|
|
add_edit(vid.want_vsync);
|
|
}
|
|
else
|
|
dialog::addBreak(100);
|
|
#endif
|
|
|
|
if(need_to_apply_screen_settings()) {
|
|
dialog::addItem(XLAT("apply changes"), 'A');
|
|
dialog::add_action(apply_screen_settings);
|
|
dialog::addBreak(100);
|
|
}
|
|
else
|
|
dialog::addBreak(200);
|
|
|
|
add_edit(vid.relative_font);
|
|
if(vid.relative_font)
|
|
add_edit(vid.fontscale);
|
|
else
|
|
add_edit(vid.abs_fsize);
|
|
|
|
dialog::addSelItem(XLAT("vector settings"), XLAT("width") + " " + fts(vid.linewidth), 'w');
|
|
dialog::add_action_push(show_vector_settings);
|
|
|
|
#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
|
|
|
|
dialog::addSelItem(XLAT("scrolling speed"), fts(vid.sspeed), 'a');
|
|
|
|
dialog::addSelItem(XLAT("camera movement speed"), fts(camera_speed), 'c');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(camera_speed, -10, 10, 0.1, 1, XLAT("camera movement speed"),
|
|
"This affects:\n\nin 2D: scrolling with arrow keys and Wheel Up\n\nin 3D: camera movement with Home/End."
|
|
);
|
|
});
|
|
dialog::addSelItem(XLAT("camera rotation speed"), fts(camera_rot_speed), 'r');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(camera_rot_speed, -10, 10, 0.1, 1, XLAT("camera rotation speed"),
|
|
"This affects view rotation with Page Up/Down, and in 3D, camera rotation with arrow keys or mouse."
|
|
);
|
|
});
|
|
|
|
dialog::addSelItem(XLAT("movement animation speed"), fts(vid.mspeed), 'm');
|
|
|
|
dialog::addSelItem(XLAT("idle animation speed"), fts(vid.ispeed), 'i');
|
|
dialog::add_action([] {
|
|
dialog::editNumber(vid.ispeed, 0, 4, 0.1, 1,
|
|
XLAT("idle animation speed"),
|
|
"0 = disable\n\nThis affects non-movement animations such as orb effects, item rotation, and more."
|
|
);
|
|
});
|
|
|
|
dialog::addBoolItem_action(XLAT("flashing effects"), (vid.flasheffects), 'h');
|
|
if(getcstat == 'h')
|
|
mouseovers = XLAT("Disable if you are photosensitive. Replaces flashing effects such as Orb of Storms lightning with slow, adjustable animations.");
|
|
|
|
dialog::addItem(XLAT("extra graphical effects"), 'u');
|
|
|
|
dialog::addBreak(50);
|
|
dialog::addBack();
|
|
dialog::display();
|
|
|
|
keyhandler = [] (int sym, int uni) {
|
|
dialog::handleNavigation(sym, uni);
|
|
|
|
char xuni = uni | 96;
|
|
|
|
if((uni >= 32 && uni < 64) || uni == 'L' || uni == 'C') xuni = uni;
|
|
|
|
if(xuni == 'u') pushScreen(showSpecialEffects);
|
|
|
|
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"));
|
|
|
|
#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 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::get_di().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() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
|
|
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
|
|
add_edit(musicvolume);
|
|
add_edit(effvolume);
|
|
#endif
|
|
|
|
menuitem_sightrange('r');
|
|
|
|
add_edit(vid.faraway_highlight);
|
|
add_edit(vid.faraway_highlight_color);
|
|
|
|
#ifdef WHATEVER
|
|
dialog::addSelItem(XLAT("whatever"), fts(whatever[0]), 'j');
|
|
dialog::add_action([] { edit_whatever('f', 0); });
|
|
#endif
|
|
|
|
add_edit(savefile_selection);
|
|
|
|
dialog::addBreak(50);
|
|
dialog::addBack();
|
|
|
|
dialog::display();
|
|
}
|
|
|
|
EX void configureInterface() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
dialog::init(XLAT("interface"));
|
|
|
|
#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);
|
|
});
|
|
|
|
add_edit(nohelp);
|
|
|
|
add_edit(vid.msgleft);
|
|
|
|
add_edit(glyphsortorder);
|
|
add_edit(vid.graphglyph);
|
|
add_edit(less_in_landscape);
|
|
add_edit(less_in_portrait);
|
|
|
|
add_edit(display_yasc_codes);
|
|
add_edit(vid.orbmode);
|
|
|
|
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::get_di().extra_options = [] {
|
|
dialog::addColorItem(XLAT("crosshair color"), crosshair_color, 'X');
|
|
dialog::add_action([] { dialog::openColorDialog(crosshair_color); });
|
|
};
|
|
});
|
|
|
|
add_edit(menu_darkening);
|
|
add_edit(centered_menus);
|
|
add_edit(startanims::enabled);
|
|
|
|
dialog::addBreak(50);
|
|
dialog::addBack();
|
|
|
|
dialog::display();
|
|
}
|
|
|
|
#if CAP_SDLJOY
|
|
EX void showJoyConfig() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
|
|
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"), autojoy ? XLAT("automatic") : XLAT("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::addSelItem(XLAT("smoothen"), its(vid.joysmooth) + " ms", 'e');
|
|
|
|
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(uni == 'e')
|
|
dialog::editNumber(vid.joypanspeed, 0, 2000, 20, 200, XLAT("smoothen"), "large values help if the joystick is imprecise");
|
|
|
|
else if(doexiton(sym, uni)) popScreen();
|
|
};
|
|
}
|
|
#endif
|
|
|
|
EX void projectionDialog() {
|
|
vid.tc_alpha = ticks;
|
|
dialog::editNumber(vpconf.alpha, -5, 5, .1, 1,
|
|
XLAT("projection distance"),
|
|
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. "
|
|
"See also the conformal mode (in the special modes menu) "
|
|
"for more models."));
|
|
dialog::get_di().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::get_ne().editwhat = 1; vpconf.scale = 1; dialog::get_ne().s = "1"; });
|
|
dialog::addSelItem(sphere ? "gnomonic" : "Klein model", "0", 'K');
|
|
dialog::add_action([] () { *dialog::get_ne().editwhat = 0; vpconf.scale = 1; dialog::get_ne().s = "0"; });
|
|
if(hyperbolic) {
|
|
dialog::addSelItem("inverted Poincaré model", "-1", 'I');
|
|
dialog::add_action([] () { *dialog::get_ne().editwhat = -1; vpconf.scale = 1; dialog::get_ne().s = "-1"; });
|
|
}
|
|
dialog::addItem(sphere ? "orthographic" : "Gans model", 'O');
|
|
dialog::add_action([] () { vpconf.alpha = vpconf.scale = 999; dialog::get_ne().reset_str(); });
|
|
dialog::addItem(sphere ? "towards orthographic" : "towards Gans model", 'T');
|
|
dialog::add_action([] () { double d = 1.1; vpconf.alpha *= d; vpconf.scale *= d; dialog::get_ne().reset_str(); });
|
|
};
|
|
}
|
|
|
|
EX void menuitem_projection_distance(char key) {
|
|
dialog::addSelItem(XLAT("projection distance"), fts(vpconf.alpha) + " (" + current_proj_name() + ")", key);
|
|
dialog::add_action(projectionDialog);
|
|
}
|
|
|
|
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 ld max_fov_angle() {
|
|
auto& p = panini_alpha ? panini_alpha : stereo_alpha;
|
|
if(p >= 1 || p <= -1) return 360;
|
|
return acos(-p) * 2 / degree;
|
|
}
|
|
|
|
EX void add_edit_fov(char key IS('f')) {
|
|
|
|
string sfov = fts(vid.fov) + "°";
|
|
if(panini_alpha || stereo_alpha) {
|
|
sfov += " / " + fts(max_fov_angle()) + "°";
|
|
}
|
|
dialog::addSelItem(XLAT("field of view"), sfov, key);
|
|
dialog::add_action([=] {
|
|
dialog::editNumber(vid.fov, 1, max_fov_angle(), 1, 90, "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.") + "\n\n" +
|
|
XLAT(
|
|
"Must be less than %1°. Panini projection can be used to get higher values.",
|
|
fts(max_fov_angle())
|
|
)
|
|
);
|
|
dialog::bound_low(1e-8);
|
|
dialog::bound_up(max_fov_angle() - 0.01);
|
|
string quick =
|
|
XLAT(
|
|
"HyperRogue uses "
|
|
"a quick implementation, so parameter values too close to 1 may "
|
|
"be buggy (outside of raycasting); try e.g. 0.9 instead."
|
|
);
|
|
dialog::get_di().extra_options = [quick] {
|
|
dialog::addSelItem(XLAT("Panini projection"), fts(panini_alpha), 'P');
|
|
dialog::add_action([quick] {
|
|
dialog::editNumber(panini_alpha, 0, 1, 0.1, 0, "Panini parameter",
|
|
XLAT(
|
|
"The Panini projection is an alternative perspective projection "
|
|
"which allows very wide field-of-view values.\n\n") + quick
|
|
);
|
|
#if CAP_GL
|
|
dialog::get_di().reaction = reset_all_shaders;
|
|
#endif
|
|
dialog::get_di().extra_options = [] { add_edit_fov('F'); };
|
|
});
|
|
dialog::addSelItem(XLAT("spherical perspective projection"), fts(stereo_alpha), 'S');
|
|
dialog::add_action([quick] {
|
|
dialog::editNumber(stereo_alpha, 0, 1, 0.1, 0, "spherical perspective parameter",
|
|
XLAT(
|
|
"Set to 1 to get stereographic projection, "
|
|
"which allows very wide field-of-view values.\n\n") + quick
|
|
);
|
|
#if CAP_GL
|
|
dialog::get_di().reaction = reset_all_shaders;
|
|
#endif
|
|
dialog::get_di().extra_options = [] { add_edit_fov('F'); };
|
|
});
|
|
};
|
|
});
|
|
}
|
|
|
|
bool supported_ods() {
|
|
if(!CAP_ODS) return false;
|
|
return rug::rugged || (hyperbolic && GDIM == 3);
|
|
}
|
|
|
|
EX void showStereo() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
dialog::init(XLAT("stereo vision config"));
|
|
|
|
add_edit(vid.stereo_mode);
|
|
|
|
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 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::get_di().reaction = [] {
|
|
#if MAXMDIM >= 4
|
|
if(floor_textures) {
|
|
delete floor_textures;
|
|
floor_textures = NULL;
|
|
}
|
|
#endif
|
|
};
|
|
});
|
|
}
|
|
|
|
EX void edit_levellines(char c) {
|
|
if(levellines)
|
|
dialog::addSelItem(XLAT("level lines"), fts(levellines), c);
|
|
else
|
|
dialog::addBoolItem(XLAT("level lines"), false, c);
|
|
dialog::add_action([] {
|
|
dialog::editNumber(levellines, 0, 100, 0.5, 0, XLAT("level lines"),
|
|
XLAT(
|
|
"This feature superimposes level lines on the rendered screen. These lines depend on the Z coordinate. In 3D hyperbolic the Z coordinate is taken from the Klein model. "
|
|
"Level lines can be used to observe the curvature: circles correspond to positive curvature, while hyperbolas correspond to negative. See e.g. the Hypersian Rug mode.")
|
|
);
|
|
dialog::get_di().reaction = ray::reset_raycaster;
|
|
dialog::get_di().extra_options = [] {
|
|
dialog::addBoolItem(XLAT("disable textures"), disable_texture, 'T');
|
|
dialog::add_action([] { ray::reset_raycaster(); disable_texture = !disable_texture; });
|
|
dialog::addItem(XLAT("disable level lines"), 'D');
|
|
dialog::add_action([] { ray::reset_raycaster(); levellines = 0; popScreen(); });
|
|
};
|
|
dialog::bound_low(0);
|
|
});
|
|
}
|
|
|
|
EX geom3::eSpatialEmbedding shown_spatial_embedding() {
|
|
if(GDIM == 2) return geom3::seNone;
|
|
return geom3::spatial_embedding;
|
|
}
|
|
|
|
EX bool in_tpp() { return pmodel == mdDisk && !models::camera_straight; }
|
|
|
|
EX void display_embedded_errors() {
|
|
using namespace geom3;
|
|
auto eucs = [] {
|
|
dialog::addItem(XLAT("set square tiling"), 'A'); dialog::add_action([] { dialog::do_if_confirmed( [] { stop_game(); set_geometry(gEuclidSquare); set_variation(eVariation::pure); start_game(); });});
|
|
dialog::addItem(XLAT("set hex tiling"), 'B'); dialog::add_action([] { dialog::do_if_confirmed( [] { stop_game(); set_geometry(gEuclid); set_variation(eVariation::pure); start_game(); });});
|
|
};
|
|
if(among(spatial_embedding, seNil, seProductH, seProductS, seCliffordTorus, seSL2) && (!among(geometry, gEuclid, gEuclidSquare) || !PURE)) {
|
|
dialog::addInfo(XLAT("error: currently works only in PURE Euclidean regular square or hex tiling"), 0xC00000);
|
|
eucs();
|
|
return;
|
|
}
|
|
if(among(spatial_embedding, seSol, seSolN, seNIH) && (!bt::in() && !among(geometry, gEuclid, gEuclidSquare))) {
|
|
dialog::addInfo(XLAT("error: currently works only in pure Euclidean, or binary tiling and similar"), 0xC00000);
|
|
eucs();
|
|
dialog::addItem(XLAT("set binary tiling variant"), 'C'); dialog::add_action([] { dialog::do_if_confirmed( [] { stop_game(); set_geometry(gBinaryTiling); geom3::switch_fpp(); geom3::switch_fpp(); start_game(); }); });
|
|
dialog::addItem(XLAT("set ternary tiling"), 'D'); dialog::add_action([] { dialog::do_if_confirmed( [] { stop_game(); set_geometry(gTernary); geom3::switch_fpp(); geom3::switch_fpp(); start_game(); }); });
|
|
dialog::addItem(XLAT("set binary tiling"), 'E'); dialog::add_action([] { dialog::do_if_confirmed( [] { stop_game(); set_geometry(gBinary4); geom3::switch_fpp(); geom3::switch_fpp(); start_game(); }); });
|
|
return;
|
|
}
|
|
if(shmup::on && cgi.emb->no_spin()) {
|
|
dialog::addInfo(XLAT("error: this embedding does not work in shmup"), 0xC00000);
|
|
return;
|
|
}
|
|
if(meuclid && spatial_embedding == seCliffordTorus) {
|
|
if(!clifford_torus_valid()) {
|
|
dialog::addInfo(XLAT("error: this method works only in rectangular torus"), 0xC00000);
|
|
dialog::addItem(XLAT("set 20x20 torus"), 'A'); dialog::add_action([] { dialog::do_if_confirmed( [] { stop_game();
|
|
auto& T0 = euc::eu_input.user_axes;
|
|
T0[0][0] = T0[1][1] = 20;
|
|
T0[0][1] = 0;
|
|
T0[1][0] = geometry == gEuclid ? 10 : 0;
|
|
euc::eu_input.twisted = false;
|
|
euc::build_torus3();
|
|
geom3::apply_settings_full(); start_game(); }); });
|
|
return;
|
|
}
|
|
}
|
|
if(meuclid && spatial_embedding == seProductS) {
|
|
#if CAP_RUG
|
|
rug::clifford_torus ct;
|
|
bool err = sqhypot_d(2, ct.xh) < 1e-3 && sqhypot_d(2, ct.yh) < 1e-3;
|
|
if(err) {
|
|
dialog::addInfo(XLAT("error: this method works only in cylinder"), 0xC00000);
|
|
dialog::addItem(XLAT("set cylinder"), 'A'); dialog::add_action([] { dialog::do_if_confirmed( [] { stop_game();
|
|
auto& T0 = euc::eu_input.user_axes;
|
|
T0[0][0] = 10;
|
|
T0[0][1] = T0[1][0] = T0[1][1] = 0;
|
|
euc::eu_input.twisted = false;
|
|
euc::build_torus3();
|
|
geom3::apply_settings_full(); start_game(); }); });
|
|
return;
|
|
}
|
|
#else
|
|
dialog::addInfo(XLAT("error: not supported"), 0xC00000);
|
|
#endif
|
|
}
|
|
if(msphere && !among(spatial_embedding, seNone, seDefault, seLowerCurvature, seMuchLowerCurvature, seProduct, seProductS)) {
|
|
dialog::addInfo(XLAT("error: this method does not work in spherical geometry"), 0xC00000);
|
|
return;
|
|
}
|
|
if(mhyperbolic && !among(spatial_embedding, seNone, seDefault, seLowerCurvature, seMuchLowerCurvature, seProduct, seProductH, seSol, seSolN, seNIH)) {
|
|
dialog::addInfo(XLAT("error: this method does not work in hyperbolic geometry"), 0xC00000);
|
|
return;
|
|
}
|
|
}
|
|
|
|
EX void show_spatial_embedding() {
|
|
cmode = sm::SIDE | sm::MAYDARK | sm::CENTER | sm::PANNING | sm::SHOWCURSOR;
|
|
gamescreen();
|
|
dialog::init(XLAT("3D styles"));
|
|
auto emb = shown_spatial_embedding();
|
|
add_edit(geom3::auto_configure);
|
|
|
|
dialog::addBreak(100);
|
|
|
|
auto &seo = geom3::spatial_embedding_options;
|
|
|
|
for(int i=0; i<isize(seo); i++) {
|
|
auto se = geom3::eSpatialEmbedding(i);
|
|
dialog::addBoolItem(XLAT(seo[i].first), emb == i, 'a' + i);
|
|
dialog::add_action([se] { invoke_embed(se); });
|
|
string s = why_wrong(se);
|
|
if(s != "")
|
|
dialog::items.back().value = (emb == i ? ONOFF(true) : XLAT("needs")) + s;
|
|
}
|
|
|
|
dialog::addBreak(100);
|
|
dialog::addHelp(XLAT(seo[emb].second));
|
|
display_embedded_errors();
|
|
dialog::addBreak(100);
|
|
|
|
if(geom3::auto_configure) {
|
|
if(emb == geom3::seNone) {
|
|
dialog::addBoolItem(XLAT("third-person perspective"), in_tpp(), 'T');
|
|
dialog::add_action(geom3::switch_tpp);
|
|
#if CAP_RUG
|
|
dialog::addBoolItem(XLAT("Hypersian Rug"), rug::rugged, 'u');
|
|
dialog::add_action([] {
|
|
if(in_tpp()) geom3::switch_tpp();
|
|
if(!rug::rugged) {
|
|
pconf.alpha = 1, pconf.scale = 1; if(!rug::rugged) rug::init();
|
|
}
|
|
else rug::close();
|
|
});
|
|
#endif
|
|
dialog::addBreak(100);
|
|
}
|
|
else {
|
|
if(geom3::supports_flat()) add_edit(geom3::flat_embedding);
|
|
else dialog::addBreak(100);
|
|
if(geom3::supports_invert()) add_edit(geom3::inverted_embedding);
|
|
else dialog::addBreak(100);
|
|
}
|
|
}
|
|
|
|
dialog::addSelItem(XLAT("reset view"), embedded_plane && isize(current_display->radarpoints) == 0 ? XLAT("(fix errors)") : !cells_drawn ? XLAT("(fix errors)") : "", ' ');
|
|
dialog::add_action([] {
|
|
if(rug::rug_control())
|
|
rug::reset_view();
|
|
else
|
|
fullcenter();
|
|
});
|
|
|
|
dialog::addBreak(100);
|
|
dialog::addBack();
|
|
|
|
dialog::display();
|
|
}
|
|
|
|
EX void show3D_height_details() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
dialog::init(XLAT("3D detailed settings"));
|
|
|
|
add_edit(vid.wall_height);
|
|
|
|
dialog::addBreak(50);
|
|
|
|
add_edit(vid.rock_wall_ratio);
|
|
add_edit(vid.human_wall_ratio);
|
|
add_edit(vid.lake_top);
|
|
add_edit(vid.lake_shallow);
|
|
add_edit(vid.lake_bottom);
|
|
|
|
dialog::addBreak(50);
|
|
|
|
if(embedded_plane) {
|
|
add_edit(auto_remove_roofs);
|
|
add_edit(vid.wall_height2);
|
|
add_edit(vid.wall_height3);
|
|
add_edit(draw_sky);
|
|
add_edit(vid.lowsky_height);
|
|
add_edit(vid.sky_height);
|
|
add_edit(vid.star_height);
|
|
add_edit(vid.infdeep_height);
|
|
add_edit(vid.sun_size);
|
|
add_edit(vid.star_size);
|
|
#if MAXMDIM >= 4
|
|
add_edit(star_prob);
|
|
add_edit(vid.height_limits);
|
|
if(euclid && msphere) add_edit(use_euclidean_infinity);
|
|
#endif
|
|
|
|
dialog::addBreak(100);
|
|
dialog::addHelp(lalign(0, "absolute altitudes:\n\n"
|
|
"depth ", cgi.INFDEEP,
|
|
" water ", tie(cgi.BOTTOM, cgi.SHALLOW, cgi.LAKE),
|
|
" floor ", cgi.FLOOR,
|
|
" eye ", vid.eye,
|
|
" walls ", tie(cgi.WALL, cgi.HIGH, cgi.HIGH2),
|
|
" star ", cgi.STAR,
|
|
" sky ", cgi.SKY,
|
|
"\n\n",
|
|
"recommended: ", cgi.emb->height_limit(-1), " to ", cgi.emb->height_limit(1)
|
|
));
|
|
}
|
|
else dialog::addInfo(XLAT("more options in 3D engine"));
|
|
|
|
dialog::addBreak(100);
|
|
|
|
dialog::addBack();
|
|
dialog::display();
|
|
}
|
|
|
|
EX void show3D() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
dialog::init(XLAT("3D configuration"));
|
|
|
|
#if MAXMDIM >=4
|
|
if(WDIM == 2) {
|
|
dialog::addSelItem("3D style", geom3::spatial_embedding_options[shown_spatial_embedding()].first, 'E');
|
|
dialog::add_action_push(show_spatial_embedding);
|
|
|
|
display_embedded_errors();
|
|
dialog::addBreak(50);
|
|
}
|
|
#endif
|
|
|
|
if(vid.use_smart_range == 0 && GDIM == 2) {
|
|
add_edit(vid.highdetail);
|
|
add_edit(vid.middetail);
|
|
dialog::addBreak(50);
|
|
}
|
|
|
|
if(WDIM == 2) {
|
|
if(cgi.emb->is_euc_scalable()) {
|
|
add_edit(geom3::euclid_embed_scale);
|
|
add_edit(geom3::euclid_embed_scale_y);
|
|
add_edit(geom3::euclid_embed_rotate);
|
|
}
|
|
add_edit(embedded_shift_method_choice);
|
|
add_edit(vid.camera);
|
|
if(GDIM == 3)
|
|
add_edit(vid.eye);
|
|
|
|
add_edit(vid.depth);
|
|
|
|
if(GDIM == 2) {
|
|
dialog::addSelItem(XLAT("Projection at the ground level"), fts(pconf.alpha), 'p');
|
|
dialog::add_action(projectionDialog);
|
|
}
|
|
else if(!in_perspective())
|
|
dialog::addSelItem(XLAT("projection distance"), fts(pconf.alpha), 'p');
|
|
|
|
dialog::addBreak(50);
|
|
add_edit(vid.wall_height);
|
|
dialog::addSelItem("height details", "", 'D');
|
|
dialog::add_action_push(show3D_height_details);
|
|
|
|
if(scale_used())
|
|
add_edit(vid.creature_scale);
|
|
}
|
|
else {
|
|
add_edit(vid.creature_scale);
|
|
add_edit(vid.height_width);
|
|
menuitem_sightrange('s');
|
|
}
|
|
|
|
dialog::addBreak(50);
|
|
add_edit(vid.yshift);
|
|
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");
|
|
});
|
|
}
|
|
if(true) add_edit(vpconf.cam());
|
|
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::get_di().dialogflags |= sm::CENTER;
|
|
}
|
|
});
|
|
}
|
|
|
|
if(mproduct || embedded_plane)
|
|
dialog::addBoolItem_action(XLAT("fixed Y/Z rotation"), vid.fixed_yz, 'Z');
|
|
|
|
if(WDIM == 2 && GDIM == 3) {
|
|
add_edit(vid.pseudohedral);
|
|
// add_edit(vid.depth_bonus);
|
|
}
|
|
|
|
if(true) {
|
|
dialog::addBreak(50);
|
|
dialog::addSelItem(XLAT("projection"), current_proj_name(), 'M');
|
|
dialog::add_action_push(models::model_menu);
|
|
}
|
|
#if CAP_RUG
|
|
if(GDIM == 2) {
|
|
dialog::addItem(XLAT("configure Hypersian Rug"), 'u');
|
|
dialog::add_action_push(rug::show);
|
|
}
|
|
#endif
|
|
|
|
#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::get_di().extra_options = [] () { draw_radar(true); };
|
|
});
|
|
}
|
|
|
|
if(WDIM == 3 && sphere && stretch::factor) {
|
|
dialog::addItem(XLAT("Berger sphere limit"), berger_limit);
|
|
dialog::add_action([] () {
|
|
dialog::editNumber(berger_limit, 0, 10, 1, 2, "",
|
|
XLAT("Primitive-based rendering of Berger sphere is currently very slow and low quality. "
|
|
"Here you can choose how many images to draw.")
|
|
);
|
|
});
|
|
}
|
|
|
|
#if CAP_RAY
|
|
if(GDIM == 3) {
|
|
dialog::addItem(XLAT("configure raycasting"), 'A');
|
|
dialog::add_action_push(ray::configure);
|
|
}
|
|
#endif
|
|
|
|
edit_levellines('L');
|
|
|
|
if(WDIM == 3 || (GDIM == 3 && meuclid)) {
|
|
dialog::addSelItem(XLAT("radar range"), fts(vid.radarrange), 'R');
|
|
dialog::add_action([] () {
|
|
dialog::editNumber(vid.radarrange, 0, 10, 0.5, 2, "", XLAT(""));
|
|
dialog::get_di().extra_options = [] () { draw_radar(true); };
|
|
});
|
|
}
|
|
if(GDIM == 3) add_edit_wall_quality('W');
|
|
#endif
|
|
|
|
#if CAP_RUG
|
|
if(rug::rugged) {
|
|
dialog::addBoolItem_action(XLAT("3D monsters/walls on the surface"), rug::spatial_rug, 'S');
|
|
}
|
|
#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::add_action_push(showStereo);
|
|
|
|
#if CAP_VR
|
|
dialog::addBoolItem(XLAT("VR settings"), vrhr::active(), 'v');
|
|
dialog::add_action_push(vrhr::show_vr_settings);
|
|
#endif
|
|
|
|
dialog::addBack();
|
|
dialog::display();
|
|
}
|
|
|
|
EX int config3 = addHook(hooks_configfile, 100, [] {
|
|
param_f(vid.eye, "eyelevel", 0)
|
|
->editable(-5, 5, .1, "eye level", "", 'E')
|
|
->set_extra([] {
|
|
dialog::get_di().dialogflags |= sm::CENTER;
|
|
vid.tc_camera = ticks;
|
|
|
|
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::get_di().reaction = [] { vid.auto_eye = false; };
|
|
dialog::add_action([] () {
|
|
vid.auto_eye = !vid.auto_eye;
|
|
geom3::do_auto_eye();
|
|
});
|
|
});
|
|
|
|
addsaver(vid.auto_eye, "auto-eyelevel", false);
|
|
|
|
param_b(nomenukey, "nomenukey");
|
|
param_b(showstartmenu, "showstartmenu");
|
|
param_b(draw_centerover, "draw_centerover");
|
|
|
|
param_enum(nohelp, "help_messages", "help_messages", 0)
|
|
-> editable({
|
|
{"all", "all context help/welcome messages"},
|
|
{"none", "no context help/welcome messages"},
|
|
{"automatic", "I know I can press F1 for help"},
|
|
}, "context help", 'H');
|
|
|
|
param_f(vid.creature_scale, "creature_scale", "3d-creaturescale", 1)
|
|
->editable(0, 1, .1, "Creature scale", "", 'C');
|
|
param_f(vid.height_width, "heiwi", "3d-heightwidth", 1.5)
|
|
->editable(0, 1, .1, "Height to width", "", 'h');
|
|
param_f(vid.yshift, "yshift", "Y shift", 0)
|
|
->editable(0, 1, .1, "Y shift", "Don't center on the player character.", 'y')
|
|
->set_extra([] {
|
|
if(WDIM == 3 && pmodel == mdPerspective)
|
|
dialog::addBoolItem_action(XLAT("reduce if walls on the way"), vid.use_wall_radar, 'R');
|
|
});
|
|
addsaver(vid.use_wall_radar, "wallradar", true);
|
|
addsaver(vid.fixed_facing, "fixed facing", 0);
|
|
addsaver(vid.fixed_facing_dir, "fixed facing dir", 90);
|
|
param_b(vid.fixed_yz, "fixed YZ", true);
|
|
param_b(frustum_culling, "frustum_culling");
|
|
param_b(numerical_minefield, "numerical_minefield")
|
|
->editable("display mine counts numerically", 'n');
|
|
param_b(dont_display_minecount, "dont_display_minecount");
|
|
#if MAXMDIM >= 4
|
|
param_enum(draw_sky, "draw_sky", "draw_sky", skyAutomatic)
|
|
-> editable({{"NO", "do not draw sky"}, {"automatic", ""}, {"skybox", "works only in Euclidean"}, {"always", "might be glitched in some settings"}}, "sky rendering", 's');
|
|
param_b(use_euclidean_infinity, "use_euclidean_infinity", true)
|
|
-> editable("infinite sky", 'i');
|
|
#endif
|
|
param_f(linepatterns::parallel_count, "parallel_count")
|
|
->editable(0, 24, 1, "number of parallels drawn", "", 'n');
|
|
param_f(linepatterns::parallel_max, "parallel_max")
|
|
->editable(0, TAU, 15*degree, "last parallel drawn", "", 'n');
|
|
param_f(linepatterns::mp_ori, "mp_ori")
|
|
->editable(0, TAU, 15*degree, "parallel/meridian orientation", "", 'n');
|
|
param_f(linepatterns::meridian_max, "meridian_max");
|
|
param_f(linepatterns::meridian_count, "meridian_count");
|
|
param_f(linepatterns::meridian_length, "meridian_length");
|
|
param_f(linepatterns::meridian_prec, "meridian_prec");
|
|
param_f(linepatterns::meridian_prec2, "meridian_prec2");
|
|
|
|
param_f(linepatterns::dual_length, "dual_length");
|
|
param_matrix(linepatterns::dual_angle.v2, "dual_angle", 2);
|
|
param_matrix(linepatterns::dual_angle.v3, "dual_angle3", 3);
|
|
|
|
param_f(twopoint_xscale, "twopoint_xscale");
|
|
param_i(twopoint_xshape, "twopoint_xshape");
|
|
param_f(twopoint_xwidth, "twopoint_xwidth");
|
|
param_f(periodwidth, "periodwidth", 1);
|
|
|
|
param_b(draw_plain_floors, "draw_plain_floors", false)
|
|
->editable("draw plain floors in 3D", 'p');
|
|
param_i(default_flooralpha, "floor_alpha")
|
|
->editable(0, 255, 15, "floor alpha", "255 = opaque", 'a');
|
|
|
|
param_f(vid.depth_bonus, "depth_bonus", 0)
|
|
->editable(-5, 5, .1, "depth bonus in pseudohedral", "", 'b');
|
|
param_enum(vid.pseudohedral, "pseudohedral", "pseudohedral", phOFF)
|
|
->editable(
|
|
{{"OFF", "the tiles are curved"},
|
|
{"inscribed", "the tiles are inscribed"},
|
|
{"circumscribed", "the tiles are circumscribed"}},
|
|
"make the tiles flat", 'p');
|
|
param_f(vid.depth, "depth", "3D depth", 1)
|
|
->editable(0, 5, .1, "Ground level below the plane", "", 'd')
|
|
->set_extra([] {
|
|
vid.tc_depth = ticks;
|
|
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(current_display->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);
|
|
})
|
|
->set_reaction([] {
|
|
bool b = vid.tc_alpha < vid.tc_camera;
|
|
if(vid.tc_alpha >= vid.tc_depth) vid.tc_alpha = vid.depth - 1;
|
|
if(vid.tc_camera >= vid.tc_depth) vid.tc_camera = vid.depth - 1;
|
|
if(vid.tc_alpha == vid.tc_camera) (b ? vid.tc_alpha : vid.tc_camera)--;
|
|
geom3::apply_settings_light();
|
|
});
|
|
param_f(vid.camera, "camera", "3D camera level", 1)
|
|
->editable(0, 5, .1, "", "", 'c')
|
|
->modif([] (float_setting* x) { x->menu_item_name = (GDIM == 2 ? "Camera level above the plane" : "Z shift"); })
|
|
->set_extra([] {
|
|
vid.tc_camera = ticks;
|
|
if(GDIM == 2)
|
|
dialog::addHelp(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))));
|
|
if(GDIM == 3)
|
|
dialog::addHelp(XLAT("Look from behind."));
|
|
if(GDIM == 3 && pmodel == mdPerspective)
|
|
dialog::addBoolItem_action(XLAT("reduce if walls on the way"), vid.use_wall_radar, 'R');
|
|
});
|
|
param_f(vid.wall_height, "wall_height", "3D wall height", .3)
|
|
->editable(0, 1, .1, "Height of walls", "", 'w')
|
|
->set_extra([] () {
|
|
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(geom3::actual_wall_height()), fts(geom3::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;
|
|
});
|
|
})
|
|
->set_reaction(geom3::apply_settings_light);
|
|
param_f(vid.rock_wall_ratio, "rock_wall_ratio", "3D rock-wall ratio", .9)
|
|
->editable(0, 1, .1, "Rock-III to wall ratio", "", 'r')
|
|
->set_extra([] { 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))));
|
|
});
|
|
param_f(vid.human_wall_ratio, "human_wall_ratio", "3D human-wall ratio", .7)
|
|
->editable(0, 1, .1, "Human to wall ratio", "", 'h')
|
|
->set_extra([] { 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)))
|
|
);
|
|
});
|
|
string unitwarn =
|
|
"The unit this is value is given in is wall height. "
|
|
"Note that, in exponentially expanding spaces, too high values could cause rendering issues. So "
|
|
"if you want infinity, values of 5 or similar should be used -- there is no visible difference "
|
|
"from infinity and glitches are avoided.";
|
|
param_f(vid.lake_top, "lake_top", "3D lake top", .25 / 0.3)
|
|
->editable(0, 1, .1, "Level of water surface", unitwarn, 'l');
|
|
param_f(vid.lake_shallow, "lake_shallow", "3D lake shallow", .4 / 0.3)
|
|
->editable(0, 1, .1, "Level of shallow water", unitwarn, 's');
|
|
param_f(vid.lake_bottom, "lake_bottom", "3D lake bottom", .9 / 0.3)
|
|
->editable(0, 1, .1, "Level of water bottom", unitwarn, 'k');
|
|
param_f(vid.wall_height2, "wall_height2", "wall_height2", 2)
|
|
->editable(0, 5, .1, "ratio of high walls to normal walls", unitwarn, '2');
|
|
param_f(vid.wall_height3, "wall_height3", "wall_height3", 3)
|
|
->editable(0, 5, .1, "ratio of very high walls to normal walls", unitwarn, '3');
|
|
param_f(vid.lowsky_height, "lowsky_height", "lowsky_height", 2)
|
|
->editable(0, 5, .1, "sky fake height", "Sky is rendered at the distance computed based on "
|
|
"the sky height, which might be beyond the range visible in fog. To prevent this, "
|
|
"the intensity of the fog effect depends on the value here rather than the actual distance. "
|
|
"Stars are affected similarly.", '4');
|
|
#if MAXMDIM >= 4
|
|
param_fd(vid.sky_height, "sky_height")
|
|
->set_hint([] { return geom3::to_wh(cgi.SKY); })
|
|
->editable(0, 10, .1, "altitude of the sky", unitwarn, '5')
|
|
->set_reaction(delete_sky);
|
|
#endif
|
|
param_fd(vid.star_height, "star_height")
|
|
->set_hint([] { return geom3::to_wh(cgi.STAR); })
|
|
->editable(0, 10, .1, "altitude of the stars", unitwarn, '6');
|
|
param_fd(vid.infdeep_height, "infdeep_height")
|
|
->set_hint([] { return geom3::to_wh(cgi.INFDEEP); })
|
|
->editable(0, 10, .1, "infinite depth", unitwarn, '7');
|
|
param_f(vid.sun_size, "sun_size", "sun_size", 8)
|
|
->editable(0, 10, .1, "sun size (relative to item sizes)", "", '8');
|
|
param_f(vid.star_size, "star_size", "star_size", 0.75)
|
|
->editable(0, 10, .1, "night star size (relative to item sizes)", "", '9');
|
|
#if MAXMDIM >= 4
|
|
param_f(star_prob, "star_prob", 0.3)
|
|
->editable(0, 1, .01, "star probability", "probability of star per tile", '*');
|
|
#endif
|
|
param_b(vid.height_limits, "height_limits", true)
|
|
->editable("prevent exceeding recommended altitudes", 'l');
|
|
param_b(auto_remove_roofs, "auto_remove_roofs", true)
|
|
->editable("do not render higher levels if camera too high", 'r');
|
|
addsaver(vid.tc_depth, "3D TC depth", 1);
|
|
addsaver(vid.tc_camera, "3D TC camera", 2);
|
|
addsaver(vid.tc_alpha, "3D TC alpha", 3);
|
|
param_f(vid.highdetail, "highdetail", "3D highdetail", 8)
|
|
->editable(0, 5, .5, "High detail range", "", 'n')
|
|
->set_extra(explain_detail)
|
|
->set_reaction([] {
|
|
if(vid.highdetail > vid.middetail) vid.middetail = vid.highdetail;
|
|
});
|
|
param_f(vid.middetail, "middetail", "3D middetail", 8)
|
|
->editable(0, 5, .5, "Mid detail range", "", 'm')
|
|
->set_extra(explain_detail)
|
|
->set_reaction([] {
|
|
if(vid.highdetail > vid.middetail) vid.highdetail = vid.middetail;
|
|
});
|
|
param_i(debug_tiles, "debug_tiles")->editable(0, 2, 1,
|
|
"display tile debug values",
|
|
"Display cell type IDs, as well as vertex and edge identifiers.\n\n"
|
|
"Setting 1 uses the internal shape IDs, while setting 2 in tes files uses "
|
|
"the original IDs in case if extra tile types were added to "
|
|
"separate mirror images or different football types.", 'd');
|
|
param_f(global_boundary_ratio, "global_boundary_ratio")
|
|
->editable(0, 5, 0.1, "Width of cell boundaries",
|
|
"How wide should the cell boundaries be.", '0');
|
|
addsaver(vid.gp_autoscale_heights, "3D Goldberg autoscaling", true);
|
|
addsaver(scorefile, "savefile");
|
|
param_b(savefile_selection, "savefile_selection")
|
|
-> editable("select the score/save file on startup", 's')
|
|
-> set_reaction([] {
|
|
if(savefile_selection)
|
|
addMessage(XLAT("Save the config and restart to select another score/save file."));
|
|
else if(scorefile == "")
|
|
addMessage(XLAT("Save the config to always play without recording your progress."));
|
|
else
|
|
addMessage(XLAT("Save the config to always use %1.", scorefile));
|
|
});
|
|
});
|
|
|
|
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;
|
|
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
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;
|
|
|
|
flat_model_enabler fme;
|
|
|
|
initquickqueue();
|
|
transmatrix V = atscreenpos(vid.xres/2, firsty, scale);
|
|
|
|
double alpha = atan2(mousex - vid.xres/2, mousey - firsty) - 90._deg;
|
|
V = V * spin(alpha);
|
|
drawMonsterType(moPlayer, NULL, shiftless(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 color_t addalpha(color_t c) { return (c << 8) | 0xFF; }
|
|
|
|
EX void edit_color_table(colortable& ct, const reaction_t& r IS(reaction_t()), bool has_bit IS(false)) {
|
|
cmode = sm::SIDE;
|
|
gamescreen();
|
|
dialog::init(XLAT("colors & aura"));
|
|
|
|
for(int i=0; i<isize(ct); i++) {
|
|
dialog::addColorItem(its(i), addalpha(ct[i]), '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::get_di().reaction = r;
|
|
dialog::colorAlpha = false;
|
|
dialog::get_di().dialogflags |= sm::SIDE;
|
|
});
|
|
}
|
|
|
|
dialog::addItem("add a color", 'A');
|
|
dialog::add_action([&ct, r] {
|
|
ct.push_back(rand() & 0x1FFFFFF);
|
|
r();
|
|
});
|
|
|
|
if(isize(ct) > 2) {
|
|
dialog::addItem("delete a color", 'D');
|
|
dialog::add_action([&ct, r] {
|
|
ct.pop_back();
|
|
r();
|
|
});
|
|
}
|
|
|
|
dialog::addBack();
|
|
dialog::display();
|
|
}
|
|
|
|
EX void show_color_dialog() {
|
|
cmode = sm::SIDE | sm::DIALOG_STRICT_X;
|
|
getcstat = '-';
|
|
gamescreen();
|
|
dialog::init(XLAT("colors & aura"));
|
|
|
|
dialog::addColorItem(XLAT("background"), addalpha(backcolor), 'b');
|
|
dialog::add_action([] () { dialog::openColorDialog(backcolor); dialog::colorAlpha = false; dialog::get_di().dialogflags |= sm::SIDE; });
|
|
|
|
if(WDIM == 2 && GDIM == 3 && hyperbolic)
|
|
dialog::addBoolItem_action(XLAT("cool fog effect"), context_fog, 'B');
|
|
|
|
dialog::addColorItem(XLAT("foreground"), addalpha(forecolor), 'f');
|
|
dialog::add_action([] () { dialog::openColorDialog(forecolor); dialog::colorAlpha = false; dialog::get_di().dialogflags |= sm::SIDE; });
|
|
|
|
dialog::addColorItem(XLAT("borders"), addalpha(bordcolor), 'o');
|
|
dialog::add_action([] () { dialog::openColorDialog(bordcolor); dialog::colorAlpha = false; dialog::get_di().dialogflags |= sm::SIDE; });
|
|
|
|
dialog::addColorItem(XLAT("projection boundary"), ringcolor, 'r');
|
|
dialog::add_action([] () { dialog::openColorDialog(ringcolor); dialog::get_di().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::get_di().dialogflags |= sm::SIDE; });
|
|
|
|
dialog::addColorItem(XLAT("standard grid color"), stdgridcolor, 'g');
|
|
dialog::add_action([] () { vid.grid = true; dialog::openColorDialog(stdgridcolor); dialog::get_di().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::get_di().dialogflags |= sm::SIDE; });
|
|
|
|
dialog::addColorItem(XLAT("dialogs"), addalpha(dialog::dialogcolor), 'd');
|
|
dialog::add_action([] () { dialog::openColorDialog(dialog::dialogcolor); dialog::colorAlpha = false; dialog::get_di().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(patterns::whichCanvas == 'R') {
|
|
dialog::addItem(XLAT("unreversed colors"), 'U');
|
|
dialog::add_action_push([] { edit_color_table(colortables['A'], 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
|
|
|
|
if(cwt.at->land == laTortoise) {
|
|
dialog::addBoolItem_action(XLAT("Galápagos shading"), tortoise::shading_enabled, 'T');
|
|
}
|
|
|
|
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::get_di().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() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
dialog::init("select language"); // intentionally not translated
|
|
|
|
int v = vid.language;
|
|
dynamicval<int> d(vid.language, -1);
|
|
|
|
for(int i=0; i<NUMLAN; 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_changed();
|
|
}
|
|
|
|
else if(uni >= 'a' && uni < 'a'+NUMLAN) {
|
|
vid.language = uni - 'a';
|
|
android_settings_changed();
|
|
}
|
|
|
|
else if(doexiton(sym, uni))
|
|
popScreen();
|
|
};
|
|
}
|
|
#endif
|
|
|
|
EX void configureMouse() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
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::get_di().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();
|
|
}
|
|
|
|
vector<setting*> last_changed;
|
|
|
|
EX void add_to_changed(setting *f) {
|
|
auto orig_f = f;
|
|
for(int i=0; i<isize(last_changed); i++) {
|
|
if(last_changed[i] == f)
|
|
return;
|
|
swap(last_changed[i], f);
|
|
if(f == orig_f) return;
|
|
}
|
|
last_changed.push_back(f);
|
|
}
|
|
|
|
EX setting *find_edit(void *val) {
|
|
for(auto& fs: params) {
|
|
fs.second->check_change();
|
|
if(fs.second->affects(val))
|
|
return &*fs.second;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
EX void add_edit_ptr(void *val) {
|
|
int found = 0;
|
|
for(auto& fs: params) {
|
|
fs.second->check_change();
|
|
if(fs.second->affects(val))
|
|
fs.second->show_edit_option(), found++;
|
|
}
|
|
if(found != 1) println(hlog, "found = ", found);
|
|
}
|
|
|
|
#if HDR
|
|
template<class T> void add_edit(T& val) {
|
|
add_edit_ptr(&val);
|
|
}
|
|
#endif
|
|
|
|
EX void find_setting() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
|
|
dialog::init(XLAT("find a setting"));
|
|
if(dialog::infix != "") mouseovers = dialog::infix;
|
|
|
|
dialog::start_list(900, 900, '1');
|
|
|
|
int found = 0;
|
|
|
|
for(auto& p: params) {
|
|
auto& fs = p.second;
|
|
string key = fs->search_key();
|
|
if(fs->available() && dialog::hasInfix(key)) {
|
|
fs->show_edit_option(dialog::list_fake_key++);
|
|
found++;
|
|
}
|
|
}
|
|
|
|
dialog::end_list();
|
|
|
|
dialog::addBreak(100);
|
|
dialog::addInfo(XLAT("press letters to search"));
|
|
dialog::addSelItem(XLAT("matching items"), its(found), 0);
|
|
dialog::display();
|
|
|
|
keyhandler = [] (int sym, int uni) {
|
|
dialog::handleNavigation(sym, uni);
|
|
if(dialog::editInfix(uni)) dialog::list_skip = 0;
|
|
else if(doexiton(sym, uni)) popScreen();
|
|
};
|
|
}
|
|
|
|
EX void edit_all_settings() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
dialog::init(XLAT("recently changed settings"));
|
|
|
|
for(auto &fs: params) fs.second->check_change();
|
|
|
|
dialog::start_list(1000, 1000, 'a');
|
|
for(auto l: last_changed)
|
|
if(l->available())
|
|
l->show_edit_option(dialog::list_fake_key++);
|
|
dialog::end_list();
|
|
|
|
dialog::addBreak(100);
|
|
dialog::addItem(XLAT("find a setting"), '/');
|
|
dialog::add_action_push(find_setting);
|
|
dialog::addBack();
|
|
dialog::display();
|
|
}
|
|
|
|
void list_setting::show_edit_option(int key) {
|
|
string opt = options[get_value()].first;
|
|
dialog::addSelItem(XLAT(menu_item_name), XLAT(opt), key);
|
|
dialog::add_action_push([this] {
|
|
add_to_changed(this);
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
dialog::init(XLAT(menu_item_name));
|
|
dialog::addBreak(100);
|
|
int q = isize(options);
|
|
|
|
int need_list = q > 15 ? 2 : q > 10 ? 1 : 0;
|
|
|
|
if(need_list >= 2) dialog::start_list(1500, 1500, 'a');
|
|
for(int i=0; i<q; i++) {
|
|
dialog::addBoolItem(XLAT(options[i].first), get_value() == i, need_list >= 2 ? dialog::list_fake_key++ : 'a' + i);
|
|
dialog::add_action([this, i, need_list] { set_value(i); if(reaction) reaction(); if(need_list == 0) popScreen(); });
|
|
if(need_list == 0 && options[i].second != "") {
|
|
dialog::addBreak(100);
|
|
dialog::addHelp(XLAT(options[i].second));
|
|
dialog::addBreak(100);
|
|
}
|
|
}
|
|
if(need_list >= 2) dialog::end_list();
|
|
dialog::addBreak(100);
|
|
|
|
if(need_list >= 1 && options[get_value()].second != "") {
|
|
dialog::addHelp(XLAT(options[get_value()].second));
|
|
dialog::addBreak(100);
|
|
}
|
|
dialog::addBack();
|
|
dialog::display();
|
|
});
|
|
}
|
|
|
|
EX void showSettings() {
|
|
cmode = sm::SIDE | sm::MAYDARK;
|
|
gamescreen();
|
|
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::quick_model);
|
|
|
|
dialog::addItem(XLAT("colors & aura"), 'c');
|
|
dialog::add_action_push(show_color_dialog);
|
|
|
|
#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("find a setting"), '/');
|
|
dialog::add_action_push(edit_all_settings);
|
|
|
|
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 = argcolor(24);
|
|
}
|
|
else if(argis("-fillmodel")) {
|
|
PHASEFROM(2); shift(); modelcolor = argcolor(32);
|
|
}
|
|
else if(argis("-apeirocolor")) {
|
|
PHASEFROM(2); shift(); patterns::apeirogonal_color = argcolor(32);
|
|
}
|
|
else if(argis("-ring")) {
|
|
PHASEFROM(2); shift(); ringcolor = argcolor(32);
|
|
}
|
|
else if(argis("-ringw")) {
|
|
PHASEFROM(2); shift_arg_formula(vid.multiplier_ring);
|
|
}
|
|
else if(argis("-stdgrid")) {
|
|
PHASEFROM(2); shift(); stdgridcolor = argcolor(32);
|
|
}
|
|
else if(argis("-gridw")) {
|
|
PHASEFROM(2); shift_arg_formula(vid.multiplier_grid);
|
|
}
|
|
else if(argis("-period")) {
|
|
PHASEFROM(2); shift(); periodcolor = argcolor(32);
|
|
}
|
|
else if(argis("-crosshair")) {
|
|
PHASEFROM(2); shift(); crosshair_color = argcolor(32);
|
|
shift_arg_formula(crosshair_size);
|
|
}
|
|
else if(argis("-borders")) {
|
|
PHASEFROM(2); shift(); bordcolor = argcolor(24);
|
|
}
|
|
else if(argis("-fore")) {
|
|
PHASEFROM(2); shift(); forecolor = argcolor(24);
|
|
}
|
|
else if(argis("-title")) {
|
|
PHASEFROM(2); shift(); titlecolor = argcolor(24);
|
|
}
|
|
else if(argis("-dialog")) {
|
|
PHASEFROM(2); shift(); dialog::dialogcolor = argcolor(24);
|
|
}
|
|
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.want_antialias = argi(); apply_screen_settings(); }
|
|
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; }
|
|
else if(argis("-gridoff")) { vid.grid = false; }
|
|
|
|
// non-configurable options
|
|
else if(argis("-vsync_off")) {
|
|
vid.want_vsync = false;
|
|
apply_screen_settings();
|
|
}
|
|
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("-nomsg")) {
|
|
PHASEFROM(2);
|
|
nomsg = 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();
|
|
if(vid.want_fullscreen) {
|
|
int clWidth=0, clHeight=0, clFont=0;
|
|
sscanf(argcs(), "%dx%dx%d", &clWidth, &clHeight, &clFont);
|
|
vid.change_fullscr = clWidth;
|
|
if(clWidth) vid.fullscreen_x = clWidth;
|
|
if(clHeight) vid.fullscreen_y = clHeight;
|
|
if(clFont) vid.abs_fsize = clFont, vid.relative_font = true;
|
|
}
|
|
else if(args().find(".") != string::npos) {
|
|
vid.relative_window_size = true;
|
|
ld dWidth=0, dHeight=0;
|
|
sscanf(argcs(), "%lfx%lf", &dWidth, &dHeight);
|
|
if(dWidth) vid.window_rel_x = dWidth;
|
|
if(dHeight) vid.window_rel_y = dHeight;
|
|
}
|
|
else {
|
|
vid.want_fullscreen = false;
|
|
vid.relative_window_size = false;
|
|
int clFont=0;
|
|
sscanf(argcs(), "%dx%dx%d", &vid.window_x, &vid.window_y, &clFont);
|
|
if(clFont) vid.abs_fsize = clFont, vid.relative_font = true;
|
|
}
|
|
}
|
|
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("-pside")) {
|
|
PHASEFROM(2);
|
|
permaside = true;
|
|
}
|
|
else if(argis("-xy")) {
|
|
PHASEFROM(2);
|
|
shift_arg_formula(pconf.xposition);
|
|
shift_arg_formula(pconf.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("-levellines")) {
|
|
PHASEFROM(2); shift_arg_formula(levellines);
|
|
}
|
|
else if(argis("-level-notexture")) {
|
|
PHASEFROM(2); disable_texture = true;
|
|
}
|
|
else if(argis("-level-texture")) {
|
|
PHASEFROM(2); disable_texture = false;
|
|
}
|
|
else if(argis("-msens")) {
|
|
PHASEFROM(2); shift_arg_formula(mouseaim_sensitivity);
|
|
}
|
|
TOGGLE('o', vid.wantGL, { vid.wantGL = !vid.wantGL; apply_screen_settings();})
|
|
TOGGLE('f', vid.want_fullscreen, { vid.want_fullscreen = !vid.want_fullscreen; apply_screen_settings(); })
|
|
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("-neon")) {
|
|
PHASEFROM(2);
|
|
shift(); neon_mode = eNeon(argi());
|
|
}
|
|
else if(argis("-dmc")) {
|
|
PHASEFROM(2);
|
|
shift(); vid.drawmousecircle = argi();
|
|
}
|
|
else if(argis("-smooths")) {
|
|
PHASEFROM(2);
|
|
shift(); smooth_scrolling = argi();
|
|
}
|
|
else if(argis("-via-shader")) {
|
|
PHASEFROM(2);
|
|
shift(); vid.consider_shader_projection = argi();
|
|
}
|
|
else if(argis("-neonnf")) {
|
|
PHASEFROM(2);
|
|
shift(); neon_nofill = argi();
|
|
}
|
|
else if(argis("-precw")) {
|
|
PHASEFROM(2);
|
|
shift_arg_formula(precise_width);
|
|
}
|
|
else if(argis("-d:all")) {
|
|
PHASEFROM(2); launch_dialog(edit_all_settings);
|
|
}
|
|
else if(argis("-d:find")) {
|
|
PHASEFROM(2); launch_dialog(find_setting);
|
|
}
|
|
else if(argis("-d:param")) {
|
|
PHASEFROM(2);
|
|
shift();
|
|
string s = args();
|
|
for(auto& fs: params) if(fs.first == s) {
|
|
dialog::items.clear();
|
|
dialog::key_actions.clear();
|
|
fs.second->show_edit_option();
|
|
for(auto p: dialog::key_actions) { p.second(); return 0; }
|
|
println(hlog, "no key action");
|
|
return 0;
|
|
}
|
|
println(hlog, "unknown param to edit: ", s);
|
|
}
|
|
else if(argis("-char")) {
|
|
auto& cs = vid.cs;
|
|
shift();
|
|
string s = args();
|
|
set_char_by_name(cs, s);
|
|
}
|
|
else return 1;
|
|
return 0;
|
|
}
|
|
|
|
EX void set_char_by_name(charstyle& cs, const string& s) {
|
|
if(s == "dodek") {
|
|
cs.charid = 4;
|
|
cs.lefthanded = false;
|
|
cs.skincolor = 0x202020FF;
|
|
cs.eyecolor = 0x20C000FF;
|
|
cs.haircolor = 0x202020FF;
|
|
cs.dresscolor =0x424242FF;
|
|
cs.swordcolor = 0xF73333FF;
|
|
}
|
|
else if(s == "rudy") {
|
|
cs.charid = 4;
|
|
cs.lefthanded = false;
|
|
cs.skincolor = 0xA44139FF;
|
|
cs.eyecolor = 0xD59533FF;
|
|
cs.haircolor = 0xC6634AFF;
|
|
cs.dresscolor =0xC6634AFF;
|
|
cs.swordcolor = 0x3CBB33FF;
|
|
}
|
|
else if(s == "running") {
|
|
cs.charid = 6;
|
|
cs.lefthanded = false;
|
|
cs.skincolor = 0xFFFFFFFF;
|
|
cs.eyecolor = 0xFF;
|
|
cs.haircolor = 0xFFFFFFFF;
|
|
cs.dresscolor =0xFFFFFFFF;
|
|
cs.swordcolor = 0xFF0000FF;
|
|
}
|
|
else if(s == "princess") {
|
|
cs.charid = 3;
|
|
cs.lefthanded = true;
|
|
cs.skincolor = 0xEFD0C9FF;
|
|
cs.haircolor = 0x301800FF;
|
|
cs.eyecolor = 0xC000FF;
|
|
cs.dresscolor = 0x408040FF;
|
|
cs.swordcolor = 0xFFFFFFFF;
|
|
}
|
|
else if(s == "worker") {
|
|
cs.charid = 2;
|
|
cs.skincolor = 0xC77A58FF;
|
|
cs.haircolor = 0x502810FF;
|
|
cs.dresscolor = 0xC0C000FF;
|
|
cs.eyecolor = 0x500040FF;
|
|
cs.swordcolor = 0x808080FF;
|
|
}
|
|
else {
|
|
cs.charid = atoi(s.c_str());
|
|
cs.lefthanded = cs.charid >= 10;
|
|
cs.charid %= 10;
|
|
}
|
|
}
|
|
|
|
EX int read_param_args() {
|
|
const string& s = arg::args();
|
|
auto pos = s.find("=");
|
|
if(pos == string::npos) return 1;
|
|
string name = s.substr(0, pos);
|
|
string value = s.substr(pos+1);
|
|
PHASEFROM(2);
|
|
if(!params.count(name)) {
|
|
println(hlog, "parameter unknown: ", name);
|
|
exit(1);
|
|
}
|
|
params[name]->load_as_animation(value);
|
|
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('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_param_args) +
|
|
addHook(hooks_args, 0, read_gamemode_args) + addHook(hooks_args, 0, read_color_args);
|
|
#endif
|
|
|
|
/* local parameter, for another game */
|
|
|
|
local_parameter_set* current_lps;
|
|
|
|
void local_parameter_set::pswitch() {
|
|
if(extends) extends->pswitch();
|
|
for(auto s: swaps) {
|
|
s.first->swap_with(s.second);
|
|
swap(s.first->name, s.second->name);
|
|
}
|
|
}
|
|
|
|
EX void lps_enable(local_parameter_set *lps) {
|
|
if(lps == current_lps) return;
|
|
if(current_lps) current_lps->pswitch();
|
|
current_lps = lps;
|
|
if(current_lps) current_lps->pswitch();
|
|
}
|
|
|
|
#if HDR
|
|
//template<class T> vector<std::unique_ptr<T>> lps_of_type;
|
|
extern vector<void*> lps_of_type;
|
|
|
|
template<class T, class U> void lps_add(local_parameter_set& lps, T&val, U nvalue) {
|
|
int found = 0;
|
|
for(auto& fs: savers) {
|
|
if(fs->affects(&val)) {
|
|
found++;
|
|
T* nv = new T(nvalue);
|
|
lps_of_type.emplace_back(nv);
|
|
println(hlog, lps.label, " found saver: ", fs->name);
|
|
fs->clone(lps, nv);
|
|
return;
|
|
}
|
|
}
|
|
if(found != 1) println(hlog, lps.label, " saver not found");
|
|
}
|
|
#endif
|
|
|
|
vector<void*> lps_of_type;
|
|
|
|
}
|