1 // Hyperbolic Rogue -- Orb Strategy Mode 2 // Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details 3 4 /** \file inventory.cpp 5 * \brief Orb Strategy Mode 6 */ 7 8 #include "hyper.h" 9 namespace hr { 10 11 /** \brief Implementation of the Orb Strategy Mode. 12 * 13 * The most important functions called outside is hr::inv::show(). 14 */ 15 EX namespace inv { 16 17 #if CAP_INV 18 /** \brief is the Orb Strategy Mode active? */ 19 EX bool on; 20 /** \brief the number of Orbs used up in each type */ 21 EX array<int, ittypes> usedup; 22 /** \brief the number of Orbs remaining in each type -- it is recalculated based on your treasure and hr::inv::usedup after every move */ 23 EX array<int, ittypes> remaining; 24 /** \brief extra orbs can be added to OSM using -IX commandline option */ 25 EX array<int, ittypes> extra_orbs; 26 27 /** \brief random seed used for hr::inv::invr */ 28 EX int rseed; 29 /** \brief have we used any 'forbidden' orbs? */ 30 EX bool usedForbidden; 31 32 /** \brief initialize the OSM data for a new game */ init()33 EX void init() { 34 rseed = hrandpos(); 35 usedForbidden = false; 36 for(int i=0; i<ittypes; i++) usedup[i] = 0; 37 } 38 39 static const int MIRRORED = 1000; 40 static const int TESTMIRRORED = 900; 41 42 struct lateextraorb { 43 eItem treasure; 44 eItem orb; 45 }; 46 47 vector<lateextraorb> lateextraorbs = { 48 {itPower, itOrbFlash}, 49 {itPower, itOrbSpeed}, 50 {itPower, itOrbAether}, 51 {itPower, itOrbWinter}, 52 53 {itTrollEgg, itOrbFish}, 54 {itTrollEgg, itOrbStunning}, 55 {itTrollEgg, itOrbLuck}, 56 {itTrollEgg, itOrbLife}, 57 {itTrollEgg, itOrbDigging}, 58 {itTrollEgg, itOrbSpace}, 59 60 {itFulgurite, itOrbLightning}, 61 {itWindstone, itOrbSpeed}, 62 {itDragon, itOrbDragon}, 63 {itSlime, itOrbFlash}, 64 {itDodeca, itOrbShield}, 65 66 {itGreenGrass, itOrbHorns}, 67 {itGreenGrass, itOrbShield}, 68 {itGreenGrass, itOrbThorns} 69 }; 70 71 /** \brief how many orbs can we get from Orb-of-Mirroring orb */ mirrorqty0(eItem orb)72 int mirrorqty0(eItem orb) { 73 if(shmup::on && isShmupLifeOrb(orb)) 74 return 3; 75 if(orb == itOrbWater) return 10; 76 if(orb == itOrbSummon) return 9; 77 if(orb == itOrbEmpathy) return 9; 78 if(orb == itOrbMatter) return 9; 79 if(orb == itOrbIntensity) return 8; 80 if(orb == itOrbLuck) return 8; 81 if(orb == itOrbSpace) return 7; 82 83 if(orb == itOrbWinter) return 6; 84 if(orb == itOrbLife) return 6; 85 if(orb == itOrbLove) return 6; 86 if(orb == itOrbRecall) return 6; 87 if(orb == itOrbDigging) return 6; 88 if(orb == itOrbGravity) return 6; 89 if(orb == itOrbImpact) return 6; 90 91 if(orb == itOrbTime) return 5; 92 if(orb == itOrbAir) return 5; 93 if(orb == itOrbFish) return 5; 94 if(orb == itOrbStunning) return 5; 95 if(orb == itOrbUndeath) return 5; 96 if(orb == itOrb37) return 5; 97 if(orb == itOrbDomination) return 5; 98 if(orb == itOrbBull) return 5; 99 if(orb == itOrbHorns) return 5; 100 101 if(orb == itOrbAether) return 4; 102 if(orb == itOrbInvis) return 4; 103 if(orb == itOrbFire) return 4; 104 if(orb == itOrbDragon) return 4; 105 if(orb == itOrbIllusion) return 4; 106 if(orb == itOrbDiscord) return 4; 107 if(orb == itOrbBeauty) return 4; 108 109 if(orb == itOrbMirror) return 1; 110 return 3; 111 } 112 mirrorqty(eItem orb)113 int mirrorqty(eItem orb) { 114 if(orb == itOrbMirror) return 1; 115 return int(mirrorqty0(orb) * sqrt(1.000001+items[itPower]/20.)); 116 } 117 118 /** \brief PRNG used for calculating how many Orbs you get for your collected treasure */ 119 std::mt19937 invr; 120 121 /** \brief initialize hr::inv::invr */ sirand(int i)122 void sirand(int i) { 123 invr.seed(i); 124 } 125 126 /** \brief get the next random value from hr::inv::invr */ irand(int i)127 int irand(int i) { 128 return invr() % i; 129 } 130 131 EX eItem whichorbinfo; 132 EX string orbinfoline, extra; 133 extraline(eItem it,string s)134 string extraline(eItem it, string s) { 135 return " "+XLAT1(iinf[it].name) + " ("+s+")"; 136 } 137 gainOrbs(eItem it,eItem o)138 void gainOrbs(eItem it, eItem o) { 139 int qty = items[it]; 140 if(it == itHolyGrail) { 141 remaining[itOrbIllusion] += qty; 142 if(it == itOrbIllusion) { 143 orbinfoline += XLAT("Unlocked by: %1 in %2", it, landof(it)); 144 orbinfoline += XLAT(" (next at %1)", its(qty+1)); 145 } 146 } 147 else { 148 bool nextfound = false; 149 int fst = (ls::any_chaos() ? 5 : 10); 150 if(qty >= fst) remaining[o]++; 151 else { 152 if(whichorbinfo == o) { 153 if(it == itHyperstone) { 154 extra += extraline(it, its(fst)); 155 } 156 else { 157 orbinfoline += XLAT("Unlocked by: %1 in %2", it, landof(it)); 158 orbinfoline += XLAT(" (next at %1)", its(10)); 159 } 160 } 161 nextfound = true; 162 } 163 int last = fst; 164 for(int k=0; k<30 || !nextfound; k++) { 165 int maxstep = ls::any_chaos() ? 10 + 2 * k : 15 + 5 * k; 166 if(o == itOrbMirror) 167 maxstep += 5 * (k-1) * (k-2); 168 else 169 maxstep += (k-1) * (k-2); 170 int xnext; 171 if(k >= 30 || o == itOrbMirror) { 172 xnext = last + maxstep/2; last = xnext-1; 173 maxstep = 1; 174 } 175 else 176 xnext = last + 1 + irand(maxstep); 177 if(xnext > qty && !nextfound) { 178 if(whichorbinfo == o) { 179 if(it == itHyperstone) { 180 extra += extraline(it, its(last+maxstep)); 181 } 182 else { 183 orbinfoline += XLAT("Unlocked by: %1 in %2", it, landof(it)); 184 if(maxstep == 1) 185 orbinfoline += XLAT(" (next at %1)", its(last+1)); 186 else 187 orbinfoline += XLAT(" (next at %1 to %2)", its(last+1), its(last + maxstep)); 188 } 189 } 190 nextfound = true; 191 } 192 if(xnext <= qty) remaining[o]++; 193 last = xnext; 194 } 195 } 196 } 197 nextp2(int i)198 int nextp2(int i) { 199 int z = 1; 200 while(z <= i) z <<= 1; 201 return z; 202 } 203 gainMirrors(eItem forwhich)204 void gainMirrors(eItem forwhich) { 205 int qtl = items[forwhich]; 206 while(qtl > 0) qtl >>= 1, remaining[itOrbMirror]++; 207 if(whichorbinfo == itOrbMirror) 208 extra += extraline(forwhich, its(nextp2(items[forwhich]))); 209 } 210 211 vector<eItem> offensiveOrbs = { 212 itOrbFlash, itOrbLightning, itOrbPsi, itOrbThorns, 213 itOrbFreedom, itOrbSword, itOrbSword2, 214 itOrbHorns, itOrbDragon, itOrbStunning 215 }; 216 217 vector<eItem> elementalOrbs = {itOrbFire, itOrbWater, itOrbDigging, itOrbAir}; 218 219 vector<eItem> demonicOrbs = {itOrbFire, itOrbHorns, itOrbSummon}; 220 isIn(eItem o,vector<eItem> & l)221 bool isIn(eItem o, vector<eItem>& l) { 222 for(auto it: l) if(it == o) return true; 223 return false; 224 } 225 gainRandomOrbs(vector<eItem> orblist,eItem which,int each,int reduce)226 void gainRandomOrbs(vector<eItem> orblist, eItem which, int each, int reduce) { 227 const int qoff = isize(orblist); 228 for(int i=1; i<qoff; i++) swap(orblist[i], orblist[irand(1+i)]); 229 for(int i=0; i<20; i++) { 230 int nextat = (i+1)*each + reduce; 231 if(items[which] >= nextat) { 232 remaining[orblist[i%qoff]]++; 233 } 234 else { 235 if(isIn(whichorbinfo, orblist)) 236 extra += extraline(which, its(nextat) + "?"); 237 break; 238 } 239 } 240 } 241 gainGuestOrbs()242 void gainGuestOrbs() { 243 for(auto& oi: orbinfos) { 244 if(oi.flags & orbgenflags::OSM_AT10) { 245 eItem it = treasureType(oi.l); 246 int fst = ls::any_chaos() ? 5 : 10; 247 if(items[it] >= fst) { 248 remaining[oi.orb]++; 249 } 250 if(whichorbinfo == oi.orb) extra += extraline(it, its(fst)); 251 } 252 } 253 } 254 gainLove()255 void gainLove() { 256 if(princess::reviveAt) { 257 remaining[itOrbLove]++; 258 int s = gold(NO_LOVE); 259 int last = princess::reviveAt; 260 for(int k=0;; k++) { 261 int nextstep = 50 + 20 * k; 262 last += nextstep; 263 if(last > s) { 264 if(whichorbinfo == itOrbLove) { 265 orbinfoline += XLAT("Unlocked by: %1 in %2", itSavedPrincess, laPrincessQuest); 266 orbinfoline += XLAT(" (next at %1)", its(last)); 267 } 268 break; 269 } 270 else { last += nextstep; remaining[itOrbLove]++; } 271 } 272 } 273 } 274 gainLate(eItem tr,eItem orb)275 void gainLate(eItem tr, eItem orb) { 276 int at = 10 + irand(41); 277 int itr = items[tr]; 278 if(itr >= at) remaining[orb]++; 279 if(whichorbinfo == orb) 280 extra += extraline(tr, itr >= at ? (its(at)+"!") : "10-50"); 281 } 282 283 /** \brief Compute how many orbs you get for your current treasure. This is called after every move, and should give consistent results */ compute()284 EX void compute() { 285 extra = ""; 286 orbinfoline = ""; 287 288 for(int i=0; i<ittypes; i++) remaining[i] = extra_orbs[i]-usedup[i]; 289 for(int i=0; i<ittypes; i++) if(usedup[i] >= TESTMIRRORED) { 290 remaining[i] += MIRRORED; 291 remaining[i] -= mirrorqty0(eItem(i)); 292 remaining[i] += mirrorqty(eItem(i)); 293 } 294 295 sirand(rseed); 296 297 gainGuestOrbs(); 298 299 gainOrbs(itShard, itOrbMirror); 300 gainOrbs(itHyperstone, itOrbMirror); 301 gainOrbs(itDiamond, itOrbFlash); 302 gainOrbs(itGold, itOrbLife); 303 gainOrbs(itSpice, itOrbShield); 304 gainOrbs(itRuby, itOrbLightning); 305 gainOrbs(itElixir, itOrbSpeed); 306 gainOrbs(itBone, itGreenStone); 307 gainOrbs(itHell, itOrbYendor); 308 gainOrbs(itStatue, itOrbTeleport); 309 gainOrbs(itFeather, itOrbSafety); 310 gainOrbs(itSapphire, itOrbMorph); 311 gainOrbs(itFernFlower, itOrbThorns); 312 gainOrbs(itWine, itOrbAether); 313 gainOrbs(itSilver, itOrbDigging); 314 gainOrbs(itRoyalJelly, itOrbInvis); 315 gainOrbs(itEmerald, itOrbPsi); 316 gainOrbs(itPower, itOrbFire); 317 gainOrbs(itHolyGrail, itOrbIllusion); 318 gainOrbs(itGrimoire, itOrbDragon); 319 gainOrbs(itPirate, itOrbTime); 320 gainOrbs(itRedGem, itOrbSpace); 321 gainOrbs(itBombEgg, itOrbFriend); 322 gainOrbs(itCoast, itOrbEmpathy); 323 gainOrbs(itWhirlpool, itOrbWater); 324 gainOrbs(itPalace, itOrbDiscord); 325 gainOrbs(itFjord, itOrbFish); 326 gainOrbs(itSavedPrincess, itOrbLove); 327 gainOrbs(itIvory, itOrbMatter); 328 gainOrbs(itZebra, itOrbFrog); 329 gainOrbs(itElemental, itOrbSummon); 330 gainOrbs(itFulgurite, itOrbStunning); 331 gainOrbs(itMutant, itOrbWoods); 332 gainOrbs(itMutant2, itOrbFreedom); 333 gainOrbs(itLotus, itOrbUndeath); 334 gainOrbs(itWindstone, itOrbAir); 335 gainOrbs(itRose, itOrbBeauty); 336 gainOrbs(itCoral, itOrb37); 337 gainOrbs(itBabyTortoise, itOrbShell); 338 gainOrbs(itApple, itOrbEnergy); 339 gainOrbs(itDragon, itOrbDomination); 340 gainOrbs(itKraken, itOrbSword); 341 gainOrbs(itBarrow, itOrbSword2); 342 gainOrbs(itTrollEgg, itOrbStone); 343 gainOrbs(itSlime, itOrbRecall); 344 gainOrbs(itAmethyst, itOrbNature); 345 gainOrbs(itDodeca, itOrbDash); 346 gainOrbs(itGreenGrass, itOrbBull); 347 gainOrbs(itBull, itOrbHorns); 348 if(items[itOrbYendor]) remaining[itOrbMirror]++; 349 gainMirrors(itOrbYendor); 350 gainMirrors(itHolyGrail); 351 gainLove(); 352 gainRandomOrbs(offensiveOrbs, itBone, 25, 0); 353 gainRandomOrbs(elementalOrbs, itElemental, 12, 0); 354 gainRandomOrbs(demonicOrbs, itHell, 20, 100); 355 gainOrbs(itLavaLily, itOrbLava); 356 gainOrbs(itHunting, itOrbSide3); 357 gainOrbs(itBlizzard, itOrbWinter); 358 gainOrbs(itTerra, itOrbSide1); 359 360 for(auto& it: lateextraorbs) gainLate(it.treasure, it.orb); 361 362 gainOrbs(itGlowCrystal, itOrbSide2); 363 gainOrbs(itSwitch, itOrbPhasing); 364 gainOrbs(itMagnet, itOrbMagnetism); 365 gainOrbs(itRuins, itOrbSlaying); 366 367 gainOrbs(itWest, itOrbGravity); 368 gainOrbs(itVarTreasure, itOrbIntensity); 369 gainOrbs(itBrownian, itOrbChoice); 370 371 gainOrbs(itFrog, itOrbImpact); 372 gainOrbs(itWet, itOrbPlague); 373 gainOrbs(itEclectic, itOrbChaos); 374 375 gainOrbs(itCursed, itOrbPurity); 376 gainOrbs(itDice, itOrbLuck); 377 378 #if CAP_DAILY 379 daily::gifts(); 380 #endif 381 382 if(items[itOrbLove] && !items[itSavedPrincess]) items[itSavedPrincess] = 1; 383 384 int& r = remaining[itGreenStone]; 385 386 if(items[itBone] >= 0) { 387 for(int i=0; i<ittypes; i++) if(i != itGreenStone) { 388 r += usedup[i]; 389 if(usedup[i] >= TESTMIRRORED) r -= (MIRRORED - mirrorqty0(eItem(i))); 390 } 391 } 392 393 items[itGreenStone] += r; 394 usedup[itGreenStone] += r; 395 r = 0; 396 397 if(shmup::on) for(int i=0; i<ittypes; i++) { 398 if(remaining[i] && isShmupLifeOrb(eItem(i))) { 399 gainLife(); 400 remaining[i]--; 401 usedup[i]++; 402 } 403 } 404 405 items[itInventory] = 0; 406 for(int i=0; i<ittypes; i++) 407 if(i != itGreenStone && i != itOrbYendor) 408 items[itInventory] += remaining[i]; 409 } 410 411 map<char, eItem> orbmap; 412 string orbkeys = "zfwplSetsTaMIYgCcPOWAFydLGRUkouE.,bVNxDjJZnrvhBm!23456789@#$%()"; 413 414 typedef pair<int, int> pxy; 415 vector<pxy> orbcoord; 416 sq(pxy p)417 int sq(pxy p) { 418 int zz = (2*p.first+p.second)*(2*p.first+p.second) + 3*p.second*p.second; 419 zz *= 20; zz += abs(p.second); 420 zz *= 20; zz += abs(p.first); 421 zz *= 4; zz += (p.first>0)*2+(p.second>0); 422 return zz; 423 } 424 425 bool plain; 426 427 eItem which; 428 429 bool mirroring; 430 431 EX const char* helptext = 432 "You are playing in the Orb Strategy Mode. Collecting treasure " 433 "gives you access to magical Orb powers. In this mode, " 434 "unlocking requirements are generally higher, and " 435 "several quests and lands " 436 "give you extremely powerful Orbs of the Mirror.\n"; 437 evokeBeautyAt(cell * c)438 void evokeBeautyAt(cell *c) { 439 forCellEx(c2, c) 440 if(c2->monst && !isFriendly(c2->monst) && !isIvy(c2->monst)) { 441 c2->stuntime += 3; 442 checkStunKill(c2); 443 } 444 } 445 evokeOrb(eItem it)446 void evokeOrb(eItem it) { 447 if(it == itOrbFreedom) 448 for(cell *pc: player_positions()) 449 checkFreedom(pc); 450 451 if(it == itOrbBeauty) { 452 for(cell *pc: player_positions()) 453 evokeBeautyAt(pc); 454 if(items[itOrbEmpathy]) 455 for(cell *c: dcal) if(isFriendly(c->monst)) 456 evokeBeautyAt(c); 457 } 458 459 if(it == itOrbDigging) { 460 forCellCM(c2, cwt.at) { 461 earthFloor(c2); 462 if(c2->wall == waCavewall && !c2->monst) 463 c2->wall = waNone; 464 } 465 } 466 467 if(it == itOrbSword || it == itOrbSword2) { 468 for(int i: player_indices()) { 469 cwt.at = playerpos(i); 470 multi::cpid = i; 471 swordAttackStatic(it == itOrbSword2); 472 } 473 } 474 } 475 osminfo(eItem orb)476 EX string osminfo(eItem orb) { 477 string s = XLAT("Number of uses left: %1", its(remaining[orb])); 478 int us = usedup[orb]; 479 if(us >= TESTMIRRORED) s += XLAT(" (mirrored)"), us = us - MIRRORED + mirrorqty0(orb); 480 if(us) s += XLAT(" (used %1 times)", its(us)); 481 return s; 482 } 483 484 EX bool activating; 485 486 /** \brief show the OSM Orb screen */ show()487 EX void show() { 488 489 multi::cpid = 0; /* just in case */ 490 491 if(remaining[itOrbSword]) items[itOrbSword]++; 492 if(remaining[itOrbSword2]) items[itOrbSword2]++; 493 gamescreen(2); 494 if(remaining[itOrbSword]) items[itOrbSword]--; 495 if(remaining[itOrbSword2]) items[itOrbSword2]--; 496 cmode = sm::CENTER; 497 498 orbcoord.clear(); 499 for(int y=-3; y<=3; y++) for(int x=-5; x<=5; x++) if(x+y<=6 && x+y >= -6 && (x||y)) 500 orbcoord.emplace_back(x,y); 501 sort(orbcoord.begin(), orbcoord.end(), [](pxy p1, pxy p2) { 502 return sq(p1) < sq(p2); }); 503 504 ld rad = min(vid.xres, vid.yres) / 20; 505 ld rad3 = int(rad * sqrt(3)); 506 507 compute(); 508 orbmap.clear(); 509 which = itNone; 510 511 if(plain) dialog::init(mirroring ? XLAT("mirror what?") : XLAT("inventory"), forecolor, 150, 100); 512 513 int j = 0, oc = 6; 514 515 if(1) { 516 flat_model_enabler fme; 517 518 for(int i=0; i<ittypes; i++) { 519 eItem o = eItem(i); 520 if(itemclass(o) == IC_ORB && !(classflag(o) & IF_CURSE)) { 521 char c = orbkeys[j++]; 522 if(c == 0) println(hlog, "missing char for ", dnameof(o)); 523 if(remaining[i] || usedup[i]) { 524 orbmap[c] = o; 525 if(plain) 526 dialog::addSelItem(XLAT1(iinf[o].name), its(remaining[i]), c); 527 else { 528 if(oc >= isize(orbcoord)) { 529 println(hlog, "error: oc=", oc, " with only ", isize(orbcoord), " positions"); 530 continue; 531 } 532 auto pos = orbcoord[oc++]; 533 ld px = current_display->xcenter + 2*rad*pos.first + rad*pos.second; 534 ld py = current_display->ycenter + pos.second * rad3; 535 int icol = iinf[o].color; 536 if(!remaining[i]) icol = gradient(icol, 0, 0, .5, 1); 537 bool gg = graphglyph(false); 538 539 if(!hiliteclick) { 540 if(gg) { 541 initquickqueue(); 542 transmatrix V = atscreenpos(px, py, rad*2); 543 drawItemType(o, NULL, shiftless(V), icol, ticks/3 + i * 137, false); 544 quickqueue(); 545 } 546 547 int tcol = remaining[i] ? darkenedby(icol, 1) : 0; 548 549 if(remaining[i] != 1 || !gg) 550 displaystr(px, py, 2, gg?rad:rad*3/2, remaining[i] <= 0 ? "X" : remaining[i] == 1 ? "o" : its(remaining[i]), tcol, 8); 551 } 552 553 bool b = hypot(mousex-px, mousey-py) < rad; 554 if(b) { 555 getcstat = c, 556 which = o; 557 } 558 } 559 } 560 } 561 } 562 } 563 564 if(plain) { 565 dialog::addBreak(750); 566 dialog::addItem(XLAT("help"), SDLK_F1); 567 dialog::addItem(XLAT("return to the game"), 'i'); 568 dialog::display(); 569 which = orbmap[getcstat]; 570 } 571 else { 572 if(which == itNone) { 573 displaystr(vid.xres/2, vid.fsize*2, 2, vid.fsize*2, XLAT("Which orb to use?"), 0xC0C0C0, 8); 574 } 575 else { 576 int icol = iinf[which].color; 577 displaystr(vid.xres/2, vid.fsize*2, 2, vid.fsize*2, XLAT1(iinf[which].name), icol, 8); 578 579 if(mirroring) 580 displaystr(vid.xres/2, vid.fsize*4, 2, vid.fsize, usedup[which] >= TESTMIRRORED ? XLAT("already mirrored") : XLAT("Uses to gain: %1", its(mirrorqty(which))), icol, 8); 581 else { 582 whichorbinfo = which; 583 compute(); 584 585 displaystr(vid.xres/2, vid.fsize*4, 2, vid.fsize, orbinfoline, icol, 8); 586 587 if(extra != "") 588 displaystr(vid.xres/2, vid.fsize*5, 2, vid.fsize, XLAT("Extras:")+extra, icol, 8); 589 } 590 591 if(remaining[which] != 1 || usedup[which]) { 592 displaystr(vid.xres/2, vid.yres - vid.fsize*6, 2, vid.fsize, osminfo(which), icol, 8); 593 } 594 595 #if !ISMOBILE 596 string hot = XLAT1("Hotkey: "); hot += getcstat; 597 displaystr(vid.xres/2, vid.yres - vid.fsize*5, 2, vid.fsize, hot, icol, 8); 598 #endif 599 600 eLand pl = getPrizeLand(); 601 eOrbLandRelation olr = getOLR(which, pl); 602 603 color_t col = 0; 604 const char *fmsg = NULL; 605 if(olr == olrDangerous) 606 col = 0xC00000, 607 fmsg = "Using %the1 in %the2 sounds dangerous..."; 608 else if(olr == olrUseless) 609 col = 0xC00000, 610 fmsg = "%The1 is mostly useless in %the2..."; 611 else if(olr == olrForbidden) 612 col = 0x804000, 613 fmsg = "%The1 is forbidden in %the2 (disables some achievements)"; 614 615 if(fmsg) 616 displaystr(vid.xres/2, vid.yres - vid.fsize*4, 2, vid.fsize, XLAT(fmsg, which, pl), col, 8); 617 618 } 619 } 620 dialog::displayPageButtons(7, 0); 621 mouseovers = ""; 622 keyhandler = [] (int sym, int uni) { 623 if(plain) dialog::handleNavigation(sym, uni); 624 625 if(orbmap.count(uni)) { 626 eItem orb = orbmap[uni]; 627 if(remaining[orb] <= 0) ; 628 else if(orb == itOrbMirror) { 629 mirroring = !mirroring; 630 // an amusing message 631 if(remaining[itOrbMirror] >= 2 && !mirroring) 632 addMessage(XLAT("You mirror %the1.", orb)); 633 if(mirroring) { 634 bool next = false; 635 forCellEx(c2, cwt.at) if(c2->wall == waMirror || c2->wall == waCloud || c2->wall == waMirrorWall) 636 next = true; 637 if(!next) { 638 addMessage(XLAT("You need to stand next to a magic mirror or cloud to use %the1.", itOrbMirror)); 639 mirroring = false; 640 } 641 } 642 } 643 else if(mirroring) { 644 if(usedup[orb] >= TESTMIRRORED) { 645 addMessage(XLAT("Each orb type can be mirrored only once.")); 646 mirroring = false; 647 } 648 else if(remaining[orb] > 0) { 649 usedup[itOrbMirror]++; 650 usedup[orb] += MIRRORED; 651 usedup[orb] -= mirrorqty0(orb); 652 addMessage(XLAT("You mirror %the1.", orb)); 653 mirroring = false; 654 } 655 else mirroring = false; 656 } 657 else if((isHaunted(cwt.at->land) || cwt.at->land == laDungeon) && orb == itOrbSafety) { 658 addMessage(XLAT("This would only move you deeper into the trap!")); 659 } 660 else { 661 eItem it = cwt.at->item; 662 cwt.at->item = orbmap[uni]; 663 inv::activating = true; 664 collectItem(cwt.at, true); 665 inv::activating = false; 666 addMessage(XLAT("You activate %the1.", orbmap[uni])); 667 if(!cwt.at->item) usedup[orbmap[uni]]++; 668 if(getOLR(it, getPrizeLand()) == olrForbidden) 669 usedForbidden = true; 670 cwt.at->item = it; 671 evokeOrb(orbmap[uni]); 672 checkmove(); 673 popScreenAll(); 674 } 675 } 676 677 else if(uni == '1') plain = !plain; 678 else if(sym == SDLK_F1) 679 gotoHelp(which ? generateHelpForItem(which) : XLAT(helptext)); 680 else if(doexiton(sym, uni)) { 681 if(mirroring) mirroring = false; 682 popScreen(); 683 } 684 }; 685 } 686 687 #if CAP_SAVE applyBox(eItem it)688 EX void applyBox(eItem it) { 689 scores::applyBoxNum(inv::usedup[it], "@inv-" + dnameof(it)); 690 } 691 #endif 692 693 EX int incheck; 694 check(int delta)695 EX void check(int delta) { 696 incheck += delta; 697 for(int i=0; i<ittypes; i++) { 698 eItem it = eItem(i); 699 if(itemclass(it) == IC_ORB) 700 items[it] += delta * remaining[it] * orbcharges(it); 701 } 702 } 703 704 #endif 705 706 #if !CAP_INV 707 EX always_false on, activating; 708 #endif 709 EX } 710 711 } 712