Theming Ruins. Implemented Orb of Slaying (also cleaning up some kill/stun/Vizier code).

This commit is contained in:
Zeno Rogue 2018-01-03 21:49:14 +01:00
parent 86e760a562
commit a8460b1ac7
11 changed files with 151 additions and 87 deletions

View File

@ -759,11 +759,11 @@ monstertype minf[motypes] = {
"When your plan has clearly failed, it is better to abandon it and go to a safe place, to have a chance of succeeding next time. This dog clearly knows this."},
{ 'B', 0xC00000, "North Pole", NODESCYET},
{ 'B', 0x0000C0, "South Pole", NODESCYET},
{ 'P', 0xC0D000, "Pair Demon", NODESCYET},
{ 'H', 0x00C0D0, "Hex Demon", NODESCYET},
{ 'A', 0xD000C0, "Alt Demon", NODESCYET},
{ 'M', 0x904000, "Monk", NODESCYET},
{ 'C', 0x004070, "Crusher", NODESCYET},
{ 'P', 0xC03000, "Red Raider", NODESCYET},
{ 'H', 0xC0C0C0, "Gray Raider", NODESCYET},
{ 'A', 0x80B080, "Green Raider", NODESCYET},
{ 'M', 0x904000, "Brown Raider", NODESCYET},
{ 'C', 0x0060E0, "Blue Raider", NODESCYET},
{ '@', 0xC00000, "Switcher A", NODESCYET},
{ '@', 0x0000C0, "Switcher B", NODESCYET},
@ -1213,12 +1213,13 @@ itemtype iinf[ittypes] = {
{ '!', 0x80FF00, "Glowing Crystal", crystaldesc},
{ '!', 0x80FF80, "Snake Oil", NODESCYET},
{ '*', 0x80FF80, "Sea Glass", NODESCYET},
{ '*', 0xFFFFFF, "Invix Treasure", NODESCYET},
{ '*', 0xD0D8ED, "Chalcedony", NODESCYET},
{ '*', 0x80FF80, "Monopole", NODESCYET},
{ '*', 0xFFFF80, "Junk", NODESCYET},
{ '*', 0xBBCC99, "Chrysoberyl", NODESCYET},
{ 'o', 0x80FF80, "Orb of Phasing", NODESCYET},
{ 'o', 0xFFFF80, "Orb of Magnetism", NODESCYET},
{ 'o', 0xFFFF80, "Orb of Destruction", NODESCYET},
{ 'o', 0x202020, "Orb of Slaying", NODESCYET},
// { '*', 0x26619C, "Lapis Lazuli", NODESCYET},
};
// --- wall types ---
@ -1398,6 +1399,7 @@ walltype winf[walltypes] = {
{ '&', 0xD00000, "lava", lavadesc},
{ '=', 0x804000, "dock", "A dock."},
{ '^', 0xFF8000, "burning dock", "A burning dock."},
{ '#', 0xE07070, "ruin wall", "A ruin wall."},
};
// --- land types ---
@ -1585,7 +1587,7 @@ const landtype linf[landtypes] = {
{ 0x80FF00, "Crystal World", crystaldesc},
{ 0x306030, "Snake Nest", NODESCYET},
{ 0x80FF00, "Docks", NODESCYET},
{ 0x306030, "Invincible", NODESCYET},
{ 0x306030, "Ruined City", NODESCYET},
{ 0x306030, "Magnetosphere", NODESCYET},
{ 0x306030, "Switch", NODESCYET},
};

View File

@ -117,10 +117,10 @@ enum eItem {
itOrbSide1, itOrbSide2, itOrbSide3,
itOrbLava, itOrbMorph, itGlowCrystal, itSnake,
itDock, itInvix, itMagnet, itSwitch,
itOrbPhasing, itOrbMagnetism, itOrbDestruction
itOrbPhasing, itOrbMagnetism, itOrbSlaying
};
static const int walltypes = 107;
static const int walltypes = 108;
struct walltype {
char glyph;
@ -161,7 +161,7 @@ enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCav
waTempBridgeBlocked,
waTerraWarrior, waBubble,
waArrowTrap, waMercury, waMagma,
waDock, waBurningDock
waDock, waBurningDock, waRuinWall
};
static const int landtypes = 83;

View File

