mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-11-30 15:39:54 +00:00
subdivided game.cpp; split movepcto into separate functions
This commit is contained in:
parent
2fb1210811
commit
26fb19e7a8
@ -159,7 +159,7 @@ makeh$(EXE_EXTENSION): makeh.cpp
|
||||
$(CXX) makeh.cpp -o $@
|
||||
|
||||
autohdr.h: makeh$(EXE_EXTENSION) *.cpp
|
||||
./makeh locations.cpp hyperpoint.cpp geometry.cpp goldberg.cpp init.cpp floorshapes.cpp cell.cpp multi.cpp shmup.cpp pattern2.cpp mapeditor.cpp graph.cpp textures.cpp hprint.cpp language.cpp *.cpp > autohdr.h
|
||||
./makeh locations.cpp hyperpoint.cpp geometry.cpp goldberg.cpp init.cpp floorshapes.cpp cell.cpp multi.cpp shmup.cpp pattern2.cpp mapeditor.cpp graph.cpp textures.cpp hprint.cpp language.cpp complex.cpp *.cpp > autohdr.h
|
||||
|
||||
language-data.cpp: langen$(EXE_EXTENSION)
|
||||
./langen > language-data.cpp
|
||||
|
1198
attack.cpp
Normal file
1198
attack.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -79,7 +79,7 @@ int utfsize(char c) {
|
||||
}
|
||||
|
||||
EX int get_sightrange() { return getDistLimit() + sightrange_bonus; }
|
||||
EX int get_sightrange_ambush() { return max(get_sightrange(), ambush_distance); }
|
||||
EX int get_sightrange_ambush() { return max(get_sightrange(), ambush::distance); }
|
||||
|
||||
bool display_data::in_anaglyph() { return vid.stereo_mode == sAnaglyph; }
|
||||
bool display_data::stereo_active() { return vid.stereo_mode != sOFF; }
|
||||
|
@ -23,7 +23,7 @@ EX int newRoundTableRadius() {
|
||||
|
||||
EX int getAnthraxData(cell *c, bool b) {
|
||||
int d = celldistAlt(c);
|
||||
int rad = 28 + 3 * anthraxBonus;
|
||||
int rad = 28 + 3 * camelot::anthraxBonus;
|
||||
while(d < -rad) {
|
||||
d += rad + 12;
|
||||
rad += 3;
|
||||
|
116
cell.cpp
116
cell.cpp
@ -1120,4 +1120,120 @@ EX void clearCellMemory() {
|
||||
|
||||
auto cellhooks = addHook(clearmemory, 500, clearCellMemory);
|
||||
|
||||
EX bool isNeighbor(cell *c1, cell *c2) {
|
||||
for(int i=0; i<c1->type; i++) if(c1->move(i) == c2) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool isNeighborCM(cell *c1, cell *c2) {
|
||||
for(int i=0; i<c1->type; i++) if(createMov(c1, i) == c2) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX int neighborId(cell *ofWhat, cell *whichOne) {
|
||||
for(int i=0; i<ofWhat->type; i++) if(ofWhat->move(i) == whichOne) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
EX int mine_adjacency_rule = 0;
|
||||
|
||||
EX map<cell*, vector<cell*>> adj_memo;
|
||||
|
||||
EX bool geometry_has_alt_mine_rule() {
|
||||
if(WDIM == 2) return VALENCE > 3;
|
||||
if(WDIM == 3) return !among(geometry, gHoroHex, gCell5, gBitrunc3, gCell8, gECell8, gCell120, gECell120);
|
||||
return true;
|
||||
}
|
||||
|
||||
EX vector<cell*> adj_minefield_cells(cell *c) {
|
||||
vector<cell*> res;
|
||||
if(mine_adjacency_rule == 0 || !geometry_has_alt_mine_rule())
|
||||
forCellCM(c2, c) res.push_back(c2);
|
||||
else if(WDIM == 2) {
|
||||
cellwalker cw(c, 0);
|
||||
cw += wstep;
|
||||
cw++;
|
||||
cellwalker cw1 = cw;
|
||||
do {
|
||||
res.push_back(cw.at);
|
||||
cw += wstep;
|
||||
cw++;
|
||||
if(cw.cpeek() == c) cw++;
|
||||
}
|
||||
while(cw != cw1);
|
||||
}
|
||||
else if(adj_memo.count(c)) return adj_memo[c];
|
||||
else {
|
||||
const vector<hyperpoint> vertices = currentmap->get_vertices(c);
|
||||
manual_celllister cl;
|
||||
cl.add(c);
|
||||
for(int i=0; i<isize(cl.lst); i++) {
|
||||
cell *c1 = cl.lst[i];
|
||||
bool shares = false;
|
||||
if(c != c1) {
|
||||
transmatrix T = currentmap->relative_matrix(c1->master, c->master, C0);
|
||||
for(hyperpoint h: vertices) for(hyperpoint h2: vertices)
|
||||
if(hdist(h, T * h2) < 1e-6) shares = true;
|
||||
if(shares) res.push_back(c1);
|
||||
}
|
||||
if(shares || c == c1) forCellEx(c2, c1) cl.add(c2);
|
||||
}
|
||||
println(hlog, "adjacent to ", c, " = ", isize(res));
|
||||
adj_memo[c] = res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
EX vector<int> reverse_directions(cell *c, int dir) {
|
||||
if(PURE) return reverse_directions(c->master, dir);
|
||||
int d = c->degree();
|
||||
if(d & 1)
|
||||
return { gmod(dir + c->type/2, c->type), gmod(dir + (c->type+1)/2, c->type) };
|
||||
else
|
||||
return { gmod(dir + c->type/2, c->type) };
|
||||
}
|
||||
|
||||
EX vector<int> reverse_directions(heptagon *c, int dir) {
|
||||
int d = c->degree();
|
||||
switch(geometry) {
|
||||
case gBinary3:
|
||||
if(dir < 4) return {8};
|
||||
else if(dir >= 8) return {0, 1, 2, 3};
|
||||
else return {dir ^ 1};
|
||||
|
||||
case gHoroTris:
|
||||
if(dir < 4) return {7};
|
||||
else if(dir == 4) return {5, 6};
|
||||
else if(dir == 5) return {6, 4};
|
||||
else if(dir == 6) return {4, 5};
|
||||
else return {0, 1, 2, 3};
|
||||
|
||||
case gHoroRec:
|
||||
if(dir < 2) return {6};
|
||||
else if(dir == 6) return {0, 1};
|
||||
else return {dir^1};
|
||||
|
||||
case gKiteDart3: {
|
||||
if(dir < 4) return {dir ^ 2};
|
||||
if(dir >= 6) return {4, 5};
|
||||
vector<int> res;
|
||||
for(int i=6; i<c->type; i++) res.push_back(i);
|
||||
return res;
|
||||
}
|
||||
|
||||
case gHoroHex: {
|
||||
if(dir < 6) return {12, 13};
|
||||
if(dir >= 12) return {0, 1, 2, 3, 4, 5};
|
||||
const int dt[] = {0,0,0,0,0,0,10,11,9,8,6,7,0,0};
|
||||
return {dt[dir]};
|
||||
}
|
||||
|
||||
default:
|
||||
if(d & 1)
|
||||
return { gmod(dir + c->type/2, c->type), gmod(dir + (c->type+1)/2, c->type) };
|
||||
else
|
||||
return { gmod(dir + c->type/2, c->type) };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ void celldrawer::setcolors() {
|
||||
fcol = 0x404040;
|
||||
for(int a=0; a<21; a++)
|
||||
if((b >> a) & 1)
|
||||
fcol += variant_features[a].color_change;
|
||||
fcol += variant::features[a].color_change;
|
||||
if(c->wall == waAncientGrave)
|
||||
wcol = 0x080808;
|
||||
else if(c->wall == waFreshGrave)
|
||||
@ -506,9 +506,9 @@ void celldrawer::setcolors() {
|
||||
break;
|
||||
|
||||
case waMineUnknown: case waMineMine:
|
||||
if(mineMarkedSafe(c))
|
||||
if(mine::marked_safe(c))
|
||||
fcol = wcol = gradient(wcol, 0x40FF40, 0, 0.2, 1);
|
||||
else if(mineMarked(c))
|
||||
else if(mine::marked_mine(c))
|
||||
fcol = wcol = gradient(wcol, 0xFF4040, -1, sintick(100), 1);
|
||||
// fallthrough
|
||||
|
||||
@ -1255,7 +1255,7 @@ void celldrawer::draw_features() {
|
||||
}
|
||||
|
||||
case waTerraWarrior:
|
||||
drawTerraWarrior(V, randterra ? (c->landparam & 7) : (5 - (c->landparam & 7)), 7, 0);
|
||||
drawTerraWarrior(V, terracotta::randterra ? (c->landparam & 7) : (5 - (c->landparam & 7)), 7, 0);
|
||||
break;
|
||||
|
||||
case waBoat: case waStrandedBoat:
|
||||
|
451
checkmove.cpp
Normal file
451
checkmove.cpp
Normal file
@ -0,0 +1,451 @@
|
||||
// Hyperbolic Rogue - checkmate rule
|
||||
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
|
||||
|
||||
/** \file checkmove.cpp
|
||||
* \brief Check the validity of move (checkmate rule)
|
||||
*/
|
||||
|
||||
#include "hyper.h"
|
||||
|
||||
namespace hr {
|
||||
|
||||
#if HDR
|
||||
#define PUREHARDCORE_LEVEL 10
|
||||
#endif
|
||||
|
||||
/** are we in the hardcore mode */
|
||||
EX bool hardcore = false;
|
||||
/** when did we switch to the hardcore mode */
|
||||
EX int hardcoreAt;
|
||||
|
||||
EX bool pureHardcore() { return hardcore && hardcoreAt < PUREHARDCORE_LEVEL; }
|
||||
|
||||
/** can we still move? */
|
||||
EX bool canmove = true;
|
||||
|
||||
// how many monsters are near
|
||||
EX eMonster who_kills_me;
|
||||
|
||||
EX int lastkills;
|
||||
|
||||
EX bool legalmoves[MAX_EDGE+1];
|
||||
|
||||
EX bool hasSafeOrb(cell *c) {
|
||||
return
|
||||
c->item == itOrbSafety ||
|
||||
c->item == itOrbShield ||
|
||||
c->item == itOrbShell ||
|
||||
(c->item == itOrbYendor && yendor::state(c) == yendor::ysUnlocked);
|
||||
}
|
||||
|
||||
#if HDR
|
||||
struct stalemate1 {
|
||||
eMonster who;
|
||||
cell *moveto;
|
||||
cell *killed;
|
||||
cell *pushto;
|
||||
cell *comefrom;
|
||||
cell *swordlast[2], *swordtransit[2], *swordnext[2];
|
||||
bool isKilled(cell *c);
|
||||
stalemate1(eMonster w, cell *mt, cell *ki, cell *pt, cell *cf) : who(w), moveto(mt), killed(ki), pushto(pt), comefrom(cf) {}
|
||||
};
|
||||
#endif
|
||||
|
||||
bool stalemate1::isKilled(cell *w) {
|
||||
if(w->monst == moNone || w == killed) return true;
|
||||
if(!moveto) return false;
|
||||
|
||||
for(int b=0; b<2; b++)
|
||||
if((w == swordnext[b] || w == swordtransit[b]) && canAttack(moveto, who, w, w->monst, AF_SWORD))
|
||||
return true;
|
||||
|
||||
if(logical_adjacent(moveto, who, w) && moveto != comefrom) {
|
||||
int wid = neighborId(moveto, w);
|
||||
int wfrom = neighborId(moveto, comefrom);
|
||||
int flag = AF_APPROACH;
|
||||
if(wid >= 0 && wfrom >= 0 && anglestraight(moveto, wfrom, wid)) flag |= AF_HORNS;
|
||||
if(canAttack(moveto, who, w, w->monst, flag)) return true;
|
||||
}
|
||||
|
||||
if(isNeighbor(w, comefrom) && comefrom == moveto && killed) {
|
||||
int d1 = neighborId(comefrom, w);
|
||||
int d2 = neighborId(comefrom, killed);
|
||||
int di = angledist(comefrom->type, d1, d2);
|
||||
if(di && items[itOrbSide1-1+di] && canAttack(moveto, who, w, w->monst, AF_SIDE))
|
||||
return true;
|
||||
}
|
||||
|
||||
if(logical_adjacent(comefrom, who, w) && logical_adjacent(moveto, who, w) && moveto != comefrom)
|
||||
if(canAttack(moveto, who, w, w->monst, AF_STAB))
|
||||
return true;
|
||||
|
||||
if(who == moPlayer && (killed || moveto != comefrom) && mirror::isKilledByMirror(w)) return true;
|
||||
if(w->monst == moIvyHead || w->monst == moIvyBranch || isMutantIvy(w))
|
||||
return isChild(w, killed);
|
||||
|
||||
if(isDragon(w->monst) && killed && isDragon(killed->monst) && killed->hitpoints) {
|
||||
cell *head1 = dragon::findhead(w);
|
||||
cell *head2 = dragon::findhead(killed);
|
||||
if(head1 == head2 && dragon::totalhp(head1) ==1) return true;
|
||||
}
|
||||
|
||||
if((w->monst == moPair || isMagneticPole(w->monst)) && killed && w->move(w->mondir) == killed)
|
||||
return true;
|
||||
|
||||
if(w->monst == moKrakenT && killed && killed->monst == moKrakenT && killed->hitpoints) {
|
||||
cell *head1 = w->move(w->mondir);
|
||||
cell *head2 = killed->move(killed->mondir);
|
||||
if(head1 == head2 && kraken::totalhp(head1) == 1) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
EX namespace stalemate {
|
||||
EX bool isKilled(cell *w) {
|
||||
for(int f=0; f<isize(moves); f++)
|
||||
if(moves[f].isKilled(w)) return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
EX }
|
||||
|
||||
EX bool krakensafe(cell *c) {
|
||||
return items[itOrbFish] || items[itOrbAether] ||
|
||||
(c->item == itOrbFish && c->wall == waBoat) ||
|
||||
(c->item == itOrbAether && c->wall == waBoat);
|
||||
}
|
||||
|
||||
EX bool monstersnear(stalemate1& sm) {
|
||||
|
||||
cell *c = sm.moveto;
|
||||
bool eaten = false;
|
||||
|
||||
if(hardcore && sm.who == moPlayer) return false;
|
||||
|
||||
int res = 0;
|
||||
bool fast = false;
|
||||
|
||||
elec::builder b;
|
||||
if(elec::affected(c)) { who_kills_me = moLightningBolt; res++; }
|
||||
|
||||
if(c->wall == waArrowTrap && c->wparam == 2) {
|
||||
who_kills_me = moArrowTrap; res++;
|
||||
}
|
||||
|
||||
for(auto c1: crush_now) if(c == c1) {
|
||||
who_kills_me = moCrusher; res++;
|
||||
}
|
||||
|
||||
if(sm.who == moPlayer || items[itOrbEmpathy]) {
|
||||
fast = (items[itOrbSpeed] && (items[itOrbSpeed] & 1));
|
||||
if(sm.who == moPlayer && sm.moveto->item == itOrbSpeed && !items[itOrbSpeed]) fast = true;
|
||||
}
|
||||
|
||||
if(havewhat&HF_OUTLAW) {
|
||||
for(cell *c1: gun_targets(c))
|
||||
if(c1->monst == moOutlaw && !c1->stuntime && !stalemate::isKilled(c1)) {
|
||||
res++; who_kills_me = moOutlaw;
|
||||
}
|
||||
}
|
||||
|
||||
for(int t=0; t<c->type; t++) {
|
||||
cell *c2 = c->move(t);
|
||||
|
||||
// consider monsters who attack from distance 2
|
||||
if(c2) forCellEx(c3, c2) if(c3 != c) {
|
||||
// only these monsters can attack from two spots...
|
||||
if(!among(c3->monst, moLancer, moWitchSpeed, moWitchFlash))
|
||||
continue;
|
||||
// take logical_adjacent into account
|
||||
if(c3->monst != moWitchFlash)
|
||||
if(!logical_adjacent(c3, c3->monst, c2) || !logical_adjacent(c2, c3->monst, c))
|
||||
continue;
|
||||
if(elec::affected(c3) || stalemate::isKilled(c3)) continue;
|
||||
if(c3->stuntime > (sm.who == moPlayer ? 0 : 1)) continue;
|
||||
// speedwitches can only attack not-fastened monsters,
|
||||
// others can only attack if the move is not fastened
|
||||
if(c3->monst == moWitchSpeed && items[itOrbSpeed]) continue;
|
||||
if(c3->monst != moWitchSpeed && fast) continue;
|
||||
// cannot attack if the immediate cell is impassable (except flashwitches)
|
||||
if(c3->monst != moWitchFlash) {
|
||||
if(!passable(c2, c3, stalemate::isKilled(c2)?P_MONSTER:0)) continue;
|
||||
if(isPlayerOn(c2) && items[itOrbFire]) continue;
|
||||
}
|
||||
// flashwitches cannot attack if it would kill another enemy
|
||||
if(c3->monst == moWitchFlash && flashWouldKill(c3, 0)) continue;
|
||||
res++, who_kills_me = c3->monst;
|
||||
}
|
||||
|
||||
// consider normal monsters
|
||||
if(c2 &&
|
||||
isArmedEnemy(c2, sm.who) &&
|
||||
!stalemate::isKilled(c2) &&
|
||||
(c2->monst != moLancer || isUnarmed(sm.who) || !logical_adjacent(c, sm.who, c2))) {
|
||||
eMonster m = c2->monst;
|
||||
if(elec::affected(c2)) continue;
|
||||
if(fast && c2->monst != moWitchSpeed) continue;
|
||||
// Krakens just destroy boats
|
||||
if(c2->monst == moKrakenT && onboat(sm)) {
|
||||
if(krakensafe(c)) continue;
|
||||
else if(warningprotection(XLAT("This move appears dangerous -- are you sure?")) && res == 0) m = moWarning;
|
||||
else continue;
|
||||
}
|
||||
// they cannot attack through vines
|
||||
if(!canAttack(c2, c2->monst, c, sm.who, AF_NEXTTURN)) continue;
|
||||
if(c2->monst == moWorm || c2->monst == moTentacle || c2->monst == moHexSnake) {
|
||||
if(passable_for(c2->monst, c, c2, 0))
|
||||
eaten = true;
|
||||
else if(c2->monst != moHexSnake) continue;
|
||||
}
|
||||
res++, who_kills_me = m;
|
||||
}
|
||||
}
|
||||
|
||||
if(sm.who == moPlayer && res && (markOrb2(itOrbShield) || markOrb2(itOrbShell)) && !eaten)
|
||||
res = 0;
|
||||
|
||||
if(sm.who == moPlayer && res && markOrb2(itOrbDomination) && c->monst)
|
||||
res = 0;
|
||||
|
||||
return !!res;
|
||||
}
|
||||
|
||||
EX bool monstersnear2();
|
||||
|
||||
EX bool monstersnear2() {
|
||||
multi::cpid++;
|
||||
bool b = false;
|
||||
bool recorduse[ittypes];
|
||||
for(int i=0; i<ittypes; i++) recorduse[i] = orbused[i];
|
||||
if(multi::cpid == multi::players || multi::players == 1 || multi::checkonly) {
|
||||
|
||||
dynamicval<eMonster> sw(passive_switch, passive_switch);
|
||||
|
||||
// check for safe orbs and switching first
|
||||
for(auto &sm: stalemate::moves) if(sm.who == moPlayer) {
|
||||
|
||||
if(hasSafeOrb(sm.moveto)) {
|
||||
multi::cpid--; return 0;
|
||||
}
|
||||
if(sm.moveto->item && itemclass(sm.moveto->item) == IC_TREASURE)
|
||||
passive_switch = active_switch();
|
||||
if(items[itOrbMagnetism]) forCellEx(c2, sm.moveto)
|
||||
if(canPickupItemWithMagnetism(c2, sm.comefrom)) {
|
||||
if(itemclass(c2->item) == IC_TREASURE)
|
||||
passive_switch = active_switch();
|
||||
if(hasSafeOrb(c2)) {
|
||||
multi::cpid--;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=0; i<isize(stalemate::moves); i++)
|
||||
for(int j=0; j<isize(stalemate::moves); j++) if(i != j) {
|
||||
if(swordConflict(stalemate::moves[i], stalemate::moves[j])) {
|
||||
b = true;
|
||||
who_kills_me = moEnergySword;
|
||||
}
|
||||
if(multi::player[i].at == multi::player[j].at)
|
||||
{ b = true; who_kills_me = moFireball; }
|
||||
if(celldistance(multi::player[i].at, multi::player[j].at) > 8)
|
||||
{ b = true; who_kills_me = moAirball; }
|
||||
}
|
||||
|
||||
for(int i=0; !b && i<isize(stalemate::moves); i++)
|
||||
b = monstersnear(stalemate::moves[i]);
|
||||
}
|
||||
else b = !multimove();
|
||||
multi::cpid--;
|
||||
for(int i=0; i<ittypes; i++) orbused[i] = recorduse[i];
|
||||
return b;
|
||||
}
|
||||
|
||||
EX bool monstersnear(cell *c, cell *nocount, eMonster who, cell *pushto, cell *comefrom) {
|
||||
|
||||
if(peace::on) return 0; // you are safe
|
||||
|
||||
stalemate1 sm(who, c, nocount, pushto, comefrom);
|
||||
|
||||
if(who == moPlayer) for(int b=0; b<2; b++) sm.swordlast[b] = sword::pos(multi::cpid, b);
|
||||
|
||||
cell *none = NULL;
|
||||
cell **wcw = &cwt.at;
|
||||
if(who != moPlayer) wcw = &none;
|
||||
else if(multi::players > 1) wcw = &multi::player[multi::cpid].at;
|
||||
|
||||
dynamicval<cell*> x5(*wcw, c);
|
||||
dynamicval<bool> x6(stalemate::nextturn, true);
|
||||
dynamicval<sword::sworddir> x7(sword::dir[multi::cpid],
|
||||
who == moPlayer ? sword::shift(comefrom, c, sword::dir[multi::cpid]) :
|
||||
sword::dir[multi::cpid]);
|
||||
|
||||
for(int b=0; b<2; b++) {
|
||||
if(who == moPlayer) {
|
||||
sm.swordnext[b] = sword::pos(multi::cpid, b);
|
||||
sm.swordtransit[b] = NULL;
|
||||
if(sm.swordnext[b] && sm.swordnext[b] != sm.swordlast[b] && !isNeighbor(sm.swordlast[b], sm.swordnext[b])) {
|
||||
forCellEx(c2, sm.swordnext[b])
|
||||
if(c2 != c && c2 != comefrom && isNeighbor(c2, S3==3 ? sm.swordlast[b] : *wcw))
|
||||
sm.swordtransit[b] = c2;
|
||||
if(S3 == 4)
|
||||
forCellEx(c2, c)
|
||||
if(c2 != comefrom && isNeighbor(c2, sm.swordlast[b]))
|
||||
sm.swordtransit[b] = c2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
sm.swordnext[b] = sm.swordtransit[b] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
stalemate::moves.push_back(sm);
|
||||
|
||||
// dynamicval<eMonster> x7(stalemate::who, who);
|
||||
|
||||
bool b;
|
||||
if(who == moPlayer && c->wall == waBigStatue) {
|
||||
eWall w = comefrom->wall;
|
||||
c->wall = waNone;
|
||||
if(doesnotFall(comefrom)) comefrom->wall = waBigStatue;
|
||||
b = monstersnear2();
|
||||
comefrom->wall = w;
|
||||
c->wall = waBigStatue;
|
||||
}
|
||||
else if(who == moPlayer && isPushable(c->wall)) {
|
||||
eWall w = c->wall;
|
||||
c->wall = waNone;
|
||||
b = monstersnear2();
|
||||
c->wall = w;
|
||||
}
|
||||
else {
|
||||
b = monstersnear2();
|
||||
}
|
||||
stalemate::moves.pop_back();
|
||||
return b;
|
||||
}
|
||||
|
||||
EX namespace stalemate {
|
||||
EX vector<stalemate1> moves;
|
||||
EX bool nextturn;
|
||||
|
||||
EX bool isMoveto(cell *c) {
|
||||
for(int i=0; i<isize(moves); i++) if(moves[i].moveto == c) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool isKilledDirectlyAt(cell *c) {
|
||||
for(int i=0; i<isize(moves); i++) if(moves[i].killed == c) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool isPushto(cell *c) {
|
||||
for(int i=0; i<isize(moves); i++) if(moves[i].pushto == c) return true;
|
||||
return false;
|
||||
}
|
||||
EX }
|
||||
|
||||
EX bool onboat(stalemate1& sm) {
|
||||
cell *c = sm.moveto;
|
||||
cell *cf = sm.comefrom;
|
||||
return (c->wall == waBoat) || (cf->wall == waBoat && c->wall == waSea);
|
||||
}
|
||||
|
||||
EX bool multimove() {
|
||||
if(multi::cpid == 0) lastkills = tkills();
|
||||
if(!multi::playerActive(multi::cpid)) return !monstersnear2();
|
||||
cellwalker bcwt = cwt;
|
||||
cwt = multi::player[multi::cpid];
|
||||
bool b = movepcto(multi::whereto[multi::cpid]);
|
||||
if(b) {
|
||||
multi::aftermove = true;
|
||||
multi::player[multi::cpid] = cwt;
|
||||
multi::whereto[multi::cpid].d = MD_UNDECIDED;
|
||||
int curkills = tkills();
|
||||
multi::kills[multi::cpid] += (curkills - lastkills);
|
||||
lastkills = curkills;
|
||||
}
|
||||
cwt = bcwt;
|
||||
return b;
|
||||
}
|
||||
|
||||
EX namespace multi {
|
||||
EX bool checkonly = false;
|
||||
EX bool aftermove;
|
||||
EX }
|
||||
|
||||
EX bool swordConflict(const stalemate1& sm1, const stalemate1& sm2) {
|
||||
if(items[itOrbSword] || items[itOrbSword2])
|
||||
for(int b=0; b<2; b++)
|
||||
if(sm1.comefrom == sm2.swordlast[b] || sm1.comefrom == sm2.swordtransit[b] || sm1.comefrom == sm2.swordnext[b])
|
||||
if(sm1.moveto == sm2.swordlast[b] || sm1.moveto == sm2.swordtransit[b] || sm1.moveto == sm2.swordnext[b])
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX void checkmove() {
|
||||
|
||||
if(dual::state == 2) return;
|
||||
if(shmup::on) return;
|
||||
|
||||
dynamicval<eGravity> gs(gravity_state, gravity_state);
|
||||
|
||||
#if CAP_INV
|
||||
if(inv::on) inv::compute();
|
||||
#endif
|
||||
|
||||
if(multi::players > 1 && !multi::checkonly) return;
|
||||
if(hardcore) return;
|
||||
bool orbusedbak[ittypes];
|
||||
|
||||
// do not activate orbs!
|
||||
for(int i=0; i<ittypes; i++) orbusedbak[i] = orbused[i];
|
||||
|
||||
for(int i=0; i<=MAX_EDGE; i++) legalmoves[i] = false;
|
||||
|
||||
canmove = haveRangedTarget();
|
||||
items[itWarning]+=2;
|
||||
if(movepcto(-1, 0, true)) canmove = legalmoves[MAX_EDGE] = true;
|
||||
|
||||
if(vid.mobilecompasssize || !canmove)
|
||||
for(int i=0; i<cwt.at->type; i++)
|
||||
if(movepcto(1, -1, true))
|
||||
canmove = legalmoves[cwt.spin] = true;
|
||||
if(vid.mobilecompasssize || !canmove)
|
||||
for(int i=0; i<cwt.at->type; i++)
|
||||
if(movepcto(1, 1, true))
|
||||
canmove = legalmoves[cwt.spin] = true;
|
||||
if(kills[moPlayer]) canmove = false;
|
||||
|
||||
#if CAP_INV
|
||||
if(inv::on && !canmove && !inv::incheck) {
|
||||
if(inv::remaining[itOrbSafety] || inv::remaining[itOrbFreedom])
|
||||
canmove = true;
|
||||
else {
|
||||
inv::check(1);
|
||||
checkmove();
|
||||
inv::check(-1);
|
||||
}
|
||||
if(canmove)
|
||||
pushScreen(inv::show);
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!canmove) {
|
||||
achievement_final(true);
|
||||
if(cmode & sm::NORMAL) showMissionScreen();
|
||||
}
|
||||
|
||||
if(canmove && timerstopped) {
|
||||
timerstart = time(NULL);
|
||||
timerstopped = false;
|
||||
}
|
||||
items[itWarning]-=2;
|
||||
|
||||
for(int i=0; i<ittypes; i++) orbused[i] = orbusedbak[i];
|
||||
if(recallCell.at && !markOrb(itOrbRecall)) activateRecall();
|
||||
}
|
||||
|
||||
|
||||
}
|
512
complex2.cpp
512
complex2.cpp
@ -274,21 +274,22 @@ EX namespace westwall {
|
||||
}
|
||||
EX }
|
||||
|
||||
EX namespace variant {
|
||||
#if HDR
|
||||
struct variant_feature {
|
||||
struct feature {
|
||||
color_t color_change;
|
||||
int rate_change;
|
||||
eMonster wanderer;
|
||||
void (*build)(cell*);
|
||||
};
|
||||
|
||||
extern array<variant_feature, 21> variant_features;
|
||||
extern array<feature, 21> features;
|
||||
#endif
|
||||
|
||||
#define VF [] (cell *c)
|
||||
|
||||
array<variant_feature, 21> variant_features {{
|
||||
variant_feature{(color_t)(-0x202020), 5, moNecromancer, VF {
|
||||
array<feature, 21> features {{
|
||||
feature{(color_t)(-0x202020), 5, moNecromancer, VF {
|
||||
if(c->wall == waNone && hrand(1500) < 20) c->wall = waFreshGrave;
|
||||
if(hrand(20000) < 10 + items[itVarTreasure])
|
||||
c->monst = moNecromancer;
|
||||
@ -322,6 +323,509 @@ array<variant_feature, 21> variant_features {{
|
||||
{0x100708, 1, moRatling, VF { if(c->wall == waNone && !c->monst && hrand(50000) < 25 + items[itVarTreasure]) c->monst = moRatling; }}
|
||||
}};
|
||||
#undef VF
|
||||
EX }
|
||||
|
||||
EX namespace camelot {
|
||||
/** number of Grails collected, to show you as a knight */
|
||||
EX int knighted = 0;
|
||||
|
||||
/** this value is used when using Orb of Safety in the Camelot in Pure Tactics Mode */
|
||||
EX int anthraxBonus = 0;
|
||||
|
||||
EX void roundTableMessage(cell *c2) {
|
||||
if(!euclid && !cwt.at->master->alt) return;
|
||||
if(!euclid && !c2->master->alt) return;
|
||||
int dd = celldistAltRelative(c2) - celldistAltRelative(cwt.at);
|
||||
|
||||
bool tooeasy = (roundTableRadius(c2) < newRoundTableRadius());
|
||||
|
||||
if(dd>0) {
|
||||
if(grailWasFound(cwt.at)) {
|
||||
addMessage(XLAT("The Knights congratulate you on your success!"));
|
||||
knighted = roundTableRadius(cwt.at);
|
||||
}
|
||||
else if(!tooeasy)
|
||||
addMessage(XLAT("The Knights laugh at your failure!"));
|
||||
}
|
||||
else {
|
||||
if(grailWasFound(cwt.at))
|
||||
addMessage(XLAT("The Knights stare at you!"));
|
||||
else if(tooeasy) {
|
||||
if(!tactic::on)
|
||||
addMessage(XLAT("Come on, this is too easy... find a bigger castle!"));
|
||||
}
|
||||
else
|
||||
addMessage(XLAT("The Knights wish you luck!"));
|
||||
}
|
||||
}
|
||||
|
||||
EX void knightFlavorMessage(cell *c2) {
|
||||
|
||||
if(!eubinary && !c2->master->alt) {
|
||||
addMessage(XLAT("\"I am lost...\""));
|
||||
return;
|
||||
}
|
||||
|
||||
if(tactic::on) {
|
||||
addMessage(XLAT("\"The Knights of the Horocyclic Table salute you!\""));
|
||||
return;
|
||||
}
|
||||
|
||||
bool grailfound = grailWasFound(c2);
|
||||
int rad = roundTableRadius(c2);
|
||||
bool tooeasy = (rad < newRoundTableRadius());
|
||||
|
||||
static int msgid = 0;
|
||||
|
||||
retry:
|
||||
if(msgid >= 32) msgid = 0;
|
||||
|
||||
if(msgid == 0 && grailfound) {
|
||||
addMessage(XLAT("\"I would like to congratulate you again!\""));
|
||||
}
|
||||
else if(msgid == 1 && !tooeasy) {
|
||||
addMessage(XLAT("\"Find the Holy Grail to become one of us!\""));
|
||||
}
|
||||
else if(msgid == 2 && !tooeasy) {
|
||||
addMessage(XLAT("\"The Holy Grail is in the center of the Round Table.\""));
|
||||
}
|
||||
#if CAP_CRYSTAL
|
||||
else if(msgid == 3 && cryst) {
|
||||
if(crystal::pure())
|
||||
addMessage(XLAT("\"Each piece of the Round Table is exactly %1 steps away from the Holy Grail.\"", its(roundTableRadius(c2))));
|
||||
else
|
||||
addMessage(XLAT("\"According to Merlin, the Round Table is a perfect Euclidean sphere in %1 dimensions.\"", its(ginf[gCrystal].sides/2)));
|
||||
}
|
||||
#endif
|
||||
else if(msgid == 3 && !peace::on && in_full_game()) {
|
||||
addMessage(XLAT("\"I enjoy watching the hyperbug battles.\""));
|
||||
}
|
||||
else if(msgid == 4 && in_full_game()) {
|
||||
addMessage(XLAT("\"Have you visited a temple in R'Lyeh?\""));
|
||||
}
|
||||
else if(msgid == 5 && in_full_game()) {
|
||||
addMessage(XLAT("\"Nice castle, eh?\""));
|
||||
}
|
||||
else if(msgid == 6 && items[itSpice] < 10 && !peace::on && in_full_game()) {
|
||||
addMessage(XLAT("\"The Red Rock Valley is dangerous, but beautiful.\""));
|
||||
}
|
||||
else if(msgid == 7 && items[itSpice] < 10 && !peace::on && in_full_game()) {
|
||||
addMessage(XLAT("\"Train in the Desert first!\""));
|
||||
}
|
||||
else if(msgid == 8 && sizes_known() && !tactic::on) {
|
||||
string s = "";
|
||||
if(0) ;
|
||||
#if CAP_CRYSTAL
|
||||
else if(cryst)
|
||||
s = crystal::get_table_boundary();
|
||||
#endif
|
||||
else if(!quotient)
|
||||
s = expansion.get_descendants(rad).get_str(100);
|
||||
if(s == "") { msgid++; goto retry; }
|
||||
addMessage(XLAT("\"Our Table seats %1 Knights!\"", s));
|
||||
}
|
||||
else if(msgid == 9 && sizes_known() && !tactic::on) {
|
||||
string s = "";
|
||||
if(0);
|
||||
#if CAP_CRYSTAL
|
||||
else if(cryst)
|
||||
s = crystal::get_table_volume();
|
||||
#endif
|
||||
else if(!quotient)
|
||||
s = expansion.get_descendants(rad-1, expansion.diskid).get_str(100);
|
||||
if(s == "") { msgid++; goto retry; }
|
||||
addMessage(XLAT("\"There are %1 floor tiles inside our Table!\"", s));
|
||||
}
|
||||
else if(msgid == 10 && !items[itPirate] && !items[itWhirlpool] && !peace::on && in_full_game()) {
|
||||
addMessage(XLAT("\"Have you tried to take a boat and go into the Ocean? Try it!\""));
|
||||
}
|
||||
else if(msgid == 11 && !princess::saved && in_full_game()) {
|
||||
addMessage(XLAT("\"When I visited the Palace, a mouse wanted me to go somewhere.\""));
|
||||
}
|
||||
else if(msgid == 12 && !princess::saved && in_full_game()) {
|
||||
addMessage(XLAT("\"I wonder what was there...\""));
|
||||
}
|
||||
else if(msgid == 13 && !peace::on && in_full_game()) {
|
||||
addMessage(XLAT("\"Be careful in the Rose Garden! It is beautiful, but very dangerous!\""));
|
||||
}
|
||||
else if(msgid == 14) {
|
||||
addMessage(XLAT("\"There is no royal road to geometry.\""));
|
||||
}
|
||||
else if(msgid == 15) {
|
||||
addMessage(XLAT("\"There is no branch of mathematics, however abstract, "));
|
||||
addMessage(XLAT("which may not some day be applied to phenomena of the real world.\""));
|
||||
}
|
||||
else if(msgid == 16) {
|
||||
addMessage(XLAT("\"It is not possession but the act of getting there, "));
|
||||
addMessage(XLAT("which grants the greatest enjoyment.\""));
|
||||
}
|
||||
else if(msgid == 17) {
|
||||
addMessage(XLAT("\"We live in a beautiful and orderly world, "));
|
||||
addMessage(XLAT("and not in a chaos without norms.\""));
|
||||
}
|
||||
else if(msgid == 25) {
|
||||
addMessage(XLAT("\"Thank you very much for talking, and have a great rest of your day!\""));
|
||||
}
|
||||
else {
|
||||
msgid++; goto retry;
|
||||
}
|
||||
|
||||
msgid++;
|
||||
}
|
||||
EX }
|
||||
|
||||
EX namespace mine {
|
||||
|
||||
EX bool uncoverMines(cell *c, int lev, int dist, bool just_checking) {
|
||||
bool b = false;
|
||||
if(c->wall == waMineMine && just_checking) return true;
|
||||
if(c->wall == waMineUnknown) {
|
||||
if(just_checking)
|
||||
return true;
|
||||
else {
|
||||
c->wall = waMineOpen;
|
||||
b = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool minesNearby = false;
|
||||
bool nominesNearby = false;
|
||||
bool mineopens = false;
|
||||
|
||||
auto adj = adj_minefield_cells(c);
|
||||
|
||||
for(cell *c2: adj) {
|
||||
if(c2->wall == waMineMine) minesNearby = true;
|
||||
if(c2->wall == waMineOpen) mineopens = true;
|
||||
if(c2->wall == waMineUnknown && !c2->item) nominesNearby = true;
|
||||
}
|
||||
|
||||
if(lev && (nominesNearby || mineopens) && !minesNearby) for(cell *c2: adj)
|
||||
if(c2->wall == waMineUnknown || c2->wall == waMineOpen) {
|
||||
b |= uncoverMines(c2, lev-1, dist+1, just_checking);
|
||||
if(b && just_checking) return true;
|
||||
}
|
||||
|
||||
if(minesNearby && !nominesNearby && dist == 0) {
|
||||
for(cell *c2: adj)
|
||||
if(c2->wall == waMineMine && c2->land == laMinefield)
|
||||
c2->landparam |= 1;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
EX bool mightBeMine(cell *c) {
|
||||
return c->wall == waMineUnknown || c->wall == waMineMine;
|
||||
}
|
||||
|
||||
EX hookset<bool(cell*)> *hooks_mark;
|
||||
|
||||
EX void performMarkCommand(cell *c) {
|
||||
if(!c) return;
|
||||
if(callhandlers(false, hooks_mark, c)) return;
|
||||
if(c->land == laCA && c->wall == waNone)
|
||||
c->wall = waFloorA;
|
||||
else if(c->land == laCA && c->wall == waFloorA)
|
||||
c->wall = waNone;
|
||||
if(c->land != laMinefield) return;
|
||||
if(c->item) return;
|
||||
if(!mightBeMine(c)) return;
|
||||
bool adj = false;
|
||||
forCellEx(c2, c) if(c2->wall == waMineOpen) adj = true;
|
||||
if(adj) c->landparam ^= 1;
|
||||
}
|
||||
|
||||
EX bool marked_mine(cell *c) {
|
||||
if(!mightBeMine(c)) return false;
|
||||
if(c->item) return false;
|
||||
if(c->land != laMinefield) return true;
|
||||
return c->landparam & 1;
|
||||
}
|
||||
|
||||
EX bool marked_safe(cell *c) {
|
||||
if(!mightBeMine(c)) return false;
|
||||
if(c->item) return true;
|
||||
if(c->land != laMinefield) return false;
|
||||
return c->landparam & 2;
|
||||
}
|
||||
|
||||
EX bool safe() {
|
||||
return items[itOrbAether];
|
||||
}
|
||||
|
||||
EX void uncover_full(cell *c2) {
|
||||
int mineradius =
|
||||
bounded ? 3 :
|
||||
(items[itBombEgg] < 1 && !tactic::on) ? 0 :
|
||||
items[itBombEgg] < 20 ? 1 :
|
||||
items[itBombEgg] < 30 ? 2 :
|
||||
3;
|
||||
|
||||
bool nomine = !normal_gravity_at(c2);
|
||||
if(!nomine && uncoverMines(c2, mineradius, 0, true) && markOrb(itOrbAether))
|
||||
nomine = true;
|
||||
|
||||
if(!nomine) {
|
||||
uncoverMines(c2, mineradius, 0, false);
|
||||
mayExplodeMine(c2, moPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
EX void auto_teleport_charges() {
|
||||
if(specialland == laMinefield && firstland == laMinefield && bounded)
|
||||
items[itOrbTeleport] = isFire(cwt.at->wall) ? 0 : 1;
|
||||
}
|
||||
|
||||
EX }
|
||||
|
||||
EX namespace terracotta {
|
||||
#if HDR
|
||||
// predictable or not
|
||||
static constexpr bool randterra = false;
|
||||
#endif
|
||||
|
||||
EX void check(cell *c) {
|
||||
if(c->wall == waTerraWarrior && !c->monst && !racing::on) {
|
||||
bool live = false;
|
||||
if(randterra) {
|
||||
c->landparam++;
|
||||
if((c->landparam == 3 && hrand(3) == 0) ||
|
||||
(c->landparam == 4 && hrand(2) == 0) ||
|
||||
c->landparam == 5)
|
||||
live = true;
|
||||
}
|
||||
else {
|
||||
c->landparam--;
|
||||
live = !c->landparam;
|
||||
}
|
||||
if(live)
|
||||
c->monst = moTerraWarrior,
|
||||
c->hitpoints = 7,
|
||||
c->wall = waNone;
|
||||
}
|
||||
}
|
||||
|
||||
EX void check_around(cell *c) {
|
||||
forCellEx(c2, c)
|
||||
check(c2);
|
||||
}
|
||||
|
||||
EX void check() {
|
||||
for(int i=0; i<numplayers(); i++)
|
||||
forCellEx(c, playerpos(i)) {
|
||||
if(shmup::on) {
|
||||
forCellEx(c2, c)
|
||||
check(c2);
|
||||
}
|
||||
else
|
||||
check(c);
|
||||
}
|
||||
}
|
||||
EX }
|
||||
|
||||
EX namespace ambush {
|
||||
|
||||
EX void mark(cell *c, manual_celllister& cl) {
|
||||
if(!cl.add(c)) return;
|
||||
forCellEx(c2, c)
|
||||
if(c2->cpdist < c->cpdist)
|
||||
mark(c2, cl);
|
||||
}
|
||||
|
||||
EX int distance;
|
||||
EX bool ambushed;
|
||||
|
||||
EX void check_state() {
|
||||
if(havewhat & HF_HUNTER) {
|
||||
manual_celllister cl;
|
||||
for(cell *c: dcal) {
|
||||
if(c->monst == moHunterDog) {
|
||||
if(c->cpdist > distance)
|
||||
distance = c->cpdist;
|
||||
mark(c, cl);
|
||||
}
|
||||
if(c->monst == moHunterGuard && c->cpdist <= 4)
|
||||
mark(c, cl);
|
||||
}
|
||||
if(items[itHunting] > 5 && items[itHunting] <= 22) {
|
||||
int q = 0;
|
||||
for(int i=0; i<numplayers(); i++)
|
||||
forCellEx(c2, playerpos(i))
|
||||
if(cl.listed(c2))
|
||||
q++;
|
||||
if(q == 1) havewhat |= HF_FAILED_AMBUSH;
|
||||
if(q == 2) {
|
||||
for(int i=0; i<numplayers(); i++)
|
||||
forCellEx(c2, playerpos(i))
|
||||
if(cl.listed(c2))
|
||||
forCellEx(c3, playerpos(i))
|
||||
if(c3 != c2 && isNeighbor(c2,c3))
|
||||
if(cl.listed(c3))
|
||||
havewhat |= HF_FAILED_AMBUSH;
|
||||
}
|
||||
if(havewhat & HF_FAILED_AMBUSH && ambushed) {
|
||||
addMessage(XLAT("The Hunting Dogs give up."));
|
||||
ambushed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EX int fixed_size;
|
||||
|
||||
EX int size(cell *c, eItem what) {
|
||||
bool restricted = false;
|
||||
for(cell *c2: dcal) {
|
||||
if(c2->cpdist > 3) break;
|
||||
if(c2->monst && !isFriendly(c2) && !slowMover(c2) && !isMultitile(c2)) restricted = true;
|
||||
}
|
||||
|
||||
int qty = items[itHunting];
|
||||
if(fixed_size)
|
||||
return fixed_size;
|
||||
switch(what) {
|
||||
case itCompass:
|
||||
return 0;
|
||||
|
||||
case itHunting:
|
||||
return min(min(qty, max(33-qty, 6)), 15);
|
||||
|
||||
case itOrbSide3:
|
||||
return restricted ? 10 : 20;
|
||||
|
||||
case itOrbFreedom:
|
||||
return restricted ? 10 : 60;
|
||||
|
||||
case itOrbThorns:
|
||||
case itOrb37:
|
||||
return 20;
|
||||
|
||||
case itOrbLava:
|
||||
return 20;
|
||||
|
||||
case itOrbBeauty:
|
||||
return 35;
|
||||
|
||||
case itOrbShell:
|
||||
return 35;
|
||||
|
||||
case itOrbPsi:
|
||||
// return 40; -> no benefits
|
||||
return 20;
|
||||
|
||||
case itOrbDash:
|
||||
case itOrbFrog:
|
||||
return 40;
|
||||
|
||||
case itOrbAir:
|
||||
case itOrbDragon:
|
||||
return 50;
|
||||
|
||||
case itOrbStunning:
|
||||
// return restricted ? 50 : 60; -> no benefits
|
||||
return 30;
|
||||
|
||||
case itOrbBull:
|
||||
case itOrbSpeed:
|
||||
case itOrbShield:
|
||||
return 60;
|
||||
|
||||
case itOrbInvis:
|
||||
return 80;
|
||||
|
||||
case itOrbTeleport:
|
||||
return 300;
|
||||
|
||||
case itGreenStone:
|
||||
case itOrbSafety:
|
||||
case itOrbYendor:
|
||||
return 0;
|
||||
|
||||
case itKey:
|
||||
return 16;
|
||||
|
||||
case itWarning:
|
||||
return qty;
|
||||
|
||||
default:
|
||||
return restricted ? 6 : 10;
|
||||
break;
|
||||
|
||||
// Flash can survive about 70, but this gives no benefits
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EX int ambush(cell *c, eItem what) {
|
||||
int maxdist = gamerange();
|
||||
celllister cl(c, maxdist, 1000000, NULL);
|
||||
cell *c0 = c;
|
||||
int d = 0;
|
||||
int dogs0 = 0;
|
||||
for(cell *cx: cl.lst) {
|
||||
int dh = cl.getdist(cx);
|
||||
if(dh <= 2 && cx->monst == moHunterGuard)
|
||||
cx->monst = moHunterDog, dogs0++;
|
||||
if(dh > d) c0 = cx, d = dh;
|
||||
}
|
||||
if(sphere) {
|
||||
int dogs = size(c, what);
|
||||
for(int i = cl.lst.size()-1; i>0 && dogs; i--)
|
||||
if(!isPlayerOn(cl.lst[i]) && !cl.lst[i]->monst)
|
||||
cl.lst[i]->monst = moHunterDog, dogs--;
|
||||
}
|
||||
vector<cell*> around;
|
||||
cell *clast = NULL;
|
||||
cell *ccur = c0;
|
||||
int v = VALENCE;
|
||||
if(v > 4) {
|
||||
for(cell *c: cl.lst) if(cl.getdist(c) == d) around.push_back(c);
|
||||
hrandom_shuffle(&around[0], isize(around));
|
||||
}
|
||||
else {
|
||||
for(int tries=0; tries<10000; tries++) {
|
||||
cell *c2 = NULL;
|
||||
if(v == 3) {
|
||||
forCellEx(c1, ccur)
|
||||
if(c1 != clast && cl.listed(c1) && cl.getdist(c1) == d)
|
||||
c2 = c1;
|
||||
}
|
||||
if(v == 4) {
|
||||
for(int i=0; i<ccur->type; i++) {
|
||||
cell *c1 = (cellwalker(ccur, i) + wstep + 1).peek();
|
||||
if(!c1) continue;
|
||||
if(c1 != clast && cl.listed(c1) && cl.getdist(c1) == d)
|
||||
c2 = c1;
|
||||
}
|
||||
}
|
||||
if(!c2) break;
|
||||
if(c2->land == laHunting && c2->wall == waNone && c2->monst == moNone)
|
||||
around.push_back(c2);
|
||||
clast = ccur; ccur = c2;
|
||||
if(c2 == c0) break;
|
||||
}
|
||||
}
|
||||
int N = isize(around);
|
||||
int dogs = size(c, what);
|
||||
|
||||
int gaps = dogs;
|
||||
if(!N) return dogs0;
|
||||
ambushed = true;
|
||||
int shift = hrand(N);
|
||||
dogs = min(dogs, N);
|
||||
gaps = min(gaps, N);
|
||||
for(int i=0; i<dogs; i++) {
|
||||
int pos = (shift + (N * i) / gaps) % N;
|
||||
cell *nextdog = around[pos];
|
||||
nextdog->monst = moHunterDog;
|
||||
nextdog->stuntime = 1;
|
||||
drawFlash(nextdog);
|
||||
}
|
||||
return dogs + dogs0;
|
||||
}
|
||||
EX }
|
||||
|
||||
}
|
||||
#endif
|
||||
|
@ -426,7 +426,7 @@ EX void handleKeyNormal(int sym, int uni) {
|
||||
if(DEFAULTNOR(sym)) {
|
||||
gmodekeys(sym, uni);
|
||||
if(uni == 'm' && canmove && (centerover == cwt.at ? mouseover : centerover))
|
||||
performMarkCommand(mouseover);
|
||||
mine::performMarkCommand(mouseover);
|
||||
}
|
||||
|
||||
if(DEFAULTCONTROL) {
|
||||
|
@ -664,7 +664,7 @@ int read_cheat_args() {
|
||||
// make all ambushes use the given number of dogs
|
||||
// example: hyper -W Hunt -IP Shield 1 -ambush 60
|
||||
PHASE(3) cheat();
|
||||
shift(); ambushval = argi();
|
||||
shift(); ambush::fixed_size = argi();
|
||||
}
|
||||
else if(argis("-testdistances")) {
|
||||
PHASE(3); shift(); test_distances(argi());
|
||||
|
789
environment.cpp
Normal file
789
environment.cpp
Normal file
@ -0,0 +1,789 @@
|
||||
// Hyperbolic Rogue - environment
|
||||
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
|
||||
|
||||
/** \file environment.cpp
|
||||
* \brief game environment: routines related to the game that affect all the map. Monsters to move are detected here, but their moves are implemented in monstermove.cpp
|
||||
*/
|
||||
|
||||
#include "hyper.h"
|
||||
|
||||
namespace hr {
|
||||
|
||||
#if HDR
|
||||
#define HF_BUG Flag(0)
|
||||
#define HF_EARTH Flag(1)
|
||||
#define HF_BIRD Flag(2)
|
||||
#define HF_LEADER Flag(3)
|
||||
#define HF_HEX Flag(4)
|
||||
#define HF_WHIRLPOOL Flag(5)
|
||||
#define HF_WATER Flag(6)
|
||||
#define HF_AIR Flag(7)
|
||||
#define HF_MUTANT Flag(8)
|
||||
#define HF_OUTLAW Flag(9)
|
||||
#define HF_WHIRLWIND Flag(10)
|
||||
#define HF_ROSE Flag(11)
|
||||
#define HF_DRAGON Flag(12)
|
||||
#define HF_KRAKEN Flag(13)
|
||||
#define HF_SHARK Flag(14)
|
||||
#define HF_BATS Flag(15)
|
||||
#define HF_REPTILE Flag(16)
|
||||
#define HF_EAGLES Flag(17)
|
||||
#define HF_SLOW Flag(18)
|
||||
#define HF_FAST Flag(19)
|
||||
#define HF_WARP Flag(20)
|
||||
#define HF_MOUSE Flag(21)
|
||||
#define HF_RIVER Flag(22)
|
||||
#define HF_MIRROR Flag(23)
|
||||
#define HF_VOID Flag(24)
|
||||
#define HF_HUNTER Flag(25)
|
||||
#define HF_FAILED_AMBUSH Flag(26)
|
||||
#define HF_MAGNET Flag(27)
|
||||
#define HF_HEXD Flag(28)
|
||||
#define HF_ALT Flag(29)
|
||||
#define HF_MONK Flag(30)
|
||||
#define HF_WESTWALL Flag(31)
|
||||
#endif
|
||||
|
||||
EX flagtype havewhat, hadwhat;
|
||||
|
||||
/** monsters of specific types to move */
|
||||
EX vector<cell*> worms, ivies, ghosts, golems, hexsnakes;
|
||||
|
||||
/** temporary changes during bfs */
|
||||
vector<pair<cell*, eMonster>> tempmonsters;
|
||||
|
||||
/** additional direction information for BFS algorithms.
|
||||
* It remembers from where we have got to this location
|
||||
* the opposite cell will be added to the queue first,
|
||||
* which helps the AI.
|
||||
**/
|
||||
EX vector<int> reachedfrom;
|
||||
|
||||
/** The position of the first cell in dcal in distance 7. New wandering monsters can be generated in dcal[first7..]. */
|
||||
EX int first7;
|
||||
|
||||
/** the list of all nearby cells, according to cpdist */
|
||||
EX vector<cell*> dcal;
|
||||
/** the list of all nearby cells, according to current pathdist */
|
||||
EX vector<cell*> pathq;
|
||||
|
||||
/** the number of big statues -- they increase monster generation */
|
||||
EX int statuecount;
|
||||
|
||||
/** list of monsters to move (pathq restriced to monsters) */
|
||||
EX vector<cell*> pathqm;
|
||||
|
||||
/** which hex snakes are there */
|
||||
EX set<int> snaketypes;
|
||||
|
||||
EX int gamerange_bonus = 0;
|
||||
EX int gamerange() { return getDistLimit() + gamerange_bonus; }
|
||||
|
||||
// pathdist begin
|
||||
EX cell *pd_from;
|
||||
EX int pd_range;
|
||||
|
||||
EX void onpath(cell *c, int d) {
|
||||
c->pathdist = d;
|
||||
pathq.push_back(c);
|
||||
}
|
||||
|
||||
EX void onpath(cell *c, int d, int sp) {
|
||||
c->pathdist = d;
|
||||
pathq.push_back(c);
|
||||
reachedfrom.push_back(sp);
|
||||
}
|
||||
|
||||
EX void clear_pathdata() {
|
||||
for(auto c: pathq) c->pathdist = PINFD;
|
||||
pathq.clear();
|
||||
pathqm.clear();
|
||||
reachedfrom.clear();
|
||||
}
|
||||
|
||||
EX int pathlock = 0;
|
||||
|
||||
EX void compute_graphical_distance() {
|
||||
if(pathlock) { printf("path error: compute_graphical_distance\n"); }
|
||||
cell *c1 = centerover ? centerover : pd_from ? pd_from : cwt.at;
|
||||
int sr = get_sightrange_ambush();
|
||||
if(pd_from == c1 && pd_range == sr) return;
|
||||
clear_pathdata();
|
||||
|
||||
pd_from = c1;
|
||||
pd_range = sr;
|
||||
c1->pathdist = 0;
|
||||
pathq.push_back(pd_from);
|
||||
|
||||
for(int qb=0; qb<isize(pathq); qb++) {
|
||||
cell *c = pathq[qb];
|
||||
if(c->pathdist == pd_range) break;
|
||||
if(qb == 0) forCellCM(c1, c) ;
|
||||
forCellEx(c1, c)
|
||||
if(c1->pathdist == PINFD)
|
||||
onpath(c1, c->pathdist + 1);
|
||||
}
|
||||
}
|
||||
|
||||
EX void computePathdist(eMonster param) {
|
||||
|
||||
for(cell *c: targets)
|
||||
onpath(c, isPlayerOn(c) ? 0 : 1, hrand(c->type));
|
||||
|
||||
int qtarg = isize(targets);
|
||||
|
||||
int limit = gamerange();
|
||||
|
||||
for(int qb=0; qb < isize(pathq); qb++) {
|
||||
cell *c = pathq[qb];
|
||||
int fd = reachedfrom[qb] + c->type/2;
|
||||
if(c->monst && !isBug(c) && !(isFriendly(c) && !c->stuntime)) {
|
||||
pathqm.push_back(c);
|
||||
continue; // no paths going through monsters
|
||||
}
|
||||
if(isMounted(c) && !isPlayerOn(c)) {
|
||||
// don't treat the Worm you are riding as passable
|
||||
pathqm.push_back(c);
|
||||
continue;
|
||||
}
|
||||
if(c->cpdist > limit && !(c->land == laTrollheim && turncount < c->landparam)) continue;
|
||||
int d = c->pathdist;
|
||||
if(d == PINFD - 1) continue;
|
||||
for(int j=0; j<c->type; j++) {
|
||||
int i = (fd+j) % c->type;
|
||||
// printf("i=%d cd=%d\n", i, c->move(i)->cpdist);
|
||||
cell *c2 = c->move(i);
|
||||
|
||||
if(c2 && c2->pathdist == PINFD &&
|
||||
passable(c2, (qb<qtarg) && !nonAdjacent(c,c2) && !thruVine(c,c2) ?NULL:c, P_MONSTER | P_REVDIR)) {
|
||||
|
||||
if(qb >= qtarg) {
|
||||
if(param == moTortoise && nogoSlow(c, c2)) continue;
|
||||
if(param == moIvyRoot && strictlyAgainstGravity(c, c2, false, MF_IVY)) continue;
|
||||
if(param == moWorm && (cellUnstable(c) || cellEdgeUnstable(c) || prairie::no_worms(c))) continue;
|
||||
if(items[itOrbLava] && c2->cpdist <= 5 && pseudohept(c) && makeflame(c2, 1, true))
|
||||
continue;
|
||||
}
|
||||
|
||||
onpath(c2, d+1, c->c.spin(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if HDR
|
||||
struct pathdata {
|
||||
void checklock() {
|
||||
if(pd_from) pd_from = NULL, clear_pathdata();
|
||||
if(pathlock) printf("path error\n");
|
||||
pathlock++;
|
||||
}
|
||||
~pathdata() {
|
||||
pathlock--;
|
||||
clear_pathdata();
|
||||
}
|
||||
pathdata(eMonster m) {
|
||||
checklock();
|
||||
computePathdist(m);
|
||||
}
|
||||
pathdata(int i) {
|
||||
checklock();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
// pathdist end
|
||||
|
||||
/** calculate cpdist, 'have' flags, and do general fixings */
|
||||
EX void bfs() {
|
||||
|
||||
calcTidalPhase();
|
||||
|
||||
yendor::onpath();
|
||||
|
||||
int dcs = isize(dcal);
|
||||
for(int i=0; i<dcs; i++) dcal[i]->cpdist = INFD;
|
||||
worms.clear(); ivies.clear(); ghosts.clear(); golems.clear();
|
||||
tempmonsters.clear(); targets.clear();
|
||||
statuecount = 0;
|
||||
hexsnakes.clear();
|
||||
|
||||
hadwhat = havewhat;
|
||||
havewhat = 0; jiangshi_on_screen = 0;
|
||||
snaketypes.clear();
|
||||
if(!(hadwhat & HF_WARP)) { avengers = 0; }
|
||||
if(!(hadwhat & HF_MIRROR)) { mirrorspirits = 0; }
|
||||
|
||||
elec::havecharge = false;
|
||||
elec::afterOrb = false;
|
||||
elec::haveelec = false;
|
||||
airmap.clear();
|
||||
if(!(hadwhat & HF_ROSE)) rosemap.clear();
|
||||
|
||||
dcal.clear(); reachedfrom.clear();
|
||||
|
||||
recalcTide = false;
|
||||
|
||||
for(int i=0; i<numplayers(); i++) {
|
||||
cell *c = playerpos(i);
|
||||
if(!c) continue;
|
||||
if(c->cpdist == 0) continue;
|
||||
c->cpdist = 0;
|
||||
checkTide(c);
|
||||
dcal.push_back(c);
|
||||
reachedfrom.push_back(hrand(c->type));
|
||||
if(!invismove) targets.push_back(c);
|
||||
}
|
||||
|
||||
int distlimit = gamerange();
|
||||
|
||||
for(int i=0; i<numplayers(); i++) {
|
||||
cell *c = playerpos(i);
|
||||
if(!c) continue;
|
||||
if(items[itOrbDomination])
|
||||
if(c->monst == moTentacle || c->monst == moTentaclewait || c->monst == moTentacleEscaping)
|
||||
worms.push_back(c);
|
||||
}
|
||||
|
||||
int qb = 0;
|
||||
first7 = 0;
|
||||
while(true) {
|
||||
if(qb == isize(dcal)) break;
|
||||
int i, fd = reachedfrom[qb] + 3;
|
||||
cell *c = dcal[qb++];
|
||||
|
||||
int d = c->cpdist;
|
||||
|
||||
if(WDIM == 2 && d == distlimit) { first7 = qb; break; }
|
||||
|
||||
for(int j=0; j<c->type; j++) if(i = (fd+j) % c->type, c->move(i)) {
|
||||
// printf("i=%d cd=%d\n", i, c->move(i)->cpdist);
|
||||
cell *c2 = c->move(i);
|
||||
if(!c2) continue;
|
||||
|
||||
if(isWarpedType(c2->land)) havewhat |= HF_WARP;
|
||||
if(c2->land == laMirror) havewhat |= HF_MIRROR;
|
||||
|
||||
if((c->wall == waBoat || c->wall == waSea) &&
|
||||
(c2->wall == waSulphur || c2->wall == waSulphurC))
|
||||
c2->wall = waSea;
|
||||
|
||||
if(c2 && signed(c2->cpdist) > d+1) {
|
||||
if(WDIM == 3 && !gmatrix.count(c2)) {
|
||||
if(!first7) first7 = qb;
|
||||
continue;
|
||||
}
|
||||
c2->cpdist = d+1;
|
||||
|
||||
// remove treasures
|
||||
if(!peace::on && c2->item && c2->cpdist == distlimit && itemclass(c2->item) == IC_TREASURE &&
|
||||
c2->item != itBabyTortoise &&
|
||||
(items[c2->item] >= (chaosmode?10:20) + currentLocalTreasure || getGhostcount() >= 2)) {
|
||||
c2->item = itNone;
|
||||
if(c2->land == laMinefield) { c2->landparam &= ~3; }
|
||||
}
|
||||
|
||||
if(c2->item == itBombEgg && c2->cpdist == distlimit && items[itBombEgg] >= c2->landparam) {
|
||||
c2->item = itNone;
|
||||
c2->landparam |= 2;
|
||||
c2->landparam &= ~1;
|
||||
if(!c2->monst) c2->monst = moBomberbird;
|
||||
}
|
||||
|
||||
if(c2->item == itBarrow && c2->cpdist == distlimit && c2->wall != waBarrowDig) {
|
||||
c2->item = itNone;
|
||||
}
|
||||
|
||||
if(c2->item == itLotus && c2->cpdist == distlimit && items[itLotus] >= getHauntedDepth(c2)) {
|
||||
c2->item = itNone;
|
||||
}
|
||||
|
||||
if(c2->item == itMutant2 && timerghost) {
|
||||
bool rotten = true;
|
||||
for(int i=0; i<c2->type; i++)
|
||||
if(c2->move(i) && c2->move(i)->monst == moMutant)
|
||||
rotten = false;
|
||||
if(rotten) c2->item = itNone;
|
||||
}
|
||||
|
||||
if(c2->item == itDragon && (shmup::on ? shmup::curtime-c2->landparam>300000 :
|
||||
turncount-c2->landparam > 500))
|
||||
c2->item = itNone;
|
||||
|
||||
if(c2->item == itTrollEgg && c2->cpdist == distlimit && !shmup::on && c2->landparam && turncount-c2->landparam > 650)
|
||||
c2->item = itNone;
|
||||
|
||||
if(c2->item == itWest && c2->cpdist == distlimit && items[itWest] >= c2->landparam + 4)
|
||||
c2->item = itNone;
|
||||
|
||||
if(c2->item == itMutant && c2->cpdist == distlimit && items[itMutant] >= c2->landparam) {
|
||||
c2->item = itNone;
|
||||
}
|
||||
|
||||
if(c2->item == itIvory && c2->cpdist == distlimit && items[itIvory] >= c2->landparam) {
|
||||
c2->item = itNone;
|
||||
}
|
||||
|
||||
if(c2->item == itAmethyst && c2->cpdist == distlimit && items[itAmethyst] >= -celldistAlt(c2)/5) {
|
||||
c2->item = itNone;
|
||||
}
|
||||
|
||||
if(!keepLightning) c2->ligon = 0;
|
||||
dcal.push_back(c2);
|
||||
reachedfrom.push_back(c->c.spin(i));
|
||||
|
||||
checkTide(c2);
|
||||
|
||||
if(c2->wall == waBigStatue && c2->land != laTemple)
|
||||
statuecount++;
|
||||
|
||||
if(cellHalfvine(c2) && isWarped(c2)) {
|
||||
addMessage(XLAT("%The1 is destroyed!", c2->wall));
|
||||
destroyHalfvine(c2);
|
||||
}
|
||||
|
||||
if(c2->wall == waCharged) elec::havecharge = true;
|
||||
if(c2->land == laStorms) elec::haveelec = true;
|
||||
|
||||
if(c2->land == laWhirlpool) havewhat |= HF_WHIRLPOOL;
|
||||
if(c2->land == laWhirlwind) havewhat |= HF_WHIRLWIND;
|
||||
if(c2->land == laWestWall) havewhat |= HF_WESTWALL;
|
||||
if(c2->land == laPrairie) havewhat |= HF_RIVER;
|
||||
|
||||
if(c2->wall == waRose) havewhat |= HF_ROSE;
|
||||
|
||||
if((hadwhat & HF_ROSE) && (rosemap[c2] & 3)) havewhat |= HF_ROSE;
|
||||
|
||||
if(c2->monst) {
|
||||
if(isHaunted(c2->land) &&
|
||||
c2->monst != moGhost && c2->monst != moZombie && c2->monst != moNecromancer)
|
||||
survivalist = false;
|
||||
if(c2->monst == moHexSnake || c2->monst == moHexSnakeTail) {
|
||||
havewhat |= HF_HEX;
|
||||
if(c2->mondir != NODIR)
|
||||
snaketypes.insert(snake_pair(c2));
|
||||
if(c2->monst == moHexSnake) hexsnakes.push_back(c2);
|
||||
else findWormIvy(c2);
|
||||
}
|
||||
else if(c2->monst == moKrakenT || c2->monst == moKrakenH) {
|
||||
havewhat |= HF_KRAKEN;
|
||||
}
|
||||
else if(c2->monst == moDragonHead || c2->monst == moDragonTail) {
|
||||
havewhat |= HF_DRAGON;
|
||||
}
|
||||
else if(c2->monst == moWitchSpeed)
|
||||
havewhat |= HF_FAST;
|
||||
else if(c2->monst == moMutant)
|
||||
havewhat |= HF_MUTANT;
|
||||
else if(c2->monst == moJiangshi)
|
||||
jiangshi_on_screen++;
|
||||
else if(c2->monst == moOutlaw)
|
||||
havewhat |= HF_OUTLAW;
|
||||
else if(isGhostMover(c2->monst))
|
||||
ghosts.push_back(c2);
|
||||
else if(isWorm(c2) || isIvy(c2)) findWormIvy(c2);
|
||||
else if(isBug(c2)) {
|
||||
havewhat |= HF_BUG;
|
||||
targets.push_back(c2);
|
||||
}
|
||||
else if(isFriendly(c2)) {
|
||||
if(c2->monst != moMouse && !markEmpathy(itOrbInvis) && !(isWatery(c2) && markEmpathy(itOrbFish)) &&
|
||||
!c2->stuntime) targets.push_back(c2);
|
||||
if(c2->monst == moGolem) golems.push_back(c2);
|
||||
if(c2->monst == moFriendlyGhost) golems.push_back(c2);
|
||||
if(c2->monst == moKnight) golems.push_back(c2);
|
||||
if(c2->monst == moTameBomberbird) golems.push_back(c2);
|
||||
if(c2->monst == moMouse) { golems.push_back(c2); havewhat |= HF_MOUSE; }
|
||||
if(c2->monst == moPrincess || c2->monst == moPrincessArmed) golems.push_back(c2);
|
||||
if(c2->monst == moIllusion) {
|
||||
if(items[itOrbIllusion]) items[itOrbIllusion]--;
|
||||
else c2->monst = moNone;
|
||||
}
|
||||
}
|
||||
else if(c2->monst == moButterfly) {
|
||||
addButterfly(c2);
|
||||
}
|
||||
else if(isAngryBird(c2->monst)) {
|
||||
havewhat |= HF_BIRD;
|
||||
if(c2->monst == moBat) havewhat |= HF_BATS | HF_EAGLES;
|
||||
if(c2->monst == moEagle) havewhat |= HF_EAGLES;
|
||||
}
|
||||
else if(c2->monst == moReptile) havewhat |= HF_REPTILE;
|
||||
else if(isLeader(c2->monst)) havewhat |= HF_LEADER;
|
||||
else if(c2->monst == moEarthElemental) havewhat |= HF_EARTH;
|
||||
else if(c2->monst == moWaterElemental) havewhat |= HF_WATER;
|
||||
else if(c2->monst == moVoidBeast) havewhat |= HF_VOID;
|
||||
else if(c2->monst == moHunterDog) havewhat |= HF_HUNTER;
|
||||
else if(isMagneticPole(c2->monst)) havewhat |= HF_MAGNET;
|
||||
else if(c2->monst == moAltDemon) havewhat |= HF_ALT;
|
||||
else if(c2->monst == moHexDemon) havewhat |= HF_HEXD;
|
||||
else if(c2->monst == moMonk) havewhat |= HF_MONK;
|
||||
else if(c2->monst == moShark || c2->monst == moCShark) havewhat |= HF_SHARK;
|
||||
else if(c2->monst == moAirElemental)
|
||||
havewhat |= HF_AIR, airmap.push_back(make_pair(c2,0));
|
||||
}
|
||||
// pheromones!
|
||||
if(c2->land == laHive && c2->landparam >= 50 && c2->wall != waWaxWall)
|
||||
havewhat |= HF_BUG;
|
||||
if(c2->wall == waThumperOn)
|
||||
targets.push_back(c2);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(recalcTide) {
|
||||
recalcTide = false;
|
||||
for(int i=0; i<isize(dcal); i++) checkTide(dcal[i]);
|
||||
}
|
||||
|
||||
for(auto& t: tempmonsters) t.first->monst = t.second;
|
||||
|
||||
buildAirmap();
|
||||
}
|
||||
|
||||
EX void moverefresh(bool turn IS(true)) {
|
||||
int dcs = isize(dcal);
|
||||
|
||||
for(int i=0; i<dcs; i++) {
|
||||
cell *c = dcal[i];
|
||||
|
||||
if(c->monst == moWolfMoved) c->monst = moWolf;
|
||||
if(c->monst == moIvyNext) {
|
||||
c->monst = moIvyHead; ivynext(c);
|
||||
}
|
||||
if(c->monst == moIvyDead)
|
||||
removeIvy(c);
|
||||
refreshFriend(c);
|
||||
if(c->monst == moSlimeNextTurn) c->monst = moSlime;
|
||||
if(c->monst == moLesser && !cellEdgeUnstable(c)) c->monst = moLesserM;
|
||||
else if(c->monst == moLesserM) c->monst = moLesser;
|
||||
if(c->monst == moGreater && !cellEdgeUnstable(c)) c->monst = moGreaterM;
|
||||
else if(c->monst == moGreaterM) c->monst = moGreater;
|
||||
|
||||
if(c->monst == moPair && !c->stuntime) {
|
||||
cell *c2 = c->move(c->mondir);
|
||||
if(c2->monst != moPair) continue;
|
||||
if(true) for(int i: {-1, 1}) {
|
||||
cell *c3 = c->modmove(c->mondir + i);
|
||||
if(among(c3->wall, waRuinWall, waColumn, waStone, waVinePlant, waPalace)) {
|
||||
drawParticles(c3, winf[c3->wall].color, 30);
|
||||
c3->wall = waNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(c->stuntime && !isMutantIvy(c)) {
|
||||
c->stuntime--;
|
||||
int breathrange = sphere ? 2 : 3;
|
||||
if(c->stuntime == 0 && c->monst == moDragonHead) {
|
||||
// if moDragonHead is renamed to "Dragon Head", we might need to change this
|
||||
eMonster subject = c->monst;
|
||||
if(!c->hitpoints) c->hitpoints = 1;
|
||||
else if(shmup::on && dragon::totalhp(c) > 2 && shmup::dragonbreath(c)) {
|
||||
c->hitpoints = 0;
|
||||
}
|
||||
else if(dragon::totalhp(c) <= 2) ;
|
||||
else if(isMounted(c)) {
|
||||
if(dragon::target && celldistance(c, dragon::target) <= breathrange && makeflame(dragon::target, 5, true)) {
|
||||
addMessage(XLAT("%The1 breathes fire!", subject));
|
||||
makeflame(dragon::target, 5, false);
|
||||
playSound(dragon::target, "fire");
|
||||
c->hitpoints = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for(int i=0; i<isize(targets); i++) {
|
||||
cell *t = targets[i];
|
||||
if(celldistance(c, t) <= breathrange && makeflame(t, 5, true)) {
|
||||
if(isPlayerOn(t)) addMessage(XLAT("%The1 breathes fire at you!", subject));
|
||||
else if(t->monst)
|
||||
addMessage(XLAT("%The1 breathes fire at %the2!", subject, t->monst));
|
||||
else
|
||||
addMessage(XLAT("%The1 breathes fire!", subject));
|
||||
makeflame(t, 5, false);
|
||||
playSound(t, "fire");
|
||||
c->hitpoints = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tortoises who have found their children no longer move
|
||||
if(saved_tortoise_on(c))
|
||||
c->stuntime = 2;
|
||||
|
||||
if(c->monst == moReptile) {
|
||||
if(c->wall == waChasm || cellUnstable(c)) {
|
||||
c->monst = moNone;
|
||||
c->wall = waReptile;
|
||||
c->wparam = reptilemax();
|
||||
playSound(c, "click");
|
||||
}
|
||||
else if(isChasmy(c) || isWatery(c)) {
|
||||
if(c->wall == waMercury) {
|
||||
fallMonster(c, AF_FALL);
|
||||
c->wall = waNone;
|
||||
}
|
||||
else {
|
||||
c->wall = waReptileBridge;
|
||||
c->wparam = reptilemax();
|
||||
c->monst = moNone;
|
||||
}
|
||||
c->item = itNone;
|
||||
playSound(c, "click");
|
||||
}
|
||||
}
|
||||
|
||||
if(c->wall == waChasm) {
|
||||
if(c->land != laWhirlwind) c->item = itNone;
|
||||
|
||||
if(c->monst && !survivesChasm(c->monst) && c->monst != moReptile && normal_gravity_at(c)) {
|
||||
if(c->monst != moRunDog && c->land == laMotion)
|
||||
achievement_gain("FALLDEATH1");
|
||||
addMessage(XLAT("%The1 falls!", c->monst));
|
||||
fallMonster(c, AF_FALL);
|
||||
}
|
||||
}
|
||||
|
||||
else if(isReptile(c->wall) && turn) {
|
||||
if(c->monst || isPlayerOn(c)) c->wparam = -1;
|
||||
else if(c->cpdist <= 7) {
|
||||
c->wparam--;
|
||||
if(c->wparam == 0) {
|
||||
if(c->wall == waReptile) c->wall = waChasm;
|
||||
else placeWater(c, NULL);
|
||||
c->monst = moReptile;
|
||||
c->hitpoints = 3;
|
||||
c->stuntime = 0;
|
||||
int gooddirs[MAX_EDGE], qdirs = 0;
|
||||
// in the peace mode, a reptile will
|
||||
// prefer to walk on the ground, rather than the chasm
|
||||
for(int i=0; i<c->type; i++) {
|
||||
int i0 = (i+3) % c->type;
|
||||
int i1 = (i+c->type-3) % c->type;
|
||||
if(c->move(i0) && passable(c->move(i0), c, 0))
|
||||
if(c->move(i1) && passable(c->move(i1), c, 0))
|
||||
gooddirs[qdirs++] = i;
|
||||
}
|
||||
if(qdirs) c->mondir = gooddirs[hrand(qdirs)];
|
||||
playSound(c, "click");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if(isFire(c)) {
|
||||
if(c->monst == moSalamander) c->stuntime = max<int>(c->stuntime, 1);
|
||||
else if(c->monst && !survivesFire(c->monst) && !isWorm(c->monst)) {
|
||||
addMessage(XLAT("%The1 burns!", c->monst));
|
||||
if(isBull(c->monst)) {
|
||||
addMessage(XLAT("Fire is extinguished!"));
|
||||
c->wall = waNone;
|
||||
}
|
||||
fallMonster(c, AF_CRUSH);
|
||||
}
|
||||
if(c->item && itemBurns(c->item)) {
|
||||
addMessage(XLAT("%The1 burns!", c->item));
|
||||
c->item = itNone;
|
||||
}
|
||||
}
|
||||
|
||||
else if(isWatery(c)) {
|
||||
if(c->monst == moLesser || c->monst == moLesserM || c->monst == moGreater || c->monst == moGreaterM)
|
||||
c->monst = moGreaterShark;
|
||||
if(c->monst && !survivesWater(c->monst) && normal_gravity_at(c)) {
|
||||
playSound(c, "splash"+pick12());
|
||||
if(isNonliving(c->monst))
|
||||
addMessage(XLAT("%The1 sinks!", c->monst));
|
||||
else
|
||||
addMessage(XLAT("%The1 drowns!", c->monst));
|
||||
if(isBull(c->monst)) {
|
||||
addMessage(XLAT("%The1 is filled!", c->wall));
|
||||
c->wall = waNone;
|
||||
}
|
||||
fallMonster(c, AF_FALL);
|
||||
}
|
||||
}
|
||||
else if(c->wall == waSulphur || c->wall == waSulphurC || c->wall == waMercury) {
|
||||
if(c->monst && !survivesPoison(c->monst, c->wall) && normal_gravity_at(c)) {
|
||||
playSound(c, "splash"+pick12());
|
||||
if(isNonliving(c->monst))
|
||||
addMessage(XLAT("%The1 sinks!", c->monst));
|
||||
else
|
||||
addMessage(XLAT("%The1 drowns!", c->monst));
|
||||
if(isBull(c->monst)) {
|
||||
addMessage(XLAT("%The1 is filled!", c->wall));
|
||||
c->wall = waNone;
|
||||
}
|
||||
fallMonster(c, AF_FALL);
|
||||
}
|
||||
}
|
||||
else if(c->wall == waMagma) {
|
||||
if(c->monst == moSalamander) c->stuntime = max<int>(c->stuntime, 1);
|
||||
else if(c->monst && !survivesPoison(c->monst, c->wall) && normal_gravity_at(c)) {
|
||||
if(isNonliving(c->monst))
|
||||
addMessage(XLAT("%The1 is destroyed by lava!", c->monst));
|
||||
else
|
||||
addMessage(XLAT("%The1 is killed by lava!", c->monst));
|
||||
playSound(c, "steamhiss", 70);
|
||||
fallMonster(c, AF_FALL);
|
||||
}
|
||||
}
|
||||
else if(!isWateryOrBoat(c)) {
|
||||
if(c->monst == moGreaterShark)
|
||||
c->monst = moGreaterM;
|
||||
else if(c->monst == moShark || c->monst == moCShark) {
|
||||
addMessage(XLAT("%The1 suffocates!", c->monst));
|
||||
fallMonster(c, AF_CRUSH);
|
||||
}
|
||||
else if(c->monst == moKrakenH) {
|
||||
addMessage(XLAT("%The1 suffocates!", c->monst));
|
||||
kraken::kill(c, moNone);
|
||||
}
|
||||
}
|
||||
|
||||
if(c->monst == moVineSpirit && !cellHalfvine(c) && c->wall != waVinePlant) {
|
||||
addMessage(XLAT("%The1 is destroyed!", c->monst));
|
||||
fallMonster(c, AF_CRUSH);
|
||||
}
|
||||
|
||||
if(c->monst) mayExplodeMine(c, c->monst);
|
||||
|
||||
if(c->monst && c->wall == waClosedGate && !survivesWall(c->monst)) {
|
||||
playSound(c, "hit-crush"+pick123());
|
||||
addMessage(XLAT("%The1 is crushed!", c->monst));
|
||||
fallMonster(c, AF_CRUSH);
|
||||
}
|
||||
|
||||
if(c->monst && cellUnstable(c) && !ignoresPlates(c->monst) && !shmup::on)
|
||||
doesFallSound(c);
|
||||
}
|
||||
}
|
||||
|
||||
// find worms and ivies
|
||||
EX void settemp(cell *c) {
|
||||
tempmonsters.emplace_back(c, (eMonster) c->monst);
|
||||
c->monst = moNone;
|
||||
}
|
||||
|
||||
EX void findWormIvy(cell *c) {
|
||||
while(true) {
|
||||
if(c->monst == moWorm || c->monst == moTentacle || c->monst == moWormwait || c->monst == moTentaclewait ||
|
||||
c->monst == moTentacleEscaping) {
|
||||
worms.push_back(c); settemp(c);
|
||||
break;
|
||||
}
|
||||
else if(c->monst == moHexSnake) {
|
||||
hexsnakes.push_back(c); settemp(c);
|
||||
}
|
||||
else if(c->monst == moWormtail || c->monst == moHexSnakeTail) {
|
||||
bool bug = true;
|
||||
for(int i=0; i<c->type; i++) {
|
||||
cell* c2 = c->move(i);
|
||||
if(c2 && isWorm(c2) && c2->mondir != NODIR && c2->move(c2->mondir) == c) {
|
||||
settemp(c);
|
||||
c = c2;
|
||||
bug = false;
|
||||
}
|
||||
}
|
||||
if(bug) break;
|
||||
}
|
||||
else if(c->monst == moIvyWait) {
|
||||
cell* c2 = c->move(c->mondir);
|
||||
settemp(c); c=c2;
|
||||
}
|
||||
else if(c->monst == moIvyHead) {
|
||||
ivies.push_back(c); settemp(c);
|
||||
break;
|
||||
}
|
||||
else if(c->monst == moIvyBranch || c->monst == moIvyRoot) {
|
||||
bool bug = true;
|
||||
for(int i=0; i<c->type; i++) {
|
||||
cell* c2 = c->move(i);
|
||||
if(c2 && (c2->monst == moIvyHead || c2->monst == moIvyBranch) && c2->move(c2->mondir) == c) {
|
||||
settemp(c);
|
||||
c = c2;
|
||||
bug = false;
|
||||
}
|
||||
}
|
||||
if(bug) break;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
EX void monstersTurn() {
|
||||
checkSwitch();
|
||||
mirror::breakAll();
|
||||
DEBB(DF_TURN, ("bfs"));
|
||||
bfs();
|
||||
DEBB(DF_TURN, ("charge"));
|
||||
if(elec::havecharge) elec::act();
|
||||
DEBB(DF_TURN, ("mmo"));
|
||||
int phase2 = (1 & items[itOrbSpeed]);
|
||||
if(!phase2) movemonsters();
|
||||
for(int i=0; i<numplayers(); i++) if(playerpos(i)->item == itOrbSafety) {
|
||||
collectItem(playerpos(i), true);
|
||||
return;
|
||||
}
|
||||
|
||||
if(playerInPower() && (phase2 || !items[itOrbSpeed]) && (havewhat & HF_FAST))
|
||||
moveNormals(moWitchSpeed);
|
||||
|
||||
if(phase2 && markOrb(itOrbEmpathy)) {
|
||||
bfs();
|
||||
movegolems(AF_FAST);
|
||||
for(int i=0; i<isize(dcal); i++) {
|
||||
if(dcal[i]->monst == moFriendlyGhost && dcal[i]->stuntime)
|
||||
dcal[i]->stuntime--;
|
||||
refreshFriend(dcal[i]);
|
||||
}
|
||||
}
|
||||
DEBB(DF_TURN, ("rop"));
|
||||
if(!dual::state) reduceOrbPowers();
|
||||
int phase1 = (1 & items[itOrbSpeed]);
|
||||
if(dual::state && items[itOrbSpeed]) phase1 = !phase1;
|
||||
DEBB(DF_TURN, ("lc"));
|
||||
if(!phase1) livecaves();
|
||||
if(!phase1) ca::simulate();
|
||||
if(!phase1) heat::processfires();
|
||||
|
||||
for(cell *c: crush_now) {
|
||||
playSound(NULL, "closegate");
|
||||
if(canAttack(c, moCrusher, c, c->monst, AF_GETPLAYER | AF_CRUSH)) {
|
||||
attackMonster(c, AF_MSG | AF_GETPLAYER | AF_CRUSH, moCrusher);
|
||||
}
|
||||
moveEffect(movei(c, FALL), moDeadBird);
|
||||
destroyBoats(c, NULL, true);
|
||||
explodeBarrel(c);
|
||||
}
|
||||
|
||||
crush_now = move(crush_next);
|
||||
crush_next.clear();
|
||||
|
||||
DEBB(DF_TURN, ("heat"));
|
||||
heat::processheat();
|
||||
// if(elec::havecharge) elec::drawcharges();
|
||||
|
||||
orbbull::check();
|
||||
|
||||
if(!phase1) terracotta::check();
|
||||
|
||||
if(items[itOrbFreedom])
|
||||
for(int i=0; i<numplayers(); i++)
|
||||
if(multi::playerActive(i))
|
||||
checkFreedom(playerpos(i));
|
||||
|
||||
DEBB(DF_TURN, ("check"));
|
||||
checkmove();
|
||||
if(canmove) elec::checklightningfast();
|
||||
|
||||
|
||||
#if CAP_HISTORY
|
||||
for(int i=0; i<numplayers(); i++)
|
||||
if(multi::playerActive(i))
|
||||
history::movehistory.push_back(playerpos(i));
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
@ -976,6 +976,7 @@ EX void drawTerraWarrior(const transmatrix& V, int t, int hp, double footphase)
|
||||
|
||||
void drawPlayer(eMonster m, cell *where, const transmatrix& V, color_t col, double footphase, bool stop = false) {
|
||||
charstyle& cs = getcs();
|
||||
auto& knighted = camelot::knighted;
|
||||
|
||||
if(mapeditor::drawplayer && !mapeditor::drawUserShape(V, mapeditor::sgPlayer, cs.charid, cs.skincolor, where)) {
|
||||
|
||||
@ -1219,7 +1220,7 @@ void drawMimic(eMonster m, cell *where, const transmatrix& V, color_t col, doubl
|
||||
else if(!where || shmup::curtime >= shmup::getPlayer()->nextshot)
|
||||
queuepoly(VBODY * VBS, cgi.shPKnife, darkena(col, 0, 0XC0));
|
||||
|
||||
if(knighted)
|
||||
if(camelot::knighted)
|
||||
queuepoly(VBODY3 * VBS, cgi.shKnightCloak, darkena(col, 1, 0xC0));
|
||||
|
||||
queuepoly(VHEAD1, (cs.charid&1) ? cgi.shFemaleHair : cgi.shPHead, darkena(col, 1, 0XC0));
|
||||
@ -3263,7 +3264,7 @@ EX bool placeSidewall(cell *c, int i, int sidepar, const transmatrix& V, color_t
|
||||
#endif
|
||||
|
||||
bool openorsafe(cell *c) {
|
||||
return c->wall == waMineOpen || mineMarkedSafe(c);
|
||||
return c->wall == waMineOpen || mine::marked_safe(c);
|
||||
}
|
||||
|
||||
#define Dark(x) darkena(x,0,0xFF)
|
||||
@ -3989,8 +3990,8 @@ EX void drawMarkers() {
|
||||
queuecircleat(lmouseover, .8, darkena(lmouseover->cpdist > 1 ? 0x00FFFF : 0xFF0000, 0, 0xFF));
|
||||
}
|
||||
|
||||
if(global_pushto && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON && WDIM == 2) {
|
||||
queuecircleat(global_pushto, .6, darkena(0xFFD500, 0, 0xFF));
|
||||
if(pcm.mip.t && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON && WDIM == 2) {
|
||||
queuecircleat(pcm.mip.t, .6, darkena(0xFFD500, 0, 0xFF));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
2
help.cpp
2
help.cpp
@ -885,7 +885,7 @@ EX void describeMouseover() {
|
||||
out += XLAT1(iinf[c->item].name);
|
||||
if(c->item == itBarrow) out += " (x" + its(c->landparam) + ")";
|
||||
if(c->land == laHunting) {
|
||||
int i = ambushSize(c, c->item);
|
||||
int i = ambush::size(c, c->item);
|
||||
if(i) out += " (" + XLAT("ambush:") + " " + its(i) + ")";
|
||||
}
|
||||
if(c->item == itBabyTortoise && tortoise::seek())
|
||||
|
@ -55,6 +55,14 @@
|
||||
#include "complex2.cpp"
|
||||
#include "savemem.cpp"
|
||||
#include "game.cpp"
|
||||
#include "passable.cpp"
|
||||
#include "checkmove.cpp"
|
||||
#include "pcmove.cpp"
|
||||
#include "environment.cpp"
|
||||
#include "monstermove.cpp"
|
||||
#include "mapeffects.cpp"
|
||||
#include "attack.cpp"
|
||||
#include "items.cpp"
|
||||
#include "orbgen.cpp"
|
||||
#include "monstergen.cpp"
|
||||
#include "landlock.cpp"
|
||||
|
1
hyper.h
1
hyper.h
@ -410,7 +410,6 @@ typedef function<int(struct cell*)> cellfunction;
|
||||
#define PT(x, y) ((tactic::on || quotient == 2 || daily::on) ? (y) : inv::on ? min(2*(y),x) : (x))
|
||||
#define ROCKSNAKELENGTH 50
|
||||
#define WORMLENGTH 15
|
||||
#define PUREHARDCORE_LEVEL 10
|
||||
#define PRIZEMUL 7
|
||||
|
||||
#define INF 9999
|
||||
|
631
items.cpp
Normal file
631
items.cpp
Normal file
@ -0,0 +1,631 @@
|
||||
// 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 cannotPickupItem(cell *c, bool telekinesis) {
|
||||
return itemHidden(c) && !telekinesis && !(isWatery(c) && markOrb(itOrbFish));
|
||||
}
|
||||
|
||||
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) {
|
||||
cell *csaf = NULL;
|
||||
if(items[itOrbMagnetism])
|
||||
forCellEx(c3, c) if(canPickupItemWithMagnetism(c3, 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))
|
||||
collectItem(c3, false);
|
||||
}
|
||||
if(csaf)
|
||||
return collectItem(csaf, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
EX void pickupMovedItems(cell *c) {
|
||||
if(!c->item) return;
|
||||
if(c->item == itOrbSafety) return;
|
||||
if(isPlayerOn(c)) collectItem(c, true);
|
||||
if(items[itOrbMagnetism])
|
||||
forCellEx(c2, c)
|
||||
if(isPlayerOn(c2) && canPickupItemWithMagnetism(c, c2))
|
||||
collectItem(c, true);
|
||||
}
|
||||
|
||||
EX bool collectItem(cell *c2, bool telekinesis IS(false)) {
|
||||
|
||||
bool dopickup = true;
|
||||
bool had_choice = false;
|
||||
|
||||
if(cannotPickupItem(c2, telekinesis))
|
||||
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)
|
||||
princess::forceVizier = true;
|
||||
|
||||
if(!cantGetGrimoire(c2, false)) collectMessage(c2, c2->item);
|
||||
if(c2->item == itDodeca && peace::on) peace::simon::extend();
|
||||
}
|
||||
|
||||
if(c2->land == laHunting && c2->item && !inv::activating) {
|
||||
int dogs = ambush::ambush(c2, c2->item);
|
||||
if(dogs)
|
||||
addMessage(XLAT("You are ambushed!"));
|
||||
}
|
||||
|
||||
if(isRevivalOrb(c2->item) && multi::revive_queue.size()) {
|
||||
multiRevival(cwt.at, 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");
|
||||
if(items[itOrbChoice]) items[itOrbChoice] = 0, had_choice = true;
|
||||
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(cwt.at, c2, moGolem);
|
||||
}
|
||||
else if(c2->item == itOrbFriend) {
|
||||
playSound(c2, "pickup-orb"); // TODO summon
|
||||
placeGolem(cwt.at, c2, moTameBomberbird);
|
||||
}
|
||||
#if CAP_TOUR
|
||||
else if(tour::on && (c2->item == itOrbSafety || c2->item == itOrbRecall)) {
|
||||
addMessage(XLAT("This Orb is not compatible with the Tutorial."));
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
else if(c2->item == itOrbSafety) {
|
||||
playSound(c2, "pickup-orb"); // TODO safety
|
||||
if(!dual::state) items[c2->item] = 7;
|
||||
if(shmup::on)
|
||||
shmup::delayed_safety = true;
|
||||
else
|
||||
activateSafety(c2->land);
|
||||
return true;
|
||||
}
|
||||
else if(c2->item == itBabyTortoise) {
|
||||
using namespace tortoise;
|
||||
int bnew = babymap[c2];
|
||||
babymap.erase(c2);
|
||||
int bold = seekbits;
|
||||
seekbits = bnew;
|
||||
tortoise::last = seekbits;
|
||||
if(seek()) {
|
||||
cell *c = passable(cwt.at, NULL, 0) ? cwt.at : c2;
|
||||
c->item = itBabyTortoise;
|
||||
if(c == c2) dopickup = false;
|
||||
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->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)
|
||||
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) {
|
||||
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 {
|
||||
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) {
|
||||
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 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) {
|
||||
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) {
|
||||
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)) survivalist = false;
|
||||
}
|
||||
}
|
||||
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)
|
||||
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)
|
||||
yendor::yi[i].path[YDIST-1] = to;
|
||||
for(int i=0; i<isize(yendor::yi); i++) if(yendor::yi[i].actualKey == from)
|
||||
yendor::yi[i].actualKey = to;
|
||||
}
|
||||
|
||||
if(from->item == itBabyTortoise) {
|
||||
tortoise::babymap[to] = tortoise::babymap[from];
|
||||
tortoise::babymap.erase(from);
|
||||
}
|
||||
|
||||
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 > 3) break;
|
||||
eItem i2 = treasureType(c2->land);
|
||||
if(i2 && items[i2] < currentLocalTreasure)
|
||||
currentLocalTreasure = items[i2];
|
||||
}
|
||||
}
|
||||
|
||||
#if HDR
|
||||
static const int NO_TREASURE = 1;
|
||||
static const int NO_YENDOR = 2;
|
||||
static const int NO_GRAIL = 4;
|
||||
static const 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(eItem it, int v) {
|
||||
if(!yendor::on)
|
||||
if(v > hiitems[modecode()][it]) hiitems[modecode()][it] = v;
|
||||
}
|
||||
|
||||
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]));
|
||||
}
|
||||
|
||||
if(it == itBounty)
|
||||
items[itRevolver] = 6;
|
||||
|
||||
if(it == itHyperstone && items[itHyperstone] == 10)
|
||||
achievement_victory(true);
|
||||
|
||||
if(chaosmode && gold() >= 300 && !chaosAchieved) {
|
||||
achievement_gain("CHAOS", rg::chaos);
|
||||
chaosAchieved = true;
|
||||
}
|
||||
|
||||
#if ISMOBILE==1
|
||||
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(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 9 kinds...", its(R10)));
|
||||
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);
|
||||
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(chaosmode) return false;
|
||||
if(!eubinary && !c2->master->alt) return false;
|
||||
if(c2->item == itGrimoire && items[itGrimoire] > celldistAlt(c2)/-TEMPLE_EACH) {
|
||||
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 highlights 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 && !chaosmode)
|
||||
addMessage(XLAT("You feel the presence of free saves on the Crossroads."));
|
||||
else if(which == itHell && items[itHell] == 25-1 && !specialmode && inv::on && !chaosmode)
|
||||
addMessage(XLAT("You feel the Orbs of Yendor nearby..."));
|
||||
else if(which == itHell && items[itHell] == 50-1 && !specialmode && inv::on && !chaosmode)
|
||||
addMessage(XLAT("You feel the Orbs of Yendor in the Crossroads..."));
|
||||
else if(which == itHell && items[itHell] == 100-1 && !specialmode && inv::on && !chaosmode)
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
@ -12,6 +12,8 @@ namespace hr {
|
||||
|
||||
// land generation routines
|
||||
|
||||
EX int explore[10], exploreland[10][landtypes], landcount[landtypes];
|
||||
|
||||
EX bool safety = false;
|
||||
|
||||
EX eLand lastland;
|
||||
@ -1118,7 +1120,7 @@ EX void giantLandSwitch(cell *c, int d, cell *from) {
|
||||
createArrowTrapAt(c, laTerracotta);
|
||||
if(pseudohept(c) && hrand(100) < 40 && c->wall == waNone && !racing::on) {
|
||||
c->wall = waTerraWarrior;
|
||||
c->landparam = randterra ? 0 : 3 + hrand(3);
|
||||
c->landparam = terracotta::randterra ? 0 : 3 + hrand(3);
|
||||
if(hrand(100) < items[itTerra]-10)
|
||||
c->landparam--;
|
||||
if(hrand(100) < items[itTerra]-10)
|
||||
@ -2430,8 +2432,8 @@ EX void giantLandSwitch(cell *c, int d, cell *from) {
|
||||
if(fargen) {
|
||||
int treasure_rate = 2;
|
||||
for(int i=0; i<21; i++) if((b>>i) & 1) {
|
||||
treasure_rate += variant_features[i].rate_change;
|
||||
variant_features[i].build(c);
|
||||
treasure_rate += variant::features[i].rate_change;
|
||||
variant::features[i].build(c);
|
||||
}
|
||||
if(hrand(2000 - PT(kills[moVariantWarrior] * 5, 250)) < treasure_rate && !c->wall && !c->monst)
|
||||
c->item = itVarTreasure;
|
||||
|
12
landlock.cpp
12
landlock.cpp
@ -8,6 +8,18 @@
|
||||
#include "hyper.h"
|
||||
namespace hr {
|
||||
|
||||
EX bool in_full_game() {
|
||||
if(tactic::on) return false;
|
||||
if(princess::challenge) return false;
|
||||
if(chaosmode) return true;
|
||||
if(euclid && isCrossroads(specialland)) return true;
|
||||
if(weirdhyperbolic && specialland == laCrossroads4) return true;
|
||||
if(cryst && isCrossroads(specialland)) return true;
|
||||
if((in_s2xe() || nonisotropic || (hybri && hybrid::under_class() != gcSphere)) && isCrossroads(specialland)) return true;
|
||||
if(geometry == gNormal && !NONSTDVAR) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool nodisplay(eMonster m) {
|
||||
return
|
||||
m == moIvyDead ||
|
||||
|
929
mapeffects.cpp
Normal file
929
mapeffects.cpp
Normal file
@ -0,0 +1,929 @@
|
||||
// Hyperbolic Rogue - Map effects
|
||||
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
|
||||
|
||||
/** \file mapeffects.cpp
|
||||
* \brief Routines handling the map effects
|
||||
*/
|
||||
|
||||
#include "hyper.h"
|
||||
namespace hr {
|
||||
|
||||
/** offscreen cells to take care off */
|
||||
EX vector<cell*> offscreen;
|
||||
|
||||
EX void initcell(cell *c) {
|
||||
c->mpdist = INFD; // minimum distance from the player, ever
|
||||
c->cpdist = INFD; // current distance from the player
|
||||
c->pathdist = PINFD;// current distance from the player, along paths (used by yetis)
|
||||
c->landparam = 0; c->landflags = 0; c->wparam = 0;
|
||||
c->listindex = -1;
|
||||
c->wall = waNone;
|
||||
c->item = itNone;
|
||||
c->monst = moNone;
|
||||
c->bardir = NODIR;
|
||||
c->mondir = NODIR;
|
||||
c->barleft = c->barright = laNone;
|
||||
c->land = laNone;
|
||||
c->ligon = 0;
|
||||
c->stuntime = 0;
|
||||
c->monmirror = 0;
|
||||
}
|
||||
|
||||
EX bool doesnotFall(cell *c) {
|
||||
if(c->wall == waChasm) return false;
|
||||
else if(cellUnstable(c) && !in_gravity_zone(c)) {
|
||||
fallingFloorAnimation(c);
|
||||
c->wall = waChasm;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
EX bool doesFall(cell *c) { return !doesnotFall(c); }
|
||||
|
||||
EX bool doesFallSound(cell *c) {
|
||||
if(c->land != laMotion && c->land != laZebra)
|
||||
playSound(c, "trapdoor");
|
||||
return !doesnotFall(c);
|
||||
}
|
||||
|
||||
EX void destroyBoats(cell *c, cell *c2, bool strandedToo) {
|
||||
if(c->wall == waBoat) placeWater(c, c2);
|
||||
if(strandedToo && c->wall == waStrandedBoat) c->wall = waNone;
|
||||
shmup::destroyBoats(c);
|
||||
}
|
||||
|
||||
#if HDR
|
||||
enum eGravity { gsNormal, gsLevitation, gsAnti };
|
||||
#endif
|
||||
EX eGravity gravity_state, last_gravity_state;
|
||||
|
||||
EX bool bird_disruption(cell *c) {
|
||||
return c->cpdist <= 5 && items[itOrbGravity];
|
||||
}
|
||||
|
||||
EX bool in_gravity_zone(cell *c) {
|
||||
return gravity_state && c->cpdist <= 5;
|
||||
}
|
||||
|
||||
EX int gravity_zone_diff(cell *c) {
|
||||
if(in_gravity_zone(c)) {
|
||||
if(gravity_state == gsLevitation) return 0;
|
||||
if(gravity_state == gsAnti) return -1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
EX bool isJWall(cell *c) {
|
||||
return isWall(c) || c->monst == passive_switch;
|
||||
}
|
||||
|
||||
EX eGravity get_static_gravity(cell *c) {
|
||||
if(isGravityLand(c->land))
|
||||
return gsLevitation;
|
||||
if(among(c->wall, waArrowTrap, waFireTrap, waClosePlate, waOpenPlate, waTrapdoor))
|
||||
return gsNormal;
|
||||
forCellEx(c2, c) if(isJWall(c2))
|
||||
return gsAnti;
|
||||
if(isWatery(c) || isChasmy(c) || among(c->wall, waMagma, waMineUnknown, waMineMine, waMineOpen))
|
||||
return gsLevitation;
|
||||
return gsNormal;
|
||||
}
|
||||
|
||||
EX eGravity get_move_gravity(cell *c, cell *c2) {
|
||||
if(isGravityLand(c->land) && isGravityLand(c2->land)) {
|
||||
int d = gravityLevelDiff(c, c2);
|
||||
if(d > 0) return gsNormal;
|
||||
if(d == 0) return gsLevitation;
|
||||
if(d < 0) return gsAnti;
|
||||
return gsNormal;
|
||||
}
|
||||
else {
|
||||
if(snakelevel(c) != snakelevel(c2)) {
|
||||
int d = snakelevel(c2) - snakelevel(c);
|
||||
if(d > 0) return gsAnti;
|
||||
if(d == -3) return gsLevitation;
|
||||
return gsNormal;
|
||||
}
|
||||
forCellEx(c3, c) if(isJWall(c3))
|
||||
return gsAnti;
|
||||
forCellEx(c3, c2) if(isJWall(c3))
|
||||
return gsAnti;
|
||||
if(isWatery(c2) && c->wall == waBoat && !againstCurrent(c2, c))
|
||||
return gsNormal;
|
||||
if(isWatery(c2) || isChasmy(c2) || among(c2->wall, waMagma, waMineUnknown, waMineMine, waMineOpen) || anti_alchemy(c2, c))
|
||||
return gsLevitation;
|
||||
return gsNormal;
|
||||
}
|
||||
}
|
||||
|
||||
EX bool isWarped(cell *c) {
|
||||
return isWarpedType(c->land) || (!inmirrororwall(c->land) && (items[itOrb37] && c->cpdist <= 4));
|
||||
}
|
||||
|
||||
EX bool nonAdjacent(cell *c, cell *c2) {
|
||||
if(isWarped(c) && isWarped(c2) && warptype(c) == warptype(c2)) {
|
||||
/* int i = neighborId(c, c2);
|
||||
cell *c3 = c->modmove(i+1), *c4 = c->modmove(i-1);
|
||||
if(c3 && !isTrihepta(c3)) return false;
|
||||
if(c4 && !isTrihepta(c4)) return false; */
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool nonAdjacentPlayer(cell *c, cell *c2) {
|
||||
return nonAdjacent(c, c2) && !markOrb(itOrb37);
|
||||
}
|
||||
|
||||
EX bool thruVine(cell *c, cell *c2) {
|
||||
return (cellHalfvine(c) && c2->wall == c->wall && c2 != c);
|
||||
// ((c->wall == waFloorC || c->wall == waFloorD) && c2->wall == c->wall && !c2->item && !c->item);
|
||||
}
|
||||
|
||||
EX void useup(cell *c) {
|
||||
c->wparam--;
|
||||
if(c->wparam == 0) {
|
||||
drawParticles(c, c->wall == waFire ? 0xC00000 : winf[c->wall].color, 10, 50);
|
||||
if(c->wall == waTempFloor)
|
||||
c->wall = waChasm;
|
||||
else if(c->wall == waTempBridge || c->wall == waTempBridgeBlocked || c->wall == waBurningDock || c->land == laBrownian)
|
||||
placeWater(c, c);
|
||||
else {
|
||||
c->wall = c->land == laCaribbean ? waCIsland2 : waNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EX bool earthFloor(cell *c) {
|
||||
if(c->monst) return false;
|
||||
if(c->wall == waDeadwall) { c->wall = waDeadfloor; return true; }
|
||||
if(c->wall == waDune) { c->wall = waNone; return true; }
|
||||
if(c->wall == waStone && c->land != laTerracotta) { c->wall = waNone; return true; }
|
||||
if(c->wall == waAncientGrave || c->wall == waFreshGrave || c->wall == waRuinWall) {
|
||||
c->wall = waNone;
|
||||
return true;
|
||||
}
|
||||
if((c->wall == waSea || c->wall == waNone) && c->land == laOcean) {
|
||||
c->wall = waCIsland;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waSea && c->land == laCaribbean) {
|
||||
c->wall = waCIsland;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waSea && c->land == laWarpSea)
|
||||
c->wall = waNone;
|
||||
if(c->wall == waBoat && c->land == laWarpSea)
|
||||
c->wall = waStrandedBoat;
|
||||
if(c->wall == waMercury) {
|
||||
c->wall = waNone;
|
||||
return true;
|
||||
}
|
||||
if((c->wall == waBarrowDig || c->wall == waBarrowWall) && c->land == laBurial) {
|
||||
c->item = itNone;
|
||||
c->wall = waNone;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waPlatform && c->land == laMountain) {
|
||||
c->wall = waNone;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waChasm && c->land == laHunting) {
|
||||
c->wall = waNone;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool earthWall(cell *c) {
|
||||
if(c->wall == waDeadfloor || c->wall == waDeadfloor2 || c->wall == waEarthD) {
|
||||
c->item = itNone;
|
||||
c->wall = waDeadwall;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waNone && c->land == laMountain) {
|
||||
c->wall = waPlatform;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waNone && c->land == laDesert) {
|
||||
c->item = itNone;
|
||||
c->wall = waDune;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waNone && c->land == laRuins) {
|
||||
c->item = itNone;
|
||||
c->wall = waRuinWall;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waNone && isElemental(c->land)) {
|
||||
c->item = itNone;
|
||||
c->wall = waStone;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waNone && c->land == laRedRock) {
|
||||
c->item = itNone;
|
||||
c->wall = waRed3;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waNone && c->land == laSnakeNest) {
|
||||
c->item = itNone;
|
||||
c->wall = waRed3;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waNone && c->land == laBurial) {
|
||||
c->item = itNone;
|
||||
c->wall = waBarrowDig;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waNone && c->land == laHunting) {
|
||||
c->item = itNone;
|
||||
c->wall = waChasm;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waNone && c->land == laTerracotta) {
|
||||
c->wall = waMercury;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waArrowTrap && c->land == laTerracotta) {
|
||||
destroyTrapsOn(c);
|
||||
c->wall = waMercury;
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waCIsland || c->wall == waCIsland2 || (c->wall == waNone && c->land == laOcean)) {
|
||||
c->item = itNone;
|
||||
c->wall = waSea;
|
||||
if(c->land == laOcean) c->landparam = 40;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool snakepile(cell *c, eMonster m) {
|
||||
if(c->wall == waSea && c->land == laOcean) {
|
||||
c->land = laBrownian, c->landparam = 0;
|
||||
}
|
||||
if(c->land == laWestWall) return false;
|
||||
if(c->land == laBrownian) {
|
||||
if(c->wall == waNone) {
|
||||
#if CAP_COMPLEX2
|
||||
c->landparam += brownian::level;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
if(c->wall == waSea || c->wall == waBoat) {
|
||||
c->wall = waNone;
|
||||
c->landparam++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if(c->item && c->wall != waRed3) c->item = itNone;
|
||||
if(c->wall == waRed1 || c->wall == waOpenGate) c->wall = waRed2;
|
||||
else if(c->wall == waRed2) c->wall = waRed3;
|
||||
else if(doesFall(c)) return false;
|
||||
else if((c->wall == waSea && c->land == laLivefjord))
|
||||
c->wall = waNone;
|
||||
else if((c->wall == waSea && isWarpedType(c->land)))
|
||||
c->wall = waNone;
|
||||
else if(isGravityLand(c->land)) {
|
||||
if(m == moHexSnake)
|
||||
c->wall = waPlatform;
|
||||
else
|
||||
c->wall = waDeadTroll2;
|
||||
}
|
||||
else if(c->wall == waNone || isAlchAny(c) ||
|
||||
c->wall == waCIsland || c->wall == waCIsland2 ||
|
||||
c->wall == waOpenPlate || c->wall == waClosePlate ||
|
||||
c->wall == waMineUnknown || c->wall == waMineOpen || isReptile(c->wall)) {
|
||||
if(isReptile(c->wall)) kills[moReptile]++;
|
||||
c->wall = waRed1;
|
||||
if(among(m, moDarkTroll, moBrownBug)) c->wall = waDeadfloor2;
|
||||
}
|
||||
else if(c->wall == waDeadfloor)
|
||||
c->wall = waDeadfloor2;
|
||||
else if(c->wall == waDeadfloor2) {
|
||||
if(m == moDarkTroll && c->land == laDeadCaves) return false;
|
||||
else
|
||||
c->wall = waDeadwall;
|
||||
}
|
||||
else if(c->wall == waRubble || c->wall == waGargoyleFloor || c->wall == waGargoyleBridge ||
|
||||
c->wall == waTempFloor || c->wall == waTempBridge || c->wall == waPetrifiedBridge) {
|
||||
if(c->land == laWhirlpool) return false;
|
||||
c->wall = waRed2;
|
||||
if(m == moDarkTroll) c->wall = waDeadwall;
|
||||
}
|
||||
else if(c->wall == waCavefloor) c->wall = waCavewall;
|
||||
else if(c->wall == waSea && c->land == laCaribbean) c->wall = waCIsland;
|
||||
else if(c->wall == waSea && c->land == laWhirlpool) return false;
|
||||
else if(c->wall == waSea) c->wall = waNone;
|
||||
else if(isWateryOrBoat(c) || c->wall == waFrozenLake) c->wall = waNone;
|
||||
else if(isWateryOrBoat(c) || c->wall == waFrozenLake) c->wall = waNone;
|
||||
else if(cellHalfvine(c)) {
|
||||
destroyHalfvine(c, waRed1);
|
||||
if(c->wall == waRed1 && m == moDarkTroll) c->wall = waDeadfloor2;
|
||||
}
|
||||
else return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
EX bool makeflame(cell *c, int timeout, bool checkonly) {
|
||||
if(!checkonly) destroyTrapsOn(c);
|
||||
if(itemBurns(c->item)) {
|
||||
if(checkonly) return true;
|
||||
if(c->cpdist <= 7)
|
||||
addMessage(XLAT("%The1 burns!", c->item));
|
||||
c->item = itNone;
|
||||
}
|
||||
if(cellUnstable(c)) {
|
||||
if(checkonly) return true;
|
||||
doesFall(c);
|
||||
}
|
||||
else if(c->wall == waChasm || c->wall == waOpenGate || c->wall == waRed2 || c->wall == waRed3 ||
|
||||
c->wall == waTower)
|
||||
return false;
|
||||
else if(c->wall == waBoat) {
|
||||
if(isPlayerOn(c) && markOrb(itOrbWinter)) {
|
||||
addMessage(XLAT("%the1 protects your boat!", itOrbWinter));
|
||||
}
|
||||
if(checkonly) return true;
|
||||
if(c->cpdist <= 7)
|
||||
addMessage(XLAT("%The1 burns!", winf[c->wall].name));
|
||||
drawFireParticles(c, 24);
|
||||
placeWater(c, c);
|
||||
if(isIcyLand(c)) HEAT(c) += 1;
|
||||
}
|
||||
else if(c->wall == waNone && c->land == laCocytus) {
|
||||
if(checkonly) return true;
|
||||
c->wall = waLake, HEAT(c) += 1;
|
||||
}
|
||||
else if(c->wall == waFireTrap) {
|
||||
if(checkonly) return true;
|
||||
if(c->wparam == 0) c->wparam = 1;
|
||||
}
|
||||
else if(c->wall == waFrozenLake) {
|
||||
if(checkonly) return true;
|
||||
drawParticles(c, MELTCOLOR, 8, 8);
|
||||
c->wall = waLake, HEAT(c) += 1;
|
||||
}
|
||||
else if(c->wall == waIcewall) {
|
||||
if(checkonly) return true;
|
||||
drawParticles(c, MELTCOLOR, 8, 8);
|
||||
c->wall = waNone;
|
||||
}
|
||||
else if(c->wall == waMineMine) {
|
||||
if(checkonly) return true;
|
||||
explodeMine(c);
|
||||
}
|
||||
else if(c->wall != waCTree && c->wall != waBigTree && c->wall != waSmallTree &&
|
||||
c->wall != waVinePlant && !passable(c, NULL, P_MONSTER | P_MIRROR) &&
|
||||
c->wall != waSaloon && c->wall != waRose) return false;
|
||||
// reptiles are able to use the water to put the fire off
|
||||
else if(c->wall == waReptileBridge) return false;
|
||||
else if(c->wall == waDock) {
|
||||
if(checkonly) return true;
|
||||
c->wall = waBurningDock;
|
||||
c->wparam = 3;
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
eWall w = eternalFire(c) ? waEternalFire : waFire;
|
||||
if(!checkonly) drawFireParticles(c, 10);
|
||||
if(w == c->wall) return false;
|
||||
if(checkonly) return true;
|
||||
if(isReptile(c->wall)) kills[moReptile]++;
|
||||
destroyHalfvine(c);
|
||||
if(!isFire(c)) c->wparam = 0;
|
||||
c->wall = w;
|
||||
c->wparam = max(c->wparam, (char) timeout);
|
||||
if(c->land == laBrownian) c->landparam = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
EX void explosion(cell *c, int power, int central) {
|
||||
playSound(c, "explosion");
|
||||
drawFireParticles(c, 30, 150);
|
||||
|
||||
brownian::dissolve_brownian(c, 2);
|
||||
makeflame(c, central, false);
|
||||
|
||||
forCellEx(c2, c) {
|
||||
destroyTrapsOn(c2);
|
||||
brownian::dissolve_brownian(c2, 1);
|
||||
if(c2->wall == waRed2 || c2->wall == waRed3)
|
||||
c2->wall = waRed1;
|
||||
else if(c2->wall == waDeadTroll || c2->wall == waDeadTroll2 || c2->wall == waPetrified || c2->wall == waGargoyle) {
|
||||
c2->wall = waNone;
|
||||
makeflame(c2, power/2, false);
|
||||
}
|
||||
else if(c2->wall == waPetrifiedBridge || c2->wall == waGargoyleBridge) {
|
||||
placeWater(c, c);
|
||||
}
|
||||
else if(c2->wall == waPalace || c2->wall == waOpenGate || c2->wall == waClosedGate ||
|
||||
c2->wall == waSandstone || c2->wall == waMetal || c2->wall == waSaloon || c2->wall == waRuinWall) {
|
||||
c2->wall = waNone;
|
||||
makeflame(c2, power/2, false);
|
||||
}
|
||||
else if(c2->wall == waTower)
|
||||
c2->wall = waRubble;
|
||||
else if(c2->wall == waBarrowWall)
|
||||
c2->wall = waBarrowDig;
|
||||
else if(c2->wall == waBarrowDig)
|
||||
c2->wall = waNone;
|
||||
else if(c2->wall == waFireTrap) {
|
||||
if(c2->wparam == 0)
|
||||
c2->wparam = 1;
|
||||
}
|
||||
else if(c2->wall == waExplosiveBarrel)
|
||||
explodeBarrel(c2);
|
||||
else makeflame(c2, power, false);
|
||||
}
|
||||
}
|
||||
|
||||
EX void explodeMine(cell *c) {
|
||||
if(c->wall != waMineMine)
|
||||
return;
|
||||
|
||||
c->wall = waMineOpen;
|
||||
explosion(c, 20, 20);
|
||||
mine::auto_teleport_charges();
|
||||
}
|
||||
|
||||
EX void explodeBarrel(cell *c) {
|
||||
if(c->wall != waExplosiveBarrel)
|
||||
return;
|
||||
|
||||
c->wall = waNone;
|
||||
explosion(c, 20, 20);
|
||||
}
|
||||
|
||||
EX bool mayExplodeMine(cell *c, eMonster who) {
|
||||
if(c->wall != waMineMine) return false;
|
||||
if(ignoresPlates(who)) return false;
|
||||
explodeMine(c);
|
||||
return true;
|
||||
}
|
||||
|
||||
EX void flameHalfvine(cell *c, int val) {
|
||||
if(itemBurns(c->item)) {
|
||||
addMessage(XLAT("%The1 burns!", c->item));
|
||||
c->item = itNone;
|
||||
}
|
||||
c->wall = waPartialFire;
|
||||
c->wparam = val;
|
||||
}
|
||||
|
||||
EX bool destroyHalfvine(cell *c, eWall newwall IS(waNone), int tval IS(6)) {
|
||||
if(cellHalfvine(c)) {
|
||||
for(int t=0; t<c->type; t++) if(c->move(t)->wall == c->wall) {
|
||||
if(newwall == waPartialFire) flameHalfvine(c->move(t), tval);
|
||||
else if(newwall == waRed1) c->move(t)->wall = waVinePlant;
|
||||
else c->move(t)->wall = newwall;
|
||||
}
|
||||
if(newwall == waPartialFire) flameHalfvine(c, tval);
|
||||
else c->wall = newwall;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EX int coastvalEdge(cell *c) { return coastval(c, laIvoryTower); }
|
||||
|
||||
EX int gravityLevel(cell *c) {
|
||||
if(c->land == laIvoryTower || c->land == laEndorian)
|
||||
return coastval(c, laIvoryTower);
|
||||
if(c->land == laDungeon)
|
||||
return -coastval(c, laIvoryTower);
|
||||
if(c->land == laMountain)
|
||||
return 1-celldistAlt(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EX int gravityLevelDiff(cell *c, cell *d) {
|
||||
if(c->land != laWestWall || d->land != laWestWall)
|
||||
return gravityLevel(c) - gravityLevel(d);
|
||||
|
||||
if(shmup::on) return 0;
|
||||
|
||||
int nid = neighborId(c, d);
|
||||
int id1 = parent_id(c, 1, coastvalEdge) + 1;
|
||||
int di1 = angledist(c->type, id1, nid);
|
||||
|
||||
int id2 = parent_id(c, -1, coastvalEdge) - 1;
|
||||
int di2 = angledist(c->type, id2, nid);
|
||||
|
||||
if(di1 < di2) return 1;
|
||||
if(di1 > di2) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
EX bool canUnstable(eWall w, flagtype flags) {
|
||||
return w == waNone || w == waCanopy || w == waOpenPlate || w == waClosePlate ||
|
||||
w == waOpenGate || ((flags & MF_STUNNED) && (w == waLadder || w == waTrunk || w == waSolidBranch || w == waWeakBranch
|
||||
|| w == waBigBush || w == waSmallBush));
|
||||
}
|
||||
|
||||
EX bool cellEdgeUnstable(cell *c, flagtype flags IS(0)) {
|
||||
if(!isGravityLand(c->land) || !canUnstable(c->wall, flags)) return false;
|
||||
if(shmup::on && c->land == laWestWall) return false;
|
||||
forCellEx(c2, c) {
|
||||
if(isAnyIvy(c2->monst) &&
|
||||
c->land == laMountain && !(flags & MF_IVY)) return false;
|
||||
int d = gravityLevelDiff(c, c2);
|
||||
if(d == gravity_zone_diff(c)) {
|
||||
if(againstWind(c2, c)) return false;
|
||||
if(!passable(c2, NULL, P_MONSTER | P_DEADLY))
|
||||
return false;
|
||||
if(isWorm(c2))
|
||||
return false;
|
||||
}
|
||||
if(WDIM == 3) {
|
||||
if(d == 0 && !passable(c2, NULL, P_MONSTER | P_DEADLY)) return false;
|
||||
if(d == -1 && !passable(c2, NULL, P_MONSTER | P_DEADLY)) forCellEx(c3, c2) if(c3 != c && gravityLevelDiff(c3, c) == 0) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int tidalphase;
|
||||
|
||||
EX int tidalsize, tide[200];
|
||||
|
||||
EX void calcTidalPhase() {
|
||||
if(!tidalsize) {
|
||||
for(int i=0; i<5; i++) tide[tidalsize++] = 1;
|
||||
|
||||
for(int i=1; i<4; i++) for(int j=0; j<3; j++)
|
||||
tide[tidalsize++] = i;
|
||||
|
||||
for(int i=4; i<7; i++) for(int j=0; j<2; j++)
|
||||
tide[tidalsize++] = i;
|
||||
|
||||
for(int i=7; i<20; i++)
|
||||
tide[tidalsize++] = i;
|
||||
|
||||
for(int i=20; i<23; i++) for(int j=0; j<2; j++)
|
||||
tide[tidalsize++] = i;
|
||||
|
||||
for(int i=23; i<26; i++) for(int j=0; j<3; j++)
|
||||
tide[tidalsize++] = i;
|
||||
|
||||
for(int i=0; i+i<tidalsize; i++) tide[tidalsize++] = 27 - tide[i];
|
||||
|
||||
/* printf("tidalsize = %d\n", tidalsize);
|
||||
for(int i=0; i<tidalsize; i++) printf("%d ", tide[i]);
|
||||
printf("\n"); */
|
||||
}
|
||||
tidalphase = tide[
|
||||
(shmup::on ? shmup::curtime/600 : turncount)
|
||||
% tidalsize];
|
||||
if(peace::on)
|
||||
tidalphase = 5 + tidalphase / 6;
|
||||
}
|
||||
|
||||
EX int tidespeed() {
|
||||
return tide[(turncount+6) % tidalsize] - tidalphase;
|
||||
}
|
||||
|
||||
EX bool recalcTide;
|
||||
|
||||
#if HDR
|
||||
#define SEADIST LHU.bytes[0]
|
||||
#define LANDDIST LHU.bytes[1]
|
||||
#define CHAOSPARAM LHU.bytes[2]
|
||||
#endif
|
||||
|
||||
#if CAP_FIELD
|
||||
EX int lavatide(cell *c, int t) {
|
||||
int tc = (shmup::on ? shmup::curtime/400 : turncount);
|
||||
return (windmap::at(c) + (tc+t)*4) & 255;
|
||||
}
|
||||
#endif
|
||||
|
||||
EX void checkTide(cell *c) {
|
||||
if(c->land == laOcean) {
|
||||
int t = c->landparam;
|
||||
|
||||
if(chaosmode) {
|
||||
char& csd(c->SEADIST); if(csd == 0) csd = 7;
|
||||
char& cld(c->LANDDIST); if(cld == 0) cld = 7;
|
||||
int seadist=csd, landdist=cld;
|
||||
for(int i=0; i<c->type; i++) {
|
||||
cell *c2 = c->move(i);
|
||||
if(!c2) continue;
|
||||
if(c2->land == laBarrier || c2->land == laOceanWall) ;
|
||||
else if(c2->land == laOcean)
|
||||
seadist = min(seadist, c2->SEADIST ? c2->SEADIST+1 : 7),
|
||||
landdist = min(landdist, c2->LANDDIST ? c2->LANDDIST+1 : 7);
|
||||
else if(isSealand(c2->land)) seadist = 1;
|
||||
else landdist = 1;
|
||||
}
|
||||
if(seadist < csd) csd = seadist, recalcTide = true;
|
||||
if(landdist < cld) cld = landdist, recalcTide = true;
|
||||
if(seadist == 1 && landdist == 1) t = 15;
|
||||
else t = c->CHAOSPARAM = 1 + (29 * (landdist-1)) / (seadist+landdist-2);
|
||||
}
|
||||
|
||||
if(c->wall == waStrandedBoat || c->wall == waBoat)
|
||||
c->wall = t >= tidalphase ? waBoat : waStrandedBoat;
|
||||
if(c->wall == waSea || c->wall == waNone)
|
||||
c->wall = t >= tidalphase ? waSea : waNone;
|
||||
if(isFire(c) && t >= tidalphase)
|
||||
c->wall = waSea;
|
||||
}
|
||||
#if CAP_FIELD
|
||||
if(c->land == laVolcano) {
|
||||
int id = lavatide(c, 0);
|
||||
if(id < 96) {
|
||||
if(c->wall == waNone || isWateryOrBoat(c) || c->wall == waVinePlant || isAlch(c)) {
|
||||
if(isWateryOrBoat(c) || isAlch(c))
|
||||
playSound(c, "steamhiss");
|
||||
c->wall = waMagma;
|
||||
if(itemBurns(c->item)) {
|
||||
addMessage(XLAT("%The1 burns!", c->item)), c->item = itNone;
|
||||
playSound(c, "steamhiss", 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(c->wall == waMagma) c->wall = waNone;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
EX bool makeEmpty(cell *c) {
|
||||
|
||||
if(c->monst != moPrincess) {
|
||||
if(isAnyIvy(c->monst)) killMonster(c, moPlayer, 0);
|
||||
else if(c->monst == moPair) {
|
||||
if(c->move(c->mondir)->monst == moPair)
|
||||
c->move(c->mondir)->monst = moNone;
|
||||
}
|
||||
else if(isWorm(c->monst)) {
|
||||
if(!items[itOrbDomination]) return false;
|
||||
}
|
||||
else c->monst = moNone;
|
||||
}
|
||||
|
||||
if(c->land == laCocytus)
|
||||
c->wall = waFrozenLake;
|
||||
else if(c->land == laAlchemist || c->land == laCanvas)
|
||||
;
|
||||
else if(c->land == laDual)
|
||||
;
|
||||
else if(c->land == laCaves || c->land == laEmerald)
|
||||
c->wall = waCavefloor;
|
||||
else if(c->land == laDeadCaves)
|
||||
c->wall = waDeadfloor2;
|
||||
else if(c->land == laCaribbean || c->land == laOcean || c->land == laWhirlpool || c->land == laLivefjord || c->land == laWarpSea || c->land == laKraken || c->wall == waBoat)
|
||||
c->wall = waBoat; // , c->item = itOrbYendor;
|
||||
else if(c->land == laMinefield)
|
||||
c->wall = waMineOpen;
|
||||
else if(c->wall == waFan && bounded)
|
||||
;
|
||||
else if(c->wall == waOpenPlate && bounded)
|
||||
;
|
||||
else if(c->wall == waTrunk || c->wall == waSolidBranch || c->wall == waWeakBranch)
|
||||
;
|
||||
else if(c->wall == waGiantRug)
|
||||
;
|
||||
else if(c->wall == waInvisibleFloor)
|
||||
;
|
||||
else if(c->wall == waDock)
|
||||
;
|
||||
else if(c->wall == waLadder)
|
||||
;
|
||||
else if(c->land == laDocks)
|
||||
c->wall = waBoat;
|
||||
else if(c->wall == waFreshGrave && bounded)
|
||||
;
|
||||
else if(c->wall == waBarrier && sphere && WDIM == 3)
|
||||
;
|
||||
else if(isReptile(c->wall))
|
||||
c->wparam = reptilemax();
|
||||
else if(c->wall == waAncientGrave && bounded)
|
||||
;
|
||||
else
|
||||
c->wall = waNone;
|
||||
|
||||
if(c->land == laBrownian && c->wall == waNone && c->landparam == 0)
|
||||
c->landparam = 1;
|
||||
|
||||
if(c->item != itCompass)
|
||||
c->item = itNone;
|
||||
|
||||
if(c->land == laWildWest) {
|
||||
forCellEx(c2, c)
|
||||
forCellEx(c3, c2)
|
||||
if(c3->wall != waBarrier)
|
||||
c3->wall = waNone;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int numgates = 0;
|
||||
|
||||
EX void toggleGates(cell *c, eWall type, int rad) {
|
||||
if(!c) return;
|
||||
celllister cl(c, rad, 1000000, NULL);
|
||||
for(cell *ct: cl.lst) {
|
||||
if(ct->wall == waOpenGate && type == waClosePlate) {
|
||||
bool onWorm = false;
|
||||
if(isWorm(ct)) onWorm = true;
|
||||
for(int i=0; i<ct->type; i++)
|
||||
if(ct->move(i) && ct->move(i)->wall == waOpenGate && isWorm(ct->move(i))) onWorm = true;
|
||||
if(!onWorm) {
|
||||
ct->wall = waClosedGate, numgates++;
|
||||
if(ct->item) {
|
||||
playSound(ct, "hit-crush"+pick123());
|
||||
addMessage(XLAT("%The1 is crushed!", ct->item));
|
||||
ct->item = itNone;
|
||||
}
|
||||
toggleGates(ct, type, 1);
|
||||
}
|
||||
}
|
||||
if(ct->wall == waClosedGate && type == waOpenPlate) {
|
||||
ct->wall = waOpenGate, numgates++;
|
||||
toggleGates(ct, type, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EX void toggleGates(cell *ct, eWall type) {
|
||||
playSound(ct, "click");
|
||||
numgates = 0;
|
||||
if(type == waClosePlate && PURE)
|
||||
toggleGates(ct, type, 2);
|
||||
else
|
||||
toggleGates(ct, type, (GOLDBERG && !sphere && !a4) ? gp::dist_3() : 3);
|
||||
if(numgates && type == waClosePlate)
|
||||
playSound(ct, "closegate");
|
||||
if(numgates && type == waOpenPlate)
|
||||
playSound(ct, "opengate");
|
||||
}
|
||||
|
||||
EX void destroyTrapsOn(cell *c) {
|
||||
if(c->wall == waArrowTrap) c->wall = waNone, destroyTrapsAround(c);
|
||||
}
|
||||
|
||||
EX void destroyTrapsAround(cell *c) {
|
||||
forCellEx(c2, c) destroyTrapsOn(c2);
|
||||
}
|
||||
|
||||
EX void destroyWeakBranch(cell *cf, cell *ct, eMonster who) {
|
||||
if(cf && ct && cf->wall == waWeakBranch && cellEdgeUnstable(ct) &&
|
||||
gravityLevelDiff(ct, cf) >= 0 && !ignoresPlates(who)) {
|
||||
cf->wall = waCanopy;
|
||||
if(!cellEdgeUnstable(cf)) { cf->wall = waWeakBranch; return; }
|
||||
playSound(cf, "trapdoor");
|
||||
drawParticles(cf, winf[waWeakBranch].color, 4);
|
||||
}
|
||||
if(cf && ct && cf->wall == waSmallBush && cellEdgeUnstable(ct) &&
|
||||
gravityLevelDiff(ct, cf) >= 0 && !ignoresPlates(who)) {
|
||||
cf->wall = waNone;
|
||||
if(!cellEdgeUnstable(cf)) { cf->wall = waSmallBush; return; }
|
||||
playSound(cf, "trapdoor");
|
||||
drawParticles(cf, winf[waWeakBranch].color, 4);
|
||||
}
|
||||
}
|
||||
|
||||
EX bool isCentralTrap(cell *c) {
|
||||
if(c->wall != waArrowTrap) return false;
|
||||
int i = 0;
|
||||
forCellEx(c2, c) if(c2->wall == waArrowTrap) i++;
|
||||
return i == 2;
|
||||
}
|
||||
|
||||
EX array<cell*, 5> traplimits(cell *c) {
|
||||
array<cell*, 5> res;
|
||||
int q = 0;
|
||||
res[2] = c;
|
||||
for(int d=0; d<c->type; d++) {
|
||||
cellwalker cw(c, d);
|
||||
cw += wstep;
|
||||
if(cw.at->wall != waArrowTrap) continue;
|
||||
res[1+q*2] = cw.at;
|
||||
cw += (cw.at->type/2);
|
||||
if((cw.at->type&1) && (cw+wstep).at->wall != waStone) cw += 1;
|
||||
cw += wstep;
|
||||
res[(q++)*4] = cw.at;
|
||||
}
|
||||
while(q<2) { res[q*4] = res[1+q*2] = NULL; q++; }
|
||||
return res;
|
||||
}
|
||||
|
||||
EX void activateArrowTrap(cell *c) {
|
||||
if(c->wall == waArrowTrap && c->wparam == 0) {
|
||||
playSound(c, "click");
|
||||
c->wparam = shmup::on ? 2 : 1;
|
||||
forCellEx(c2, c) activateArrowTrap(c2);
|
||||
if(shmup::on) shmup::activateArrow(c);
|
||||
}
|
||||
}
|
||||
|
||||
#if HDR
|
||||
template<class T>
|
||||
movei determinePush(cellwalker who, int subdir, const T& valid) {
|
||||
if(subdir != 1 && subdir != -1) {
|
||||
subdir = 1;
|
||||
static bool first = true;
|
||||
if(first)
|
||||
first = false,
|
||||
addMessage("bad push: " + its(subdir));
|
||||
}
|
||||
cellwalker push = who;
|
||||
push += wstep;
|
||||
cell *c2 = push.at;
|
||||
if(binarytiling) {
|
||||
auto rd = reverse_directions(push.at, push.spin);
|
||||
for(int i: rd) {
|
||||
push.spin = i;
|
||||
if(valid(push.cpeek())) return movei(push.at, push.spin);
|
||||
}
|
||||
return movei(c2, NO_SPACE);
|
||||
}
|
||||
int pd = push.at->type/2;
|
||||
push += pd * -subdir;
|
||||
push += wstep;
|
||||
if(valid(push.at)) return movei(c2, (push+wstep).spin);
|
||||
if(c2->type&1) {
|
||||
push = push + wstep - subdir + wstep;
|
||||
if(valid(push.at)) return movei(c2, (push+wstep).spin);
|
||||
}
|
||||
if(gravityLevelDiff(push.at, c2) < 0) {
|
||||
push = push + wstep + 1 + wstep;
|
||||
if(gravityLevelDiff(push.at, c2) < 0) {
|
||||
push = push + wstep - 2 + wstep;
|
||||
}
|
||||
if(gravityLevelDiff(push.at, c2) < 0) {
|
||||
push = push + wstep + 1 + wstep;
|
||||
}
|
||||
if(valid(push.at)) return movei(c2, (push+wstep).spin);
|
||||
}
|
||||
return movei(c2, NO_SPACE);
|
||||
}
|
||||
#endif
|
||||
|
||||
// for sandworms
|
||||
EX void explodeAround(cell *c) {
|
||||
for(int j=0; j<c->type; j++) {
|
||||
cell* c2 = c->move(j);
|
||||
if(c2) {
|
||||
if(isIcyLand(c2)) HEAT(c2) += 0.5;
|
||||
eWall ow = c2->wall;
|
||||
if((c2->wall == waDune || c2->wall == waIcewall ||
|
||||
c2->wall == waAncientGrave || c2->wall == waFreshGrave ||
|
||||
c2->wall == waColumn || c2->wall == waThumperOff || c2->wall == waThumperOn ||
|
||||
(isFire(c2) && !eternalFire(c2)) ||
|
||||
c2->wall == waBigTree || c2->wall == waSmallTree ||
|
||||
c2->wall == waVinePlant || c2->wall == waVineHalfA || c2->wall == waVineHalfB)) {
|
||||
destroyHalfvine(c2);
|
||||
c2->wall = waNone;
|
||||
}
|
||||
if(c2->wall == waExplosiveBarrel) explodeBarrel(c2);
|
||||
if(c2->wall == waCavewall || c2->wall == waDeadTroll) c2->wall = waCavefloor;
|
||||
if(c2->wall == waDeadTroll2) c2->wall = waNone;
|
||||
if(c2->wall == waPetrified) c2->wall = waNone;
|
||||
if(c2->wall == waDeadfloor2) c2->wall = waDeadfloor;
|
||||
if(c2->wall == waGargoyleFloor) c2->wall = waChasm;
|
||||
if(c2->wall == waGargoyleBridge || c2->wall == waPetrifiedBridge) placeWater(c2, c2);
|
||||
if(c2->wall == waRubble) c2->wall = waNone;
|
||||
if(c2->wall == waPlatform) c2->wall = waNone;
|
||||
if(c2->wall == waStone) c2->wall = waNone, destroyTrapsAround(c2);
|
||||
if(c2->wall == waRose) c2->wall = waNone;
|
||||
if(c2->wall == waRuinWall) c2->wall = waNone;
|
||||
if(c2->wall == waLadder) c2->wall = waNone;
|
||||
if(c2->wall == waGargoyle) c2->wall = waNone;
|
||||
if(c2->wall == waSandstone) c2->wall = waNone;
|
||||
if(c2->wall == waSaloon) c2->wall = waNone;
|
||||
if(c2->wall == waDeadwall) c2->wall = waDeadfloor2;
|
||||
if(c2->wall == waBigStatue) c2->wall = waNone;
|
||||
if(c2->wall == waPalace || c2->wall == waOpenGate || c2->wall == waClosedGate)
|
||||
c2->wall = waNone;
|
||||
if(isAlch(c2) && isAlch(c))
|
||||
c2->wall = c->wall;
|
||||
if(c2->wall != ow && ow) drawParticles(c2, winf[ow].color, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EX bool earthMove(const movei& mi) {
|
||||
auto& from = mi.s;
|
||||
bool b = false;
|
||||
cell *c2 = mi.t;
|
||||
b |= earthWall(from);
|
||||
if(!mi.proper()) return b;
|
||||
int d = mi.rev_dir_or(0);
|
||||
if(c2) for(int u=2; u<=c2->type-2; u++) {
|
||||
cell *c3 = c2->modmove(d + u);
|
||||
if(c3) b |= earthFloor(c3);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
EX bool cellDangerous(cell *c) {
|
||||
return cellUnstableOrChasm(c) || isFire(c) || c->wall == waClosedGate;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -8,6 +8,8 @@
|
||||
#include "hyper.h"
|
||||
namespace hr {
|
||||
|
||||
EX int avengers, mirrorspirits, wandering_jiangshi, jiangshi_on_screen;
|
||||
|
||||
EX bool timerghost = true;
|
||||
EX bool gen_wandering = true;
|
||||
|
||||
@ -312,7 +314,7 @@ EX void wandering() {
|
||||
kills[moBomberbird] = 0;
|
||||
kills[moTameBomberbird] = 0;
|
||||
for(cell *c: currentmap->allcells()) if(c->wall == waMineUnknown) kills[moBomberbird]++;
|
||||
for(cell *c: currentmap->allcells()) if(among(c->wall, waMineMine, waMineUnknown) && mineMarked(c)) kills[moTameBomberbird]++;
|
||||
for(cell *c: currentmap->allcells()) if(among(c->wall, waMineMine, waMineUnknown) && mine::marked_mine(c)) kills[moTameBomberbird]++;
|
||||
return;
|
||||
}
|
||||
if(!canmove) return;
|
||||
@ -440,7 +442,7 @@ EX void wandering() {
|
||||
else if(c->land == laVariant && wchance(items[itVarTreasure], 50)) {
|
||||
int i = hrand(21);
|
||||
if(getBits(c) & (1>>i)) {
|
||||
eMonster m = variant_features[i].wanderer;
|
||||
eMonster m = variant::features[i].wanderer;
|
||||
if(m) c->monst = m, c->hitpoints = 3;
|
||||
}
|
||||
continue;
|
||||
|
2083
monstermove.cpp
Normal file
2083
monstermove.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -413,6 +413,10 @@ EX bool buildPrizeMirror(cell *c, int freq) {
|
||||
return mirror::build(c);
|
||||
}
|
||||
|
||||
#if HDR
|
||||
extern cellwalker cwt;
|
||||
#endif
|
||||
|
||||
EX eLand getPrizeLand(cell *c IS(cwt.at)) {
|
||||
eLand l = c->land;
|
||||
if(isElemental(l)) l = laElementalWall;
|
||||
|
6
orbs.cpp
6
orbs.cpp
@ -8,6 +8,8 @@
|
||||
#include "hyper.h"
|
||||
namespace hr {
|
||||
|
||||
EX bool orbused[ittypes], lastorbused[ittypes];
|
||||
|
||||
EX bool markOrb(eItem it) {
|
||||
if(!items[it]) return false;
|
||||
orbused[it] = true;
|
||||
@ -85,7 +87,7 @@ EX bool reduceOrbPower(eItem it, int cap) {
|
||||
return true;
|
||||
}
|
||||
if(items[it] > cap && timerghost) items[it] = cap;
|
||||
auto_teleport_charges();
|
||||
mine::auto_teleport_charges();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -579,7 +581,7 @@ void teleportTo(cell *dest) {
|
||||
checkmoveO();
|
||||
|
||||
movecost(from, dest, 2);
|
||||
auto_teleport_charges();
|
||||
mine::auto_teleport_charges();
|
||||
}
|
||||
|
||||
EX void jumpTo(cell *dest, eItem byWhat, int bonuskill IS(0), eMonster dashmon IS(moNone)) {
|
||||
|
647
passable.cpp
Normal file
647
passable.cpp
Normal file
@ -0,0 +1,647 @@
|
||||
// Hyperbolic Rogue - passability
|
||||
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
|
||||
|
||||
/** \file checkmove.cpp
|
||||
* \brief check whether monster/PC can move in the given direction
|
||||
*/
|
||||
|
||||
#include "hyper.h"
|
||||
|
||||
namespace hr {
|
||||
|
||||
// === MOVEMENT FUNCTIONS ===
|
||||
|
||||
// w = from->move(d)
|
||||
EX bool againstCurrent(cell *w, cell *from) {
|
||||
if(from->land != laWhirlpool) return false;
|
||||
if(againstWind(from, w)) return false; // wind is stronger than current
|
||||
if(!eubinary && (!from->master->alt || !w->master->alt)) return false;
|
||||
int dfrom = celldistAlt(from);
|
||||
int dw = celldistAlt(w);
|
||||
if(dw < dfrom) return false;
|
||||
if(dfrom < dw) return true;
|
||||
for(int d=0; d<from->type; d++)
|
||||
if(from->move(d) == w) {
|
||||
cell *c3 = from->modmove(d-1);
|
||||
if(!c3) return false;
|
||||
return celldistAlt(c3) < dfrom;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool boatGoesThrough(cell *c) {
|
||||
if(isGravityLand(c->land)) return false;
|
||||
return
|
||||
(c->wall == waNone && c->land != laMotion && c->land != laZebra && c->land != laReptile) ||
|
||||
isAlchAny(c) ||
|
||||
c->wall == waCavefloor || c->wall == waFrozenLake || isReptile(c->wall) ||
|
||||
c->wall == waDeadfloor || c->wall == waCIsland || c->wall == waCIsland2 ||
|
||||
c->wall == waMineUnknown || c->wall == waMineMine || c->wall == waMineOpen ||
|
||||
c->wall == waBonfireOff || c->wall == waFire || c->wall == waPartialFire ||
|
||||
c->wall == waArrowTrap;
|
||||
}
|
||||
|
||||
EX void placeWater(cell *c, cell *c2) {
|
||||
destroyTrapsOn(c);
|
||||
if(isWatery(c)) ;
|
||||
else if(c2 && isAlchAny(c2))
|
||||
c->wall = c2->wall;
|
||||
else if(isIcyLand(c))
|
||||
c->wall = waLake;
|
||||
else
|
||||
c->wall = waSea;
|
||||
// destroy the ancient treasure!
|
||||
if(c->item == itBarrow) c->item = itNone;
|
||||
}
|
||||
|
||||
EX int incline(cell *cfrom, cell *cto) {
|
||||
return snakelevel(cto) - snakelevel(cfrom);
|
||||
}
|
||||
|
||||
#define F(x) checkflags(flags,x)
|
||||
|
||||
EX bool checkflags(flagtype flags, flagtype x) {
|
||||
if(flags & x) return true;
|
||||
if(flags & P_ISPLAYER) {
|
||||
if((x & P_WINTER) && markOrb(itOrbWinter)) return true;
|
||||
if((x & P_IGNORE37) && markOrb(itOrb37)) return true;
|
||||
if((x & P_FISH) && markOrb(itOrbFish)) return true;
|
||||
if((x & P_MARKWATER) && markOrb(itOrbWater)) return true;
|
||||
if((x & P_AETHER) && markOrb2(itOrbAether) && !(flags&P_NOAETHER)) return true;
|
||||
}
|
||||
if(flags & P_ISFRIEND) if(items[itOrbEmpathy])
|
||||
if(checkflags(flags ^ P_ISPLAYER ^ P_ISFRIEND, x) && markOrb(itOrbEmpathy))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool strictlyAgainstGravity(cell *w, cell *from, bool revdir, flagtype flags) {
|
||||
return
|
||||
cellEdgeUnstable(w, flags) && cellEdgeUnstable(from, flags) &&
|
||||
!(shmup::on && from == w) && gravityLevelDiff(from, w) != (revdir?-1:1) * gravity_zone_diff(from);
|
||||
}
|
||||
|
||||
EX bool anti_alchemy(cell *w, cell *from) {
|
||||
bool alch1 = w->wall == waFloorA && from && from->wall == waFloorB && !w->item && !from->item;
|
||||
alch1 |= w->wall == waFloorB && from && from->wall == waFloorA && !w->item && !from->item;
|
||||
return alch1;
|
||||
}
|
||||
|
||||
#if HDR
|
||||
#define P_MONSTER Flag(0) // can move through monsters
|
||||
#define P_MIRROR Flag(1) // can move through mirrors
|
||||
#define P_REVDIR Flag(2) // reverse direction movement
|
||||
#define P_WIND Flag(3) // can move against the wind
|
||||
#define P_GRAVITY Flag(4) // can move against the gravity
|
||||
#define P_ISPLAYER Flag(5) // player-only moves (like the Round Table jump)
|
||||
#define P_ONPLAYER Flag(6) // always can step on the player
|
||||
#define P_FLYING Flag(7) // is flying
|
||||
#define P_BULLET Flag(8) // bullet can fly through more things
|
||||
#define P_MIRRORWALL Flag(9) // mirror images go through mirror walls
|
||||
#define P_JUMP1 Flag(10) // first part of a jump
|
||||
#define P_JUMP2 Flag(11) // second part of a jump
|
||||
#define P_TELE Flag(12) // teleport onto
|
||||
#define P_BLOW Flag(13) // Orb of Air -- blow, or push
|
||||
#define P_AETHER Flag(14) // aethereal
|
||||
#define P_FISH Flag(15) // swimming
|
||||
#define P_WINTER Flag(16) // fire resistant
|
||||
#define P_USEBOAT Flag(17) // can use boat
|
||||
#define P_NOAETHER Flag(18) // disable AETHER
|
||||
#define P_FRIENDSWAP Flag(19) // can move on friends (to swap with tem)
|
||||
#define P_ISFRIEND Flag(20) // is a friend (can use Empathy + Winter/Aether/Fish combo)
|
||||
#define P_LEADER Flag(21) // can push statues and use boats
|
||||
#define P_MARKWATER Flag(22) // mark Orb of Water as used
|
||||
#define P_EARTHELEM Flag(23) // Earth Elemental
|
||||
#define P_WATERELEM Flag(24) // Water Elemental
|
||||
#define P_IGNORE37 Flag(25) // ignore the triheptagonal board
|
||||
#define P_CHAIN Flag(26) // for chaining moves with boats
|
||||
#define P_DEADLY Flag(27) // suicide moves allowed
|
||||
#define P_ROSE Flag(28) // rose smell
|
||||
#define P_CLIMBUP Flag(29) // allow climbing up
|
||||
#define P_CLIMBDOWN Flag(30) // allow climbing down
|
||||
#define P_REPTILE Flag(31) // is reptile
|
||||
#define P_VOID Flag(32) // void beast
|
||||
#define P_PHASE Flag(33) // phasing movement
|
||||
#define P_PULLMAGNET Flag(34) // pull the other part of the magnet
|
||||
#endif
|
||||
|
||||
EX bool passable(cell *w, cell *from, flagtype flags) {
|
||||
bool revdir = (flags&P_REVDIR);
|
||||
bool vrevdir = revdir ^ bool(flags&P_VOID);
|
||||
|
||||
if(from && from != w && nonAdjacent(from, w) && !F(P_IGNORE37 | P_BULLET)) return false;
|
||||
|
||||
for(int i=0; i<numplayers(); i++) {
|
||||
cell *pp = playerpos(i);
|
||||
if(!pp) continue;
|
||||
if(w == pp && F(P_ONPLAYER)) return true;
|
||||
if(from == pp && F(P_ONPLAYER) && F(P_REVDIR)) return true;
|
||||
|
||||
if(from && !((flags & P_ISPLAYER) && pp->monst)) {
|
||||
int i = vrevdir ? incline(w, from) : incline(from, w);
|
||||
if(in_gravity_zone(w)) {
|
||||
if(gravity_state == gsLevitation) i = 0;
|
||||
if(gravity_state == gsAnti && i > 1) i = 1;
|
||||
}
|
||||
if(i < -1 && F(P_ROSE)) return false;
|
||||
if((i > 1) && !F(P_JUMP1 | P_JUMP2 | P_BULLET | P_FLYING | P_BLOW | P_CLIMBUP | P_AETHER | P_REPTILE))
|
||||
return false;
|
||||
if((i < -2) && !F(P_DEADLY | P_JUMP1 | P_JUMP2 | P_BULLET | P_FLYING | P_BLOW | P_CLIMBDOWN | P_AETHER | P_REPTILE))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(F(P_ROSE)) {
|
||||
if(airdist(w) < 3) return false;
|
||||
if(againstWind(w,from)) return false;
|
||||
}
|
||||
|
||||
if(from && strictlyAgainstGravity(w, from, vrevdir, flags)
|
||||
&& !((flags & P_ISPLAYER) && shmup::on)
|
||||
&& !F(P_GRAVITY | P_BLOW | P_JUMP1 | P_JUMP2 | P_FLYING | P_BULLET | P_AETHER)
|
||||
) return false;
|
||||
|
||||
if(from && (vrevdir ? againstWind(from,w) : againstWind(w, from)) && !F(P_WIND | P_BLOW | P_JUMP1 | P_JUMP2 | P_BULLET | P_AETHER)) return false;
|
||||
|
||||
if(revdir && from && w->monst && passable(from, w, flags &~ (P_REVDIR|P_MONSTER)))
|
||||
return true;
|
||||
|
||||
if(!shmup::on && sword::at(w, flags & P_ISPLAYER) && !F(P_DEADLY | P_BULLET | P_ROSE))
|
||||
return false;
|
||||
|
||||
bool alch1 = anti_alchemy(w, from);
|
||||
|
||||
if(alch1) {
|
||||
bool alchok = (in_gravity_zone(w) || in_gravity_zone(from));
|
||||
alchok = alchok || (F(P_JUMP1 | P_JUMP2 | P_FLYING | P_TELE | P_BLOW | P_AETHER | P_BULLET)
|
||||
&& !F(P_ROSE));
|
||||
if(!alchok) return false;
|
||||
}
|
||||
|
||||
if(from && thruVine(from, w) && !F(P_AETHER)) return false;
|
||||
|
||||
if(w->monst == moMouse && F(P_JUMP1)) ;
|
||||
else if(w->monst && isFriendly(w) && F(P_FRIENDSWAP)) ;
|
||||
else if(w->monst && !F(P_MONSTER)) return false;
|
||||
|
||||
if(w->wall == waMirror || w->wall == waCloud)
|
||||
return F(P_MIRROR | P_AETHER);
|
||||
|
||||
if(w->wall == waMirrorWall)
|
||||
return F(P_MIRRORWALL);
|
||||
|
||||
if(F(P_BULLET)) {
|
||||
if(isFire(w) || w->wall == waBonfireOff || cellHalfvine(w) ||
|
||||
w->wall == waMagma ||
|
||||
w->wall == waAncientGrave || w->wall == waFreshGrave || w->wall == waRoundTable)
|
||||
return true;
|
||||
}
|
||||
|
||||
if(F(P_LEADER)) {
|
||||
if(from && from->wall == waBoat && isWatery(w) && from->item == itOrbYendor)
|
||||
return false;
|
||||
|
||||
if(from && from->wall == waBoat && isWateryOrBoat(w) && !againstCurrent(w, from))
|
||||
return true;
|
||||
|
||||
if(from && isWatery(from) && w->wall == waBoat && F(P_CHAIN))
|
||||
return true;
|
||||
|
||||
if(from && isWatery(from) && isWatery(w) && F(P_CHAIN) && !againstCurrent(w, from))
|
||||
return true;
|
||||
|
||||
if(w->wall == waBigStatue && from && canPushStatueOn(from)) return true;
|
||||
}
|
||||
|
||||
if(F(P_EARTHELEM)) {
|
||||
// cannot go through Living Caves...
|
||||
if(w->wall == waCavefloor) return false;
|
||||
// but can dig through...
|
||||
if(w->wall == waDeadwall || w->wall == waDune || w->wall == waStone)
|
||||
return true;
|
||||
// and can swim through...
|
||||
if(w->wall == waSea && w->land == laLivefjord)
|
||||
return true;
|
||||
}
|
||||
|
||||
if(F(P_WATERELEM)) {
|
||||
if(isWatery(w) || boatGoesThrough(w) ||
|
||||
w->wall == waBoat ||
|
||||
w->wall == waDeadTroll || w->wall == waDeadTroll2) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(isThorny(w->wall) && F(P_BLOW | P_DEADLY)) return true;
|
||||
|
||||
if(isFire(w) || w->wall == waMagma) {
|
||||
if(w->wall == waMagma && in_gravity_zone(w)) ;
|
||||
else if(!F(P_AETHER | P_WINTER | P_BLOW | P_JUMP1 | P_BULLET | P_DEADLY)) return false;
|
||||
}
|
||||
|
||||
if(in_gravity_zone(w) && gravity_state == gsAnti && !isGravityLand(w->land) && (!from || !isGravityLand(from->land)))
|
||||
if(!F(P_AETHER | P_BLOW | P_JUMP1 | P_BULLET | P_FLYING)) {
|
||||
bool next_to_wall = false;
|
||||
forCellEx(c2, w) if(isJWall(c2)) next_to_wall = true;
|
||||
if(from) forCellEx(c2, from) if(isJWall(c2)) next_to_wall = true;
|
||||
if(!next_to_wall && (!from || incline(from, w) * (vrevdir?-1:1) <= 0)) return false;
|
||||
}
|
||||
|
||||
if(isWatery(w)) {
|
||||
if(in_gravity_zone(w)) ;
|
||||
else if(from && from->wall == waBoat && F(P_USEBOAT) &&
|
||||
(!againstCurrent(w, from) || F(P_MARKWATER)) && !(from->item == itOrbYendor)) ;
|
||||
else if(!F(P_AETHER | P_FISH | P_FLYING | P_BLOW | P_JUMP1 | P_BULLET | P_DEADLY | P_REPTILE)) return false;
|
||||
}
|
||||
if(isChasmy(w)) {
|
||||
if(in_gravity_zone(w)) ;
|
||||
else if(!F(P_AETHER | P_FLYING | P_BLOW | P_JUMP1 | P_BULLET | P_DEADLY | P_REPTILE)) return false;
|
||||
}
|
||||
|
||||
if(w->wall == waRoundTable && from && from->wall != waRoundTable && (flags & P_ISPLAYER)) return true;
|
||||
if(isNoFlight(w) && F(P_FLYING | P_BLOW | P_JUMP1)) return false;
|
||||
|
||||
if(isWall(w)) {
|
||||
// a special case: empathic aethereal beings cannot go through Round Table
|
||||
// (but truly aetheral beings can)
|
||||
if(w->wall == waRoundTable) {
|
||||
if(!(flags & P_AETHER)) return false;
|
||||
}
|
||||
else if(!F(P_AETHER)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
EX vector<pair<cell*, int> > airmap;
|
||||
|
||||
EX int airdist(cell *c) {
|
||||
if(!(havewhat & HF_AIR)) return 3;
|
||||
vector<pair<cell*, int> >::iterator it =
|
||||
lower_bound(airmap.begin(), airmap.end(), make_pair(c,0));
|
||||
if(it != airmap.end() && it->first == c) return it->second;
|
||||
return 3;
|
||||
}
|
||||
|
||||
EX ld calcAirdir(cell *c) {
|
||||
if(!c || c->monst == moAirElemental || !passable(c, NULL, P_BLOW))
|
||||
return 0;
|
||||
for(int i=0; i<c->type; i++) {
|
||||
cell *c2 = c->move(i);
|
||||
if(c2 && c2->monst == moAirElemental) {
|
||||
return c->c.spin(i) * 2 * M_PI / c2->type;
|
||||
}
|
||||
}
|
||||
for(int i=0; i<c->type; i++) {
|
||||
cell *c2 = c->move(i);
|
||||
if(!c2) continue;
|
||||
if(!passable(c2, c, P_BLOW | P_MONSTER)) continue;
|
||||
if(!passable(c, c2, P_BLOW | P_MONSTER)) continue;
|
||||
for(int i=0; i<c2->type; i++) {
|
||||
cell *c3 = c2->move(i);
|
||||
if(c3 && c3->monst == moAirElemental) {
|
||||
return c2->c.spin(i) * 2 * M_PI / c3->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
EX bool againstWind(cell *cto, cell *cfrom) {
|
||||
if(!cfrom || !cto) return false;
|
||||
int dcto = airdist(cto), dcfrom = airdist(cfrom);
|
||||
if(dcto < dcfrom) return true;
|
||||
#if CAP_FIELD
|
||||
if(cfrom->land == laBlizzard && !shmup::on && cto->land == laBlizzard && dcto == 3 && dcfrom == 3) {
|
||||
char vfrom = windmap::at(cfrom);
|
||||
char vto = windmap::at(cto);
|
||||
int z = (vfrom-vto) & 255;
|
||||
if(z >= windmap::NOWINDBELOW && z < windmap::NOWINDFROM)
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
whirlwind::calcdirs(cfrom);
|
||||
int d = neighborId(cfrom, cto);
|
||||
if(whirlwind::winddir(d) == -1) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool ghostmove(eMonster m, cell* to, cell* from) {
|
||||
if(!isGhost(m) && nonAdjacent(to, from)) return false;
|
||||
if(sword::at(to, 0)) return false;
|
||||
if(!shmup::on && isPlayerOn(to)) return false;
|
||||
if(to->monst && !(to->monst == moTentacletail && isGhost(m) && m != moFriendlyGhost)
|
||||
&& !(to->monst == moTortoise && isGhost(m) && m != moFriendlyGhost))
|
||||
return false;
|
||||
if((m == moWitchGhost || m == moWitchWinter) && to->land != laPower)
|
||||
return false;
|
||||
if(isGhost(m))
|
||||
for(int i=0; i<to->type; i++) if(to->move(i)) {
|
||||
if(inmirror(to->move(i))) return false;
|
||||
if(to->move(i) && to->move(i) != from && isGhost(to->move(i)->monst) &&
|
||||
(to->move(i)->monst == moFriendlyGhost) == (m== moFriendlyGhost))
|
||||
return false;
|
||||
}
|
||||
if(isGhost(m) || m == moWitchGhost) return true;
|
||||
if(m == moGreaterShark) return isWatery(to);
|
||||
if(m == moWitchWinter)
|
||||
return passable(to, from, P_WINTER | P_ONPLAYER);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool slimepassable(cell *w, cell *c) {
|
||||
if(w == c || !c) return true;
|
||||
int u = neighborId(c, w);
|
||||
if(nonAdjacent(w,c)) return false;
|
||||
if(isPlayerOn(w)) return true;
|
||||
int group = slimegroup(c);
|
||||
if(!group) return false;
|
||||
int ogroup = slimegroup(w);
|
||||
if(!ogroup) return false;
|
||||
bool hv = (group == ogroup);
|
||||
|
||||
if(sword::at(w, 0)) return false;
|
||||
|
||||
if(w->item) return false;
|
||||
|
||||
// only travel to halfvines correctly
|
||||
if(cellHalfvine(c)) {
|
||||
int i=0;
|
||||
for(int t=0; t<c->type; t++) if(c->move(t) && c->move(t)->wall == c->wall) i=t;
|
||||
int z = i-u; if(z<0) z=-z; z%=6;
|
||||
if(z>1) return false;
|
||||
hv=(group == ogroup);
|
||||
}
|
||||
// only travel from halfvines correctly
|
||||
if(cellHalfvine(w)) {
|
||||
int i=0;
|
||||
for(int t=0; t<w->type; t++) if(w->move(t) && w->move(t)->wall == w->wall) i=t;
|
||||
int z = i-c->c.spin(u); if(z<0) z=-z; z%=6;
|
||||
if(z>1) return false;
|
||||
hv=(group == ogroup);
|
||||
}
|
||||
if(!hv) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sharkpassable(cell *w, cell *c) {
|
||||
if(w == c || !c) return true;
|
||||
if(nonAdjacent(w,c)) return false;
|
||||
if(isPlayerOn(w)) return true;
|
||||
if(!isWatery(w)) return false;
|
||||
if(sword::at(w, 0)) return false;
|
||||
|
||||
// don't go against the current
|
||||
if(isWateryOrBoat(w) && isWateryOrBoat(c))
|
||||
return !againstCurrent(w, c);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EX bool canPushStatueOn(cell *c) {
|
||||
return passable(c, NULL, P_MONSTER) && !snakelevel(c) &&
|
||||
!isWorm(c->monst) && !isReptile(c->wall) && !peace::on &&
|
||||
!among(c->wall, waBoat, waFireTrap, waArrowTrap);
|
||||
}
|
||||
|
||||
EX void moveBoat(const movei& mi) {
|
||||
eWall x = mi.t->wall; mi.t->wall = mi.s->wall; mi.s->wall = x;
|
||||
mi.t->mondir = mi.rev_dir_or(NODIR);
|
||||
moveItem(mi.s, mi.t, false);
|
||||
animateMovement(mi, LAYER_BOAT);
|
||||
}
|
||||
|
||||
EX void moveBoatIfUsingOne(const movei& mi) {
|
||||
if(mi.s->wall == waBoat && isWatery(mi.t)) moveBoat(mi);
|
||||
else if(mi.s->wall == waBoat && boatGoesThrough(mi.t) && isFriendly(mi.t) && markEmpathy(itOrbWater)) {
|
||||
placeWater(mi.t, mi.s);
|
||||
moveBoat(mi);
|
||||
}
|
||||
}
|
||||
|
||||
bool againstMagnet(cell *c1, cell *c2, eMonster m) { // (from, to)
|
||||
if(false) forCellEx(c3, c2) {
|
||||
if(c3 == c1) continue;
|
||||
if(c3->monst == m)
|
||||
return true;
|
||||
/* if(c3->monst == otherpole(m) && c3->move(c3->mondir) != c1) {
|
||||
int i = 0;
|
||||
forCellEx(c4, c3) if(c4->monst == m) i++;
|
||||
if(i == 2) return true;
|
||||
} */
|
||||
}
|
||||
if(c1->monst == m && !isNeighbor(c2, c1->move(c1->mondir)))
|
||||
return true;
|
||||
forCellEx(c3, c1)
|
||||
if(c3->monst != m && isMagneticPole(c3->monst))
|
||||
if(!isNeighbor(c3, c2))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool againstPair(cell *c1, cell *c2, eMonster m) { // (from, to)
|
||||
if(c1->monst == m && !isNeighbor(c2, c1->move(c1->mondir)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
EX bool notNearItem(cell *c) {
|
||||
forCellCM(c2, c) if(c2->item) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
EX bool passable_for(eMonster m, cell *w, cell *from, flagtype extra) {
|
||||
if(w->monst && !(extra & P_MONSTER) && !isPlayerOn(w))
|
||||
return false;
|
||||
if(m == moWolf) {
|
||||
return (isIcyLand(w) || w->land == laVolcano) && (isPlayerOn(w) || passable(w, from, extra));
|
||||
}
|
||||
if(isMagneticPole(m))
|
||||
return !(w && from && againstMagnet(from, w, m)) && passable(w, from, extra);
|
||||
if(m == moPair)
|
||||
return !(w && from && againstPair(from, w, m)) && passable(w, from, extra);
|
||||
if(m == passive_switch) return false;
|
||||
if(minf[m].mgroup == moYeti || isBug(m) || isDemon(m) || m == moHerdBull || m == moMimic || m == moAsteroid) {
|
||||
if((isWitch(m) || m == moEvilGolem) && w->land != laPower && w->land != laHalloween)
|
||||
return false;
|
||||
return passable(w, from, extra);
|
||||
}
|
||||
if(m == moDragonHead && prairie::isriver(w))
|
||||
return false;
|
||||
if(isShark(m))
|
||||
return sharkpassable(w, from);
|
||||
if(isSlimeMover(m))
|
||||
return slimepassable(w, from);
|
||||
if(m == moKrakenH) {
|
||||
if(extra & P_ONPLAYER) {
|
||||
if(isPlayerOn(w)) return true;
|
||||
}
|
||||
if((extra & P_ONPLAYER) && isPlayerOn(w))
|
||||
return true;
|
||||
if(kraken_pseudohept(w) || kraken_pseudohept(from)) return false;
|
||||
if(w->wall != waBoat && !slimepassable(w, from)) return false;
|
||||
forCellEx(w2, w) if(w2->wall != waBoat && !passable(w2, w, P_FISH | P_MONSTER)) return false;
|
||||
return true;
|
||||
}
|
||||
if(m == moEarthElemental)
|
||||
return passable(w, from, extra | P_EARTHELEM);
|
||||
if(m == moWaterElemental)
|
||||
return passable(w, from, extra | P_WATERELEM);
|
||||
if(m == moGreaterShark)
|
||||
return isWatery(w) || w->wall == waBoat || w->wall == waFrozenLake;
|
||||
if(isGhostMover(m) || m == moFriendlyGhost)
|
||||
return ghostmove(m, w, from);
|
||||
// for the purpose of Shmup this is correct
|
||||
if(m == moTameBomberbird)
|
||||
return passable(w, from, extra | P_FLYING | P_ISFRIEND);
|
||||
if(m == moHexSnake)
|
||||
return !pseudohept(w) && passable(w, from, extra|P_WIND|P_FISH);
|
||||
if(isBird(m)) {
|
||||
if(bird_disruption(w) && (!from || bird_disruption(from)) && markOrb(itOrbGravity))
|
||||
return passable(w, from, extra);
|
||||
else
|
||||
return passable(w, from, extra | P_FLYING);
|
||||
}
|
||||
if(m == moReptile)
|
||||
return passable(w, from, extra | P_REPTILE);
|
||||
if(isDragon(m))
|
||||
return passable(w, from, extra | P_FLYING | P_WINTER);
|
||||
if(m == moAirElemental)
|
||||
return passable(w, from, extra | P_FLYING | P_WIND);
|
||||
if(isLeader(m)) {
|
||||
if(from && from->wall == waBoat && from->item == itCoral && !from->monst) return false; // don't move Corals!
|
||||
return passable(w, from, extra | P_LEADER);
|
||||
}
|
||||
if(isPrincess(m))
|
||||
return passable(w, from, extra | P_ISFRIEND | P_USEBOAT);
|
||||
if(isGolemOrKnight(m))
|
||||
return passable(w, from, extra | P_ISFRIEND);
|
||||
if(isWorm(m))
|
||||
return passable(w, from, extra) && !cellUnstable(w) && ((m != moWorm && m != moTentacle) || !cellEdgeUnstable(w));
|
||||
if(m == moVoidBeast)
|
||||
return passable(w, from, extra | P_VOID);
|
||||
if(m == moHexDemon) {
|
||||
if(extra & P_ONPLAYER) {
|
||||
if(isPlayerOn(w)) return true;
|
||||
}
|
||||
return !pseudohept(w) && passable(w, from, extra);
|
||||
}
|
||||
if(m == moAltDemon) {
|
||||
if(extra & P_ONPLAYER) {
|
||||
if(isPlayerOn(w)) return true;
|
||||
}
|
||||
return (!w || !from || w==from || pseudohept(w) || pseudohept(from)) && passable(w, from, extra);
|
||||
}
|
||||
if(m == moMonk) {
|
||||
if(extra & P_ONPLAYER) {
|
||||
if(isPlayerOn(w)) return true;
|
||||
}
|
||||
return notNearItem(w) && passable(w, from, extra);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
EX eMonster movegroup(eMonster m) { return minf[m].mgroup; }
|
||||
|
||||
EX bool logical_adjacent(cell *c1, eMonster m1, cell *c2) {
|
||||
if(!c1 || !c2) return true; // cannot really check
|
||||
eMonster m2 = c2->monst;
|
||||
if(!isNeighbor(c1, c2))
|
||||
return false;
|
||||
if(thruVine(c1, c2) && !attackThruVine(m1) && !attackThruVine(m2) &&
|
||||
!checkOrb(m1, itOrbAether) && !checkOrb(m2, itOrbAether))
|
||||
return false;
|
||||
if(nonAdjacent(c1, c2) && !attackNonAdjacent(m1) && !attackNonAdjacent(m2) &&
|
||||
!checkOrb(m1, itOrb37) && !checkOrb(m1, itOrbAether) && !checkOrb(m2, itOrbAether))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
EX void buildAirmap() {
|
||||
for(int k=0; k<isize(airmap); k++) {
|
||||
int d = airmap[k].second;
|
||||
if(d == 2) break;
|
||||
cell *c = airmap[k].first;
|
||||
for(int i=0; i<c->type; i++) {
|
||||
cell *c2 = c->move(i);
|
||||
if(!c2) continue;
|
||||
if(!passable(c2, c, P_BLOW | P_MONSTER)) continue;
|
||||
if(!passable(c, c2, P_BLOW | P_MONSTER)) continue;
|
||||
airmap.push_back(make_pair(c2, d+1));
|
||||
}
|
||||
}
|
||||
sort(airmap.begin(), airmap.end());
|
||||
}
|
||||
|
||||
EX int rosewave, rosephase;
|
||||
|
||||
/** current state of the rose scent
|
||||
* rosemap[c] &3 can be:
|
||||
* 0 - wave not reached
|
||||
* 1 - wave expanding
|
||||
* 2 - wave phase 1
|
||||
* 3 - wave phase 2
|
||||
*/
|
||||
EX map<cell*, int> rosemap;
|
||||
|
||||
EX int rosedist(cell *c) {
|
||||
if(!(havewhat&HF_ROSE)) return 0;
|
||||
int&r (rosemap[c]);
|
||||
if((r&7) == 7) return 0;
|
||||
if(r&3) return (r&3)-1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
EX bool againstRose(cell *cfrom, cell *cto) {
|
||||
if(rosedist(cfrom) != 1) return false;
|
||||
if(cto && rosedist(cto) == 2) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
EX bool withRose(cell *cfrom, cell *cto) {
|
||||
if(rosedist(cfrom) != 1) return false;
|
||||
if(rosedist(cto) != 2) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
EX void buildRosemap() {
|
||||
|
||||
rosephase++; rosephase &= 7;
|
||||
|
||||
if((havewhat&HF_ROSE) && !rosephase) {
|
||||
rosewave++;
|
||||
for(int k=0; k<isize(dcal); k++) {
|
||||
cell *c = dcal[k];
|
||||
if(c->wall == waRose && c->cpdist <= gamerange() - 2)
|
||||
rosemap[c] = rosewave * 8 + 2;
|
||||
}
|
||||
}
|
||||
|
||||
for(map<cell*, int>::iterator it = rosemap.begin(); it != rosemap.end(); it++) {
|
||||
cell *c = it->first;
|
||||
int r = it->second;
|
||||
if(r < (rosewave) * 8) continue;
|
||||
if((r&7) == 2) if(c->wall == waRose || !isWall(c)) for(int i=0; i<c->type; i++) {
|
||||
cell *c2 = c->move(i);
|
||||
if(!c2) continue;
|
||||
// if(snakelevel(c2) <= snakelevel(c) - 2) continue;
|
||||
if(!passable(c2, c, P_BLOW | P_MONSTER | P_ROSE)) continue;
|
||||
int& r2 = rosemap[c2];
|
||||
if(r2 < r) r2 = r-1;
|
||||
}
|
||||
}
|
||||
|
||||
for(map<cell*, int>::iterator it = rosemap.begin(); it != rosemap.end(); it++) {
|
||||
int& r = it->second;
|
||||
if((r&7) == 1 || (r&7) == 2 || (r&7) == 3) r++;
|
||||
if(airdist(it->first) < 3 || whirlwind::cat(it->first)) r |= 7;
|
||||
if(it->first->land == laBlizzard) r |= 7;
|
||||
forCellEx(c2, it->first) if(airdist(c2) < 3) r |= 7;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EX bool scentResistant() {
|
||||
return markOrb(itOrbBeauty) || markOrb(itOrbAether) || markOrb(itOrbShield);
|
||||
}
|
||||
|
||||
|
||||
}
|
1267
pcmove.cpp
Normal file
1267
pcmove.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -1155,7 +1155,7 @@ void movePlayer(monster *m, int delta) {
|
||||
cwt.at = c2; afterplayermoved();
|
||||
if(c2->item && c2->land == laAlchemist) c2->wall = m->base->wall;
|
||||
if(m->base->wall == waRoundTable)
|
||||
roundTableMessage(c2);
|
||||
camelot::roundTableMessage(c2);
|
||||
if(c2->wall == waCloud || c2->wall == waMirror) {
|
||||
visibleFor(500);
|
||||
cellwalker cw(c2, 0, false);
|
||||
@ -1176,7 +1176,7 @@ void movePlayer(monster *m, int delta) {
|
||||
items[itOrbLife] = 0;
|
||||
m->dead = true;
|
||||
}
|
||||
uncoverMinesFull(c2);
|
||||
mine::uncover_full(c2);
|
||||
|
||||
if(isWatery(c2) && isWatery(m->base) && m->inBoat)
|
||||
moveItem(m->base, c2, true);
|
||||
@ -2622,7 +2622,7 @@ EX void turn(int delta) {
|
||||
#if CAP_INV
|
||||
if(inv::on) inv::compute();
|
||||
#endif
|
||||
terracotta();
|
||||
terracotta::check();
|
||||
heat::processfires();
|
||||
if(havewhat&HF_WHIRLPOOL) whirlpool::move();
|
||||
if(havewhat&HF_WHIRLWIND) whirlwind::move();
|
||||
|
2
sky.cpp
2
sky.cpp
@ -182,7 +182,7 @@ void celldrawer::draw_ceiling() {
|
||||
col = 0x404040;
|
||||
for(int a=0; a<21; a++)
|
||||
if((b >> a) & 1)
|
||||
col += variant_features[a].color_change;
|
||||
col += variant::features[a].color_change;
|
||||
col = col & 0x00FF00;
|
||||
break;
|
||||
}
|
||||
|
10
system.cpp
10
system.cpp
@ -203,7 +203,7 @@ EX void initgame() {
|
||||
}
|
||||
|
||||
if((tactic::on || yendor::on || peace::on) && isCyclic(firstland)) {
|
||||
anthraxBonus = items[itHolyGrail];
|
||||
camelot::anthraxBonus = items[itHolyGrail];
|
||||
cwt.at->move(0)->land = firstland;
|
||||
if(firstland == laWhirlpool) cwt.at->move(0)->wall = waSea;
|
||||
|
||||
@ -358,7 +358,7 @@ EX void initgame() {
|
||||
#if CAP_INV
|
||||
if(inv::on) inv::init();
|
||||
#endif
|
||||
auto_teleport_charges();
|
||||
mine::auto_teleport_charges();
|
||||
if(!use_special_land) {
|
||||
if(firstland != (princess::challenge ? laPalace : laIce)) cheater++;
|
||||
}
|
||||
@ -1125,10 +1125,10 @@ EX void loadsave() {
|
||||
// printf("boxid = %d\n", boxid);
|
||||
if(items[itHolyGrail]) {
|
||||
items[itHolyGrail]--;
|
||||
knighted = newRoundTableRadius();
|
||||
camelot::knighted = newRoundTableRadius();
|
||||
items[itHolyGrail]++;
|
||||
}
|
||||
else knighted = 0;
|
||||
else camelot::knighted = 0;
|
||||
safety = true;
|
||||
if(items[itSavedPrincess] < 0) items[itSavedPrincess] = 0;
|
||||
addMessage(XLAT("Game loaded."));
|
||||
@ -1169,7 +1169,7 @@ EX void stop_game() {
|
||||
princess::reviveAt = 0;
|
||||
princess::forceVizier = false;
|
||||
princess::forceMouse = false;
|
||||
knighted = 0;
|
||||
camelot::knighted = 0;
|
||||
// items[itGreenStone] = 100;
|
||||
clearMemory();
|
||||
game_active = false;
|
||||
|
Loading…
Reference in New Issue
Block a user