improved the checkmove.cpp, also works for golems now

This commit is contained in:
Zeno Rogue 2020-11-05 14:24:01 +01:00
parent 2e236be695
commit 63f895974e
9 changed files with 161 additions and 198 deletions

View File

@ -1183,19 +1183,19 @@ EX void killThePlayerAt(eMonster m, cell *c, flagtype flags) {
}
#if HDR
template<class T> void do_swords(cell *mf, cell *mt, eMonster who, const T& f) {
template<class T> void do_swords(movei mi, eMonster who, const T& f) {
for(int bb=0; bb<2; bb++) if(who == moPlayer && sword::orbcount(bb)) {
cell *sf = sword::pos(mf, sword::dir[multi::cpid], bb);
cell *st = sword::pos(mt, sword::shift(mf, mt, sword::dir[multi::cpid]), bb);
cell *sf = sword::pos(mi.s, sword::dir[multi::cpid], bb);
cell *st = sword::pos(mi.t, sword::shift(mi, sword::dir[multi::cpid]), bb);
f(st, bb);
if(sf != st && !isNeighbor(sf,st)) {
// also attack the in-transit cell
if(S3 == 3) {
forCellEx(sb, sf) if(isNeighbor(sb, st) && sb != mf && sb != mt) f(sb, bb);
forCellEx(sb, sf) if(isNeighbor(sb, st) && sb != mi.s && sb != mi.t) f(sb, bb);
}
else {
forCellEx(sb, mf) if(isNeighbor(sb, st) && sb != mt) f(sb, bb);
forCellEx(sb, mt) if(isNeighbor(sb, sf) && sb != mf) f(sb, bb);
forCellEx(sb, mi.s) if(isNeighbor(sb, st) && sb != mi.t) f(sb, bb);
forCellEx(sb, mi.t) if(isNeighbor(sb, sf) && sb != mi.s) f(sb, bb);
}
}
}
@ -1204,14 +1204,16 @@ template<class T> void do_swords(cell *mf, cell *mt, eMonster who, const T& f) {
int lastdouble = -3;
EX void stabbingAttack(cell *mf, cell *mt, eMonster who, int bonuskill IS(0)) {
EX void stabbingAttack(movei mi, eMonster who, int bonuskill IS(0)) {
int numsh = 0, numflail = 0, numlance = 0, numslash = 0, numbb[2];
numbb[0] = numbb[1] = 0;
int backdir = neighborId(mt, mf);
cell *mf = mi.s;
cell *mt = mi.t;
int backdir = mi.rev_dir();
do_swords(mf, mt, who, [&] (cell *c, int bb) { if(swordAttack(mt, who, c, bb)) numbb[bb]++, numslash++; });
do_swords(mi, who, [&] (cell *c, int bb) { if(swordAttack(mt, who, c, bb)) numbb[bb]++, numslash++; });
for(int bb=0; bb<2; bb++) achievement_count("SLASH", numbb[bb], 0);

View File

@ -39,28 +39,47 @@ EX bool hasSafeOrb(cell *c) {
}
#if HDR
struct stalemate1 {
eMonster who;
cell *moveto;
cell *pushto;
cell *comefrom;
struct player_move_info {
movei mi;
cell *swordlast[2], *swordtransit[2], *swordnext[2];
stalemate1(eMonster w, cell *mt, cell *pt, cell *cf) : who(w), moveto(mt), pushto(pt), comefrom(cf) {}
player_move_info(movei mi);
};
#endif
EX vector<player_move_info> pmi;
EX vector<cell*> pushes;
player_move_info::player_move_info(movei _mi) : mi(_mi) {
for(int b=0; b<2; b++) swordlast[b] = sword::pos(multi::cpid, b);
dynamicval<sword::sworddir> x7(sword::dir[multi::cpid], sword::shift(mi, sword::dir[multi::cpid]));
for(int b=0; b<2; b++) {
swordnext[b] = sword::pos(multi::cpid, b);
swordtransit[b] = NULL;
if(swordnext[b] && swordnext[b] != swordlast[b] && !isNeighbor(swordlast[b], swordnext[b])) {
forCellEx(c2, swordnext[b])
if(c2 != mi.t && c2 != mi.s && isNeighbor(c2, S3==3 ? swordlast[b] : mi.t))
swordtransit[b] = c2;
if(S3 == 4)
forCellEx(c2, mi.s)
if(c2 != mi.s && isNeighbor(c2, swordlast[b]))
swordtransit[b] = c2;
}
}
}
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) {
EX bool monstersnear(cell *c, eMonster who) {
cell *c = sm.moveto;
bool eaten = false;
if(hardcore && sm.who == moPlayer) return false;
if(hardcore && who == moPlayer) return false;
int res = 0;
bool fast = false;
@ -76,9 +95,9 @@ EX bool monstersnear(stalemate1& sm) {
who_kills_me = moCrusher; res++;
}
if(sm.who == moPlayer || items[itOrbEmpathy]) {
if(who == moPlayer || items[itOrbEmpathy]) {
fast = (items[itOrbSpeed] && (items[itOrbSpeed] & 1));
if(sm.who == moPlayer && sm.moveto->item == itOrbSpeed && !items[itOrbSpeed]) fast = true;
if(who == moPlayer && c->item == itOrbSpeed && !items[itOrbSpeed]) fast = true;
}
if(havewhat&HF_OUTLAW) {
@ -103,7 +122,7 @@ EX bool monstersnear(stalemate1& sm) {
if(!logical_adjacent(c3, c3->monst, c2) || !logical_adjacent(c2, c3->monst, c) || (c3->monst == moWitchSpeed && c2->land != laPower))
continue;
if(elec::affected(c3)) continue;
if(c3->stuntime > (sm.who == moPlayer ? 0 : 1)) continue;
if(c3->stuntime > (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;
@ -120,19 +139,19 @@ EX bool monstersnear(stalemate1& sm) {
// consider normal monsters
if(c2 &&
isArmedEnemy(c2, sm.who) &&
(c2->monst != moLancer || isUnarmed(sm.who) || !logical_adjacent(c, sm.who, c2))) {
isArmedEnemy(c2, who) &&
(c2->monst != moLancer || isUnarmed(who) || !logical_adjacent(c, 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(c2->monst == moKrakenT && c->wall == waBoat) {
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(!canAttack(c2, c2->monst, c, who, AF_NEXTTURN)) continue;
if(c2->monst == moWorm || c2->monst == moTentacle || c2->monst == moHexSnake) {
if(passable_for(c2->monst, c, c2, 0))
eaten = true;
@ -142,18 +161,16 @@ EX bool monstersnear(stalemate1& sm) {
}
}
if(sm.who == moPlayer && res && (markOrb2(itOrbShield) || markOrb2(itOrbShell)) && !eaten)
if(who == moPlayer && res && (markOrb2(itOrbShield) || markOrb2(itOrbShell)) && !eaten)
res = 0;
if(sm.who == moPlayer && res && markOrb2(itOrbDomination) && c->monst)
if(who == moPlayer && res && markOrb2(itOrbDomination) && c->monst)
res = 0;
return !!res;
}
EX bool monstersnear2();
EX bool monstersnear2() {
EX bool monstersnear_aux() {
changes.value_set(passive_switch, (gold() & 1) ? moSwitch1 : moSwitch2);
multi::cpid++;
bool b = false;
@ -162,22 +179,33 @@ EX bool monstersnear2() {
if(multi::cpid == multi::players || multi::players == 1 || multi::checkonly) {
if(shmup::delayed_safety) return false;
dynamicval<eMonster> sw(passive_switch, passive_switch);
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])) {
for(int i=0; i<isize(pmi); i++)
for(int j=0; j<isize(pmi); j++) if(i != j) {
if(swordConflict(pmi[i], pmi[j])) {
b = true;
who_kills_me = moEnergySword;
}
if(multi::player[i].at == multi::player[j].at)
if(pmi[i].mi.t == pmi[j].mi.t)
{ b = true; who_kills_me = moFireball; }
if(celldistance(multi::player[i].at, multi::player[j].at) > 8)
if(celldistance(pmi[i].mi.t, pmi[j].mi.t) > 8)
{ b = true; who_kills_me = moAirball; }
}
for(int i=0; !b && i<isize(stalemate::moves); i++)
b = monstersnear(stalemate::moves[i]);
for(auto& pushto: pushes)
for(auto& mi: pmi)
if(pushto == mi.mi.t) {
b = true; who_kills_me = moTongue;
}
for(int i=0; i<isize(pushes); i++)
for(int j=0; j<i; j++)
if(pushes[i] == pushes[j]) {
b = true; who_kills_me = moCrushball;
}
for(int i=0; !b && i<isize(pmi); i++)
b = monstersnear(pmi[i].mi.t, moPlayer);
}
else b = !multimove();
multi::cpid--;
@ -185,94 +213,17 @@ EX bool monstersnear2() {
return b;
}
EX bool monstersnear(cell *c, eMonster who, cell *pushto, cell *comefrom) {
if(peace::on) return 0; // you are safe
stalemate1 sm(who, c, 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();
/** like monstersnear but add the potential moves of other players into account */
EX bool monstersnear_add_pmi(player_move_info pmi0) {
pmi.push_back(pmi0);
bool b = monstersnear_aux();
pmi.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 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();
if(!multi::playerActive(multi::cpid)) return !monstersnear_aux();
cellwalker bcwt = cwt;
cwt = multi::player[multi::cpid];
bool b = movepcto(multi::whereto[multi::cpid]);
@ -293,11 +244,11 @@ EX namespace multi {
EX bool aftermove;
EX }
EX bool swordConflict(const stalemate1& sm1, const stalemate1& sm2) {
EX bool swordConflict(const player_move_info& sm1, const player_move_info& 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])
if(sm1.mi.s == sm2.swordlast[b] || sm1.mi.s == sm2.swordtransit[b] || sm1.mi.s == sm2.swordnext[b])
if(sm1.mi.t == sm2.swordlast[b] || sm1.mi.t == sm2.swordtransit[b] || sm1.mi.t == sm2.swordnext[b])
return true;
return false;
}

View File

@ -2867,10 +2867,13 @@ EX namespace sword {
}
// from c1 to c2
EX sworddir shift(cell *c1, cell *c2, sworddir d) {
if(!c1 || !c2) return d;
int s1 = neighborId(c1, c2);
int s2 = neighborId(c2, c1);
EX sworddir shift(movei mi, sworddir d) {
cell *c1 = mi.s;
cell *c2 = mi.t;
if(!mi.proper()) return d;
int s1 = mi.d;
int s2 = mi.rev_dir();
neighborId(c2, c1);
if(s1 < 0 || s2 < 0) return d;
if(SWORDDIM == 2) {
int sub = (hybri) ? 2 : 0;

View File

@ -182,7 +182,7 @@ EX bool activateRecall() {
killFriendlyIvy();
movecost(cwt.at, recallCell.at, 3);
playerMoveEffects(cwt.at, recallCell.at);
playerMoveEffects(movei(cwt.at, recallCell.at, TELEPORT));
mirror::destroyAll();
sword::reset();

View File

@ -313,6 +313,8 @@ EX void dropGreenStone(cell *c) {
return;
}
if(items[itGreenStone] && c->item == itNone) {
changes.ccell(c);
changes.value_keep(items[itGreenStone]);
items[itGreenStone]--;
if(false) {
c->item = itNone;

View File

@ -459,6 +459,7 @@ constexpr int FALL = 98;
constexpr int NO_SPACE = 97;
constexpr int TELEPORT = 96;
constexpr int JUMP = 95;
constexpr int STAY = 94;
namespace whirlwind { cell *jumpDestination(cell*); }

View File

@ -108,6 +108,8 @@ EX void moveMonster(const movei& mi) {
auto& cf = mi.s;
auto& ct = mi.t;
eMonster m = cf->monst;
changes.ccell(cf);
changes.ccell(ct);
bool fri = isFriendly(cf);
if(isDragon(m)) {
printf("called for Dragon\n");
@ -117,6 +119,7 @@ EX void moveMonster(const movei& mi) {
// the following line is necessary because otherwise plates disappear only inside the sight range
if(cellUnstable(cf) && !ignoresPlates(m)) {
fallingFloorAnimation(cf);
changes.ccell(cf);
cf->wall = waChasm;
}
moveEffect(mi, m);
@ -124,6 +127,7 @@ EX void moveMonster(const movei& mi) {
(m == moShark || m == moCShark || m == moGreaterShark))
achievement_gain_once("MOATSHARK");
if(m == moTentacleGhost) {
changes.ccell(cf);
cf->monst = moTentacletail;
m = moGhost;
}
@ -156,10 +160,11 @@ EX void moveMonster(const movei& mi) {
}
}
if(fri || isBug(m) || items[itOrbDiscord]) stabbingAttack(cf, ct, m);
if(fri || isBug(m) || items[itOrbDiscord]) stabbingAttack(mi, m);
if(mi.d == JUMP && m == moVaulter) {
cell *cm = common_neighbor(cf, ct);
changes.ccell(cm);
if(cm->wall == waShrub) cm->wall = waNone;
if(cm->wall == waSmallTree) cm->wall = waNone;
if(cm->wall == waBigTree) cm->wall = waSmallTree;
@ -1518,7 +1523,10 @@ EX int stayvalue(eMonster m, cell *c) {
}
/** for an ally m at c, evaluate moving to c2 */
EX int movevalue(eMonster m, cell *c, cell *c2, flagtype flags) {
EX int movevalue(eMonster m, cell *c, int dir, flagtype flags) {
auto mi = movei(c, dir);
auto& c2 = mi.t;
int val = 0;
if(isPlayerOn(c2)) val = -3000;
@ -1537,7 +1545,6 @@ EX int movevalue(eMonster m, cell *c, cell *c2, flagtype flags) {
isInactiveEnemy(c2,m) ? 1000 :
-500;
else if(monstersnear(c2, m, NULL, c)) val = 50; // linked with mouse suicide!
else if(passable_for(m, c2, c, 0)) {
#if CAP_COMPLEX2
if(mine::marked_mine(c2) && !ignoresPlates(m))
@ -1545,6 +1552,12 @@ EX int movevalue(eMonster m, cell *c, cell *c2, flagtype flags) {
else
#endif
val = 4000;
changes.init(true);
moveMonster(mi);
bool b = monstersnear(mi.t, m);
changes.rollback();
if(b) val = 50;
}
else if(passable_for(m, c2, c, P_DEADLY)) val = -1100;
else val = -1750;
@ -1636,8 +1649,7 @@ EX void movegolems(flagtype flags) {
DEBB(DF_TURN, ("moveval"));
for(int k=0; k<c->type; k++) if(c->move(k)) {
cell *c2 = c->move(k);
int val = movevalue(m, c, c2, flags);
int val = movevalue(m, c, k, flags);
if(val > bestv) bestv = val, bdirs.clear();
if(val == bestv) bdirs.push_back(k);
@ -1646,7 +1658,7 @@ EX void movegolems(flagtype flags) {
if(m == moTameBomberbird) {
cell *c2 = whirlwind::jumpDestination(c);
if(c2 && !c2->monst) {
int val = movevalue(m, c, c2, flags);
int val = movevalue(m, c, STRONGWIND, flags);
// printf("val = %d bestv = %d\n",
if(val > bestv) bestv = val, bdirs.clear();
if(val == bestv) bdirs.push_back(STRONGWIND);

View File

@ -567,8 +567,8 @@ EX void teleportTo(cell *dest) {
if(b) {
killFriendlyIvy();
drainOrb(itOrbTeleport);
movecost(cwt.at, dest, 3);
playerMoveEffects(cwt.at, dest);
movecost(cwt.at, dest, 3);
playerMoveEffects(movei(cwt.at, dest, TELEPORT));
afterplayermoved();
bfs();
}
@ -580,7 +580,7 @@ EX void teleportTo(cell *dest) {
killFriendlyIvy();
cell *from = cwt.at;
movecost(from, dest, 1);
playerMoveEffects(cwt.at, dest);
playerMoveEffects(movei(cwt.at, dest, TELEPORT));
current_display->which_copy = unshift(ggmatrix(dest));
cwt.at = dest; cwt.spin = hrand(dest->type); flipplayer = !!(hrand(2));
drainOrb(itOrbTeleport);
@ -638,8 +638,9 @@ EX bool jumpTo(orbAction a, cell *dest, eItem byWhat, int bonuskill IS(0), eMons
}
sword::reset();
stabbingAttack(c1, dest, moPlayer, bonuskill);
playerMoveEffects(c1, dest);
auto mi = movei(c1, dest, JUMP);
stabbingAttack(mi, moPlayer, bonuskill);
playerMoveEffects(mi);
if(itemclass(byWhat) == IC_ORB)
apply_impact(dest);
@ -658,7 +659,7 @@ EX bool jumpTo(orbAction a, cell *dest, eItem byWhat, int bonuskill IS(0), eMons
mirror::destroyAll();
if(monstersnearO(a, dest, moPlayer, NULL, c1)) {
if(monstersnearO(a, dest)) {
changes.rollback();
return false;
}
@ -939,7 +940,7 @@ bool gun_attack(orbAction a, cell *dest) {
attackMonster(dest, AF_GUN, moNone);
apply_impact(dest);
if(monstersnearO(a, cwt.at, moPlayer, NULL, cwt.at)) {
if(monstersnearO(a, cwt.at)) {
changes.rollback();
return false;
}
@ -1068,13 +1069,13 @@ void useOrbOfDragon(cell *c) {
checkmoveO();
}
EX bool monstersnearO(orbAction a, cell *c, eMonster who, cell *pushto, cell *comefrom) {
EX bool monstersnearO(orbAction a, cell *c) {
// printf("[a = %d] ", a);
if(shmup::on) return false;
if(a == roCheck && multi::players > 1)
return true;
else if(a == roMultiCheck) return false;
else return monstersnear(c, who, pushto, comefrom);
else return monstersnear(c, moPlayer);
}
EX bool isCheck(orbAction a) { return a == roCheck || a == roMultiCheck; }
@ -1241,12 +1242,12 @@ EX eItem targetRangedOrb(cell *c, orbAction a) {
if(c->monst) {
if(!canAttack(cf, moFriendlyIvy, c, c->monst, 0)) continue;
if(monstersnear(cwt.at, moPlayer, NULL, cwt.at)) continue;
if(monstersnear(cwt.at, moPlayer)) continue;
}
else {
if(!passable(c, cf, P_ISPLAYER | P_MONSTER)) continue;
if(strictlyAgainstGravity(c, cf, false, MF_IVY)) continue;
if(monstersnear(cwt.at, moPlayer, c, cwt.at)) continue;
if(monstersnear(cwt.at, moPlayer)) continue;
}
dirs.push_back(d);
}

View File

@ -357,8 +357,8 @@ bool pcmove::swing() {
sideAttack(cwt.at, d, moPlayer, 0);
mirror::act(origd, mirror::SPINMULTI | mirror::ATTACK);
if(monstersnear(cwt.at, moPlayer, nullptr, cwt.at)) {
if(monstersnear_add_pmi(movei(cwt.at, STAY))) {
if(vmsg())
wouldkill("You would be killed by %the1!");
return false;
@ -510,6 +510,11 @@ struct changes_t {
void at_rollback(reaction_t act) {
if(on) rollbacks.emplace_back(act);
}
void push_push(cell *tgt) {
pushes.push_back(tgt);
rollbacks.push_back([] { pushes.pop_back(); });
}
};
#endif
@ -601,7 +606,7 @@ bool pcmove::actual_move() {
return false;
}
if(items[itOrbDomination] > ORBBASE && isMountable(c2->monst) && !monstersnear2() && fmsMove) {
if(items[itOrbDomination] > ORBBASE && isMountable(c2->monst) && fmsMove) {
if(checkonly) { nextmovetype = lmMove; return true; }
if(!isMountable(cwt.at->monst)) dragon::target = NULL;
movecost(cwt.at, c2, 3);
@ -629,6 +634,7 @@ bool pcmove::actual_move() {
addMessage(XLAT("You push %the1.", c2->wall));
lastmovetype = lmPush; lastmove = cwt.at;
pushThumper(mip);
changes.push_push(mip.t);
return perform_actual_move();
}
@ -646,8 +652,6 @@ bool pcmove::actual_move() {
if(!c2->monst && cwt.at->wall == waBoat && cwt.at->item != itOrbYendor && boatGoesThrough(c2) && markOrb(itOrbWater) && !nonAdjacentPlayer(c2, cwt.at) && fmsMove) {
if(havePushConflict(cwt.at, checkonly)) return false;
if(c2->item && !cwt.at->item) moveItem(c2, cwt.at, false), boatmove = true;
placeWater(c2, cwt.at);
moveBoat(mi);
@ -663,7 +667,6 @@ bool pcmove::actual_move() {
bool pcmove::boat_move() {
cell *& c2 = mi.t;
if(havePushConflict(cwt.at, checkonly)) return false;
if(againstWind(c2, cwt.at)) {
if(vmsg()) addMessage(XLAT(airdist(c2) < 3 ? "The Air Elemental blows you away!" : "You cannot go against the wind!"));
@ -744,8 +747,6 @@ bool pcmove::after_escape() {
return false;
}
if(havePushConflict(cwt.at, checkonly)) return false;
changes.ccell(c2);
changes.ccell(cwt.at);
@ -849,7 +850,7 @@ bool pcmove::move_if_okay() {
if(switchplace_prevent(cwt.at, c2, checkonly))
return false;
if(!checkonly && warningprotection_hit(do_we_stab_a_friend(cwt.at, c2, moPlayer)))
if(!checkonly && warningprotection_hit(do_we_stab_a_friend(mi, moPlayer)))
return false;
nextmovetype = lmMove;
@ -899,7 +900,7 @@ bool pcmove::attack() {
if(!ca) {
if(forcedmovetype == fmAttack) {
if(monstersnear(cwt.at,moPlayer,NULL,cwt.at)) {
if(monstersnear_add_pmi(movei(cwt.at, STAY))) {
if(vmsg()) wouldkill("%The1 would get you!");
return false;
}
@ -922,10 +923,9 @@ bool pcmove::attack() {
else
mip.t = c2;
if(mip.t) changes.ccell(mip.t);
changes.push_push(mip.t);
}
if(havePushConflict(mip.t, checkonly)) return false;
if(!(isWatery(cwt.at) && c2->monst == moWaterElemental) && checkNeedMove(checkonly, true))
return false;
@ -982,7 +982,7 @@ bool pcmove::attack() {
lastmovetype = lmAttack; lastmove = c2;
swordAttackStatic();
if(monstersnear(cwt.at, moPlayer, nullptr, cwt.at)) {
if(monstersnear_add_pmi(movei(cwt.at, STAY))) {
if(vmsg()) wouldkill("You would be killed by %the1!");
return false;
}
@ -1090,18 +1090,18 @@ bool pcmove::perform_move_or_jump() {
lastmovetype = lmMove; lastmove = cwt.at;
apply_chaos();
stabbingAttack(cwt.at, mi.t, moPlayer);
cell *c1 = cwt.at;
stabbingAttack(mi, moPlayer);
changes.value_keep(cwt);
cwt += wstep;
mirror::act(origd, mirror::SPINMULTI | mirror::ATTACK | mirror::GO);
playerMoveEffects(c1, mi.t);
auto pmi = player_move_info(mi);
playerMoveEffects(mi);
if(mi.t->monst == moFriendlyIvy) changes.ccell(mi.t), mi.t->monst = moNone;
if(monstersnear(cwt.at, moPlayer, nullptr, c1)) {
if(monstersnear_add_pmi(pmi)) {
if(vmsg()) wouldkill("%The1 would kill you there!");
return false;
}
@ -1135,19 +1135,21 @@ bool pcmove::stay() {
return false;
swordAttackStatic();
nextmovetype = lmSkip;
if(monstersnear(cwt.at, moPlayer, nullptr, cwt.at)) {
mi = movei(cwt.at, STAY);
if(last_gravity_state && !gravity_state)
playerMoveEffects(mi);
if(d == -2)
dropGreenStone(cwt.at);
if(monstersnear_add_pmi(mi)) {
if(vmsg()) wouldkill("%The1 would get you!");
return false;
}
if(checkonly) return true;
if(changes.on) changes.commit();
if(d == -2)
dropGreenStone(cwt.at);
if(cellUnstable(cwt.at) && !markOrb(itOrbAether))
doesFallSound(cwt.at);
if(last_gravity_state && !gravity_state)
playerMoveEffects(cwt.at, cwt.at);
doesFallSound(cwt.at);
return after_move();
}
@ -1246,12 +1248,14 @@ EX bool playerInPower() {
return false;
}
EX void playerMoveEffects(cell *c1, cell *c2) {
EX void playerMoveEffects(movei mi) {
cell *c1 = mi.s;
cell *c2 = mi.t;
if(peace::on) items[itOrbSword] = c2->land == laBurial ? 100 : 0;
changes.value_keep(sword::dir[multi::cpid]);
sword::dir[multi::cpid] = sword::shift(c1, c2, sword::dir[multi::cpid]);
sword::dir[multi::cpid] = sword::shift(mi, sword::dir[multi::cpid]);
destroyWeakBranch(c1, c2, moPlayer);
@ -1457,20 +1461,20 @@ EX void sideAttack(cell *mf, int dir, eMonster who, int bonuskill) {
}
}
EX eMonster do_we_stab_a_friend(cell *mf, cell *mt, eMonster who) {
EX eMonster do_we_stab_a_friend(movei mi, eMonster who) {
eMonster m = moNone;
do_swords(mf, mt, who, [&] (cell *c, int bb) {
if(!peace::on && canAttack(mt, who, c, c->monst, AF_SWORD) && c->monst && isFriendly(c)) m = c->monst;
do_swords(mi, who, [&] (cell *c, int bb) {
if(!peace::on && canAttack(mi.t, who, c, c->monst, AF_SWORD) && c->monst && isFriendly(c)) m = c->monst;
});
for(int t=0; t<mf->type; t++) {
cell *c = mf->move(t);
for(int t=0; t<mi.s->type; t++) {
cell *c = mi.s->move(t);
if(!c) continue;
bool stabthere = false;
if(logical_adjacent(mt, who, c)) stabthere = true;
if(logical_adjacent(mi.t, who, c)) stabthere = true;
if(stabthere && canAttack(mt,who,c,c->monst,AF_STAB) && isFriendly(c))
if(stabthere && canAttack(mi.t,who,c,c->monst,AF_STAB) && isFriendly(c))
return c->monst;
}
@ -1484,27 +1488,14 @@ EX void wouldkill(const char *msg) {
addMessage(XLAT("Cannot move into the current location of another player!"));
else if(who_kills_me == moAirball)
addMessage(XLAT("Players cannot get that far away!"));
else if(who_kills_me == moTongue)
addMessage(XLAT("Cannot push into another player!"));
else if(who_kills_me == moCrushball)
addMessage(XLAT("Cannot push into the same location!"));
else
addMessage(XLAT(msg, who_kills_me));
}
EX bool havePushConflict(cell *pushto, bool checkonly) {
if(pushto && multi::activePlayers() > 1) {
for(int i=0; i<multi::players; i++) if(i != multi::cpid && multi::playerActive(i))
if(multi::origpos[i] == pushto || multi::origtarget[i] == pushto) {
addMessage(XLAT("Cannot push into another player!"));
return true;
}
for(int i=0; i<isize(stalemate::moves); i++) {
if(pushto == stalemate::moves[i].pushto) {
addMessage(XLAT("Cannot push into the same location!"));
return true;
}
}
}
return false;
}
EX void movecost(cell* from, cell *to, int phase) {
if(from->land == laPower && to->land != laPower && (phase & 1)) {
int n=0;