From 63f895974e0329722cded3acc0bce304d6d82756 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Thu, 5 Nov 2020 14:24:01 +0100 Subject: [PATCH] improved the checkmove.cpp, also works for golems now --- attack.cpp | 20 +++--- checkmove.cpp | 183 ++++++++++++++++++------------------------------ complex.cpp | 11 +-- game.cpp | 2 +- items.cpp | 2 + locations.cpp | 1 + monstermove.cpp | 24 +++++-- orbs.cpp | 23 +++--- pcmove.cpp | 93 +++++++++++------------- 9 files changed, 161 insertions(+), 198 deletions(-) diff --git a/attack.cpp b/attack.cpp index 0b8bc352..fa1a2d03 100644 --- a/attack.cpp +++ b/attack.cpp @@ -1183,19 +1183,19 @@ EX void killThePlayerAt(eMonster m, cell *c, flagtype flags) { } #if HDR -template void do_swords(cell *mf, cell *mt, eMonster who, const T& f) { +template 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 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); diff --git a/checkmove.cpp b/checkmove.cpp index 473b6188..51d41f47 100644 --- a/checkmove.cpp +++ b/checkmove.cpp @@ -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 pmi; +EX vector 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 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 sw(passive_switch, passive_switch); - - for(int i=0; i 8) + if(celldistance(pmi[i].mi.t, pmi[j].mi.t) > 8) { b = true; who_kills_me = moAirball; } } - for(int i=0; !b && i 1) wcw = &multi::player[multi::cpid].at; - - dynamicval x5(*wcw, c); - dynamicval x6(stalemate::nextturn, true); - dynamicval 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 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 moves; - EX bool nextturn; - - EX bool isMoveto(cell *c) { - for(int i=0; iwall == 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; } diff --git a/complex.cpp b/complex.cpp index e5b22f0d..8fae488e 100644 --- a/complex.cpp +++ b/complex.cpp @@ -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; diff --git a/game.cpp b/game.cpp index 4fb9aaee..9dd82110 100644 --- a/game.cpp +++ b/game.cpp @@ -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(); diff --git a/items.cpp b/items.cpp index e59a0e46..3634c860 100644 --- a/items.cpp +++ b/items.cpp @@ -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; diff --git a/locations.cpp b/locations.cpp index 2dc5bd10..e52e3cb5 100644 --- a/locations.cpp +++ b/locations.cpp @@ -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*); } diff --git a/monstermove.cpp b/monstermove.cpp index 4a7564da..7925b570 100644 --- a/monstermove.cpp +++ b/monstermove.cpp @@ -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; ktype; 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); diff --git a/orbs.cpp b/orbs.cpp index e0031ccf..f81d9a33 100644 --- a/orbs.cpp +++ b/orbs.cpp @@ -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); } diff --git a/pcmove.cpp b/pcmove.cpp index e6d30f6d..f237d137 100644 --- a/pcmove.cpp +++ b/pcmove.cpp @@ -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; ttype; t++) { - cell *c = mf->move(t); + for(int t=0; ttype; 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; iland == laPower && to->land != laPower && (phase & 1)) { int n=0;