diff --git a/classes.cpp b/classes.cpp index 51d0c52f..faeeaca5 100644 --- a/classes.cpp +++ b/classes.cpp @@ -1202,6 +1202,7 @@ itemtype iinf[ittypes] = { "This ranged Orb will transform the target monster into one without any special powers. It also stuns them for one turn. " "Does not affect multi-tile monsters."}, { '!', 0x80FF00, "Glowing Crystal", crystaldesc}, + { '!', 0x80FF80, "Snake Oil", NODESCYET}, }; // --- wall types --- @@ -1563,7 +1564,8 @@ const landtype linf[landtypes] = { { 0x207068, "Hunting Ground", huntingdesc}, { 0xE2725B, "Terracotta Army", terraldesc}, { 0xE2725B, "Terracotta Army", terraldesc}, - { 0x80FF00, "Crystal World", crystaldesc} + { 0x80FF00, "Crystal World", crystaldesc}, + { 0x306030, "Snake Nest", NODESCYET}, }; struct landtacinfo { eLand l; int tries, multiplier; }; diff --git a/classes.h b/classes.h index 720f68eb..ff361ad7 100644 --- a/classes.h +++ b/classes.h @@ -68,7 +68,7 @@ struct genderswitch_t { #define NUM_GS 6 -static const int ittypes = 122; +static const int ittypes = 123; struct itemtype { char glyph; @@ -112,7 +112,7 @@ enum eItem { itInventory, itLavaLily, itHunting, itBlizzard, itTerra, itOrbSide1, itOrbSide2, itOrbSide3, - itOrbLava, itOrbMorph, itGlowCrystal + itOrbLava, itOrbMorph, itGlowCrystal, itSnake }; static const int walltypes = 105; @@ -158,7 +158,7 @@ enum eWall { waNone, waIcewall, waBarrier, waFloorA, waFloorB, waCavewall, waCav waArrowTrap, waMercury, waMagma }; -static const int landtypes = 78; +static const int landtypes = 79; struct landtype { int color; @@ -184,7 +184,7 @@ enum eLand { laNone, laBarrier, laCrossroads, laDesert, laIce, laCaves, laJungle laMirrorWall, laMirrored, laMirrorWall2, laMirrored2, laMirrorOld, laVolcano, laBlizzard, laHunting, laTerracotta, laMercuryRiver, - laDual + laDual, laSnakeNest }; enum eGeometry {gNormal, gEuclid, gSphere, gElliptic, gQuotient, gQuotient2, gTorus, gOctagon, g45, g46, g47, gSmallSphere, gTinySphere, gGUARD}; diff --git a/game.cpp b/game.cpp index ec381b1d..eea5ba27 100644 --- a/game.cpp +++ b/game.cpp @@ -21,6 +21,8 @@ bool survivalist; bool hardcore = false; int hardcoreAt; +set snaketypes; + flagtype havewhat, hadwhat; #define HF_BUG Flag(0) @@ -2656,6 +2658,7 @@ void bfs() { hadwhat = havewhat; havewhat = 0; + snaketypes.clear(); if(!(hadwhat & HF_WARP)) { avengers = 0; } if(!(hadwhat & HF_MIRROR)) { mirrorspirits = 0; } @@ -2793,6 +2796,8 @@ void bfs() { survivalist = false; if(c2->monst == moHexSnake || c2->monst == moHexSnakeTail) { havewhat |= HF_HEX; + if(c2->mondir != NODIR) + snaketypes.insert(snake_pair(c2)); if(c2->monst == moHexSnake) hexsnakes.push_back(c2); else findWormIvy(c2); } @@ -4374,8 +4379,16 @@ bool goodmount(cell *c, bool mounted) { else return !isMounted(c); } +int inpair(cell *c, int colorpair) { + return (colorpair >> pattern_threecolor(c)) & 1; + } + +int snake_pair(cell *c) { + return (1 << pattern_threecolor(c)) | (1 << pattern_threecolor(c->mov[c->mondir])); + } + // note: move from 'c' to 'from'! -void hexvisit(cell *c, cell *from, int d, bool mounted) { +void hexvisit(cell *c, cell *from, int d, bool mounted, int colorpair) { if(!c) return; if(cellUnstable(c) || cellEdgeUnstable(c)) return; if(eq(c->aitmp, sval)) return; @@ -4387,10 +4400,10 @@ void hexvisit(cell *c, cell *from, int d, bool mounted) { if(from->cpdist && (!passable(from, c, P_MONSTER|P_WIND|P_FISH))) return; - if(c->monst == moHexSnake) { + if(c->monst == moHexSnake && snake_pair(c) == colorpair) { // printf("got snake\n"); - if(pseudohept(from)) return; + if(!inpair(from, colorpair)) return; if(!goodmount(c, mounted)) return; if(canAttack(c, moHexSnake, from, from->monst, AF_EAT | (mounted ? AF_ONLY_ENEMY : AF_ONLY_FBUG | AF_GETPLAYER))) { @@ -4410,7 +4423,7 @@ void hexvisit(cell *c, cell *from, int d, bool mounted) { hexdfs.push_back(c); } -void movehex(bool mounted) { +void movehex(bool mounted, int colorpair) { sval++; hexdfs.clear(); @@ -4429,29 +4442,29 @@ void movehex(bool mounted) { for(int i=0; itype; t++) if(c->mov[t] && !pseudohept(c->mov[t])) + for(int t=0; ttype; t++) if(c->mov[t] && inpair(c->mov[t], colorpair)) dirtable[qdirtable++] = t; random_shuffle(dirtable, dirtable+qdirtable); while(qdirtable--) { int t = dirtable[qdirtable]; - hexvisit(c->mov[t], c, t, mounted); + hexvisit(c->mov[t], c, t, mounted, colorpair); } } for(int i=0; imonst == moHexSnake) { + if(c->monst == moHexSnake && snake_pair(c) == colorpair) { if(!goodmount(c, mounted)) continue; int t[MAX_EDGE]; for(int i=0; itype; i++) t[i] = i; for(int j=1; jtype; j++) swap(t[j], t[hrand(j+1)]); for(int u=0; utype; u++) { createMov(c, t[u]); - if(!pseudohept(c->mov[t[u]])) - hexvisit(c, c->mov[t[u]], c->spn(t[u]), mounted); + if(inpair(c->mov[t[u]], colorpair)) + hexvisit(c, c->mov[t[u]], c->spn(t[u]), mounted, colorpair); } } - if(c->monst == moHexSnake) { + if(c->monst == moHexSnake && snake_pair(c) == colorpair) { snakeAttack(c, mounted); kills[moHexSnake]++; playSound(c, "die-troll"); @@ -5479,6 +5492,13 @@ void checkAmbushState() { } } + +void movehex_all() { + for(int i: snaketypes) { + movehex(false, i); + if(!shmup::on && haveMount()) movehex(true, i); + } + } void movemonsters() { ambush_distance = 0; @@ -5550,10 +5570,8 @@ void movemonsters() { savepos[i] = playerpos(i); moveworms(); - if(havewhat & HF_HEX) { - movehex(false); - if(haveMount()) movehex(true); - } + if(havewhat & HF_HEX) + movehex_all(); if(havewhat & HF_KRAKEN) kraken::attacks(), groupmove(moKrakenH, 0); if(havewhat & HF_DRAGON) groupmove(moDragonHead, MF_NOFRIEND); diff --git a/graph.cpp b/graph.cpp index 5fb0237e..6f7e29b7 100644 --- a/graph.cpp +++ b/graph.cpp @@ -1717,6 +1717,12 @@ bool drawMonster(const transmatrix& Vparam, int ct, cell *c, int col) { if(isIvy(c) || isWorm(c) || isMutantIvy(c) || c->monst == moFriendlyIvy) { + if((m == moHexSnake || m == moHexSnakeTail) && c->land == laSnakeNest && c->mondir != NODIR) { + int c1 = nestcolors[pattern_threecolor(c)]; + int c2 = nestcolors[pattern_threecolor(c->mov[c->mondir])]; + col = (c1 + c2); // sum works because they are dark and should be brightened + } + if(isDragon(c->monst) && c->stuntime == 0) col = 0xFF6000; transmatrix Vb0 = Vb; @@ -1835,8 +1841,10 @@ bool drawMonster(const transmatrix& Vparam, int ct, cell *c, int col) { queuepoly(Vbb, shDragonWings, darkena(col, c->hitpoints?0:1, 0xFF)); } } - else if(!(c->mondir == NODIR && (c->monst == moTentacletail || (c->monst == moWormtail && wormpos(c) < WORMLENGTH)))) - queuepoly(Vb, shJoint, darkena(col, 0, 0xFF)); + else { + if(!(c->mondir == NODIR && (c->monst == moTentacletail || (c->monst == moWormtail && wormpos(c) < WORMLENGTH)))) + queuepoly(Vb, shJoint, darkena(col, 0, 0xFF)); + } } if(!mmmon) return true; @@ -2407,6 +2415,8 @@ ld wavefun(ld x) { else return 0; */ } +const unsigned int nestcolors[8] = { 0x800000, 0x008000, 0x000080, 0x404040, 0x700070, 0x007070, 0x707000, 0x606060 }; + void setcolors(cell *c, int& wcol, int &fcol) { wcol = fcol = winf[c->wall].color; @@ -2707,6 +2717,14 @@ void setcolors(cell *c, int& wcol, int &fcol) { else fcol = gradient(0xD0D090, 0xD0D020, -1, sin((double) c->landparam), 1); break; + + case laSnakeNest: { + int fv = pattern_threecolor(c); + fcol = nestcolors[fv&7]; + if(realred(c->wall)) + wcol = fcol * (4 + snakelevel(c)) / 4; + break; + } default: if(isElemental(c->land)) fcol = linf[c->land].color; @@ -3133,6 +3151,9 @@ int getfd(cell *c) { case laReptile: case laCanvas: return 0; + + case laSnakeNest: + return realred(c->wall) ? 0 : 1; case laTerracotta: case laMercuryRiver: @@ -3173,6 +3194,19 @@ int getfd(cell *c) { return 2; } } + +int getSnakelevColor(cell *c, int i, int last, int fd, int wcol) { + int col; + if(c->wall == waTower) + col = 0xD0D0D0-i*0x101010; + else if(c->land == laSnakeNest) + return darkena(nestcolors[pattern_threecolor(c)] * (5 + i) / 4, 0, 0xFF); + else if(i == last-1) + col = wcol; + else + col = winf[waRed1+i].color; + return darkena(col, fd, 0xFF); + } void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { @@ -3843,7 +3877,7 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { else if(c->land == laLivefjord) qfloor(c, Vf, CAVEFLOOR, darkena(fcol, fd, 0xFF)); - else if(c->land == laRedRock) + else if(c->land == laRedRock || c->land == laSnakeNest) qfloor_eswap(c, Vf, DESERTFLOOR, darkena(fcol, fd, 0xFF)); else if(c->land == laPalace || c->land == laTerracotta) @@ -3917,11 +3951,11 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { if(realred(c->wall) && !wmspatial) { int s = snakelevel(c); if(s >= 1) - qfloor(c, V, shRedRockFloor[0][ct6], darkena(winf[waRed1].color, 0, 0xFF)); + qfloor(c, V, shRedRockFloor[0][ct6], getSnakelevColor(c, 0, 7, fd, wcol)); if(s >= 2) - queuepoly(V, shRedRockFloor[1][ct6], darkena(winf[waRed2].color, 0, 0xFF)); + queuepoly(V, shRedRockFloor[1][ct6], getSnakelevColor(c, 1, 7, fd, wcol)); if(s >= 3) - queuepoly(V, shRedRockFloor[2][ct6], darkena(winf[waRed3].color, 0, 0xFF)); + queuepoly(V, shRedRockFloor[2][ct6], getSnakelevColor(c, 2, 7, fd, wcol)); } if(c->wall == waTower && !wmspatial) { @@ -4076,13 +4110,11 @@ void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) { bool w = isWarped(c); warpfloor(c, (*Vdp), darkena(wcol, fd, 0xFF), PPR_REDWALL-4+4*sl, w); floorShadow(c, V, SHADOW_SL * sl, w); - bool tower = c->wall == waTower; for(int s=0; s= sl2) - placeSidewallX(c, i, SIDE_SLEV+s, V, w, false, - darkena(tower?0xD0D0D0-i*0x101010 : s==sl-1?wcol:winf[waRed1+s].color, fd, 0xFF)); + placeSidewallX(c, i, SIDE_SLEV+s, V, w, false, getSnakelevColor(c, s, sl, fd, wcol)); } } diff --git a/hyper.h b/hyper.h index b95cd028..7e699f5b 100644 --- a/hyper.h +++ b/hyper.h @@ -2295,3 +2295,8 @@ namespace fieldpattern { } int emeraldval(cell *c); + +int inpair(cell *c, int colorpair); +int snake_pair(cell *c); + +extern const unsigned int nestcolors[8]; diff --git a/landgen.cpp b/landgen.cpp index a3b0dfd4..59bf6bdd 100644 --- a/landgen.cpp +++ b/landgen.cpp @@ -1477,30 +1477,8 @@ void giantLandSwitch(cell *c, int d, cell *from) { int i = -1; for(int t=0; ttype; t++) if(c->mov[t]->mpdist > c->mpdist && !pseudohept(c->mov[t])) i = t; - if(i != -1 && !peace::on) { - c->monst = moHexSnake; - preventbarriers(c); - int len = nontruncated ? 2 : ROCKSNAKELENGTH; - cell *c2 = c; - vector rocksnake; - while(--len) { - rocksnake.push_back(c2); - preventbarriers(c2); - c2->mondir = i; - createMov(c2, i); - int j = c2->spn(i); - cell *c3 = c2->mov[i]; - if(c3->monst || c3->bardir != NODIR || c3->wall) break; - c2 = c3; - c2->monst = moHexSnakeTail; - i = (j + (S6==8 ? 4 : (len%2 ? 2 : 4))) % S6; - } - if(size(rocksnake) < ROCKSNAKELENGTH/2 && !nontruncated) { - for(int i=0; imonst = moNone; - } - else c2->mondir = NODIR; - } + if(i != -1 && !peace::on) + generateSnake(c, i); } else if(hrand(16000) < 50+items[itRedGem]+yendor::hardness() && (nontruncated?hrand(10)<3:!ishept(c)) && !c->monst) c->monst = moRedTroll, @@ -1508,6 +1486,20 @@ void giantLandSwitch(cell *c, int d, cell *from) { } break; + case laSnakeNest: + ONEMPTY { + if(hrand(30000) < 30+items[itRedGem]+yendor::hardness() && !c->monst && !c->wall && !peace::on) { + vector gooddir; + for(int t=0; ttype; t++) if(c->mov[t]->mpdist > c->mpdist && !pseudohept(c->mov[t])) + gooddir.push_back(t); + if(size(gooddir)) + generateSnake(c, gooddir[hrand(size(gooddir))]); + } + else if(hrand(16000) < kills[moHexSnake] * 100 && c->wall == waNone) + c->item = itSnake, c->wall = waRed3; + } + break; + case laWarpSea: case laWarpCoast: if(d == 9 && randomPatternsMode) @@ -2055,7 +2047,7 @@ void repairLandgen(cell *c) { if(c->wall == waIcewall && c->land != laIce && c->land != laCocytus && c->land != laBlizzard) c->wall = waNone; - if(c->wall == waRed3 && c->land != laRedRock) + if(c->wall == waRed3 && c->land != laRedRock && c->land != laSnakeNest) c->wall = waNone; if(c->item == itRedGem && c->land != laRedRock) diff --git a/landlock.cpp b/landlock.cpp index f0e4c8ea..977c9010 100644 --- a/landlock.cpp +++ b/landlock.cpp @@ -200,6 +200,9 @@ int isNative(eLand l, eMonster m) { case laDual: return m == moRatling ? 2 : 0; + + case laSnakeNest: + return m == moHexSnake ? 2 : 0; case laCA: return 0; } @@ -294,6 +297,7 @@ eItem treasureType(eLand l) { case laBlizzard: return itBlizzard; case laHunting: return itHunting; case laDual: return itGlowCrystal; + case laSnakeNest: return itSnake; case laCA: return itNone; } @@ -399,7 +403,7 @@ bool landUnlocked(eLand l) { case laStorms: case laWhirlwind: return gold() >= R60; - case laWildWest: case laHalloween: case laDual: + case laWildWest: case laHalloween: case laDual: case laSnakeNest: return false; case laIce: case laJungle: case laCaves: case laDesert: diff --git a/mapeditor.cpp b/mapeditor.cpp index 40ac472f..91cba38e 100644 --- a/mapeditor.cpp +++ b/mapeditor.cpp @@ -1820,10 +1820,8 @@ namespace mapeditor { return pseudohept(c) ? 0x202020 : 0xC0C0C0; } if(whichCanvas == 'T') { - static unsigned int fcol[8] = { 0x800000, 0x008000, 0x000080, 0x404040, - 0x800080, 0x008080, 0x808000, 0xD0D0D0 }; int fv = pattern_threecolor(c); - return fcol[fv&7]; + return nestcolors[fv&7]; } return canvasback; } diff --git a/monstergen.cpp b/monstergen.cpp index 9f00dd03..0753c596 100644 --- a/monstergen.cpp +++ b/monstergen.cpp @@ -626,3 +626,37 @@ void wandering() { } } +void generateSnake(cell *c, int i) { + c->monst = moHexSnake; + int cpair = (1<mov[i])); + preventbarriers(c); + int len = nontruncated ? 2 : ROCKSNAKELENGTH; + cell *c2 = c; + vector rocksnake; + while(--len) { + rocksnake.push_back(c2); + preventbarriers(c2); + c2->mondir = i; + createMov(c2, i); + int j = c2->spn(i); + cell *c3 = c2->mov[i]; + if(c3->monst || c3->bardir != NODIR || c3->wall) break; + c2 = c3; + c2->monst = moHexSnakeTail; + i = (j + (c2->type%4 == 0 ? c2->type/2 : (len%2 ? 2 : c2->type - 2))) % S6; + createMov(c2, i); + if(!inpair(c2->mov[i], cpair)) { + vector goodsteps; + for(int i=0; itype; i++) + if(inpair(c2->mov[i], cpair)) + goodsteps.push_back(i); + if(!size(goodsteps)) break; + i = goodsteps[hrand(size(goodsteps))]; + } + } + if(size(rocksnake) < ROCKSNAKELENGTH/2 && !nontruncated) { + for(int i=0; imonst = moNone; + } + else c2->mondir = NODIR; + } diff --git a/pattern2.cpp b/pattern2.cpp index ca5d8aec..ec5a7e7b 100644 --- a/pattern2.cpp +++ b/pattern2.cpp @@ -677,24 +677,13 @@ int pattern_threecolor(cell *c) { if(S7 == 3 && nontruncated) return c->master->fiftyval; if(euclid) return eupattern(c); - return 3; + return ishept(c); } // returns ishept in the normal tiling; // in the 'pure heptagonal' tiling, returns true for a set of cells // which roughly corresponds to the heptagons in the normal tiling bool pseudohept(cell *c) { - if(nontruncated) { - if(bigsphere) - return - c->master == getDodecahedron(3) || - c->master == getDodecahedron(5) || - c->master == getDodecahedron(6); - if(S7 == 3) - return c->master == getDodecahedron(0); - else - return pattern_threecolor(c) == 0; - } - else return ishept(c); + return pattern_threecolor(c) == 0; } diff --git a/shmup.cpp b/shmup.cpp index 6a7de108..c8876a3a 100644 --- a/shmup.cpp +++ b/shmup.cpp @@ -2961,7 +2961,7 @@ void turn(int delta) { moveworms(); moveivy(); movemutant(); - if(havewhat&HF_HEX) movehex(false); + if(havewhat&HF_HEX) movehex_all(); wandering(); livecaves(); terracotta();