diff --git a/celldrawer.cpp b/celldrawer.cpp index 28b0fac6..e0e868d3 100644 --- a/celldrawer.cpp +++ b/celldrawer.cpp @@ -146,7 +146,7 @@ void celldrawer::setcolors() { case laDesert: case laKraken: case laDocks: case laMotion: case laGraveyard: case laWineyard: case laLivefjord: case laRlyeh: case laHell: case laCrossroads: case laJungle: - case laAlchemist: case laFrog: case laCursed: + case laAlchemist: case laFrog: case laCursed: case laDice: fcol = floorcolors[c->land]; break; case laCA: @@ -1600,6 +1600,10 @@ void celldrawer::draw_features() { } poly_outline = OUTLINE_DEFAULT; } + + else if(c->wall == waDie) { + dice::draw_die(c, V); + } else if(c->wall == waExplosiveBarrel) { if(GDIM == 3 && qfi.fshape) { diff --git a/complex2.cpp b/complex2.cpp index 51b8dfe3..a326a9d7 100644 --- a/complex2.cpp +++ b/complex2.cpp @@ -899,6 +899,186 @@ EX void ambush(cell *c, int dogs) { if(result) addMessage(XLAT("You are ambushed!")); } + +EX } + +EX namespace dice { + + /* vector> sides = { + {1, 3, 5}, {0, 4, 2}, {3, 1, 7}, {2, 6, 0}, {5, 7, 1}, {4, 0, 6}, {7, 5, 3}, {6, 2, 4} + }; */ + + vector> sides = { + {13-1, 7-1, 19-1}, {20-1, 12-1, 18-1}, {19-1, 17-1, 16-1}, {14-1, 18-1, 11-1}, {13-1, 18-1, 15-1}, + {14-1, 9-1, 16-1}, { 1-1, 15-1, 17-1}, {20-1, 16-1, 10-1}, {19-1, 6-1, 11-1}, { 8-1, 17-1, 12-1}, + {13-1, 9-1, 4-1}, { 2-1, 10-1, 15-1}, { 1-1, 11-1, 5-1}, {20-1, 4-1, 6-1}, { 7-1, 5-1, 12-1}, + { 8-1, 6-1, 3-1}, { 7-1, 10-1, 3-1}, { 2-1, 5-1, 4-1}, { 1-1, 3-1, 9-1}, { 8-1, 2-1, 14-1} + }; + + vector> spins; + int order; + + int faces() { return isize(sides); } + + EX void generate_on(cell *c) { + c->wparam = hrand(faces()) + faces() * (1 + hrand(3) * 2); + } + + bool prepared; + + void prepare() { + if(prepared) return; + prepared = true; + spins = sides; + order = faces() == 8 ? 4 : 5; + for(int i=0; itype; + + int val = th->wparam % faces(); + int dir = th->wparam / faces(); + + int si = isize(sides[val]); + + if(t % si) { println(hlog, "error: bad roll\n"); return; } + + int sideid = (rdir - dir) * si / t; + if(sideid < 0) sideid += si; + + int val1 = sides[val][sideid]; + + int si1 = isize(sides[val1]); + + println(hlog, tie(val, si, rdir), " to ", tie(val1, si1)); + + int sideid1 = spins[val][sideid]; + + int t1 = cto->type; + if(t1 % si1) { println(hlog, "error: bad roll target\n"); return; } + + int rdir1 = mi.rev_dir_force(); + + int dir1 = rdir1 - sideid1 * t1 / si1; + + dir1 = gmod(dir1, t1); + + th->wall = waNone; + cto->wall = waDie; + cto->wparam = val1 + faces() * dir1; + } + + EX void draw_die(cell *c, const shiftmatrix& V) { + prepare(); + int val = c->wparam % faces(); + int dir = c->wparam / faces(); + queuestr(V, .5, its(val+1), 0xFFFFFFFF); + auto& side = sides[val]; + int si = isize(side); + + for(int i=0; itype * i / isize(side); + d = gmod(d, c->type); + hyperpoint nxt = tC0(currentmap->adj(c, d)); + hyperpoint mid = normalize(C0 * 1.3 + nxt * -.3); + queuestr(V * rgpushxto0(mid), .25, its(side[i]+1), 0xFFFFFFFF); + } + + shiftmatrix V1 = V * iddspin(c, dir) * spin(M_PI); + + vector face_drawn(faces(), false); + + vector > facequeue; + + auto add_to_queue = [&] (const transmatrix& T, int d) { + if(face_drawn[d]) return; + face_drawn[d] = true; + facequeue.emplace_back(T, d); + }; + + add_to_queue(Id, val); + + ld outradius, inradius; + + if(1) { + dynamicval g(geometry, gSphere); + ld alpha = 360 * degree / order; + inradius = edge_of_triangle_with_angles(alpha, 60*degree, 60*degree); + outradius = edge_of_triangle_with_angles(60*degree, alpha, 60*degree); + } + + ld dieradius = 0.5; + + ld base_to_base; + + if(1) { + dynamicval g(geometry, gSpace534); + hyperpoint h = cspin(2, 0, outradius) * zpush0(-dieradius); + base_to_base = binsearch(-5, 5, [h] (ld d) { + return (zpush(d) * h)[2] >= sin_auto(vid.depth); + }); + // ld base_to_base = cspin(2, 0, outradius) * zpush0(-dieradius); + } + + vector > ordering; + + for(int i=0; i g(geometry, gSpace534); + add_to_queue(T * cspin(0, 1, 2*M_PI*d/si) * cspin(2, 0, inradius) * cspin(0, 1, M_PI-2*M_PI*spins[ws][d]/si), sides[ws][d]); + } + + if(1) { + dynamicval g(geometry, gSpace534); + hyperpoint h = zpush(base_to_base) * T * zpush0(dieradius); + ld z = asin_auto(h[2]); + ordering.emplace_back(-z, i); + } + } + + sort(ordering.begin(), ordering.end()); + + for(auto o: ordering) { + int i = o.second; + transmatrix T = facequeue[i].first; + + for(int d=0; d<=si; d++) { + hyperpoint h; + ld z = 0; + if(1) { + dynamicval g(geometry, gSpace534); + h = zpush(base_to_base) * T * cspin(0, 1, 2*M_PI*(d+.5)/si) * cspin(2, 0, outradius) * zpush0(dieradius); + z = asin_auto(h[2]); + h /= cos_auto(z); + } + h[2] = h[3]; h[3] = 0; + h = zshift(h, geom3::scale_at_lev(z)); + curvepoint(h); + } + queuecurve(V1, 0xFFFFFFFF, 0x40A040FF, PPR::WALL); + } + + } + EX } } diff --git a/content.cpp b/content.cpp index c817d357..17c236b7 100644 --- a/content.cpp +++ b/content.cpp @@ -1683,13 +1683,24 @@ MONSTER('H', 0x181818, "Hag", moHexer, CF_FACE_UP, RESERVED, moYeti, LAND(0xC0C0FF, "Cursed Land", laCursed, 0, itCursed, RESERVED, "This land is full of curses!") - + ITEM('/', 0x211F6F, "Cursed Gold", itCursed, IC_TREASURE, ZERO, RESERVED, osNone, "A cursed gold.") ITEM('o', 0x208020, "Orb of the Woods", itOrbWoods, IC_ORB, ZERO, RESERVED, osTerraform, "Lets you swap positions with the trees.") +LAND(0xC0C0FF, "Land of Dice", laDice, 0, itCursed, RESERVED, + "This land is full of dice!") + +ITEM('/', 0xD0D0D8, "Crystal Die", itDice, IC_TREASURE, ZERO, RESERVED, osNone, + "A nice souvenir from the Land of Dice. Make sure to collect the whole set!") + +WALL('d', 0x181818, "Rollable Die", waDie, WF_WALL | WF_PUSHABLE, RESERVED, 0, sgNone, NODESC) + +WALL('d', 0x181818, "Rollable Die", waDie1, ZERO, RESERVED, 0, sgNone, NODESC) +WALL('d', 0x181818, "Rollable Die", waDie2, ZERO, RESERVED, 0, sgNone, NODESC) + //shmupspecials MONSTER( '@', 0xC0C0C0, "Rogue", moPlayer, CF_FACE_UP | CF_PLAYER, RESERVED, moNone, "In the Shoot'em Up mode, you are armed with thrown Knives.") diff --git a/game.cpp b/game.cpp index c6392b71..9d1865f0 100644 --- a/game.cpp +++ b/game.cpp @@ -374,6 +374,8 @@ EX void pushThumper(const movei& mi) { cto->wall = waCrateOnTarget; th->wall = waCrateTarget; } + else if(w == waDie) + dice::roll(mi); else cto->wall = w; if(explode) cto->wall = waFireTrap, cto->wparam = explode; @@ -382,7 +384,9 @@ EX void pushThumper(const movei& mi) { } EX bool canPushThumperOn(cell *tgt, cell *thumper, cell *player) { - if(tgt->wall == waBoat || tgt->wall == waStrandedBoat) return false; + if(thumper->wall == waDie && tgt->type == 7) + return false; + if(tgt->wall == waBoat || tgt->wall == waStrandedBoat) return false; if(isReptile(tgt->wall)) return false; if(isWatery(tgt) && !tgt->monst) return true; diff --git a/geometry.cpp b/geometry.cpp index 567f1b50..4ce596d8 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -745,7 +745,7 @@ EX namespace geom3 { // projection: projection parameter // factor: zoom factor - ld abslev_to_projection(ld abslev) { + EX ld abslev_to_projection(ld abslev) { if(sphere || euclid) return vid.camera+abslev; return tanh(abslev) / tanh(vid.camera); } diff --git a/landgen.cpp b/landgen.cpp index fe2a2c0e..3977691f 100644 --- a/landgen.cpp +++ b/landgen.cpp @@ -2585,6 +2585,20 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { break; } + case laDice: { + if(fargen) { + int pct = hrand(100); + if(pct < 10) + c->wall = waDie; + // c->wall = pick(waNone, waNone, waStone, waNone, waNone, waStone, waNone, waNone, waStone, waBigStatue, waCrateCrate, waDie); + if(c->wall == waDie) { + if(ctof(c)) c->wall = waNone; + else dice::generate_on(c); + } + } + break; + } + case laCursed: { if(fargen) { c->wall = waStone;