From 4421143ae5c7ff2deff35a0f5a5768254dd3bca5 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Thu, 14 Mar 2024 19:27:08 +0100 Subject: [PATCH] customize land mode --- complex2.cpp | 8 ++-- config.cpp | 12 ++++++ help.cpp | 24 ++++++----- hyper.h | 2 +- landgen.cpp | 18 +++++++- landlock.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ legacy.cpp | 1 + menus.cpp | 3 ++ monstergen.cpp | 11 ++++- yendor.cpp | 10 +++++ 10 files changed, 183 insertions(+), 19 deletions(-) diff --git a/complex2.cpp b/complex2.cpp index 6381bc42..f7d97961 100644 --- a/complex2.cpp +++ b/complex2.cpp @@ -166,11 +166,11 @@ EX namespace brownian { ONEMPTY { if(hrand(10000) < min(250, 100 + 2 * PT(kills[moAcidBird] + kills[moBrownBug], 50)) * (25 + min(items[itBrownian], 100)) / 25 && c->landparam >= 4 && c->landparam < 24) c->item = itBrownian; - if(hrand_monster(8000) < 15 + items[itBrownian]) + if(hrand_monster_in(laBrownian, 8000) < 15 + items[itBrownian]) c->monst = moAcidBird; - else if(hrand_monster(8000) < 15) + else if(hrand_monster_in(laBrownian, 8000) < 15) c->monst = moAlbatross; - else if(hrand_monster(8000) < 15 + items[itBrownian]) { + else if(hrand_monster_in(laBrownian, 8000) < 15 + items[itBrownian]) { c->monst = moBrownBug; c->hitpoints = 3; } @@ -291,7 +291,7 @@ extern array features; #define VF [] (cell *c) -bool hrand_var(int i) { return hrand_monster(i) < 25 + items[itVarTreasure] + yendor::hardness(); } +bool hrand_var(int i) { return hrand_monster_in(laVariant, i) < 25 + items[itVarTreasure] + yendor::hardness(); } array features {{ feature{(color_t)(-0x202020), 5, moNecromancer, VF { diff --git a/config.cpp b/config.cpp index be4f39bd..0211243f 100644 --- a/config.cpp +++ b/config.cpp @@ -1663,6 +1663,18 @@ EX void initConfig() { param_i(stamplen, "stamplen"); param_f(anims::period, "animperiod"); + + addsaver(use_custom_land_list, "customland_use"); + for(int i=0; i cellfunction; // passable flags #define SAGEMELT .1 -#define PT(x, y) ((tactic::on || quotient == 2 || daily::on) ? (y) : inv::on ? min(2*(y),x) : (x)) +#define PT(x, y) rebalance_treasure(x, y, c->land) #define ROCKSNAKELENGTH 50 #define WORMLENGTH 15 #define PRIZEMUL 7 diff --git a/landgen.cpp b/landgen.cpp index 27364b9f..3f7f90a8 100644 --- a/landgen.cpp +++ b/landgen.cpp @@ -171,8 +171,8 @@ EX void place_elemental_wall(cell *c) { else if(c->land == laEEarth) c->wall = waStone; } -// automatically adjust monster generation for 3D geometries -EX int hrand_monster(int x) { +// automatically adjust monster generation for 3D geometries and custom difficulty +EX int hrand_monster_in(eLand l, int x) { // dual geometry mode is much harder, so generate less monsters to balance it if(dual::state) x *= 3; // in 3D monster generation depends on the sight range @@ -180,9 +180,15 @@ EX int hrand_monster(int x) { int t = isize(gmatrix); if(t > 500) x = int(((long long)(x)) * t / 500); } + if(use_custom_land_list) { + x = x * 100 / custom_land_difficulty[l]; + if(x == 0) x = 1; + } return hrand(x); } +#define hrand_monster(x) hrand_monster_in(c->land, x) + EX bool is_zebra_trapdoor(cell *c) { if(euclid && closed_or_bounded) return false; #if CAP_ARCM @@ -280,6 +286,12 @@ EX void gen_baby_tortoise(cell *c) { tortoise::babymap[c] = tortoise::getb(c) ^ tortoise::getRandomBits(); } +EX int rebalance_treasure(int x, int y, eLand l) { + int res = ((tactic::on || quotient == 2 || daily::on) ? (y) : inv::on ? min(2*(y),x) : (x)); + if(use_custom_land_list) res = (res * custom_land_treasure[l] + 50) / 100; + return res; + } + EX void giantLandSwitch(cell *c, int d, cell *from) { bool fargen = d == 9; switch(c->land) { @@ -3139,4 +3151,6 @@ EX void setdist(cell *c, int d, cell *from) { #endif } +#undef hrand_monster + } diff --git a/landlock.cpp b/landlock.cpp index 33c8eaa9..1ba7b6c0 100644 --- a/landlock.cpp +++ b/landlock.cpp @@ -712,8 +712,15 @@ EX eLand getLandForList(cell *c) { return l; } +EX bool use_custom_land_list; +EX array custom_land_list; +EX array custom_land_treasure; +EX array custom_land_difficulty; +EX array custom_land_wandering; + EX bool isLandIngame(eLand l) { if(isElemental(l)) l = laElementalWall; + if(use_custom_land_list) return custom_land_list[l]; if(dual::state == 2 && !dual::check_side(l)) return false; if((eubinary || sol) && isCyclic(l) && l != specialland) return false; if(l == laCamelot && hyperbolic && WDIM == 3) return false; @@ -802,6 +809,112 @@ EX const int cursed_when = 386; EX const int walls_when = 388; +EX void mark_tamper() { cheater++; } + +EX void customize_land_in_list(eLand l) { + cmode = sm::DARKEN; gamescreen(); + + dialog::init(XLATN(linf[l].name), linf[l].color); + + help = generateHelpForLand(l); + addHelpWithTitle(); + + dialog::addBreak(100); + + dialog::addBoolItem(XLAT("land in game"), custom_land_list[l], 'a'); + dialog::add_action([l] { + custom_land_list[l] = !custom_land_list[l]; + cheater++; + }); + + dialog::addSelItem(XLAT("treasure rate"), its(custom_land_treasure[l]), 't'); + dialog::add_action([l] { + dialog::editNumber(custom_land_treasure[l], 0, 1000, 10, 100, XLAT("treasure rate in %the1", linf[l].name), ""); + dialog::get_ne().reaction = mark_tamper; + }); + + dialog::addSelItem(XLAT("difficulty"), its(custom_land_difficulty[l]), 'd'); + dialog::add_action([l] { + dialog::editNumber(custom_land_difficulty[l], 0, 1000, 10, 100, XLAT("difficulty of %the1", linf[l].name), ""); + dialog::get_ne().reaction = mark_tamper; + }); + + dialog::addSelItem(XLAT("wandering"), its(custom_land_wandering[l]), 'w'); + dialog::add_action([l] { + dialog::editNumber(custom_land_wandering[l], 0, 1000, 10, 100, XLAT("difficulty of %the1", linf[l].name), ""); + dialog::get_ne().reaction = mark_tamper; + }); + + dialog::addBack(); + dialog::display(); + } + +EX void customize_land_list() { + cmode = sm::DARKEN; gamescreen(); + dialog::init(XLAT("custom land list")); + if(dialog::infix != "") mouseovers = dialog::infix; + + generateLandList([] (eLand l) { + if(!use_custom_land_list) { + custom_land_list[l] = isLandIngame(l); + custom_land_treasure[l] = 100; + custom_land_difficulty[l] = 100; + custom_land_wandering[l] = 100; + } + if(dialog::infix != "" && !dialog::hasInfix(linf[l].name)) return false; + if(l == laCanvas) return true; + return !!(land_validity(l).flags & lv::appears_in_geom_exp); + }); + stable_sort(landlist.begin(), landlist.end(), [] (eLand l1, eLand l2) { return land_validity(l1).quality_level > land_validity(l2).quality_level; }); + + dialog::start_list(900, 900, '1'); + for(eLand l: landlist) { + dialog::addBoolItem(XLAT1(linf[l].name), custom_land_list[l], dialog::list_fake_key++); + string s; + if(custom_land_treasure[l] != 100) s += "$" + its(custom_land_treasure[l]) + " "; + if(custom_land_difficulty[l] != 100) s += "!" + its(custom_land_difficulty[l]) + " "; + if(custom_land_wandering[l] != 100) s += "^" + its(custom_land_wandering[l]) + " "; + if(s != "") dialog::lastItem().value = s; + dialog::add_action_confirmed([l] { + stop_game(); + use_custom_land_list = true; + start_game(); + pushScreen([l] { customize_land_in_list(l); }); + }); + } + dialog::end_list(); + + dialog::addInfo(XLAT("press letters to search")); + dialog::addBoolItem("custom land list mode", use_custom_land_list, 'U'); + dialog::add_action_confirmed([] { + stop_game(); + use_custom_land_list = !use_custom_land_list; + start_game(); + }); + + dialog::addHelp(); + dialog::add_action([] { + gotoHelp(XLAT( + "In this mode, you can choose the lands you want to be in game. You can also customize their treasure rate and difficulty.\n\n" + "While the game automatically selects a list of lands by default, " + "based on whether it thinks they work well in the currently selected tiling, " + "you might not agree with this selection.\n\n" + "Note that, often, lands are enabled or disabled for a GOOD reason! Use at your own risk.\n\n" + "Just click on a land to configure it. If you are not in the custom land list mode, " + "this will restart the game. You can change the settings during a custom game, but it counts as a cheat." + )); + }); + dialog::addBack(); + dialog::display(); + + keyhandler = [] (int sym, int uni) { + dialog::handleNavigation(sym, uni); + + if(dialog::editInfix(uni)) dialog::list_skip = 0; + else if(doexiton(sym, uni)) popScreen(); + }; + } + // check if the given land should appear in lists EX land_validity_t& land_validity(eLand l) { diff --git a/legacy.cpp b/legacy.cpp index 93c38a36..84ba7adb 100644 --- a/legacy.cpp +++ b/legacy.cpp @@ -206,6 +206,7 @@ EX modecode_t legacy_modecode() { if(int(geometry) > 3 || int(variation) > 1) return UNKNOWN; if(casual) return UNKNOWN; if(bow::weapon) return UNKNOWN; + if(use_custom_land_list) return UNKNOWN; bool is_default_land_structure = (princess::challenge || tactic::on) ? ls::single() : diff --git a/menus.cpp b/menus.cpp index e9243c87..b7dba73a 100644 --- a/menus.cpp +++ b/menus.cpp @@ -685,6 +685,9 @@ EX void showChangeMode() { multi::cpid = 0; menuitem_land_structure('l'); + dialog::addBoolItem(XLAT("custom land list"), use_custom_land_list, 'L'); + dialog::add_action_push(customize_land_list); + dialog::addBoolItem(XLAT("weapon selection"), bow::weapon, 'b'); dialog::add_action_push(bow::showMenu); diff --git a/monstergen.cpp b/monstergen.cpp index 39f0c96d..ef5e57f5 100644 --- a/monstergen.cpp +++ b/monstergen.cpp @@ -192,7 +192,7 @@ EX int reptilemax() { return r; } -bool wchance(int a, int of, int reduction = 0) { +bool wchance_in(eLand l, int a, int of, int reduction = 0) { of *= 10; a += yendor::hardness() + 1; if(isCrossroads(cwt.at->land)) @@ -206,6 +206,11 @@ bool wchance(int a, int of, int reduction = 0) { a -= reduction; if(a < 0) return false; + if(use_custom_land_list) { + of *= 100; + a *= custom_land_wandering[l]; + } + return hrand(a+of) < a; } @@ -337,7 +342,7 @@ EX void wandering() { if(closed_or_bounded && specialland == laClearing) clearing::new_root(); - if(cwt.at->land == laZebra && cwt.at->wall == waNone && wchance(items[itZebra], 20)) + if(cwt.at->land == laZebra && cwt.at->wall == waNone && wchance_in(laZebra, items[itZebra], 20)) wanderingZebra(cwt.at); bool smallbounded_generation = smallbounded || (closed_manifold && specialland == laClearing); @@ -370,6 +375,8 @@ EX void wandering() { cell *c = dcal[i]; if(!valid(c)) continue; if(isPlayerOn(c)) break; + + auto wchance = [c] (int a, int of, int reduction = 0) { return wchance_in(c->land, a, of, reduction); }; if(specialland == laStorms) { // place the sandstone wall completely randomly (but not on the player) diff --git a/yendor.cpp b/yendor.cpp index 354418cb..ad987a2c 100644 --- a/yendor.cpp +++ b/yendor.cpp @@ -986,6 +986,16 @@ void save_mode_data(hstream& f) { f.write(bow::weapon); f.write(bow::style); } + if(use_custom_land_list) { + f.write(3); + f.write(landtypes); + for(int i=0; i(custom_land_list[i]); + f.write(custom_land_treasure[i]); + f.write(custom_land_difficulty[i]); + f.write(custom_land_wandering[i]); + } + } } EX modecode_t modecode(int mode) {