diff --git a/blizzard.cpp b/blizzard.cpp index cc219937..1338febe 100644 --- a/blizzard.cpp +++ b/blizzard.cpp @@ -202,10 +202,10 @@ void drawArrowTraps() { try { transmatrix& t0 = gmatrix.at(r[0]); - transmatrix& t1 = gmatrix.at(r[1]); + transmatrix& t1 = gmatrix.at(r[4]); queueline(tC0(t0), tC0(t1), 0xFF0000FF, 4, PPR_ITEM); - if((c->wparam & 7) == 3) { + if((c->wparam & 7) == 3 && !shmup::on) { // queueline(t0 * randomPointIn(r[0]->type), t1 * randomPointIn(r[1]->type), 0xFFFFFFFF, 4, PPR_ITEM); int tt = ticks % 401; @@ -221,4 +221,9 @@ void drawArrowTraps() { catch(out_of_range) {} } } - \ No newline at end of file + +auto ccm_blizzard = addHook(clearmemory, 0, [] () { + arrowtraps.clear(); + blizzardcells.clear(); + bcells.clear(); + }); diff --git a/complex.cpp b/complex.cpp index 70bdd170..5a4e1a98 100644 --- a/complex.cpp +++ b/complex.cpp @@ -2008,7 +2008,7 @@ namespace heat { } } - if(c->wall == waArrowTrap && c->wparam) { + if(c->wall == waArrowTrap && c->wparam && !shmup::on) { c->wparam++; if(c->wparam == 3) { if(canAttack(c, moArrowTrap, c, c->monst, AF_GETPLAYER)) diff --git a/flags.cpp b/flags.cpp index 0fd311c8..c64cea62 100644 --- a/flags.cpp +++ b/flags.cpp @@ -183,8 +183,8 @@ bool isAnyIvy(eMonster m) { bool isBulletType(eMonster m) { return - m == moBullet || m == moFlailBullet || - m == moFireball || m == moTongue || m == moAirball; + m == moBullet || m == moFlailBullet || m == moFireball || + m == moTongue || m == moAirball || m == moArrowTrap; } bool isMutantIvy(cell *c) { return isMutantIvy(c->monst); } diff --git a/game.cpp b/game.cpp index 5a0e473c..e1f2c01b 100644 --- a/game.cpp +++ b/game.cpp @@ -2983,26 +2983,29 @@ bool isCentralTrap(cell *c) { return i == 2; } -array traplimits(cell *c) { - array res; +array traplimits(cell *c) { + array res; int q = 0; + res[2] = c; for(int d=0; dtype; d++) { cellwalker cw(c, d); cwstep(cw); if(cw.c->wall != waArrowTrap) continue; + res[1+q*2] = cw.c; cwspin(cw, cw.c->type/2); if((cw.c->type&1) && cwpeek(cw, 0)->wall != waStone) cwspin(cw, 1); cwstep(cw); - res[q++] = cw.c; + res[(q++)*4] = cw.c; } - while(q<2) res[q++] = NULL; + while(q<2) { res[q*4] = res[1+q*2] = NULL; q++; } return res; } void activateArrowTrap(cell *c) { if(c->wall == waArrowTrap && c->wparam == 0) { - c->wparam = 1; + c->wparam = shmup::on ? 2 : 1; forCellEx(c2, c) activateArrowTrap(c2); + if(shmup::on) shmup::activateArrow(c); } } @@ -3093,7 +3096,7 @@ 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)) + if(c2->wall == waArrowTrap && c2->wparam == 0 && !markOrb(itOrbAether)) activateArrowTrap(c2); princess::playernear(c2); @@ -6620,27 +6623,37 @@ namespace orbbull { // predictable or not static constexpr bool randterra = false; +void terracotta(cell *c) { + if(c->wall == waTerraWarrior) { + bool live = false; + if(randterra) { + c->landparam++; + if((c->landparam == 3 && hrand(3) == 0) || + (c->landparam == 4 && hrand(2) == 0) || + c->landparam == 5) + live = true; + } + else { + c->landparam--; + live = !c->landparam; + } + if(live) + c->monst = moTerraWarrior, + c->hitpoints = 7, + c->wall = waNone; + } + } + void terracotta() { for(int i=0; iwall == waTerraWarrior) { - bool live = false; - if(randterra) { - c2->landparam++; - if((c2->landparam == 3 && hrand(3) == 0) || - (c2->landparam == 4 && hrand(2) == 0) || - c2->landparam == 5) - live = true; - } - else { - c2->landparam--; - live = !c2->landparam; - } - if(live) - c2->monst = moTerraWarrior, - c2->hitpoints = 7, - c2->wall = waNone; + forCellEx(c, playerpos(i)) { + if(shmup::on) { + forCellEx(c2, c) + terracotta(c2); } + else + terracotta(c); + } } void monstersTurn() { diff --git a/hyper.h b/hyper.h index 7e69624a..5f283dd6 100644 --- a/hyper.h +++ b/hyper.h @@ -233,6 +233,7 @@ namespace shmup { void virtualRebase(shmup::monster *m, bool tohex); void fixStorage(); void addShmupHelp(string& out); + void activateArrow(cell *c); } // graph diff --git a/shmup.cpp b/shmup.cpp index 104d7e06..2fa7950a 100644 --- a/shmup.cpp +++ b/shmup.cpp @@ -1444,6 +1444,48 @@ bool hornKills(eMonster m) { m != moSalamander && m != moTerraWarrior; } +queue> traplist; + +void activateArrow(cell *c) { + if(isCentralTrap(c)) + traplist.emplace(ticks + 500, c); + } + +monster arrowtrap_fakeparent; + +void doTraps() { + while(true) { + if(traplist.empty()) return; + auto t = traplist.front(); + if(t.first > ticks) return; + int d = t.second->wparam; + if(d == 2) { + auto tl = traplimits(t.second); + for(int i=1; i<4; i++) if(tl[i]) tl[i]->wparam = 3; + traplist.emplace(t.first + 500, t.second); + + for(int i=0; i<5; i += 4) try { + transmatrix& tu = gmatrix.at(tl[i]); + transmatrix& tv = gmatrix.at(tl[4-i]); + monster* bullet = new monster; + bullet->base = tl[i]; + bullet->at = rspintox(inverse(tu) * tC0(tv)); + bullet->type = moArrowTrap; + bullet->parent = &arrowtrap_fakeparent; + bullet->pid = 0; + bullet->parenttype = moArrowTrap; + additional.push_back(bullet); + } + catch(out_of_range) {} + } + else if(d == 3) { + auto tl = traplimits(t.second); + for(int i=1; i<4; i++) if(tl[i]) tl[i]->wparam = 0; + } + traplist.pop(); + } + } + bool hornStuns(eMonster m) { return !isBulletType(m) && m != moRoseBeauty; } @@ -1756,6 +1798,9 @@ void movePlayer(monster *m, int delta) { if(c2->wall == waClosePlate || c2->wall == waOpenPlate) toggleGates(c2, c2->wall); + + if(c2->wall == waArrowTrap && c2->wparam == 0 && !markOrb(itOrbAether)) + activateArrowTrap(c2); if(c2->item == itOrbYendor && !peace::on) yendor::check(c2); collectItem(c2); @@ -2106,6 +2151,8 @@ void moveBullet(monster *m, int delta) { m->vel = 1/500.; else if(m->type == moAirball) m->vel = 1/200.; + else if(m->type == moArrowTrap) + m->vel = 1/200.; else if(m->type == moTongue) { m->vel = 1/1500.; if(m->isVirtual || !m->parent || intval(nat*C0, m->parent->pat*C0) > SCALE2 * 0.4) @@ -2131,7 +2178,7 @@ void moveBullet(monster *m, int delta) { bool godragon = m->type == moFireball && isDragon(c2->monst); - if(m->type != moTongue && !(godragon || passable(c2, m->base, P_BULLET | P_MIRRORWALL))) { + if(m->type != moTongue && !(godragon || (c2==m->base && m->type == moArrowTrap) || passable(c2, m->base, P_BULLET | P_MIRRORWALL))) { m->dead = true; if(m->type != moAirball) killMonster(c2, m->parent ? m->parent->type : moNone); // cell *c = m->base; @@ -2161,7 +2208,8 @@ void moveBullet(monster *m, int delta) { // items[itOrbWinter] = 100; items[itOrbLife] = 100; if(!m->isVirtual) for(monster* m2: nonvirtual) { - if(m2 == m || (m2 == m->parent && m->vel >= 0) || m2->parent == m->parent) continue; + if(m2 == m || (m2 == m->parent && m->vel >= 0) || m2->parent == m->parent) + continue; // Flailers only killable by themselves if(m2->type == moFlailer && m2 != m->parent) continue; @@ -2221,7 +2269,7 @@ void moveBullet(monster *m, int delta) { continue; } // conventional missiles cannot hurt some monsters - bool conv = (m->type == moBullet || m->type == moFlailBullet || m->type == moTongue); + bool conv = (m->type == moBullet || m->type == moFlailBullet || m->type == moTongue || m->type == moArrowTrap); if(m2->type == moGreater && conv) { m->dead = true; @@ -2243,7 +2291,7 @@ void moveBullet(monster *m, int delta) { } // Knights reflect bullets if(m2->type == moKnight) { - if(m->parent) { + if(m->parent && m->parent != &arrowtrap_fakeparent) { nat = nat * rspintox(inverse(m->pat) * m->parent->pat * C0); m->rebasePat(nat); } @@ -2379,7 +2427,7 @@ void moveMonster(monster *m, int delta) { else { if(m->type == moSleepBull && !m->isVirtual) { - for(monster *m2: nonvirtual) if(m2!=m && m2->type != moBullet) { + for(monster *m2: nonvirtual) if(m2!=m && m2->type != moBullet && m2->type != moArrowTrap) { double d = intval(m2->pat*C0, nat*C0); if(d < SCALE2*3 && m2->type == moPlayer) m->type = moRagingBull; } @@ -2554,7 +2602,7 @@ void moveMonster(monster *m, int delta) { monster* crashintomon = NULL; - if(!m->isVirtual) for(monster *m2: nonvirtual) if(m2!=m && m2->type != moBullet) { + if(!m->isVirtual) for(monster *m2: nonvirtual) if(m2!=m && m2->type != moBullet && m2->type != moArrowTrap) { double d = intval(m2->pat*C0, nat*C0); if(d < SCALE2 * 0.1) crashintomon = m2; } @@ -2619,7 +2667,10 @@ void moveMonster(monster *m, int delta) { if(c2 != m->base && (c2->wall == waClosePlate || c2->wall == waOpenPlate) && !ignoresPlates(m->type)) toggleGates(c2, c2->wall, 3); - + + if(c2 != m->base && c2->wall == waArrowTrap && c2->wparam == 0 && !ignoresPlates(m->type)) + activateArrowTrap(c2); + if(c2 != m->base && mayExplodeMine(c2, m->type)) killMonster(m, moNone); @@ -2891,6 +2942,7 @@ void turn(int delta) { break; case moBullet: case moFlailBullet: case moFireball: case moTongue: case moAirball: + case moArrowTrap: moveBullet(m, delta); break; @@ -2945,6 +2997,8 @@ void turn(int delta) { if(shmup::on) { + doTraps(); + bool tick = curtime >= nextmove; keepLightning = ticks <= lightat + 1000; cwt.c = pc[0]->base; @@ -2977,6 +3031,7 @@ void turn(int delta) { if(havewhat&HF_HEX) movehex(false); wandering(); livecaves(); + terracotta(); heat::processfires(); if(havewhat&HF_WHIRLPOOL) whirlpool::move(); if(havewhat&HF_WHIRLWIND) whirlwind::move(); @@ -3201,6 +3256,11 @@ bool drawMonster(const transmatrix& V, cell *c, const transmatrix*& Vboat, trans } break; } + case moArrowTrap: { + queuepoly(mmscale(view, 1.15), shTrapArrow, 0xFFFFFFFF); + ShadowV(view, shTrapArrow); + break; + } case moTongue: { queuepoly(mmscale(view, 1.15), shTongue, (minf[m->parenttype].color << 8) | 0xFF); ShadowV(view, shTongue); @@ -3261,6 +3321,7 @@ void clearMonsters() { void clearMemory() { clearMonsters(); gmatrix.clear(); + traplist = {}; curtime = 0; nextmove = 0; nextdragon = 0;