From fe34a4a5555890fc28d83e64729f98f216218ea2 Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Sat, 30 Sep 2017 11:46:41 +0200 Subject: [PATCH] new lands for 10.1 -- initial commit --- classes.cpp | 54 +++++-- complex.cpp | 47 ++++-- debug.cpp | 1 + flags.cpp | 25 +++- game.cpp | 402 ++++++++++++++++++++++++++++++++++++++++++++-------- graph.cpp | 166 +++++++++++++++++----- hyper.cpp | 10 ++ hyper.h | 141 +++++++++--------- landgen.cpp | 134 +++++++++++++++++- orbs.cpp | 6 +- 10 files changed, 793 insertions(+), 193 deletions(-) diff --git a/classes.cpp b/classes.cpp index 160b5b44..2948eb77 100644 --- a/classes.cpp +++ b/classes.cpp @@ -346,7 +346,7 @@ const char *mirroreddesc = "Mirror walls reflect Mimics, lightning bolts, and " "missiles perfectly."; -const int motypes = 141; +const int motypes = 150; struct monstertype { char glyph; @@ -696,6 +696,14 @@ monstertype minf[motypes] = { "If you attack a Mirror Spirit physically, it is delayed, but not destroyed -- " "more reflections will come out of the mirror. Use Mimics to destroy them." }, + { 'W', 0x202020, "Hunting Dog", NODESC}, + { 'T', 0xA0A0A0, "Terracotta Warrior", NODESC}, + { 'H', 0xA0A0A0, "Mercury Warrior", NODESC}, + { 'B', 0xA00000, "Void Beast", NODESC}, + { 'L', 0xA00000, "Lemur", NODESC}, + { 'W', 0x202020, "Hunting Dog (guarding)", NODESC}, + { 'G', 0xC0C0FF, "Ice Golem", NODESC}, + { 'B', 0xC0C0FF, "Sand Bird", NODESC}, // shmup specials { '@', 0xC0C0C0, "Rogue", "In the Shoot'em Up mode, you are armed with thrown Knives."}, @@ -706,10 +714,11 @@ monstertype minf[motypes] = { { '*', 0xFFFFFF, "Airball", "This magical missile pushes back whatever it hits."}, // technical { '?', 0x00C000, "dead bug", NODESC}, - { '?', 0xFFFF00, "electric discharge", NODESC}, // appears as 'killed by electrocution' + { '?', 0xFFFF00, "electric discharge", NODESC}, // appears as 'killed by electric discharge' { '?', 0xE06000, "dead bird", NODESC}, { '?', 0xE06000, "Energy Sword", NODESC}, { '!', 0xFF0000, "Warning", warningdesc}, + { '!', 0xFF0000, "arrow trap", NODESC}, { '*', 0, "vertex", "A vertex from rogueviz."} }; @@ -756,10 +765,12 @@ enum eMonster { moVampire, moBat, moReptile, moHerdBull, moRagingBull, moSleepBull, moButterfly, moNarciss, moMirrorSpirit, + moHunterDog, moTerraWarrior, moMercuryGuy, moVoidBeast, moLemur, moHunterGuard, + moIceGolem, moSandBird, // shmup specials moPlayer, moBullet, moFlailBullet, moFireball, moTongue, moAirball, // temporary - moDeadBug, moLightningBolt, moDeadBird, moEnergySword, moWarning, + moDeadBug, moLightningBolt, moDeadBird, moEnergySword, moWarning, moArrowTrap, moRogueviz }; @@ -790,7 +801,7 @@ genderswitch_t genderswitch[NUM_GS] = { // --- items --- -const int ittypes = 112; +const int ittypes = 119; struct itemtype { char glyph; @@ -1183,6 +1194,13 @@ itemtype iinf[ittypes] = { }, { 'O', 0xF0F0F0, "your orbs", "Click this to see your orbs."}, + { '$', 0xD0D000, "Sage's Stone", NODESCYET}, + { '*', 0x40E0D0, "Turquoise", NODESCYET}, + { '*', 0x009090, "Forgotten Relic", NODESCYET}, + { '*', 0x009090, "Warstone", NODESCYET}, + { 'o', 0x307080, "Orb of the Side I", NODESCYET}, + { 'o', 0x30A080, "Orb of the Side II", NODESCYET}, + { 'o', 0x30D080, "Orb of the Side III", NODESCYET}, }; enum eItem { itNone, itDiamond, itGold, itSpice, itRuby, itElixir, itShard, itBone, itHell, itStatue, @@ -1216,12 +1234,14 @@ enum eItem { itNone, itDiamond, itGold, itSpice, itRuby, itElixir, itShard, itBo itTrollEgg, itWarning, itOrbStone, itOrbNature, itTreat, itSlime, itAmethyst, itOrbRecall, itDodeca, itOrbDash, itGreenGrass, itOrbHorns, itOrbBull, itBull, itOrbMirror, - itInventory + itInventory, + itAlchemy2, itDogPlains, itBlizzard, itTerra, + itOrbSide1, itOrbSide2, itOrbSide3 }; // --- wall types --- -const int walltypes = 100; +const int walltypes = 103; struct walltype { char glyph; @@ -1372,8 +1392,8 @@ walltype winf[walltypes] = { { '?', 0xFF00FF, "", NODESC}, { '?', 0xFF00FF, "", NODESC}, - { '+', 0x607030, "unnamed floor C", NODESC}, - { '+', 0xC0C0FF, "unnamed floor D", NODESC}, + { '+', 0x00F000, "green slime", NODESC}, + { '+', 0xF0F000, "yellow slime", NODESC}, { '#', 0x764e7c, "rosebush", roselanddesc}, { '#', 0xC0C000, "warp gate", "This gate separates the warped area from the normal land."}, @@ -1404,6 +1424,9 @@ walltype winf[walltypes] = { { '#', 0xC0C0FF, "mirror wall", mirroreddesc}, { '.', 0xE0E0E0, "stepping stones", "A petrified creature."}, { '#', 0x309060, "temporary wall", twdesc}, + { 'S', 0xB0B0B0, "warrior statue", NODESCYET}, + { '=', 0xB0B0B0, "bubbling slime", NODESCYET}, + { '^', 0xD00000, "arrow trap", NODESCYET}, }; enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCavefloor, waDeadTroll, waDune, @@ -1426,7 +1449,7 @@ enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCav waCharged, waGrounded, waSandstone, waSaloon, waMetal, waDeadTroll2, waFan, waTemporary, waEarthD, waElementalTmp, waElementalD, - waFloorC, waFloorD, waRose, waWarpGate, + waSlime1, waSlime2, waRose, waWarpGate, waTrunk, waSolidBranch, waWeakBranch, waCanopy, waBarrowWall, waBarrowDig, waPetrified, waTower, @@ -1435,12 +1458,14 @@ enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCav waInvisibleFloor, waMirrorWall, waPetrifiedBridge, - waTempBridgeBlocked + waTempBridgeBlocked, + waTerraWarrior, waBubble, + waArrowTrap }; // --- land types --- -const int landtypes = 73; +const int landtypes = 76; struct landtype { int color; @@ -1623,7 +1648,10 @@ const landtype linf[landtypes] = { { 0xC8C8FF, "Reflection", mirroreddesc}, { 0xC8C8FF, "Mirror Land", "A strange land which contains mirrors and mirages, protected by Mirror Rangers."}, - { 0xFFFFFF, "Alchemy II", NODESCYET} + { 0xA06000, "Alchemy II", NODESCYET}, + { 0x8080FF, "Blizzard", NODESCYET}, + { 0x207068, "Hunting Ground", NODESCYET}, + { 0x888888, "Terracotta Army", NODESCYET} }; enum eLand { laNone, laBarrier, laCrossroads, laDesert, laIce, laCaves, laJungle, laAlchemist, laMirror, laGraveyard, @@ -1643,7 +1671,7 @@ enum eLand { laNone, laBarrier, laCrossroads, laDesert, laIce, laCaves, laJungle laPrairie, laBull, laCrossroads5, laCA, laMirrorWall, laMirrored, laMirrorWall2, laMirrored2, laMirrorOld, - laAlchemy2 + laAlchemy2, laBlizzard, laDogPlains, laTerracotta }; // cell information for the game diff --git a/complex.cpp b/complex.cpp index bdc2f030..8aeb04f7 100644 --- a/complex.cpp +++ b/complex.cpp @@ -1195,8 +1195,12 @@ namespace mirror { cell *c2 = cw2.c; if(c2->monst) { c->monst = moMimic; - if(!peace::on && canAttack(c,moMimic,c2,c2->monst, 0)) + eMonster m2 = c2->monst; + if(!peace::on && canAttack(c,moMimic,c2,m2, 0)) { attackMonster(c2, AF_MSG | AF_ORSTUN, moMimic); + if(!fwd) produceGhost(c2, m2, moMimic); + sideAttack(c, m.second.spin, m2, 0); + } c->monst = moNone; } if(c2->wall == waBigTree) @@ -1758,9 +1762,13 @@ inline float& HEAT(cell *c) { return c->LHU.heat; } namespace heat { + void affect(cell *c, double delta) { + if(isIcyLand(c)) HEAT(c) += delta; + } + double absheat(cell *c) { if(c->land == laCocytus) return HEAT(c) -.6; - if(c->land == laIce) return HEAT(c) -.4; + if(c->land == laIce || c->land == laBlizzard) return HEAT(c) -.4; return 0; } @@ -1850,21 +1858,30 @@ namespace heat { forCellEx(ct, c) if(!isIcyLand(ct) && isFire(ct)) hmod += xrate*.1; - for(int j=0; jtype; j++) if(c->mov[j]) { - if(!isIcyLand(c->mov[j])) { + forCellEx(ct, c) { + if(!isIcyLand(ct)) { // make sure that we can still enter Cocytus, // it won't heat up right away even without Orb of Winter or Orb of Speed - if(isPlayerOn(c->mov[j]) && (c->land == laIce || markOrb(itOrbWinter))) + if(isPlayerOn(ct) && (c->land == laIce || markOrb(itOrbWinter))) hmod += (markOrb(itOrbWinter) ? -1.2 : 1.2) / 4 * xrate; continue; } - ld hdiff = absheat(c->mov[j]) - absheat(c); + ld hdiff = absheat(ct) - absheat(c); hdiff /= 10; - if(shmup::on && (c->land == laCocytus || c->mov[j]->land == laCocytus)) + + if(ct->land == laBlizzard) { + int v = (windmap::at(ct) - windmap::at(c)) & 255; + if(v > 128) v -= 256; + if(v < windmap::NOWINDFROM && v > -windmap::NOWINDFROM) + hdiff = hdiff * (1 - v * 5. / windmap::NOWINDFROM); + } + + if(shmup::on && (c->land == laCocytus || ct->land == laCocytus)) hdiff /= 3; // if(c->mov[j]->cpdist > 7 && !quotient) hdiff += -HEAT(c) / 30; hmod += hdiff; } + // printf("%d ", vsum); hmods[i] = hmod; } @@ -1971,7 +1988,16 @@ namespace heat { useup(c); offscreen2.push_back(c); } - } + } + + if(c->wall == waArrowTrap && c->wparam) { + c->wparam++; + if(c->wparam == 3) { + if(canAttack(c, moArrowTrap, c, c->monst, AF_GETPLAYER)) + attackMonster(c, AF_ORSTUN | AF_MSG | AF_GETPLAYER, moArrowTrap); + } + if(c->wparam == 4) c->wparam = 0; + } } for(int i=0; iwall == waChasm || c->wall == waSulphur || c->wall == waSulphurC; + return c->wall == waChasm || c->wall == waSulphur || c->wall == waSulphurC || c->wall == waBubble; } bool isWateryOrBoat(cell *c) { @@ -267,7 +267,10 @@ int itemclass(eItem i) { i == itBabyTortoise || i == itDragon || i == itApple || i == itKraken || i == itBarrow || i == itTrollEgg || i == itTreat || i == itSlime || i == itAmethyst || i == itDodeca || - i == itGreenGrass || i == itBull) + i == itGreenGrass || i == itBull || + i == itAlchemy2 || i == itDogPlains || + i == itBlizzard || i == itTerra + ) return IC_TREASURE; if(i == itSavedPrincess || i == itStrongWind || i == itWarning) return IC_NAI; @@ -281,10 +284,18 @@ bool isAlch(eWall w) { return w == waFloorA || w == waFloorB; } +bool isAlch2(eWall w, bool bubbletoo) { + return w == waSlime1 || w == waSlime2 || (bubbletoo && w == waBubble); + } + +bool isAlch2(cell *c, bool bubbletoo) { + return isAlch2(c->wall, bubbletoo); + } + bool isAlch(cell *c) { return isAlch(c->wall); } bool isAlchAny(eWall w) { - return w == waFloorA || w == waFloorB || w == waFloorC || w == waFloorD; + return w == waFloorA || w == waFloorB; } bool isAlchAny(cell *c) { return isAlchAny(c->wall); } @@ -320,7 +331,8 @@ bool isWall(cell *w) { w->wall == waLadder || w->wall == waTrunk || w->wall == waSolidBranch || w->wall == waWeakBranch || w->wall == waCanopy || w->wall == waTower || 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) return false; if(isWatery(w) || isChasmy(w) || isFire(w)) return false; return true; @@ -362,7 +374,8 @@ bool normalMover(eMonster m) { m == moRoseBeauty || m == moWolf || m == moResearcher || m == moRagingBull || m == moNarciss || m == moMirrorSpirit || - slowMover(m); + m == moHunterDog || m == moTerraWarrior || m == moMercuryGuy || m == moLemur || m == moHunterGuard || + m == moIceGolem || slowMover(m); } // from-to diff --git a/game.cpp b/game.cpp index 34b76d55..a41d9472 100644 --- a/game.cpp +++ b/game.cpp @@ -23,30 +23,31 @@ int hardcoreAt; flagtype havewhat, hadwhat; -#define HF_BUG (1<<0) -#define HF_EARTH (1<<1) -#define HF_BIRD (1<<2) -#define HF_LEADER (1<<3) -#define HF_HEX (1<<4) -#define HF_WHIRLPOOL (1<<5) -#define HF_WATER (1<<6) -#define HF_AIR (1<<7) -#define HF_MUTANT (1<<8) -#define HF_OUTLAW (1<<9) -#define HF_WHIRLWIND (1<<10) -#define HF_ROSE (1<<11) -#define HF_DRAGON (1<<12) -#define HF_KRAKEN (1<<13) -#define HF_SHARK (1<<14) -#define HF_BATS (1<<15) -#define HF_REPTILE (1<<16) -#define HF_EAGLES (1<<17) -#define HF_SLOW (1<<18) -#define HF_FAST (1<<19) -#define HF_WARP (1<<20) -#define HF_MOUSE (1<<21) -#define HF_RIVER (1<<22) -#define HF_MIRROR (1<<23) +#define HF_BUG Flag(0) +#define HF_EARTH Flag(1) +#define HF_BIRD Flag(2) +#define HF_LEADER Flag(3) +#define HF_HEX Flag(4) +#define HF_WHIRLPOOL Flag(5) +#define HF_WATER Flag(6) +#define HF_AIR Flag(7) +#define HF_MUTANT Flag(8) +#define HF_OUTLAW Flag(9) +#define HF_WHIRLWIND Flag(10) +#define HF_ROSE Flag(11) +#define HF_DRAGON Flag(12) +#define HF_KRAKEN Flag(13) +#define HF_SHARK Flag(14) +#define HF_BATS Flag(15) +#define HF_REPTILE Flag(16) +#define HF_EAGLES Flag(17) +#define HF_SLOW Flag(18) +#define HF_FAST Flag(19) +#define HF_WARP Flag(20) +#define HF_MOUSE Flag(21) +#define HF_RIVER Flag(22) +#define HF_MIRROR Flag(23) +#define HF_VOID Flag(24) bool seenSevenMines = false; @@ -332,6 +333,7 @@ int* killtable[] = { &kills[moHerdBull], &kills[moSleepBull], &kills[moRagingBull], &kills[moGadfly], &kills[moButterfly], &kills[moNarciss], &kills[moMirrorSpirit], + &kills[moHunterDog], &kills[moIceGolem], &kills[moVoidBeast], NULL }; @@ -459,6 +461,7 @@ bool strictlyAgainstGravity(cell *w, cell *from, bool revdir, flagtype flags) { bool passable(cell *w, cell *from, flagtype flags) { bool revdir = (flags&P_REVDIR); + bool vrevdir = revdir ^ bool(flags&P_VOID); if(from && from != w && nonAdjacent(from, w) && !F(P_IGNORE37 | P_BULLET)) return false; @@ -469,7 +472,7 @@ bool passable(cell *w, cell *from, flagtype flags) { if(from == pp && F(P_ONPLAYER) && F(P_REVDIR)) return true; if(from && !((flags & P_ISPLAYER) && getMount(i))) { - int i = revdir ? incline(w, from) : incline(from, w); + int i = vrevdir ? incline(w, from) : incline(from, w); if(i < -1 && F(P_ROSE)) return false; if((i > 1) && !F(P_JUMP1 | P_JUMP2 | P_BULLET | P_FLYING | P_BLOW | P_CLIMBUP | P_AETHER | P_REPTILE)) return false; @@ -483,11 +486,11 @@ bool passable(cell *w, cell *from, flagtype flags) { if(againstWind(w,from)) return false; } - if(from && strictlyAgainstGravity(w, from, revdir, flags) + if(from && strictlyAgainstGravity(w, from, vrevdir, flags) && !F(P_GRAVITY | P_BLOW | P_JUMP1 | P_JUMP2 | P_FLYING | P_BULLET | P_AETHER) ) return false; - if(from && (revdir ? againstWind(from,w) : againstWind(w, from)) && !F(P_WIND | P_BLOW | P_JUMP1 | P_JUMP2 | P_BULLET | P_AETHER)) return false; + if(from && (vrevdir ? againstWind(from,w) : againstWind(w, from)) && !F(P_WIND | P_BLOW | P_JUMP1 | P_JUMP2 | P_BULLET | P_AETHER)) return false; if(revdir && from && w->monst && passable(from, w, flags &~ (P_REVDIR|P_MONSTER))) return true; @@ -621,7 +624,15 @@ void calcAirdir(cell *c) { bool againstWind(cell *cto, cell *cfrom) { if(!cfrom || !cto) return false; - if(airdist(cto) < airdist(cfrom)) return true; + int dcto = airdist(cto), dcfrom = airdist(cfrom); + if(dcto < dcfrom) return true; + if(cfrom->land == laBlizzard && cto->land == laBlizzard && dcto == 3 && dcfrom == 3) { + char vfrom = windmap::at(cfrom); + char vto = windmap::at(cto); + int z = (vfrom-vto) & 255; + if(z >= windmap::NOWINDBELOW && z < windmap::NOWINDFROM) + return true; + } whirlwind::calcdirs(cfrom); int d = neighborId(cfrom, cto); if(whirlwind::winddir(d) == -1) return true; @@ -777,6 +788,8 @@ bool passable_for(eMonster m, cell *w, cell *from, flagtype extra) { return passable(w, from, extra | P_ISFRIEND); if(isWorm(m)) return passable(w, from, extra) && !cellUnstable(w) && ((m != moWorm && m != moTentacle) || !cellEdgeUnstable(w)); + if(m == moVoidBeast) + return passable(w, from, extra | P_VOID); return false; } @@ -805,6 +818,7 @@ eMonster movegroup(eMonster m) { if(m == moWaterElemental) return moWaterElemental; if(m == moAirElemental) return moAirElemental; if(isBull(m)) return moRagingBull; + if(m == moVoidBeast) return moVoidBeast; return moNone; } @@ -959,6 +973,14 @@ bool stalemate1::isKilled(cell *w) { if(canAttack(moveto, who, w, w->monst, flag)) return true; } + if(isNeighbor(w, comefrom) && comefrom == moveto && killed) { + int d1 = neighborId(comefrom, w); + int d2 = neighborId(comefrom, killed); + int di = angledist(comefrom->type, d1, d2); + if(di && items[itOrbSide1-1+di] && canAttack(moveto, who, w, w->monst, AF_SIDE)) + return true; + } + if(isNeighbor(w, comefrom) && isNeighbor(w, moveto) && moveto != comefrom) if(canAttack(moveto, who, w, w->monst, AF_STAB)) return true; @@ -1279,13 +1301,16 @@ int monstersnear(cell *c, cell *nocount, eMonster who, cell *pushto, cell *comef return b; } -void petrify(cell *c, eWall walltype, eMonster m) { +bool petrify(cell *c, eWall walltype, eMonster m) { destroyHalfvine(c); playSound(c, "die-troll"); + if(walltype == waIcewall && !isIcyLand(c->land)) + return false; + if(isWateryOrBoat(c) && c->land == laWhirlpool) { c->wall = waSea; - return; + return false; } if(walltype == waGargoyle && cellUnstableOrChasm(c)) @@ -1296,11 +1321,11 @@ void petrify(cell *c, eWall walltype, eMonster m) { walltype = waPetrifiedBridge; else if((c->wall == waTempBridge || c->wall == waTempBridgeBlocked) && c->land == laWhirlpool) { c->wall = waTempBridgeBlocked; - return; + return true; } else if(!doesnotFall(c)) { fallingFloorAnimation(c, walltype, m); - return; + return true; } if(isReptile(c->wall)) kills[moReptile]++; @@ -1308,6 +1333,7 @@ void petrify(cell *c, eWall walltype, eMonster m) { c->wall = walltype; c->wparam = m; c->item = itNone; + return true; } void killIvy(cell *c, eMonster who) { @@ -1755,6 +1781,7 @@ void killMonster(cell *c, eMonster who, flagtype deathflags) { if(isPrincess(m)) m = moPrincess; if(m == moTentacleGhost) m = moGhost; if(!isBulletType(m)) kills[m]++; + if(m == moHunterGuard) m = moHunterDog; if(!c->item) if(m == moButterfly && (deathflags & AF_BULL)) c->item = itBull; @@ -1816,6 +1843,12 @@ void killMonster(cell *c, eMonster who, flagtype deathflags) { if(connected) petrify(c, waGargoyle, m), pcount = 0; } + + if(m == moIceGolem) { + if(petrify(c, waIcewall, m)) pcount = 0; + heat::affect(c, -1); + forCellEx(c2, c) heat::affect(c2, -.5); + } if(m == moTroll) { petrify(c, waDeadTroll, m); pcount = 0; @@ -2359,6 +2392,10 @@ bool recalcTide; #define LANDDIST LHU.bytes[1] #define CHAOSPARAM LHU.bytes[2] +int alchemyval(cell *c, int t) { + return (windmap::at(c) + (turncount+t)*4) & 255; + } + void checkTide(cell *c) { if(c->land == laOcean) { int t = c->landparam; @@ -2390,6 +2427,11 @@ void checkTide(cell *c) { if(isFire(c) && t >= tidalphase) c->wall = waSea; } + if(isAlch2(c, true)) { + int id = alchemyval(c, 0); + if(id < 96) c->wall = waBubble; + else c->wall = waSlime1; + } } void buildAirmap() { @@ -2746,6 +2788,7 @@ void bfs() { else if(isLeader(c2->monst)) havewhat |= HF_LEADER; else if(c2->monst == moEarthElemental) havewhat |= HF_EARTH; else if(c2->monst == moWaterElemental) havewhat |= HF_WATER; + else if(c2->monst == moVoidBeast) havewhat |= HF_VOID; else if(c2->monst == moShark || c2->monst == moCShark) havewhat |= HF_SHARK; else if(c2->monst == moAirElemental) havewhat |= HF_AIR, airmap.push_back(make_pair(c2,0)); @@ -2878,6 +2921,14 @@ void destroyWeakBranch(cell *cf, cell *ct, eMonster who) { } } +void activateArrowTrap(cell *c) { + if(c->wall == waArrowTrap && c->wparam == 0) { + c->wparam = 1; + forCellEx(c2, c) activateArrowTrap(c2); + } + } + + // effect of moving monster m from cf to ct // this is called from moveMonster, or separately from moveIvy/moveWorm, // or when a dead bird falls (then m == moDeadBird) @@ -2893,6 +2944,9 @@ void moveEffect(cell *ct, cell *cf, eMonster m) { if((ct->wall == waClosePlate || ct->wall == waOpenPlate) && !ignoresPlates(m)) toggleGates(ct, ct->wall); + + if(ct->wall == waArrowTrap && !ignoresPlates(m)) + activateArrowTrap(ct); if(cf && isPrincess(m)) princess::move(ct, cf); @@ -2961,6 +3015,9 @@ void playerMoveEffects(cell *c1, cell *c2) { if((c2->wall == waClosePlate || c2->wall == waOpenPlate) && !markOrb(itOrbAether)) toggleGates(c2, c2->wall); + if(c2->wall == waArrowTrap && !markOrb(itOrbAether)) + activateArrowTrap(c2); + princess::playernear(c2); if(c2->wall == waGlass && items[itOrbAether] > ORBBASE+1) { @@ -3383,6 +3440,7 @@ int stayval(cell *c, flagtype mf) { if(isWorm(c->monst)) return 550; if(c->monst == moRagingBull) return -1690; // worse than to stay in place if(c->monst == moBat && batsAfraid(c)) return 575; + if(c->monst == moHunterGuard) return 1600; // prefers to stay in place return 1000; } @@ -3423,8 +3481,11 @@ int determinizeBullPush(cellwalker bull) { return 1; } +int posdir[10], nc; + int pickMoveDirection(cell *c, flagtype mf) { - int posdir[10], nc = 0, bestval = stayval(c, mf); + int bestval = stayval(c, mf); + nc = 1; posdir[0] = -1; // printf("stayval [%p, %s]: %d\n", c, dnameof(c->monst), bestval); for(int d=0; dtype; d++) { @@ -3439,8 +3500,7 @@ int pickMoveDirection(cell *c, flagtype mf) { determinizeBull(c, posdir, nc); if(!nc) return -1; - nc = hrand(nc); - return posdir[nc]; + return posdir[hrand(nc)]; } int pickDownDirection(cell *c, flagtype mf) { @@ -3523,13 +3583,15 @@ void beastAttack(cell *c, bool player) { } } +bool quantum; + cell *moveNormal(cell *c, flagtype mf) { eMonster m = c->monst; int d; if(c->stuntime) { - if(cellEdgeUnstable(c, MF_STUNNED)) d = pickDownDirection(c, mf); + if(cellEdgeUnstable(c, MF_STUNNED)) d = pickDownDirection(c, mf), nc = 1, posdir[0] = d; else return NULL; } else { @@ -3541,24 +3603,55 @@ cell *moveNormal(cell *c, flagtype mf) { stayEffect(c); return c; } - cell *c2 = c->mov[d]; - - if(isPlayerOn(c2)) { - killThePlayerAt(m, c2, 0); - return c2; - } - - eMonster m2 = c2->monst; - if(m2) { - attackMonster(c2, AF_ORSTUN | AF_MSG, m); - if(m == moFlailer && m2 == moIllusion) - attackMonster(c, 0, m2); - return c2; - } - moveMonster(c2, c); - if(m == moRagingBull) beastAttack(c2, false); - return c2; + if(!quantum) { + cell *c2 = c->mov[d]; + if(isPlayerOn(c2)) { + killThePlayerAt(m, c2, 0); + return c2; + } + + eMonster m2 = c2->monst; + if(m2) { + attackMonster(c2, AF_ORSTUN | AF_MSG, m); + if(m == moFlailer && m2 == moIllusion) + attackMonster(c, 0, m2); + return c2; + } + + moveMonster(c2, c); + if(m == moRagingBull) beastAttack(c2, false); + return c2; + } + else { + bool attacking = false; + for(int i=0; imov[posdir[i]]; + + if(isPlayerOn(c2)) { + killThePlayerAt(m, c2, 0); + attacking = true; + } + + else { + eMonster m2 = c2->monst; + if(m2) { + attackMonster(c2, AF_ORSTUN | AF_MSG, m); + if(m == moFlailer && m2 == moIllusion) + attackMonster(c, 0, m2); + attacking = true; + } + } + } + + if(!attacking) for(int i=0; imov[posdir[i]]; + if(!c->monst) c->monst = m; + moveMonster(c2, c); + if(m == moRagingBull) beastAttack(c2, false); + } + return c->mov[d]; + } } void explodeAround(cell *c) { @@ -4136,8 +4229,9 @@ void snakeAttack(cell *c, bool mounted) { for(int j=0; jtype; j++) 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); + produceGhost(c->mov[j], moHexSnake, m2); } } @@ -4425,6 +4519,36 @@ void swordAttackStatic() { swordAttackStatic(bb); } +void sideAttack(cell *mf, int dir, eMonster who, int bonus, eItem orb) { + if(!items[orb]) return; + if(who != moPlayer && !items[itOrbEmpathy]) return; + for(int k: {-1, 1}) { + cell *mt = getMovR(mf, dir + k*bonus); + eMonster m = mt->monst; + 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)) + produceGhost(mt, m, who); + } + else if(mt->wall == waBigTree) + mt->wall = waSmallTree; + else if(mt->wall == waSmallTree) + mt->wall = waNone; + } + } + +void sideAttack(cell *mf, int dir, eMonster who, int bonuskill) { + + int k = tkills(); + sideAttack(mf, dir, who, 1, itOrbSide1); + sideAttack(mf, dir, who, 2, itOrbSide2); + sideAttack(mf, dir, who, 3, itOrbSide3); + + int kills = tkills() - k + bonuskill; + if(kills >= 5) achievement_gain("MELEE5"); + } + void stabbingAttack(cell *mf, cell *mt, eMonster who, int bonuskill) { int numsh = 0, numflail = 0, numlance = 0, numslash = 0; @@ -4685,6 +4809,7 @@ void movegolems(flagtype flags) { } attackMonster(c2, ((revenge||jealous)?0:AF_ORSTUN) | AF_MSG, m); produceGhost(c2, m2, m); + sideAttack(c, dir, m, 0); if(revenge) c->monst = m = moPrincessArmed; if(jealous) { playSound(c2, princessgender() ? "dzia-princess" : "dzia-prince"); @@ -4960,7 +5085,7 @@ void moverefresh(bool turn = true) { if(c->monst == moTortoise && c->item == itBabyTortoise && !((tortoise::getb(c) ^ tortoise::babymap[c]) & tortoise::mask)) c->stuntime = 2; - + if(c->monst == moReptile) { if(c->wall == waChasm || cellUnstable(c)) { c->monst = moNone; @@ -5166,6 +5291,8 @@ void movemonsters() { if(havewhat & HF_EARTH) groupmove(moEarthElemental, 0); DEBT("water"); if(havewhat & HF_WATER) groupmove(moWaterElemental, 0); + DEBT("void"); + if(havewhat & HF_VOID) groupmove(moVoidBeast, 0); DEBT("leader"); if(havewhat & HF_LEADER) groupmove(moPirate, 0); DEBT("mutant"); @@ -5717,6 +5844,140 @@ void collectMessage(cell *c2, eItem which) { } } +int ambushval; + +void ambush(cell *c, eItem what) { + int maxdist = purehepta ? 5 : 7; + celllister cl(c, maxdist, 1000000, NULL); + cell *c0 = c; + int d = 0; + cl.prepare(); + bool restricted = false; + for(cell *cx: cl.lst) { + int dh = cl.getdist(cx); + if(dh <= 2 && cx->monst == moHunterGuard) + cx->monst = moHunterDog; + if(dh <= 3 && cx->monst) restricted = true; + if(dh > d) c0 = cx, d = dh; + } + vector around; + cell *clast = NULL; + cell *ccur = c0; + while(true) { + cell *c2 = NULL; + forCellEx(c1, ccur) + if(c1 != clast && cl.listed(c1) && cl.getdist(c1) == d) + c2 = c1; + if(!c2) break; + if(c2->land == laDogPlains && c2->wall == waNone && c2->monst == moNone) + around.push_back(c2); + clast = ccur; ccur = c2; + if(c2 == c0) break; + } + int N = size(around); + int dogs; + + int qty = items[itDogPlains]; + + if(ambushval) dogs = ambushval; + else + switch(what) { + case itDogPlains: + if(qty <= 16) + dogs = qty; + else + dogs = max(33-qty, 6); + break; + + case itOrbSide3: + dogs = restricted ? 10 : 20; + break; + + case itOrbFreedom: + dogs = restricted ? 10 : 60; + break; + + case itOrbThorns: + case itOrb37: + dogs = 20; + return; + + case itOrbBeauty: + dogs = 35; + return; + + case itOrbShell: + dogs = 35; + break; + + case itOrbPsi: + // dogs = 40; -> no benefits + dogs = 20; + break; + + case itOrbDash: + case itOrbFrog: + dogs = 40; + break; + + case itOrbAir: + case itOrbDragon: + dogs = 50; + break; + + case itOrbStunning: + // dogs = restricted ? 50 : 60; -> no benefits + dogs = 30; + break; + + case itOrbBull: + case itOrbSpeed: + case itOrbShield: + dogs = 60; + break; + + case itOrbInvis: + dogs = 80; + break; + + case itOrbTeleport: + dogs = 300; + break; + + case itGreenStone: + case itOrbSafety: + case itOrbYendor: + return; + + case itKey: + dogs = 16; + break; + + case itWarning: + dogs = qty; + break; + + default: + dogs = restricted ? 6 : 10; + break; + + // Flash can survive about 70, but this gives no benefits + } + + int gaps = dogs; + if(!N) return; + int shift = hrand(N); + dogs = min(dogs, N); + gaps = min(gaps, N); + for(int i=0; imonst = moHunterDog; + nextdog->stuntime = 1; + drawFlash(nextdog); + } + } + bool collectItem(cell *c2, bool telekinesis) { int pg = gold(); @@ -5739,6 +6000,11 @@ bool collectItem(cell *c2, bool telekinesis) { if(!cantGetGrimoire(c2, false)) collectMessage(c2, c2->item); if(c2->item == itDodeca && peace::on) peace::simon::extend(); } + + if(c2->land == laDogPlains && c2->item) { + addMessage(XLAT("You are ambushed!")); + ambush(c2, c2->item); + } if(isRevivalOrb(c2->item) && multi::revive_queue.size()) { multiRevival(cwt.c, c2); @@ -5874,7 +6140,7 @@ bool collectItem(cell *c2, bool telekinesis) { gainItem(itElemental); gainItem(itElemental); addMessage(XLAT("You construct some Elemental Gems!", c2->item) + itemcounter(items[itElemental])); - } + } if(c2->item == itBounty) items[itRevolver] = 6; @@ -6199,6 +6465,19 @@ namespace orbbull { } } +void terracotta() { + for(int i=0; iwall == waTerraWarrior) { + c2->landparam++; + if((c2->landparam == 3 && hrand(3) == 0) || + (c2->landparam == 4 && hrand(2) == 0) || + c2->landparam == 5) + c2->monst = moTerraWarrior, + c2->wall = waNone; + } + } + void monstersTurn() { mirror::breakAll(); DEBT("bfs"); @@ -6239,7 +6518,8 @@ void monstersTurn() { orbbull::check(); - + terracotta(); + DEBT("check"); checkmove(); if(canmove) elec::checklightningfast(); @@ -6559,16 +6839,19 @@ bool movepcto(int d, int subdir, bool checkonly) { addMessage(XLAT("You start chopping down the tree.")); playSound(c2, "hit-axe" + pick123()); c2->wall = waNone; + sideAttack(cwt.c, d, moPlayer, 0); } else if(c2->wall == waBigTree) { drawParticles(c2, winf[c2->wall].color, 8); addMessage(XLAT("You chop down the tree.")); playSound(c2, "hit-axe" + pick123()); - c2->wall = waSmallTree; + sideAttack(cwt.c, d, moPlayer, 0); } else { - if(!peace::on) + if(!peace::on) { addMessage(XLAT("You swing your sword at the mirror.")); + sideAttack(cwt.c, d, moPlayer, 0); + } } if(survivalist && isHaunted(c2->land)) survivalist = false; @@ -6692,6 +6975,7 @@ bool movepcto(int d, int subdir, bool checkonly) { } } + sideAttack(cwt.c, d, moPlayer, 0); lastmovetype = lmAttack; lastmove = c2; swordAttackStatic(); } diff --git a/graph.cpp b/graph.cpp index 790626ce..11d606be 100644 --- a/graph.cpp +++ b/graph.cpp @@ -403,9 +403,8 @@ void ShadowV(const transmatrix& V, const hpcshape& bp, int prio) { if(mmspatial) { if(pmodel == mdHyperboloid || pmodel == mdBall) return; // shadows break the depth testing - int p = poly_outline; poly_outline = OUTLINE_TRANS; + dynamicval p(poly_outline, OUTLINE_TRANS); queuepolyat(V, bp, SHADOW_MON, prio); - poly_outline = p; } } @@ -503,6 +502,7 @@ bool drawItemType(eItem it, cell *c, const transmatrix& V, int icol, int ticks, it == itHolyGrail ? &shGrail : isElementalShard(it) ? &shElementalShard : (it == itBombEgg || it == itTrollEgg) ? &shEgg : + it == itDogPlains ? &shTriangle : it == itDodeca ? &shDodeca : xch == '*' ? &shGem[ct6] : it == itShard ? &shMFloor[0] : @@ -610,8 +610,12 @@ bool drawItemType(eItem it, cell *c, const transmatrix& V, int icol, int ticks, if(xsh == &shBookCover && mmitem) queuepoly(V2, shBook, 0x805020FF); + + int pr = PPR_ITEM; + int alpha = hidden ? (it == itKraken ? 0xC0 : 0x40) : 0xF0; + if(c && c->wall == waIcewall) pr = PPR_HIDDEN, alpha = 0x80; - queuepoly(V2, *xsh, darkena(icol, 0, hidden ? (it == itKraken ? 0xC0 : 0x40) : 0xF0)); + queuepolyat(V2, *xsh, darkena(icol, 0, alpha), pr); if(it == itZebra) queuepolyat(V * spin(ticks / 1500. + M_PI/(ct6+6)), *xsh, darkena(0x202020, 0, hidden ? 0x40 : 0xF0), PPR_ITEMb); @@ -645,6 +649,17 @@ bool drawItemType(eItem it, cell *c, const transmatrix& V, int icol, int ticks, return false; } +void drawTerraWarrior(const transmatrix& V, int t, double footphase) { + int col = 0xC0C0C0; + int col2 = t > 6 ? 0x4040C0 : 0x6060A0; + ShadowV(V, shPBody); + otherbodyparts(V, darkena(t > 4 ? col2 : col, 0, 0xF0), moDesertman, footphase); + queuepoly(VBODY, shPBody, darkena(t > 0 ? col2 : col, 0, 0xF0)); + queuepoly(VBODY, shPrinceDress, darkena(t > 1 ? col2 : col, 0, 0xF0)); + if(!peace::on) queuepoly(VBODY * Mirror, shPSword, darkena(t > 2 ? col2 : col, 0, 0xF0)); + queuepoly(VHEAD, shTurban1, darkena(t > 3 ? col2 : col, 0, 0xF0)); + } + bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, double footphase) { char xch = minf[m].glyph; @@ -984,7 +999,7 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou } queuepoly(VABODY, shBugArmor, darkena(col, 1, 0xFF)); } - else if(m == moRunDog) { + else if(m == moRunDog || m == moHunterDog || m == moHunterGuard) { if(!mmspatial && !footphase) queuepoly(VABODY, shDogBody, darkena(col, 0, 0xFF)); else { @@ -993,9 +1008,18 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou animallegs(VALEGS, moRunDog, darkena(col, 0, 0xFF), footphase); } queuepoly(VAHEAD, shDogHead, darkena(col, 0, 0xFF)); - queuepoly(VAHEAD, shWolf1, darkena(0x202020, 0, 0xFF)); - queuepoly(VAHEAD, shWolf2, darkena(0x202020, 0, 0xFF)); - queuepoly(VAHEAD, shWolf3, darkena(0x202020, 0, 0xFF)); + + { + dynamicval dp(poly_outline); + dynamicval dw(minwidth_global); + bool redeyes = m != moRunDog; + int eyes = darkena(redeyes ? 0xFF0000 : 0x202020, 0, 0xFF); + + if(redeyes) poly_outline = eyes, minwidth_global = 1; + queuepoly(VAHEAD, shWolf1, eyes); + queuepoly(VAHEAD, shWolf2, eyes); + } + queuepoly(VAHEAD, shWolf3, darkena(m == moRunDog ? 0x202020 : 0x000000, 0, 0xFF)); } else if(m == moOrangeDog) { if(!mmspatial && !footphase) @@ -1014,7 +1038,8 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou else if(m == moShark || m == moGreaterShark || m == moCShark) queuepoly(VFISH, shShark, darkena(col, 0, 0xFF)); else if(m == moEagle || m == moParrot || m == moBomberbird || m == moAlbatross || - m == moTameBomberbird || m == moWindCrow || m == moTameBomberbirdMoved) { + m == moTameBomberbird || m == moWindCrow || m == moTameBomberbirdMoved || + m == moSandBird) { ShadowV(V, shEagle); queuepoly(VBIRD, shEagle, darkena(col, 0, 0xFF)); } @@ -1062,6 +1087,8 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou ShadowV(V, shPBody); queuepoly(VBODY, shPBody, c); } + else if(m == moTerraWarrior) + drawTerraWarrior(V, 7, footphase); else if(m == moDesertman) { otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase); ShadowV(V, shPBody); @@ -1180,6 +1207,15 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou queuepoly(VHEAD, shPFace, darkena(col, 1, 0x90)); queuepoly(VHEAD, shArmor, darkena(col, 0, 0xC0)); } + else if(m == moTerraWarrior || m == moMercuryGuy || m == moLemur) { + ShadowV(V, shPBody); + otherbodyparts(V, darkena(col, 0, 0xF0), m, footphase); + queuepoly(VBODY, shPBody, darkena(col, 0, 0xF0)); + if(!peace::on) queuepoly(VBODY * Mirror, shPSword, darkena(col, 0, 0xF0)); + queuepoly(VHEAD, shPHead, darkena(col, 1, 0xF0)); + queuepoly(VHEAD, shPFace, darkena(col, 1, 0xF0)); + queuepoly(VHEAD, shArmor, darkena(col, 0, 0xF0)); + } else if(m == moGhost || m == moSeep || m == moFriendlyGhost) { if(m == moFriendlyGhost) col = fghostcolor(ticks, where); queuepoly(VGHOST, shGhost, darkena(col, 0, m == moFriendlyGhost ? 0xC0 : 0x80)); @@ -1285,6 +1321,12 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou if(b > 6) b = 6; queuepoly(VHEAD, shWightCloak, 0x605040A0 + 0x10101000 * b); } + else if(m == moVoidBeast) { + otherbodyparts(V, 0x080808D0, m, footphase); + queuepoly(VBODY, shPBody, 0x080808D0); + queuepoly(VHEAD, shPHead, 0x080808D0); + queuepoly(VHEAD, shWightCloak, 0xFF0000A0); + } else if(m == moGoblin) { otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase); ShadowV(V, shYeti); @@ -1386,7 +1428,7 @@ bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, int col, dou int acol = col; queuepoly(VAHEAD, shTrylobiteHead, darkena(acol, 0, 0xFF)); } - else if(m == moEvilGolem) { + else if(m == moEvilGolem || m == moIceGolem) { otherbodyparts(V, darkena(col, 2, 0xC0), m, footphase); ShadowV(V, shPBody); queuepoly(VBODY, shPBody, darkena(col, 0, 0XC0)); @@ -2271,7 +2313,7 @@ void setcolors(cell *c, int& wcol, int &fcol) { if(isWateryOrBoat(c) || c->wall == waReptileBridge) { if(c->land == laOcean) fcol = (c->landparam > 25 && !chaosmode) ? ( - 0x90 + 8 * sin(windmap::windcodes[windmap::getId(c)] * M_PI / 128 - SDL_GetTicks()/1000.) + 0x90 + 8 * sin(windmap::windcodes[windmap::getId(c)] * M_PI / 128 - ticks/1000.) ) : 0x1010C0 + int(32 * sin(ticks / 500. + (chaosmode ? c->CHAOSPARAM : c->landparam)*1.5)); else if(c->land == laOceanWall) @@ -2366,11 +2408,6 @@ void setcolors(cell *c, int& wcol, int &fcol) { case laCanvas: fcol = c->landparam; break; - case laAlchemy2: - fcol = (windmap::windcodes[windmap::getId(c)] - SDL_GetTicks()/10) & 255; - if(fcol > 128) fcol = 256 - fcol; - fcol += 96; - break; case laPalace: fcol = 0x806020; if(c->wall == waClosedGate || c->wall == waOpenGate) @@ -2422,6 +2459,17 @@ void setcolors(cell *c, int& wcol, int &fcol) { fcol = wcol[whirlwind::fzebra3(c)]; } break; + + case laDogPlains: + // fcol = pseudohept(c) ? 0x205050 : 0x306060; + fcol = 0x40E0D0; + fcol /= 2; + if(pseudohept(c)) fcol = fcol * 3/4; + break; + + case laTerracotta: + fcol = 0x909090; + break; case laIvoryTower: fcol = 0x10101 * (32 + (c->landparam&1) * 32) - 0x000010; @@ -2492,24 +2540,34 @@ void setcolors(cell *c, int& wcol, int &fcol) { break; } - case laIce: case laCocytus: + case laIce: case laCocytus: case laBlizzard: if(isIcyWall(c)) { float h = HEAT(c); bool showcoc = c->land == laCocytus && chaosmode && !wmescher; - if(h < -0.4) - wcol = gradient(showcoc ? 0x4080FF : 0x4040FF, 0x0000FF, -0.4, h, -1); + + int colorN04 = showcoc ? 0x4080FF : 0x4040FF; + int colorN10 = 0x0000FF; + int color0 = c->land == laBlizzard ? 0x5050C0 : showcoc ? 0x80C0FF : 0x8080FF; + int color02 = 0xFFFFFF; + int color06 = 0xFF0000; + int color08 = 0xFFFF00; + + if(h < -1) + wcol = colorN10; + else if(h < -0.4) + wcol = gradient(colorN04, colorN10 , -0.4, h, -1); else if(h < 0) - wcol = gradient(showcoc ? 0x80C0FF : 0x8080FF, showcoc ? 0x4080FF : 0x4040FF, 0, h, -0.4); + wcol = gradient(color0, colorN04, 0, h, -0.4); else if(h < 0.2) - wcol = gradient(showcoc ? 0x80C0FF : 0x8080FF, 0xFFFFFF, 0, h, 0.2); + wcol = gradient(color0, color02, 0, h, 0.2); // else if(h < 0.4) // wcol = gradient(0xFFFFFF, 0xFFFF00, 0.2, h, 0.4); else if(h < 0.6) - wcol = gradient(0xFFFFFF, 0xFF0000, 0.2, h, 0.6); + wcol = gradient(color02, color06, 0.2, h, 0.6); else if(h < 0.8) - wcol = gradient(0xFF0000, 0xFFFF00, 0.6, h, 0.8); + wcol = gradient(color06, color08, 0.6, h, 0.8); else - wcol = 0xFFFF00; + wcol = color08; if(c->wall == waFrozenLake) fcol = wcol; else @@ -2563,6 +2621,15 @@ void setcolors(cell *c, int& wcol, int &fcol) { else fcol = wcol; } + + if(isAlch2(c, true)) { + int id = alchemyval(c, -1); + if(id < 96) + wcol = gradient(0x800000, 0xFF0000, 0, id, 96); + else + wcol = gradient(0x00FF00, 0xFFFF00, 96, id, 255); + fcol = wcol; + } if(c->wall == waDeadTroll2 || c->wall == waPetrified || c->wall == waPetrifiedBridge) { eMonster m = eMonster(c->wparam); @@ -2884,6 +2951,8 @@ bool allemptynear(cell *c) { return true; } +#include "blizzard.cpp" + void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { qfi.shape = NULL; qfi.special = false; @@ -3160,7 +3229,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { int fd = c->land == laRedRock ? 0 : (c->land == laOcean || c->land == laLivefjord || c->land == laWhirlpool) ? 1 : - c->land == laAlchemist || c->land == laIce || c->land == laGraveyard || + c->land == laAlchemist || c->land == laIce || c->land == laGraveyard || c->land == laBlizzard || c->land == laRlyeh || c->land == laTemple || c->land == laWineyard || c->land == laDeadCaves || c->land == laPalace || c->land == laCA ? 1 : c->land == laCanvas ? 0 : @@ -3179,7 +3248,14 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { c->land == laHalloween ? 1 : c->land == laTrollheim ? 2 : c->land == laReptile ? 0 : + c->land == laDogPlains ? 1 : 2; + + if(c->land == laAlchemy2) { + int id = alchemyval(c, -1); + if(id/4 == 95/4 || id/4 == 255/4) fd = 0; + if(id/4 == 95/4-1 || id/4 == 255/4-1) fd = 1; + } poly_outline = OUTLINE_DEFAULT; @@ -3360,6 +3436,11 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { else if(wmblack == 1 && c->wall == waMineOpen && vid.grid) ; +// else if(true) +// qfloor(c, Vf, shWave[wavephase][ct6], darkena(fcol, fd, 0xFF)); +// else if(true) +// qfloor(c, Vf, shSeabed[ct6], darkena(fcol, fd, 0xFF)); + else if(wmblack) { qfloor(c, Vf, shBFloor[ct6], darkena(fcol, 0, 0xFF)); int rd = rosedist(c); @@ -3505,7 +3586,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { else if(c->land == laHell) qfloor(c, Vf, (euclid ? shStarFloor : shDemonFloor)[ct6], darkena(fcol, fd, 0xFF)); - else if(c->land == laIce) + else if(c->land == laIce || c->land == laBlizzard) qfloor(c, Vf, shStarFloor[ct6], darkena(fcol, fd, 0xFF)); else if(c->land == laCocytus) @@ -3680,8 +3761,11 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { placeSidewallX(c, i, SIDE_LAKE, V, isWarped(c), false, darkena(gradient(0, col, 0, .8, 1), fd, 0xFF)); chasmg = 1; } + + if(c->wall == waTerraWarrior) + drawTerraWarrior(V, c->landparam & 7, 0); - if(c->wall == waBoat || c->wall == waStrandedBoat) { + else if(c->wall == waBoat || c->wall == waStrandedBoat) { double footphase; bool magical = items[itOrbWater] && (isPlayerOn(c) || (isFriendly(c) && items[itOrbEmpathy])); int outcol = magical ? watercolor(0) : 0xC06000FF; @@ -3741,7 +3825,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { else if(c->wall == waFrozenLake || c->wall == waLake || c->wall == waCamelotMoat || c->wall == waSea || c->wall == waClosePlate || c->wall == waOpenPlate || - c->wall == waOpenGate || c->wall == waTrapdoor) + c->wall == waOpenGate || c->wall == waTrapdoor || c->wall == waBubble) ; else if(c->wall == waRose) { @@ -3837,32 +3921,35 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { } else { transmatrix Vdepth = mscale(V, geom3::WALL); + int alpha = 0xFF; + if(c->wall == waIcewall) + alpha = 0xC0; bool warp = isWarped(c); if(starcol && !(wmescher && c->wall == waPlatform)) queuepolyat(Vdepth, shThisWall, darkena(starcol, 0, 0xFF), PPR_WALL3A); - warpfloor(c, Vdepth, darkena(wcol0, fd, 0xFF), PPR_WALL3, warp); + warpfloor(c, Vdepth, darkena(wcol0, fd, alpha), PPR_WALL3, warp); floorShadow(c, V, SHADOW_WALL, warp); if(c->wall == waCamelot) { forCellIdEx(c2, i, c) { - placeSidewallX(c, i, SIDE_SLEV, V, warp, false, darkena(wcol2, fd, 0xFF)); + placeSidewallX(c, i, SIDE_SLEV, V, warp, false, darkena(wcol2, fd, alpha)); } forCellIdEx(c2, i, c) { - placeSidewallX(c, i, SIDE_SLEV+1, V, warp, false, darkena(wcol2, fd, 0xFF)); + placeSidewallX(c, i, SIDE_SLEV+1, V, warp, false, darkena(wcol2, fd, alpha)); } forCellIdEx(c2, i, c) { - placeSidewallX(c, i, SIDE_SLEV+2, V, warp, false, darkena(wcol2, fd, 0xFF)); + placeSidewallX(c, i, SIDE_SLEV+2, V, warp, false, darkena(wcol2, fd, alpha)); } forCellIdEx(c2, i, c) { - placeSidewallX(c, i, SIDE_WTS3, V, warp, false, darkena(wcol2, fd, 0xFF)); + placeSidewallX(c, i, SIDE_WTS3, V, warp, false, darkena(wcol2, fd, alpha)); } } else forCellIdEx(c2, i, c) { if(!highwall(c2) || conegraph(c2)) - { placeSidewallX(c, i, SIDE_WALL, V, warp, false, darkena(wcol2, fd, 0xFF)); } + { placeSidewallX(c, i, SIDE_WALL, V, warp, false, darkena(wcol2, fd, alpha)); } } } } @@ -3872,6 +3959,11 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { queuepoly(V * spin(M_PI/6 - fanframe * M_PI / 3), shFan, darkena(wcol, 0, 0xFF)); } + else if(c->wall == waArrowTrap) { + int trapcol[4] = {0x904040, 0xA02020, 0xD00000, 0x303030}; + queuepoly(V, shDisk, darkena(trapcol[c->wparam&3], 0, 0xFF)); + } + else if(xch == '%') { if(doHighlight()) poly_outline = (c->land == laMirror) ? OUTLINE_TREASURE : OUTLINE_ORB; @@ -3912,6 +4004,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { else if(xch != '.' && xch != '+' && xch != '>' && xch != ':'&& xch != '-' && xch != ';' && c->wall != waSulphur && xch != ',') error = true; } + else if(!(it || c->monst || c->cpdist == 0)) error = true; int sha = shallow(c); @@ -4070,6 +4163,9 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { } } + if(c->land == laBlizzard) + blizzardcells[c].frame = frameid; + if(c->land == laWhirlwind) { whirlwind::calcdirs(c); @@ -4472,6 +4568,7 @@ void drawFlashes() { bool allowIncreasedSight() { if(cheater) return true; + if(inHighQual) return true; if(peace::on) return true; #if CAP_TOUR if(tour::on) return true; @@ -4487,7 +4584,7 @@ void drawthemap() { frameid++; - wavephase = (-(SDL_GetTicks() / 100)) & 7; + wavephase = (-(ticks / 100)) & 7; if(!allowIncreasedSight()) { if(sightrange > 7) sightrange = 7; @@ -4562,6 +4659,7 @@ void drawthemap() { maxreclevel, hsOrigin, ypush(vid.yshift) * sphereflip * View); } + drawBlizzards(); ivoryz = false; linepatterns::drawAll(); diff --git a/hyper.cpp b/hyper.cpp index 34d220d8..efc27619 100644 --- a/hyper.cpp +++ b/hyper.cpp @@ -150,6 +150,12 @@ int arg::readCommon() { shift(); int q = argi(); placeItems(q, i); } + else if(argis("-ambush")) { + // make all ambushes use the given number of dogs + // example: hyper -W Hunt -IP Shield 1 -ambush 60 + PHASE(3) cheater++; timerghost = false; + shift(); ambushval = argi(); + } else if(argis("-M")) { PHASE(3) cheater++; timerghost = false; shift(); eMonster m = readMonster(args()); @@ -272,6 +278,10 @@ else if(args()[0] == '-' && args()[1] == x && args()[2] == '0') { showstartmenu fieldpattern::info(); exit(0); } + else if(argis("-quantum")) { + quantum = true; + autocheat = true; + } else if(argis("-P")) { PHASE(2); shift(); vid.scfg.players = argi(); diff --git a/hyper.h b/hyper.h index c500371e..c2646365 100644 --- a/hyper.h +++ b/hyper.h @@ -53,7 +53,8 @@ void achievement_pump(); vector achievementsReceived; // game forward declarations -typedef int flagtype; +typedef unsigned long long flagtype; +#define Flag(i) (flagtype(1ull<= R30 && items[itElixir] >= U10; + case laDogPlains: + return true; + + case laTerracotta: + return gold() >= 60; + + case laBlizzard: + return items[itDiamond] >= 5 && items[itWindstone] >= 5; + case laCrossroads5: return gold() >= R300; } @@ -3512,6 +3534,15 @@ int reptilemax() { bool is02(int i) { return i == 0 || i == 2; } +bool openplains(cell *c) { + celllister cl(c, purehepta ? 5 : 7, 1000000, NULL); + for(cell *c: cl.lst) { + while(c->mpdist > 8) setdist(c, c->mpdist-1, NULL); + if(c->land != laDogPlains) return false; + } + return true; + } + // This function generates all lands. Warning: it's very long! void setdist(cell *c, int d, cell *from) { @@ -3611,7 +3642,7 @@ void setdist(cell *c, int d, cell *from) { else buildPrizeMirror(c, 1000); } } - + if(d == 7) prairie::generateTreasure(c); @@ -3986,7 +4017,55 @@ void setdist(cell *c, int d, cell *from) { if(c->land == laAlchemist) c->wall = (randomPatternsMode ? RANDPAT : hrand(2)) ? waFloorA : waFloorB; + + if(c->land == laAlchemy2) + c->wall = waSlime1; + if(c->land == laBlizzard) { + bool windless = true; + int w = windmap::at(c); + forCellCM(c2, c) { + int w2 = windmap::at(c2); + if(((w2-w) & 255) >= windmap::NOWINDBELOW) + if(((w-w2) & 255) >= windmap::NOWINDBELOW) + windless = false; + } + if(windless) { + c->wall = waIcewall; + if(hrand(500) < PT(100 + 2 * kills[moVoidBeast] + 2 * kills[moIceGolem], 200) && notDippingFor(itBlizzard)) + c->item = itBlizzard; + } + } + + if(c->land == laTerracotta) { + if(hrand(500) < 15) { + cellwalker cw(c, hrand(c->type)); + cell* cc[5]; + cc[2] = c; + cellwalker cw2 = cw; + cwstep(cw); cc[3] = cw.c; cwrevstep(cw); cc[4] = cw.c; + cwrevstep(cw2); cc[1] = cw2.c; cwrevstep(cw2); cc[0] = cw2.c; + bool ok = true; + for(int i=1; i<4; i++) { + forCellEx(c2, cc[i]) if(c2->wall == waArrowTrap) ok = false; + if(cc[i]->land != laNone && cc[i]->land != laTerracotta) ok = false; + if(cc[i]->bardir != NODIR) ok = false; + cc[i]->bardir = NOBARRIERS; + } + if(ok) { + for(int i=1; i<4; i++) + cc[i]->wall = waArrowTrap, + cc[i]->wparam = 0; + cc[0]->wall = waStone; + cc[4]->wall = waStone; + } + } + if(pseudohept(c) && hrand(100) < 40 && c->wall == waNone) { + c->wall = waTerraWarrior; + c->landparam = 0; + } + } + if(c->land == laDryForest) { if(randomPatternsMode) c->wall = RANDPAT ? waNone : RANDPATV(laHell) ? waBigTree : waSmallTree; @@ -4652,6 +4731,23 @@ void setdist(cell *c, int d, cell *from) { if(d == 7 && c->land == laCaves && c->wall == waCavewall && hrand(5000) < items[itGold] + hard && !safety) c->monst = moSeep; + + if(d == 7 && c->land == laDogPlains) { + if(hrand(1000) < 10) { + if(openplains(c)) { + c->item = itDogPlains; + vector next; + forCellEx(c2, c) if(c2->mpdist > 7) next.push_back(c2); + if(size(next) && items[itDogPlains] < 10) { + cell *c3 = next[hrand(size(next))]; + forCellEx(c4, c3) if(c4->mpdist > 7 && !isNeighbor(c4, c)) + c4->monst = moHunterGuard; + } + } + } + if(hrand(5000) < items[itDogPlains]- 17 + hard) + c->monst = moHunterDog; + } if(d == 7 && c->land == laLivefjord && c->wall == waSea && hrand(5000) < 15 + items[itFjord] + hard && !safety) { if(items[itFjord] >= 5 && hrand(100) < 20 && !peace::on) @@ -4821,7 +4917,7 @@ void setdist(cell *c, int d, cell *from) { placePrizeOrb(c); } - if(d == 7 && c->wall == waIcewall && c->land != laIce && c->land != laCocytus) + if(d == 7 && c->wall == waIcewall && c->land != laIce && c->land != laCocytus && c->land != laBlizzard) c->wall = waNone; if(d == 7 && c->wall == waRed3 && c->land != laRedRock) @@ -4972,6 +5068,27 @@ void setdist(cell *c, int d, cell *from) { if(hrand(8000) < 2 * (items[itDiamond] + hard)) c->monst = hrand(2) ? moYeti : moWolf; } + + if(c->land == laBlizzard) { + if(hrand(8000) < 10 + 2 * (items[itBlizzard] + hard)) + c->monst = pick(moVoidBeast, moIceGolem); + } + + if(c->land == laAlchemy2) { + if(hrand(5000) < PT(100 + 2 * kills[moLemur], 200) && notDippingFor(itAlchemy2)) + c->item = itAlchemy2; + if(hrand(8000) < 2 * (items[itAlchemy2] + hard)) + c->monst = moLemur; + } + + if(c->land == laTerracotta) { + bool nearwarrior = false; + forCellEx(c2, c) if(c2->wall == waTerraWarrior) nearwarrior = true; + if(nearwarrior && hrand(5000) < PT(100 + 2 * kills[moMercuryGuy], 200) && notDippingFor(itTerra)) + c->item = itTerra; + if(hrand(8000) < 2 * (items[itTerra] + hard)) + c->monst = moMercuryGuy; + } if(c->land == laTrollheim && !safety) { if(hrand(8000) < items[itTrollEgg] + hardness_empty()) @@ -5482,7 +5599,7 @@ void setdist(cell *c, int d, cell *from) { #endif } -bool wchance(int a, int of) { +bool wchance(int a, int of, int reduction = 0) { of *= 10; a += yendor::hardness() + 1; if(isCrossroads(cwt.c->land)) @@ -5492,6 +5609,10 @@ bool wchance(int a, int of) { for(int i=0; iland == laIce && wchance(items[itDiamond], 10)) c->monst = hrand(2) ? moWolf : moYeti; + else if(c->land == laDogPlains && wchance(items[itDogPlains], 50, 26)) + c->monst = moHunterDog; + else if(c->land == laDesert && wchance(items[itSpice], 10)) c->monst = (hrand(10) || peace::on) ? moDesertman : moWorm; diff --git a/orbs.cpp b/orbs.cpp index e8dcb27e..d4fb7a56 100644 --- a/orbs.cpp +++ b/orbs.cpp @@ -440,8 +440,7 @@ void castLightningBolt(cellwalker lig) { bnc++; if(bnc > 10) break; } else { - cwspin(lig, 3); - if(c->type == 7) cwspin(lig, hrand(2)); + cwrev(lig); } if(lig.c->wall == waCloud) { @@ -654,8 +653,6 @@ eMonster summonedAt(cell *dest) { return dest->land == laPalace ? moPalace : moBat; if(dest->wall == waFloorA || dest->wall == waFloorB) return moSlime; - if(dest->wall == waFloorC || dest->wall == waFloorD) - return moRatling; if(dest->wall == waCavefloor) return moTroll; if(dest->wall == waDeadfloor) @@ -698,6 +695,7 @@ eMonster summonedAt(cell *dest) { if(dest->wall == waGiantRug) return moVizier; if(dest->wall == waNone) { + if(dest->land == laDogPlains) return moAirElemental; if(dest->land == laBull) return moRagingBull; if(dest->land == laPrairie) return moAirElemental; if(dest->land == laZebra) return moAirElemental;