1 // Hyperbolic Rogue -- configuration
2 // Copyright (C) 2017-2018 Zeno Rogue, see 'hyper.cpp' for details
3
4 /** \file config.cpp
5 * \brief Configuration -- initial settings, saving/loading ini files, menus, etc.
6 */
7
8 #include "hyper.h"
9 namespace hr {
10
11 #if HDR
12 enum eCentering { face, edge, vertex };
13 #endif
14
15 EX eCentering centering;
16
17 EX function<bool()> auto_restrict;
18
19 EX void add_to_changed(struct setting *f);
20
return_false()21 EX bool return_false() { return false; }
22
23 #if HDR
24 struct supersaver {
25 string name;
26 virtual string save() = 0;
27 virtual void load(const string& s) = 0;
28 virtual bool dosave() = 0;
29 virtual void reset() = 0;
affectshr::supersaver30 virtual bool affects(void* v) { return false; }
31 virtual void set_default() = 0;
32 virtual ~supersaver() = default;
33 };
34
35 typedef vector<shared_ptr<supersaver>> saverlist;
36
37 extern saverlist savers;
38
39 struct setting {
40 function<bool()> restrict;
41 string parameter_name;
42 string config_name;
43 string menu_item_name;
44 string help_text;
45 reaction_t reaction;
46 char default_key;
47 cld last_value;
48 bool is_editable;
availablehr::setting49 virtual bool available() { if(restrict) return restrict(); return true; }
affectshr::setting50 virtual bool affects(void *v) { return false; }
add_as_saverhr::setting51 virtual void add_as_saver() {}
show_edit_optionhr::setting52 void show_edit_option() { show_edit_option(default_key); }
show_edit_optionhr::setting53 virtual void show_edit_option(char key) {
54 println(hlog, "default called!"); }
search_keyhr::setting55 virtual string search_key() {
56 return parameter_name + "|" + config_name + "|" + menu_item_name + "|" + help_text;
57 }
58 virtual cld get_cld() = 0;
settinghr::setting59 explicit setting() { restrict = auto_restrict; is_editable = false; }
check_changehr::setting60 virtual void check_change() {
61 cld val = get_cld();
62 if(val != last_value) {
63 last_value = val;
64 add_to_changed(this);
65 }
66 }
67 reaction_t sets;
set_setshr::setting68 setting *set_sets(const reaction_t& s) { sets = s; return this; }
69 setting *set_extra(const reaction_t& r);
70 setting *set_reaction(const reaction_t& r);
71 virtual ~setting() = default;
load_fromhr::setting72 virtual void load_from(const string& s) {
73 println(hlog, "cannot load this parameter");
74 exit(1);
75 }
76 };
77 #endif
78
set_extra(const reaction_t & r)79 setting *setting::set_extra(const reaction_t& r) {
80 auto s = sets; set_sets([s, r] { if(s) s(); dialog::extra_options = r; }); return this;
81 }
82
set_reaction(const reaction_t & r)83 setting *setting::set_reaction(const reaction_t& r) {
84 reaction = r; return this;
85 }
86
87 EX map<string, std::unique_ptr<setting>> params;
88
89 EX void show_edit_option_enum(char* value, const string& name, const vector<pair<string, string>>& options, char key, setting *s);
90
91 #if HDR
92 struct list_setting : setting {
93 virtual int get_value() = 0;
94 virtual void set_value(int i) = 0;
95 vector<pair<string, string> > options;
editablehr::list_setting96 list_setting* editable(const vector<pair<string, string> >& o, string menu_item_name, char key) {
97 is_editable = true;
98 options = o;
99 this->menu_item_name = menu_item_name;
100 default_key = key;
101 return this;
102 }
103 void show_edit_option(char key) override;
104 };
105
106 template<class T> struct enum_setting : list_setting {
107 T *value;
108 T dft;
get_valuehr::enum_setting109 int get_value() override { return (int) *value; }
set_valuehr::enum_setting110 void set_value(int i) override { *value = (T) i; }
affectshr::enum_setting111 bool affects(void* v) override { return v == value; }
112 void add_as_saver() override;
get_cldhr::enum_setting113 cld get_cld() override { return get_value(); }
load_fromhr::enum_setting114 void load_from(const string& s) override {
115 *value = (T) parseint(s);
116 }
117 };
118
119 struct float_setting : public setting {
120 ld *value;
121 ld dft;
122 ld min_value, max_value, step;
123 string unit;
editablehr::float_setting124 float_setting *editable(ld min_value, ld max_value, ld step, string menu_item_name, string help_text, char key) {
125 is_editable = true;
126 this->min_value = min_value;
127 this->max_value = max_value;
128 this->menu_item_name = menu_item_name;
129 this->help_text = help_text;
130 this->step = step;
131 default_key = key;
132 return this;
133 }
134 function<void(float_setting*)> modify_me;
modifhr::float_setting135 float_setting *modif(const function<void(float_setting*)>& r) { modify_me = r; return this; }
136 void add_as_saver() override;
affectshr::float_setting137 bool affects(void *v) override { return v == value; }
138 void show_edit_option(char key) override;
get_cldhr::float_setting139 cld get_cld() override { return *value; }
140 void load_from(const string& s) override;
141 };
142
143 struct int_setting : public setting {
144 int *value;
145 int dft;
146 int min_value, max_value;
147 ld step;
148 void add_as_saver() override;
149 function<void(int_setting*)> modify_me;
modifhr::int_setting150 int_setting *modif(const function<void(int_setting*)>& r) { modify_me = r; return this; }
affectshr::int_setting151 bool affects(void *v) override { return v == value; }
152 void show_edit_option(char key) override;
get_cldhr::int_setting153 cld get_cld() override { return *value; }
editablehr::int_setting154 int_setting *editable(int min_value, int max_value, ld step, string menu_item_name, string help_text, char key) {
155 this->min_value = min_value;
156 this->max_value = max_value;
157 this->menu_item_name = menu_item_name;
158 this->help_text = help_text;
159 this->step = step;
160 default_key = key;
161 return this;
162 }
163
load_fromhr::int_setting164 void load_from(const string& s) override {
165 *value = parseint(s);
166 }
167 };
168
169 struct bool_setting : public setting {
170 bool *value;
171 bool dft;
172 void add_as_saver() override;
173 reaction_t switcher;
editablehr::bool_setting174 bool_setting* editable(string cap, char key ) {
175 is_editable = true;
176 menu_item_name = cap; default_key = key; return this;
177 }
affectshr::bool_setting178 bool affects(void *v) override { return v == value; }
179 void show_edit_option(char key) override;
get_cldhr::bool_setting180 cld get_cld() override { return *value ? 1 : 0; }
load_fromhr::bool_setting181 void load_from(const string& s) override {
182 *value = parseint(s);
183 }
184 };
185
186 struct custom_setting : public setting {
187 function<void(char)> custom_viewer;
188 function<cld()> custom_value;
189 function<bool(void*)> custom_affect;
show_edit_optionhr::custom_setting190 void show_edit_option(char key) override { custom_viewer(key); }
get_cldhr::custom_setting191 cld get_cld() override { return custom_value(); }
affectshr::custom_setting192 bool affects(void *v) override { return custom_affect(v); }
193 };
194
195 #if CAP_CONFIG
196
197 template<class T> struct dsaver : supersaver {
198 T& val;
199 T dft;
dosavehr::dsaver200 bool dosave() override { return val != dft; }
resethr::dsaver201 void reset() override { val = dft; }
dsaverhr::dsaver202 explicit dsaver(T& val) : val(val) { }
affectshr::dsaver203 bool affects(void* v) override { return v == &val; }
set_defaulthr::dsaver204 void set_default() override { dft = val; }
205 };
206
207 template<class T> struct saver : dsaver<T> {};
208
addsaver(T & i,U name,V dft)209 template<class T, class U, class V> void addsaver(T& i, U name, V dft) {
210 auto s = make_shared<saver<T>> (i);
211 s->dft = dft;
212 s->name = name;
213 savers.push_back(s);
214 }
215
addsaver(T & i,string name)216 template<class T> void addsaver(T& i, string name) {
217 addsaver(i, name, i);
218 }
219
removesaver(T & val)220 template<class T> void removesaver(T& val) {
221 for(int i=0; i<isize(savers); i++)
222 if(savers[i]->affects(&val))
223 savers.erase(savers.begin() + i);
224 }
225
set_saver_default(T & val)226 template<class T> void set_saver_default(T& val) {
227 for(auto sav: savers)
228 if(sav->affects(&val))
229 sav->set_default();
230 }
231
232 template<class T> struct saverenum : supersaver {
233 T& val;
234 T dft;
saverenumhr::saverenum235 explicit saverenum(T& v) : val(v) { }
dosavehr::saverenum236 bool dosave() override { return val != dft; }
resethr::saverenum237 void reset() override { val = dft; }
savehr::saverenum238 string save() override { return its(int(val)); }
loadhr::saverenum239 void load(const string& s) override { val = (T) atoi(s.c_str()); }
affectshr::saverenum240 bool affects(void* v) override { return v == &val; }
set_defaulthr::saverenum241 void set_default() override { dft = val; }
242 };
243
addsaverenum(T & i,U name,T dft)244 template<class T, class U> void addsaverenum(T& i, U name, T dft) {
245 auto s = make_shared<saverenum<T>> (i);
246 s->dft = dft;
247 s->name = name;
248 savers.push_back(s);
249 }
250
addsaverenum(T & i,U name)251 template<class T, class U> void addsaverenum(T& i, U name) {
252 addsaverenum(i, name, i);
253 }
254
255 template<> struct saver<int> : dsaver<int> {
saverhr::saver256 explicit saver(int& val) : dsaver<int>(val) { }
savehr::saver257 string save() override { return its(val); }
loadhr::saver258 void load(const string& s) override { val = atoi(s.c_str()); }
259 };
260
261 template<> struct saver<char> : dsaver<char> {
saverhr::saver262 explicit saver(char& val) : dsaver<char>(val) { }
savehr::saver263 string save() override { return its(val); }
loadhr::saver264 void load(const string& s) override { val = atoi(s.c_str()); }
265 };
266
267 template<> struct saver<bool> : dsaver<bool> {
saverhr::saver268 explicit saver(bool& val) : dsaver<bool>(val) { }
savehr::saver269 string save() override { return val ? "yes" : "no"; }
loadhr::saver270 void load(const string& s) override { val = isize(s) && s[0] == 'y'; }
271 };
272
273 template<> struct saver<unsigned> : dsaver<unsigned> {
saverhr::saver274 explicit saver(unsigned& val) : dsaver<unsigned>(val) { }
savehr::saver275 string save() override { return itsh(val); }
loadhr::saver276 void load(const string& s) override { val = (unsigned) strtoll(s.c_str(), NULL, 16); }
277 };
278
279 template<> struct saver<string> : dsaver<string> {
saverhr::saver280 explicit saver(string& val) : dsaver<string>(val) { }
savehr::saver281 string save() override { return val; }
loadhr::saver282 void load(const string& s) override { val = s; }
283 };
284
285 template<> struct saver<ld> : dsaver<ld> {
saverhr::saver286 explicit saver(ld& val) : dsaver<ld>(val) { }
savehr::saver287 string save() override { return fts(val, 10); }
loadhr::saver288 void load(const string& s) override {
289 if(s == "0.0000000000e+000") ; // ignore!
290 else val = atof(s.c_str());
291 }
292 };
293 #endif
294 #endif
295
add_as_saver()296 void float_setting::add_as_saver() {
297 #if CAP_CONFIG
298 addsaver(*value, config_name, dft);
299 #endif
300 }
301
add_as_saver()302 void int_setting::add_as_saver() {
303 #if CAP_CONFIG
304 addsaver(*value, config_name, dft);
305 #endif
306 }
307
add_as_saver()308 void bool_setting::add_as_saver() {
309 #if CAP_CONFIG
310 addsaver(*value, config_name, dft);
311 #endif
312 }
313
load_from(const string & s)314 void float_setting::load_from(const string& s) {
315 anims::animate_parameter(*value, s, reaction);
316 }
317
non_editable()318 void non_editable() {
319 dialog::addHelp("Warning: editing this value through this menu may not work correctly");
320 }
321
show_edit_option(char key)322 void float_setting::show_edit_option(char key) {
323 if(modify_me) modify_me(this);
324 dialog::addSelItem(XLAT(menu_item_name), fts(*value) + unit, key);
325 dialog::add_action([this] () {
326 add_to_changed(this);
327 dialog::editNumber(*value, min_value, max_value, step, dft, XLAT(menu_item_name), help_text);
328 if(sets) sets();
329 if(reaction) dialog::reaction = reaction;
330 if(!is_editable) dialog::extra_options = non_editable;
331 });
332 }
333
show_edit_option(char key)334 void int_setting::show_edit_option(char key) {
335 if(modify_me) modify_me(this);
336 dialog::addSelItem(XLAT(menu_item_name), its(*value), key);
337 dialog::add_action([this] () {
338 add_to_changed(this);
339 dialog::editNumber(*value, min_value, max_value, step, dft, XLAT(menu_item_name), help_text);
340 if(sets) sets();
341 if(reaction) dialog::reaction = reaction;
342 if(!is_editable) dialog::extra_options = non_editable;
343 });
344 }
345
show_edit_option(char key)346 void bool_setting::show_edit_option(char key) {
347 dialog::addBoolItem(XLAT(menu_item_name), *value, key);
348 dialog::add_action([this] () {
349 add_to_changed(this);
350 switcher(); if(sets) sets();
351 if(reaction) reaction();
352 });
353 }
354
param_f(ld & val,const string p,const string s,ld dft)355 EX float_setting *param_f(ld& val, const string p, const string s, ld dft) {
356 unique_ptr<float_setting> u ( new float_setting );
357 u->parameter_name = p;
358 u->config_name = s;
359 u->menu_item_name = s;
360 u->value = &val;
361 u->last_value = dft;
362 if(dft == 0) {
363 u->min_value = -100;
364 u->max_value = +100;
365 }
366 else {
367 u->min_value = 0;
368 u->max_value = 2 * dft;
369 }
370 u->step = dft / 10;
371 u->dft = dft;
372 val = dft;
373 u->add_as_saver();
374 auto f = &*u;
375 params[u->parameter_name] = std::move(u);
376 return f;
377 }
378
param_esc(string s)379 EX string param_esc(string s) {
380 string out;
381 for(char c: s)
382 if(c == ' ' || c == '-' || c == ':')
383 out += '_';
384 else
385 out += c;
386 return out;
387 }
388
param_i(int & val,const string s,int dft)389 EX int_setting *param_i(int& val, const string s, int dft) {
390 unique_ptr<int_setting> u ( new int_setting );
391 u->parameter_name = param_esc(s);
392 u->config_name = s;
393 u->menu_item_name = s;
394 u->value = &val;
395 u->last_value = dft;
396 u->dft = dft;
397 val = dft;
398 if(dft == 0) {
399 u->min_value = -100;
400 u->max_value = +100;
401 }
402 else {
403 u->min_value = 0;
404 u->max_value = 2 * dft;
405 }
406 u->add_as_saver();
407 auto f = &*u;
408 params[u->parameter_name] = std::move(u);
409 return f;
410 }
411
param_i(int & val,const string s)412 EX int_setting *param_i(int& val, const string s) { return param_i(val, s, val); }
413
param_b(bool & val,const string s,bool dft)414 EX bool_setting *param_b(bool& val, const string s, bool dft) {
415 unique_ptr<bool_setting> u ( new bool_setting );
416 u->parameter_name = param_esc(s);
417 u->config_name = s;
418 u->menu_item_name = s;
419 u->value = &val;
420 u->last_value = dft;
421 u->dft = dft;
422 u->switcher = [&val] { val = !val; };
423 val = dft;
424 u->add_as_saver();
425 auto f = &*u;
426 params[u->parameter_name] = std::move(u);
427 return f;
428 }
429
param_b(bool & val,const string s)430 EX bool_setting *param_b(bool& val, const string s) { return param_b(val, s, val); }
431
432 #if HDR
add_as_saver()433 template<class T> void enum_setting<T>::add_as_saver() {
434 #if CAP_CONFIG
435 addsaverenum(*value, config_name, dft);
436 #endif
437 }
438
param_enum(T & val,const string p,const string s,T dft)439 template<class T> enum_setting<T> *param_enum(T& val, const string p, const string s, T dft) {
440 unique_ptr<enum_setting<T>> u ( new enum_setting<T> );
441 u->parameter_name = p;
442 u->config_name = s;
443 u->menu_item_name = s;
444 u->value = &val;
445 u->dft = dft;
446 val = dft;
447 u->last_value = u->get_cld();
448 u->add_as_saver();
449 auto f = &*u;
450 params[p] = std::move(u);
451 return f;
452 }
453 #endif
454
param_f(ld & val,const string s)455 EX float_setting* param_f(ld& val, const string s) {
456 return param_f(val, param_esc(s), s, val);
457 }
458
param_f(ld & val,const string p,const string s)459 EX float_setting* param_f(ld& val, const string p, const string s) {
460 return param_f(val, p, s, val);
461 }
462
param_f(ld & val,const string s,ld dft)463 EX float_setting* param_f(ld& val, const string s, ld dft) {
464 return param_f(val, param_esc(s), s, dft);
465 }
466
467 #if HDR
468 template<class T>
param_custom(T & val,const string & s,function<void (char)> menuitem,char key)469 custom_setting* param_custom(T& val, const string& s, function<void(char)> menuitem, char key) {
470 unique_ptr<custom_setting> u ( new custom_setting );
471 u->parameter_name = param_esc(s);
472 u->config_name = s;
473 u->menu_item_name = s;
474 u->last_value = (int) val;
475 u->custom_viewer = menuitem;
476 u->custom_value = [&val] () { return (int) val; };
477 u->custom_affect = [&val] (void *v) { return &val == v; };
478 u->default_key = key;
479 u->is_editable = true;
480 auto f = &*u;
481 params[s] = std::move(u);
482 return f;
483 }
484 #endif
485
486 EX ld bounded_mine_percentage = 0.1;
487 EX int bounded_mine_quantity, bounded_mine_max;
488
489 EX const char *conffile = "hyperrogue.ini";
490
491 /* extra space if more geometries are added */
492 EX array<ld, gGUARD+64> sightranges;
493
494 EX videopar vid;
495
496 #define DEFAULT_WALLMODE (ISMOBILE ? 3 : 5)
497 #define DEFAULT_MONMODE (ISMOBILE ? 2 : 4)
498
android_settings_changed()499 EX void android_settings_changed() {
500 #if ISANDROID
501 settingsChanged = true;
502 #endif
503 }
504
505 extern color_t floorcolors[landtypes];
506
getcs(int id IS (multi::cpid))507 EX charstyle& getcs(int id IS(multi::cpid)) {
508 if(multi::players>1 && id >= 0 && id < multi::players)
509 return multi::scs[id];
510 else
511 return vid.cs;
512 }
513
514 struct charstyle_old {
515 int charid;
516 color_t skincolor, haircolor, dresscolor, swordcolor, dresscolor2, uicolor;
517 bool lefthanded;
518 };
519
hread(hstream & hs,charstyle & cs)520 EX void hread(hstream& hs, charstyle& cs) {
521 // before 0xA61A there was no eyecolor
522 if(hs.get_vernum() < 0xA61A) {
523 charstyle_old cso;
524 hread_raw(hs, cso);
525 cs.charid = cso.charid;
526 cs.skincolor = cso.skincolor;
527 cs.haircolor = cso.haircolor;
528 cs.dresscolor = cso.dresscolor;
529 cs.swordcolor = cs.eyecolor = cso.swordcolor;
530 if(cs.charid < 4) cs.eyecolor = 0;
531 cs.dresscolor2 = cso.dresscolor2;
532 cs.uicolor = cso.uicolor;
533 cs.lefthanded = cso.lefthanded;
534 }
535 else hread_raw(hs, cs);
536 }
537
hwrite(hstream & hs,const charstyle & cs)538 EX void hwrite(hstream& hs, const charstyle& cs) {
539 hwrite_raw(hs, cs);
540 }
541
542 // void hread(hstream& hs, charstyle& cs) { hread_raw(hs, cs); }
543 // void hwrite(hstream& hs, const charstyle& cs) { hwrite_raw(hs, cs); }
544
csnameid(int id)545 EX string csnameid(int id) {
546 if(id == 0) return XLAT("male");
547 if(id == 1) return XLAT("female");
548 if(id == 2) return XLAT("Prince");
549 if(id == 3) return XLAT("Princess");
550 if(id == 4 || id == 5) return XLAT("cat");
551 if(id == 6 || id == 7) return XLAT("dog");
552 if(id == 8 || id == 9) return XLATN("Familiar");
553 return XLAT("none");
554 }
555
csname(charstyle & cs)556 EX string csname(charstyle& cs) {
557 return csnameid(cs.charid);
558 }
559
playergender()560 EX int playergender() {
561 return (getcs().charid >= 0 && (getcs().charid&1)) ? GEN_F : GEN_M;
562 }
princessgender()563 EX int princessgender() {
564 int g = playergender();
565 if(vid.samegender) return g;
566 return g == GEN_M ? GEN_F : GEN_M;
567 }
568
569 EX int default_language;
570
lang()571 EX int lang() {
572 if(vid.language >= 0)
573 return vid.language;
574 return default_language;
575 }
576
577 EX bool autojoy = true;
578
579 #if CAP_CONFIG
580 saverlist savers;
581 #endif
582
583 #if !CAP_CONFIG
addsaver(T & i,U name,V dft)584 template<class T, class U, class V> void addsaver(T& i, U name, V dft) {
585 i = dft;
586 }
587
addsaver(T & i,U name)588 template<class T, class U> void addsaver(T& i, U name) {}
addsaverenum(T & i,U name)589 template<class T, class U> void addsaverenum(T& i, U name) {}
addsaverenum(T & i,U name,T dft)590 template<class T, class U> void addsaverenum(T& i, U name, T dft) { i = dft; }
591 #endif
592
addsaver(charstyle & cs,string s)593 EX void addsaver(charstyle& cs, string s) {
594 addsaver(cs.charid, s + ".charid");
595 addsaver(cs.skincolor, s + ".skincolor");
596 addsaver(cs.eyecolor, s + ".eyecolor");
597 addsaver(cs.haircolor, s + ".haircolor");
598 addsaver(cs.dresscolor, s + ".dresscolor");
599 addsaver(cs.swordcolor, s + ".swordcolor");
600 addsaver(cs.dresscolor2, s + ".dresscolor2");
601 addsaver(cs.uicolor, s + ".uicolor");
602 addsaver(cs.lefthanded, s + ".lefthanded");
603 }
604
605 // R:239, G:208, B:207
606
607 unsigned int skincolors[] = { 7, 0xD0D0D0FF, 0xEFD0C9FF, 0xC77A58FF, 0xA58869FF, 0x602010FF, 0xFFDCB1FF, 0xEDE4C8FF };
608 unsigned int haircolors[] = { 8, 0x686868FF, 0x8C684AFF, 0xF2E1AEFF, 0xB55239FF, 0xFFFFFFFF, 0x804000FF, 0x502810FF, 0x301800FF };
609 unsigned int dresscolors[] = { 6, 0xC00000FF, 0x00C000FF, 0x0000C0FF, 0xC0C000FF, 0xC0C0C0FF, 0x202020FF };
610 unsigned int dresscolors2[] = { 7, 0x8080FFC0, 0x80FF80C0, 0xFF8080C0, 0xFFFF80C0, 0xFF80FFC0, 0x80FFFFC0, 0xFFFFFF80 };
611 unsigned int swordcolors[] = { 6, 0xC0C0C0FF, 0xFFFFFFFF, 0xFFC0C0FF, 0xC0C0FFFF, 0x808080FF, 0x202020FF };
612 unsigned int eyecolors[] = { 4, 0x00C000FF, 0x0000C0FF, 0xC00000FF, 0xC0C000FF, 0x804010FF, 0x00C000FF };
613
initcs(charstyle & cs)614 EX void initcs(charstyle &cs) {
615 cs.charid = 0;
616 cs.skincolor = 0xD0D0D0FF;
617 cs.haircolor = 0x686868FF;
618 cs.dresscolor = 0xC00000FF;
619 cs.swordcolor = 0xD0D0D0FF;
620 cs.dresscolor2= 0x8080FFC0;
621 cs.uicolor = 0xFF0000FF;
622 cs.eyecolor = 0x603000FF;
623 cs.lefthanded = false;
624 }
625
savecolortable(colortable & ct,string name)626 EX void savecolortable(colortable& ct, string name) {
627 for(int i=0; i<isize(ct); i++)
628 addsaver(ct[i], "color:" + name + ":" + its(i));
629 }
630
631 EX purehookset hooks_configfile;
632
initConfig()633 EX void initConfig() {
634
635 // basic config
636 param_i(vid.flashtime, "flashtime", 8);
637 param_enum(vid.msgleft, "message_style", "message style", 2)
638 -> editable({{"centered", ""}, {"left-aligned", ""}, {"line-broken", ""}}, "message style", 'a');
639
640 param_i(vid.msglimit, "message limit", 5);
641 param_i(vid.timeformat, "message log time format", 0);
642
643 param_b(resizable, "resizable", true)
644 -> editable("resizable window", 'r');
645
646 param_b(display_yasc_codes, "yasc", false)
647 -> editable("YASC codes", 'Y')
648 -> set_reaction([] {
649 addMessage(XLAT("YASC codes: Sides-Entity-Restrict-Threat-Wall"));
650 });
651
652 param_b(vid.relative_font, "relative_font", true)
653 -> editable("set relative font size", 'r')
654 -> set_reaction(compute_fsize);
655 param_i(vid.fontscale, "fontscale", 100)
656 -> editable(25, 400, 10, "font scale", "", 'b')
657 -> set_reaction(compute_fsize)
658 -> set_sets([] { dialog::bound_low(0); });
659
660 param_i(vid.abs_fsize, "fsize", 12)
661 -> editable(1, 72, 1, "font size", "", 'b')
662 -> set_reaction(compute_fsize)
663 -> set_sets([] { dialog::bound_low(0); });
664
665 param_i(vid.mobilecompasssize, "mobile compass size", 0); // ISMOBILE || ISPANDORA ? 30 : 0);
666 param_i(vid.radarsize, "radarsize size", 120);
667 addsaver(vid.radarrange, "radarrange", 2.5);
668 param_i(vid.axes, "movement help", 1);
669 param_b(vid.axes3, "movement help3", true);
670 param_i(vid.shifttarget, "shift-targetting", 2);
671 addsaver(vid.steamscore, "scores to Steam", 1);
672 initcs(vid.cs); addsaver(vid.cs, "single");
673 param_b(vid.samegender, "princess choice", false);
674 addsaver(vid.language, "language", -1);
675 param_b(vid.drawmousecircle, "mouse circle", ISMOBILE || ISPANDORA);
676 param_b(vid.revcontrol, "reverse control", false);
677 #if CAP_AUDIO
678 param_i(musicvolume, "music volume")
679 ->editable(0, 128, 10, "background music volume", "", 'b')
680 ->set_sets(sets_music_volume);
681 #endif
682 #if CAP_SDLAUDIO
683 addsaver(music_out_of_focus, "music out of focus", false);
684 #endif
685 #if CAP_AUDIO
686 param_i(effvolume, "sound effect volume")
687 ->editable(0, 128, 10, "sound effects volume", "", 'e')
688 ->set_sets(sets_sfx_volume);
689 #endif
690
691 param_enum(vid.faraway_highlight, "faraway_highlight", "highlight faraway monsters", tlNoThreat)
692 ->editable({{"off", ""}, {"spam", ""}, {"normal monsters", ""}, {"high-threat monsters only", ""}}, "highlight faraway monsters", 'h');
693
694 param_i(vid.faraway_highlight_color, "faraway_highlight_color", 50)
695 -> editable(0, 100, 10, "faraway highlight color", "0 = monster color, 100 = red-light oscillation", 'c');
696
697 param_enum(glyphsortorder, "glyph_sort", "glyph sort order", glyphsortorder)
698 ->editable({
699 {"first on top", ""},
700 {"first on bottom", ""},
701 {"last on top", ""},
702 {"last on bottom", ""},
703 {"by land", ""},
704 {"by number", ""}
705 }, "inventory/kill sorting", 'k');
706
707 // basic graphics
708
709 param_b(vid.wantGL, "usingGL", true)
710 ->editable("openGL mode", 'o');
711
712 addsaver(vid.want_antialias, "antialias", AA_NOGL | AA_FONT | (ISWEB ? AA_MULTI : AA_LINES) | AA_VERSION);
713 addsaver(vid.fineline, "fineline", true);
714 param_f(vid.linewidth, "linewidth", 1);
715 addsaver(precise_width, "precisewidth", .5);
716 addsaver(perfect_linewidth, "perfect_linewidth", 1);
717 param_f(linepatterns::width, "lpwidth", "pattern-linewidth", 1);
718 addsaver(fat_edges, "fat-edges");
719 param_f(vid.sspeed, "sspeed", "scrollingspeed", 0);
720 param_f(vid.mspeed, "mspeed", "movement speed", 1);
721 addsaver(vid.aurastr, "aura strength", ISMOBILE ? 0 : 128);
722 addsaver(vid.aurasmoothen, "aura smoothen", 5);
723 param_enum(vid.graphglyph, "graphglyph", "graphical items/kills", 1)
724 -> editable({{"letters", ""}, {"auto", ""}, {"images", ""}}, "inventory/kill mode", 'd');
725
726 param_f(vid.binary_width, "bwidth", "binary-tiling-width", 1);
727 param_custom(vid.binary_width, "binary tiling width", menuitem_binary_width, 'v');
728
729 addsaver(vid.particles, "extra effects", 1);
730 param_i(vid.framelimit, "frame limit", 999);
731
732 #if !ISMOBWEB
733 param_b(vid.want_vsync, "vsync", true)
734 ->editable("vsync", 'v');
735 #endif
736
737 param_b(vid.want_fullscreen, "fullscreen", false)
738 ->editable("fullscreen mode", 'f');
739 param_b(vid.change_fullscr, "fullscreen_change", false)
740 ->editable("use specific fullscreen resolution", 'g');
741 param_b(vid.relative_window_size, "window_relative", true)
742 ->editable("specify relative window size", 'g');
743
744 param_custom(vid.xres, "xres", [] (char ch) {}, 0)->restrict = return_false;
745 param_custom(vid.yres, "yres", [] (char ch) {}, 0)->restrict = return_false;
746
747 param_i(vid.fullscreen_x, "fullscreen_x", 1280)
748 -> editable(640, 3840, 640, "fullscreen resolution to use (X)", "", 'x')
749 -> set_sets([] { dialog::bound_low(640); });
750
751 param_i(vid.fullscreen_y, "fullscreen_y", 1024)
752 -> editable(480, 2160, 480, "fullscreen resolution to use (Y)", "", 'x')
753 -> set_sets([] { dialog::bound_low(480); });
754
755 param_i(vid.window_x, "window_x", 1280)
756 -> editable(160, 3840, 160, "window resolution to use (X)", "", 'x')
757 -> set_sets([] { dialog::bound_low(160); });
758
759 param_i(vid.window_y, "window_y", 1024)
760 -> editable(120, 2160, 120, "window resolution to use (Y)", "", 'x')
761 -> set_sets([] { dialog::bound_low(120); });
762
763 param_f(vid.window_rel_x, "window_rel_x", .9)
764 -> editable(.1, 1, .1, "screen size percentage to use (X)", "", 'x')
765 -> set_sets([] { dialog::bound_low(.1); });
766
767 param_f(vid.window_rel_y, "window_rel_y", .9)
768 -> editable(.1, 1, .1, "screen size percentage to use (Y)", "", 'x')
769 -> set_sets([] { dialog::bound_low(.1); });
770
771 param_b(vid.darkhepta, "mark heptagons", false);
772
773 for(auto& lp: linepatterns::patterns) {
774 addsaver(lp->color, "lpcolor-" + lp->lpname);
775 addsaver(lp->multiplier, "lpwidth-" + lp->lpname);
776 }
777
778 // special graphics
779
780 addsaver(vid.monmode, "monster display mode", DEFAULT_MONMODE);
781 addsaver(vid.wallmode, "wall display mode", DEFAULT_WALLMODE);
782 addsaver(vid.highlightmode, "highlightmode");
783
784 addsaver(vid.always3, "3D always", false);
785
786 addsaver(memory_saving_mode, "memory_saving_mode", (ISMOBILE || ISPANDORA || ISWEB) ? 1 : 0);
787 param_i(reserve_limit, "memory_reserve", 128);
788 addsaver(show_memory_warning, "show_memory_warning");
789
790 addsaver(rug::renderonce, "rug-renderonce");
791 addsaver(rug::rendernogl, "rug-rendernogl");
792 addsaver(rug::texturesize, "rug-texturesize");
793 #if CAP_RUG
794 param_f(rug::model_distance, "rug_model_distance", "rug-model-distance");
795 #endif
796
797 param_b(vid.backeffects, "background particle effects", (ISMOBILE || ISPANDORA) ? false : true);
798 // control
799
800 param_i(vid.joyvalue, "vid.joyvalue", 4800);
801 param_i(vid.joyvalue2, "vid.joyvalue2", 5600);
802 param_i(vid.joysmooth, "vid.joysmooth", 200);
803 param_i(vid.joypanthreshold, "vid.joypanthreshold", 2500);
804 param_f(vid.joypanspeed, "vid.joypanspeed", ISPANDORA ? 0.0001 : 0);
805 addsaver(autojoy, "autojoy");
806
807 vid.killreduction = 0;
808
809 param_b(vid.skipstart, "skip the start menu", false);
810 param_b(vid.quickmouse, "quick mouse", !ISPANDORA);
811
812 // colors
813
814 param_f(crosshair_size, "size:crosshair");
815 addsaver(crosshair_color, "color:crosshair");
816
817 addsaver(backcolor, "color:background");
818 addsaver(forecolor, "color:foreground");
819 addsaver(bordcolor, "color:borders");
820 addsaver(ringcolor, "color:ring");
821 param_f(vid.multiplier_ring, "mring", "mult:ring", 1);
822 addsaver(modelcolor, "color:model");
823 addsaver(periodcolor, "color:period");
824 addsaver(stdgridcolor, "color:stdgrid");
825 param_f(vid.multiplier_grid, "mgrid", "mult:grid", 1);
826 addsaver(dialog::dialogcolor, "color:dialog");
827 for(auto& p: colortables)
828 savecolortable(p.second, s0+"canvas"+p.first);
829 savecolortable(distcolors, "distance");
830 savecolortable(minecolors, "mines");
831 #if CAP_COMPLEX2
832 savecolortable(brownian::colors, "color:brown");
833 #endif
834
835 for(int i=0; i<motypes; i++)
836 addsaver(minf[i].color, "color:monster:" + its(i));
837 for(int i=0; i<ittypes; i++)
838 addsaver(iinf[i].color, "color:item:" + its(i));
839 for(int i=0; i<landtypes; i++)
840 addsaver(floorcolors[i], "color:land:" + its(i));
841 for(int i=0; i<walltypes; i++)
842 addsaver(winf[i].color, "color:wall:" + its(i));
843
844 // modes
845
846 addsaverenum(geometry, "mode-geometry");
847 addsaver(shmup::on, "mode-shmup", false);
848 addsaver(hardcore, "mode-hardcore", false);
849 addsaverenum(land_structure, "mode-chaos");
850 #if CAP_INV
851 addsaver(inv::on, "mode-Orb Strategy");
852 #endif
853 addsaverenum(variation, "mode-variation", eVariation::bitruncated);
854 addsaver(peace::on, "mode-peace");
855 addsaver(peace::otherpuzzles, "mode-peace-submode");
856 addsaverenum(specialland, "land for special modes");
857
858 addsaver(viewdists, "expansion mode");
859 param_f(backbrightness, "back", "brightness behind sphere");
860
861 param_f(vid.ipd, "ipd", "interpupilar-distance", 0.05);
862 param_f(vid.lr_eyewidth, "lr", "eyewidth-lr", 0.5);
863 param_f(vid.anaglyph_eyewidth, "anaglyph", "eyewidth-anaglyph", 0.1);
864 param_f(vid.fov, "fov", "field-of-vision", 90);
865 addsaver(vid.desaturate, "desaturate", 0);
866
867 param_enum(vid.stereo_mode, "stereo_mode", "stereo-mode", vid.stereo_mode)
868 ->editable({{"OFF", ""}, {"anaglyph", ""}, {"side-by-side", ""}
869 #if CAP_ODS
870 , {"ODS", ""}
871 #endif
872 }, "stereo mode", 'm');
873
874 param_f(vid.plevel_factor, "plevel_factor", 0.7);
875
876 #if CAP_GP
877 addsaver(gp::param.first, "goldberg-x", gp::param.first);
878 addsaver(gp::param.second, "goldberg-y", gp::param.second);
879 #endif
880
881 param_b(nohud, "no-hud", false);
882 param_b(nofps, "no-fps", false);
883
884 #if CAP_IRR
885 addsaver(irr::density, "irregular-density", 2);
886 addsaver(irr::cellcount, "irregular-cellcount", 150);
887 addsaver(irr::quality, "irregular-quality", .2);
888 addsaver(irr::place_attempts, "irregular-place", 10);
889 addsaver(irr::rearrange_max_attempts, "irregular-rearrange-max", 50);
890 addsaver(irr::rearrange_less, "irregular-rearrangeless", 10);
891 #endif
892
893 param_i(vid.linequality, "line quality", 0);
894
895 #if CAP_FILES && CAP_SHOT && CAP_ANIMATIONS
896 addsaver(anims::animfile, "animation file format");
897 #endif
898
899 #if CAP_RUG
900 addsaver(rug::move_on_touch, "rug move on touch");
901 #endif
902
903 #if CAP_CRYSTAL
904 param_f(crystal::compass_probability, "cprob", "compass-probability");
905 addsaver(crystal::view_coordinates, "crystal-coordinates");
906 #endif
907
908 #if CAP_TEXTURE
909 param_b(texture::texture_aura, "texture-aura", false);
910 #endif
911
912 addsaver(vid.use_smart_range, "smart-range", 0);
913 param_f(vid.smart_range_detail, "smart-range-detail", 8)
914 ->editable(1, 50, 1, "minimum visible cell in pixels", "", 'd');
915
916 param_f(vid.smart_range_detail_3, "smart-range-detail-3", 30)
917 ->editable(1, 50, 1, "minimum visible cell in pixels", "", 'd');
918
919 param_b(vid.smart_area_based, "smart-area-based", false);
920 param_i(vid.cells_drawn_limit, "limit on cells drawn", 10000);
921 param_i(vid.cells_generated_limit, "limit on cells generated", 250);
922
923 #if CAP_SOLV
924 addsaver(sn::solrange_xy, "solrange-xy");
925 addsaver(sn::solrange_z, "solrange-z");
926 #endif
927 addsaver(slr::steps, "slr-steps");
928 addsaver(slr::range_xy, "slr-range-xy");
929
930 param_f(arcm::euclidean_edge_length, "arcm-euclid-length");
931
932 #if CAP_ARCM
933 addsaver(arcm::current.symbol, "arcm-symbol", "4^5");
934 #endif
935 addsaverenum(hybrid::underlying, "product-underlying");
936
937 for(int i=0; i<isize(ginf); i++) {
938 if(ginf[i].flags & qELLIPTIC)
939 sightranges[i] = M_PI;
940 else if(ginf[i].cclass == gcSphere)
941 sightranges[i] = 2 * M_PI;
942 else if(ginf[i].cclass == gcEuclid)
943 sightranges[i] = 10;
944 else if(ginf[i].cclass == gcSL2)
945 sightranges[i] = 4.5;
946 else if(ginf[i].cclass == gcHyperbolic && ginf[i].g.gameplay_dimension == 2)
947 sightranges[i] = 4.5;
948 else
949 sightranges[i] = 5;
950 sightranges[gArchimedean] = 10;
951 if(i < gBinary3) addsaver(sightranges[i], "sight-g" + its(i));
952 }
953
954 ld bonus = 0;
955 ld emul = 1;
956
957 param_b(dialog::onscreen_keyboard, "onscreen_keyboard")
958 ->editable("onscreen keyboard", 'k');
959
960 param_b(context_fog, "coolfog");
961
962 addsaver(sightranges[gBinary3], "sight-binary3", 3.1 + bonus);
963 addsaver(sightranges[gCubeTiling], "sight-cubes", 10);
964 addsaver(sightranges[gCell120], "sight-120cell", 2 * M_PI);
965 addsaver(sightranges[gECell120], "sight-120cell-elliptic", M_PI);
966 addsaver(sightranges[gRhombic3], "sight-rhombic", 10.5 * emul);
967 addsaver(sightranges[gBitrunc3], "sight-bitrunc", 12 * emul);
968 addsaver(sightranges[gSpace534], "sight-534", 4 + bonus);
969 addsaver(sightranges[gSpace435], "sight-435", 3.8 + bonus);
970
971 addsaver(sightranges[gCell5], "sight-5cell", 2 * M_PI);
972 addsaver(sightranges[gCell8], "sight-8cell", 2 * M_PI);
973 addsaver(sightranges[gECell8], "sight-8cell-elliptic", M_PI);
974 addsaver(sightranges[gCell16], "sight-16cell", 2 * M_PI);
975 addsaver(sightranges[gECell16], "sight-16cell-elliptic", M_PI);
976 addsaver(sightranges[gCell24], "sight-24cell", 2 * M_PI);
977 addsaver(sightranges[gECell24], "sight-24cell-elliptic", M_PI);
978 addsaver(sightranges[gCell600], "sight-600cell", 2 * M_PI);
979 addsaver(sightranges[gECell600], "sight-600cell-elliptic", M_PI);
980 addsaver(sightranges[gHoroTris], "sight-horotris", 2.9 + bonus);
981 addsaver(sightranges[gHoroRec], "sight-hororec", 2.2 + bonus);
982 addsaver(sightranges[gHoroHex], "sight-horohex", 2.75 + bonus);
983
984 addsaver(sightranges[gKiteDart3], "sight-kd3", 2.25 + bonus);
985
986 addsaver(sightranges[gField435], "sight-field435", 4 + bonus);
987 addsaver(sightranges[gField534], "sight-field534", 3.8 + bonus);
988 addsaver(sightranges[gSol], "sight-sol");
989 addsaver(sightranges[gNil], "sight-nil", 6.5 + bonus);
990 addsaver(sightranges[gNIH], "sight-nih");
991 addsaver(sightranges[gSolN], "sight-solnih");
992
993 addsaver(sightranges[gCrystal344], "sight-crystal344", 2.5); /* assume raycasting */
994 addsaver(sightranges[gSpace344], "sight-344", 4.5);
995 addsaver(sightranges[gSpace336], "sight-336", 4);
996
997 param_b(vid.sloppy_3d, "sloppy3d", true);
998
999 param_i(vid.texture_step, "wall-quality", 4);
1000
1001 param_b(smooth_scrolling, "smooth-scrolling", false);
1002 addsaver(mouseaim_sensitivity, "mouseaim_sensitivity", 0.01);
1003
1004 param_b(vid.consider_shader_projection, "shader-projection", true);
1005
1006 param_b(tortoise::shading_enabled, "tortoise_shading", true);
1007
1008 addsaver(bounded_mine_percentage, "bounded_mine_percentage");
1009
1010 param_b(nisot::geodesic_movement, "solv_geodesic_movement", true);
1011
1012 addsaver(s2xe::qrings, "s2xe-rings");
1013 addsaver(rots::underlying_scale, "rots-underlying-scale");
1014
1015 param_b(vid.bubbles_special, "bubbles-special", 1);
1016 param_b(vid.bubbles_threshold, "bubbles-threshold", 1);
1017 param_b(vid.bubbles_all, "bubbles-all", 0);
1018
1019 #if CAP_SHMUP
1020 multi::initConfig();
1021 #endif
1022
1023 addsaver(asonov::period_xy, "asonov:period_xy");
1024 addsaver(asonov::period_z, "asonov:period_z");
1025 addsaver(nilv::nilperiod[0], "nilperiod_x");
1026 addsaver(nilv::nilperiod[1], "nilperiod_y");
1027 addsaver(nilv::nilperiod[2], "nilperiod_z");
1028
1029 param_enum(neon_mode, "neon_mode", "neon_mode", neon_mode)
1030 ->editable(
1031 {{"OFF", ""}, {"standard", ""}, {"no boundary mode", ""}, {"neon mode II", ""}, {"illustration mode", ""}},
1032 "neon mode", 'M'
1033 );
1034
1035 addsaverenum(neon_nofill, "neon_nofill");
1036 param_b(noshadow, "noshadow");
1037 param_b(bright, "bright");
1038 param_b(cblind, "cblind");
1039
1040 addsaver(berger_limit, "berger_limit");
1041
1042 addsaverenum(centering, "centering");
1043
1044 param_f(camera_speed, "camspd", "camera-speed", 1);
1045 param_f(camera_rot_speed, "camrot", "camera-rot-speed", 1);
1046
1047 param_f(panini_alpha, "panini_alpha", 0);
1048 param_f(stereo_alpha, "stereo_alpha", 0);
1049
1050 callhooks(hooks_configfile);
1051
1052 #if CAP_SHOT
1053 addsaver(levellines, "levellines");
1054 #endif
1055
1056 #if CAP_CONFIG
1057 for(auto s: savers) s->reset();
1058 #endif
1059
1060 param_custom(sightrange_bonus, "sightrange_bonus", menuitem_sightrange_bonus, 'r');
1061 param_custom(vid.use_smart_range, "sightrange_style", menuitem_sightrange_style, 's');
1062
1063 param_custom(gp::param.first, "Goldberg x", menuitem_change_variation, 0);
1064 param_custom(gp::param.second, "Goldberg y", menuitem_change_variation, 0);
1065 param_custom(variation, "variation", menuitem_change_variation, 'v')
1066 ->help_text = "variation|dual|bitruncated";
1067 param_custom(geometry, "geometry", menuitem_change_geometry, 0)
1068 ->help_text = "hyperbolic|spherical|Euclidean";
1069
1070 param_i(stamplen, "stamplen");
1071 }
1072
inSpecialMode()1073 EX bool inSpecialMode() {
1074 return !ls::nice_walls() || ineligible_starting_land || !BITRUNCATED || peace::on ||
1075 #if CAP_TOUR
1076 tour::on ||
1077 #endif
1078 yendor::on || tactic::on || randomPatternsMode ||
1079 geometry != gNormal || pmodel != mdDisk || pconf.alpha != 1 || pconf.scale != 1 ||
1080 rug::rugged || vid.monmode != DEFAULT_MONMODE ||
1081 vid.wallmode != DEFAULT_WALLMODE;
1082 }
1083
have_current_settings()1084 EX bool have_current_settings() {
1085 int modecount = 0;
1086 if(inv::on) modecount++;
1087 if(shmup::on) modecount += 10;
1088 #if CAP_TOUR
1089 if(tour::on) modecount += 10;
1090 #endif
1091 if(!ls::nice_walls()) modecount += 10;
1092 if(!BITRUNCATED) modecount += 10;
1093 if(peace::on) modecount += 10;
1094 if(yendor::on) modecount += 10;
1095 if(tactic::on) modecount += 10;
1096 if(randomPatternsMode) modecount += 10;
1097 if(geometry != gNormal) modecount += 10;
1098
1099 if(modecount > 1)
1100 return true;
1101
1102 return false;
1103 }
1104
have_current_graph_settings()1105 EX bool have_current_graph_settings() {
1106 if(pconf.xposition || pconf.yposition || pconf.alpha != 1 || pconf.scale != 1)
1107 return true;
1108 if(pmodel != mdDisk || vid.monmode != DEFAULT_MONMODE || vid.wallmode != DEFAULT_WALLMODE)
1109 return true;
1110 if(firstland != laIce || multi::players != 1 || rug::rugged)
1111 return true;
1112
1113 return false;
1114 }
1115
reset_graph_settings()1116 EX void reset_graph_settings() {
1117 pmodel = mdDisk; pconf.alpha = 1; pconf.scale = 1;
1118 pconf.xposition = pconf.yposition = 0;
1119 #if CAP_RUG
1120 if(rug::rugged) rug::close();
1121 #endif
1122
1123 vid.monmode = DEFAULT_MONMODE;
1124 vid.wallmode = DEFAULT_WALLMODE;
1125 }
1126
1127 EX void resetModes(char leave IS('c')) {
1128 while(game_active || gamestack::pushed()) {
1129 if(game_active) stop_game();
1130 if(gamestack::pushed()) gamestack::pop();
1131 }
1132 if(shmup::on != (leave == rg::shmup)) stop_game_and_switch_mode(rg::shmup);
1133 if(inv::on != (leave == rg::inv)) stop_game_and_switch_mode(rg::inv);
1134
1135 /* we do this twice to make sure that stop_game_and_switch_mode switches to the correct land_structure */
1136 for(int i=0; i<2; i++) {
1137 if(leave == rg::chaos && !ls::std_chaos()) stop_game_and_switch_mode(rg::chaos);
1138 if(leave != rg::chaos && !ls::nice_walls()) stop_game_and_switch_mode(rg::chaos);
1139 }
1140
1141 if((!!dual::state) != (leave == rg::dualmode)) stop_game_and_switch_mode(rg::dualmode);
1142
1143 if(peace::on != (leave == rg::peace)) stop_game_and_switch_mode(rg::peace);
1144 #if CAP_TOUR
1145 if(tour::on != (leave == rg::tour)) stop_game_and_switch_mode(rg::tour);
1146 #endif
1147 if(yendor::on != (leave == rg::yendor)) stop_game_and_switch_mode(rg::yendor);
1148 if(tactic::on != (leave == rg::tactic)) stop_game_and_switch_mode(rg::tactic);
1149 if(randomPatternsMode != (leave == rg::randpattern)) stop_game_and_switch_mode(rg::randpattern);
1150 if(multi::players != 1) {
1151 stop_game_and_switch_mode(); multi::players = 1;
1152 }
1153 if(firstland != laIce || specialland != laIce) {
1154 stop_game();
1155 firstland = laIce; specialland = laIce; stop_game_and_switch_mode();
1156 }
1157
1158 set_geometry(gNormal);
1159 set_variation(leave == rg::heptagons ? eVariation::pure : eVariation::bitruncated);
1160
1161 start_game();
1162 }
1163
1164 #if CAP_CONFIG
resetConfig()1165 EX void resetConfig() {
1166 dynamicval<int> rx(vid.xres, 0);
1167 dynamicval<int> ry(vid.yres, 0);
1168 dynamicval<int> rf(vid.fsize, 0);
1169 dynamicval<bool> rfs(vid.full, false);
1170 for(auto s: savers)
1171 if(s->name.substr(0,5) != "mode-")
1172 s->reset();
1173 }
1174 #endif
1175
1176 #if CAP_CONFIG
saveConfig()1177 EX void saveConfig() {
1178 DEBB(DF_INIT, ("save config\n"));
1179 FILE *f = fopen(conffile, "wt");
1180 if(!f) {
1181 addMessage(s0 + "Could not open the config file: " + conffile);
1182 return;
1183 }
1184
1185 {
1186 int pt_depth = 0, pt_camera = 0, pt_alpha = 0;
1187 if(vid.tc_depth > vid.tc_camera) pt_depth++;
1188 if(vid.tc_depth < vid.tc_camera) pt_camera++;
1189 if(vid.tc_depth > vid.tc_alpha ) pt_depth++;
1190 if(vid.tc_depth < vid.tc_alpha ) pt_alpha ++;
1191 if(vid.tc_alpha > vid.tc_camera) pt_alpha++;
1192 if(vid.tc_alpha < vid.tc_camera) pt_camera++;
1193 vid.tc_alpha = pt_alpha;
1194 vid.tc_camera = pt_camera;
1195 vid.tc_depth = pt_depth;
1196 }
1197
1198 for(auto s: savers) if(s->dosave())
1199 fprintf(f, "%s=%s\n", s->name.c_str(), s->save().c_str());
1200
1201 fclose(f);
1202 #if !ISMOBILE
1203 addMessage(s0 + "Configuration saved to: " + conffile);
1204 #else
1205 addMessage(s0 + "Configuration saved");
1206 #endif
1207 }
1208
readf(FILE * f,ld & x)1209 void readf(FILE *f, ld& x) {
1210 double fl = x;
1211 hr::ignore(fscanf(f, "%lf", &fl));
1212 x = fl;
1213 }
1214
1215 map<string, shared_ptr<supersaver> > allconfigs;
1216
parseline(const string & str)1217 EX void parseline(const string& str) {
1218 if(str[0] == '#') return;
1219 for(int i=0; i<isize(str); i++) if(str[i] == '=') {
1220 string cname = str.substr(0, i);
1221 if(!allconfigs.count(cname)) {
1222 printf("Warning: unknown config variable: %s\n", str.c_str());
1223 return;
1224 }
1225 auto sav = allconfigs[cname];
1226 sav->load(str.substr(i+1));
1227 return;
1228 }
1229 printf("Warning: config line without equality sign: %s\n", str.c_str());
1230 }
1231
loadNewConfig(FILE * f)1232 EX void loadNewConfig(FILE *f) {
1233 for(auto& c: savers) allconfigs[c->name] = c;
1234 string rd;
1235 while(true) {
1236 int c = fgetc(f);
1237 if(c == -1) break;
1238 if(c == 10 || c == 13) {
1239 if(rd != "") parseline(rd);
1240 rd = "";
1241 }
1242 else rd += c;
1243 }
1244 allconfigs.clear();
1245 }
1246
loadConfig()1247 EX void loadConfig() {
1248
1249 DEBB(DF_INIT, ("load config"));
1250 vid.xres = 9999; vid.yres = 9999; vid.framelimit = 999;
1251 FILE *f = fopen(conffile, "rt");
1252 if(f) {
1253 int err;
1254 int fs;
1255 err=fscanf(f, "%d%d%d%d", &vid.xres, &vid.yres, &fs, &vid.fsize);
1256 if(err != 4)
1257 loadNewConfig(f);
1258 else {
1259 vid.full = fs;
1260 #if CAP_LEGACY
1261 loadOldConfig(f);
1262 #endif
1263 }
1264
1265 fclose(f);
1266 DEBB(DF_INIT, ("Loaded configuration: %s\n", conffile));
1267 }
1268
1269 geom3::apply_always3();
1270 polygonal::solve();
1271 check_cgi();
1272 cgi.prepare_basics();
1273 }
1274 #endif
1275
1276 EX void add_cells_drawn(char c IS('C')) {
1277 dialog::addSelItem(XLAT("cells drawn"), (noclipped ? its(cells_drawn) + " (" + its(noclipped) + ")" : its(cells_drawn)) + " / " + its(vid.cells_drawn_limit), c);
__anond343dad31302() 1278 dialog::add_action([] () {
1279 dialog::editNumber(vid.cells_drawn_limit, 100, 1000000, log(10), 10000, XLAT("limit on cells drawn"),
1280 XLAT("This limit exists to protect the engine from freezing when too many cells would be drawn according to the current options.")
1281 );
1282 dialog::scaleLog();
1283 });
1284 if(WDIM == 3 || vid.use_smart_range == 2) {
1285 dialog::addSelItem(XLAT("limit generated cells per frame"), its(vid.cells_generated_limit), 'L');
__anond343dad31402() 1286 dialog::add_action([] () {
1287 dialog::editNumber(vid.cells_generated_limit, 1, 1000, log(10), 25, XLAT("limit generated cells per frame"),
1288 XLAT("In the 3D mode, lowering this value may help if the game lags while exploring new areas.")
1289 );
1290 });
1291 }
1292 }
1293
solhelp()1294 string solhelp() {
1295 #if CAP_SOLV
1296 return XLAT(
1297 "Solv (aka Sol) is a 3D space where directions work in different ways. It is described by the following metric:\n"
1298 "ds² = (eᶻdx)² + (e⁻ᶻdy)² + dz²\n\n"
1299 "You are currently displaying Solv in the perspective projection based on native geodesics. You can control how "
1300 "the fog effects depends on the geodesic distance, and how far object in X/Y/Z coordinates are rendered."
1301 );
1302 #else
1303 return "";
1304 #endif
1305 }
1306
menuitem_sightrange_bonus(char c)1307 EX void menuitem_sightrange_bonus(char c) {
1308 dialog::addSelItem(XLAT("sight range bonus"), its(sightrange_bonus), c);
1309 dialog::add_action([]{
1310 dialog::editNumber(sightrange_bonus, -5, allowIncreasedSight() ? 3 : 0, 1, 0, XLAT("sight range"),
1311 XLAT("Roughly 42% cells are on the edge of your sight range. Reducing "
1312 "the sight range makes HyperRogue work faster, but also makes "
1313 "the game effectively harder."));
1314 dialog::reaction = doOvergenerate;
1315 dialog::bound_low(1-getDistLimit());
1316 dialog::bound_up(allowIncreasedSight() ? euclid ? 99 : gp::dist_2() * 5 : 0);
1317 });
1318 }
1319
edit_sightrange()1320 EX void edit_sightrange() {
1321 #if CAP_RUG
1322 USING_NATIVE_GEOMETRY_IN_RUG;
1323 #endif
1324 gamescreen(0);
1325 dialog::init("sight range settings");
1326 add_edit(vid.use_smart_range);
1327 if(vid.use_smart_range)
1328 add_edit(WDIM == 2 ? vid.smart_range_detail : vid.smart_range_detail_3);
1329 else {
1330 if(WDIM == 2) {
1331 add_edit(sightrange_bonus);
1332 if(GDIM == 3) {
1333 dialog::addSelItem(XLAT("3D sight range for the fog effect"), fts(sightranges[geometry]), 'r');
1334 dialog::add_action([] {
1335 dialog::editNumber(sightranges[geometry], 0, 2 * M_PI, 0.5, M_PI, XLAT("fog effect"), "");
1336 });
1337 }
1338 }
1339 if(WDIM == 3) {
1340 dialog::addSelItem(XLAT("3D sight range"), fts(sightranges[geometry]), 'r');
1341 dialog::add_action([] {
1342 dialog::editNumber(sightranges[geometry], 0, 2 * M_PI, 0.5, M_PI, XLAT("3D sight range"),
1343 XLAT(
1344 "Sight range for 3D geometries is specified in the absolute units. This value also affects the fog effect.\n\n"
1345 "In spherical geometries, the sight range of 2π will let you see things behind you as if they were in front of you, "
1346 "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"
1347 "In hyperbolic geometries, the number of cells to render depends exponentially on the sight range. More cells to drawn "
1348 "reduces the performance.\n\n"
1349 "Sight range affects the gameplay, and monsters act iff they are visible. Monster generation takes this into account."
1350 )
1351 );
1352 });
1353 }
1354 }
1355 #if CAP_SOLV
1356 if(pmodel == mdGeodesic && sol) {
1357 dialog::addSelItem(XLAT("max difference in X/Y coordinates"), fts(sn::solrange_xy), 'x');
1358 dialog::add_action([] {
1359 dialog::editNumber(sn::solrange_xy, 0.01, 200, 0.1, 50, XLAT("max difference in X/Y coordinates"), solhelp()), dialog::scaleLog();
1360 });
1361 dialog::addSelItem(XLAT("max difference in Z coordinate"), fts(sn::solrange_z), 'z');
1362 dialog::add_action([] {
1363 dialog::editNumber(sn::solrange_z, 0, 20, 0.1, 6, XLAT("max difference in Z coordinates"), solhelp());
1364 });
1365 }
1366 #endif
1367 if(pmodel == mdGeodesic && sl2) {
1368 dialog::addSelItem(XLAT("max difference in X/Y coordinates"), fts(slr::range_xy), 'x');
1369 dialog::add_action([] {
1370 dialog::editNumber(slr::range_xy, 0, 10, 0.5, 4, XLAT("max difference in X/Y coordinates"), "");
1371 });
1372 dialog::addSelItem(XLAT("steps"), its(slr::steps), 'z');
1373 dialog::add_action([] {
1374 dialog::editNumber(slr::steps, 0, 50, 1, 10, "", "");
1375 });
1376 }
1377 if(vid.use_smart_range && WDIM == 2) {
1378 dialog::addBoolItem_action(XLAT("area-based range"), vid.smart_area_based, 'a');
1379 }
1380 if(vid.use_smart_range == 0 && allowChangeRange() && WDIM == 2) {
1381 dialog::addSelItem(XLAT("generation range bonus"), its(genrange_bonus), 'o');
1382 dialog::add_action([] () { genrange_bonus = sightrange_bonus; doOvergenerate(); });
1383 dialog::addSelItem(XLAT("game range bonus"), its(gamerange_bonus), 's');
1384 dialog::add_action([] () { gamerange_bonus = sightrange_bonus; doOvergenerate(); });
1385 }
1386 if(WDIM == 3 && !vid.use_smart_range) {
1387 dialog::addBoolItem_action(XLAT("sloppy range checking"), vid.sloppy_3d, 's');
1388 }
1389 if(GDIM == 3 && !vid.use_smart_range) {
1390 dialog::addSelItem(XLAT("limit generation"), fts(extra_generation_distance), 'e');
1391 dialog::add_action([] {
1392 dialog::editNumber(extra_generation_distance, 0, 999, 0.5, 999, XLAT("limit generation"),
1393 "Cells over this distance will not be generated, but they will be drawn if they are already generated and in the sight range."
1394 );
1395 });
1396 }
1397 add_cells_drawn('c');
1398 dialog::display();
1399 }
1400
1401 EX void menuitem_sightrange_style(char c IS('c')) {
1402 dialog::addSelItem(XLAT("draw range based on"),
1403 vid.use_smart_range == 0 ? XLAT("distance") :
1404 vid.use_smart_range == 1 ? XLAT("size (no gen)") :
1405 XLAT("size"),
1406 c
1407 );
__anond343dad31f02null1408 dialog::add_action_push([] {
1409 dialog::init(XLAT("draw range based on"));
1410 dialog::addBoolItem(XLAT("draw range based on distance"), vid.use_smart_range == 0, 'd');
1411 dialog::add_action([] () { vid.use_smart_range = 0; popScreen(); edit_sightrange(); });
1412 if(WDIM == 2 && allowIncreasedSight()) {
1413 dialog::addBoolItem(XLAT("draw based on size in the projection (no generation)"), vid.use_smart_range == 1, 'n');
1414 dialog::add_action([] () { vid.use_smart_range = 1; popScreen(); edit_sightrange(); });
1415 }
1416 if(allowChangeRange() && allowIncreasedSight()) {
1417 dialog::addBoolItem(XLAT("draw based on size in the projection (generation)"), vid.use_smart_range == 2, 'g');
1418 dialog::add_action([] () { vid.use_smart_range = 2; popScreen(); edit_sightrange(); });
1419 }
1420 if(!allowChangeRange() || !allowIncreasedSight()) {
1421 dialog::addItem(XLAT("enable the cheat mode for additional options"), 'X');
1422 dialog::add_action(enable_cheat);
1423 }
1424 dialog::display();
1425 });
1426 }
1427
1428 EX void menuitem_sightrange(char c IS('c')) {
1429 #if CAP_SOLV
1430 if(pmodel == mdGeodesic && sol)
1431 dialog::addSelItem(XLAT("sight range settings"), fts(sn::solrange_xy) + "x" + fts(sn::solrange_z), c);
1432 else
1433 #endif
1434 if(vid.use_smart_range)
1435 dialog::addSelItem(XLAT("sight range settings"), fts(WDIM == 3 ? vid.smart_range_detail_3 : vid.smart_range_detail) + " px", c);
1436 else if(WDIM == 3)
1437 dialog::addSelItem(XLAT("sight range settings"), fts(sightranges[geometry]) + "au", c);
1438 else
1439 dialog::addSelItem(XLAT("sight range settings"), format("%+d", sightrange_bonus), c);
1440 dialog::add_action_push(edit_sightrange);
1441 }
1442
sets_sfx_volume()1443 EX void sets_sfx_volume() {
1444 #if CAP_AUDIO
1445 dialog::numberdark = dialog::DONT_SHOW;
1446 #if ISANDROID
1447 dialog::reaction = [] () {
1448 settingsChanged = true;
1449 };
1450 #endif
1451 dialog::bound_low(0);
1452 dialog::bound_up(MIX_MAX_VOLUME);
1453 #endif
1454 }
1455
sets_music_volume()1456 EX void sets_music_volume() {
1457 #if CAP_AUDIO
1458 dialog::numberdark = dialog::DONT_SHOW;
1459 dialog::reaction = [] () {
1460 #if CAP_SDLAUDIO
1461 Mix_VolumeMusic(musicvolume);
1462 #endif
1463 #if ISANDROID
1464 settingsChanged = true;
1465 #endif
1466 };
1467 dialog::bound_low(0);
1468 dialog::bound_up(MIX_MAX_VOLUME);
1469 #if CAP_SDLAUDIO
1470 dialog::extra_options = [] {
1471 dialog::addBoolItem_action(XLAT("play music when out of focus"), music_out_of_focus, 'A');
1472 };
1473 #endif
1474 #endif
1475 }
1476
showSpecialEffects()1477 EX void showSpecialEffects() {
1478 cmode = vid.xres > vid.yres * 1.4 ? sm::SIDE : sm::MAYDARK;
1479 gamescreen(0);
1480 dialog::init(XLAT("extra graphical effects"));
1481
1482 dialog::addBoolItem_action(XLAT("particles on attack"), (vid.particles), 'p');
1483 dialog::addBoolItem_action(XLAT("floating bubbles: special"), vid.bubbles_special, 's');
1484 dialog::addBoolItem_action(XLAT("floating bubbles: treasure thresholds"), vid.bubbles_threshold, 't');
1485 dialog::addBoolItem_action(XLAT("floating bubbles: all treasures"), vid.bubbles_all, 'a');
1486 dialog::addBoolItem_action(XLAT("background particle effects"), (vid.backeffects), 'b');
1487
1488 dialog::addBreak(50);
1489 dialog::addBack();
1490 dialog::display();
1491 }
1492
show_vector_settings()1493 EX void show_vector_settings() {
1494 cmode = vid.xres > vid.yres * 1.4 ? sm::SIDE : sm::MAYDARK;
1495 gamescreen(0);
1496 dialog::init(XLAT("vector settings"));
1497
1498 dialog::addSelItem(XLAT("line width"), fts(vid.linewidth), 'w');
1499 dialog::add_action([] {
1500 dialog::editNumber(vid.linewidth, 0, 10, 0.1, 1, XLAT("line width"),
1501 vid.usingGL ? "" : XLAT("Line width setting is only taken into account in OpenGL."));
1502 });
1503
1504 dialog::addSelItem(XLAT("line quality"), its(vid.linequality), 'l');
1505 dialog::add_action([] {
1506 dialog::editNumber(vid.linequality, -3, 5, 1, 1, XLAT("line quality"),
1507 XLAT("Higher numbers make the curved lines smoother, but reduce the performance."));
1508 });
1509
1510 dialog::addBoolItem("perfect width", perfect_linewidth == 2, 'p');
1511 if(perfect_linewidth == 1)
1512 dialog::lastItem().value = XLAT("shots only");
1513 dialog::add_action([] { perfect_linewidth = (1 + perfect_linewidth) % 3; });
1514
1515 dialog::addBoolItem_action("finer lines at the boundary", vid.fineline, 'O');
1516
1517 if(vid.fineline) {
1518 dialog::addSelItem("variable width", fts(precise_width), 'm');
1519 dialog::add_action([] () {
1520 dialog::editNumber(precise_width, 0, 2, 0.1, 0.5,
1521 XLAT("variable width"), XLAT("lines longer than this value will be split into shorter lines, with width computed separately for each of them.")
1522 );
1523 });
1524 }
1525 else dialog::addBreak(100);
1526
1527 add_edit(neon_mode);
1528 dialog::addBreak(100);
1529 dialog::addInfo(XLAT("hint: press Alt while testing modes"));
1530 dialog::addBreak(100);
1531 dialog::addBoolItem_action(XLAT("disable shadows"), noshadow, 'f');
1532 dialog::addBoolItem_action(XLAT("bright mode"), bright, 'g');
1533 dialog::addBoolItem_action(XLAT("colorblind simulation"), cblind, 'h');
1534
1535 dialog::addBoolItem_action(XLAT("no fill in neon mode"), neon_nofill, 'n');
1536
1537 dialog::addBreak(50);
1538 dialog::addBack();
1539 dialog::display();
1540 }
1541
showGraphConfig()1542 EX void showGraphConfig() {
1543 cmode = vid.xres > vid.yres * 1.4 ? sm::SIDE : sm::MAYDARK;
1544 gamescreen(0);
1545
1546 dialog::init(XLAT("graphics configuration"));
1547
1548 #if !ISIOS && !ISWEB
1549 add_edit(vid.want_fullscreen);
1550
1551 #if !ISANDROID && !ISFAKEMOBILE
1552 if(vid.want_fullscreen) {
1553 add_edit(vid.change_fullscr);
1554 if(vid.change_fullscr)
1555 add_edit(vid.fullscreen_x), add_edit(vid.fullscreen_y);
1556 else
1557 dialog::addBreak(200);
1558 }
1559 else {
1560 add_edit(vid.relative_window_size);
1561 if(vid.relative_window_size)
1562 add_edit(vid.window_rel_x), add_edit(vid.window_rel_y);
1563 else
1564 add_edit(vid.window_x), add_edit(vid.window_y);
1565 }
1566 #endif
1567 #endif
1568
1569 #if CAP_GLORNOT
1570 add_edit(vid.wantGL);
1571 #endif
1572
1573 #if !ISIOS && !ISANDROID && !ISFAKEMOBILE
1574 if(!vid.usingGL) {
1575 dialog::addBoolItem(XLAT("anti-aliasing"), vid.want_antialias & AA_NOGL, 'O');
1576 dialog::add_action([] {
1577 if(!vid.usingGL)
1578 vid.want_antialias ^= AA_NOGL | AA_FONT;
1579 });
1580 }
1581 else {
1582 dialog::addSelItem(XLAT("anti-aliasing"),
1583 (vid.want_antialias & AA_POLY) ? "polygons" :
1584 (vid.want_antialias & AA_LINES) ? "lines" :
1585 (vid.want_antialias & AA_MULTI) ? "multisampling" :
1586 "NO", 'O');
1587 dialog::add_action([] {
1588 if(vid.want_antialias & AA_MULTI)
1589 vid.want_antialias ^= AA_MULTI;
1590 else if(vid.want_antialias & AA_POLY)
1591 vid.want_antialias ^= AA_POLY | AA_LINES | AA_MULTI;
1592 else if(vid.want_antialias & AA_LINES)
1593 vid.want_antialias |= AA_POLY;
1594 else
1595 vid.want_antialias |= AA_LINES;
1596 });
1597 }
1598 #endif
1599
1600 #if !ISIOS && !ISANDROID && !ISFAKEMOBILE
1601 if(vid.usingGL) {
1602 if(vrhr::active())
1603 dialog::addInfo(XLAT("(vsync disabled in VR)"));
1604 else
1605 add_edit(vid.want_vsync);
1606 }
1607 else
1608 dialog::addBreak(100);
1609 #endif
1610
1611 if(need_to_apply_screen_settings()) {
1612 dialog::addItem(XLAT("apply changes"), 'A');
1613 dialog::add_action(apply_screen_settings);
1614 dialog::addBreak(100);
1615 }
1616 else
1617 dialog::addBreak(200);
1618
1619 add_edit(vid.relative_font);
1620 if(vid.relative_font)
1621 add_edit(vid.fontscale);
1622 else
1623 add_edit(vid.abs_fsize);
1624
1625 dialog::addSelItem(XLAT("vector settings"), XLAT("width") + " " + fts(vid.linewidth), 'w');
1626 dialog::add_action_push(show_vector_settings);
1627
1628 #if CAP_FRAMELIMIT
1629 dialog::addSelItem(XLAT("framerate limit"), its(vid.framelimit), 'l');
1630 if(getcstat == 'l')
1631 mouseovers = XLAT("Reduce the framerate limit to conserve CPU energy");
1632 #endif
1633
1634 dialog::addSelItem(XLAT("scrolling speed"), fts(vid.sspeed), 'a');
1635
1636 dialog::addSelItem(XLAT("camera movement speed"), fts(camera_speed), 'c');
1637 dialog::add_action([] {
1638 dialog::editNumber(camera_speed, -10, 10, 0.1, 1, XLAT("camera movement speed"),
1639 "This affects:\n\nin 2D: scrolling with arrow keys and Wheel Up\n\nin 3D: camera movement with Home/End."
1640 );
1641 });
1642 dialog::addSelItem(XLAT("camera rotation speed"), fts(camera_rot_speed), 'r');
1643 dialog::add_action([] {
1644 dialog::editNumber(camera_rot_speed, -10, 10, 0.1, 1, XLAT("camera rotation speed"),
1645 "This affects view rotation with Page Up/Down, and in 3D, camera rotation with arrow keys or mouse."
1646 );
1647 });
1648
1649 dialog::addSelItem(XLAT("movement animation speed"), fts(vid.mspeed), 'm');
1650
1651 dialog::addItem(XLAT("extra graphical effects"), 'u');
1652
1653 dialog::addBreak(50);
1654 dialog::addBack();
1655 dialog::display();
1656
1657 keyhandler = [] (int sym, int uni) {
1658 dialog::handleNavigation(sym, uni);
1659
1660 char xuni = uni | 96;
1661
1662 if((uni >= 32 && uni < 64) || uni == 'L' || uni == 'C') xuni = uni;
1663
1664 if(xuni == 'u') pushScreen(showSpecialEffects);
1665
1666 else if(xuni == 'a') dialog::editNumber(vid.sspeed, -5, 5, 1, 0,
1667 XLAT("scrolling speed"),
1668 XLAT("+5 = center instantly, -5 = do not center the map")
1669 + "\n\n" +
1670 XLAT("press Space or Home to center on the PC"));
1671
1672 else if(xuni == 'm') dialog::editNumber(vid.mspeed, -5, 5, 1, 0,
1673 XLAT("movement animation speed"),
1674 XLAT("+5 = move instantly"));
1675
1676 #if CAP_FRAMELIMIT
1677 else if(xuni == 'l') {
1678 dialog::editNumber(vid.framelimit, 5, 300, 10, 300, XLAT("framerate limit"), "");
1679 dialog::bound_low(5);
1680 }
1681 #endif
1682
1683 else if(xuni =='p')
1684 vid.backeffects = !vid.backeffects;
1685
1686 else if(doexiton(sym, uni)) popScreen();
1687 };
1688 }
1689
edit_whatever(char type,int index)1690 EX void edit_whatever(char type, int index) {
1691 if(type == 'f') {
1692 dialog::editNumber(whatever[index], -10, 10, 1, 0, XLAT("whatever"),
1693 "f:" + its(index));
1694 }
1695 else {
1696 dialog::editNumber(whateveri[index], -10, 10, 1, 0, XLAT("whatever"),
1697 "i:" + its(index));
1698 }
1699 dialog::extra_options = [type, index] {
1700 dialog::addItem(XLAT("integer"), 'X');
1701 dialog::add_action( [index] { popScreen(); edit_whatever('i', index); });
1702 dialog::addItem(XLAT("float"), 'Y');
1703 dialog::add_action( [index] { popScreen(); edit_whatever('f', index); });
1704 for(int x=0; x<8; x++) {
1705 dialog::addSelItem(its(x), type == 'i' ? its(whateveri[x]) : fts(whatever[x]), 'A' + x);
1706 dialog::add_action([type,x] { popScreen(); edit_whatever(type, x); });
1707 }
1708 };
1709 }
1710
configureOther()1711 EX void configureOther() {
1712 gamescreen(3);
1713
1714 dialog::init(XLAT("other settings"));
1715
1716 #if ISSTEAM
1717 dialog::addBoolItem(XLAT("send scores to Steam leaderboards"), (vid.steamscore&1), 'x');
1718 dialog::add_action([] {vid.steamscore = vid.steamscore^1; });
1719 #endif
1720
1721 dialog::addBoolItem_action(XLAT("skip the start menu"), vid.skipstart, 'm');
1722
1723 dialog::addItem(XLAT("memory configuration"), 'y');
1724 dialog::add_action_push(show_memory_menu);
1725
1726 // dialog::addBoolItem_action(XLAT("forget faraway cells"), memory_saving_mode, 'y');
1727
1728 #if CAP_AUDIO
1729 add_edit(musicvolume);
1730 add_edit(effvolume);
1731 #endif
1732
1733 menuitem_sightrange('r');
1734
1735 add_edit(vid.faraway_highlight);
1736 add_edit(vid.faraway_highlight_color);
1737
1738 #ifdef WHATEVER
1739 dialog::addSelItem(XLAT("whatever"), fts(whatever[0]), 'j');
1740 dialog::add_action([] { edit_whatever('f', 0); });
1741 #endif
1742
1743 dialog::addBreak(50);
1744 dialog::addBack();
1745
1746 dialog::display();
1747 }
1748
configureInterface()1749 EX void configureInterface() {
1750 gamescreen(3);
1751 dialog::init(XLAT("interface"));
1752
1753 #if CAP_TRANS
1754 dialog::addSelItem(XLAT("language"), XLAT("EN"), 'l');
1755 dialog::add_action_push(selectLanguageScreen);
1756 #endif
1757
1758 dialog::addSelItem(XLAT("player character"), numplayers() > 1 ? "" : csname(vid.cs), 'g');
1759 dialog::add_action_push(showCustomizeChar);
1760 if(getcstat == 'g') mouseovers = XLAT("Affects looks and grammar");
1761
1762 dialog::addSelItem(XLAT("message flash time"), its(vid.flashtime), 't');
1763 dialog::add_action([] {
1764 dialog::editNumber(vid.flashtime, 0, 64, 1, 8, XLAT("message flash time"),
1765 XLAT("How long should the messages stay on the screen."));
1766 dialog::bound_low(0);
1767 });
1768
1769 dialog::addSelItem(XLAT("limit messages shown"), its(vid.msglimit), 'z');
1770 dialog::add_action([] {
1771 dialog::editNumber(vid.msglimit, 0, 64, 1, 5, XLAT("limit messages shown"),
1772 XLAT("Maximum number of messages on screen."));
1773 dialog::bound_low(0);
1774 });
1775
1776 add_edit(vid.msgleft);
1777
1778 add_edit(glyphsortorder);
1779 add_edit(vid.graphglyph);
1780
1781 add_edit(display_yasc_codes);
1782
1783 dialog::addSelItem(XLAT("draw crosshair"), crosshair_size > 0 ? fts(crosshair_size) : ONOFF(false), 'x');
1784 dialog::add_action([] () {
1785 dialog::editNumber(crosshair_size, 0, 100, 1, 10, XLAT("crosshair size"), XLAT(
1786 "Display a targetting reticle in the center of the screen. Might be useful when exploring 3D modes, "
1787 "as it precisely shows the direction we are going. However, the option is available in all modes."
1788 ));
1789 dialog::bound_low(0);
1790 dialog::extra_options = [] {
1791 dialog::addColorItem(XLAT("crosshair color"), crosshair_color, 'X');
1792 dialog::add_action([] { dialog::openColorDialog(crosshair_color); });
1793 };
1794 });
1795
1796 dialog::addBreak(50);
1797 dialog::addBack();
1798
1799 dialog::display();
1800 }
1801
1802 #if CAP_SDLJOY
showJoyConfig()1803 EX void showJoyConfig() {
1804 gamescreen(4);
1805
1806 dialog::init(XLAT("joystick configuration"));
1807
1808 dialog::addSelItem(XLAT("first joystick position (movement)"), its(joyx)+","+its(joyy), 0);
1809 dialog::addSelItem(XLAT("second joystick position (panning)"), its(panjoyx)+","+its(panjoyy), 0);
1810
1811 dialog::addSelItem(XLAT("joystick mode"), autojoy ? XLAT("automatic") : XLAT("manual"), 'p');
1812 if(getcstat == 'p') {
1813 if(autojoy)
1814 mouseovers = XLAT("joystick mode: automatic (release the joystick to move)");
1815 if(!autojoy)
1816 mouseovers = XLAT("joystick mode: manual (press a button to move)");
1817 }
1818
1819 dialog::addSelItem(XLAT("first joystick: movement threshold"), its(vid.joyvalue), 'a');
1820 dialog::addSelItem(XLAT("first joystick: execute movement threshold"), its(vid.joyvalue2), 'b');
1821 dialog::addSelItem(XLAT("second joystick: pan threshold"), its(vid.joypanthreshold), 'c');
1822 dialog::addSelItem(XLAT("second joystick: panning speed"), fts(vid.joypanspeed * 1000), 'd');
1823 dialog::addSelItem(XLAT("smoothen"), its(vid.joysmooth) + " ms", 'e');
1824
1825 dialog::addBreak(50);
1826 dialog::addBack();
1827 dialog::display();
1828
1829 keyhandler = [] (int sym, int uni) {
1830 dialog::handleNavigation(sym, uni);
1831 if(uni == 'p') autojoy = !autojoy;
1832 else if(uni == 'a') {
1833 dialog::editNumber(vid.joyvalue, 0, 32768, 100, 4800, XLAT("first joystick: movement threshold"), "");
1834 dialog::bound_low(0);
1835 }
1836 else if(uni == 'b') {
1837 dialog::editNumber(vid.joyvalue2, 0, 32768, 100, 5600, XLAT("first joystick: execute movement threshold"), "");
1838 dialog::bound_low(0);
1839 }
1840 else if(uni == 'c') {
1841 dialog::editNumber(vid.joypanthreshold, 0, 32768, 100, 2500, XLAT("second joystick: pan threshold"), "");
1842 dialog::bound_low(0);
1843 }
1844 else if(uni == 'd')
1845 dialog::editNumber(vid.joypanspeed, 0, 1e-2, 1e-5, 1e-4, XLAT("second joystick: panning speed"), "");
1846 else if(uni == 'e')
1847 dialog::editNumber(vid.joypanspeed, 0, 2000, 20, 200, XLAT("smoothen"), "large values help if the joystick is imprecise");
1848
1849 else if(doexiton(sym, uni)) popScreen();
1850 };
1851 }
1852 #endif
1853
projectionDialog()1854 EX void projectionDialog() {
1855 vid.tc_alpha = ticks;
1856 dialog::editNumber(vpconf.alpha, -5, 5, .1, 1,
1857 XLAT("projection distance"),
1858 XLAT("HyperRogue uses the Minkowski hyperboloid model internally. "
1859 "Klein and Poincaré models can be obtained by perspective, "
1860 "and the Gans model is obtained by orthogonal projection. "
1861 "See also the conformal mode (in the special modes menu) "
1862 "for more models."));
1863 dialog::extra_options = [] () {
1864 dialog::addBreak(100);
1865 if(GDIM == 2) dialog::addHelp(XLAT(
1866 "If we are viewing an equidistant g absolute units below a plane, "
1867 "from a point c absolute units above the plane, this corresponds "
1868 "to viewing a Minkowski hyperboloid from a point "
1869 "tanh(g)/tanh(c) units below the center. This in turn corresponds to "
1870 "the Poincaré model for g=c, and Klein-Beltrami model for g=0."));
1871 dialog::addSelItem(sphere ? "stereographic" : "Poincaré model", "1", 'P');
1872 dialog::add_action([] () { *dialog::ne.editwhat = 1; vpconf.scale = 1; dialog::ne.s = "1"; });
1873 dialog::addSelItem(sphere ? "gnomonic" : "Klein model", "0", 'K');
1874 dialog::add_action([] () { *dialog::ne.editwhat = 0; vpconf.scale = 1; dialog::ne.s = "0"; });
1875 if(hyperbolic) {
1876 dialog::addSelItem("inverted Poincaré model", "-1", 'I');
1877 dialog::add_action([] () { *dialog::ne.editwhat = -1; vpconf.scale = 1; dialog::ne.s = "-1"; });
1878 }
1879 dialog::addItem(sphere ? "orthographic" : "Gans model", 'O');
1880 dialog::add_action([] () { vpconf.alpha = vpconf.scale = 999; dialog::ne.s = dialog::disp(vpconf.alpha); });
1881 dialog::addItem(sphere ? "towards orthographic" : "towards Gans model", 'T');
1882 dialog::add_action([] () { double d = 1.1; vpconf.alpha *= d; vpconf.scale *= d; dialog::ne.s = dialog::disp(vpconf.alpha); });
1883 };
1884 }
1885
menuitem_projection_distance(char key)1886 EX void menuitem_projection_distance(char key) {
1887 dialog::addSelItem(XLAT("projection distance"), fts(vpconf.alpha) + " (" + current_proj_name() + ")", key);
1888 dialog::add_action(projectionDialog);
1889 }
1890
explain_detail()1891 EX void explain_detail() {
1892 dialog::addHelp(XLAT(
1893 "Objects at distance less than %1 absolute units "
1894 "from the center will be displayed with high "
1895 "detail, and at distance at least %2 with low detail.",
1896 fts(vid.highdetail), fts(vid.middetail)
1897 ));
1898 }
1899
max_fov_angle()1900 EX ld max_fov_angle() {
1901 auto& p = panini_alpha ? panini_alpha : stereo_alpha;
1902 if(p >= 1 || p <= -1) return 360;
1903 return acos(-p) * 2 / degree;
1904 }
1905
1906 EX void add_edit_fov(char key IS('f'), bool pop IS(false)) {
1907
1908 string sfov = fts(vid.fov) + "°";
1909 if(panini_alpha || stereo_alpha) {
1910 sfov += " / " + fts(max_fov_angle()) + "°";
1911 }
1912 dialog::addSelItem(XLAT("field of view"), sfov, key);
__anond343dad34102null1913 dialog::add_action([=] {
1914 if(pop) popScreen();
1915 dialog::editNumber(vid.fov, 1, max_fov_angle(), 1, 90, "field of view",
1916 XLAT(
1917 "Horizontal field of view, in angles. "
1918 "This affects the Hypersian Rug mode (even when stereo is OFF) "
1919 "and non-disk models.") + "\n\n" +
1920 XLAT(
1921 "Must be less than %1°. Panini projection can be used to get higher values.",
1922 fts(max_fov_angle())
1923 )
1924 );
1925 dialog::bound_low(1e-8);
1926 dialog::bound_up(max_fov_angle() - 0.01);
1927 string quick =
1928 XLAT(
1929 "HyperRogue uses "
1930 "a quick implementation, so parameter values too close to 1 may "
1931 "be buggy (outside of raycasting); try e.g. 0.9 instead."
1932 );
1933 dialog::extra_options = [quick] {
1934 dialog::addSelItem(XLAT("Panini projection"), fts(panini_alpha), 'P');
1935 dialog::add_action([quick] {
1936 popScreen();
1937 dialog::editNumber(panini_alpha, 0, 1, 0.1, 0, "Panini parameter",
1938 XLAT(
1939 "The Panini projection is an alternative perspective projection "
1940 "which allows very wide field-of-view values.\n\n") + quick
1941 );
1942 #if CAP_GL
1943 dialog::reaction = reset_all_shaders;
1944 #endif
1945 dialog::extra_options = [] {
1946 add_edit_fov('F', true);
1947 };
1948 });
1949 dialog::addSelItem(XLAT("spherical perspective projection"), fts(stereo_alpha), 'S');
1950 dialog::add_action([quick] {
1951 popScreen();
1952 dialog::editNumber(stereo_alpha, 0, 1, 0.1, 0, "spherical perspective parameter",
1953 XLAT(
1954 "Set to 1 to get stereographic projection, "
1955 "which allows very wide field-of-view values.\n\n") + quick
1956 );
1957 #if CAP_GL
1958 dialog::reaction = reset_all_shaders;
1959 #endif
1960 dialog::extra_options = [] {
1961 add_edit_fov('F', true);
1962 };
1963 });
1964 };
1965 });
1966 }
1967
supported_ods()1968 bool supported_ods() {
1969 if(!CAP_ODS) return false;
1970 return rug::rugged || (hyperbolic && GDIM == 3);
1971 }
1972
showStereo()1973 EX void showStereo() {
1974 cmode = sm::SIDE | sm::MAYDARK;
1975 gamescreen(0);
1976 dialog::init(XLAT("stereo vision config"));
1977
1978 add_edit(vid.stereo_mode);
1979
1980 dialog::addSelItem(XLAT("pupillary distance"), fts(vid.ipd), 'e');
1981
1982 switch(vid.stereo_mode) {
1983 case sAnaglyph:
1984 dialog::addSelItem(XLAT("distance between images"), fts(vid.anaglyph_eyewidth), 'd');
1985 break;
1986 case sLR:
1987 dialog::addSelItem(XLAT("distance between images"), fts(vid.lr_eyewidth), 'd');
1988 break;
1989 default:
1990 dialog::addBreak(100);
1991 break;
1992 }
1993
1994 dialog::addSelItem(XLAT("desaturate colors"), its(vid.desaturate)+"%", 'c');
1995 dialog::add_action([] {
1996 dialog::editNumber(vid.desaturate, 0, 100, 10, 0, XLAT("desaturate colors"),
1997 XLAT("Make the game colors less saturated. This is useful in the anaglyph mode.")
1998 );
1999 });
2000
2001 add_edit_fov('f');
2002
2003 dialog::addBack();
2004 dialog::display();
2005
2006 keyhandler = [] (int sym, int uni) {
2007 dialog::handleNavigation(sym, uni);
2008
2009 string help3 = XLAT(
2010 "This allows you to view the world of HyperRogue in three dimensions. "
2011 "Best used with the Hypersian Rug mode. When used in the disk model, "
2012 "this lets you look at the Minkowski hyperboloid (which means the "
2013 "depth of terrain features is actually reversed). It also works with non-disk models, "
2014 "from the conformal menu."
2015 ) + " " + XLAT(
2016 "Currently, red-cyan anaglyph glasses and mobile VR googles are supported."
2017 ) + "\n\n";
2018
2019 if(uni == 'm') {
2020 vid.stereo_mode = eStereo((1 + vid.stereo_mode) % 4);
2021 if(vid.stereo_mode == sODS && !supported_ods()) vid.stereo_mode = sOFF;
2022 }
2023
2024 else if(uni == 'e')
2025 dialog::editNumber(vid.ipd, -10, 10, 0.01, 0, XLAT("pupillary distance"),
2026 help3 +
2027 XLAT("The distance between your eyes in the represented 3D object. This is given in absolute units.")
2028 ), dialog::scaleSinh100();
2029
2030 else if(uni == 'd' && vid.stereo_mode == sAnaglyph)
2031 dialog::editNumber(vid.anaglyph_eyewidth, -1, 1, 0.01, 0, XLAT("distance between images"),
2032 help3 +
2033 XLAT("The distance between your eyes. 1 is the width of the screen."));
2034
2035 else if(uni == 'd' && vid.stereo_mode == sLR)
2036 dialog::editNumber(vid.lr_eyewidth, -1, 1, 0.01, 0, XLAT("distance between images"),
2037 help3 +
2038 XLAT("The distance between your eyes. 1 is the width of the screen."));
2039
2040 else if(doexiton(sym, uni)) popScreen();
2041 };
2042 }
2043
add_edit_wall_quality(char c)2044 EX void add_edit_wall_quality(char c) {
2045 dialog::addSelItem(XLAT("wall quality"), its(vid.texture_step), c);
2046 dialog::add_action([] {
2047 dialog::editNumber(vid.texture_step, 1, 16, 1, 1, XLAT("wall quality"),
2048 XLAT(
2049 "Controls the number of triangles used for wall surfaces. "
2050 "Higher numbers reduce the performance. "
2051 "This has a strong effect when the walls are curved indeed "
2052 "(floors in 2D geometries, honeycombs based on horospheres, and projections other than native perspective), "
2053 "but otherwise, usually it can be set to 1 without significant adverse effects other "
2054 "than slightly incorrect texturing."
2055 )
2056 );
2057 dialog::bound_low(1);
2058 dialog::bound_up(128);
2059 dialog::reaction = [] {
2060 #if MAXMDIM >= 4
2061 if(floor_textures) {
2062 delete floor_textures;
2063 floor_textures = NULL;
2064 }
2065 #endif
2066 };
2067 });
2068 }
2069
edit_levellines(char c)2070 EX void edit_levellines(char c) {
2071 if(levellines)
2072 dialog::addSelItem(XLAT("level lines"), fts(levellines), c);
2073 else
2074 dialog::addBoolItem(XLAT("level lines"), false, c);
2075 dialog::add_action([] {
2076 dialog::editNumber(levellines, 0, 100, 0.5, 0, XLAT("level lines"),
2077 XLAT(
2078 "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. "
2079 "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.")
2080 );
2081 dialog::reaction = ray::reset_raycaster;
2082 dialog::extra_options = [] {
2083 dialog::addBoolItem(XLAT("disable textures"), disable_texture, 'T');
2084 dialog::add_action([] { ray::reset_raycaster(); disable_texture = !disable_texture; });
2085 dialog::addItem(XLAT("disable level lines"), 'D');
2086 dialog::add_action([] { ray::reset_raycaster(); levellines = 0; popScreen(); });
2087 };
2088 dialog::bound_low(0);
2089 });
2090 }
2091
show3D()2092 EX void show3D() {
2093 cmode = sm::SIDE | sm::MAYDARK;
2094 gamescreen(0);
2095 dialog::init(XLAT("3D configuration"));
2096
2097 if(GDIM == 2) {
2098 dialog::addBoolItem(XLAT("configure TPP automatically"), pmodel == mdDisk && pconf.camera_angle, 'T');
2099 dialog::add_action(geom3::switch_tpp);
2100 }
2101
2102 #if MAXMDIM >=4
2103 if(WDIM == 2) {
2104 dialog::addBoolItem(XLAT("configure FPP automatically"), GDIM == 3, 'F');
2105 dialog::add_action(geom3::switch_fpp);
2106 }
2107 #endif
2108 dialog::addBreak(50);
2109
2110 #if MAXMDIM >= 4
2111 if(WDIM == 2) {
2112 dialog::addBoolItem(XLAT("use the full 3D models"), vid.always3, 'U');
2113 dialog::add_action(geom3::switch_always3);
2114 }
2115 #endif
2116 if(vid.use_smart_range == 0 && GDIM == 2) {
2117 add_edit(vid.highdetail);
2118 add_edit(vid.middetail);
2119 dialog::addBreak(50);
2120 }
2121
2122 if(WDIM == 2) {
2123 add_edit(vid.camera);
2124 if(GDIM == 3)
2125 add_edit(vid.eye);
2126
2127 add_edit(vid.depth);
2128
2129 if(GDIM == 2) {
2130 dialog::addSelItem(XLAT("Projection at the ground level"), fts(pconf.alpha), 'p');
2131 dialog::add_action(projectionDialog);
2132 }
2133 else if(!in_perspective())
2134 dialog::addSelItem(XLAT("projection distance"), fts(pconf.alpha), 'p');
2135
2136 dialog::addBreak(50);
2137 add_edit(vid.wall_height);
2138
2139 add_edit(vid.rock_wall_ratio);
2140 add_edit(vid.human_wall_ratio);
2141 add_edit(vid.lake_top);
2142 add_edit(vid.lake_bottom);
2143 if(scale_used())
2144 add_edit(vid.creature_scale);
2145 }
2146 else {
2147 add_edit(vid.creature_scale);
2148 add_edit(vid.height_width);
2149 menuitem_sightrange('s');
2150 }
2151
2152 dialog::addBreak(50);
2153 add_edit(vid.yshift);
2154 if(GDIM == 3) {
2155 dialog::addSelItem(XLAT("mouse aiming sensitivity"), fts(mouseaim_sensitivity), 'a');
2156 dialog::add_action([] () {
2157 dialog::editNumber(mouseaim_sensitivity, -1, 1, 0.002, 0.01, XLAT("mouse aiming sensitivity"), "set to 0 to disable");
2158 });
2159 }
2160 if(true) {
2161 dialog::addSelItem(XLAT("camera rotation"), fts(vpconf.camera_angle), 'S');
2162 dialog::add_action([] {
2163 dialog::editNumber(vpconf.camera_angle, -180, 180, 5, 0, XLAT("camera rotation"),
2164 XLAT("Rotate the camera. Can be used to obtain a first person perspective, "
2165 "or third person perspective when combined with Y shift.")
2166 );
2167 });
2168 }
2169 if(GDIM == 2) {
2170 dialog::addSelItem(XLAT("fixed facing"), vid.fixed_facing ? fts(vid.fixed_facing_dir) : XLAT("OFF"), 'f');
2171 dialog::add_action([] () { vid.fixed_facing = !vid.fixed_facing;
2172 if(vid.fixed_facing) {
2173 dialog::editNumber(vid.fixed_facing_dir, 0, 360, 15, 90, "", "");
2174 dialog::dialogflags |= sm::CENTER;
2175 }
2176 });
2177 }
2178
2179 if((WDIM == 2 && GDIM == 3) || prod)
2180 dialog::addBoolItem_action(XLAT("fixed Y/Z rotation"), vid.fixed_yz, 'Z');
2181
2182 if(true) {
2183 dialog::addBreak(50);
2184 dialog::addSelItem(XLAT("projection"), current_proj_name(), 'M');
2185 dialog::add_action_push(models::model_menu);
2186 }
2187 #if MAXMDIM >= 4
2188 if(GDIM == 3) add_edit_fov('f');
2189 if(GDIM == 3) {
2190 dialog::addSelItem(XLAT("radar size"), fts(vid.radarsize), 'r');
2191 dialog::add_action([] () {
2192 dialog::editNumber(vid.radarsize, 0, 360, 15, 90, "", XLAT("set to 0 to disable"));
2193 dialog::extra_options = [] () { draw_radar(true); };
2194 });
2195 }
2196
2197 if(WDIM == 3 && sphere && stretch::factor) {
2198 dialog::addItem(XLAT("Berger sphere limit"), berger_limit);
2199 dialog::add_action([] () {
2200 dialog::editNumber(berger_limit, 0, 10, 1, 2, "",
2201 XLAT("Primitive-based rendering of Berger sphere is currently very slow and low quality. "
2202 "Here you can choose how many images to draw.")
2203 );
2204 });
2205 }
2206
2207 #if CAP_RAY
2208 if(GDIM == 3) {
2209 dialog::addItem(XLAT("configure raycasting"), 'A');
2210 dialog::add_action_push(ray::configure);
2211 }
2212 #endif
2213
2214 edit_levellines('L');
2215
2216 if(WDIM == 3 || (GDIM == 3 && euclid)) {
2217 dialog::addSelItem(XLAT("radar range"), fts(vid.radarrange), 'R');
2218 dialog::add_action([] () {
2219 dialog::editNumber(vid.radarrange, 0, 10, 0.5, 2, "", XLAT(""));
2220 dialog::extra_options = [] () { draw_radar(true); };
2221 });
2222 }
2223 if(GDIM == 3) add_edit_wall_quality('W');
2224 #endif
2225
2226 #if CAP_RUG
2227 if(rug::rugged) {
2228 dialog::addBoolItem_action(XLAT("3D monsters/walls on the surface"), rug::spatial_rug, 'S');
2229 }
2230 #endif
2231
2232 if(0);
2233 #if CAP_RUG
2234 else if(rug::rugged && !rug::spatial_rug)
2235 dialog::addBreak(100);
2236 #endif
2237 else if(GDIM == 2 && non_spatial_model())
2238 dialog::addInfo(XLAT("no 3D effects available in this projection"), 0xC00000);
2239 else if(GDIM == 2 && !spatial_graphics)
2240 dialog::addInfo(XLAT("set 3D monsters or walls in basic config first"));
2241 else if(geom3::invalid != "")
2242 dialog::addInfo(XLAT("error: ")+geom3::invalid, 0xC00000);
2243 else
2244 dialog::addInfo(XLAT("parameters set correctly"));
2245 dialog::addBreak(50);
2246 dialog::addItem(XLAT("stereo vision config"), 'e');
2247 dialog::add_action_push(showStereo);
2248
2249 #if CAP_VR
2250 dialog::addBoolItem(XLAT("VR settings"), vrhr::active(), 'v');
2251 dialog::add_action_push(vrhr::show_vr_settings);
2252 #endif
2253
2254 dialog::addBack();
2255 dialog::display();
2256 }
2257
__anond343dad35702null2258 EX int config3 = addHook(hooks_configfile, 100, [] {
2259 param_f(vid.eye, "eyelevel", 0)
2260 ->editable(-5, 5, .1, "eye level", "", 'E')
2261 ->set_extra([] {
2262 dialog::dialogflags |= sm::CENTER;
2263 vid.tc_depth = ticks;
2264
2265 dialog::addHelp(XLAT("In the FPP mode, the camera will be set at this altitude (before applying shifts)."));
2266
2267 dialog::addBoolItem(XLAT("auto-adjust to eyes on the player model"), vid.auto_eye, 'O');
2268 dialog::reaction = [] { vid.auto_eye = false; };
2269 dialog::add_action([] () {
2270 vid.auto_eye = !vid.auto_eye;
2271 geom3::do_auto_eye();
2272 });
2273 });
2274
2275 addsaver(vid.auto_eye, "auto-eyelevel", false);
2276
2277 param_f(vid.creature_scale, "creature_scale", "3d-creaturescale", 1)
2278 ->editable(0, 1, .1, "Creature scale", "", 'C');
2279 param_f(vid.height_width, "heiwi", "3d-heightwidth", 1.5)
2280 ->editable(0, 1, .1, "Height to width", "", 'h');
2281 param_f(vid.yshift, "yshift", "Y shift", 0)
2282 ->editable(0, 1, .1, "Y shift", "Don't center on the player character.", 'y')
2283 ->set_extra([] {
2284 if(WDIM == 3 && pmodel == mdPerspective)
2285 dialog::addBoolItem_action(XLAT("reduce if walls on the way"), vid.use_wall_radar, 'R');
2286 });
2287 addsaver(vid.use_wall_radar, "wallradar", true);
2288 addsaver(vid.fixed_facing, "fixed facing", 0);
2289 addsaver(vid.fixed_facing_dir, "fixed facing dir", 90);
2290 addsaver(vid.fixed_yz, "fixed YZ", true);
2291 param_f(vid.depth, "depth", "3D depth", 1)
2292 ->editable(0, 5, .1, "Ground level below the plane", "", 'd')
2293 ->set_extra([] {
2294 vid.tc_depth = ticks;
2295 help = XLAT(
2296 "Ground level is actually an equidistant surface, "
2297 "%1 absolute units below the plane P. "
2298 "Theoretically, this value affects the world -- "
2299 "for example, eagles could fly %2 times faster by "
2300 "flying above the ground level, on the plane P -- "
2301 "but the actual game mechanics are not affected. ", fts(vid.depth), fts(cosh(vid.depth)));
2302 if(GDIM == 2)
2303 help += XLAT(
2304 "(Distances reported by the vector graphics editor "
2305 "are not about points on the ground level, but "
2306 "about the matching points on the plane P -- "
2307 "divide them by the factor above to get actual "
2308 "distances.)"
2309 );
2310 if(GDIM == 3 && pmodel == mdPerspective && !euclid) {
2311 ld current_camera_level = hdist0(tC0(radar_transform));
2312 help += "\n\n";
2313 if(abs(current_camera_level) < 1e-6)
2314 help += XLAT(
2315 "The camera is currently exactly on the plane P. "
2316 "The horizon is seen as a straight line."
2317 );
2318 else help += XLAT(
2319 "The camera is currently %1 units above the plane P. "
2320 "This makes you see the floor level as in general perspective projection "
2321 "with parameter %2.", fts(current_camera_level), fts(tan_auto(vid.depth) / tan_auto(current_camera_level)));
2322 }
2323 dialog::addHelp(help);
2324 });
2325 param_f(vid.camera, "camera", "3D camera level", 1)
2326 ->editable(0, 5, .1, "", "", 'c')
2327 ->modif([] (float_setting* x) { x->menu_item_name = (GDIM == 2 ? "Camera level above the plane" : "Z shift"); })
2328 ->set_extra([] {
2329 vid.tc_camera = ticks;
2330 if(GDIM == 2)
2331 dialog::addHelp(XLAT(
2332 "Camera is placed %1 absolute units above a plane P in a three-dimensional "
2333 "world. Ground level is actually an equidistant surface, %2 absolute units "
2334 "below the plane P. The plane P (as well as the ground level or any "
2335 "other equidistant surface below it) is viewed at an angle of %3 "
2336 "(the tangent of the angle between the point in "
2337 "the center of your vision and a faraway location is 1/cosh(c) = %4).",
2338 fts(vid.camera),
2339 fts(vid.depth),
2340 fts(atan(1/cosh(vid.camera))*2/degree),
2341 fts(1/cosh(vid.camera))));
2342 if(GDIM == 3)
2343 dialog::addHelp(XLAT("Look from behind."));
2344 if(GDIM == 3 && pmodel == mdPerspective)
2345 dialog::addBoolItem_action(XLAT("reduce if walls on the way"), vid.use_wall_radar, 'R');
2346 });
2347 param_f(vid.wall_height, "wall_height", "3D wall height", .3)
2348 ->editable(0, 1, .1, "Height of walls", "", 'w')
2349 ->set_extra([] () {
2350 dialog::addHelp(GDIM == 3 ? "" : XLAT(
2351 "The height of walls, in absolute units. For the current values of g and c, "
2352 "wall height of %1 absolute units corresponds to projection value of %2.",
2353 fts(geom3::actual_wall_height()), fts(geom3::factor_to_projection(cgi.WALL))));
2354 dialog::addBoolItem(XLAT("auto-adjust in Goldberg grids"), vid.gp_autoscale_heights, 'O');
2355 dialog::add_action([] () {
2356 vid.gp_autoscale_heights = !vid.gp_autoscale_heights;
2357 });
2358 });
2359 param_f(vid.rock_wall_ratio, "rock_wall_ratio", "3D rock-wall ratio", .9)
2360 ->editable(0, 1, .1, "Rock-III to wall ratio", "", 'r')
2361 ->set_extra([] { dialog::addHelp(XLAT(
2362 "The ratio of Rock III to walls is %1, so Rock III are %2 absolute units high. "
2363 "Length of paths on the Rock III level is %3 of the corresponding length on the "
2364 "ground level.",
2365 fts(vid.rock_wall_ratio), fts(vid.wall_height * vid.rock_wall_ratio),
2366 fts(cosh(vid.depth - vid.wall_height * vid.rock_wall_ratio) / cosh(vid.depth))));
2367 });
2368 param_f(vid.human_wall_ratio, "human_wall_ratio", "3D human-wall ratio", .7)
2369 ->editable(0, 1, .1, "Human to wall ratio", "", 'h')
2370 ->set_extra([] { dialog::addHelp(XLAT(
2371 "Humans are %1 "
2372 "absolute units high. Your head travels %2 times the distance travelled by your "
2373 "feet.",
2374 fts(vid.wall_height * vid.human_wall_ratio),
2375 fts(cosh(vid.depth - vid.wall_height * vid.human_wall_ratio) / cosh(vid.depth)))
2376 );
2377 });
2378 param_f(vid.lake_top, "lake_top", "3D lake top", .25)
2379 ->editable(0, 1, .1, "Level of water surface", "", 'l');
2380 param_f(vid.lake_bottom, "lake_bottom", "3D lake bottom", .9)
2381 ->editable(0, 1, .1, "Level of water bottom", "", 'k');
2382 addsaver(vid.tc_depth, "3D TC depth", 1);
2383 addsaver(vid.tc_camera, "3D TC camera", 2);
2384 addsaver(vid.tc_alpha, "3D TC alpha", 3);
2385 param_f(vid.highdetail, "highdetail", "3D highdetail", 8)
2386 ->editable(0, 5, .5, "High detail range", "", 'n')
2387 ->set_extra(explain_detail)
2388 ->set_reaction([] {
2389 if(vid.highdetail > vid.middetail) vid.middetail = vid.highdetail;
2390 });
2391 param_f(vid.middetail, "middetail", "3D middetail", 8)
2392 ->editable(0, 5, .5, "Mid detail range", "", 'm')
2393 ->set_extra(explain_detail)
2394 ->set_reaction([] {
2395 if(vid.highdetail > vid.middetail) vid.highdetail = vid.middetail;
2396 });
2397 addsaver(vid.gp_autoscale_heights, "3D Goldberg autoscaling", true);
2398 });
2399
switchcolor(unsigned int & c,unsigned int * cs)2400 EX void switchcolor(unsigned int& c, unsigned int* cs) {
2401 dialog::openColorDialog(c, cs);
2402 }
2403
2404
2405 double cc_footphase;
2406 int lmousex, lmousey;
2407
showCustomizeChar()2408 EX void showCustomizeChar() {
2409
2410 cc_footphase += hypot(mousex - lmousex, mousey - lmousey);
2411 lmousex = mousex; lmousey = mousey;
2412
2413 gamescreen(4);
2414 dialog::init(XLAT("Customize character"));
2415
2416 if(shmup::on || multi::players) multi::cpid = multi::cpid_edit % multi::players;
2417 charstyle& cs = getcs();
2418
2419 dialog::addSelItem(XLAT("character"), csname(cs), 'g');
2420 dialog::addColorItem(XLAT("skin color"), cs.skincolor, 's');
2421 dialog::addColorItem(XLAT("eye color"), cs.eyecolor, 'e');
2422 dialog::addColorItem(XLAT("weapon color"), cs.swordcolor, 'w');
2423 dialog::addColorItem(XLAT("hair color"), cs.haircolor, 'h');
2424
2425 if(cs.charid >= 1) dialog::addColorItem(XLAT("dress color"), cs.dresscolor, 'd');
2426 else dialog::addBreak(100);
2427 if(cs.charid == 3) dialog::addColorItem(XLAT("dress color II"), cs.dresscolor2, 'f');
2428 else dialog::addBreak(100);
2429
2430 dialog::addColorItem(XLAT("movement color"), cs.uicolor, 'u');
2431
2432 if(!shmup::on && multi::players == 1) dialog::addSelItem(XLAT("save whom"), XLAT1(minf[moPrincess].name), 'p');
2433
2434 if(numplayers() > 1) dialog::addSelItem(XLAT("player"), its(multi::cpid+1), 'a');
2435
2436 dialog::addBoolItem(XLAT("left-handed"), cs.lefthanded, 'l');
2437
2438 dialog::addBreak(50);
2439 dialog::addBack();
2440 dialog::display();
2441
2442 int firsty = dialog::items[0].position / 2;
2443 int scale = firsty - 2 * vid.fsize;
2444
2445 flat_model_enabler fme;
2446
2447 initquickqueue();
2448 transmatrix V = atscreenpos(vid.xres/2, firsty, scale);
2449
2450 double alpha = atan2(mousex - vid.xres/2, mousey - firsty) - M_PI/2;
2451 V = V * spin(alpha);
2452 drawMonsterType(moPlayer, NULL, shiftless(V), 0, cc_footphase / scale, NOCOLOR);
2453 quickqueue();
2454
2455 keyhandler = [] (int sym, int uni) {
2456 dialog::handleNavigation(sym, uni);
2457
2458 if(shmup::on || multi::players) multi::cpid = multi::cpid_edit % multi::players;
2459 charstyle& cs = getcs();
2460 bool cat = cs.charid >= 4;
2461 if(uni == 'a') { multi::cpid_edit++; multi::cpid_edit %= 60; }
2462 else if(uni == 'g') {
2463 cs.charid++;
2464 if(cs.charid == 2 && !princess::everSaved && !autocheat) cs.charid = 4;
2465 cs.charid %= 10;
2466 }
2467 else if(uni == 'p') vid.samegender = !vid.samegender;
2468 else if(uni == 's') switchcolor(cs.skincolor, cat ? haircolors : skincolors);
2469 else if(uni == 'h') switchcolor(cs.haircolor, haircolors);
2470 else if(uni == 'w') switchcolor(cs.swordcolor, swordcolors);
2471 else if(uni == 'd') switchcolor(cs.dresscolor, cat ? haircolors : dresscolors);
2472 else if(uni == 'f') switchcolor(cs.dresscolor2, dresscolors2);
2473 else if(uni == 'u') switchcolor(cs.uicolor, eyecolors);
2474 else if(uni == 'e') switchcolor(cs.eyecolor, eyecolors);
2475 else if(uni == 'l') cs.lefthanded = !cs.lefthanded;
2476 else if(doexiton(sym, uni)) popScreen();
2477 };
2478 }
2479
refresh_canvas()2480 EX void refresh_canvas() {
2481 manual_celllister cl;
2482 cl.add(cwt.at);
2483
2484 int at = 0;
2485 while(at < isize(cl.lst)) {
2486 cell *c2 = cl.lst[at];
2487 c2->landparam = patterns::generateCanvas(c2);
2488 at++;
2489
2490 forCellEx(c3, c2) cl.add(c3);
2491 }
2492 }
2493
edit_color_table(colortable & ct,const reaction_t & r IS (reaction_t ()),bool has_bit IS (false))2494 EX void edit_color_table(colortable& ct, const reaction_t& r IS(reaction_t()), bool has_bit IS(false)) {
2495 cmode = sm::SIDE;
2496 gamescreen(0);
2497 dialog::init(XLAT("colors & aura"));
2498
2499 for(int i=0; i<isize(ct); i++) {
2500 dialog::addColorItem(its(i), ct[i] << 8, 'a'+i);
2501 if(WDIM == 3 && has_bit && !(ct[i] & 0x1000000)) dialog::lastItem().value = XLAT("(no wall)");
2502 dialog::add_action([i, &ct, r, has_bit] () {
2503 if(WDIM == 3 && has_bit) {
2504 ct[i] ^= 0x1000000;
2505 if(!(ct[i] & 0x1000000)) return;
2506 }
2507 dialog::openColorDialog(ct[i]);
2508 dialog::reaction = r;
2509 dialog::colorAlpha = false;
2510 dialog::dialogflags |= sm::SIDE;
2511 });
2512 }
2513
2514 dialog::addBack();
2515 dialog::display();
2516 }
2517
show_color_dialog()2518 EX void show_color_dialog() {
2519 cmode = sm::SIDE | sm::DIALOG_STRICT_X;
2520 getcstat = '-';
2521 gamescreen(0);
2522 dialog::init(XLAT("colors & aura"));
2523
2524 dialog::addColorItem(XLAT("background"), backcolor << 8, 'b');
2525 dialog::add_action([] () { dialog::openColorDialog(backcolor); dialog::colorAlpha = false; dialog::dialogflags |= sm::SIDE; });
2526
2527 if(WDIM == 2 && GDIM == 3 && hyperbolic)
2528 dialog::addBoolItem_action(XLAT("cool fog effect"), context_fog, 'B');
2529
2530 dialog::addColorItem(XLAT("foreground"), forecolor << 8, 'f');
2531 dialog::add_action([] () { dialog::openColorDialog(forecolor); dialog::colorAlpha = false; dialog::dialogflags |= sm::SIDE; });
2532
2533 dialog::addColorItem(XLAT("borders"), bordcolor << 8, 'o');
2534 dialog::add_action([] () { dialog::openColorDialog(bordcolor); dialog::colorAlpha = false; dialog::dialogflags |= sm::SIDE; });
2535
2536 dialog::addColorItem(XLAT("projection boundary"), ringcolor, 'r');
2537 dialog::add_action([] () { dialog::openColorDialog(ringcolor); dialog::dialogflags |= sm::SIDE; });
2538
2539 dialog::addSelItem(XLAT("boundary width multiplier"), fts(vid.multiplier_ring), 'R');
2540 dialog::add_action([] () { dialog::editNumber(vid.multiplier_ring, 0, 10, 1, 1, XLAT("boundary width multiplier"), ""); });
2541
2542 dialog::addColorItem(XLAT("projection background"), modelcolor, 'c');
2543 dialog::add_action([] () { dialog::openColorDialog(modelcolor); dialog::dialogflags |= sm::SIDE; });
2544
2545 dialog::addColorItem(XLAT("standard grid color"), stdgridcolor, 'g');
2546 dialog::add_action([] () { vid.grid = true; dialog::openColorDialog(stdgridcolor); dialog::dialogflags |= sm::SIDE; });
2547
2548 dialog::addSelItem(XLAT("grid width multiplier"), fts(vid.multiplier_grid), 'G');
2549 dialog::add_action([] () { dialog::editNumber(vid.multiplier_grid, 0, 10, 1, 1, XLAT("grid width multiplier"), ""); });
2550
2551 dialog::addSelItem(XLAT("brightness behind the sphere"), fts(backbrightness), 'i');
2552 dialog::add_action([] () { dialog::editNumber(backbrightness, 0, 1, .01, 0.25, XLAT("brightness behind the sphere"),
2553 XLAT("In the orthogonal projection, objects on the other side of the sphere are drawn darker.")); dialog::bound_low(0); });
2554
2555 dialog::addColorItem(XLAT("projection period"), periodcolor, 'p');
2556 dialog::add_action([] () { dialog::openColorDialog(periodcolor); dialog::dialogflags |= sm::SIDE; });
2557
2558 dialog::addColorItem(XLAT("dialogs"), dialog::dialogcolor << 8, 'd');
2559 dialog::add_action([] () { dialog::openColorDialog(dialog::dialogcolor); dialog::colorAlpha = false; dialog::dialogflags |= sm::SIDE; });
2560
2561 dialog::addBreak(50);
2562 if(specialland == laCanvas && colortables.count(patterns::whichCanvas)) {
2563 dialog::addItem(XLAT("pattern colors"), 'P');
2564 dialog::add_action_push([] { edit_color_table(colortables[patterns::whichCanvas], refresh_canvas, true); });
2565 }
2566
2567 if(cwt.at->land == laMinefield) {
2568 dialog::addItem(XLAT("minefield colors"), 'm');
2569 dialog::add_action_push([] { edit_color_table(minecolors); });
2570 }
2571
2572 if(viewdists) {
2573 dialog::addItem(XLAT("distance colors"), 'd');
2574 dialog::add_action_push([] () {edit_color_table(distcolors); });
2575 }
2576
2577 #if CAP_CRYSTAL
2578 if(cryst && cheater) {
2579 dialog::addItem(XLAT("crystal coordinate colors"), 'C');
2580 dialog::add_action([] () { crystal::view_coordinates = true; pushScreen([] () { edit_color_table(crystal::coordcolors); });});
2581 }
2582 #endif
2583
2584 if(cwt.at->land == laTortoise) {
2585 dialog::addBoolItem_action(XLAT("Galápagos shading"), tortoise::shading_enabled, 'T');
2586 }
2587
2588 dialog::addInfo(XLAT("colors of some game objects can be edited by clicking them."));
2589
2590 dialog::addBreak(50);
2591
2592 dialog::addSelItem(XLAT("aura brightness"), its(vid.aurastr), 'a');
2593 dialog::add_action([] () { dialog::editNumber(vid.aurastr, 0, 256, 10, 128, XLAT("aura brightness"), ""); dialog::bound_low(0); });
2594
2595 dialog::addSelItem(XLAT("aura smoothening factor"), its(vid.aurasmoothen), 's');
2596 dialog::add_action([] () { dialog::editNumber(vid.aurasmoothen, 1, 180, 1, 5, XLAT("aura smoothening factor"), ""); dialog::bound_low(1); });
2597
2598 dialog::addBreak(50);
2599 dialog::addBack();
2600 dialog::display();
2601
2602 keyhandler = [] (int sym, int uni) {
2603 if(uni == '-') {
2604 cell *c = mouseover;
2605 if(!c) return;
2606 else if(c == cwt.at) {
2607 pushScreen(showCustomizeChar);
2608 return;
2609 }
2610 else if(c->monst)
2611 dialog::openColorDialog(minf[c->monst].color);
2612 else if(c->item)
2613 dialog::openColorDialog(iinf[c->item].color);
2614 else if(c->wall)
2615 dialog::openColorDialog(winf[c->wall == waMineMine ? waMineUnknown : c->wall].color);
2616 #if CAP_COMPLEX2
2617 else if(c->land == laBrownian)
2618 dialog::openColorDialog(brownian::get_color_edit(c->landparam));
2619 #endif
2620 else
2621 dialog::openColorDialog(floorcolors[c->land]);
2622 dialog::colorAlpha = false;
2623 dialog::dialogflags |= sm::SIDE;
2624 return;
2625 }
2626 else dialog::handleNavigation(sym, uni);
2627 if(doexiton(sym, uni)) popScreen();
2628 };
2629 }
2630
2631 #if CAP_CONFIG
resetConfigMenu()2632 EX void resetConfigMenu() {
2633 dialog::init(XLAT("reset all configuration"));
2634 dialog::addInfo("Are you sure?");
2635 dialog::addItem("yes, and delete the config file", 'd');
2636 dialog::addItem("yes", 'y');
2637 dialog::addItem("cancel", 'n');
2638 dialog::addItem("reset the special game modes", 'r');
2639 dialog::display();
2640 keyhandler = [] (int sym, int uni) {
2641 dialog::handleNavigation(sym, uni);
2642
2643 if(uni == 'd') {
2644 resetConfig();
2645 unlink(conffile);
2646 popScreen();
2647 }
2648 else if(uni == 'y') {
2649 printf("resetting config\n");
2650 resetConfig();
2651 printf("config reset\n");
2652 popScreen();
2653 }
2654 else if(uni == 'r')
2655 resetModes();
2656 else if(uni == 'n' || doexiton(sym, uni))
2657 popScreen();
2658
2659 };
2660 }
2661 #endif
2662
2663 #if CAP_TRANS
selectLanguageScreen()2664 EX void selectLanguageScreen() {
2665 gamescreen(4);
2666 dialog::init("select language"); // intentionally not translated
2667
2668 int v = vid.language;
2669 dynamicval<int> d(vid.language, -1);
2670
2671 for(int i=0; i<NUMLAN-1 || i == v; i++) {
2672 vid.language = i;
2673 dialog::addSelItem(XLAT("EN"), its(100 * transcompleteness[i] / transcompleteness[0]) + "%", 'a'+i);
2674 }
2675
2676 dialog::addBreak(50);
2677 vid.language = -1;
2678 dialog::addBoolItem(XLAT("default") + ": " + XLAT("EN"), v == -1, '0');
2679 dialog::addBack();
2680
2681 dialog::addBreak(50);
2682
2683 vid.language = v;
2684 if(lang() >= 1)
2685 dialog::addHelp(XLAT("add credits for your translation here"));
2686 else
2687 dialog::addHelp(XLAT("original language"));
2688
2689 if(lang() != 0) {
2690 string tw = "";
2691 string s = XLAT("TRANSLATIONWARNING");
2692 if(s != "" && s != "TRANSLATIONWARNING") tw += s;
2693 s = XLAT("TRANSLATIONWARNING2");
2694 if(s != "" && s != "TRANSLATIONWARNING2") { if(tw != "") tw += " "; tw += s; }
2695 if(tw != "") {
2696 dialog::addHelp(tw);
2697 dialog::lastItem().color = 0xFF0000;
2698 }
2699 }
2700
2701 dialog::display();
2702
2703 keyhandler = [] (int sym, int uni) {
2704 dialog::handleNavigation(sym, uni);
2705
2706 if(uni == '0') {
2707 vid.language = -1;
2708 android_settings_changed();
2709 }
2710
2711 else if(uni >= 'a' && uni < 'a'+NUMLAN) {
2712 vid.language = uni - 'a';
2713 android_settings_changed();
2714 }
2715
2716 else if(doexiton(sym, uni))
2717 popScreen();
2718 };
2719 }
2720 #endif
2721
configureMouse()2722 EX void configureMouse() {
2723 gamescreen(1);
2724 dialog::init(XLAT("mouse & touchscreen"));
2725
2726 dialog::addBoolItem_action(XLAT("reverse pointer control"), (vid.revcontrol), 'r');
2727
2728 dialog::addBoolItem_action(XLAT("draw circle around the target"), (vid.drawmousecircle), 'd');
2729
2730 if(GDIM == 3) {
2731 dialog::addBoolItem_action(XLAT("highlight the cell forward"), vid.axes3, 'f');
2732 }
2733
2734 #if ISMOBILE
2735 dialog::addBoolItem(XLAT("targetting ranged Orbs long-click only"), (vid.shifttarget&2), 'i');
2736 #else
2737 dialog::addBoolItem(XLAT("targetting ranged Orbs Shift+click only"), (vid.shifttarget&1), 'i');
2738 #endif
2739 dialog::add_action([] {vid.shifttarget = vid.shifttarget^3; });
2740
2741 #if !ISMOBILE
2742 dialog::addBoolItem_action(XLAT("quick mouse"), vid.quickmouse, 'M');
2743 #endif
2744
2745 dialog::addSelItem(XLAT("move by clicking on compass"), its(vid.mobilecompasssize), 'C');
2746 dialog::add_action([] {
2747 dialog::editNumber(vid.mobilecompasssize, 0, 100, 10, 20, XLAT("compass size"), XLAT("0 to disable"));
2748 // we need to check the moves
2749 dialog::reaction = checkmove;
2750 dialog::bound_low(0);
2751 });
2752
2753 #if CAP_ORIENTATION
2754 if(GDIM == 2) {
2755 dialog::addSelItem(XLAT("scrolling by device rotation"), ors::choices[ors::mode], '1');
2756 dialog::add_action_push(ors::show);
2757 }
2758 #endif
2759
2760 dialog::addBack();
2761 dialog::display();
2762 }
2763
2764 vector<setting*> last_changed;
2765
add_to_changed(setting * f)2766 EX void add_to_changed(setting *f) {
2767 auto orig_f = f;
2768 for(int i=0; i<isize(last_changed); i++) {
2769 if(last_changed[i] == f)
2770 return;
2771 swap(last_changed[i], f);
2772 if(f == orig_f) return;
2773 }
2774 last_changed.push_back(f);
2775 }
2776
find_edit(void * val)2777 EX setting *find_edit(void *val) {
2778 for(auto& fs: params) {
2779 fs.second->check_change();
2780 if(fs.second->affects(val))
2781 return &*fs.second;
2782 }
2783 return nullptr;
2784 }
2785
add_edit_ptr(void * val)2786 EX void add_edit_ptr(void *val) {
2787 int found = 0;
2788 for(auto& fs: params) {
2789 fs.second->check_change();
2790 if(fs.second->affects(val))
2791 fs.second->show_edit_option(), found++;
2792 }
2793 if(found != 1) println(hlog, "found = ", found);
2794 }
2795
2796 #if HDR
add_edit(T & val)2797 template<class T> void add_edit(T& val) {
2798 add_edit_ptr(&val);
2799 }
2800 #endif
2801
find_setting()2802 EX void find_setting() {
2803 gamescreen(1);
2804
2805 dialog::init(XLAT("find a setting"));
2806 if(dialog::infix != "") mouseovers = dialog::infix;
2807
2808 vector<setting*> found;
2809
2810 for(auto& p: params) {
2811 auto& fs = p.second;
2812 string key = fs->search_key();
2813 if(fs->available() && dialog::hasInfix(key))
2814 found.push_back(&*fs);
2815 }
2816
2817 for(int i=0; i<9; i++) {
2818 if(i < isize(found)) {
2819 found[i]->show_edit_option('1' + i);
2820 }
2821 else dialog::addBreak(100);
2822 }
2823
2824 dialog::addBreak(100);
2825 dialog::addInfo(XLAT("press letters to search"));
2826 dialog::addSelItem(XLAT("matching items"), its(isize(found)), 0);
2827 dialog::display();
2828
2829 keyhandler = [] (int sym, int uni) {
2830 dialog::handleNavigation(sym, uni);
2831 if(dialog::editInfix(uni)) ;
2832 else if(doexiton(sym, uni)) popScreen();
2833 };
2834 }
2835
edit_all_settings()2836 EX void edit_all_settings() {
2837 gamescreen(1);
2838 dialog::init(XLAT("recently changed settings"));
2839
2840 for(auto &fs: params) fs.second->check_change();
2841
2842 int id = 0;
2843 for(auto l: last_changed)
2844 if(l->available() && id < 10)
2845 l->show_edit_option('a'+(id++));
2846
2847 dialog::addBreak(100);
2848 dialog::addItem(XLAT("find a setting"), '/');
2849 dialog::add_action_push(find_setting);
2850 dialog::addBack();
2851 dialog::display();
2852 }
2853
show_edit_option(char key)2854 void list_setting::show_edit_option(char key) {
2855 string opt = options[get_value()].first;
2856 dialog::addSelItem(XLAT(menu_item_name), XLAT(opt), key);
2857 dialog::add_action_push([this] {
2858 add_to_changed(this);
2859 gamescreen(2);
2860 dialog::init(XLAT(menu_item_name));
2861 dialog::addBreak(100);
2862 int q = isize(options);
2863 for(int i=0; i<q; i++) {
2864 dialog::addBoolItem(XLAT(options[i].first), get_value() == i, 'a'+i);
2865 dialog::add_action([this, i] { set_value(i); popScreen(); });
2866 dialog::addBreak(100);
2867 if(options[i].second != "") {
2868 dialog::addHelp(XLAT(options[i].second));
2869 dialog::addBreak(100);
2870 }
2871 }
2872 dialog::addBreak(100);
2873 dialog::addBack();
2874 dialog::display();
2875 });
2876 }
2877
showSettings()2878 EX void showSettings() {
2879 gamescreen(1);
2880 dialog::init(XLAT("settings"));
2881
2882 dialog::addItem(XLAT("interface"), 'i');
2883 dialog::add_action_push(configureInterface);
2884
2885 dialog::addItem(XLAT("general graphics"), 'g');
2886 dialog::add_action_push(showGraphConfig);
2887
2888 dialog::addItem(XLAT("3D configuration"), '9');
2889 dialog::add_action_push(show3D);
2890
2891 dialog::addItem(XLAT("quick options"), 'q');
2892 dialog::add_action_push(showGraphQuickKeys);
2893
2894 dialog::addItem(XLAT("models & projections"), 'p');
2895 dialog::add_action_push(models::quick_model);
2896
2897 dialog::addItem(XLAT("colors & aura"), 'c');
2898 dialog::add_action_push(show_color_dialog);
2899
2900 #if CAP_SHMUP && !ISMOBILE
2901 dialog::addSelItem(XLAT("keyboard & joysticks"), "", 'k');
2902 dialog::add_action(multi::configure);
2903 #endif
2904
2905 dialog::addSelItem(XLAT("mouse & touchscreen"), "", 'm');
2906 dialog::add_action_push(configureMouse);
2907
2908 dialog::addItem(XLAT("other settings"), 'o');
2909 dialog::add_action_push(configureOther);
2910
2911 dialog::addBreak(100);
2912
2913 #if CAP_CONFIG
2914 dialog::addItem(XLAT("recently changed settings"), '/');
2915 dialog::add_action_push(edit_all_settings);
2916
2917 dialog::addItem(XLAT("save the current config"), 's');
2918 dialog::add_action(saveConfig);
2919
2920 dialog::addItem(XLAT("reset all configuration"), 'R');
2921 dialog::add_action_push(resetConfigMenu);
2922 #endif
2923
2924 if(getcstat == 's') mouseovers = XLAT("Config file: %1", conffile);
2925
2926 dialog::addBack();
2927 dialog::display();
2928 }
2929
2930 #if CAP_COMMANDLINE
2931
read_color_args()2932 EX int read_color_args() {
2933 using namespace arg;
2934
2935 if(argis("-back")) {
2936 PHASEFROM(2); shift(); backcolor = arghex();
2937 }
2938 else if(argis("-fillmodel")) {
2939 PHASEFROM(2); shift(); modelcolor = arghex();
2940 }
2941 else if(argis("-ring")) {
2942 PHASEFROM(2); shift(); ringcolor = arghex();
2943 }
2944 else if(argis("-ringw")) {
2945 PHASEFROM(2); shift_arg_formula(vid.multiplier_ring);
2946 }
2947 else if(argis("-stdgrid")) {
2948 PHASEFROM(2); shift(); stdgridcolor = arghex();
2949 }
2950 else if(argis("-gridw")) {
2951 PHASEFROM(2); shift_arg_formula(vid.multiplier_grid);
2952 }
2953 else if(argis("-period")) {
2954 PHASEFROM(2); shift(); periodcolor = arghex();
2955 }
2956 else if(argis("-crosshair")) {
2957 PHASEFROM(2); shift(); crosshair_color = arghex();
2958 shift_arg_formula(crosshair_size);
2959 }
2960 else if(argis("-borders")) {
2961 PHASEFROM(2); shift(); bordcolor = arghex();
2962 }
2963 else if(argis("-fore")) {
2964 PHASEFROM(2); shift(); forecolor = arghex();
2965 }
2966 else if(argis("-dialog")) {
2967 PHASEFROM(2); shift(); dialog::dialogcolor = arghex();
2968 }
2969 else if(argis("-d:color"))
2970 launch_dialog(show_color_dialog);
2971 else return 1;
2972 return 0;
2973 }
2974
read_config_args()2975 EX int read_config_args() {
2976 using namespace arg;
2977
2978 if(argis("-c")) { PHASE(1); shift(); conffile = argcs(); }
2979 // change the configuration from the command line
2980 else if(argis("-aa")) { PHASEFROM(2); shift(); vid.want_antialias = argi(); apply_screen_settings(); }
2981 else if(argis("-lw")) { PHASEFROM(2); shift_arg_formula(vid.linewidth); }
2982 else if(argis("-wm")) { PHASEFROM(2); shift(); vid.wallmode = argi(); }
2983 else if(argis("-mm")) { PHASEFROM(2); shift(); vid.monmode = argi(); }
2984
2985 else if(argis("-noshadow")) { noshadow = true; }
2986 else if(argis("-bright")) { bright = true; }
2987 else if(argis("-gridon")) { vid.grid = true; }
2988 else if(argis("-gridoff")) { vid.grid = false; }
2989
2990 // non-configurable options
2991 else if(argis("-vsync_off")) {
2992 vid.want_vsync = false;
2993 apply_screen_settings();
2994 }
2995 else if(argis("-aura")) {
2996 PHASEFROM(2);
2997 shift(); vid.aurastr = argi();
2998 shift(); vid.aurasmoothen = argi();
2999 }
3000 else if(argis("-nofps")) {
3001 PHASEFROM(2);
3002 nofps = true;
3003 }
3004 else if(argis("-nohud")) {
3005 PHASEFROM(2);
3006 nohud = true;
3007 }
3008 else if(argis("-nomenu")) {
3009 PHASEFROM(2);
3010 nomenukey = true;
3011 }
3012 else if(argis("-nomsg")) {
3013 PHASEFROM(2);
3014 nomsg = true;
3015 }
3016 #if MAXMDIM >= 4
3017 else if(argis("-switch-fpp")) {
3018 PHASEFROM(2);
3019 geom3::switch_fpp();
3020 }
3021 #endif
3022 else if(argis("-switch-tpp")) {
3023 PHASEFROM(2);
3024 geom3::switch_tpp();
3025 }
3026 #if MAXMDIM >= 4
3027 else if(argis("-switch-3d")) {
3028 PHASEFROM(2);
3029 geom3::switch_always3();
3030 }
3031 #endif
3032 else if(argis("-nohelp")) {
3033 PHASEFROM(2);
3034 nohelp = true;
3035 }
3036 else if(argis("-dont_face_pc")) {
3037 PHASEFROM(2);
3038 dont_face_pc = true;
3039 }
3040
3041 #if CAP_TRANS
3042 else if(argis("-lang")) {
3043 PHASEFROM(2); shift(); vid.language = argi();
3044 }
3045 #endif
3046 else if(argis("-vlq")) {
3047 PHASEFROM(2); shift(); vid.linequality = argi();
3048 }
3049 else if(argis("-fov")) {
3050 PHASEFROM(2); shift_arg_formula(vid.fov);
3051 }
3052 else if(argis("-r")) {
3053 PHASEFROM(2);
3054 shift();
3055 int clWidth=0, clHeight=0, clFont=0;
3056 sscanf(argcs(), "%dx%dx%d", &clWidth, &clHeight, &clFont);
3057 if(clWidth) vid.xres = clWidth;
3058 if(clHeight) vid.yres = clHeight;
3059 if(clFont) vid.abs_fsize = clFont, vid.relative_font = true;
3060 }
3061 else if(argis("-msm")) {
3062 PHASEFROM(2); memory_saving_mode = true;
3063 }
3064 else if(argis("-mrsv")) {
3065 PHASEFROM(2); shift(); reserve_limit = argi(); apply_memory_reserve();
3066 }
3067 else if(argis("-yca")) {
3068 PHASEFROM(2);
3069 shift_arg_formula(vid.yshift);
3070 shift_arg_formula(pconf.camera_angle);
3071 }
3072 else if(argis("-pside")) {
3073 PHASEFROM(2);
3074 permaside = true;
3075 }
3076 else if(argis("-xy")) {
3077 PHASEFROM(2);
3078 shift_arg_formula(pconf.xposition);
3079 shift_arg_formula(pconf.yposition);
3080 }
3081 else if(argis("-fixdir")) {
3082 PHASEFROM(2);
3083 vid.fixed_facing = true;
3084 shift_arg_formula(vid.fixed_facing_dir);
3085 }
3086 else if(argis("-fixdiroff")) {
3087 PHASEFROM(2);
3088 vid.fixed_facing = false;
3089 }
3090 else if(argis("-msmoff")) {
3091 PHASEFROM(2); memory_saving_mode = false;
3092 }
3093 else if(argis("-levellines")) {
3094 PHASEFROM(2); shift_arg_formula(levellines);
3095 }
3096 else if(argis("-level-notexture")) {
3097 PHASEFROM(2); disable_texture = true;
3098 }
3099 else if(argis("-level-texture")) {
3100 PHASEFROM(2); disable_texture = false;
3101 }
3102 else if(argis("-msens")) {
3103 PHASEFROM(2); shift_arg_formula(mouseaim_sensitivity);
3104 }
3105 TOGGLE('o', vid.wantGL, { vid.wantGL = !vid.wantGL; apply_screen_settings();})
3106 TOGGLE('f', vid.want_fullscreen, { vid.want_fullscreen = !vid.want_fullscreen; apply_screen_settings(); })
3107 else if(argis("-noshaders")) {
3108 PHASE(1);
3109 glhr::noshaders = true;
3110 }
3111 else if(argis("-d:sight")) {
3112 PHASEFROM(2); launch_dialog(); edit_sightrange();
3113 }
3114 else if(argis("-d:char")) {
3115 PHASEFROM(2); launch_dialog(showCustomizeChar);
3116 }
3117 else if(argis("-d:3")) {
3118 PHASEFROM(2); launch_dialog(show3D);
3119 }
3120 else if(argis("-d:stereo")) {
3121 PHASEFROM(2); launch_dialog(showStereo);
3122 }
3123 else if(argis("-d:iface")) {
3124 PHASEFROM(2); launch_dialog(configureInterface);
3125 }
3126 else if(argis("-d:graph")) {
3127 PHASEFROM(2); launch_dialog(showGraphConfig);
3128 }
3129 else if(argis("-tstep")) {
3130 PHASEFROM(2); shift(); vid.texture_step = argi();
3131 }
3132 else if(argis("-csc")) {
3133 PHASEFROM(2); shift_arg_formula(vid.creature_scale);
3134 }
3135 else if(argis("-neon")) {
3136 PHASEFROM(2);
3137 shift(); neon_mode = eNeon(argi());
3138 }
3139 else if(argis("-dmc")) {
3140 PHASEFROM(2);
3141 shift(); vid.drawmousecircle = argi();
3142 }
3143 else if(argis("-smooths")) {
3144 PHASEFROM(2);
3145 shift(); smooth_scrolling = argi();
3146 }
3147 else if(argis("-via-shader")) {
3148 PHASEFROM(2);
3149 shift(); vid.consider_shader_projection = argi();
3150 }
3151 else if(argis("-neonnf")) {
3152 PHASEFROM(2);
3153 shift(); neon_nofill = argi();
3154 }
3155 else if(argis("-precw")) {
3156 PHASEFROM(2);
3157 shift_arg_formula(precise_width);
3158 }
3159 else if(argis("-d:all")) {
3160 PHASEFROM(2); launch_dialog(edit_all_settings);
3161 }
3162 else if(argis("-char")) {
3163 auto& cs = vid.cs;
3164 shift();
3165 string s = args();
3166 if(s == "dodek") {
3167 cs.charid = 4;
3168 cs.lefthanded = false;
3169 cs.skincolor = 0x202020FF;
3170 cs.eyecolor = 0x20C000FF;
3171 cs.haircolor = 0x202020FF;
3172 cs.dresscolor =0x424242FF;
3173 cs.swordcolor = 0xF73333FF;
3174 }
3175 else if(s == "rudy") {
3176 cs.charid = 4;
3177 cs.lefthanded = false;
3178 cs.skincolor = 0xA44139FF;
3179 cs.eyecolor = 0xD59533FF;
3180 cs.haircolor = 0xC6634AFF;
3181 cs.dresscolor =0xC6634AFF;
3182 cs.swordcolor = 0x3CBB33FF;
3183 }
3184 else if(s == "running") {
3185 cs.charid = 6;
3186 cs.lefthanded = false;
3187 cs.skincolor = 0xFFFFFFFF;
3188 cs.eyecolor = 0xFF;
3189 cs.haircolor = 0xFFFFFFFF;
3190 cs.dresscolor =0xFFFFFFFF;
3191 cs.swordcolor = 0xFF0000FF;
3192 }
3193 else if(s == "princess") {
3194 cs.charid = 3;
3195 cs.lefthanded = true;
3196 cs.skincolor = 0xEFD0C9FF;
3197 cs.haircolor = 0x301800FF;
3198 cs.eyecolor = 0xC000FF;
3199 cs.dresscolor = 0x408040FF;
3200 cs.swordcolor = 0xFFFFFFFF;
3201 }
3202 else if(s == "worker") {
3203 cs.charid = 2;
3204 cs.skincolor = 0xC77A58FF;
3205 cs.haircolor = 0x502810FF;
3206 cs.dresscolor = 0xC0C000FF;
3207 cs.eyecolor = 0x500040FF;
3208 cs.swordcolor = 0x808080FF;
3209 }
3210 else {
3211 cs.charid = argi();
3212 cs.lefthanded = cs.charid >= 10;
3213 cs.charid %= 10;
3214 }
3215 }
3216 else return 1;
3217 return 0;
3218 }
3219
read_param_args()3220 EX int read_param_args() {
3221 const string& s = arg::args();
3222 auto pos = s.find("=");
3223 if(pos == string::npos) return 1;
3224 string name = s.substr(0, pos);
3225 string value = s.substr(pos+1);
3226 PHASEFROM(2);
3227 if(!params.count(name)) {
3228 println(hlog, "parameter unknown: ", name);
3229 exit(1);
3230 }
3231 params[name]->load_from(value);
3232 return 0;
3233 }
3234
3235 // mode changes:
3236
read_gamemode_args()3237 EX int read_gamemode_args() {
3238 using namespace arg;
3239
3240 if(argis("-P")) {
3241 PHASE(2); shift();
3242 stop_game_and_switch_mode(rg::nothing);
3243 multi::players = argi();
3244 }
3245 TOGGLE('S', shmup::on, stop_game_and_switch_mode(rg::shmup))
3246 TOGGLE('H', hardcore, switchHardcore())
3247 TOGGLE('R', randomPatternsMode, stop_game_and_switch_mode(rg::randpattern))
3248 TOGGLE('i', inv::on, stop_game_and_switch_mode(rg::inv))
3249
3250 else return 1;
3251 return 0;
3252 }
3253
3254 auto ah_config =
3255 addHook(hooks_args, 0, read_config_args) +
3256 addHook(hooks_args, 0, read_param_args) +
3257 addHook(hooks_args, 0, read_gamemode_args) + addHook(hooks_args, 0, read_color_args);
3258 #endif
3259
3260 }
3261