mirror of
				https://github.com/zenorogue/hyperrogue.git
				synced 2025-10-30 13:32:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			754 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			754 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Hyperbolic Rogue - items
 | |
| // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
 | |
| 
 | |
| /** \file item.cpp
 | |
|  *  \brief routines related to items
 | |
|  */
 | |
| 
 | |
| #include "hyper.h"
 | |
| 
 | |
| namespace hr {
 | |
| 
 | |
| EX int currentLocalTreasure;
 | |
| 
 | |
| /** for treasures, the number collected; for orbs, the number of charges */
 | |
| EX array<int, ittypes> items;
 | |
| 
 | |
| EX map<modecode_t, array<int, ittypes> > hiitems;
 | |
| 
 | |
| EX bool pickable_from_water(eItem it) {
 | |
|   return among(it, itOrbFish, itOrbAether);
 | |
|   }
 | |
| 
 | |
| EX bool cannotPickupItem(cell *c, bool telekinesis) {
 | |
|   if(pickable_from_water(c->item) && isWatery(c)) return false;
 | |
|   return itemHidden(c) && !telekinesis && !(isWatery(c) && (markOrb(itOrbFish) || markOrb(itOrbAether)));
 | |
|   }
 | |
| 
 | |
| EX bool canPickupItemWithMagnetism(cell *c, cell *from) {
 | |
|   if(!c->item || c->item == itOrbYendor || isWall(c) || cannotPickupItem(c, false))
 | |
|     return false;
 | |
|   if(c->item == itCompass && from->item) 
 | |
|     return false;
 | |
|   return true;
 | |
|   }
 | |
| 
 | |
| EX bool doPickupItemsWithMagnetism(cell *c) {
 | |
|   /* repulsion works first -- Magnetism will collect the item if not repulsed */
 | |
|   if(items[itCurseRepulsion])
 | |
|     forCellIdEx(c3, i, c) if(c3->item) {
 | |
|       cellwalker cw(c, i);
 | |
|       cw += wstep;
 | |
|       for(int j=1; j<c3->type; j++) {
 | |
|         cell *c4 = (cw+j).peek();
 | |
|         if(!isNeighbor(c, c4) && c3->item && !c4->item && passable(c4, c3, ZERO)) {
 | |
|           changes.ccell(c3);
 | |
|           changes.ccell(c4);
 | |
|           moveItem(c3, c4, false);
 | |
|           animateMovement(match(c3, c4), LAYER_BOAT);
 | |
|           moveEffect(movei(c4, c4, NODIR), moDeadBird);
 | |
|           markOrb(itCurseRepulsion);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|   cell *csaf = NULL;
 | |
|   if(items[itOrbMagnetism])
 | |
|     forCellEx(c3, c) if(canPickupItemWithMagnetism(c3, c)) {      
 | |
|       changes.ccell(c3);
 | |
|       changes.ccell(c);
 | |
|       if(c3->item == itCompass) {
 | |
|         if(!c->item)
 | |
|           moveItem(c3, c, false);
 | |
|         }
 | |
|       else if(c3->item == itOrbSafety || c3->item == itBuggy || c3->item == itBuggy2)
 | |
|         csaf = c3;
 | |
|       else if(markOrb(itOrbMagnetism)) {
 | |
|         eItem it = c3->item;
 | |
|         collectItem(c3, c3, false);
 | |
|         if(!c3->item)
 | |
|           animate_item_throw(c3, c, it);
 | |
|         }
 | |
|       }
 | |
|   if(csaf)
 | |
|     return collectItem(csaf, csaf, false);
 | |
|   return false;
 | |
|   }
 | |
| 
 | |
| EX void pickupMovedItems(cell *c, cell *from) {
 | |
|   if(!c->item) return;
 | |
|   if(c->item == itOrbSafety) return;
 | |
|   if(isPlayerOn(c)) collectItem(c, from, true);
 | |
|   if(items[itOrbMagnetism])
 | |
|     forCellEx(c2, c)
 | |
|       if(isPlayerOn(c2) && canPickupItemWithMagnetism(c, c2)) {
 | |
|         changes.ccell(c2);
 | |
|         changes.ccell(c);
 | |
|         collectItem(c, c2, true);
 | |
|         }
 | |
|   }
 | |
| 
 | |
| EX bool in_lovasz() {
 | |
|   return specialland == laMotion && closed_or_bounded && ls::single() && !daily::on;
 | |
|   }
 | |
| 
 | |
| EX int threshold_met(int i) {
 | |
|   for(int a: {500, 250, 100, 50, 25, 10, 5})
 | |
|     if(i >= a) return a;
 | |
|   return 0;
 | |
|   }
 | |
| 
 | |
| EX bool collectItem(cell *c2, cell *last, bool telekinesis IS(false)) {
 | |
| 
 | |
|   bool dopickup = true;
 | |
|   bool had_choice = false;
 | |
| 
 | |
|   if(shmup::on && !canmove) return false;
 | |
| 
 | |
|   if(cannotPickupItem(c2, telekinesis))
 | |
|     return false;
 | |
| 
 | |
|   if(items[itCurseGluttony] && c2->item) {
 | |
|     addMessage(XLAT("%The1 is consumed!", c2->item));
 | |
|     playSound(c2, "apple");
 | |
|     items[itCurseGluttony] = 0;
 | |
|     c2->item = itNone;
 | |
|     return false;
 | |
|     }
 | |
|   
 | |
|   /* if(c2->item == itHolyGrail && telekinesis)
 | |
|     return false; */
 | |
| 
 | |
|   if(c2->item) {
 | |
|     invismove = false;
 | |
|     if(shmup::on) shmup::visibleFor(2000);
 | |
|     string s0 = "";
 | |
|     
 | |
|     if(c2->item == itPalace && items[c2->item] == 12)
 | |
|       changes.value_set(princess::forceVizier, true);
 | |
|     
 | |
|     if(!cantGetGrimoire(c2, false)) collectMessage(c2, c2->item);
 | |
|     if(c2->item == itDodeca && peace::on) peace::simon::extend();
 | |
|     }
 | |
| 
 | |
|   #if CAP_COMPLEX2
 | |
|   if(c2->land == laHunting && c2->item && !inv::activating) {
 | |
|     ambush::ambush(c2, ambush::size(c2, c2->item));
 | |
|     }
 | |
|   #endif
 | |
|   
 | |
|   if(isRevivalOrb(c2->item) && multi::revive_queue.size()) {
 | |
|     multiRevival(last, c2);
 | |
|     }
 | |
|   else if(isShmupLifeOrb(c2->item) && shmup::on) {
 | |
|     playSound(c2, "pickup-orb"); // TODO summon
 | |
|     gainLife();
 | |
|     }
 | |
|   else if(orbcharges(c2->item)) {
 | |
|     eItem it = c2->item;
 | |
|     if(it == itOrbRecall && !dual::state) {
 | |
|       cellwalker cw2 = cwt;
 | |
|       cw2++;
 | |
|       cw2.at = c2;
 | |
|       saveRecall(cw2);
 | |
|       }
 | |
|     if(it == itOrbFire) playSound(c2, "fire");
 | |
|     else if(it == itOrbFire) playSound(c2, "fire");
 | |
|     else if(it == itOrbWinter) playSound(c2, "pickup-winter");
 | |
|     else if(it == itOrbSpeed) playSound(c2, "pickup-speed");
 | |
|     else if(it == itRevolver) playSound(c2, "pickup-key");
 | |
|     else playSound(c2, "pickup-orb");
 | |
|     had_choice = items[itOrbChoice];
 | |
|     
 | |
|     if(it == itOrbPurity) {
 | |
|       bool no_curses = true;
 | |
|       for(cell *c: dcal) if(c->land == laCursed) no_curses = false;
 | |
|       if(no_curses) {
 | |
|         items[itOrbSpeed] += 5;
 | |
|         items[itOrbWinter] += 5;
 | |
|         items[itOrbSlaying] += 5;
 | |
|         items[itOrbTime] += 5;
 | |
|         items[itOrbMagnetism] += 5;
 | |
|         items[itOrbChoice] += 5;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|     if(had_choice) items[itOrbChoice] = 0;
 | |
| 
 | |
|     int oc = orbcharges(it);
 | |
|     if(dual::state && among(it, itOrbTeleport, itOrbFrog, itOrbPhasing, itOrbDash, itOrbRecall)) {
 | |
|       oc = 10;
 | |
|       it = itOrbSpeed;
 | |
|       }
 | |
|     if(c2->land == laAsteroids) oc = 10;
 | |
|     if(markOrb(itOrbIntensity)) oc = intensify(oc);
 | |
|     if(!items[it]) items[it]++;
 | |
|     items[it] += oc;
 | |
|     }
 | |
|   else if(c2->item == itOrbLife) {
 | |
|     playSound(c2, "pickup-orb"); // TODO summon
 | |
|     placeGolem(last, c2, moGolem);
 | |
|     if(cwt.at->monst == moGolem) cwt.at->stuntime = 0;
 | |
|     }
 | |
|   else if(c2->item == itOrbFriend) {
 | |
|     playSound(c2, "pickup-orb"); // TODO summon
 | |
|     placeGolem(last, c2, moTameBomberbird);
 | |
|     if(cwt.at->monst == moTameBomberbird) cwt.at->stuntime = 0;
 | |
|     }
 | |
| #if CAP_TOUR
 | |
|   else if(tour::on && (c2->item == itOrbSafety || c2->item == itOrbRecall)) {
 | |
|     addMessage(XLAT("This Orb is not compatible with the Tutorial."));
 | |
|     if(changes.on) changes.rollback();
 | |
|     return true;
 | |
|     }
 | |
| #endif
 | |
|   else if(c2->item == itOrbSafety) {
 | |
| 
 | |
|     if(changes.on) {
 | |
|       if(changes.checking) {
 | |
|         changes.rollback();
 | |
|         return true;
 | |
|         }
 | |
|       changes.commit();
 | |
|       }
 | |
| 
 | |
|     playSound(c2, "pickup-orb"); // TODO safety
 | |
|     if(!dual::state) items[c2->item] = 7;
 | |
|     if(shmup::on || multi::players > 1) {
 | |
|       shmup::delayed_safety = true;
 | |
|       shmup::delayed_safety_land = c2->land;
 | |
|       c2->item = itNone;
 | |
|       }
 | |
|     else 
 | |
|       activateSafety(c2->land);
 | |
|     return true;
 | |
|     }
 | |
|   else if(c2->item == itBabyTortoise) {
 | |
|     using namespace tortoise;
 | |
|     int bnew = babymap[c2];
 | |
|     changes.map_value(babymap, c2);
 | |
|     babymap.erase(c2);
 | |
|     int bold = seekbits;
 | |
|     changes.value_set(tortoise::seekbits, bnew);
 | |
|     changes.value_set(tortoise::last, seekbits);
 | |
|     if(seek()) {
 | |
|       cell *c = passable(last, NULL, 0) ? last : c2;
 | |
|       changes.ccell(c);
 | |
|       c->item = itBabyTortoise;
 | |
|       if(c == c2) dopickup = false;
 | |
|       changes.map_value(babymap, c);
 | |
|       babymap[c] = bold;
 | |
|       }
 | |
|     else items[itBabyTortoise]++;
 | |
|     }
 | |
|   else if(c2->item == itOrbYendor && peace::on) {
 | |
|     if(!items[itDodeca]) {
 | |
|       addMessage(XLAT("Collect as many Dodecahedra as you can, then return here!"));
 | |
|       }
 | |
|     else {
 | |
|       addMessage(XLAT("Your score: %1", its(items[itDodeca])));
 | |
|       peace::simon::restore();
 | |
|       }
 | |
|     dopickup = false;
 | |
|     }
 | |
|   else if(c2->item == itOrbYendor && yendor::state(c2) != yendor::ysUnlocked) {
 | |
|     dopickup = false;
 | |
|     }
 | |
|   else if(c2->item == itOrbYendor) 
 | |
|     yendor::collected(c2);    
 | |
|   else if(c2->item == itHolyGrail) {
 | |
|     playSound(c2, "tada");
 | |
|     int v = newRoundTableRadius() + 12;
 | |
|     items[itOrbTeleport] += v;
 | |
|     items[itOrbSpeed] += v;
 | |
|     items[itHolyGrail]++;
 | |
|     addMessage(XLAT("Congratulations! You have found the Holy Grail!"));
 | |
|     if(!eubinary && c2->master->alt) {
 | |
|       changes.value_keep(c2->master->alt->emeraldval);
 | |
|       c2->master->alt->emeraldval |= GRAIL_FOUND;
 | |
|       }
 | |
|     achievement_collection(c2->item);
 | |
|     }
 | |
|   else if(c2->item == itKey) {
 | |
|     playSound(c2, "pickup-key");
 | |
|     for(int i=0; i<isize(yendor::yi); i++) if(yendor::yi[i].actual_key() == c2)
 | |
|       changes.value_set(yendor::yi[i].found, true);
 | |
|     items[itKey]++;
 | |
|     }
 | |
|   else if(!telekinesis && cantGetGrimoire(c2)) {
 | |
|     // telekinesis checks the condition earlier
 | |
|     dopickup = false;
 | |
|     }
 | |
|   else if(c2->item == itCompass) {
 | |
|     dopickup = false;
 | |
|     }
 | |
|   else if(c2->item == itBuggy || c2->item == itBuggy2) {
 | |
| 
 | |
|     if(changes.on) {
 | |
|       if(changes.checking) {
 | |
|         changes.rollback();
 | |
|         return true;
 | |
|         }
 | |
|       changes.commit();
 | |
|       }
 | |
| 
 | |
|     items[itOrbSafety] += 7;
 | |
|     if(shmup::on)
 | |
|       shmup::delayed_safety = true;
 | |
|     else {
 | |
|       buggyGeneration = false;
 | |
|       activateSafety(laCrossroads);
 | |
|       }
 | |
|     return true;
 | |
|     }
 | |
|   else if(c2->item == itTreat) {
 | |
|     playSound(c2, "pickup-scroll");
 | |
|     halloween::getTreat(c2);
 | |
|     }   
 | |
|   else {
 | |
|     int q = items[c2->item];
 | |
|     int q_el = items[itElemental];
 | |
| 
 | |
|     if(c2->item == itBarrow) 
 | |
|       for(int i=0; i<c2->landparam; i++) gainItem(c2->item);
 | |
|     else if(c2->item) gainItem(c2->item);
 | |
| 
 | |
|     if(c2->item && items[c2->item] > q && (vid.bubbles_all || (threshold_met(items[c2->item]) > threshold_met(q) && vid.bubbles_threshold))) {
 | |
|       drawBubble(c2, iinf[c2->item].color, its(items[c2->item]), 0.5);
 | |
|       }
 | |
|     
 | |
|     if(c2->item && items[itElemental] > q_el && (vid.bubbles_all || (threshold_met(items[itElemental]) > threshold_met(q_el) && vid.bubbles_threshold))) {
 | |
|       drawBubble(c2, iinf[itElemental].color, its(items[itElemental]), 0.5);
 | |
|       }
 | |
| 
 | |
|     if(c2->item) {
 | |
|       char ch = iinf[c2->item].glyph;
 | |
|       if(ch == '*') playSound(c2, "pickup-gem");
 | |
|       else if(ch == '$' || ch == 'x') playSound(c2, "pickup-gold");
 | |
|       else if(ch == '%' || ch == ';') playSound(c2, "pickup-potion");
 | |
|       else if(c2->item == itApple) playSound(c2, "apple");
 | |
|       else playSound(c2, "pickup-scroll");
 | |
|       }
 | |
|     }
 | |
|   
 | |
|   if(dopickup && c2->item) {
 | |
| #if CAP_HISTORY
 | |
|     // temporary variable to avoid the "cannot bind bitfield" problem in C++11
 | |
|     eItem dummy = c2->item;
 | |
|     history::findhistory.emplace_back(c2, dummy);
 | |
| #endif
 | |
| 
 | |
|     if(c2->item == itBombEgg && c2->land == laMinefield) {
 | |
|       c2->landparam |= 2;
 | |
|       c2->landparam &= ~1;
 | |
|       }
 | |
| 
 | |
|     if(!had_choice)
 | |
|       c2->item = itNone;
 | |
|     }
 | |
| //    if(c2->land == laHive)
 | |
| //      c2->heat = 1;
 | |
| 
 | |
|   int numOrb = 0;
 | |
|   for(int i=0; i<ittypes; i++)
 | |
|     if(itemclass(eItem(i)) == IC_ORB && items[i])
 | |
|       numOrb++;
 | |
|   if(numOrb) achievement_count("ORB", numOrb, 0);
 | |
| 
 | |
|   if(princess::reviveAt && gold(NO_LOVE) >= princess::reviveAt && !inv::on) {
 | |
|     changes.value_set(princess::reviveAt, 0);
 | |
|     items[itSavedPrincess] = 1;
 | |
|     addMessage("You have enough treasure now to revive the Princess!");
 | |
|     }
 | |
| 
 | |
|   return false;
 | |
|   }
 | |
| 
 | |
| EX void glance_message() {
 | |
|   if(gold() >= 300)
 | |
|     addMessage(XLAT("You feel great, like a true treasure hunter."));
 | |
|   else if(gold() >= 200)
 | |
|     addMessage(XLAT("Your eyes shine like gems."));
 | |
|   else if(gold() >= 100)
 | |
|     addMessage(XLAT("Your eyes shine as you glance at your precious treasures."));
 | |
|   else if(gold() >= 50)
 | |
|     addMessage(XLAT("You glance at your great treasures."));
 | |
|   else if(gold() >= 10)
 | |
|     addMessage(XLAT("You glance at your precious treasures."));
 | |
|   else if(gold() > 0)
 | |
|     addMessage(XLAT("You glance at your precious treasure."));
 | |
|   else
 | |
|     addMessage(XLAT("Your inventory is empty."));
 | |
|   }
 | |
| 
 | |
| EX void dropGreenStone(cell *c) {
 | |
|   if(items[itGreenStone] && !passable(c, NULL, P_MONSTER)) {
 | |
|     // NOTE: PL/CZ translations assume that itGreenStone is dropped to avoid extra forms!
 | |
|     addMessage(XLAT("Cannot drop %the1 here!", itGreenStone));
 | |
|     return;
 | |
|     }
 | |
|   if(items[itGreenStone] && c->item == itNone) {
 | |
|     changes.ccell(c);
 | |
|     changes.value_keep(items[itGreenStone]);
 | |
|     items[itGreenStone]--;
 | |
|     if(false) {
 | |
|       c->item = itNone;
 | |
|       spill(c, eWall(c->wall ^ waFloorA ^ waFloorB), 3);
 | |
|       addMessage(XLAT("The slime reacts with %the1!", itGreenStone));
 | |
|       }
 | |
|     else {
 | |
|       c->item = itGreenStone;
 | |
|       addMessage(XLAT("You drop %the1.", itGreenStone));
 | |
|       if(isHaunted(cwt.at->land)) 
 | |
|         fail_survivalist();
 | |
|       }
 | |
|     }
 | |
|   else {
 | |
|     if(items[itGreenStone] && c->item == itGreenStone)
 | |
|       addMessage(XLAT("You juggle the Dead Orbs."));
 | |
|     else if(items[itGreenStone] && c->item)
 | |
|       addMessage(XLAT("You give %the1 a grim look.", c->item));
 | |
|     else if(items[itGreenStone]) {
 | |
|       addMessage(XLAT("Cannot drop %the1 here!", itGreenStone));
 | |
|       return;
 | |
|       }
 | |
|     else glance_message();
 | |
|     }
 | |
|   }
 | |
| 
 | |
| EX void moveItem1(cell *from, cell *to, bool activateYendor) {
 | |
|   if(from->item == itOrbYendor) {
 | |
|     bool xnew = true;
 | |
|     for(int i=0; i<isize(yendor::yi); i++) 
 | |
|       if(yendor::yi[i].path[0] == from) xnew = false;
 | |
|     if(xnew && activateYendor) yendor::check(from);
 | |
|     for(int i=0; i<isize(yendor::yi); i++) 
 | |
|       if(yendor::yi[i].path[0] == from) 
 | |
|         changes.value_set(yendor::yi[i].path[0], to);
 | |
|     }
 | |
| 
 | |
|   if(from->item == itKey) {
 | |
|     for(int i=0; i<isize(yendor::yi); i++) if(yendor::yi[i].path[YDIST-1] == from) {
 | |
|       changes.value_set(yendor::yi[i].path[YDIST-1], to);
 | |
|       }
 | |
|     for(int i=0; i<isize(yendor::yi); i++) if(yendor::yi[i].actualKey == from)
 | |
|       changes.value_set(yendor::yi[i].actualKey, to);
 | |
|     }
 | |
|   
 | |
|   if(from->item == itBabyTortoise || to->item == itBabyTortoise) {
 | |
|     tortoise::move_baby(from, to);
 | |
|     }  
 | |
| 
 | |
|   eItem i = to->item;
 | |
|   to->item = from->item;
 | |
|   from->item = i;
 | |
|   }
 | |
| 
 | |
| EX void moveItem (cell *from, cell *to, bool activateYendor) {
 | |
|   static cell dummy;
 | |
|   dummy.item = itNone;
 | |
|   moveItem1(from, &dummy, activateYendor);
 | |
|   moveItem1(to, from, activateYendor);
 | |
|   moveItem1(&dummy, to, activateYendor);
 | |
|   }
 | |
| 
 | |
| EX bool itemHidden(cell *c) {
 | |
|   return isWatery(c) && !(shmup::on && shmup::boatAt(c));
 | |
|   }
 | |
| 
 | |
| EX eItem localTreasureType() {
 | |
|   lastland = singlepos()->land;
 | |
|   return treasureType(lastland);
 | |
|   }
 | |
| 
 | |
| EX void countLocalTreasure() {
 | |
|   eItem i = localTreasureType();
 | |
|   currentLocalTreasure = i ? items[i] : 0;
 | |
|   if(i != itHyperstone) for(int i=0; i<isize(dcal); i++) {
 | |
|     cell *c2 = dcal[i];
 | |
|     if(c2->cpdist > (WDIM == 3 ? 1 : 3)) break;
 | |
|     eItem i2 = treasureType(c2->land);
 | |
|     if(i2 && items[i2] < currentLocalTreasure)
 | |
|       currentLocalTreasure = items[i2];
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #if HDR
 | |
| static constexpr int NO_TREASURE = 1;
 | |
| static constexpr int NO_YENDOR = 2;
 | |
| static constexpr int NO_GRAIL = 4;
 | |
| static constexpr int NO_LOVE = 8;
 | |
| #endif
 | |
| 
 | |
| EX int gold(int no IS(0)) {
 | |
|   int i = 0;
 | |
|   if(!(no & NO_YENDOR)) i += items[itOrbYendor] * 50;
 | |
|   if(!(no & NO_GRAIL)) i += items[itHolyGrail] * 10;
 | |
|   if(!(no & NO_LOVE)) {
 | |
|     bool love = items[itOrbLove];
 | |
| #if CAP_INV
 | |
|     if(inv::on && inv::remaining[itOrbLove])
 | |
|       love = true;
 | |
| #endif
 | |
| #if CAP_DAILY
 | |
|     if(daily::on) love = false;
 | |
| #endif
 | |
|     if(love) i += 30;
 | |
|     }
 | |
|   
 | |
|   if(!(no & NO_TREASURE)) 
 | |
|     for(int t=0; t<ittypes; t++) 
 | |
|       if(itemclass(eItem(t)) == IC_TREASURE)
 | |
|         i += items[t];
 | |
|   return i;
 | |
|   }
 | |
| 
 | |
| EX int maxgold() {
 | |
|   int mg = 0;
 | |
|   for(int i=0; i<ittypes; i++) 
 | |
|     if(itemclass(eItem(i)) == IC_TREASURE && items[i] > mg) 
 | |
|       mg = items[i];
 | |
|   return mg;
 | |
|   }
 | |
| 
 | |
| EX void updateHi_for_code(eItem it, int v, modecode_t xcode) {
 | |
|   if(!yendor::on)
 | |
|     if(v > hiitems[xcode][it]) 
 | |
|       changes.value_set(hiitems[xcode][it], v);
 | |
|   }
 | |
| 
 | |
| EX void updateHi(eItem it, int v) {
 | |
|   LATE ( updateHi(it, v) );
 | |
|   updateHi_for_code(it, v, modecode());
 | |
|   }
 | |
| 
 | |
| EX void gainItem(eItem it) {
 | |
|   int g = gold();
 | |
|   bool lhu = landUnlocked(laHell);
 | |
|   items[it]++; if(it != itLotus) updateHi(it, items[it]);
 | |
|   if(it == itRevolver && items[it] > 6) items[it] = 6;
 | |
|   achievement_collection(it);
 | |
|   multi::treasures[multi::cpid]++;
 | |
| #if CAP_DAILY
 | |
|     if(daily::on) achievement_final(false);
 | |
| #endif
 | |
| 
 | |
|   int g2 = gold();
 | |
|   if(items[itFireShard] && items[itAirShard] && items[itWaterShard] && items[itEarthShard]) {
 | |
|     items[itFireShard]--;
 | |
|     items[itAirShard]--;
 | |
|     items[itWaterShard]--;
 | |
|     items[itEarthShard]--;
 | |
|     gainItem(itElemental);
 | |
|     gainItem(itElemental);
 | |
|     gainItem(itElemental);
 | |
|     gainItem(itElemental);
 | |
|     addMessage(XLAT("You construct some Elemental Gems!", it) + itemcounter(items[itElemental]));
 | |
|     playSound(cwt.at, "elementalgem");
 | |
|     }          
 | |
| 
 | |
|   if(it == itBounty) 
 | |
|     items[itRevolver] = 6;
 | |
|       
 | |
|   if(it == itHyperstone && items[itHyperstone] == 10)
 | |
|     achievement_victory(true);
 | |
| 
 | |
|   if(ls::any_chaos() && gold() >= 300)
 | |
|     achievement_gain_once("CHAOS", rg::chaos);
 | |
| 
 | |
| #if ISMOBILE
 | |
|   if(g < lastsafety + R30*3/2 && g2 >= lastsafety + R30*3/2)
 | |
|     addMessage(XLAT("The Orb of Safety from the Land of Eternal Motion might save you."));
 | |
| #endif
 | |
|   
 | |
| #define IF(x) if(g < (x) && g2 >= x && !peace::on)
 | |
| 
 | |
|   if(in_full_game()) {
 | |
|     IF(R60/4) 
 | |
|       addMessage(XLAT("Collect treasure to access more different lands..."));
 | |
|     IF(R30)
 | |
|       addMessage(XLAT("You feel that you have enough treasure to access new lands!"));
 | |
|     IF(R30*3/2)
 | |
|       addMessage(XLAT("Collect more treasures, there are still more lands waiting..."));
 | |
|     IF(R60)
 | |
|       addMessage(XLAT("You feel that the stars are right, and you can access R'Lyeh!"));
 | |
|     IF(R30*5/2)
 | |
|       addMessage(XLAT("Kill monsters and collect treasures, and you may get access to Hell..."));
 | |
|     IF(R10 * 9) 
 | |
|       addMessage(XLAT("To access Hell, collect %1 treasures each of %2 kinds...", its(R10), its(lands_for_hell())));
 | |
|     if(landUnlocked(laHell) && !lhu) {
 | |
|       addMessage(XLAT("Abandon all hope, the gates of Hell are opened!"));
 | |
|       addMessage(XLAT("And the Orbs of Yendor await!"));
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
| EX string itemcounter(int qty) {
 | |
|   string s = ""; s += " ("; s += its(qty); s += ")"; return s;
 | |
|   }
 | |
| 
 | |
| EX void gainShard(cell *c2, const char *msg) {
 | |
|   invismove = false;
 | |
|   string s = XLAT(msg);
 | |
|   if(is_mirrorland(c2) && !peace::on) {
 | |
|      collectMessage(c2, itShard);
 | |
|      gainItem(itShard);
 | |
|      if(vid.bubbles_all || (threshold_met(items[itShard]) == items[itShard] && vid.bubbles_threshold)) {
 | |
|        drawBubble(c2, iinf[itShard].color, its(items[itShard]), 0.5);
 | |
|        }
 | |
|      s += itemcounter(items[itShard]);
 | |
|      }
 | |
|   addMessage(s);
 | |
|   c2->wall = waNone;
 | |
|   invismove = false;
 | |
|   }
 | |
| 
 | |
| EX void placeItems(int qty, eItem it) {
 | |
|   int dcs = isize(dcal);
 | |
|   for(int i=1; qty && i<dcs; i++) {
 | |
|     cell *c = dcal[i];
 | |
|     if(!c->monst && !c->item && passable(c, NULL, 0))
 | |
|       c->item = it, qty--;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| EX bool cantGetGrimoire(cell *c2, bool verbose IS(true)) {
 | |
|   if(ls::any_chaos()) return false;
 | |
|   if(!eubinary && !c2->master->alt) return false;
 | |
|   if(c2->item == itGrimoire && items[itGrimoire] > celldistAlt(c2)/-temple_layer_size()) {
 | |
|     if(verbose)
 | |
|       addMessage(XLAT("You already have this Grimoire! Seek new tomes in the inner circles."));
 | |
|     return true;
 | |
|     }
 | |
|   return false;
 | |
|   }
 | |
| 
 | |
| EX void gainLife() {
 | |
|   items[itOrbLife] ++;
 | |
|   if(items[itOrbLife] > 5 && !inv::on) items[itOrbLife] = 5;
 | |
|   }
 | |
| 
 | |
| EX void collectMessage(cell *c2, eItem which) {
 | |
|   bool specialmode = yendor::on || princess::challenge || cheater || !in_full_game();
 | |
|   
 | |
|   if(which == itDodeca && peace::on) return;  
 | |
|   if(which == itTreat) ;    
 | |
|   else if(isElementalShard(which)) {
 | |
|     int tsh = 
 | |
|       items[itFireShard] + items[itAirShard] + items[itWaterShard] + items[itEarthShard] +
 | |
|       items[itElemental];
 | |
|     if(tsh == 0) {
 | |
|       addMessage(XLAT("Collect four different Elemental Shards!"));
 | |
|       addMessage(XLAT("Unbalanced shards in your inventory are dangerous."));
 | |
|       }
 | |
|     else {
 | |
|       string t = XLAT("You collect %the1. (%2)", which, its(items[which]+1));
 | |
|       addMessage(t);
 | |
|       }
 | |
|     }
 | |
|   else if(which == itKey) {
 | |
|     addMessage(XLAT("You have found the Key! Now unlock this Orb of Yendor!"));
 | |
|     }
 | |
|   else if(which == itGreenStone && !items[itGreenStone])
 | |
|     addMessage(XLAT("This orb is dead..."));
 | |
|   else if(which == itGreenStone)
 | |
|     addMessage(XLAT("Another Dead Orb."));
 | |
|   else if(itemclass(which) != IC_TREASURE) {
 | |
|     if(!inv::activating)
 | |
|       addMessage(XLAT("You have found %the1!", which));
 | |
|     }
 | |
|   else if(which == itBabyTortoise) {
 | |
|     playSound(c2, playergender() ? "speak-princess" : "speak-prince");
 | |
|     addMessage(XLAT("Aww, poor %1... where is your family?", which));
 | |
|     }
 | |
|   else if(gold() == 0 && !specialmode)
 | |
|     addMessage(XLAT("Wow! %1! This trip should be worth it!", which));
 | |
|   else if(gold() == 1 && !specialmode)
 | |
|     addMessage(XLAT("For now, collect as much treasure as possible..."));
 | |
|   else if(gold() == 2 && !specialmode)
 | |
|     addMessage(XLAT("Prove yourself here, then find new lands, with new quests..."));
 | |
|   else if(!items[which] && itemclass(which) == IC_TREASURE)
 | |
|     addMessage(XLAT("You collect your first %1!", which));
 | |
|   else if(items[which] == 4 && maxgold() == U5-1 && !specialmode) {
 | |
|     addMessage(XLAT("You feel that %the2 become%s2 more dangerous.", which, c2->land));
 | |
|     addMessage(XLAT("With each %1 you collect...", which, c2->land));
 | |
|     }
 | |
|   else if(items[which] == 9 && maxgold() == 9 && !specialmode) {
 | |
|     if(inv::on) {
 | |
|       addMessage(XLAT("The treasure gives your magical powers!", c2->land));
 | |
|       if(!ISMOBILE) 
 | |
|         addMessage(XLAT("Press 'i' to access your magical powers.", c2->land));
 | |
|       }
 | |
|     else
 | |
|       addMessage(XLAT("Are there any magical orbs in %the1?...", c2->land));
 | |
|     }
 | |
|   else if(items[which] == R10 && maxgold() == R10 && !specialmode && !inv::on) {
 | |
|     addMessage(XLAT("You feel that %the1 slowly become%s1 dangerous...", c2->land));
 | |
|     addMessage(XLAT("Better find some other place."));
 | |
|     }
 | |
|   else if(which == itHunting && items[itHunting] == 4 && !specialmode && !ISMOBWEB)
 | |
|     addMessage(XLAT("Hint: hold Alt to highlight enemies and other important features."));
 | |
|   else if(which == itSpice && items[itSpice] == U10*7/10 && !specialmode && isLandIngame(laHell))
 | |
|     addMessage(XLAT("You have a vision of the future, fighting demons in Hell..."));
 | |
|   else if(which == itSpice && items[itSpice] == U10-1 && !specialmode && isLandIngame(laRedRock))
 | |
|     addMessage(XLAT("You will be fighting red rock snakes, too..."));
 | |
|   else if(which == itKraken && items[itKraken] == U10-1 && !specialmode)
 | |
|     addMessage(XLAT("You feel that a magical weapon is waiting for you..."));
 | |
| //  else if(which == itFeather && items[itFeather] == 10)
 | |
| //    addMessage(XLAT("There should be a Palace somewhere nearby..."));
 | |
|   else if(which == itElixir && items[itElixir] == U5-1 && !specialmode)
 | |
|     addMessage(XLAT("With this Elixir, your life should be long and prosperous..."));
 | |
|   else if(which == itRuby && items[itRuby] == U5-1 && !specialmode && isLandIngame(laMountain)) {
 | |
|     addMessage(XLAT("You feel something strange about gravity here..."));
 | |
|     }
 | |
|   else if(which == itPalace && items[itPalace] == U5-1 && !specialmode && isLandIngame(laDungeon)) {
 | |
|     addMessage(XLAT("The rug depicts a man in a deep dungeon, unable to leave."));
 | |
|     }
 | |
|   else if(which == itFeather && items[itFeather] == 25-1 && !specialmode && inv::on && !ls::any_chaos())
 | |
|     addMessage(XLAT("You feel the presence of free saves on the Crossroads."));
 | |
|   else if(which == itHell && items[itHell] == 25-1 && !specialmode && inv::on && !ls::any_chaos())
 | |
|     addMessage(XLAT("You feel the Orbs of Yendor nearby..."));
 | |
|   else if(which == itHell && items[itHell] == 50-1 && !specialmode && inv::on && !ls::any_chaos())
 | |
|     addMessage(XLAT("You feel the Orbs of Yendor in the Crossroads..."));
 | |
|   else if(which == itHell && items[itHell] == 100-1 && !specialmode && inv::on && !ls::any_chaos())
 | |
|     addMessage(XLAT("You feel the Orbs of Yendor everywhere..."));
 | |
|   else if(which == itBone && items[itBone] % 25 == 24 && !specialmode && inv::on)
 | |
|     addMessage(XLAT("You have gained an offensive power!"));
 | |
|   else if(which == itHell && items[itHell] >= 100 && items[itHell] % 25 == 24 && !specialmode && inv::on)
 | |
|     addMessage(XLAT("A small reward for braving the Hell."));
 | |
|   else if(which == itIvory && items[itIvory] == U5-1 && !specialmode && (isLandIngame(laMountain) || isLandIngame(laDungeon))) {
 | |
|     addMessage(XLAT("You feel attuned to gravity, ready to face mountains and dungeons."));
 | |
|     }
 | |
|   else if(which == itBone && items[itBone] == U5+1 && !specialmode && isLandIngame(laHell))
 | |
|     addMessage(XLAT("The Necromancer's Totem contains hellish incantations..."));
 | |
|   else if(which == itStatue && items[itStatue] == U5+1 && !specialmode)
 | |
|     addMessage(XLAT("The inscriptions on the Statue of Cthulhu point you toward your destiny..."));
 | |
|   else if(which == itStatue && items[itStatue] == U5-1 && isLandIngame(laTemple))
 | |
|     addMessage(XLAT("There must be some temples of Cthulhu in R'Lyeh..."));
 | |
|   else if(which == itDiamond && items[itDiamond] == U10-2 && !specialmode)
 | |
|     addMessage(XLAT("Still, even greater treasures lie ahead..."));
 | |
|   else if(which == itFernFlower && items[itFernFlower] == U5-1 && isLandIngame(laEmerald))
 | |
|     addMessage(XLAT("You overheard Hedgehog Warriors talking about emeralds..."));
 | |
|   else if(which == itEmerald && items[itEmerald] == U5-1 && !specialmode && isLandIngame(laCamelot))
 | |
|     addMessage(XLAT("You overhear miners talking about a castle..."));
 | |
|   else if(which == itEmerald && items[itEmerald] == U5 && !specialmode && isLandIngame(laCamelot))
 | |
|     addMessage(XLAT("A castle in the Crossroads..."));
 | |
|   else if(which == itShard) ;
 | |
|   else {
 | |
|     int qty = (which == itBarrow) ? c2->landparam : 1;
 | |
|     string t;
 | |
|     if(which == itBarrow && items[which] < 25 && items[which] + qty >= 25)
 | |
|       t = XLAT("Your energy swords get stronger!");
 | |
|     else if(maxgold() < 25 && items[which] + qty >= 25)
 | |
|       t = XLAT("You feel even more attuned to the magic of this land!");
 | |
|     else t = XLAT("You collect %the1. (%2)", which, its(items[which]+qty));
 | |
|     addMessage(t);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| EX bool itemHiddenFromSight(cell *c) {
 | |
|   return isWatery(c) && !items[itOrbInvis] && !(items[itOrbFish] && playerInWater())
 | |
|     && !(shmup::on && shmup::boatAt(c)) && !(c->cpdist <= 1 && playerInWater());
 | |
|   }
 | |
| 
 | |
| }
 | 
