variant:: first version

This commit is contained in:
Zeno Rogue 2018-12-17 00:04:59 +01:00
parent 4432b30b2a
commit 724d3516fa
10 changed files with 275 additions and 65 deletions

View File

@ -780,6 +780,7 @@ monstertype minf[motypes] = {
{ '@', 0x0000C0, "Blue Jelly", jellydesc}, { '@', 0x0000C0, "Blue Jelly", jellydesc},
{ 'B', 0xE07000, "Brown Bug", NODESCYET}, { 'B', 0xE07000, "Brown Bug", NODESCYET},
{ 'B', 0xE07060, "Acid Bird", NODESCYET}, { 'B', 0xE07060, "Acid Bird", NODESCYET},
{ 'W', 0xA04060, "Variant Warrior", NODESCYET},
// shmup specials // shmup specials
{ '@', 0xC0C0C0, "Rogue", "In the Shoot'em Up mode, you are armed with thrown Knives."}, { '@', 0xC0C0C0, "Rogue", "In the Shoot'em Up mode, you are armed with thrown Knives."},
@ -1243,6 +1244,8 @@ itemtype iinf[ittypes] = {
{ '*', 0x20C0C0, "Brownie", { '*', 0x20C0C0, "Brownie",
"Tasty cookie." "Tasty cookie."
}, },
{ '*', 0x20C0C0, "West Treasure", NODESCYET},
{ '*', 0xC020C0, "Variant Treasure", NODESCYET}
// { '*', 0x26619C, "Lapis Lazuli", NODESCYET}, // { '*', 0x26619C, "Lapis Lazuli", NODESCYET},
}; };
@ -1424,7 +1427,9 @@ walltype winf[walltypes] = {
{ '=', 0x804000, "dock", "A dock."}, { '=', 0x804000, "dock", "A dock."},
{ '^', 0xFF8000, "burning dock", "A burning dock."}, { '^', 0xFF8000, "burning dock", "A burning dock."},
{ '#', 0xE04030, "ruin wall", ruindesc}, { '#', 0xE04030, "ruin wall", ruindesc},
{ '#', 0xA04060, "Brownian generator", NODESC} { '#', 0xA04060, "Brownian generator", NODESC},
{ '^', 0xC05000, "fire trap", NODESC},
{ '^', 0xFD692F, "Explosive Barrel", NODESC},
}; };
// --- land types --- // --- land types ---
@ -1621,7 +1626,9 @@ const landtype linf[landtypes] = {
"somehow. In the meantime, its memory has been cleared, since the 'remove faraway cells from the memory'" "somehow. In the meantime, its memory has been cleared, since the 'remove faraway cells from the memory'"
" option was on." " option was on."
}, },
{ 0xA04060, "Brownian", NODESCYET} { 0xA04060, "Brownian", NODESCYET},
{ 0xA04060, "West Wall", NODESCYET},
{ 0xA04060, "Variant", NODESCYET}
}; };
vector<landtacinfo> land_tac = { vector<landtacinfo> land_tac = {

View File

@ -10,7 +10,7 @@ static inline void set_flag(flagtype& f, flagtype which, bool b) {
else f &= ~which; else f &= ~which;
} }
static const int motypes = 164; static const int motypes = 165;
struct monstertype { struct monstertype {
char glyph; char glyph;
@ -67,7 +67,7 @@ enum eMonster {
moNorthPole, moSouthPole, moNorthPole, moSouthPole,
moPair, moHexDemon, moAltDemon, moMonk, moCrusher, moPair, moHexDemon, moAltDemon, moMonk, moCrusher,
moSwitch1, moSwitch2, moSwitch1, moSwitch2,
moBrownBug, moAcidBird, moBrownBug, moAcidBird, moVariantWarrior,
// shmup specials // shmup specials
moPlayer, moBullet, moFlailBullet, moFireball, moTongue, moAirball, moCrushball, moPlayer, moBullet, moFlailBullet, moFireball, moTongue, moAirball, moCrushball,
// temporary // temporary
@ -84,7 +84,7 @@ struct genderswitch_t {
#define NUM_GS 6 #define NUM_GS 6
static const int ittypes = 131; static const int ittypes = 133;
struct itemtype { struct itemtype {
char glyph; char glyph;
@ -131,10 +131,10 @@ enum eItem {
itOrbLava, itOrbMorph, itGlowCrystal, itSnake, itOrbLava, itOrbMorph, itGlowCrystal, itSnake,
itDock, itRuins, itMagnet, itSwitch, itDock, itRuins, itMagnet, itSwitch,
itOrbPhasing, itOrbMagnetism, itOrbSlaying, itOrbPhasing, itOrbMagnetism, itOrbSlaying,
itBrownian itBrownian, itWest, itVarTreasure
}; };
static const int walltypes = 109; static const int walltypes = 111;
struct walltype { struct walltype {
char glyph; char glyph;
@ -175,10 +175,11 @@ enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCav
waTempBridgeBlocked, waTempBridgeBlocked,
waTerraWarrior, waBubble, waTerraWarrior, waBubble,
waArrowTrap, waMercury, waMagma, waArrowTrap, waMercury, waMagma,
waDock, waBurningDock, waRuinWall, waBrownian waDock, waBurningDock, waRuinWall, waBrownian,
waFireTrap, waExplosiveBarrel
}; };
static const int landtypes = 85; static const int landtypes = 87;
struct landtype { struct landtype {
color_t color; color_t color;
@ -205,7 +206,7 @@ enum eLand { laNone, laBarrier, laCrossroads, laDesert, laIce, laCaves, laJungle
laMirrorOld, laMirrorOld,
laVolcano, laBlizzard, laHunting, laTerracotta, laMercuryRiver, laVolcano, laBlizzard, laHunting, laTerracotta, laMercuryRiver,
laDual, laSnakeNest, laDocks, laRuins, laMagnetic, laDual, laSnakeNest, laDocks, laRuins, laMagnetic,
laSwitch, laMemory, laBrownian laSwitch, laMemory, laBrownian, laWestWall, laVariant
}; };
enum eGeometry { enum eGeometry {

View File

@ -2084,6 +2084,10 @@ namespace heat {
newfires.emplace_back(c2, 12); newfires.emplace_back(c2, 12);
else if(cellHalfvine(c2) && last && last->wall == c2->wall) else if(cellHalfvine(c2) && last && last->wall == c2->wall)
newfires.emplace_back(c2, 12); newfires.emplace_back(c2, 12);
else if(c2->wall == waExplosiveBarrel)
newfires.emplace_back(c2, 12);
else if(c2->wall == waFireTrap)
newfires.emplace_back(c2, 12);
// both halfvines have to be near fire at once // both halfvines have to be near fire at once
last = c2; last = c2;
} }
@ -2108,13 +2112,26 @@ namespace heat {
} }
if(c->wparam == 4) c->wparam = 0; if(c->wparam == 4) c->wparam = 0;
} }
if(c->wall == waFireTrap && c->wparam && !shmup::on) {
c->wparam++;
if(c->wparam == 3) {
c->wall = waNone;
explosion(c, 5, 10);
}
}
} }
for(int i=0; i<isize(newfires); i++) { for(int i=0; i<isize(newfires); i++) {
cell* c = newfires[i].first; cell* c = newfires[i].first;
int qty = newfires[i].second; int qty = newfires[i].second;
qty /= 2; qty /= 2;
if(c->wall == waBonfireOff) activateActiv(c, false); if(c->wall == waExplosiveBarrel) explodeBarrel(c);
else if(c->wall == waFireTrap) {
if(c->wparam == 0) c->wparam = 1;
continue;
}
else if(c->wall == waBonfireOff) activateActiv(c, false);
else if(cellHalfvine(c)) destroyHalfvine(c, waPartialFire, 6); else if(cellHalfvine(c)) destroyHalfvine(c, waPartialFire, 6);
else makeflame(c, qty, false); else makeflame(c, qty, false);
if(c->wparam < qty) c->wparam = qty; if(c->wparam < qty) c->wparam = qty;

View File

@ -360,12 +360,16 @@ bool isWall(cell *w) {
w->wall == waSmallBush || w->wall == waBigBush || w->wall == waSmallBush || w->wall == waBigBush ||
w->wall == waReptile || w->wall == waReptileBridge || w->wall == waInvisibleFloor || w->wall == waReptile || w->wall == waReptileBridge || w->wall == waInvisibleFloor ||
w->wall == waSlime1 || w->wall == waSlime2 || w->wall == waArrowTrap || w->wall == waMagma || w->wall == waSlime1 || w->wall == waSlime2 || w->wall == waArrowTrap || w->wall == waMagma ||
w->wall == waDock) w->wall == waDock || w->wall == waFireTrap)
return false; return false;
if(isWatery(w) || isChasmy(w) || isFire(w)) return false; if(isWatery(w) || isChasmy(w) || isFire(w)) return false;
return true; return true;
} }
bool isPushable(eWall w) {
return w == waThumperOn || w == waExplosiveBarrel;
}
bool isAngryBird(eMonster m) { bool isAngryBird(eMonster m) {
return m == moEagle || m == moAlbatross || m == moBomberbird || m == moGargoyle || return m == moEagle || m == moAlbatross || m == moBomberbird || m == moGargoyle ||
m == moWindCrow || m == moSparrowhawk || m == moWindCrow || m == moSparrowhawk ||
@ -412,7 +416,7 @@ bool normalMover(eMonster m) {
m == moHunterGuard || m == moHunterChanging || m == moHunterGuard || m == moHunterChanging ||
m == moIceGolem || m == moIceGolem ||
m == moSwitch1 || m == moSwitch2 || m == moCrusher || m == moPair || m == moSwitch1 || m == moSwitch2 || m == moCrusher || m == moPair ||
m == moBrownBug || m == moBrownBug || m == moVariantWarrior ||
isMagneticPole(m) || isMagneticPole(m) ||
slowMover(m); slowMover(m);
} }
@ -742,7 +746,7 @@ bool hornStuns(cell *c) {
m == moButterfly || m == moGreater || m == moGreaterM || m == moDraugr || m == moButterfly || m == moGreater || m == moGreaterM || m == moDraugr ||
m == moHedge || m == moFlailer || m == moVizier || m == moReptile || m == moSalamander || m == moHedge || m == moFlailer || m == moVizier || m == moReptile || m == moSalamander ||
m == moPair || m == moAltDemon || m == moHexDemon || m == moMonk || m == moCrusher || m == moPair || m == moAltDemon || m == moHexDemon || m == moMonk || m == moCrusher ||
attackJustStuns(c, AF_NORMAL); attackJustStuns(c, AF_NORMAL, moNone);
} }
// generate all the world first in the quotient geometry // generate all the world first in the quotient geometry

102
game.cpp
View File

@ -971,6 +971,10 @@ bool logical_adjacent(cell *c1, eMonster m1, cell *c2) {
return true; return true;
} }
bool arrow_stuns(eMonster m) {
return among(m, moCrusher, moMonk, moAltDemon, moHexDemon, moGreater, moGreaterM, moHedge);
}
bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) { bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) {
// cannot eat worms // cannot eat worms
@ -984,13 +988,15 @@ bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags) {
if(m2 == moPlayer && peace::on) return false; if(m2 == moPlayer && peace::on) return false;
if((flags & AF_MUSTKILL) && attackJustStuns(c2, flags)) if((flags & AF_MUSTKILL) && attackJustStuns(c2, flags, m1))
return false; return false;
if((flags & AF_ONLY_FRIEND) && m2 != moPlayer && !isFriendly(c2)) 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_FBUG) && m2 != moPlayer && !isFriendlyOrBug(c2)) return false;
if((flags & AF_ONLY_ENEMY) && (m2 == moPlayer || isFriendlyOrBug(c2))) return false; if((flags & AF_ONLY_ENEMY) && (m2 == moPlayer || isFriendlyOrBug(c2))) return false;
if(m1 == moArrowTrap && arrow_stuns(m2)) return true;
if(among(m2, moAltDemon, moHexDemon, moPair, moCrusher, moNorthPole, moSouthPole, moMonk) && !(flags & (AF_EAT | AF_MAGIC | AF_BULL | AF_CRUSH))) if(among(m2, moAltDemon, moHexDemon, moPair, moCrusher, moNorthPole, moSouthPole, moMonk) && !(flags & (AF_EAT | AF_MAGIC | AF_BULL | AF_CRUSH)))
return false; return false;
@ -1435,10 +1441,11 @@ bool monstersnear(cell *c, cell *nocount, eMonster who, cell *pushto, cell *come
comefrom->wall = w; comefrom->wall = w;
c->wall = waBigStatue; c->wall = waBigStatue;
} }
else if(who == moPlayer && c->wall == waThumperOn) { else if(who == moPlayer && isPushable(c->wall)) {
eWall w = c->wall;
c->wall = waNone; c->wall = waNone;
b = monstersnear2(); b = monstersnear2();
c->wall = waThumperOn; c->wall = w;
} }
else { else {
b = monstersnear2(); b = monstersnear2();
@ -1532,6 +1539,12 @@ void prespill(cell* c, eWall t, int rad, cell *from) {
c->wall == waBarrowDig || c->wall == waBarrowWall || c->wall == waBarrowDig || c->wall == waBarrowWall ||
c->wall == waMirrorWall) c->wall == waMirrorWall)
return; return;
if(c->wall == waFireTrap) {
if(c->wparam == 0) c->wparam = 1;
return;
}
if(c->wall == waExplosiveBarrel)
explodeBarrel(c);
destroyTrapsOn(c); destroyTrapsOn(c);
// these walls block further spilling // these walls block further spilling
if(c->wall == waCavewall || cellUnstable(c) || c->wall == waSulphur || if(c->wall == waCavewall || cellUnstable(c) || c->wall == waSulphur ||
@ -1788,6 +1801,10 @@ bool makeflame(cell *c, int timeout, bool checkonly) {
if(checkonly) return true; if(checkonly) return true;
c->wall = waLake, HEAT(c) += 1; 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) { else if(c->wall == waFrozenLake) {
if(checkonly) return true; if(checkonly) return true;
drawParticles(c, MELTCOLOR, 8, 8); drawParticles(c, MELTCOLOR, 8, 8);
@ -1827,16 +1844,12 @@ bool makeflame(cell *c, int timeout, bool checkonly) {
return true; return true;
} }
void explodeMine(cell *c) { void explosion(cell *c, int power, int central) {
if(c->wall != waMineMine)
return;
playSound(c, "explosion"); playSound(c, "explosion");
drawFireParticles(c, 30, 150); drawFireParticles(c, 30, 150);
c->wall = waMineOpen;
brownian::dissolve_brownian(c, 2); brownian::dissolve_brownian(c, 2);
makeflame(c, 20, false); makeflame(c, central, false);
forCellEx(c2, c) { forCellEx(c2, c) {
destroyTrapsOn(c2); destroyTrapsOn(c2);
@ -1845,7 +1858,7 @@ void explodeMine(cell *c) {
c2->wall = waRed1; c2->wall = waRed1;
else if(c2->wall == waDeadTroll || c2->wall == waDeadTroll2 || c2->wall == waPetrified || c2->wall == waGargoyle) { else if(c2->wall == waDeadTroll || c2->wall == waDeadTroll2 || c2->wall == waPetrified || c2->wall == waGargoyle) {
c2->wall = waNone; c2->wall = waNone;
makeflame(c2, 10, false); makeflame(c2, power/2, false);
} }
else if(c2->wall == waPetrifiedBridge || c2->wall == waGargoyleBridge) { else if(c2->wall == waPetrifiedBridge || c2->wall == waGargoyleBridge) {
placeWater(c, c); placeWater(c, c);
@ -1853,7 +1866,7 @@ void explodeMine(cell *c) {
else if(c2->wall == waPalace || c2->wall == waOpenGate || c2->wall == waClosedGate || 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 == waSandstone || c2->wall == waMetal || c2->wall == waSaloon || c2->wall == waRuinWall) {
c2->wall = waNone; c2->wall = waNone;
makeflame(c2, 10, false); makeflame(c2, power/2, false);
} }
else if(c2->wall == waTower) else if(c2->wall == waTower)
c2->wall = waRubble; c2->wall = waRubble;
@ -1861,10 +1874,32 @@ void explodeMine(cell *c) {
c2->wall = waBarrowDig; c2->wall = waBarrowDig;
else if(c2->wall == waBarrowDig) else if(c2->wall == waBarrowDig)
c2->wall = waNone; c2->wall = waNone;
else makeflame(c2, 20, false); else if(c2->wall == waFireTrap) {
if(c2->wparam == 0)
c2->wparam = 1;
}
else if(c2->wall == waExplosiveBarrel)
explodeBarrel(c2);
else makeflame(c2, power, false);
} }
} }
void explodeMine(cell *c) {
if(c->wall != waMineMine)
return;
c->wall = waMineOpen;
explosion(c, 20, 20);
}
void explodeBarrel(cell *c) {
if(c->wall != waExplosiveBarrel)
return;
c->wall = waNone;
explosion(c, 20, 20);
}
bool mayExplodeMine(cell *c, eMonster who) { bool mayExplodeMine(cell *c, eMonster who) {
if(c->wall != waMineMine) return false; if(c->wall != waMineMine) return false;
if(ignoresPlates(who)) return false; if(ignoresPlates(who)) return false;
@ -1872,7 +1907,7 @@ bool mayExplodeMine(cell *c, eMonster who) {
return true; return true;
} }
void stunMonster(cell *c2) { void stunMonster(cell *c2, eMonster killer, flagtype flags) {
int newtime = ( int newtime = (
c2->monst == moFatGuard ? 2 : c2->monst == moFatGuard ? 2 :
c2->monst == moSkeleton && c2->land != laPalace && c2->land != laHalloween ? 7 : c2->monst == moSkeleton && c2->land != laPalace && c2->land != laHalloween ? 7 :
@ -1892,6 +1927,7 @@ void stunMonster(cell *c2) {
c2->monst == moSalamander ? 6 : c2->monst == moSalamander ? 6 :
c2->monst == moBrownBug ? 3 : c2->monst == moBrownBug ? 3 :
3); 3);
if(killer == moArrowTrap) newtime = min(newtime + 3, 7);
if(!isMetalBeast(c2->monst) && !among(c2->monst, moSkeleton, moReptile, moSalamander, moTortoise, moBrownBug)) { if(!isMetalBeast(c2->monst) && !among(c2->monst, moSkeleton, moReptile, moSalamander, moTortoise, moBrownBug)) {
c2->hitpoints--; c2->hitpoints--;
if(c2->monst == moPrincess) if(c2->monst == moPrincess)
@ -1902,9 +1938,11 @@ void stunMonster(cell *c2) {
checkStunKill(c2); checkStunKill(c2);
} }
bool attackJustStuns(cell *c2, flagtype f) { bool attackJustStuns(cell *c2, flagtype f, eMonster attacker) {
if(f & AF_HORNS) if(f & AF_HORNS)
return hornStuns(c2); return hornStuns(c2);
else if(attacker == moArrowTrap && arrow_stuns(c2->monst))
return true;
else if((f & AF_SWORD) && c2->monst == moSkeleton) else if((f & AF_SWORD) && c2->monst == moSkeleton)
return false; return false;
else if(f & (AF_CRUSH | AF_MAGIC | AF_FALL | AF_EAT | AF_GUN)) else if(f & (AF_CRUSH | AF_MAGIC | AF_FALL | AF_EAT | AF_GUN))
@ -2427,7 +2465,7 @@ bool attackMonster(cell *c, flagtype flags, eMonster killer) {
int tkt = killtypes(); int tkt = killtypes();
bool dostun = attackJustStuns(c, flags); bool dostun = attackJustStuns(c, flags, killer);
if((flags & AF_BULL) && c->monst == moVizier && c->hitpoints > 1) { if((flags & AF_BULL) && c->monst == moVizier && c->hitpoints > 1) {
dostun = true; dostun = true;
@ -2439,7 +2477,7 @@ bool attackMonster(cell *c, flagtype flags, eMonster killer) {
if(flags & AF_MSG) fightmessage(m, killer, dostun, flags); if(flags & AF_MSG) fightmessage(m, killer, dostun, flags);
if(dostun) if(dostun)
stunMonster(c); stunMonster(c, killer, flags);
else else
killMonster(c, killer, flags); killMonster(c, killer, flags);
@ -3323,6 +3361,9 @@ void moveEffect(cell *ct, cell *cf, eMonster m, int direction_hint) {
if(ct->wall == waArrowTrap && !ignoresPlates(m)) if(ct->wall == waArrowTrap && !ignoresPlates(m))
activateArrowTrap(ct); activateArrowTrap(ct);
if(ct->wall == waFireTrap && !ignoresPlates(m) && ct->wparam == 0)
ct->wparam = 1;
if(cf && isPrincess(m)) princess::move(ct, cf); if(cf && isPrincess(m)) princess::move(ct, cf);
@ -3405,6 +3446,9 @@ void playerMoveEffects(cell *c1, cell *c2) {
if(c2->wall == waArrowTrap && c2->wparam == 0 && !markOrb(itOrbAether)) if(c2->wall == waArrowTrap && c2->wparam == 0 && !markOrb(itOrbAether))
activateArrowTrap(c2); activateArrowTrap(c2);
if(c2->wall == waFireTrap && c2->wparam == 0 && !markOrb(itOrbAether))
c2->wparam = 1;
princess::playernear(c2); princess::playernear(c2);
@ -3451,6 +3495,10 @@ void beastcrash(cell *c, cell *beast) {
c->wall = waThumperOn; c->wall = waThumperOn;
c->wparam = 100; c->wparam = 100;
} }
else if(c->wall == waExplosiveBarrel) {
addMessage(XLAT("%The1 crashes into %the2!", beast->monst, c->wall));
explodeBarrel(c);
}
else if(isBull(c->monst) || isSwitch(c->monst)) { else if(isBull(c->monst) || isSwitch(c->monst)) {
addMessage(XLAT("%The1 crashes into %the2!", beast->monst, c->monst)); addMessage(XLAT("%The1 crashes into %the2!", beast->monst, c->monst));
if(c->monst == moSleepBull) c->monst = moRagingBull, c->stuntime = 3; if(c->monst == moSleepBull) c->monst = moRagingBull, c->stuntime = 3;
@ -4031,6 +4079,10 @@ void beastAttack(cell *c, bool player) {
playSound(c2, "click"); playSound(c2, "click");
c2->wall = waThumperOn; c2->wall = waThumperOn;
} }
if(c2->wall == waExplosiveBarrel) {
playSound(c2, "click");
explodeBarrel(c2);
}
if(c2->wall == waThumperOn) { if(c2->wall == waThumperOn) {
cellwalker bull (c, d); cellwalker bull (c, d);
int subdir = determinizeBullPush(bull); int subdir = determinizeBullPush(bull);
@ -4143,6 +4195,7 @@ void explodeAround(cell *c) {
destroyHalfvine(c2); destroyHalfvine(c2);
c2->wall = waNone; c2->wall = waNone;
} }
if(c2->wall == waExplosiveBarrel) explodeBarrel(c2);
if(c2->wall == waCavewall || c2->wall == waDeadTroll) c2->wall = waCavefloor; if(c2->wall == waCavewall || c2->wall == waDeadTroll) c2->wall = waCavefloor;
if(c2->wall == waDeadTroll2) c2->wall = waNone; if(c2->wall == waDeadTroll2) c2->wall = waNone;
if(c2->wall == waPetrified) c2->wall = waNone; if(c2->wall == waPetrified) c2->wall = waNone;
@ -4482,7 +4535,7 @@ void moveivy() {
if(isPlayerOn(c->move(j))) if(isPlayerOn(c->move(j)))
killThePlayerAt(c->monst, c->move(j), 0); killThePlayerAt(c->monst, c->move(j), 0);
else { else {
if(attackJustStuns(c->move(j), 0)) if(attackJustStuns(c->move(j), 0, c->monst))
addMessage(XLAT("The ivy attacks %the1!", c->move(j)->monst)); addMessage(XLAT("The ivy attacks %the1!", c->move(j)->monst));
else if(isNonliving(c->move(j)->monst)) else if(isNonliving(c->move(j)->monst))
addMessage(XLAT("The ivy destroys %the1!", c->move(j)->monst)); addMessage(XLAT("The ivy destroys %the1!", c->move(j)->monst));
@ -7333,24 +7386,29 @@ void pushThumper(cell *th, cell *cto) {
if(th->land == laAlchemist) if(th->land == laAlchemist)
th->wall = isAlch(cwt.at) ? cwt.at->wall : cto->wall; th->wall = isAlch(cwt.at) ? cwt.at->wall : cto->wall;
else th->wall = waNone; else th->wall = waNone;
int explode = 0;
if(cto->wall == waArrowTrap && w == waExplosiveBarrel ) explode = max<int>(cto->wparam, 1);
if(cto->wall == waFireTrap) explode = max<int>(cto->wparam, 1);
destroyTrapsOn(cto);
if(cto->wall == waOpenPlate || cto->wall == waClosePlate) { if(cto->wall == waOpenPlate || cto->wall == waClosePlate) {
toggleGates(cto, cto->wall); toggleGates(cto, cto->wall);
addMessage(XLAT("%The1 destroys %the2!", waThumperOn, cto->wall)); addMessage(XLAT("%The1 destroys %the2!", w, cto->wall));
} }
if(cellUnstable(cto) && cto->land == laMotion) { if(cellUnstable(cto) && cto->land == laMotion) {
addMessage(XLAT("%The1 falls!", waThumperOn)); addMessage(XLAT("%The1 falls!", w));
doesFallSound(cto); doesFallSound(cto);
} }
else if(cellUnstableOrChasm(cto)) { else if(cellUnstableOrChasm(cto)) {
addMessage(XLAT("%The1 fills the hole!", waThumperOn)); addMessage(XLAT("%The1 fills the hole!", w));
cto->wall = waTempFloor; cto->wall = waTempFloor;
} }
else if(isWatery(cto)) { else if(isWatery(cto)) {
addMessage(XLAT("%The1 fills the hole!", waThumperOn)); addMessage(XLAT("%The1 fills the hole!", w));
cto->wall = waTempBridge; cto->wall = waTempBridge;
} }
else else
cto->wall = w; cto->wall = w;
if(explode) cto->wall = waFireTrap, cto->wparam = explode;
} }
bool canPushThumperOn(cell *tgt, cell *thumper, cell *player) { bool canPushThumperOn(cell *tgt, cell *thumper, cell *player) {
@ -7562,7 +7620,7 @@ bool movepcto(int d, int subdir, bool checkonly) {
return true; return true;
} }
if(c2->wall == waThumperOn && !c2->monst && !nonAdjacentPlayer(c2, cwt.at)) { if(isPushable(c2->wall) && !c2->monst && !nonAdjacentPlayer(c2, cwt.at)) {
int pushdir; int pushdir;
cell *c3 = determinePush(cwt, c2, subdir, [c2] (cell *c) { return canPushThumperOn(c, c2, cwt.at); }, pushdir); cell *c3 = determinePush(cwt, c2, subdir, [c2] (cell *c) { return canPushThumperOn(c, c2, cwt.at); }, pushdir);
if(c3 == c2) { if(c3 == c2) {

View File

@ -1302,6 +1302,13 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, color_t col,
} }
else if(m == moTerraWarrior) else if(m == moTerraWarrior)
drawTerraWarrior(V, 7, (where ? where->hitpoints : 7), footphase); drawTerraWarrior(V, 7, (where ? where->hitpoints : 7), footphase);
else if(m == moVariantWarrior) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBS, shPBody, darkena(0xFFD500, 0, 0xF0));
if(!peace::on) queuepoly(VBS, shPSword, 0xFFFF00FF);
queuepoly(VHEAD, shHood, 0x008000FF);
}
else if(m == moDesertman) { else if(m == moDesertman) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase); const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, shPBody); ShadowV(V, shPBody);
@ -2684,7 +2691,7 @@ void drawMovementArrows(cell *c, transmatrix V) {
poly_outline = OUTLINE_DEFAULT; poly_outline = OUTLINE_DEFAULT;
queuepoly(fixrot * spin(-d * M_PI/4), shArrow, col); queuepoly(fixrot * spin(-d * M_PI/4), shArrow, col);
if((c->type & 1) && (isStunnable(c->monst) || c->wall == waThumperOn)) { if((c->type & 1) && (isStunnable(c->monst) || isPushable(c->wall))) {
transmatrix Centered = rgpushxto0(tC0(cwtV)); transmatrix Centered = rgpushxto0(tC0(cwtV));
int sd = md.subdir; int sd = md.subdir;
queuepoly(inverse(Centered) * rgpushxto0(Centered * tC0(V)) * rspintox(Centered*tC0(V)) * spin(-sd * M_PI/S7) * xpush(0.2), shArrow, col); queuepoly(inverse(Centered) * rgpushxto0(Centered * tC0(V)) * rspintox(Centered*tC0(V)) * spin(-sd * M_PI/S7) * xpush(0.2), shArrow, col);
@ -2830,6 +2837,19 @@ void setcolors(cell *c, color_t& wcol, color_t& fcol) {
case laAlchemist: case laAlchemist:
fcol = floorcolors[c->land]; break; fcol = floorcolors[c->land]; break;
case laVariant: {
int b = getBits(c);
fcol = 0x404040;
for(int a=0; a<21; a++)
if((b >> a) & 1)
fcol += variant_features[a].color_change;
if(c->wall == waAncientGrave)
wcol = 0x080808;
else if(c->wall == waFreshGrave)
wcol = 0x202020;
break;
}
case laRuins: case laRuins:
fcol = pseudohept(c) ? 0xC0E0C0 : 0x40A040; fcol = pseudohept(c) ? 0xC0E0C0 : 0x40A040;
break; break;
@ -3134,7 +3154,7 @@ void setcolors(cell *c, color_t& wcol, color_t& fcol) {
switch(c->wall) { switch(c->wall) {
case waSulphur: case waSulphurC: case waPlatform: case waMercury: case waDock: case waSulphur: case waSulphurC: case waPlatform: case waMercury: case waDock:
case waAncientGrave: case waFreshGrave: case waThumperOn: case waThumperOff: case waBonfireOff: case waAncientGrave: case waFreshGrave: case waThumperOn: case waThumperOff: case waBonfireOff:
case waRoundTable: case waRoundTable: case waExplosiveBarrel:
// floors become fcol // floors become fcol
fcol = wcol; fcol = wcol;
break; break;
@ -4525,7 +4545,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
case waClosePlate: case waOpenPlate: { case waClosePlate: case waOpenPlate: {
transmatrix V2 = V; transmatrix V2 = V;
if(wmescher && geosupport_football() == 2 && pseudohept(c)) V2 = V * spin(M_PI / c->type); if(wmescher && geosupport_football() == 2 && pseudohept(c) && c->land == laPalace) V2 = V * spin(M_PI / c->type);
draw_floorshape(c, V2, shMFloor, darkena(winf[c->wall].color, 0, 0xFF)); draw_floorshape(c, V2, shMFloor, darkena(winf[c->wall].color, 0, 0xFF));
draw_floorshape(c, V2, shMFloor2, (!wmblack) ? darkena(fcol, 1, 0xFF) : darkena(0,1,0xFF)); draw_floorshape(c, V2, shMFloor2, (!wmblack) ? darkena(fcol, 1, 0xFF) : darkena(0,1,0xFF));
break; break;
@ -4586,6 +4606,13 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
if(isCentralTrap(c)) arrowtraps.push_back(c); if(isCentralTrap(c)) arrowtraps.push_back(c);
break; break;
case waFireTrap:
draw_floorshape(c, V, shMFloor, darkena(0xC00000, 0, 0xFF));
draw_floorshape(c, V, shMFloor2, darkena(0x600000, 0, 0xFF));
if(c->wparam >= 1)
queuepoly(V, shDisk, darkena(trapcol[c->wparam&3], 0, 0xFF));
break;
case waGiantRug: case waGiantRug:
queuepoly(V, shBigCarpet1, darkena(0xC09F00, 0, 0xFF)); queuepoly(V, shBigCarpet1, darkena(0xC09F00, 0, 0xFF));
queuepoly(V, shBigCarpet2, darkena(0x600000, 0, 0xFF)); queuepoly(V, shBigCarpet2, darkena(0x600000, 0, 0xFF));
@ -4650,6 +4677,14 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
poly_outline = OUTLINE_DEFAULT; poly_outline = OUTLINE_DEFAULT;
} }
else if(c->wall == waExplosiveBarrel) {
const int layers = 2 << detaillevel;
for(int z=1; z<=layers; z++) {
double zg = zgrad0(0, geom3::actual_wall_height(), z, layers);
queuepolyat(xyzscale(V, zg, zg), shBarrel, darkena((z&1) ? 0xFF0000 : 0xC00000, 0, 0xFF), PPR(PPR::REDWALLm+z));
}
}
else if(isFire(c) || isThumper(c) || c->wall == waBonfireOff) { else if(isFire(c) || isThumper(c) || c->wall == waBonfireOff) {
auto V2 = V; auto V2 = V;
if(hasTimeout(c)) V2 = V2 * spintick(c->land == laPower ? 5000 : 500); if(hasTimeout(c)) V2 = V2 * spintick(c->land == laPower ? 5000 : 500);
@ -4667,6 +4702,8 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
else { else {
if(c->wall == waArrowTrap) if(c->wall == waArrowTrap)
asciicol = trapcol[c->wparam & 3]; asciicol = trapcol[c->wparam & 3];
if(c->wall == waFireTrap)
asciicol = trapcol[c->wparam & 3];
if(c->wall == waTerraWarrior) if(c->wall == waTerraWarrior)
asciicol = terracol[c->landparam & 7]; asciicol = terracol[c->landparam & 7];

View File

@ -1726,7 +1726,7 @@ namespace sword {
} }
void killThePlayer(eMonster m, int id, flagtype flags); void killThePlayer(eMonster m, int id, flagtype flags);
bool attackJustStuns(cell *c2, flagtype flags); bool attackJustStuns(cell *c2, flagtype flags, eMonster attacker);
bool isTargetOrAdjacent(cell *c); bool isTargetOrAdjacent(cell *c);
bool warningprotection(const string& s); bool warningprotection(const string& s);
@ -2079,6 +2079,8 @@ extern eGlyphsortorder glyphsortorder;
void explodeMine(cell *c); void explodeMine(cell *c);
bool mayExplodeMine(cell *c, eMonster who); bool mayExplodeMine(cell *c, eMonster who);
void explosion(cell *c, int power, int central);
void explodeBarrel(cell *c);
int gravityLevel(cell *c); int gravityLevel(cell *c);
void fullcenter(); void fullcenter();

View File

@ -105,6 +105,83 @@ eMonster genRuinMonster(cell *c) {
void gen_brownian(cell *c); void gen_brownian(cell *c);
void createArrowTrapAt(cell *c, eLand land) {
cellwalker cw(c, hrand(c->type));
cell* cc[5];
cc[2] = c;
cellwalker cw2 = cw;
cw += wstep; cc[3] = cw.at; cw += revstep; cc[4] = cw.at;
cw2 += revstep; cc[1] = cw2.at; cw2 += revstep; cc[0] = cw2.at;
bool ok = true;
for(int i=0; i<5; i++) {
if(cc[i]->land != laNone && cc[i]->land != land) ok = false;
if(cc[i]->bardir != NODIR) ok = false;
}
for(int i=1; i<4; i++) {
forCellEx(c2, cc[i]) if(!among(c2->wall, waNone, waStone)) ok = false;
}
if(ok) {
if(!racing::on) for(int i=1; i<4; i++)
cc[i]->wall = waArrowTrap,
cc[i]->wparam = 0;
for(int i=0; i<5; i++)
cc[i]->bardir = NOBARRIERS;
cc[0]->wall = waStone;
cc[4]->wall = waStone;
}
}
void build_pool(cell *c, bool with_boat) {
bool vacant = true;
if(c->monst || !among(c->wall, waNone, waSea, waBoat)) vacant = false;
forCellCM(c1, c) if(!among(c1->land, laNone, laVariant) || c1->monst || !among(c1->wall, waNone, waSea, waBoat)) vacant = false;
if(vacant) {
c->wall = waSea;
forCellEx(c1, c) if(c1->wall != waBoat) c1->wall = waSea;
if(with_boat) c->move(hrand(c->type))->wall = waBoat;
}
}
struct variant_feature {
color_t color_change;
int rate_change;
void (*build)(cell*);
};
#define VF [] (cell *c)
const array<variant_feature, 21> variant_features {{
variant_feature{(color_t)(-0x202020), 5, VF {
if(hrand(1500) < 20) c->wall = waFreshGrave;
if(hrand(20000) < 10 + items[itVarTreasure])
c->monst = moNecromancer;
}},
{0x000010, 5, VF { if(c->wall == waNone && !c->monst && hrand(80000) < 25 + items[itVarTreasure]) c->monst = moLancer; } },
{0x100008,15, VF { if(c->wall == waNone && !c->monst && hrand(80000) < 25 + items[itVarTreasure]) c->monst = moMonk; } },
{0x080010, 5, VF { if(c->wall == waNone && !c->monst && hrand(80000) < 25 + items[itVarTreasure]) c->monst = moCrusher; } },
{0x181418, 5, VF { if(c->wall == waNone && !c->monst && hrand(80000) < 25 + items[itVarTreasure]) c->monst = moSkeleton, c->hitpoints = 3; } },
{0x180000, 5, VF { if(c->wall == waNone && !c->monst && hrand(80000) < 25 + items[itVarTreasure]) c->monst = moPyroCultist; } },
{0x00000C, 2, VF { if(c->wall == waNone && !c->monst && hrand(80000) < 25 + items[itVarTreasure]) c->monst = moFlailer; } },
{0x1C0800, 1, VF { if(c->wall == waNone && !c->monst && hrand(80000) < 25 + items[itVarTreasure] && VALENCE == 3) c->monst = moHedge; } },
{0x001000,-1, VF { if(hrand(1500) < 30) createArrowTrapAt(c, laVariant); } },
{0x001400,-1, VF { if(hrand(1500) < 50 && c->wall == waNone) c->wall = waTrapdoor; } },
{0x001018,-1, VF { if(hrand(1500) < 30) build_pool(c, true); } },
{0x040C00,-1, VF { if(c->wall == waNone && !c->monst && !c->monst && hrand(1500) < 10) c->wall = waThumperOff; } },
{0x080C00,-1, VF { if(hrand(1500) < 20 && !c->monst && !c->wall) c->wall = waFireTrap; } },
{0x0C0C00, 0, VF { if(c->wall == waNone && !c->monst && hrand(5000) < 100) c->wall = waExplosiveBarrel; } },
{0x061004, 0, VF {
if(c->wall == waNone && !c->monst && pseudohept(c) && hrand(30000) < 25 + items[itVarTreasure])
if(buildIvy(c, 0, c->type) && !peace::on) c->item = itVarTreasure;
}},
{0x000C08, 0, VF { if(c->wall == waNone && !c->monst && hrand(5000) < 100) c->wall = waSmallTree; }},
{0x100C10, 1, VF { if(c->wall == waNone && hrand(10000) < 10 + items[itVarTreasure]) c->monst = moSleepBull, c->hitpoints = 3; }},
{0x00140C, 0, VF { if(c->wall == waNone && !c->monst && hrand(5000) < 100) c->wall = waBigTree; }},
{0x000C28, 1, VF { if(hrand(500) < 10) build_pool(c, false); } },
{0x100C00, 2, VF { if(c->wall == waNone && !c->monst && hrand(40000) < 25 + items[itVarTreasure]) c->monst = moVariantWarrior; }},
{0x100808, 1, VF { if(c->wall == waNone && !c->monst && hrand(50000) < 25 + items[itVarTreasure]) c->monst = moRatling; }}
}};
#undef VF
void giantLandSwitch(cell *c, int d, cell *from) { void giantLandSwitch(cell *c, int d, cell *from) {
switch(c->land) { switch(c->land) {
@ -940,31 +1017,8 @@ void giantLandSwitch(cell *c, int d, cell *from) {
case laTerracotta: case laTerracotta:
if(d == 9) { if(d == 9) {
if(hrand(500) < 15) { if(hrand(500) < 15)
cellwalker cw(c, hrand(c->type)); createArrowTrapAt(c, laTerracotta);
cell* cc[5];
cc[2] = c;
cellwalker cw2 = cw;
cw += wstep; cc[3] = cw.at; cw += revstep; cc[4] = cw.at;
cw2 += revstep; cc[1] = cw2.at; cw2 += revstep; cc[0] = cw2.at;
bool ok = true;
for(int i=0; i<5; i++) {
if(cc[i]->land != laNone && cc[i]->land != laTerracotta) ok = false;
if(cc[i]->bardir != NODIR) ok = false;
}
for(int i=1; i<4; i++) {
forCellEx(c2, cc[i]) if(c2->wall == waArrowTrap) ok = false;
}
if(ok) {
if(!racing::on) for(int i=1; i<4; i++)
cc[i]->wall = waArrowTrap,
cc[i]->wparam = 0;
for(int i=0; i<5; i++)
cc[i]->bardir = NOBARRIERS;
cc[0]->wall = waStone;
cc[4]->wall = waStone;
}
}
if(pseudohept(c) && hrand(100) < 40 && c->wall == waNone && !racing::on) { if(pseudohept(c) && hrand(100) < 40 && c->wall == waNone && !racing::on) {
c->wall = waTerraWarrior; c->wall = waTerraWarrior;
c->landparam = randterra ? 0 : 3 + hrand(3); c->landparam = randterra ? 0 : 3 + hrand(3);
@ -2234,6 +2288,24 @@ void giantLandSwitch(cell *c, int d, cell *from) {
} }
break; break;
case laVariant: {
int b = getBits(c);
if(d == 9) {
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);
}
if(hrand(2000 - PT(kills[moVariantWarrior] * 5, 250)) < treasure_rate && !c->wall && !c->monst)
c->item = itVarTreasure;
}
if(d == 7 && c->wall == waTrapdoor) {
forCellEx(c1, c) if(among(c1->wall, waSea, waBoat))
c->wall = waNone;
}
break;
}
case laNone: case laNone:
case laBarrier: case laBarrier:
case laOceanWall: case laOceanWall:

View File

@ -223,6 +223,12 @@ int isNative(eLand l, eMonster m) {
return among(m, moPair, moHexDemon, moAltDemon, moMonk, moCrusher) ? 2 : return among(m, moPair, moHexDemon, moAltDemon, moMonk, moCrusher) ? 2 :
m == moSkeleton ? 1 : 0; m == moSkeleton ? 1 : 0;
case laVariant:
return
m == moVariantWarrior ? 2 :
among(m, moMonk, moCrusher, moSkeleton, moHedge, moLancer, moFlailer, moCultist, moPyroCultist, moNecromancer, moGhost, moZombie, moRatling) ? 1 :
isIvy(m) ? 1 : 0;
case laMagnetic: case laMagnetic:
return isMagneticPole(m) ? 2 : 0; return isMagneticPole(m) ? 2 : 0;
} }
@ -326,6 +332,8 @@ eItem treasureType(eLand l) {
case laRuins: return itRuins; case laRuins: return itRuins;
case laSwitch: return itSwitch; case laSwitch: return itSwitch;
case laMagnetic: return itMagnet; case laMagnetic: return itMagnet;
case laVariant: return itVarTreasure;
case laWestWall: return itWest;
case laCA: return itNone; case laCA: return itNone;
} }

View File

@ -1507,6 +1507,7 @@ hpcshape
shBFloor[2], shBFloor[2],
shWave[8][2], shWave[8][2],
shCircleFloor, shCircleFloor,
shBarrel,
shWall[2], shMineMark[2], shFan, shWall[2], shMineMark[2], shFan,
shZebra[5], shZebra[5],
shSwitchDisk, shSwitchDisk,
@ -1892,6 +1893,9 @@ void buildpolys() {
chasmifyPoly(dlow, dhi, k); chasmifyPoly(dlow, dhi, k);
} }
bshape(shBarrel, PPR::FLOOR);
for(int t=0; t<=S84; t+=2) hpcpush(ddi(t, floorrad1*.5) * C0);
bshape(shCircleFloor, PPR::FLOOR); bshape(shCircleFloor, PPR::FLOOR);
for(int t=0; t<=S84; t+=2) hpcpush(ddi(t, floorrad1*.9) * C0); for(int t=0; t<=S84; t+=2) hpcpush(ddi(t, floorrad1*.9) * C0);