first version of the Land of Dice

This commit is contained in:
Zeno Rogue 2021-05-27 13:00:20 +02:00
parent b879682d82
commit 6a6ed4ea0d
11 changed files with 330 additions and 98 deletions

View File

@ -104,6 +104,8 @@ EX bool canAttack(cell *c1, eMonster m1, cell *c2, eMonster m2, flagtype flags)
if(m1 == moArrowTrap && arrow_stuns(m2)) return true; if(m1 == moArrowTrap && arrow_stuns(m2)) return true;
if(m2 == moAnimatedDie && !(flags & (AF_MAGIC | AF_CRUSH))) return false;
if(among(m2, moAltDemon, moHexDemon, moPair, moCrusher, moNorthPole, moSouthPole, moMonk) && !(flags & (AF_EAT | AF_MAGIC | AF_BULL | AF_CRUSH))) if(among(m2, moAltDemon, moHexDemon, moPair, moCrusher, moNorthPole, moSouthPole, moMonk) && !(flags & (AF_EAT | AF_MAGIC | AF_BULL | AF_CRUSH)))
return false; return false;

View File

@ -1601,8 +1601,19 @@ void celldrawer::draw_features() {
poly_outline = OUTLINE_DEFAULT; poly_outline = OUTLINE_DEFAULT;
} }
else if(c->wall == waDie) { else if(among(c->wall, waRichDie, waBlandDie)) {
dice::draw_die(c, V); color_t col = darkena(winf[c->wall].color, 0, 0xFF);
ld footphase;
Vboat = V;
bool animated = applyAnimation(c, Vboat, footphase, LAYER_BOAT);
if(animated) {
transmatrix U = inverse_shift(V, Vboat);
U = rgpushxto0(tC0(U));
Vboat = V * U;
}
die_target = V;
dice::draw_die(c, Vboat, 1, col);
} }
else if(c->wall == waExplosiveBarrel) { else if(c->wall == waExplosiveBarrel) {

View File

@ -904,73 +904,148 @@ EX }
EX namespace dice { EX namespace dice {
/* vector<vector<int>> sides = { struct die_structure {
{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<vector<int>> sides;
}; */ vector<vector<int>> spins;
vector<int> hardness;
vector<vector<int>> sides = { int faces;
int order;
die_structure(int ord, const vector<vector<int>>& v) {
sides = v;
spins = sides;
faces = isize(sides);
order = ord;
for(int i=0; i<faces; i++)
for(int j=0; j<isize(sides[i]); j++) {
int i1 = sides[i][j];
spins[i][j] = -1;
for(int k=0; k<isize(sides[i1]); k++)
if(sides[i1][k] == i)
spins[i][j] = k;
if(spins[i][j] == -1)
println(hlog, "asymmetric");
}
hardness.resize(faces, 99);
hardness.back() = 0;
for(int it=0; it<faces; it++)
for(int i=0; i<faces; i++)
for(int j: sides[i])
hardness[i] = min(hardness[i], hardness[j]+1);
}
};
die_structure d20(5, {
{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}, {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}, {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}, {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} { 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}
});
die_structure d8(4, {
{1, 3, 5}, {0, 4, 2}, {3, 1, 7}, {2, 6, 0}, {5, 7, 1}, {4, 0, 6}, {7, 5, 3}, {6, 2, 4}
});
die_structure d4(3, {{3,2,1}, {3,0,2}, {1,0,3}, {0,1,2}});
#if HDR
struct die_data {
struct die_structure *which;
int val;
int dir;
int happy();
}; };
#endif
vector<vector<int>> spins; int die_data::happy() {
int order; if(val == which->faces-1) return 1;
if(val == 0) return -1;
int faces() { return isize(sides); } return 0;
EX void generate_on(cell *c) {
c->wparam = hrand(faces()) + faces() * (1 + hrand(3) * 2);
} }
bool prepared; EX map<cell*, die_data> data;
void prepare() { EX void generate_random(cell *c) {
if(prepared) return; auto& dd = data[c];
prepared = true; dd.which = pick(&d4, &d8, &d20);
spins = sides; dd.val = hrand(dd.which->faces);
order = faces() == 8 ? 4 : 5; dd.dir = 1 + hrand(3) * 2;
for(int i=0; i<faces(); i++) }
for(int j=0; j<isize(sides[i]); j++) {
int i1 = sides[i][j]; EX void generate_specific(cell *c, die_structure *ds, int min_hardness, int max_hardness) {
spins[i][j] = -1; auto& dd = data[c];
for(int k=0; k<isize(sides[i1]); k++) dd.which = ds;
if(sides[i1][k] == i) dd.dir = 1 + hrand(3) * 2;
spins[i][j] = k; vector<int> sides;
if(spins[i][j] == -1) for(int i=0; i<ds->faces; i++)
println(hlog, "asymmetric"); if(ds->hardness[i] >= min_hardness && ds->hardness[i] <= max_hardness)
} sides.push_back(i);
dd.val = sides[hrand(isize(sides))];
} }
EX void roll(movei mi) { EX void generate_full(cell *c, int hard) {
prepare(); int pct = hrand(100);
int pct2 = hrand(4000);
if(pct < 1) {
c->wall = waBlandDie;
generate_specific(c, &d4, 0, 99);
}
else if(pct < 3) {
c->wall = waBlandDie;
generate_specific(c, &d8, 0, 1);
}
else if(pct < 5) {
c->wall = waBlandDie;
generate_specific(c, &d20, 0, 1);
}
else if(pct < 9) {
c->wall = waRichDie;
generate_specific(c, &d20, 4, 5);
}
else if(pct < 10) {
c->wall = waRichDie;
generate_specific(c, &d8, 2, 3);
}
else if(pct2 < 1) {
c->monst = moAnimatedDie;
generate_specific(c, &d4, 0, 99);
}
else if(pct2 < 40) {
c->monst = moAnimatedDie;
generate_specific(c, &d8, 0, 99);
}
else if(pct2 < 40 + hard) {
c->monst = moAnimatedDie;
generate_specific(c, &d20, 0, 99);
}
}
EX die_data roll_effect(movei mi, die_data dd) {
auto &cto = mi.t; auto &cto = mi.t;
auto &th = mi.s; auto &th = mi.s;
int rdir = mi.dir_force(); int rdir = mi.dir_force();
int t = th->type; int t = th->type;
int val = th->wparam % faces(); int val = dd.val;
int dir = th->wparam / faces(); int dir = dd.dir;
int si = isize(sides[val]); auto& dw = dd.which;
if(t % si) { println(hlog, "error: bad roll\n"); return; } int si = isize(dw->sides[val]);
if(t % si) { println(hlog, "error: bad roll\n"); return dd; }
int sideid = (rdir - dir) * si / t; int sideid = (rdir - dir) * si / t;
if(sideid < 0) sideid += si; if(sideid < 0) sideid += si;
int val1 = sides[val][sideid]; int val1 = dw->sides[val][sideid];
int si1 = isize(sides[val1]); int si1 = isize(dw->sides[val1]);
println(hlog, tie(val, si, rdir), " to ", tie(val1, si1)); int sideid1 = dw->spins[val][sideid];
int sideid1 = spins[val][sideid];
int t1 = cto->type; int t1 = cto->type;
if(t1 % si1) { println(hlog, "error: bad roll target\n"); return; } if(t1 % si1) { println(hlog, "error: bad roll target\n"); return dd; }
int rdir1 = mi.rev_dir_force(); int rdir1 = mi.rev_dir_force();
@ -978,30 +1053,70 @@ EX namespace dice {
dir1 = gmod(dir1, t1); dir1 = gmod(dir1, t1);
th->wall = waNone; dd.val = val1;
cto->wall = waDie; dd.dir = dir1;
cto->wparam = val1 + faces() * dir1; return dd;
}
EX void roll(movei mi) {
auto &cto = mi.t;
auto &th = mi.s;
changes.map_value(data, cto);
changes.map_value(data, th);
data[cto] = roll_effect(mi, data[th]);
data.erase(th);
} }
EX void draw_die(cell *c, const shiftmatrix& V) { EX void draw_die(cell *c, const shiftmatrix& V, ld scale, color_t color) {
prepare(); if(!data.count(c)) {
int val = c->wparam % faces(); queuepoly(V, cgi.shAsymmetric, 0xFF0000FF);
int dir = c->wparam / faces(); return;
queuestr(V, .5, its(val+1), 0xFFFFFFFF); }
auto& side = sides[val];
int si = isize(side); eGeometry orig = geometry;
bool fpp = GDIM == 3;
auto& dd = data[c];
int val = dd.val;
int dir = dd.dir;
auto& dw = dd.which;
for(int i=0; i<si; i++) { auto& side = dw->sides[val];
int d = dir + c->type * i / isize(side); int si = isize(side);
d = gmod(d, c->type);
hyperpoint nxt = tC0(currentmap->adj(c, d)); if(c == lmouseover_distant) {
hyperpoint mid = normalize(C0 * 1.3 + nxt * -.3); set<cell*> visited;
queuestr(V * rgpushxto0(mid), .25, its(side[i]+1), 0xFFFFFFFF); struct celldata_t {
cell* c;
die_data dd;
shiftmatrix V;
};
vector<celldata_t> data;
auto visit = [&] (cell *c, die_data dd, shiftmatrix V) {
if(visited.count(c)) return;
visited.insert(c);
data.emplace_back(celldata_t{c, dd, V});
};
visit(c, dd, V);
for(int i=0; i<isize(data); i++) {
auto dat = data[i];
queuestr(fpp ? dat.V * zpush(cgi.FLOOR) : dat.V, .5, its(dat.dd.val+1), 0xFF8000FF);
if(i <= 22)
forCellIdEx(c2, id, dat.c) if(!ctof(c2) && !visited.count(c2)) {
auto re = roll_effect(movei(dat.c, id), dat.dd);
shiftmatrix V2 = dat.V * currentmap->adj(dat.c, id);
gridline(dat.V, C0, V2, C0, 0xFF800080, 0);
visit(c2, re, V2);
}
}
} }
shiftmatrix V1 = V * iddspin(c, dir) * spin(M_PI); shiftmatrix V1 = V * ddspin(c, dir) * spin(M_PI);
vector<bool> face_drawn(faces(), false); // loop:
vector<bool> face_drawn(dd.which->faces, false);
vector<pair<transmatrix, int> > facequeue; vector<pair<transmatrix, int> > facequeue;
@ -1011,46 +1126,64 @@ EX namespace dice {
facequeue.emplace_back(T, d); facequeue.emplace_back(T, d);
}; };
add_to_queue(Id, val); transmatrix S = Id;
ld outradius, inradius; ld outradius, inradius;
if(1) { if(1) {
dynamicval<eGeometry> g(geometry, gSphere); dynamicval<eGeometry> g(geometry, gSphere);
ld alpha = 360 * degree / order; ld alpha = 360 * degree / dw->order;
inradius = edge_of_triangle_with_angles(alpha, 60*degree, 60*degree); inradius = edge_of_triangle_with_angles(alpha, 60*degree, 60*degree);
outradius = edge_of_triangle_with_angles(60*degree, alpha, 60*degree); outradius = edge_of_triangle_with_angles(60*degree, alpha, 60*degree);
} }
ld dieradius = 0.5; hyperpoint shift = inverse_shift(V1, tC0(die_target));
hyperpoint log_shift = inverse_exp(shiftless(shift)) * (inradius / cgi.hexhexdist);
if(1) {
dynamicval<eGeometry> g(geometry, gSphere);
hyperpoint de = direct_exp(log_shift);
S = rgpushxto0(de);
if(GDIM == 3) {
for(int i=0; i<4; i++) swap(S[i][2], S[i][3]);
for(int i=0; i<4; i++) swap(S[2][i], S[3][i]);
}
for(int i=0; i<4; i++) S[i][1] *= -1;
}
add_to_queue(S, val);
ld dieradius = scale / 2;
if(dw->faces == 20) dieradius /= 1.3;
if(dw->faces == 8) dieradius /= 1.15;
ld base_to_base; ld base_to_base;
if(1) { if(1) {
dynamicval<eGeometry> g(geometry, gSpace534); dynamicval<eGeometry> g(geometry, gSpace534);
hyperpoint h = cspin(2, 0, outradius) * zpush0(-dieradius); hyperpoint h = cspin(2, 0, M_PI-outradius) * zpush0(-dieradius);
base_to_base = binsearch(-5, 5, [h] (ld d) { base_to_base = binsearch(-5, 5, [h] (ld d) {
return (zpush(d) * h)[2] >= sin_auto(vid.depth); return (zpush(d) * h)[2] >= sin_auto(vid.depth);
}); });
// ld base_to_base = cspin(2, 0, outradius) * zpush0(-dieradius);
} }
vector<pair<ld, int> > ordering; vector<pair<ld, int> > ordering;
for(int i=0; i<faces(); i++) { for(int i=0; i<dw->faces; i++) {
transmatrix T = facequeue[i].first; transmatrix T = facequeue[i].first;
int ws = facequeue[i].second; int ws = facequeue[i].second;
for(int d=0; d<si; d++) { for(int d=0; d<si; d++) {
dynamicval<eGeometry> g(geometry, gSpace534); dynamicval<eGeometry> 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]); add_to_queue(T * cspin(0, 1, 2*M_PI*d/si) * cspin(2, 0, inradius) * cspin(0, 1, M_PI-2*M_PI*dw->spins[ws][d]/si), dw->sides[ws][d]);
} }
if(1) { if(1) {
dynamicval<eGeometry> g(geometry, gSpace534); dynamicval<eGeometry> g(geometry, gSpace534);
hyperpoint h = zpush(base_to_base) * T * zpush0(dieradius); hyperpoint h = zpush(base_to_base) * T * zpush0(dieradius);
ld z = asin_auto(h[2]); ld z = fpp ? hdist0(h) : asin_auto(h[2]);
ordering.emplace_back(-z, i); ordering.emplace_back(-z, i);
} }
} }
@ -1061,22 +1194,49 @@ EX namespace dice {
int i = o.second; int i = o.second;
transmatrix T = facequeue[i].first; transmatrix T = facequeue[i].first;
transmatrix face;
hyperpoint sum = zpush(base_to_base) * C0 * -3;
auto sphere_to_space = [&] (hyperpoint h) {
if(fpp) return h;
ld z = asin_auto(h[2]);
h /= cos_auto(z);
h[2] = h[3]; h[3] = 0;
dynamicval<eGeometry> g(geometry, orig);
return zshift(h, geom3::scale_at_lev(z));
};
for(int d=0; d<=si; d++) { for(int d=0; d<=si; d++) {
hyperpoint h; hyperpoint h, hs;
ld z = 0;
if(1) { if(1) {
dynamicval<eGeometry> g(geometry, gSpace534); dynamicval<eGeometry> g(geometry, gSpace534);
h = zpush(base_to_base) * T * cspin(0, 1, 2*M_PI*(d+.5)/si) * cspin(2, 0, outradius) * zpush0(dieradius); 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]); set_column(face, d, h);
h /= cos_auto(z); sum += h;
hs = sphere_to_space(h);
} }
h[2] = h[3]; h[3] = 0; curvepoint(hs);
h = zshift(h, geom3::scale_at_lev(z));
curvepoint(h);
} }
queuecurve(V1, 0xFFFFFFFF, 0x40A040FF, PPR::WALL);
} set_column(face, 3, sum);
queuecurve(V1, 0xFFFFFFFF, color & 0xFFFFFF9F, PPR::WALL);
pointfunction pf = [&] (ld x, ld y) {
hyperpoint hs;
dynamicval<eGeometry> g(geometry, gSpace534);
return sphere_to_space(normalize(face * hyperpoint(1/3. -y/2 -x, 1/3. + y, 1/3. -y/2 +x, 1e-2)));
};
int fid = dw->faces - facequeue[i].second;
if(dw->faces == 4) fid = facequeue[i].second + 1;
string s;
if(fid == 6) s = "6.";
else if(fid == 9) s = "9.";
else s = its(fid);
write_in_space(V1, max_glfont_size, dw->faces < 10 ? -1.2 : -.75, s, 0xFFFFFFFF, 0, 8, PPR::WALL, pf);
}
} }
EX } EX }