@ -1199,7 +1199,7 @@ namespace mirror {
c->monst = moMimic;
eMonster m2 = c2->monst;
if(!peace::on && canAttack(c,moMimic,c2,m2, 0)) {
attackMonster(c2, AF_MSG | AF_ORSTUN, moMimic);
attackMonster(c2, AF_NORMAL | AF_MSG, moMimic);
if(!fwd) produceGhost(c2, m2, moMimic);
sideAttack(c, m.second.spin, m2, 0);
}
@ -1642,7 +1642,7 @@ namespace hive {
if(isBug(killed)) battlecount++;
else if(killed != moPlayer && !fightspam(c2))
addMessage(XLAT("%The1 fights with %the2!", c->monst, killed));
attackMonster(c2, AF_ORSTUN | AF_GETPLAYER, c->monst);
attackMonster(c2, AF_NORMAL | AF_GETPLAYER, c->monst);
// killMonster(c);
if(isBug(killed)) {
c2->monst = moDeadBug, deadbug.push_back(c2);
@ -2020,7 +2020,7 @@ namespace heat {
c->wparam++;
if(c->wparam == 3) {
if(canAttack(c, moArrowTrap, c, c->monst, AF_GETPLAYER))
attackMonster(c, AF_ORSTUN | AF_MSG | AF_GETPLAYER, moArrowTrap);
attackMonster(c, AF_NORMAL | AF_MSG | AF_GETPLAYER, moArrowTrap);
}
if(c->wparam == 4) c->wparam = 0;
}
@ -2589,7 +2589,7 @@ namespace kraken {
if(c->monst == moKrakenT && !c->stuntime) forCellEx(c2, c) {
bool dboat = false;
if(c2->monst && canAttack(c, moKrakenT, c2, c2->monst, AF_ONLY_FBUG)) {
attackMonster(c2, AF_ORSTUN | AF_MSG, c->monst);
attackMonster(c2, AF_NORMAL | AF_MSG, c->monst);
sleep(c);
}
else for(int i=0; i<numplayers(); i++) if(playerpos(i) == c2) {
@ -2869,7 +2869,7 @@ namespace prairie {
cell *cn = whirlline[q+1];
if(cp->monst == moHerdBull && !cp->stuntime) {
forCellEx(c2, cp) {
int flags = AF_GETPLAYER | AF_BULL | AF_ORSTUN;
int flags = AF_GETPLAYER | AF_BULL;
if(c2 != cn) flags |= AF_ONLY_FBUG;
if(canAttack(c, moHerdBull, c2, c2->monst, flags))
attackMonster(c2, flags | AF_MSG, moHerdBull);

View File

@ -117,7 +117,8 @@ bool isMetalBeast(eMonster m) {
bool isStunnable(eMonster m) {
return m == moPalace || m == moFatGuard || m == moSkeleton || isPrincess(m) ||
isMetalBeast(m) || m == moTortoise || isDragon(m) ||
m == moReptile || m == moTerraWarrior || m == moSalamander;
m == moReptile || m == moTerraWarrior || m == moSalamander ||
m == moVizier;
}
bool hasHitpoints(eMonster m) {
@ -555,7 +556,8 @@ bool isOffensiveOrb(eItem it) {
return it == itOrbLightning || it == itOrbFlash || it == itOrbThorns ||
it == itOrbDragon || it == itOrbStunning ||
it == itOrbFreedom || it == itOrbPsi ||
it == itOrbSide1 || it == itOrbSide2 || it == itOrbSide3;
it == itOrbSide1 || it == itOrbSide2 || it == itOrbSide3 ||
it == itOrbSlaying;
}
bool isRangedOrb(eItem i) {
@ -575,7 +577,8 @@ bool isEmpathyOrb(eItem i) {
i == itOrbUndeath || i == itOrbSpeed || i == itOrbShield ||
i == itOrbAether || i == itOrbInvis || i == itOrbThorns ||
i == itOrbWater || i == itOrbStone ||
i == itOrbSide1 || i == itOrbSide2 || i == itOrbSide3;
i == itOrbSide1 || i == itOrbSide2 || i == itOrbSide3 ||
i == itOrbSlaying;
}
bool isUtilityOrb(eItem i) {
@ -720,8 +723,9 @@ bool hornStuns(cell *c) {
return
m == moRagingBull || m == moSleepBull || m == moHerdBull ||
m == moButterfly || m == moGreater || m == moGreaterM || m == moDraugr ||
m == moHedge || m == moFlailer || m == moVizier ||
attackJustStuns(c);
m == moHedge || m == moFlailer || m == moVizier || m == moReptile || m == moSalamander ||
m == moPair || m == moAltDemon || m == moHexDemon || m == moMonk || m == moCrusher ||
attackJustStuns(c, AF_NORMAL);
}
// generate all the world first in the quotient geometry

107
game.cpp
View File

@ -928,6 +928,9 @@ bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) {
if(m2 == moPlayer && peace::on) return false;
if((flags & AF_MUSTKILL) && attackJustStuns(c2, flags))
return false;
if((flags & AF_ONLY_FRIEND) && m2 != moPlayer && !isFriendly(c2)) return false;
if((flags & AF_ONLY_FBUG) && m2 != moPlayer && !isFriendlyOrBug(c2)) return false;
if((flags & AF_ONLY_ENEMY) && (m2 == moPlayer || isFriendlyOrBug(c2))) return false;
@ -943,7 +946,7 @@ bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) {
(m2 == moKrakenT || m2 == moKrakenH))
return false;
if(m2 == moDraugr && !(flags & (AF_SWORD | AF_MAGIC | AF_SWORD_INTO | AF_HORNS))) return false;
if(m2 == moDraugr && !(flags & (AF_SWORD | AF_MAGIC | AF_SWORD_INTO | AF_HORNS | AF_CRUSH))) return false;
// if(m2 == moHerdBull && !(flags & AF_MAGIC)) return false;
if(isBull(m2) && !(flags & (AF_MAGIC | AF_HORNS | AF_SWORD_INTO | AF_CRUSH))) return false;
@ -998,7 +1001,7 @@ bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) {
// if(m2 == moTortoise && !(flags & AF_MAGIC)) return false;
if(m2 == moRoseBeauty)
if(!(flags & (AF_MAGIC | AF_LANCE | AF_GUN | AF_SWORD_INTO | AF_BULL)))
if(!(flags & (AF_MAGIC | AF_LANCE | AF_GUN | AF_SWORD_INTO | AF_BULL | AF_CRUSH)))
if(!isMimic(m1))
if(!checkOrb(m1, itOrbBeauty) && !checkOrb(m1, itOrbAether) && !checkOrb(m1, itOrbShield))
if(!c1 || !c2 || !withRose(c1,c2))
@ -1007,7 +1010,7 @@ bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) {
if(m2 == moFlailer && !c2->stuntime)
if(!(flags & (AF_MAGIC | AF_TOUGH | AF_EAT | AF_HORNS | AF_LANCE | AF_BACK | AF_SWORD_INTO | AF_BULL | AF_CRUSH))) return false;
if(m2 == moVizier && c2->hitpoints > 1)
if(m2 == moVizier && c2->hitpoints > 1 && !c2->stuntime)
if(!(flags & (AF_MAGIC | AF_TOUGH | AF_EAT | AF_HORNS | AF_LANCE | AF_BACK | AF_FAST | AF_BULL | AF_CRUSH))) return false;
return true;
@ -1800,7 +1803,7 @@ void stunMonster(cell *c2) {
(c2->monst == moGreater || c2->monst == moGreaterM) ? 5 :
c2->monst == moButterfly ? 2 :
c2->monst == moDraugr ? 1 :
c2->monst == moVizier ? 1 :
c2->monst == moVizier ? 0 :
c2->monst == moHedge ? 1 :
c2->monst == moFlailer ? 1 :
c2->monst == moSalamander ? 6 :
@ -1816,8 +1819,15 @@ void stunMonster(cell *c2) {
checkStunKill(c2);
}
bool attackJustStuns(cell *c2) {
return isStunnable(c2->monst) && c2->hitpoints > 1;
bool attackJustStuns(cell *c2, flagtype f) {
if(f & AF_HORNS)
return hornStuns(c2);
else if((f & AF_SWORD) && c2->monst == moSkeleton)
return false;
else if(f & (AF_CRUSH | AF_MAGIC | AF_FALL | AF_EAT | AF_GUN))
return false;
else
return isStunnable(c2->monst) && c2->hitpoints > 1;
}
void moveEffect(cell *ct, cell *cf, eMonster m);
@ -2306,17 +2316,13 @@ bool attackMonster(cell *c, flagtype flags, eMonster killer) {
int tkt = killtypes();
bool dostun = (flags & AF_ORSTUN) && attackJustStuns(c);
bool dostun = attackJustStuns(c, flags);
if((flags & AF_HORNS) && hornStuns(c)) dostun = true;
if((flags & AF_BULL) && c->monst == moVizier && c->hitpoints > 1) {
dostun = true;
c->stuntime = 2;
}
if(c->monst == moSkeleton && (flags & AF_SWORD)) dostun = false;
if(c->monst == moSkeleton && killer == moCrusher) dostun = false;
bool eu = elementalUnlocked();
bool tu = trollUnlocked();
@ -3400,7 +3406,7 @@ void moveMonster(cell *ct, cell *cf) {
for(int u=2; u<=ct->type-2; u++) {
cell *c3 = ct->mov[(ct->mondir+u)%ct->type];
if(canAttack(ct, moLancer, c3, c3->monst, AF_LANCE | AF_GETPLAYER)) {
attackMonster(c3, AF_LANCE | AF_ORSTUN | AF_MSG | AF_GETPLAYER, m);
attackMonster(c3, AF_LANCE | AF_MSG | AF_GETPLAYER, m);
}
}
}
@ -3480,7 +3486,7 @@ void moveMonster(cell *ct, cell *cf) {
fallMonster(ct);
}
if(sword::at(ct) && canAttack(NULL, moPlayer, ct, m, AF_SWORD_INTO)) {
attackMonster(ct, AF_SWORD_INTO | AF_ORSTUN | AF_MSG, moPlayer);
attackMonster(ct, AF_SWORD_INTO | AF_MSG, moPlayer);
achievement_gain("GOSWORD");
}
}
@ -3819,7 +3825,7 @@ void beastAttack(cell *c, bool player) {
if(c->mondir == NODIR) return;
forCellIdEx(c2, d, c) {
bool opposite = angledist(c, d, c->mondir) >= 3;
int flags = AF_BULL | AF_ORSTUN;
int flags = AF_BULL;
if(player) flags |= AF_GETPLAYER;
if(!opposite) flags |= AF_ONLY_FBUG;
if(canAttack(c, moRagingBull, c2, c2->monst, flags)) {
@ -3889,7 +3895,7 @@ cell *moveNormal(cell *c, flagtype mf) {
return c2;
}
else if(m2) {
attackMonster(c2, AF_ORSTUN | AF_MSG, m);
attackMonster(c2, AF_NORMAL | AF_MSG, m);
if(m == moFlailer && m2 == moIllusion)
attackMonster(c, 0, m2);
return c2;
@ -3912,7 +3918,7 @@ cell *moveNormal(cell *c, flagtype mf) {
else {
eMonster m2 = c2->monst;
if(m2) {
attackMonster(c2, AF_ORSTUN | AF_MSG, m);
attackMonster(c2, AF_NORMAL | AF_MSG, m);
if(m == moFlailer && m2 == moIllusion)
attackMonster(c, 0, m2);
attacking = true;
@ -4007,7 +4013,7 @@ void killThePlayer(eMonster m, int id, flagtype flags) {
}
else if(items[itOrbDomination] && playerpos(id)->monst) {
addMessage(XLAT("%The1 tries to dismount you!", m));
attackMonster(playerpos(id), AF_ORSTUN, m);
attackMonster(playerpos(id), AF_NORMAL, m);
useupOrb(itOrbDomination, items[itOrbDomination]/2);
}
else if(items[itOrbShell] && !(flags & AF_EAT)) {
@ -4080,7 +4086,7 @@ void moveWorm(cell *c) {
// explodeAround(c);
forCellEx(c2, c)
if(canAttack(c, c->monst, c2, c2->monst, mounted ? AF_ONLY_ENEMY : (AF_GETPLAYER | AF_ONLY_FBUG))) {
attackMonster(c2, AF_ORSTUN | AF_MSG | AF_GETPLAYER, c->monst);
attackMonster(c2, AF_NORMAL | AF_MSG | AF_GETPLAYER, c->monst);
}
cell *c2 = c;
vector<cell*> allcells;
@ -4277,13 +4283,13 @@ void moveivy() {
if(isPlayerOn(c->mov[j]))
killThePlayerAt(c->monst, c->mov[j], 0);
else {
if(attackJustStuns(c->mov[j]))
if(attackJustStuns(c->mov[j], 0))
addMessage(XLAT("The ivy attacks %the1!", c->mov[j]->monst));
else if(isNonliving(c->mov[j]->monst))
addMessage(XLAT("The ivy destroys %the1!", c->mov[j]->monst));
else
addMessage(XLAT("The ivy kills %the1!", c->mov[j]->monst));
attackMonster(c->mov[j], AF_ORSTUN, c->monst);
attackMonster(c->mov[j], AF_NORMAL, c->monst);
}
continue;
}
@ -4413,7 +4419,7 @@ void groupmove2(cell *c, cell *from, int d, eMonster movtype, flagtype mf) {
// note: move from 'c' to 'from'!
if(!(mf & MF_NOATTACKS)) for(int j=0; j<c->type; j++)
if(c->mov[j] && canAttack(c, c->monst, c->mov[j], c->mov[j]->monst, af)) {
attackMonster(c->mov[j], AF_ORSTUN | AF_GETPLAYER | AF_MSG, c->monst);
attackMonster(c->mov[j], AF_NORMAL | AF_GETPLAYER | AF_MSG, c->monst);
c->aitmp = sval;
// XLATC eagle
return;
@ -4521,7 +4527,7 @@ void snakeAttack(cell *c, bool mounted) {
if(c->mov[j] && canAttack(c, moHexSnake, c->mov[j], c->mov[j]->monst,
mounted ? AF_ONLY_ENEMY : (AF_ONLY_FBUG | AF_GETPLAYER))) {
eMonster m2 = c->mov[j]->monst;
attackMonster(c->mov[j], AF_ORSTUN | AF_GETPLAYER | AF_MSG, moHexSnake);
attackMonster(c->mov[j], AF_NORMAL | AF_GETPLAYER | AF_MSG, moHexSnake);
produceGhost(c->mov[j], moHexSnake, m2);
}
}
@ -4653,7 +4659,7 @@ void movemutant() {
if(!c2) continue;
if(c2->monst != moMutant && canAttack(c, moMutant, c2, c2->monst, AF_ONLY_FBUG | AF_GETPLAYER)) {
attackMonster(c2, AF_ORSTUN | AF_MSG | AF_GETPLAYER, moMutant);
attackMonster(c2, AF_NORMAL | AF_MSG | AF_GETPLAYER, moMutant);
continue;
}
@ -4687,7 +4693,7 @@ void moveshadow() {
if(c && c->monst == moShadow) {
for(int j=0; j<c->type; j++)
if(c->mov[j] && canAttack(c, moShadow, c->mov[j], c->mov[j]->monst, AF_ONLY_FBUG | AF_GETPLAYER))
attackMonster(c->mov[j], AF_ORSTUN | AF_MSG | AF_GETPLAYER, c->monst);
attackMonster(c->mov[j], AF_NORMAL | AF_MSG | AF_GETPLAYER, c->monst);
c->monst = moNone;
shfrom = c;
}
@ -4741,7 +4747,7 @@ void moveghosts() {
if(c->mov[j] && canAttack(c, c->monst, c->mov[j], c->mov[j]->monst, AF_GETPLAYER | AF_ONLY_FBUG)) {
// XLATC ghost/greater shark
attackMonster(c->mov[j], AF_ORSTUN | AF_MSG | AF_GETPLAYER, c->monst);
attackMonster(c->mov[j], AF_NORMAL | AF_MSG | AF_GETPLAYER, c->monst);
goto nextghost;
}
@ -4800,7 +4806,7 @@ bool swordAttack(cell *mt, eMonster who, cell *c, int bb) {
if(!peace::on && canAttack(mt, who, c, m, AF_SWORD)) {
markOrb(bb ? itOrbSword2: itOrbSword);
int k = tkills();
attackMonster(c, AF_ORSTUN | AF_MSG | AF_SWORD, who);
attackMonster(c, AF_NORMAL | AF_MSG | AF_SWORD, who);
if(c->monst == moShadow) c->monst = moNone;
produceGhost(c, m, who);
if(tkills() > k) return true;
@ -4827,7 +4833,7 @@ void sideAttack(cell *mf, int dir, eMonster who, int bonus, eItem orb) {
if(canAttack(mf, who, mt, m, AF_SIDE)) {
markOrb(orb);
if(who != moPlayer) markOrb(itOrbEmpathy);
if(attackMonster(mt, AF_ORSTUN | AF_SIDE | AF_MSG, who))
if(attackMonster(mt, AF_NORMAL | AF_SIDE | AF_MSG, who))
produceGhost(mt, m, who);
}
else if(mt->wall == waBigTree)
@ -4890,7 +4896,7 @@ void stabbingAttack(cell *mf, cell *mt, eMonster who, int bonuskill) {
}
eMonster m = c->monst;
int k = tkills();
if(attackMonster(c, AF_ORSTUN | AF_STAB | AF_MSG, who))
if(attackMonster(c, AF_STAB | AF_MSG, who))
produceGhost(c, m, who);
if(tkills() > k) numsh++;
}
@ -5064,6 +5070,8 @@ int movevalue(eMonster m, cell *c, cell *c2, flagtype flags) {
#define STRONGWIND 99
void movegolems(flagtype flags) {
if(items[itOrbEmpathy] && items[itOrbSlaying])
flags |= AF_CRUSH;
computePathdist(moMouse);
int qg = 0;
for(int i=0; i<size(golems); i++) {
@ -5115,7 +5123,10 @@ void movegolems(flagtype flags) {
playSound(c2, princessgender() ? "dzia-princess" : "dzia-prince");
addMessage(XLAT("%The1 takes %his1 revenge on %the2!", m, c2->monst));
}
attackMonster(c2, ((revenge||jealous)?0:AF_ORSTUN) | AF_MSG, m);
if(revenge || jealous) flags |= AF_CRUSH;
else if((flags & AF_CRUSH) && !canAttack(c, m, c2, c2->monst, flags ^ AF_CRUSH ^ AF_MUSTKILL))
markOrb(itOrbEmpathy), markOrb(itOrbSlaying);
attackMonster(c2, flags | AF_MSG, m);
produceGhost(c2, m2, m);
sideAttack(c, dir, m, 0);
if(revenge) c->monst = m = moPrincessArmed;
@ -5365,8 +5376,8 @@ void moverefresh(bool turn = true) {
if(c2->monst != moPair) continue;
if(!c2->stuntime) {
cell *c3 = c->mov[(c->mondir + 1) % c->type];
if(c3->wall == waColumn) {
drawParticles(c3, 0xC00000, 30);
if(among(c3->wall, waRuinWall, waColumn, waStone, waVinePlant)) {
drawParticles(c3, winf[c3->wall].color, 30);
c3->wall = waNone;
}
}
@ -7020,7 +7031,7 @@ void monstersTurn() {
for(cell *c: crush_now)
if(canAttack(c, moCrusher, c, c->monst, AF_GETPLAYER | AF_CRUSH))
attackMonster(c, AF_ORSTUN | AF_MSG | AF_GETPLAYER | AF_CRUSH, moCrusher);
attackMonster(c, AF_MSG | AF_GETPLAYER | AF_CRUSH, moCrusher);
crush_now = move(crush_next);
crush_next.clear();
@ -7144,7 +7155,7 @@ void killFriendlyIvy() {
}
bool monsterPushable(cell *c2) {
return (c2->monst != moFatGuard && !(isMetalBeast(c2->monst) && c2->stuntime < 2) && c2->monst != moTortoise && c2->monst != moTerraWarrior);
return (c2->monst != moFatGuard && !(isMetalBeast(c2->monst) && c2->stuntime < 2) && c2->monst != moTortoise && c2->monst != moTerraWarrior && c2->monst != moVizier);
}
bool movepcto(int d, int subdir, bool checkonly) {
@ -7387,9 +7398,11 @@ bool movepcto(int d, int subdir, bool checkonly) {
else if(c2->monst && (!isFriendly(c2) || c2->monst == moTameBomberbird || isMountable(c2->monst))
&& !(peace::on && !isMultitile(c2->monst) && !goodTortoise)) {
bool fast = !((!items[itOrbSpeed]) || (items[itOrbSpeed]&1));
flagtype attackflags = AF_NORMAL;
if(items[itOrbSpeed]&1) attackflags |= AF_FAST;
if(items[itOrbSlaying]) attackflags |= AF_CRUSH;
if(!canAttack(cwt.c, moPlayer, c2, c2->monst, fast ? AF_FAST : 0)) {
if(!canAttack(cwt.c, moPlayer, c2, c2->monst, attackflags)) {
if(checkonly) return false;
if(c2->monst == moWorm || c2->monst == moWormtail || c2->monst == moWormwait)
addMessage(XLAT("You cannot attack Sandworms directly!"));
@ -7411,7 +7424,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
addMessage(XLAT("You cannot attack %the1 directly!", c2->monst));
addMessage(XLAT("Make him hit himself by walking away from him."));
}
else if(c2->monst == moVizier && c2->hitpoints > 1 && !fast) {
else if(c2->monst == moVizier && c2->hitpoints > 1 && !(attackflags & AF_FAST)) {
addMessage(XLAT("You cannot attack %the1 directly!", c2->monst));
addMessage(XLAT("Hit him by walking away from him."));
}
@ -7476,24 +7489,16 @@ bool movepcto(int d, int subdir, bool checkonly) {
c2->stuntime = 2;
achievement_collection(itBabyTortoise, 0, 0);
}
else if(isStunnable(c2->monst) && c2->hitpoints > 1) {
attackMonster(c2, AF_ORSTUN | AF_MSG, moPlayer);
// salamanders are stunned for longer time when pushed into a wall
if(c2->monst == moSalamander && (pushto == c2 || !pushto)) c2->stuntime = 10;
if(pushto && pushto != c2) pushMonster(pushto, c2);
}
else if(c2->monst == moVizier && c2->hitpoints > 1) {
fightmessage(c2->monst, moPlayer, true, 0);
c2->hitpoints--;
}
else if(isDragon(c2->monst) || isKraken(c2->monst)) {
attackMonster(c2, AF_ORSTUN | AF_MSG, moPlayer);
}
else {
eMonster m = c2->monst;
if(m) {
attackMonster(c2, AF_MSG, moPlayer);
produceGhost(c2, m, moPlayer);
if((attackflags & AF_CRUSH) && !canAttack(cwt.c, moPlayer, c2, c2->monst, attackflags ^ AF_CRUSH ^ AF_MUSTKILL))
markOrb(itOrbSlaying);
attackMonster(c2, attackflags | AF_MSG, moPlayer);
// salamanders are stunned for longer time when pushed into a wall
if(c2->monst == moSalamander && (pushto == c2 || !pushto)) c2->stuntime = 10;
if(!c2->monst) produceGhost(c2, m, moPlayer);
if(pushto && pushto != c2) pushMonster(pushto, c2);
}
}

View File

@ -695,8 +695,9 @@ bool drawItemType(eItem it, cell *c, const transmatrix& V, int icol, int ticks,
if(it == itOrbAir) icol = 0xFFFFFF;
if(it == itOrbUndeath) icol = minf[moFriendlyGhost].color;
if(it == itOrbRecall) icol = 0x101010;
if(it == itOrbSlaying) icol = 0xFF0000;
int col = darkena(icol, 0, int(0x80 + 0x70 * sin(ticks / 300.)));
if(it == itOrbFish)
queuepolyat(V * spin(ticks / 1500.), shFishTail, col, PPR_ITEM_BELOW);
@ -1219,12 +1220,52 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou
if(!peace::on) queuepoly(VBODY, shPSword, 0xFFFF00FF);
queuepoly(VHEAD, shHood, darkena(col, 0, 0xFF));
}
else if(m == moPair || m == moAltDemon || m == moHexDemon || m == moMonk || m == moCrusher) {
else if(m == moMonk) {
otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(col, 0, 0xC0));
if(!peace::on) queuepoly(VBODY, shPSword, 0xFFFF00FF);
queuepoly(VHEAD, shHood, darkena(col, 0, 0xFF));
if(!peace::on) queuepoly(VBODY, shPKnife, 0xFFC0C0C0);
queuepoly(VBODY, shTerraArmor1, darkena(col, 1, 0xFF));
queuepoly(VBODY, shTerraArmor2, darkena(col, 0, 0xFF));
queuepoly(VBODY, shTerraArmor3, darkena(col, 1, 0xFF));
queuepolyat(VBODY, shRatCape2, darkena(col, 2, 0xFF), PPR_MONSTER_ARMOR0);
queuepoly(VHEAD, shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
}
else if(m == moCrusher) {
otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(col, 0, 0xFF));
queuepoly(VBODY, shTerraArmor1, darkena(col, 1, 0xFF));
queuepoly(VBODY, shTerraArmor2, darkena(col, 0, 0xFF));
queuepoly(VBODY, shTerraArmor3, darkena(col, 1, 0xFF));
queuepoly(VBODY, shFlailBall, darkena(col, 0, 0XFF));
queuepoly(VBODY, shFlailChain, darkena(col, 1, 0XFF));
queuepoly(VBODY, shFlailTrunk, darkena(col, 0, 0XFF));
queuepoly(VHEAD, shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
}
else if(m == moPair) {
otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(col, 0, 0xC0));
queuepoly(VBODY, shTerraArmor1, darkena(col, 1, 0xFF));
queuepoly(VBODY, shTerraArmor2, darkena(col, 0, 0xFF));
queuepoly(VBODY, shTerraArmor3, darkena(col, 1, 0xFF));
queuepoly(VBODY, shPickAxe, darkena(0xA0A0A0, 0, 0XFF));
queuepoly(VHEAD, shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
}
else if(m == moAltDemon || m == moHexDemon) {
otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(col, 0, 0xC0));
queuepoly(VBODY, shTerraArmor1, darkena(col, 1, 0xFF));
queuepoly(VBODY, shTerraArmor2, darkena(col, 0, 0xFF));
queuepoly(VBODY, shTerraArmor3, darkena(col, 1, 0xFF));
if(!peace::on) queuepoly(VBODY, shPSword, 0xFFD0D0D0);
queuepoly(VHEAD, shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
}
else if(m == moPalace || m == moFatGuard || m == moVizier || m == moSkeleton) {
queuepoly(VBODY, shSabre, 0xFFFFFFFF);
@ -1412,8 +1453,8 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou
queuepoly(VHEAD, shWolf3, darkena(0x202020, 0, 0xFF));
if(m == moRatlingAvenger) {
queuepoly(VBODY, shRatCape1, 0x303030FF);
queuepoly(VHEAD, shRatCape2, 0x484848FF);
queuepoly(VBODY, shRatCape2, 0x484848FF);
queuepoly(VHEAD, shRatCape1, 0x303030FF);
}
}
else if(m == moViking) {
@ -1936,7 +1977,7 @@ bool drawMonster(const transmatrix& Vparam, int ct, cell *c, int col) {
// also whatever in the lineview mode
else if(isFriendly(c) || isBug(c) || (c->monst && conformal::on) || c->monst == moKrakenH || (isBull(c->monst) && c->mondir != NODIR) || c->monst == moButterfly || isMagneticPole(c->monst) ||
isSwitch(c->monst) || c->monst == moPair) {
isSwitch(c->monst)) {
if(c->monst == moKrakenH) Vs = Vb, nospins = nospinb;
if(!nospins) Vs = Vs * ddspin(c, c->mondir, S42);
if(isFriendly(c)) drawPlayerEffects(Vs, c, false);
@ -2486,6 +2527,10 @@ void setcolors(cell *c, int& wcol, int &fcol) {
case laDragon: case laStorms: case laTerracotta: case laMercuryRiver:
fcol = linf[c->land].color; break;
case laInvincible:
fcol = pseudohept(c) ? 0xC0C0C0 : 0x40A040;
break;
case laDual:
fcol = linf[c->land].color;
if(c->landparam == 2) fcol = 0x40FF00;

View File

@ -975,6 +975,8 @@ bool withRose(cell *cfrom, cell *cto);
// canAttack/moveval flags
#define AF_NORMAL 0 // nothing special about this attack
#define AF_TOUGH Flag(0) // tough attacks: Hyperbugs
#define AF_MAGIC Flag(1) // magical attacks: Flash
#define AF_STAB Flag(2) // stabbing attacks (usually ignored except Hedgehogs)
@ -1000,7 +1002,7 @@ bool withRose(cell *cfrom, cell *cto);
#define AF_SWORD Flag(20) // big sword
#define AF_SWORD_INTO Flag(21) // moving into big sword
#define AF_MSG Flag(22) // produce a message
#define AF_ORSTUN Flag(23) // attackMonster: allow stunning
#define AF_MUSTKILL Flag(23) // when TRUE, stunning attacks are not accepted by canAttack
#define AF_NEXTTURN Flag(24) // next turn -- don't count shield at power 1
#define AF_FALL Flag(25) // death by falling
#define MF_STUNNED Flag(26) // edgeunstable: ignore ladders (as stunned monsters do)
@ -1108,7 +1110,7 @@ namespace sword {
}
void killThePlayer(eMonster m, int id, flagtype flags);
bool attackJustStuns(cell *c2);
bool attackJustStuns(cell *c2, flagtype flags);
bool isTargetOrAdjacent(cell *c);
bool warningprotection();

View File

@ -331,7 +331,7 @@ namespace inv {
gainOrbs(itGlowCrystal, itOrbSide2);
gainOrbs(itSwitch, itOrbPhasing);
gainOrbs(itMagnet, itOrbMagnetism);
gainOrbs(itInvix, itOrbDestruction);
gainOrbs(itInvix, itOrbSlaying);
if(items[itOrbLove] && !items[itSavedPrincess]) items[itSavedPrincess] = 1;

View File

@ -114,7 +114,7 @@ const orbinfo orbinfos[ORBLINES] = {
{orbgenflags::S_GUEST, laSwitch, 2000, 0, itOrbSpace},
{orbgenflags::S_NATIVE, laSwitch, 2000, 3000, itOrbPhasing},
{orbgenflags::S_NATIVE, laMagnetic, 2000, 3000, itOrbMagnetism},
{orbgenflags::S_NATIVE, laInvincible, 2000, 3000, itOrbDestruction},
{orbgenflags::S_NATIVE, laInvincible, 2000, 3000, itOrbSlaying},
{orbgenflags::S_NATIVE, laWhirlpool, 0, 2000, itOrbWater}, // needs to be last
};

View File

@ -140,6 +140,7 @@ void reduceOrbPowers() {
reduceOrbPower(itOrbHorns, 77);
reduceOrbPower(itOrbLava, 80);
reduceOrbPower(itOrbMorph, 80);
reduceOrbPower(itOrbSlaying, 120);
reduceOrbPower(itOrbSide1, 120);
reduceOrbPower(itOrbSide2, 120);
@ -615,7 +616,7 @@ void jumpTo(cell *dest, eItem byWhat, int bonuskill = 0, eMonster dashmon = moNo
void growIvyTo(cell *dest, cell *src) {
if(dest->monst)
attackMonster(dest, AF_MSG | AF_ORSTUN, moFriendlyIvy);
attackMonster(dest, AF_NORMAL | AF_MSG, moFriendlyIvy);
else {
dest->monst = moFriendlyIvy;
dest->mondir = neighborId(dest, src);
@ -1075,7 +1076,7 @@ eItem targetRangedOrb(cell *c, orbAction a) {
if(!isCheck(a)) {
int k = tkills();
eMonster m = c2->monst;
attackMonster(c2, AF_ORSTUN | AF_MSG, moPlayer);
attackMonster(c2, AF_NORMAL | AF_MSG, moPlayer);
k = tkills() - k;
jumpTo(c, itOrbDash, k, m);
}
@ -1317,6 +1318,7 @@ int orbcharges(eItem it) {
case itOrbBull:
case itOrbShell:
case itOrbAir:
case itOrbSlaying:
return 66;
case itOrbTime:
case itOrbSpace:

View File

@ -1008,7 +1008,7 @@ hpcshape
shArrow,
shPHead, shPFace, shGolemhead, shHood, shArmor,
shAztecHead, shAztecCap,
shSabre, shTurban1, shTurban2, shVikingHelmet,
shSabre, shTurban1, shTurban2, shVikingHelmet, shRaiderHelmet,
shWestHat1, shWestHat2, shGunInHand,
shKnightArmor, shKnightCloak, shWightCloak,
shGhost, shEyes, shSlime, shJoint, shWormHead, shTentHead, shShark,
@ -2122,6 +2122,7 @@ void buildpolys() {
bshape(shWestHat2, PPR_MONSTER_HAT1, scalef, 120);
bshape(shGunInHand, PPR_MONSTER_WPN, scalef, 121);
bshape(shVikingHelmet, PPR_MONSTER_HAT0, scalef, 122);
bshape(shRaiderHelmet, PPR_MONSTER_HAT0, scalef, 375);
bshape(shHood, PPR_MONSTER_HAT0, scalef, 123);
bshape(shPirateHood, PPR_MONSTER_HAT0, scalef, 125);
bshape(shEyepatch, PPR_MONSTER_HAT1, scalef, 126);
@ -3425,6 +3426,9 @@ NEWSHAPE, 373, 1, 1, -0.019312,0.304743, -0.289045,0.177117, -0.127176,-0.240665
NEWSHAPE, 374, 1, 1, -0.229502,-0.051000, 0.320183,0.006447, 0.148302,0.144065, 0.173317,0.054954, -0.253447,0.021298,
NEWSHAPE, 375, 1, 2, -0.090497,-0.016548, -0.072731,-0.044408, -0.058869,-0.063422, -0.031762,-0.071442, -0.001140,-0.143435, 0.032854,-0.162181, 0.080022,-0.161459, 0.108605,-0.129676, 0.112564,-0.096396, 0.102658,-0.077590, 0.088332,-0.113771, 0.046216,-0.129074, 0.017935,-0.063369, 0.049033,-0.046641, 0.032200,-0.027430,
NEWSHAPE
};