// Hyperbolic Rogue // namespaces for complex features (whirlwind, whirlpool, elec, princess, clearing, // mirror, hive, heat + livecaves) // Copyright (C) 2011-2016 Zeno Rogue, see 'hyper.cpp' for details #include namespace whirlwind { int fzebra3(cell *c) { if(euclid) { eucoord x, y; decodeMaster(c->master, x, y); return 1+((((signed short)(y)+int(50000))/3)%3); } return zebra3(c); } void switchTreasure(cell *c) { c->item = itNone; if(safety) return; if(hrand(5000) < PT(100 + 2 * (kills[moAirElemental] + kills[moWindCrow]), 200) && notDippingFor(itWindstone) && getGhostcount() < 2) c->item = itWindstone; else if(hrand(5000) < 20*PRIZEMUL) placeLocalOrbs(c); } int cat(cell *c) { if(c->land != laWhirlwind) return 0; if(c->wall != waNone && c->wall != waChasm && c->wall != waSea && !isAlchAny(c) && c->wall != waMineMine && c->wall != waFire) return 0; if(c->item == itKey || c->item == itOrbYendor) return 0; if(airdist(c) < 3) return 0; if(c->monst == moHexSnake || c->monst == moHexSnakeTail) return 0; return fzebra3(c); } cell *where; int dfrom[2], dto[2], qdirs; int gdist(int d, int e) { return dirdiff(d-e, where->type); } void calcdirs(cell *c) { where = c; int d = cat(c); qdirs = 0; if(d == 0) return; int qdf = 0, qdt = 0; int cats[7]; for(int i=0; itype; i++) cats[i] = cat(createMov(c,i)); for(int i=0; itype; i++) if(cats[i] == d) { bool c1 = (cats[(i+1)%c->type] != d); bool c2 = (cats[(i+c->type-1)%c->type] != d); if(c1 && !c2) dto[qdt++] = i; if(c2 && !c1) dfrom[qdf++] = i; } qdirs = qdf; if(qdirs == 2) { int cur = gdist(dfrom[0], dto[0]) + gdist(dfrom[1], dto[1]); int alt = gdist(dfrom[0], dto[1]) + gdist(dfrom[1], dto[0]); if(alt < cur) swap(dto[0], dto[1]); } } int mindist(int d, int *tab) { if(qdirs == 0) return NODIR; if(qdirs == 1) return gdist(d, tab[0]); return min(gdist(d, tab[0]), gdist(d, tab[1])); } int winddir(int d) { if(d == -1) return 0; int mdf = mindist(d, dfrom); int mdt = mindist(d, dto); // printf("dir = %d mdf = %d mdt = %d\n", d, mdf, mdt); if(mdf < mdt) return -1; if(mdf > mdt) return 1; return 0; } void build(vector& whirlline, int d) { again: cell *at = whirlline[size(whirlline)-1]; cell *prev = whirlline[size(whirlline)-2]; for(int i=0; itype; i++) if(at->mov[i] && (euclid || at->mov[i]->master->alt) && celldistAlt(at->mov[i]) == d && at->mov[i] != prev) { whirlline.push_back(at->mov[i]); goto again; } } void moveAt(cell *c) { if(eq(c->aitmp, sval)) return; calcdirs(c); if(qdirs != 1) return; vector whirlline; whirlline.push_back(c); cell *prev = c; cell *c2 = c->mov[dfrom[0]]; while(true) { // printf("c = %p dist = %d\n", c2, c2->mpdist); if(c == c2) break; calcdirs(c2); if(qdirs == 0) break; cell *cc2 = c2; if(qdirs == 1) whirlline.push_back(c2), c2 = c2->mov[dfrom[0]]; else if(c2->mov[dto[0]] == prev) c2 = c2->mov[dfrom[1]]; else c2 = c2->mov[dfrom[0]]; prev = cc2; } int z = size(whirlline); // printf("Cycle built from %p, length = %d\n", c, z); for(int i=0; impdist, whirlline[i]->item ? '*' : ' '); whirlline[i]->aitmp = sval; if(whirlline[i]->mpdist == BARLEV) switchTreasure(whirlline[i]); } for(int i=0; iitem) collectItem(cwt.c, true); } void move() { sval++; for(int i=0; imov[dfrom[0]]; if(!passable(c, c2, P_JUMP1)) return NULL; if(player && i == 0 && !passable(c, c2, P_ISPLAYER)) return NULL; c = c2; } calcdirs(c); if(qdirs != 1) return NULL; return c; } cell *jumpDestination(cell *c) { for(int i=0; i<2; i++) { calcdirs(c); if(qdirs != 1) return NULL; c = c->mov[dto[0]]; } calcdirs(c); if(qdirs != 1) return NULL; return c; } } namespace elec { bool havecharge, havethunder; bool afterOrb; // extra charge from the Orb of Lightning enum eCharge { ecCharged, ecGrounded, ecIsolator, ecConductor }; bool conduct(eCharge cf, eCharge ct) { if(ct == ecIsolator) return false; if(ct == ecConductor) return true; return ct != cf; } eCharge getCharge(cell *c) { bool ao = afterOrb && c->ligon; if(c->wall == waCharged) return ecCharged; if(c->wall == waSea || c->wall == waGrounded) return ecGrounded; if(c->wall == waSandstone || c->wall == waDeadTroll || c->wall == waDeadTroll2 || c->wall == waVinePlant || c->wall == waMetal || isAlchAny(c)) return c->land == laStorms ? ecConductor : ecGrounded; if(c->wall == waBarrier) return ecIsolator; if(c->wall == waChasm) return ecIsolator; if(shmup::on ? isPlayerOn(c) : (c == cwt.c || c == stalemate::moveto || (items[itOrbEmpathy] && isFriendly(c)))) { if(items[itOrbShield]) return ecIsolator; if(ao) return ecIsolator; if(!items[itOrbGhost]) return c->land == laStorms ? ecConductor : ecGrounded; } // if(c->monst && stalemate::moveto) printf("%p: isKilled = %d\n", c, stalemate::isKilled(c)); if( ( c->monst || (shmup::on ? isPlayerOn(c) : c == cwt.c) || c == stalemate::moveto || c == stalemate::pushto) && (c == stalemate::pushto || !stalemate::isKilled(c)) && c->monst != moGhost && c->monst != moIvyDead && c->monst != moIvyNext && !(isDragon(c->monst) && !c->hitpoints) ) return c->land == laStorms ? (ao ? ecCharged : ecConductor) : ecGrounded; if(c->land != laStorms) return ecGrounded; if(ao) return ecCharged; return ecIsolator; } // To process conductivity, consider the following graph: // - edges are between conductors and adjacent charged/grounded/conductor cells // - all charged cells are connected to one special cell '0' // - all grounded cells are connected to one special cell '1' // - cells '0' and '1' are connected // If A and B are in the same biconnected component, then there is a closed circuit, // consisting of all other cells in that component. // To find biconnected components, we are using the Hopcroft-Tarjan algorithm. struct chargedata { cell *c; int otmp; int lowlink; bool instack; bool fire; }; vector charges; vector > xstack; vector chargecells; bool hasdata(cell *c) { return c->aitmp >= 0 && c->aitmp < size(charges) && charges[c->aitmp].c == c; } void connect(int from, cell *c) { if(hasdata(c)) { // seen again: set the lowlink if(!charges[c->aitmp].instack) return; // printf("edge %d-%d\n", from, c->aitmp); if(c->aitmp < charges[from].lowlink) charges[from].lowlink = c->aitmp; } else { int id = size(charges); charges.push_back(chargedata()); chargedata& ch(charges[id]); ch.c = c; ch.otmp = c->aitmp; ch.lowlink = id; c->aitmp = id; ch.instack = true; ch.fire = false; // c->landparam = id; // printf("edge %d-%d [%s]\n", from, id, dnameof(c->wall)); xstack.push_back(make_pair(from, id)); eCharge chh = getCharge(c); if(chh == ecGrounded) { xstack.push_back(make_pair(id, 0)); ch.lowlink = 0; } else if(chh == ecCharged) { xstack.push_back(make_pair(id, 1)); if(from != 1) ch.lowlink = 1; } for(int i=0; itype; i++) { cell *c2 = c->mov[i]; if(!c2) continue; if(c2->aitmp == from) continue; eCharge ct = getCharge(c2); if(conduct(chh, ct)) connect(id, c2); } // printf("lowlink of %d [%s] = %d\n", id, dnameof(c->wall), ch.lowlink); if(ch.lowlink < charges[from].lowlink) charges[from].lowlink = ch.lowlink; if(charges[id].lowlink >= from) { while(xstack.back().first != from || xstack.back().second != id) { // printf("bcc %d,%d\n", xstack.back().first, xstack.back().second); xstack.pop_back(); } // printf("bcc %d,%d\n", xstack.back().first, xstack.back().second); xstack.pop_back(); // printf("\n"); } ch.instack = false; } } void affect(cell *c) { c->ligon = true; if(c->monst) { if(c->monst == moMetalBeast2 && !c->item) c->item = itFulgurite; killMonster(c); } if(isPlayerOn(c)) { killThePlayerAt(moLightningBolt, c, 0); } if(c->wall == waSandstone) c->wall = waNone, c->item = itFulgurite; if(c->wall == waDeadTroll) c->wall = waCavefloor; if(c->wall == waDeadTroll2 || isAlchAny(c) || c->wall == waVinePlant) c->wall = waNone; /* if(c->wall == waCharged) c->wall = waMetal; */ } void listChargedCells(cell *c, eCharge last = ecConductor) { if(eq(c->aitmp, sval)) return; eCharge here = getCharge(c); /* if(c->cpdist <= 2) { printf("monst=%s ", dnameof(c->monst)); printf("wall=%s ", dnameof(c->wall)); printf("c=%p here=%d last=%d\n", c, here, last); } */ if(here == ecIsolator) c->aitmp = sval; if(!conduct(last, here)) return; if(here == ecCharged) chargecells.push_back(c); c->aitmp = sval; for(int i=0; itype; i++) { cell *c2 = c->mov[i]; if(c2) listChargedCells(c2, here); } } void init() { chargecells.clear(); sval++; for(int i=0; iaitmp = charges[i].otmp; charges.resize(0); } void draw(cell *c, eCharge what) { if(c->ligon) return; c->ligon = true; for(int i=0; itype; i++) { cell *c2 = c->mov[i]; if(!c2) continue; eCharge ch = getCharge(c2); if(conduct(what, ch)) draw(c2, ch); } } void drawcharges() { for(int i=0; iaitmp >= 0 && c->aitmp < size(charges) && charges[c->aitmp].c == c) return charges[c->aitmp].fire; return false; } struct builder { builder() { init(); } ~builder() { cleanup(); } }; void act() { int k = tkills(); if(cwt.c->land == laStorms && !afterOrb) markOrb(itOrbShield), markOrb(itOrbGhost); builder b; fire(); if(!afterOrb) achievement_count("ELEC", tkills() - k, 0); } // 0 = no close escape, 1 = close escape, 2 = message already shown int lightningfast; void checklightningfast() { if(lightningfast == 1) { addMessage(XLAT("Wow! That was close.")); lightningfast = 2; } if(lightningfast > 1) return; builder b; if(elec::affected(cwt.c)) lightningfast = 1; } }; namespace princess { #define EPX 39 #define EPY 21 #define OUT_OF_PRISON 200 #define OUT_OF_PALACE 250 bool generating = false; bool challenge = false; bool saved = false; bool everSaved = false; bool forceVizier = false; bool forceMouse = false; bool gotoPrincess = false; int saveHP = 0, saveArmedHP = 0; struct info { int id; // id of this info cell *prison; // where was the Princess locked heptagon *alt; // alt of the prison int bestdist; // best dist achieved int bestnear; // best dist achieved, by the player int value; // number of Rugs at 120 cell *princess; // where is the Princess currently }; vector infos; void assign(info *i) { if(i->alt) i->alt->emeraldval = i->id; } void newInfo(cell *c) { info *i = new info; i->prison = c; i->princess = c; i->alt = c->master->alt; i->id = size(infos); i->bestdist = 0; i->bestnear = OUT_OF_PRISON; infos.push_back(i); assign(i); } void newFakeInfo(cell *c) { info *i = new info; i->prison = NULL; i->princess = c; i->alt = NULL; i->id = size(infos); i->bestdist = OUT_OF_PALACE; i->bestnear = 0; infos.push_back(i); assign(i); } info *getPrisonInfo(cell *c) { if(euclid) return NULL; if(c->land != laPalace) return NULL; if(!c->master->alt) return NULL; int ev = c->master->alt->emeraldval; // NEWYEARFIX if(ev < 0 || ev >= size(infos)) return NULL; if(infos[ev]->alt != c->master->alt->alt) return NULL; return infos[ev]; } info *getPrincessInfo(cell *c) { for(int i=0; iprincess == c) { while(i) { infos[i]->id = i-1; assign(infos[i]); infos[i-1]->id = i; assign(infos[i-1]); i--; } return infos[i]; } return NULL; } int dist(cell *c) { if(c->land != laPalace) return OUT_OF_PALACE; else if(euclid) return celldistAlt(c); else if(!c->master->alt) return OUT_OF_PRISON; else return celldistAlt(c); } void clear() { for(int i=0; imonst = moPrincessArmed; c->stuntime = 0; c->hitpoints = palaceHP(); drawFlash(c); info *inf = NULL; for(int i=0; iprincess && infos[i]->bestdist == OUT_OF_PALACE) inf = infos[i]; if(inf) { inf->princess->monst = moNone; inf->princess = c; } else newFakeInfo(c); return true; } void bringBack() { if(bringBackAt(cwt.c->mov[cwt.spin])) return; for(int i=1; i i->bestdist) { i->bestdist = newdist; // printf("Improved dist to %d\n", newdist); if(newdist == OUT_OF_PALACE) { if(!princess::saved) achievement_gain("PRINCESS1"); princess::saved = true; princess::everSaved = true; items[itSavedPrincess]++; } if(newdist == OUT_OF_PRISON && princess::challenge) { addMessage(XLAT("Congratulations! Your score is %1.", its(i->value))); achievement_gain("PRINCESS2"); if(!cheater) achievement_score(36, i->value); showMissionScreen(); } } } void save(cell *princess) { if(euclid) return; princess::info *i = princess::getPrincessInfo(princess); if(!i || i->bestdist <= 3) princess->monst = moNone; else if(i) setdist(i, OUT_OF_PALACE); } void move(cell *ct, cell *cf) { if(euclid) return; princess::info *i = princess::getPrincessInfo(cf); if(!i) { static bool warn = true; // note: OK if mapediting or loading if(warn) printf("Warning: unknown princess\n"); if(warn && !cheater) addMessage("Warning: unknown princess (that's a bug, please report)"); warn = false; } else { i->princess = ct; setdist(i, dist(ct)); // printf("newdist = %d (vs %d)\n", newdist, i->bestdist); } } void mouseSqueak(cell *c) { eMonster m = c->monst; info *i = getPrisonInfo(c); int d = dist(c); if(!i) addMessage(XLAT("%The1 squeaks in a confused way.", m)); else if(i->bestdist >= 6) addMessage(XLAT("%The1 squeaks gratefully!", m)); else if(!i->princess) addMessage(XLAT("%The1 squeaks hopelessly.", m)); else if(d > 120) addMessage(XLAT("%The1 squeaks in despair.", m)); else if(d > 90) addMessage(XLAT("%The1 squeaks sadly.", m)); else if(d > 60) addMessage(XLAT("%The1 squeaks with hope!", m)); else if(d > 30) addMessage(XLAT("%The1 squeaks happily!", m)); else addMessage(XLAT("%The1 squeaks excitedly!", m)); } void line(cell *c) { int d = (euclid || c->master->alt) ? celldistAlt(c) : 200; eMonster m = c->monst; static int msgid = 0; retry: if(msgid >= 32) msgid = 0; if(msgid == 0 && d < 20 && c->land == laPalace) { addMessage(XLAT("%The1 kisses you, and begs you to bring %him1 away from here.", m)); } else if(msgid == 1 && d >= 20 && c->land == laPalace) { if(m == moPrincess) addMessage(XLAT("\"I want my revenge. Stun a guard and leave him for me!\"", m)); else addMessage(XLAT("\"That felt great. Thanks!\"", m)); } else if(msgid == 2 && d >= 70 && c->land == laPalace) { addMessage(XLAT("\"Bring me out of here please!\"", m)); } else if(msgid == 3 && c->land != laPalace) { addMessage(XLAT("%The1 kisses you, and thanks you for saving %him1.", m)); } else if(msgid == 4 && c->land != laPalace && m == moPrincess) { addMessage(XLAT("\"I have been trained to fight with a Hypersian scimitar, you know?\"", m)); } else if(msgid == 5 && c->land != laPalace) { addMessage(XLAT("\"I would love to come to your world with you!\"", m)); } else if(msgid == 6 && c->land != laPalace) { addMessage(XLAT("\"Straight lines stay close to each other forever, this is so romantic!\"", m)); } else if(msgid == 7 && c->land != laPalace) { addMessage(XLAT("\"Maps... Just like the world, but smaller... how is that even possible?!\"", m)); } else { msgid++; goto retry; } msgid++; } void playernear(cell *c) { info *i = getPrisonInfo(c); int d = dist(c); // if(i) printf("d=%d bn=%d\n", d, i->bestnear); if(i && d < i->bestnear) { if(i->bestnear > 100 && d <= 100) { i->value = items[itPalace]; if(princess::challenge) addMessage(XLAT("Hardness frozen at %1.", its(i->value))); } i->bestnear = d; } } } namespace clearing { struct clearingdata { cell *root; int dist; }; bool buggyplant = false; std::map bpdata; int plantdir(cell *c) { generateAlts(c->master); for(int i=0; i<7; i++) generateAlts(c->master->move[i]); int d = celldistAlt(c); if(purehepta) { for(int i=0; i<7; i++) { cell *c2 = createMov(c, i); if(!pseudohept(c2) && celldistAlt(c2) == d-1) return i; } for(int i=0; i<7; i++) { cell *c2 = createMov(c, i); if(celldistAlt(c2) == d-1) return (i+1) % 7; } } for(int i=1; i<6; i+=2) { cell *c2 = createMov(c, i); if(celldistAlt(c2) == d-1) return i; } int quseful = 0, tuseful = 0; for(int i=1; i<6; i+=2) { cell *c2 = c->mov[i]; if(celldistAlt(c2) == d) { bool useful = false; for(int j=1; j<6; j++) { cell *c3 = createMov(c2, j); if(celldistAlt(c3) == d-1) useful = true; } if(useful) quseful++, tuseful += i; } } if(quseful == 1) return tuseful; if(quseful == 2) { int i; if(tuseful == 3+5) i = 3; if(tuseful == 5+1) i = 5; if(tuseful == 1+3) i = 1; if((d & 7) < 4) i = (i+2) % 6; return i; } printf("error in plantdir\n"); return 1; } vector onpath; vector pdir; vector rpath; void generate(cell *c) { if(buggyplant) return; if(euclid) { if(pseudohept(c)) return; c->monst = moMutant; eucoord x, y; decodeMaster(c->master, x, y); int xco = x * 2 + y + 1; c->stuntime = (8-xco/2) & 15; // 2, 4, 5, 7 if(pseudohept(createMov(c, 0))) c->mondir = 1 + hrand(2) * 4; else c->mondir = 0; return; } // cell *oc = c; if(!euclid) generateAlts(c->master); if(pseudohept(c)) return; heptagon *a = euclid ? NULL : c->master->alt->alt; clearingdata& bd(bpdata[a]); if(!bd.root) { bd.root = c; bd.dist = 8; } onpath.clear(); pdir.clear(); rpath.clear(); int steps = 0; int ds; while(true) { if(c == bd.root) {ds = bd.dist; break; } // printf("R %4d C %4d\n", celldistAlt(bd.root), celldistAlt(c)); if(celldistAlt(c) > celldistAlt(bd.root)) { if(c->mpdist <= 6) { if(c->monst != moMutant) return; // already cut! // ... else simply extend it ds = c->stuntime; break; } int d = plantdir(c); steps++; onpath.push_back(c); pdir.push_back(d); // printf("c [%4d] %p -> %p\n", celldistAlt(c), c, c->mov[d]); c = c->mov[d]; } else { bd.dist--; if(bd.dist < -1000) { for(int i=0; iitem = itBuggy; for(int i=0; i<(int) rpath.size(); i++) rpath[i]->item = itBuggy; buggyplant = true; printf("buggygen\n"); return; } rpath.push_back(bd.root); // printf("r [%4d] %p -> %p\n", celldistAlt(bd.root), bd.root, bd.root->mov[plantdir(bd.root)]); bd.root = bd.root->mov[plantdir(bd.root)]; } } // printf("steps = %d dist = %d [%d]\n", steps, bd.dist, oc->mpdist); onpath.push_back(c); pdir.push_back(plantdir(c)); while(steps >= 0) { c = onpath[steps]; if(steps == 0) { c->monst = moMutant; c->mondir = pdir[steps]; if(pdir[steps] != plantdir(c)) { printf("pdir i/ plantdir\n"); exit(1); } c->stuntime = ds; } if(c->mpdist <= 7 && c->monst != moMutant) break; steps--; ds++; } } } namespace whirlpool { bool escaped = false; // escaped the Whirlpool? // next == +1 -> next // next == -1 -> prev cell *get(cell *c, int next) { int i = 0; if(!euclid && !c->master->alt) return NULL; int d = celldistAlt(c); int d2; while(true) { if(i == c->type) return NULL; if(c->mov[i] && (d2 = celldistAlt(c->mov[i])) != d) break; i++; } if(i == c->type) return NULL; if(d>d2) next = -next; for(int j=1; jtype; j++) { cell *c2 = c->mov[(i+42+next*j) % c->type]; if(celldistAlt(c2) == d) return c2; } return NULL; } void build(vector& whirlline, int d) { again: cell *at = whirlline[size(whirlline)-1]; cell *prev = whirlline[size(whirlline)-2]; for(int i=0; itype; i++) if(at->mov[i] && (euclid || at->mov[i]->master->alt) && celldistAlt(at->mov[i]) == d && at->mov[i] != prev) { whirlline.push_back(at->mov[i]); goto again; } } void generate(cell *wto) { if(wto->wall == waBoat || wto->monst) return; if(hrand(35000) < 40 + items[itWhirlpool] + yendor::hardness()) wto->monst = moCShark; else if(hrand(5000) < 500) wto->wall = waBoat; if(wto->wall == waBoat && (euclid || wto->master->alt)) { int d = celldistAlt(wto); if(yendor::on) d -= 200; // 250 : hard if(hrand(5000) < 60 + 3 * items[itWhirlpool] + yendor::hardness()) wto->monst = moPirate; if(hrand(5000) < 20 && d < -20 && !tactic::on) wto->item = itOrbSafety; else if(hrand(5000) < 20 && d < -20 && !tactic::on && markOrb(itOrbLuck)) wto->item = itOrbSafety; else if(hrand(5000) < 20*PRIZEMUL && d < -20) placePrizeOrb(wto); else if(items[itWhirlpool] >= 10 && hrand(5000) < 20 && d < -15) wto->item = itOrbWater; else if(d<-10 && hrand(5000) < 1000-d) wto->item = itWhirlpool; } } void whirlMove(cell *wto, cell *wfrom) { // monsters don't move if(wfrom && (wfrom == cwt.c || wfrom->monst)) return; // disappear if(!wto) { wfrom->wall = waSea; wfrom->item = itNone; } if(wfrom && wto && wfrom->wall == waBoat && wto->wall == waSea && !wto->monst) { wfrom->wall = waSea; wto->wall = waBoat; } if(wfrom && wto && wfrom->item && !wto->item && wfrom->wall != waBoat) { // Keys and Orbs of Yendor never disappear! if(wfrom->item == itKey || wfrom->item == itOrbYendor) for(int i=0; itype; i++) createMov(wto, i); moveItem(wfrom, wto, false); } if(wto && !wfrom) generate(wto); } void moveAt(cell *c) { if(c->land != laWhirlpool) return; if(eq(c->aitmp, sval)) return; if(!(euclid || c->master->alt)) return; cell *c2 = get(c, 1); if(!c2) return; int d = celldistAlt(c); vector whirlline; whirlline.push_back(c); whirlline.push_back(c2); build(whirlline, d); reverse(whirlline.begin(), whirlline.end()); build(whirlline, d); int z = size(whirlline); for(int i=0; iaitmp = sval; whirlMove(NULL, whirlline[0]); for(int i=0; iwall == waNone || c->wall == waCavefloor || isAlchAny(c) || c->wall == waFrozenLake || c->wall == waDeadfloor || c->wall == waDeadfloor2 || c->wall == waGiantRug || c->wall == waCIsland || c->wall == waCIsland2 || c->wall == waGargoyleFloor || c->wall == waRubble || c->wall == waGargoyleBridge || c->wall == waTempFloor || c->wall == waTempBridge; } void createMM(cellwalker& cw, eMonster type) { if(type == moLightningBolt) castLightningBolt(cw); else if(cw.c->monst == moNone && cellMirrorable(cw.c) && cw.c != cwt.c) { cw.c->monst = type; cw.c->mondir = cw.spin; } } void createMirrors(cell *c, int dir, eMonster type) { cellwalker C(c, dir); if(type == moMirror) type = moMirage; else if(type == moMirage) type = moMirror; for(int i=0; itype; i++) { cwstep(C); if(C.c->type == c->type) { cwspin(C, i); createMM(C, type); cwspin(C, -i); } cwstep(C); cwspin(C, 1); } } void createMirages(cell *c, int dir, eMonster type) { cellwalker C(c, dir); if(purehepta) { for(int i=0; itype; i++) { cellwalker C2 = C; cwstep(C2); cwspin(C2, 3); cwstep(C2); cwspin(C2, 5); cwstep(C2); cwspin(C2, 3); cwspin(C2, -i); createMM(C2, type); cwspin(C, 1); } return; } for(int i=0; i<6; i++) { cwstep(C); if(C.c->type == 6) { cwspin(C, 2); cwstep(C); cwspin(C, 4-i); createMM(C, type); cwspin(C, 6-4+i); cwstep(C); cwspin(C, 2); cwstep(C); cwspin(C, 2-i); createMM(C, type); cwspin(C, 6-2+i); cwstep(C); cwspin(C, 2); } cwstep(C); cwspin(C, 1); } } void spin(int d) { for(int i=0; imonst == moMirror) mirrors[i]->mondir = (mirrors[i]->mondir - d + 42) % mirrors[i]->type; if(c->monst == moMirage) mirrors[i]->mondir = (mirrors[i]->mondir + d + 42) % mirrors[i]->type; } } void destroy() { for(int i=0; imonst; if(isMimic(m)) c->monst = moNone; } mirrors.clear(); } void destroyStray() { for(int i=0; icpdist > 7 && isMimic(c)) { c->monst = moNone; } } } void go(bool fwd) { int tk = tkills(); int nummirage = 0; mirrors2.clear(); for(int i=0; imonst; if(isMimic(m)) { if(m == moMirage) nummirage++; int dir = c->mondir; cell *c2 = c->mov[dir]; if(c2 && !isMimic(c2) && canAttack(c,m,c2,c2->monst, 0)) killWithMessage(c2, true, m); if(c2->wall == waBigTree) c2->wall = waSmallTree; else if(c2->wall == waSmallTree) c2->wall = waNone; if(!fwd) continue; c->monst = moNone; if(!c2) continue; if(!passable(c2, c, P_MONSTER | P_MIRROR)) continue; if(isWorm(c2)) continue; if(c2->monst == moGreater) { c2->monst = moLesser; continue; } if(c2->monst == moGreaterM) { c2->monst = moLesserM; continue; } if(c2 == cwt.c) { addMessage(XLAT("You join %the1.", m)); continue; } if(isMimic(c2)) { addMessage(XLAT("Two of your images crash and disappear!")); c2->monst = moNone; continue; } if(isIvy(c2) || c2->monst) { // killIvy(c2); continue; } c->monst = m; moveMonster(c2, c); empathyMove(c, c2, dir); mirrors2.push_back(c2); } } for(int i=0; imonst; if(c->wall == waMirror) { addMessage(XLAT("%The1 breaks the mirror!", m)); createMirrors(c, c->mondir, m); c->wall = waNone; } if(c->wall == waCloud) { addMessage(XLAT("%The1 disperses the cloud!", m)); createMirages(c, c->mondir, m); c->wall = waNone; } } achievement_count("MIRRORKILL", tkills(), tk); achievement_count("MIRAGE", nummirage, 0); } } namespace hive { struct buginfo_t { cell *where; short dist[BUGCOLORS]; }; vector buginfo; vector bugqueue[BUGCOLORS]; vector bugqueue4[BUGCOLORS]; struct bugtomove_t { int dist, moves, index; bugtomove_t(int d, int m, int i) { dist=d; moves=m; index=i; } }; bool operator < (const bugtomove_t& m1, const bugtomove_t& m2) { if(m1.dist != m2.dist) return m1.dist < m2.dist; if(m1.moves != m2.moves) return m1.moves < m2.moves; return false; } vector bugtomove; vector deadbug; vector bugcellq; int bugcount[BUGCOLORS]; bool isBugEnemy(cell *c, int k) { if(c == cwt.c && !invismove) return true; if(!c->monst) return false; if(c->monst == moBug0+k) return false; if(isIvy(c)) return false; return true; } // list bugs and targets for each color #define BUGINF 29999 void bugQueueInsert(int k, int i, int d) { if(buginfo[i].dist[k] > d) { if(buginfo[i].dist[k] != BUGINF) { printf("%d -> %d\n", buginfo[i].dist[k], d); } buginfo[i].dist[k] = d; bugqueue[k].push_back(i); } } void bugcell(cell *c) { short& i(c->aitmp); if(i >= 0 && i < size(buginfo) && buginfo[i].where == c) return; i = size(buginfo); buginfo.resize(i+1); buginfo_t& b(buginfo[i]); b.where = c; for(int k=0; ktype; dir++) { cell *c2 = c->mov[dir]; if(c2 && isBugEnemy(c2,k) && canAttack(c,eMonster(moBug0+k),c2,c2->monst, AF_TOUGH | AF_NOSHIELD | AF_GETPLAYER)) { if(isBug(c2)) havebug = true; else haveother = true; } } if(havebug) bugQueueInsert(k, i, 0); else if(haveother) bugqueue4[k].push_back(i); } /*// bugs communicate if the distance is at most 2 // also all nearby cells are inserted to the buginfo structure if(size(buginfo) < 30000) { for(int dir=0; dirtype; dir++) { cell *c2 = c->mov[dir]; if(c2) { // if(isBug(c)) bugcellq.push_back(c2); => does not help... for(int t=0; ttype; t++) if(c2->mov[t] && isBug(c2->mov[t])) bugcellq.push_back(c2), bugcellq.push_back(c2->mov[t]); } } }*/ // use pheromones! if(c->land == laHive && c->landparam > 1 && c->wall != waWaxWall) { c->landparam --; for(int dir=0; dirtype; dir++) { cell *c2 = c->mov[dir]; if(c2) { for(int t=0; ttype; t++) if(c2->mov[t]) bugcellq.push_back(c2), bugcellq.push_back(c2->mov[t]); } } } } int last_d = -1; void handleBugQueue(int k, int t) { int i = bugqueue[k][t]; buginfo_t& b(buginfo[i]); cell *c = b.where; int d = b.dist[k]; last_d = d; int goodmoves = 0; for(int dir=0; dirtype; dir++) { cell *c2 = c->mov[dir]; if(!c2) continue; if(c2->aitmp < 0 || c2->aitmp >= size(buginfo)) continue; if(!passable(c, c2, P_MONSTER)) continue; int j = c2->aitmp; if(buginfo[j].where != c2) continue; if(buginfo[j].dist[k] < d) goodmoves++; bugQueueInsert(k, j, d+1); } if(isBug(c) && c->monst == moBug0+k) { bugcount[c->monst - moBug0]++; bugtomove.push_back(bugtomove_t(d,goodmoves,i)); } } #include bool fightspam(cell *c) { return c->cpdist >= 7 || isMetalBeast(c->monst) || c->monst == moSkeleton || isIvy(c->monst) || isMutantIvy(c->monst); } void movebugs() { buginfo.clear(); for(int k=0; k check; for(int t=0; tstuntime) continue; eMonster m = c->monst; int k = (m - moBug0) % BUGCOLORS; int gmoves[8], q=0, bqual = -1; if(againstRose(c, NULL)) bqual = -40; for(int dir=0; dirtype; dir++) { cell *c2 = c->mov[dir]; int qual = -10; if(!c2) continue; else if(againstRose(c, c2)) qual = -50; else if(canAttack(c, m, c2, c2->monst, AF_GETPLAYER)) qual = c2->monst == moDeadBug ? -60: isBugEnemy(c2,k) ? 2 : -20; else if(!passable(c2, c, 0)) qual = passable(c2, c, P_DEADLY) ? -30 : -60; else if(c2->aitmp < 0 || c2->aitmp >= size(buginfo)) qual = -15; else if(buginfo[c2->aitmp].where != c2) qual = -15; else if(buginfo[c2->aitmp].dist[k] < b.dist[k]) qual = 1; else if(buginfo[c2->aitmp].dist[k] == b.dist[k]) qual = 0; // printf("%d->#%d %d: %d\n", i, dir, c2->tmp, qual); if(qual > bqual) bqual = qual, q=0; if(qual == bqual) gmoves[q++] = dir; } if(!q) { if(c->land == laHive) c->landparam += 3; continue; } int d = gmoves[hrand(q)]; cell *c2 = c->mov[d]; if(c2->monst) { eMonster killed = c2->monst; if(isBug(killed)) battlecount++; else if(!fightspam(c2)) addMessage(XLAT("%The1 fights with %the2!", c->monst, c2->monst)); killOrStunMonster(c2); // killMonster(c); if(isBug(killed)) { c2->monst = moDeadBug, deadbug.push_back(c2); bugcount[killed - moBug0]--; } // c->monst = moDeadBug, deadbug.push_back(c); } else { moveMonster(c2, c); // pheromones! if(c->land == laHive && c->landparam < 90) c->landparam += 5; if(c2->land == laHive && c2->landparam < 90) c2->landparam += 5; // if(isHive(c2->land)) c2->land = eLand(laHive0+k); /* if(c2->item == itRoyalJelly && !isQueen(m)) { // advance! c2->monst = eMonster(m+BUGCOLORS); c2->item = itNone; } */ } } // cleanup for(int i=0; imonst = moNone; if(battlecount) addMessage(XLAT("The Hyperbugs are fighting!")); int maxbug = 0; for(int k=0; k maxbug) maxbug = bugcount[k]; achievement_count("BUG", maxbug, 0); } void bugcitycell(cell *c, int d) { short& i = c->aitmp; if(i >= 0 && i < size(buginfo) && buginfo[i].where == c) return; i = size(buginfo); buginfo_t b; b.where = c; b.dist[0] = d; buginfo.push_back(b); } void createBugArmy(cell *c) { int k = randomHyperbug() - moBug0; int minbugs = 50, maxbugs = 50; int var = 5 + items[itRoyalJelly]; if(var>25) var=25; // minbugs += 100; maxbugs += 100; minbugs -= var; maxbugs += var; maxbugs += items[itRoyalJelly]; int numbugs = minbugs + hrand(maxbugs - minbugs + 1); /* int i = items[itRoyalJelly]; int chance = 20 + 25 * i + 9000; // i=0: 16% // i=10: 73% // i=50: 1270 vs 6000 eMonster m = eMonster(moBug0 + hrand(BUGCOLORS)); if(c->wall) return; for(int i=0; itype; i++) { cell *c2 = createMov(c,i); if(hrand(100+chance) < chance) { if(!c2->wall) c2->monst = m; for(int j=2; j<=c2->type-2; j++) { int jj = (j+c->spn[i]) % c2->type; cell *c3 = createMov(c2, jj); if(hrand(6000+chance) < chance && !c3->wall) c3->monst = m; } } } c->monst = eMonster(m + BUGCOLORS); */ int gdir = -1; for(int i=0; itype; i++) { if(c->mov[i] && c->mov[i]->mpdist < c->mpdist) gdir = i; } if(!gdir) return; cellwalker bf(c, gdir); int radius = 9; if(chaosmode) radius = 5; for(int i=2; itype == 6) cwspin(bf, 3); else cwspin(bf, 3 + hrand(2)); cwstep(bf); } cell *citycenter = bf.c; buginfo.clear(); // mark the area with BFS bugcitycell(citycenter, 0); for(int i=0; iland != laHive && c->land != laNone) return; if(c->bardir != NODIR) return; if(c->land == laHive && c->landparam >= 100) return; // bfs if(d < radius) for(int t=0; ttype; t++) bugcitycell(createMov(c,t), d+1); } // place everything for(int i=0; iwall == waNone) c->item = itRoyalJelly; c->bardir = NOBARRIERS; if(d == 9 || d == 6 || d == 3) c->barleft = eLand(d/3), c->barright = eLand(k); else c->barleft = laNone; if(numbugs && c->wall == waNone) c->monst = eMonster(moBug0 + k), numbugs--; c->land = laHive; // prevent barriers if(c->mpdist == INFD) c->mpdist = BUGLEV; } } } inline float& HEAT(cell *c) { return c->LHU.heat; } namespace heat { vector vinefires; vector > rosefires; double absheat(cell *c) { if(c->land == laCocytus) return HEAT(c) -.6; if(c->land == laIce) return HEAT(c) -.4; return 0; } double celsius(cell *c) { return absheat(c) * 60; } void processheat(double rate = 1, bool tick = true) { if(markOrb(itOrbSpeed)) rate /= 2; int oldmelt = kills[0]; vector offscreen2; for(int i=0; icpdist > 7) { bool readd = false; if(isIcyLand(c)) { if(HEAT(c) < .01 && HEAT(c) > -.01) HEAT(c) = 0; else { HEAT(c) *= 1 - rate/10; readd = true; } } if(hasTimeout(c)) { useup(c); if(hasTimeout(c)) readd = true; } if(readd) offscreen2.push_back(c); } } offscreen.clear(); swap(offscreen, offscreen2); /* if(cwt.c->heat > .5) cwt.c->heat += .3; if(cwt.c->heat > 1.) cwt.c->heat += .3; if(cwt.c->heat > 1.4) cwt.c->heat += .5; */ for(int i=0; iland == laCocytus && shmup::on) ? rate/3 : rate; if(purehepta) xrate *= 1.7; if(isIcyLand(c)) HEAT(c) += (markOrb(itOrbWinter) ? -1.2 : 1.2) * xrate; } vinefires.clear(); rosefires.clear(); int dcs = size(dcal); for(int i=0; iland == laCocytus && shmup::on) ? rate/3 : rate; if(purehepta) xrate *= 1.7; if(c->cpdist > 8) break; if(hasTimeout(c)) { if(tick) useup(c); readd = true; } if(isFire(c) && tick) { if(c->wall != waPartialFire) for(int i=0; itype; i++) { cell *c2 = c->mov[i]; if(c2 && c2->wall == waNone && c2->land == laRose && c->wparam >= 10) rosefires.push_back(make_pair(c2, c->wparam)); if(c2 && c2->wall == waFire && c2->land == laRose && c->wparam >= 10 && c2->wparam < c->wparam/2) rosefires.push_back(make_pair(c2, c->wparam)); if(c2 && c2->wall == waVinePlant) vinefires.push_back(c2); if(c2 && c2->wall == waRose) vinefires.push_back(c2); if(c2 && c2->wall == waSaloon) vinefires.push_back(c2); if(c2 && c2->wall == waSmallTree && c2->land != laDryForest) vinefires.push_back(c2); if(c2 && (c2->wall == waWeakBranch || c2->wall == waCanopy || c2->wall == waTrunk || c2->wall == waSolidBranch)) vinefires.push_back(c2); if(c2 && c2->wall == waBonfireOff) activateActiv(c2, false); // both halfvines have to be near fire at once if(c2 && cellHalfvine(c2) && c->mov[(i+1)%c->type]->wall == c2->wall) vinefires.push_back(c2); } // two semifires are required to spread if(c->wall == waPartialFire) for(int i=0; itype; i++) { cell *c2 = c->mov[i]; if(c2 && (c2->wall == waVinePlant)) { for(int j=0; jtype; j++) if(c2->mov[j] && c2->mov[j]->wall == waPartialFire && c2->mov[j] != c) vinefires.push_back(c2); } } } if(isIcyLand(c)) { if(c->monst == moRanger) HEAT(c) += 3 * xrate; if(c->monst == moDesertman) HEAT(c) += 4 * xrate; if(c->monst == moMonkey) HEAT(c) += xrate; if(c->wall == waDeadTroll) HEAT(c) -= 2 * xrate; if(c->wall == waDeadTroll2) HEAT(c) -= 1.5 * xrate; if(c->wall == waBigStatue) HEAT(c) -= .5 * xrate; if(c->monst == moLesser || c->monst == moLesserM || c->monst == moGreater || c->monst == moGreaterM) HEAT(c) += (c->land == laCocytus ? 1.5 : 10) * xrate; if(c->monst == moGreaterShark) HEAT(c) += 2 * xrate; if(c->monst == moCultist) HEAT(c) += 3 * xrate; if(c->monst == moCultistLeader) HEAT(c) += 4 * xrate; if(c->monst == moPyroCultist) HEAT(c) += 6 * xrate; if(c->monst == moFireFairy) HEAT(c) += 6 * xrate; if(c->monst == moFireElemental) HEAT(c) += 8 * xrate; if(isDragon(c->monst)) HEAT(c) += 2 * xrate; if(c->monst == moGhost) HEAT(c) -= xrate; if(c->monst == moWaterElemental) HEAT(c) -= xrate; if(isFire(c)) HEAT(c) += 4 * xrate; if(isPrincess(c->monst)) HEAT(c) += (markEmpathy(itOrbWinter) ? -1.2 : 1.2) * xrate; forCellEx(ct, c) if(!isIcyLand(ct) && isFire(ct)) HEAT(c) += xrate*.1; ld hmod = 0; for(int j=0; jtype; j++) if(c->mov[j]) { if(!isIcyLand(c->mov[j])) { // make sure that we can still enter Cocytus, // it won't heat up right away even without Orb of Winter or Orb of Speed if(c->mov[j] == cwt.c && (c->land == laIce || markOrb(itOrbWinter))) hmod += (markOrb(itOrbWinter) ? -1.2 : 1.2) / 4; continue; } ld hdiff = absheat(c->mov[j]) - absheat(c); hdiff /= 10; if(shmup::on && (c->land == laCocytus || c->mov[j]->land == laCocytus)) hdiff /= 3; if(c->mov[j]->cpdist <= 7) HEAT(c->mov[j]) -= hdiff * rate; else hdiff = -HEAT(c) / 30; hmod += hdiff; } HEAT(c) += hmod * rate; if(c->monst == moCrystalSage && HEAT(c) >= SAGEMELT) { addMessage(XLAT("%The1 melts away!", c->monst)); killWithMessage(c, false); } } if(readd || HEAT(c)) offscreen.push_back(c); } for(int i=0; iwall == waIcewall && HEAT(c) > .4) c->wall = waNone, kills[0]++; if(c->wall == waFrozenLake && HEAT(c) > (c->land == laCocytus ? .6 : .4)) c->wall = waLake, kills[0]++; if(c->wall == waLake && HEAT(c) < (c->land == laCocytus ? -.4 : .4) && c->monst != moGreaterShark) { c->wall = waFrozenLake; if(c->monst == moShark || c->monst == moCShark) { addMessage(XLAT("%The1 is frozen!", c->monst)); killWithMessage(c, false); } } } if(tick) for(int i=0; iwall == waNone && c->land == laRose) makeflame(c, 6, false); else if(c->wall == waVinePlant || c->wall == waSmallTree || c->wall == waSaloon || c->wall == waRose) makeflame(c, 6, false); else if(c->wall == waSolidBranch || c->wall == waTrunk || c->wall == waWeakBranch || c->wall == waCanopy) makeflame(c, 6, false); else if(cellHalfvine(c)) destroyHalfvine(c, waPartialFire, 6); } if(tick) for(int i=0; iwall == waNone && c->land == laRose) makeflame(c, qty, false); if(c->wparam < qty) c->wparam = qty; } if(kills[0] != oldmelt) bfs(); } void dryforest() { int dcs = size(dcal); for(int i=0; icpdist > 8) break; if(c->land != laDryForest) continue; for(int j=0; jtype; j++) if(c->mov[j]) { if(isFire(c->mov[j])) c->landparam++; } if(c->landparam >= 10) makeflame(c, 10, false), c->landparam = 0; } for(int i=0; icpdist > 8) break; if(c->land != laDryForest) continue; if((c->wall == waBigTree || c->wall == waSmallTree || isFire(c)) && c->landparam >= 1) c->wall = waEternalFire; } } } bool gardener = false; bool lifebrought = false; // was Life brought to the Dead Caves? void livecaves() { int dcs = size(dcal); vector bringlife; for(int i=0; icpdist > 8) break; if(c->wall == waCavefloor || c->wall == waCavewall) { c->aitmp = 0; if(c->monst == moDarkTroll) c->monst = moTroll; if(c->item || c->monst || c->cpdist == 0) continue; for(int j=0; jtype; j++) if(c->mov[j]) { if(c->mov[j]->wall == waDeadfloor) c->aitmp++, bringlife.push_back(c->mov[j]); else if(c->mov[j]->wall == waDeadwall || (c->mov[j]->wall == waDeadfloor2 && !c->mov[j]->monst)) c->aitmp--, bringlife.push_back(c->mov[j]); else if(c->mov[j]->wall == waCavefloor) c->aitmp++; else if(c->mov[j]->wall == waCavewall) c->aitmp--; else if(c->mov[j]->wall == waRubble) c->aitmp--; else if(c->mov[j]->wall == waGargoyle) c->aitmp--; else if(c->mov[j]->wall == waGargoyleFloor) c->aitmp--; else if(c->mov[j]->wall == waGargoyleBridge) c->aitmp--; else if(c->mov[j]->wall == waDeadTroll) c->aitmp -= 5; else if(c->mov[j]->wall == waDeadTroll2) c->aitmp -= 3; else if(c->mov[j]->wall == waVinePlant) c->aitmp--; else if(chaosmode && c->mov[j]->land != laCaves && c->mov[j]->land != laEmerald) ; else if(c->mov[j]->wall != waBarrier) c->aitmp += 5; if(c->mov[j]->cpdist == 0 && markOrb(itOrbDigging)) c->aitmp+=100; if(items[itOrbEmpathy] && isFriendly(c->mov[j]) && markEmpathy(itOrbDigging)) c->aitmp+=100; if(c->mov[j]->wall == waThumperOn) c->aitmp+=100; if(c->mov[j]->wall == waFire) c->aitmp+=100; if(c->mov[j]->wall == waBigStatue) c->aitmp-=100; if(c->mov[j]->item) c->aitmp+=2; if(c->mov[j]->monst == moZombie) c->aitmp += 10; if(c->mov[j]->monst == moGhost) c->aitmp += 10; if(c->mov[j]->monst == moTentacleGhost) c->aitmp += 10; if(c->mov[j]->monst == moFriendlyGhost) c->aitmp += 10; if(c->mov[j]->monst == moGargoyle) c->aitmp--; if(isDragon(c->mov[j]->monst)) c->aitmp++; if(c->mov[j]->monst == moNecromancer) c->aitmp += 10; if(c->mov[j]->monst == moWormtail) c->aitmp++; if(c->mov[j]->monst == moTentacletail) c->aitmp-=2; if(isIvy(c->mov[j])) c->aitmp--; if(isDemon(c->mov[j])) c->aitmp-=3; // if(c->mov[j]->monst) c->tmp++; // if(c->mov[j]->monst == moTroll) c->tmp -= 3; } } else if(c->land == laLivefjord) { c->aitmp = 0; if(c->monst == moWaterElemental) c->aitmp += 1000; if(isPlayerInBoatOn(c) && markOrb(itOrbWater)) c->aitmp += 1000; if(c->monst == moEarthElemental) c->aitmp -= 1000; if(isPlayerOn(c) && markOrb(itOrbDigging)) c->aitmp -= 1000; for(int j=0; jtype; j++) if(c->mov[j]) { cell *c2 = c->mov[j]; if(c2->wall == waNone || c2->wall == waStrandedBoat) c->aitmp -= (c2->land == laLivefjord ? 1 : 100); if(c2->wall == waTempFloor || c2->wall == waTempBridge) ; else if(c2->wall == waDeadTroll || c2->wall == waDeadTroll2 || c2->wall == waThumperOn || isFire(c2) || snakelevel(c2)) c->aitmp -= 10; if(c2->wall == waBigStatue) c->aitmp -= 10; if(c2->wall == waSea || c2->wall == waBoat) c->aitmp += (c2->land == laLivefjord ? 1 : 100); if(c2->monst == moWaterElemental) c->aitmp += 1000; if(c2 == cwt.c && c2->wall == waBoat && markOrb(itOrbWater)) c->aitmp += 1000; if(c2->monst == moEarthElemental) c->aitmp -= 1000; if(c2 == cwt.c && markOrb(itOrbDigging)) c->aitmp -= 1000; if(items[itOrbEmpathy] && isFriendly(c2) && markEmpathy(itOrbDigging)) c->aitmp -= 1000; if(c2->wall == waBarrier) { bool landbar = false; for(int k=0; ktype; k++) if(c2->mov[k]) { cell *c3 = c2->mov[k]; if(!isSealand(c3->land)) landbar = true; } if(landbar) c->aitmp -= 5; else c->aitmp += 5; } } } } for(int i=0; icpdist > 8) break; if(c->wall == waCavefloor || c->wall == waCavewall) { // if(c->land != laCaves) continue; // if(c->wall == waThumper || c->wall == waBonfire) continue; if(c->aitmp > 0) c->wall = waCavefloor; if(c->aitmp < 0) { c->wall = waCavewall; if(c->land != laCaves && c->land != laDeadCaves && c->land != laEmerald && !gardener) { gardener = true; achievement_gain("GARDENER"); } } } else if(c->land == laLivefjord) { if(c->aitmp > 0 && c->wall == waStrandedBoat) c->wall = waBoat; if(c->aitmp > 0 && c->wall == waNone) { if(c->item && c->cpdist == 1 && markOrb(itOrbWater)) collectItem(c); c->wall = waSea; } if(c->aitmp < 0 && c->wall == waBoat) c->wall = waStrandedBoat; if(c->aitmp < 0 && c->wall == waSea) c->wall = waNone; } } for(int i=0; iland == laDeadCaves && !lifebrought) { lifebrought = true; achievement_gain("LIFEBRINGER"); } if(c->wall == waDeadfloor) c->wall = waCavefloor; if(c->wall == waDeadfloor2) c->wall = waCavewall; if(c->wall == waDeadwall) c->wall = waCavewall; if(c->wall == waCavewall && c->item) c->wall = waCavefloor; if(c->land == laDeadCaves) c->land = laCaves; if(c->item == itSilver) c->item = itGold; if(c->item == itGreenStone) c->item = itOrbLife; if(c->monst == moEarthElemental) { addMessage(XLAT("%The1 is destroyed by the forces of Life!", c->monst)); killWithMessage(c, false); c->item = itOrbDigging; } } } /* evolver */ namespace tortoise { map emap; map babymap; enum tflag { tfShell, tfScute0, tfScute1, tfScute2, tfScute3, tfEdge1, tfEdge, tfEdge3, tfLongNeck, tfFront, tfRear, tfTail, tfEyeHue, tfShellHue, tfScuteHue, tfSkinHue, tfShellSat, tfScuteSat, tfSkinSat, tfShellDark, tfSkinDark, tfCOUNT }; const int numbits = (int) tfCOUNT; const int mask = (1<> i)&1) bi++; return bi; } int getBit(int bits, int id) { return (bits >> id) & 1; } int getRandomBits() { return hrand(1 << numbits); } bool seek() { return items[itBabyTortoise] % 5; } int seekbits; double seekval[numbits]; double currval[numbits]; void update(double& val, double target, int delta) { double d = delta / 300.; if(d>1) d = 1; if(target>val+d) val += d; else if(target res // 1 1 1 => 0 // 1 1 0 => -1 // 1 0 1 => +1 // 1 0 0 => 0 // 0 1 1 => 0 // 0 1 0 => +1 // 0 0 1 => -1 // 0 0 0 => 0 return res; } int diff(int bits) { return countBits(bits ^ tortoise::seekbits); } int progress(int bits) { return numbits - diff(bits); } string measure(int bits) { return "(" + its(progress(bits)) + "/" + its(tortoise::numbits) + ")"; } } namespace dragon { int whichturn; // which turn has the target been set on cell *target; // actually for all Orb of Control void pullback(cell *c) { int maxlen = 1000; while(maxlen-->0) { cell *c2 = c->mov[c->mondir]; if(c2 == cwt.c) mountmove(c, c->mondir, true); c->monst = c2->monst; c->hitpoints = c2->hitpoints; c->stuntime = 2; if(c2->mondir == NODIR) { c->mondir = NODIR; c2->monst = moNone; return; } c = c2; } } cell *findhead(cell *c) { cell *cor = c; int maxlen=1000; findhead: if(maxlen--<0) return c; if(c->monst == moDragonHead) return c; for(int i=0; itype; i++) if(c->mov[i] && isDragon(c->mov[i]->monst) && c->mov[i]->mondir == c->spn[i]) { c = c->mov[i]; goto findhead; } printf("dragon bug #3 (%p -> %p)\n", cor, c); return c; } int bodypart(cell *c, cell *head) { int i = 0, j = 0; int maxlen = 1000; while(maxlen-->0) { if(head == c) i = j; j++; if(head->mondir == NODIR) break; head = head->mov[head->mondir]; } if(i == 0) return 'h'; if(i == 1) return 'l'; if(i == j-2) return '2'; if(i == j-1) return 't'; if(i == 2) return 'w'; return 0; } void kill(cell *c) { int delay = false; kills[moDragonHead]++; int penalty = 0; int maxlen = 1000; while(maxlen-->0) { makeflame(c, 5, false); c->monst = moNone; if(c->wall == waFire) { if(delay) delay = false; else { if(c->land != laDragon) penalty += 3; if(penalty) penalty--; else { c->item = itDragon; c->landparam = shmup::on ? shmup::curtime : turncount; delay = true; } } } if(c->mondir == NODIR) break; c = c->mov[c->mondir]; } } int totalhp(cell *c) { int total = 0; int maxlen = 1000; while(maxlen-->0) { if(!isDragon(c->monst)) { printf("dragon bug #4\n"); return total; } total += c->hitpoints; if(c->mondir == NODIR) return total; c = c->mov[c->mondir]; } return total; } #define SWAPBITFIELD(x,y,t) { t bak=x; x=y; y=bak; } void pullfront(cell *c, cell *until) { cell *buffer = c; int maxlen = 1000; while(maxlen-->0) { SWAPBITFIELD(c->monst, buffer->monst, eMonster); SWAPBITFIELD(c->hitpoints, buffer->hitpoints, int); if(c == cwt.c) cwt.c = buffer; else if(buffer == cwt.c) mountmove(c, c->mondir, true); c->stuntime = 2; if(c == until) { while(true) { if(c->mondir == NODIR) return; c = c->mov[c->mondir]; c->stuntime = 2; } } if(c->mondir == NODIR) { printf("dragon bug\n"); break; } c = c->mov[c->mondir]; if(!c) { printf("dragon bug #2\n"); break; } } } bool move(cell *dt, cell *df) { if(df->monst == moDragonHead) { dt->mondir = neighborId(dt,df); // printf("pull back\n"); pullback(dt); dt->stuntime = 2; return true; } if(df->monst == moDragonTail && df->stuntime == 0) { cell *head = findhead(df); if(df->mondir == NODIR) { df->mondir = neighborId(df,dt); dt->mondir = NODIR; // printf("pull all: head = %p, df=%p, dt=%p\n", head, df, dt); pullfront(head, dt); } else { cell *c2 = df->mov[df->mondir]; if(!c2) return false; int id = neighborId(dt, c2); if(id == -1) return false; dt->mondir = id; df->mondir = neighborId(df, dt); // printf("pull front: head = %p, df=%p, dt=%p\n", head, df, dt); pullfront(head, dt); } return true; } return false; } }