View File

@ -1700,11 +1700,20 @@ LAND(0xC0C0FF, "Land of Dice", laDice, 0, itCursed, RESERVED,
ITEM('/', 0xD0D0D8, "Crystal Die", itDice, IC_TREASURE, ZERO, RESERVED, osNone, 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!") "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', 0x7F6A10, "Unhappy Die", waRichDie, WF_WALL | WF_PUSHABLE, RESERVED, 0, sgNone,
"Sentent dice like to be in a position such that their highest number is on top, or somewhere close. "
WALL('d', 0x181818, "Rollable Die", waDie1, ZERO, RESERVED, 0, sgNone, NODESC) "Unfortunately, someone has rolled this one into a wrong position, and did not fix this. "
WALL('d', 0x181818, "Rollable Die", waDie2, ZERO, RESERVED, 0, sgNone, NODESC) "It will reward you if you roll it so that the highest number is on top again!")
WALL('d', 0x106010, "Happy Die", waBlandDie, WF_WALL | WF_PUSHABLE, RESERVED, 0, sgNone,
"A happy sentent die. You can roll it.")
MONSTER('d', 0x603010, "Animated Die", moAnimatedDie, ZERO, RESERVED, moHexDemon,
"When sentient dice are too long in an incorrect position, they start to move on their own, "
"and attack everyone. You can still convince Animated Dice of your good intentions by "
"rolling them into a position such that the highest number is on top. "
"If you do, the die will stop moving and (if it happens in the Land of Dice) you will be rewarded. "
"Other rolls and attacks are not allowed."
)
//shmupspecials //shmupspecials
MONSTER( '@', 0xC0C0C0, "Rogue", moPlayer, CF_FACE_UP | CF_PLAYER, RESERVED, moNone, "In the Shoot'em Up mode, you are armed with thrown Knives.") MONSTER( '@', 0xC0C0C0, "Rogue", moPlayer, CF_FACE_UP | CF_PLAYER, RESERVED, moNone, "In the Shoot'em Up mode, you are armed with thrown Knives.")

View File

@ -481,6 +481,7 @@ EX void bfs() {
else if(isMagneticPole(c2->monst)) havewhat |= HF_MAGNET; else if(isMagneticPole(c2->monst)) havewhat |= HF_MAGNET;
else if(c2->monst == moAltDemon) havewhat |= HF_ALT; else if(c2->monst == moAltDemon) havewhat |= HF_ALT;
else if(c2->monst == moHexDemon) havewhat |= HF_HEXD; else if(c2->monst == moHexDemon) havewhat |= HF_HEXD;
else if(c2->monst == moAnimatedDie) havewhat |= HF_HEXD;
else if(c2->monst == moMonk) havewhat |= HF_MONK; else if(c2->monst == moMonk) havewhat |= HF_MONK;
else if(c2->monst == moShark || c2->monst == moCShark || among(c2->monst, moRusalka, moPike)) havewhat |= HF_SHARK; else if(c2->monst == moShark || c2->monst == moCShark || among(c2->monst, moRusalka, moPike)) havewhat |= HF_SHARK;
else if(c2->monst == moAirElemental) else if(c2->monst == moAirElemental)

View File

@ -379,8 +379,22 @@ EX void pushThumper(const movei& mi) {
cto->wall = waCrateOnTarget; cto->wall = waCrateOnTarget;
th->wall = waCrateTarget; th->wall = waCrateTarget;
} }
else if(w == waDie) else if(among(w, waRichDie, waBlandDie)) {
dice::roll(mi); th->wall = waNone;
cto->wall = w;
animateMovement(mi, LAYER_BOAT);
dice::roll(mi);
if(w == waRichDie && dice::data[cto].happy() > 0) {
cto->wall = waBlandDie;
if(cto->land == laDice && th->land == laDice) {
items[itDice]++;
addMessage(XLAT("The die is now happy, and you are rewarded!"));
}
else {
addMessage(XLAT("The die is now happy, but won't reward you outside of the Land of Dice!"));
}
}
}
else else
cto->wall = w; cto->wall = w;
if(explode) cto->wall = waFireTrap, cto->wparam = explode; if(explode) cto->wall = waFireTrap, cto->wparam = explode;
@ -389,7 +403,7 @@ EX void pushThumper(const movei& mi) {
} }
EX bool canPushThumperOn(cell *tgt, cell *thumper, cell *player) { EX bool canPushThumperOn(cell *tgt, cell *thumper, cell *player) {
if(thumper->wall == waDie && tgt->type == 7) if(among(thumper->wall, waRichDie, waBlandDie) && ctof(tgt))
return false; return false;
if(tgt->wall == waBoat || tgt->wall == waStrandedBoat) return false; if(tgt->wall == waBoat || tgt->wall == waStrandedBoat) return false;
if(isReptile(tgt->wall)) return false; if(isReptile(tgt->wall)) return false;

View File

@ -814,7 +814,7 @@ EX bool drawItemType(eItem it, cell *c, const shiftmatrix& V, color_t icol, int
(it == itBombEgg || it == itTrollEgg) ? &cgi.shEgg : (it == itBombEgg || it == itTrollEgg) ? &cgi.shEgg :
it == itFrog ? &cgi.shDisk : it == itFrog ? &cgi.shDisk :
it == itHunting ? &cgi.shTriangle : it == itHunting ? &cgi.shTriangle :
it == itDodeca ? &cgi.shDodeca : (it == itDodeca || it == itDice) ? &cgi.shDodeca :
xch == '*' ? &cgi.shGem[ct6] : xch == '*' ? &cgi.shGem[ct6] :
xch == '(' ? &cgi.shKnife : xch == '(' ? &cgi.shKnife :
it == itShard ? &cgi.shMFloor.b[0] : it == itShard ? &cgi.shMFloor.b[0] :
@ -2251,7 +2251,15 @@ EX bool drawMonsterType(eMonster m, cell *where, const shiftmatrix& V1, color_t
queuepoly(V, cgi.shAsteroid[1], darkena(col, 0, 0xFF)); queuepoly(V, cgi.shAsteroid[1], darkena(col, 0, 0xFF));
return true; return true;
} }
case moAnimatedDie: {
if(where)
dice::draw_die(where, V, 1, darkena(col, 0, 0xFF));
else
queuepoly(V, cgi.shDodeca, darkena(col, 0, 0xFF));
return true;
}
default: ; default: ;
} }
@ -2518,6 +2526,8 @@ void drawWormSegments() {
EX bool dont_face_pc = false; EX bool dont_face_pc = false;
EX shiftmatrix die_target;
EX bool drawMonster(const shiftmatrix& Vparam, int ct, cell *c, color_t col, color_t asciicol) { EX bool drawMonster(const shiftmatrix& Vparam, int ct, cell *c, color_t col, color_t asciicol) {
#if CAP_SHAPES #if CAP_SHAPES
@ -2876,6 +2886,13 @@ EX bool drawMonster(const shiftmatrix& Vparam, int ct, cell *c, color_t col, col
// golems, knights, and hyperbugs don't face the player (mondir-controlled) // golems, knights, and hyperbugs don't face the player (mondir-controlled)
// also whatever in the lineview mode, and whatever in the quotient geometry // also whatever in the lineview mode, and whatever in the quotient geometry
else if(c->monst == moAnimatedDie) {
transmatrix U = inverse_shift(Vparam, Vs);
U = rgpushxto0(tC0(U));
die_target = Vparam;
res = res && drawMonsterTypeDH(m, c, Vparam * U, col, darkhistory, footphase, asciicol);
}
else if((hasFacing(c) && c->mondir != NODIR) || history::on || quotient || dont_face_pc) { else if((hasFacing(c) && c->mondir != NODIR) || history::on || quotient || dont_face_pc) {
if(c->monst == moKrakenH) Vs = Vb, nospins = nospinb; if(c->monst == moKrakenH) Vs = Vb, nospins = nospinb;
if(!nospins && c->mondir < c->type) Vs = Vs * ddspin(c, c->mondir, M_PI); if(!nospins && c->mondir < c->type) Vs = Vs * ddspin(c, c->mondir, M_PI);

View File

@ -2586,16 +2586,8 @@ EX void giantLandSwitch(cell *c, int d, cell *from) {
} }
case laDice: { case laDice: {
if(fargen) { if(fargen && !ctof(c))
int pct = hrand(100); dice::generate_full(c, items[itDice] + yendor::hardness());
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; break;
} }

View File

@ -102,6 +102,9 @@ EX void moveEffect(const movei& mi, eMonster m) {
animateMovement(mi.rev(), LAYER_BOAT); animateMovement(mi.rev(), LAYER_BOAT);
tortoise::move_baby(cf, ct); tortoise::move_baby(cf, ct);
} }
if(m == moAnimatedDie && mi.proper())
dice::roll(mi);
} }
EX void moveMonster(const movei& mi) { EX void moveMonster(const movei& mi) {
@ -1086,6 +1089,11 @@ EX void groupmove2(const movei& mi, eMonster movtype, flagtype mf) {
} }
} }
else return; else return;
if(c->monst == moAnimatedDie) {
forCellEx(c3, from) if(c3 != c && c3->monst == moAnimatedDie)
return;
}
if(from->monst) { if(from->monst) {
if(mf & MF_MOUNT) { if(mf & MF_MOUNT) {

View File

@ -808,6 +808,8 @@ EX eMonster summonedAt(cell *dest) {
return moMiner; return moMiner;
if(dest->wall == waMineOpen || dest->wall == waMineMine || dest->wall == waMineUnknown) if(dest->wall == waMineOpen || dest->wall == waMineMine || dest->wall == waMineUnknown)
return moBomberbird; return moBomberbird;
if(dest->wall == waRichDie)
return moAnimatedDie;
if(dest->wall == waTrapdoor) if(dest->wall == waTrapdoor)
return dest->land == laPalace ? moFatGuard : moOrangeDog; return dest->land == laPalace ? moFatGuard : moOrangeDog;
if(dest->land == laFrog && dest->wall == waNone) { if(dest->land == laFrog && dest->wall == waNone) {
@ -906,6 +908,8 @@ void summonAt(cell *dest) {
dest->stuntime = 3; dest->stuntime = 3;
if(dest->monst == moPirate || dest->monst == moViking || (dest->monst == moRatling && dest->wall == waSea)) if(dest->monst == moPirate || dest->monst == moViking || (dest->monst == moRatling && dest->wall == waSea))
dest->wall = waBoat, dest->item = itNone; dest->wall = waBoat, dest->item = itNone;
if(dest->monst == moAnimatedDie)
dest->wall = waNone;
if(dest->monst == moViking && dest->land == laKraken) if(dest->monst == moViking && dest->land == laKraken)
dest->item = itOrbFish; dest->item = itOrbFish;
if(dest->wall == waStrandedBoat) if(dest->wall == waStrandedBoat)

View File

@ -628,6 +628,18 @@ bool pcmove::actual_move() {
activateActiv(c2, true); activateActiv(c2, true);
return after_instant(false); return after_instant(false);
} }
if(c2->monst == moAnimatedDie) {
mip = determinePush(cwt, subdir, [c2] (cell *c) { return canPushThumperOn(c, c2, cwt.at); });
if(mip.proper()) {
auto tgt = roll_effect(mip, dice::data[c2]);
if(tgt.happy() > 0) {
changes.ccell(c2);
c2->monst = moNone;
c2->wall = waRichDie;
}
}
}
if(isPushable(c2->wall) && !c2->monst && !nonAdjacentPlayer(c2, cwt.at) && fmsMove) { if(isPushable(c2->wall) && !c2->monst && !nonAdjacentPlayer(c2, cwt.at) && fmsMove) {
mip = determinePush(cwt, subdir, [c2] (cell *c) { return canPushThumperOn(c, c2, cwt.at); }); mip = determinePush(cwt, subdir, [c2] (cell *c) { return canPushThumperOn(c, c2, cwt.at); });
@ -745,6 +757,8 @@ void pcmove::tell_why_cannot_attack() {
addMessage(XLAT("You cannot attack Raiders directly!")); addMessage(XLAT("You cannot attack Raiders directly!"));
else if(isSwitch(c2->monst)) else if(isSwitch(c2->monst))
addMessage(XLAT("You cannot attack Jellies in their wall form!")); addMessage(XLAT("You cannot attack Jellies in their wall form!"));
else if(c2->monst == moAnimatedDie)
addMessage(XLAT("You can only push this die if the highest number would be on the top!"));
else else
addMessage(XLAT("For some reason... cannot attack!")); addMessage(XLAT("For some reason... cannot attack!"));
} }