// Hyperbolic Rogue -- Blizzard // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details /** \file blizzard.cpp * \brief special graphical effects, such as the Blizzard and arrow traps */ #include "hyper.h" namespace hr { struct snowball { transmatrix T; shiftmatrix global; snowball *prev; snowball *next; double phase; snowball(int t) { T = rgpushxto0(randomPointIn(t)); phase = randd(); } }; #if HDR struct blizzardcell; #endif struct blizzardcell { cell *c; int frame; int tmp; shiftmatrix *gm; char wmap; int inward, outward, ward; vector qty; vector inorder, outorder; int inid, outid; ~blizzardcell() { for(auto i: inorder) delete i; } }; EX map blizzardcells; EX void set_blizzard_frame(cell *c, int frameid) { blizzardcells[c].frame = frameid; } EX vector bcells; int blizzard_N; EX blizzardcell* getbcell(cell *c) { int i = c->listindex; if(i<0 || i >= blizzard_N) return NULL; if(bcells[i]->c != c) return NULL; return bcells[i]; } EX void drawBlizzards() { #if CAP_SHAPES && CAP_FIELD poly_outline = OUTLINE_NONE; auto it = blizzardcells.begin(); bcells.clear(); while(it != blizzardcells.end()) if(it->second.frame != frameid || !gmatrix.count(it->first)) it = blizzardcells.erase(it); else { it->second.c = it->first; bcells.push_back(&it->second); it++; } blizzard_N = isize(bcells); for(int i=0; ilistindex, bc.c->listindex = i; bc.gm = &gmatrix[bc.c]; bc.wmap = windmap::at(bc.c); } for(int i=0; itype); for(int i=0; itype; i++) { int& qty = bc.qty[i]; qty = 0; cell *c2 = c->move(i); if(!c2) continue; auto bc2 = getbcell(c2); if(!bc2) continue; int z = (bc2->wmap - bc.wmap) & 255; if(z >= windmap::NOWINDBELOW && z < windmap::NOWINDFROM) bc.outward += qty = z / 8; z = (-z) & 255; if(z >= windmap::NOWINDBELOW && z < windmap::NOWINDFROM) bc.inward += z / 8, qty = -z/8; } bc.ward = max(bc.inward, bc.outward); while(isize(bc.inorder) < bc.ward) { auto sb = new snowball(c->type); bc.inorder.push_back(sb); bc.outorder.push_back(sb); } for(auto& sb: bc.inorder) sb->prev = sb->next = NULL; bc.inid = 0; } double at = fractick(40); for(int i=0; iglobal = (*bc.gm) * sb->T; } for(int i=0; itype; d++) for(int k=0; kmove(d)); auto& sball = *bc.outorder[bc.outid++]; auto& sball2 = *bc2.inorder[bc2.inid++]; sball.next = &sball2; sball2.prev = &sball; hyperpoint t = inverse_shift(sball.global, tC0(sball2.global)); double at0 = at + sball.phase; if(at0>1) at0 -= 1; shiftmatrix tpartial = sball.global * rspintox(t) * xpush(hdist0(t) * at0); if(wmascii || wmblack) queuestr(tpartial, .2, ".", 0xFFFFFF); else queuepoly(tpartial, cgi.shSnowball, 0xFFFFFF80); } } for(int ii=0; iiglobal)); if(sb2.next) swapcost -= hdist(tC0(sb2.global), tC0(sb2.next->global)); if(sb1.next) swapcost += hdist(tC0(sb2.global), tC0(sb1.next->global)); if(sb2.next) swapcost += hdist(tC0(sb1.global), tC0(sb2.next->global)); if(swapcost < 0) { swap(bc.outorder[i], bc.outorder[j]); swap(sb1.next, sb2.next); if(sb1.next) sb1.next->prev = &sb1; if(sb2.next) sb2.next->prev = &sb2; } } if(1) { auto& sb1 = *bc.inorder[i]; auto& sb2 = *bc.inorder[j]; double swapcost = 0; if(sb1.prev) swapcost -= hdist(tC0(sb1.global), tC0(sb1.prev->global)); if(sb2.prev) swapcost -= hdist(tC0(sb2.global), tC0(sb2.prev->global)); if(sb1.prev) swapcost += hdist(tC0(sb2.global), tC0(sb1.prev->global)); if(sb2.prev) swapcost += hdist(tC0(sb1.global), tC0(sb2.prev->global)); if(swapcost < 0) { swap(bc.inorder[i], bc.inorder[j]); swap(sb1.prev, sb2.prev); if(sb1.prev) sb1.prev->next = &sb1; if(sb2.prev) sb2.prev->next = &sb2; } } auto& sbp = *bc.inorder[i]; if(sbp.next && sbp.prev) { double p1 = sbp.next->phase; double p2 = sbp.prev->phase; double d = p2-p1; if(d<=.5) d+=1; if(d>=.5) d-=1; sbp.phase = p1 + d/2; if(sbp.phase >= 1) sbp.phase -= 1; if(sbp.phase < 0) sbp.phase += 1; } } for(auto bc: bcells) bc->c->listindex = bc->tmp; #endif } EX vector arrowtraps; EX void drawArrowTraps() { for(cell *c: arrowtraps) { auto r = traplimits(c); try { shiftmatrix& t0 = gmatrix.at(r[0]); shiftmatrix& t1 = gmatrix.at(r[4]); ignore(t0); ignore(t1); #if CAP_QUEUE queueline(tC0(t0), tC0(t1), 0xFF0000FF, 4 + vid.linequality, PPR::ITEM); #endif #if CAP_SHAPES if((c->wparam & 7) == 3 && !shmup::on) { // queueline(t0 * randomPointIn(r[0]->type), t1 * randomPointIn(r[1]->type), 0xFFFFFFFF, 4, PPR::ITEM); int tt = int(fractick(64) * 401); for(int u=0; u<2; u++) { shiftmatrix& tu = u ? t0 : t1; shiftmatrix& tv = u ? t1 : t0; hyperpoint trel = inverse_shift(tu, tC0(tv)); shiftmatrix tpartial = tu * rspintox(trel) * xpush(hdist0(trel) * tt / 401.0); tpartial = tpartial * ypush(.05); if(GDIM == 3) tpartial = tpartial * cspin90(1, 2); queuepoly(tpartial, cgi.shTrapArrow, 0xFFFFFFFF); } } #endif } catch(out_of_range&) {} } } auto ccm_blizzard = addHook(hooks_clearmemory, 0, [] () { arrowtraps.clear(); blizzardcells.clear(); bcells.clear(); }) + + addHook(hooks_gamedata, 0, [] (gamedata* gd) { gd->store(arrowtraps); gd->store(blizzardcells); gd->store(bcells); gd->store(blizzard_N); }) + addHook(hooks_removecells, 0, [] () { eliminate_if(arrowtraps, is_cell_removed); }); }