From 0381dc8c8419b1ecfa604f7d258644aa9c4a9ca4 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Mon, 7 Jul 2025 14:00:11 +0200 Subject: [PATCH] new Crossroads variant: thematic crossroads --- bigstuff.cpp | 4 ++ celldrawer.cpp | 3 +- classes.cpp | 3 ++ complex.cpp | 5 +- content.cpp | 24 +++++++++ help.cpp | 7 +++ hyper.h | 2 + landgen.cpp | 20 +++++-- landlock.cpp | 142 +++++++++++++++++++++++++++++++++++++++++++++++-- orbgen.cpp | 8 +++ sound.cpp | 1 + system.cpp | 1 + 12 files changed, 211 insertions(+), 9 deletions(-) diff --git a/bigstuff.cpp b/bigstuff.cpp index 44e8a01b..d66f698c 100644 --- a/bigstuff.cpp +++ b/bigstuff.cpp @@ -1566,6 +1566,7 @@ EX int wallchance(cell *c, bool deepOcean) { l == laCrossroads4 ? 5000 : l == laCrossroads6 ? 5000 : l == laMasterCrossroads ? 10000 : + isThematic(l) ? 10000 : (l == laMirror && !yendor::generating) ? 2500 : tactic::on ? 0 : racing::on ? 0 : @@ -1829,6 +1830,9 @@ EX void build_walls(cell *c, cell *from) { return; } + else if(good_for_wall(c) && ls::any_wall() && isThematic(c->land) && hrand(10000) < 1000 && !c->master->alt && !racing::on && + buildBarrierNowall(c, getNewThematic(c->land))) ; + else if(good_for_wall(c) && ls::any_wall() && c->land == laCrossroads4 && hrand(10000) < 7000 && c->land && !c->master->alt && !tactic::on && !racing::on && buildBarrierNowall(c, getNewLand(laCrossroads4))) ; diff --git a/celldrawer.cpp b/celldrawer.cpp index d397970d..d6b73e25 100644 --- a/celldrawer.cpp +++ b/celldrawer.cpp @@ -190,6 +190,7 @@ void celldrawer::setcolors() { case laMotion: case laGraveyard: case laWineyard: case laLivefjord: case laRlyeh: case laHell: case laCrossroads: case laJungle: case laAlchemist: case laFrog: case laCursed: case laDice: + case laThematic: case laThematicNature: case laThematicUrban: case laThematicDeath: case laThematicAbstract: case laThematicWater: case laThematicEarth: fcol = floorcolors[c->land]; break; case laCA: @@ -443,7 +444,7 @@ void celldrawer::setcolors() { break; } - case laIce: case laCocytus: case laBlizzard: case laEclectic: + case laIce: case laCocytus: case laBlizzard: case laEclectic: case laThematicHeat: if(useHeatColoring(c)) { float h = HEAT(c); eLand l = c->land; diff --git a/classes.cpp b/classes.cpp index 3d8ac29f..79326431 100644 --- a/classes.cpp +++ b/classes.cpp @@ -420,6 +420,9 @@ const char *ruindesc = const char *rock_description = "Shoot the Space Rocks for score. Large Rocks will split into two smaller rocks."; +const char *thematic_crossroads = + "The Thematic Crossroads has various sections that connect to lands with specific theme."; + #if HDR enum eSlimegroup { sgNone, sgCave, sgWater, sgFloorA, sgFloorB, sgVine, sgTree }; #endif diff --git a/complex.cpp b/complex.cpp index 606b5a51..56f31cf4 100644 --- a/complex.cpp +++ b/complex.cpp @@ -2064,6 +2064,7 @@ EX namespace heat { EX double absheat(cell *c) { if(c->land == laCocytus) return HEAT(c) -.6; if(c->land == laIce || c->land == laBlizzard || c->land == laEclectic) return HEAT(c) -.4; + if(c->land == laThematicHeat) return HEAT(c) - 0.2; return 0; } @@ -2167,6 +2168,8 @@ EX namespace heat { hmod += xrate*.1; if(ct->land == laVolcano) hmod += xrate * (ct->wall == waMagma ? .4 : .2); + else if(c->land == laThematicHeat && !isThematic(ct->land) && !isIcyLand(ct->land) && ct->land != laBarrier) + hmod += xrate * .3; } for(cell* ct: ls) { @@ -2215,7 +2218,7 @@ EX namespace heat { addMessage(XLAT("%The1 melts away!", c->monst)); fallMonster(c); } - if(c->wall == waIcewall && HEAT(c) > .4) + if(c->wall == waIcewall && HEAT(c) > (c->land == laThematicHeat ? .2 : .4)) drawParticles(c, MELTCOLOR, 4, 60), c->wall = waNone, kills[0]++; if(c->wall == waFrozenLake && HEAT(c) > (c->land == laCocytus ? .6 : .4)) diff --git a/content.cpp b/content.cpp index 33826843..d6569b02 100644 --- a/content.cpp +++ b/content.cpp @@ -1754,6 +1754,30 @@ LAND( 0x7030A0, "Crossroads VI", laCrossroads6, ZERO, itHyperstone, RESERVED, "A NATIVE(0) REQ(ITEMS(itCursed, 5)) +LAND( 0xC0C0C0, "Thematic Crossroads", laThematic, ZERO, itHyperstone, RESERVED, thematic_crossroads) + NATIVE(0) REQ(GOLD(R400)) + +LAND( 0x60C060, "Thematic Crossroads (Nature)", laThematicNature, ZERO, itHyperstone, RESERVED, thematic_crossroads) + NATIVE(0) REQ(GOLD(R400)) + +LAND( 0x60C0C0, "Thematic Crossroads (Urban)", laThematicUrban, ZERO, itHyperstone, RESERVED, thematic_crossroads) + NATIVE(0) REQ(GOLD(R400)) + +LAND( 0x606060, "Thematic Crossroads (Death)", laThematicDeath, ZERO, itHyperstone, RESERVED, thematic_crossroads) + NATIVE(0) REQ(GOLD(R400)) + +LAND( 0xC060C0, "Thematic Crossroads (Abstract)", laThematicAbstract, ZERO, itHyperstone, RESERVED, thematic_crossroads) + NATIVE(0) REQ(GOLD(R400)) + +LAND( 0x6060C0, "Thematic Crossroads (Water)", laThematicWater, ZERO, itHyperstone, RESERVED, thematic_crossroads) + NATIVE(0) REQ(GOLD(R400)) + +LAND( 0x1030F0, "Thematic Crossroads (Fire/Ice)", laThematicHeat, LF_ICY, itHyperstone, RESERVED, thematic_crossroads) + NATIVE(0) REQ(GOLD(R400)) + +LAND( 0x505050, "Thematic Crossroads (Earth/Air)", laThematicEarth, ZERO, itHyperstone, RESERVED, thematic_crossroads) + NATIVE(0) REQ(GOLD(R400)) + LAND( 0xC0B090, "Master Crossroads", laMasterCrossroads, ZERO, itHyperstone, RESERVED, "A crossroads that connects only to other crossroads.") NATIVE(0) REQ(GOLD(R500)) diff --git a/help.cpp b/help.cpp index fb99625a..95908c67 100644 --- a/help.cpp +++ b/help.cpp @@ -855,6 +855,13 @@ EX string generateHelpForLand(eLand l) { } #endif + if(isThematic(l)) { + s += XLAT("\n\nThe following lands connect to this part: "); + + int qty = 0; + for(eLand l1: land_over) if(isLandIngame(l1) && which_thematic(l1) == l) { if(qty) s += ", "; qty++; s += XLATN(linf[l1].name); } + } + return s; } diff --git a/hyper.h b/hyper.h index 30884df0..e479f0be 100644 --- a/hyper.h +++ b/hyper.h @@ -469,6 +469,8 @@ extern videopar vid; #define R200 (big_unlock ? 800 : 200) // Crossroads V #define R300 (big_unlock ? 1200 : 300) +// Thematic Crossroads +#define R400 (big_unlock ? 1600 : 400) // Master Crossroads #define R500 (big_unlock ? 2000 : 500) // kill types for Dragon Chasms diff --git a/landgen.cpp b/landgen.cpp index c375a78e..e61ef343 100644 --- a/landgen.cpp +++ b/landgen.cpp @@ -2521,13 +2521,27 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { case laCrossroads5: case laCrossroads6: case laMasterCrossroads: + + case laThematic: case laThematicNature: case laThematicUrban: case laThematicDeath: case laThematicAbstract: case laThematicWater: case laThematicEarth: case laThematicHeat: if(c->wall == waTower) c->land = laCamelot; ONEMPTY { + if(isThematic(c->land) && hrand(100) < 10) switch(c->land) { + case laThematicNature: c->wall = pick(waSmallTree, waBigTree); break; + case laThematicUrban: c->wall = pick(waRuinWall, waPalace); break; + case laThematicDeath: c->wall = pick(waAncientGrave, waFreshGrave); break; + case laThematicAbstract: c->wall = pick(waFloorA, waFloorB, waTrapdoor); break; + case laThematicWater: { c->wall = waShallow; forCellCM(c1, c) if(c1->land == c->land) c1->wall = waShallow; } break; + case laThematicEarth: c->wall = waStone; break; + case laThematicHeat: c->wall = pick(waBonfireOff, waIcewall); break; + default: ; + } + int them_abstract = c->land == laThematicAbstract ? 3 : isThematic(c->land) ? 0 : 1; + if(!BITRUNCATED && c->land == laCrossroads5 && hrand(100) < 60) c->wall = waBarrier; else if(!inv::on && items[itShard] >= 10 && hrand(8000) < 120*orbcrossfun(items[itShard]) && mirror::build(c)) ; - else if(hyperstonesUnlocked() && !racing::on && hrand(8000) < 100 && mirror::build(c)) ; - else if(tactic::on && isCrossroads(specialland) && hrand(8000) < 120 && mirror::build(c)) ; + else if(hyperstonesUnlocked() && !racing::on && hrand(8000) < 100 * them_abstract && mirror::build(c)) ; + else if(tactic::on && isCrossroads(specialland) && hrand(8000) < 120 * them_abstract && mirror::build(c)) ; else if(c->land == laCrossroads4 && hrand(24000) < 10 && tactic::on) c->wall = waRose; else { @@ -2863,7 +2877,7 @@ EX void repairLandgen(cell *c) { if(c->land == laAlchemist && c->wall != waFloorA && c->wall != waFloorB) c->wall = waFloorA; - if(c->wall == waIcewall && !among(c->land, laIce, laCocytus, laBlizzard, laEclectic)) + if(c->wall == waIcewall && !among(c->land, laIce, laCocytus, laBlizzard, laEclectic, laThematicHeat)) c->wall = waNone; if(c->wall == waRed3 && c->land != laRedRock && c->land != laSnakeNest && c->land != laBrownian && c->land != laEclectic) diff --git a/landlock.cpp b/landlock.cpp index 1213bd74..c1ccdd2b 100644 --- a/landlock.cpp +++ b/landlock.cpp @@ -53,7 +53,7 @@ EX int landMultiplier(eLand l) { } EX bool isCrossroads(eLand l) { - return among(l, laCrossroads, laCrossroads2, laCrossroads3, laCrossroads4, laCrossroads5, laCrossroads6, laMasterCrossroads); + return among(l, laCrossroads, laCrossroads2, laCrossroads3, laCrossroads4, laCrossroads5, laCrossroads6, laMasterCrossroads) || isThematic(l); } EX bool isCrossroadsNM(eLand l) { @@ -61,7 +61,7 @@ EX bool isCrossroadsNM(eLand l) { } EX bool bearsCamelot(eLand l) { - return isCrossroads(l) && !among(l, laCrossroads2, laCrossroads5, laMasterCrossroads); + return isCrossroads(l) && !among(l, laCrossroads2, laCrossroads5, laMasterCrossroads) && (l == laThematicUrban || !isThematic(l)); } EX bool inmirror(const cellwalker& cw) { @@ -338,6 +338,9 @@ EX bool voronoi_sea_incompatible(eLand l1, eLand l2) { EX bool incompatible1(eLand l1, eLand l2) { if(l1 == laMasterCrossroads && !isCrossroads(l2)) return true; + + if(isThematic(l1) && which_thematic(l2) != l1) return true; + if(isCrossroadsNM(l1) && isCrossroadsNM(l2)) return true; if(l1 == laJungle && l2 == laMotion) return true; if(l1 == laMirrorOld && l2 == laMotion) return true; @@ -358,6 +361,7 @@ EX bool incompatible1(eLand l1, eLand l2) { if(l1 == laWarpSea && l2 == laKraken) return true; if(l1 == laPrairie && l2 == laCrossroads3) return true; if(l1 == laPrairie && l2 == laCrossroads4) return true; + if(l1 == laPrairie && l2 == laCrossroads6) return true; if(l1 == laWet && l2 == laDesert) return true; if(l1 == laFrog && l2 == laMotion) return true; if(l1 == laBull && l2 == laTerracotta) return true; @@ -459,6 +463,14 @@ EX bool all_unlocked = false; EX vector cheatdest_list; +EX eLand getNewThematic(eLand l) { + for(int it=0; it<100; it++) { + eLand l1 = pick(laThematicNature, laThematicUrban, laThematicDeath, laThematicAbstract, laThematicHeat, laThematicWater, laThematicEarth); + if(l1 != l) return l1; + } + return l; + } + EX eLand getNewLand(eLand old) { #if CAP_LEGACY @@ -571,7 +583,8 @@ EX eLand getNewLand(eLand old) { laDeadCaves, laRedRock, laVariant, laHell, laCocytus, laPower, laBull, laTerracotta, laRose, laGraveyard, laHive, laDragon, laTrollheim, laWet, laFrog, laEclectic, laCursed, laDice, - laCrossroads5, laCrossroads6, laMasterCrossroads + laCrossroads5, laCrossroads6, laMasterCrossroads, + laThematicNature, laThematicUrban, laThematicDeath, laThematicAbstract, laThematicHeat, laThematicWater, laThematicEarth }) if(landUnlocked(l)) tab[cnt++] = l; @@ -742,11 +755,132 @@ EX vector land_over = { laPrairie, laBull, laTerracotta, laRose, laElementalWall, laTrollheim, laHell, laCrossroads3, laCocytus, laPower, laCrossroads4, - laCrossroads5, laCrossroads6, laMasterCrossroads, + laCrossroads5, laCrossroads6, laMasterCrossroads, laThematic, // EXTRA laWildWest, laHalloween, laDual, laSnakeNest, laMagnetic, laCA, laAsteroids }; +EX bool isThematic(eLand l) { + return among(l, laThematic, laThematicNature, laThematicUrban, laThematicDeath, laThematicAbstract, laThematicHeat, laThematicWater, laThematicEarth); + } + +EX eLand which_thematic(eLand l) { + // note: even if some lands are not directly accessible from Thematic, they still affect orb generation in it + switch(l) { + case laJungle: + case laEndorian: + case laWineyard: + case laOvergrown: + case laFrog: + case laRose: + case laDryForest: + case laPrairie: + case laBull: + case laMountain: // not via crossroads + case laClearing: // not via crossroads + case laTortoise: // not via crossroads + return laThematicNature; + case laAlchemist: + case laMotion: + case laMirror: + case laMirrorOld: + case laMinefield: + case laSwitch: + case laReptile: + case laZebra: + case laDice: + case laWestWall: + case laHalloween: // not standard + case laDual: // not standard + return laThematicAbstract; + case laIvoryTower: + case laPalace: + case laDungeon: + case laRuins: + case laEclectic: + case laRlyeh: + case laVariant: + case laCamelot: // in + case laTemple: // not via crossroads + case laPrincessQuest: // not via crossroads + case laWildWest: // not standard + return laThematicUrban; + case laHunting: + case laGraveyard: + case laBurial: + case laCursed: + case laDeadCaves: + case laTerracotta: + case laHive: + case laHaunted: case laHauntedWall: case laHauntedBorder: // not via crossroads + return laThematicDeath; + case laIce: + case laVolcano: + case laDragon: + case laHell: + case laCocytus: + case laPower: + case laBlizzard: + case laEFire: + return laThematicHeat; + case laNone: + case laBarrier: + case laOceanWall: + case laCA: + case laCanvas: + case laElementalWall: + case laMercuryRiver: + case laMirrorWall: + case laMirrorWall2: + case laMirrored: + case laMirrored2: + case laMemory: + case landtypes: + return laNone; + case laCrossroads: + case laCrossroads2: + case laCrossroads3: + case laCrossroads4: + case laCrossroads5: + case laCrossroads6: + case laMasterCrossroads: + case laThematic: + case laThematicAbstract: + case laThematicNature: + case laThematicUrban: + case laThematicDeath: + case laThematicHeat: + case laThematicWater: + case laThematicEarth: + case laMagnetic: + case laAsteroids: + return laThematic; + case laOcean: + case laDocks: + case laWet: + case laWarpCoast: case laWarpSea: + case laLivefjord: + case laKraken: // not direct + case laCaribbean: // not direct + case laBrownian: // not direct + case laWhirlpool: // not direct + case laEWater: + return laThematicWater; + case laRedRock: + case laDesert: + case laCaves: + case laStorms: + case laWhirlwind: + case laEmerald: + case laEEarth: + case laEAir: + case laTrollheim: + case laSnakeNest: // not standard + return laThematicEarth; + } + return laThematic; + } + EX vector landlist; #if HDR diff --git a/orbgen.cpp b/orbgen.cpp index 2f49055d..0da71a25 100644 --- a/orbgen.cpp +++ b/orbgen.cpp @@ -599,8 +599,16 @@ EX void placeCrossroadOrbs(cell *c) { int mul = c->land == laCrossroads5 ? 10 : 1; int gch = oi.gchance; if(!inv::on) gch /= orbcrossfun(treas); else gch /= 2; + + if(isThematic(c->land)) { + bool local = which_thematic(oi.l) == c->land; + if(!local) continue; + mul *= 7; + } + if(hrand(gch) >= mul) continue; if(hrand(50+items[itHyperstone]) >= 50) continue; + c->item = oi.orb; if(oi.orb == itOrbWater && c->land != laOcean) c->wall = waStrandedBoat; } diff --git a/sound.cpp b/sound.cpp index 5c6aa4a2..bcd2b3c4 100644 --- a/sound.cpp +++ b/sound.cpp @@ -32,6 +32,7 @@ EX eLand getCurrentLandForMusic() { if(isHaunted(id)) id = laHaunted; if(id == laWarpSea) id = laWarpCoast; if(id == laMercuryRiver) id = laTerracotta; + if(isThematic(id)) id = laThematic; return id; } diff --git a/system.cpp b/system.cpp index 3cb269a7..4f1152d4 100644 --- a/system.cpp +++ b/system.cpp @@ -241,6 +241,7 @@ EX void initgame() { splitrocks = 0; if(firstland == laElementalWall) cwt.at->land = randomElementalLand(); + if(firstland == laThematic) cwt.at->land = getNewThematic(firstland); resetview(); createMov(cwt.at, 0);