1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-12-26 01:50:36 +00:00
hyperrogue/graph.cpp

6597 lines
212 KiB
C++
Raw Normal View History

// Hyperbolic Rogue -- main graphics file
2015-08-08 13:57:52 +00:00
// Copyright (C) 2011-2018 Zeno Rogue, see 'hyper.cpp' for details
2016-08-26 09:58:03 +00:00
// basic graphics:
namespace hr {
int last_firelimit, firelimit;
2017-07-16 21:00:55 +00:00
int inmirrorcount = 0;
bool spatial_graphics;
2017-03-23 10:53:57 +00:00
bool wmspatial, wmescher, wmplain, wmblack, wmascii;
bool mmspatial, mmhigh, mmmon, mmitem;
int detaillevel = 0;
bool hide_player() {
return DIM == 3 && playermoved && vid.yshift == 0 && vid.sspeed > -5;
}
2017-07-10 18:47:38 +00:00
hookset<bool(int sym, int uni)> *hooks_handleKey;
hookset<bool(cell *c, const transmatrix& V)> *hooks_drawcell;
2018-09-06 20:34:35 +00:00
purehookset hooks_frame, hooks_markers;
2017-07-10 18:47:38 +00:00
ld animation_factor = 1;
int animation_lcm = 0;
ld ptick(int period, ld phase = 0) {
if(animation_lcm) animation_lcm = animation_lcm * (period / gcd(animation_lcm, period));
return (ticks * animation_factor) / period + phase * 2 * M_PI;
}
ld fractick(int period, ld phase = 0) {
ld t = ptick(period, phase) / 2 / M_PI;
t -= floor(t);
if(t<0) t++;
return t;
}
ld sintick(int period, ld phase) {
return sin(ptick(period, phase));
}
transmatrix spintick(int period, ld phase = 0) {
return spin(ptick(period, phase));
}
2016-08-26 09:58:03 +00:00
#define WOLNIEJ 1
2016-01-02 10:09:13 +00:00
#define BTOFF 0x404040
#define BTON 0xC0C000
2017-03-23 10:53:57 +00:00
// #define PANDORA
2015-08-08 13:57:52 +00:00
2017-03-23 10:53:57 +00:00
int colorbar;
2016-08-26 09:58:03 +00:00
bool inHighQual; // taking high quality screenshot
bool auraNOGL; // aura without GL
2015-08-08 13:57:52 +00:00
//
int axestate;
int ticks;
2017-03-23 10:53:57 +00:00
int frameid;
2015-08-08 13:57:52 +00:00
2017-04-08 15:18:29 +00:00
bool camelotcheat;
bool nomap;
2017-04-08 15:18:29 +00:00
2017-03-23 10:53:57 +00:00
eItem orbToTarget;
eMonster monsterToSummon;
int sightrange_bonus = 0;
2017-03-23 10:53:57 +00:00
2015-08-08 13:57:52 +00:00
string mouseovers;
int darken = 0;
2017-03-23 10:53:57 +00:00
struct fallanim {
2017-08-06 12:50:16 +00:00
int t_mon, t_floor, pid;
2017-03-23 10:53:57 +00:00
eWall walltype;
eMonster m;
2017-08-06 12:50:16 +00:00
fallanim() { t_floor = 0; t_mon = 0; pid = 0; walltype = waNone; }
2017-03-23 10:53:57 +00:00
};
map<cell*, fallanim> fallanims;
2016-01-02 10:09:13 +00:00
bool doHighlight() {
2017-03-23 10:53:57 +00:00
return (hiliteclick && darken < 2) ? !mmhigh : mmhigh;
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
eModel pmodel = mdDisk;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
int dlit;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
ld spina(cell *c, int dir) {
return 2 * M_PI * dir / c->type;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
// cloak color
int cloakcolor(int rtr) {
rtr -= 28;
rtr /= 2;
rtr %= 10;
if(rtr < 0) rtr += 10;
// rtr = time(NULL) % 10;
int cc[10] = {
0x8080FF, 0x80FFFF, 0x80FF80, 0xFF8080, 0xFF80FF, 0xFFFF80,
0xFFFFC0, 0xFFD500, 0x421C52, 0
};
return cc[rtr];
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
int firegradient(double p) {
return gradient(0xFFFF00, 0xFF0000, 0, p, 1);
}
2015-08-08 13:57:52 +00:00
2018-09-10 15:56:37 +00:00
int firecolor(int phase, int mul) {
return gradient(0xFFFF00, 0xFF0000, -1, sintick(100*mul, phase/200./M_PI), 1);
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
int watercolor(int phase) {
2018-09-10 15:56:37 +00:00
return 0x0080C0FF + 256 * int(63 * sintick(50, phase/100./M_PI));
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
int aircolor(int phase) {
2018-09-10 15:56:37 +00:00
return 0x8080FF00 | int(32 + 32 * sintick(200, phase * 1. / S21));
2015-08-08 13:57:52 +00:00
}
int fghostcolor(cell *c) {
int phase = int(fractick(650, (int)(size_t)c) * 4000);
2017-07-10 18:47:38 +00:00
if(phase < 1000) return gradient(0xFFFF80, 0xA0C0FF, 0, phase, 1000);
else if(phase < 2000) return gradient(0xA0C0FF, 0xFF80FF, 1000, phase, 2000);
else if(phase < 3000) return gradient(0xFF80FF, 0xFF8080, 2000, phase, 3000);
else if(phase < 4000) return gradient(0xFF8080, 0xFFFF80, 3000, phase, 4000);
return 0xFFD500;
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
int weakfirecolor(int phase) {
2018-09-10 15:56:37 +00:00
return gradient(0xFF8000, 0xFF0000, -1, sintick(500, phase/1000./M_PI), 1);
2015-08-08 13:57:52 +00:00
}
color_t fc(int ph, color_t col, int z) {
2017-07-10 18:47:38 +00:00
if(items[itOrbFire]) col = darkena(firecolor(ph), 0, 0xFF);
if(items[itOrbAether]) col = (col &~0XFF) | (col&0xFF) / 2;
for(int i=0; i<numplayers(); i++) if(multi::playerActive(i))
if(items[itOrbFish] && isWatery(playerpos(i)) && z != 3) return watercolor(ph);
if(invismove)
col =
shmup::on ?
(col &~0XFF) | (int((col&0xFF) * .25))
2018-09-10 15:56:37 +00:00
: (col &~0XFF) | (int((col&0xFF) * (100+100*sintick(500)))/200);
2017-07-10 18:47:38 +00:00
return col;
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
int lightat, safetyat;
void drawLightning() { lightat = ticks; }
void drawSafety() { safetyat = ticks; }
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
void drawShield(const transmatrix& V, eItem it) {
#if CAP_CURVE
float ds = ptick(300);
color_t col = iinf[it].color;
2017-07-10 18:47:38 +00:00
if(it == itOrbShield && items[itOrbTime] && !orbused[it])
col = (col & 0xFEFEFE) / 2;
if(sphere && cwt.at->land == laHalloween && !wmblack && !wmascii)
2017-07-10 18:47:38 +00:00
col = 0;
double d = it == itOrbShield ? hexf : hexf - .1;
int mt = sphere ? 7 : 5;
2018-08-01 09:07:22 +00:00
for(ld a=0; a<=S84*mt+1e-6; a+=pow(.5, vid.linequality))
curvepoint(V*xspinpush0(a * M_PI/S42, d + sin(ds + M_PI*2*a/4/mt)*.1));
2018-08-28 12:27:23 +00:00
queuecurve(darkena(col, 0, 0xFF), 0x8080808, PPR::LINE);
#endif
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
void drawSpeed(const transmatrix& V) {
#if CAP_CURVE
ld ds = ptick(10);
color_t col = darkena(iinf[itOrbSpeed].color, 0, 0xFF);
2017-07-10 18:47:38 +00:00
for(int b=0; b<S84; b+=S14) {
2018-08-01 09:07:22 +00:00
PRING(a)
curvepoint(V*xspinpush0((ds+b+a) * M_PI/S42, hexf*a/S84));
2018-08-28 12:27:23 +00:00
queuecurve(col, 0x8080808, PPR::LINE);
2017-03-23 10:53:57 +00:00
}
#endif
2015-08-08 13:57:52 +00:00
}
2017-10-28 08:04:28 +00:00
int ctof(cell *c) {
2019-02-17 17:28:20 +00:00
#if CAP_IRR
if(IRREGULAR) return irr::ctof(c);
2019-02-17 17:28:20 +00:00
#endif
if(PURE) return 1;
2017-10-28 08:04:28 +00:00
// if(euclid) return 0;
if(!c) return 1;
2018-08-09 17:28:53 +00:00
if(binarytiling) return c->type == 7;
2017-10-28 08:04:28 +00:00
return ishept(c) ? 1 : 0;
// c->type == 6 ? 0 : 1;
}
int ctof012(cell *c) {
return ishept(c)?1:ishex1(c)?0:2;
}
2017-07-10 18:47:38 +00:00
void drawSafety(const transmatrix& V, int ct) {
#if CAP_QUEUE
ld ds = ptick(50);
color_t col = darkena(iinf[itOrbSafety].color, 0, 0xFF);
2017-07-10 18:47:38 +00:00
for(int a=0; a<ct; a++)
queueline(V*xspinpush0((ds+a*S84/ct) * M_PI/S42, 2*hexf), V*xspinpush0((ds+(a+(ct-1)/2)*S84/ct) * M_PI / S42, 2*hexf), col, vid.linequality);
#endif
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
void drawFlash(const transmatrix& V) {
#if CAP_CURVE
float ds = ptick(300);
color_t col = darkena(iinf[itOrbFlash].color, 0, 0xFF);
2017-07-10 18:47:38 +00:00
col &= ~1;
for(int u=0; u<5; u++) {
ld rad = hexf * (2.5 + .5 * sin(ds+u*.3));
PRING(a) curvepoint(V*xspinpush0(a * M_PI / S42, rad));
2018-08-28 12:27:23 +00:00
queuecurve(col, 0x8080808, PPR::LINE);
2017-03-23 10:53:57 +00:00
}
#endif
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
void drawLove(const transmatrix& V, int hdir) {
#if CAP_CURVE
float ds = ptick(300);
color_t col = darkena(iinf[itOrbLove].color, 0, 0xFF);
2017-07-10 18:47:38 +00:00
col &= ~1;
for(int u=0; u<5; u++) {
2018-08-01 09:07:22 +00:00
PRING(a) {
2017-07-10 18:47:38 +00:00
double d = (1 + cos(a * M_PI/S42)) / 2;
2018-08-01 09:07:22 +00:00
double z = a; if(z>S42) z = S84-z;
2017-07-10 18:47:38 +00:00
if(z <= 10) d += (10-z) * (10-z) * (10-z) / 3000.;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
ld rad = hexf * (2.5 + .5 * sin(ds+u*.3)) * d;
curvepoint(V*xspinpush0((S42+hdir+a-1) * M_PI/S42, rad));
2017-07-10 18:47:38 +00:00
}
2018-08-28 12:27:23 +00:00
queuecurve(col, 0x8080808, PPR::LINE);
2017-03-23 10:53:57 +00:00
}
#endif
2015-08-08 13:57:52 +00:00
}
void drawWinter(const transmatrix& V, ld hdir) {
#if CAP_QUEUE
float ds = ptick(300);
color_t col = darkena(iinf[itOrbWinter].color, 0, 0xFF);
2017-07-10 18:47:38 +00:00
for(int u=0; u<20; u++) {
ld rad = sin(ds+u * 2 * M_PI / 20) * M_PI / S7;
queueline(V*xspinpush0(M_PI+hdir+rad, hexf*.5), V*xspinpush0(M_PI+hdir+rad, hexf*3), col, 2 + vid.linequality);
2015-08-08 13:57:52 +00:00
}
#endif
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
void drawLightning(const transmatrix& V) {
#if CAP_QUEUE
color_t col = darkena(iinf[itOrbLightning].color, 0, 0xFF);
2017-07-10 18:47:38 +00:00
for(int u=0; u<20; u++) {
ld leng = 0.5 / (0.1 + (rand() % 100) / 100.0);
ld rad = rand() % 1000;
queueline(V*xspinpush0(rad, hexf*0.3), V*xspinpush0(rad, hexf*leng), col, 2 + vid.linequality);
2017-07-04 13:38:33 +00:00
}
#endif
2017-07-10 18:47:38 +00:00
}
2017-07-04 13:38:33 +00:00
ld displayspin(cell *c, int d) {
2019-02-17 17:28:20 +00:00
if(0);
#if CAP_ARCM
else if(archimedean) {
2018-08-29 02:28:34 +00:00
if(PURE) {
auto& t1 = arcm::current.get_triangle(c->master, d-1);
return -(t1.first + M_PI / c->type);
}
2018-08-30 00:11:43 +00:00
else if(DUAL) {
auto& t1 = arcm::current.get_triangle(c->master, 2*d);
return -t1.first;
}
2018-08-29 02:28:34 +00:00
else { /* BITRUNCATED */
auto& t1 = arcm::current.get_triangle(c->master, d);
return -t1.first;
}
}
2019-02-17 17:28:20 +00:00
#endif
#if CAP_IRR
else if(IRREGULAR) {
2018-07-16 18:05:23 +00:00
auto id = irr::cellindex[c];
auto& vs = irr::cells[id];
if(d < 0 || d >= c->type) return 0;
auto& p = vs.jpoints[vs.neid[d]];
return -atan2(p[1], p[0]);
2018-07-16 18:05:23 +00:00
}
2019-02-17 17:28:20 +00:00
#endif
#if CAP_BT
2018-08-09 17:28:53 +00:00
else if(binarytiling) {
if(d == NODIR) return 0;
if(d == c->type-1) d++;
2018-08-22 09:21:58 +00:00
return -(d+2)*M_PI/4;
2018-08-09 17:28:53 +00:00
}
2019-02-17 17:28:20 +00:00
#endif
else if(masterless)
return - d * 2 * M_PI / c->type;
2017-03-23 10:53:57 +00:00
else
return M_PI - d * 2 * M_PI / c->type;
2017-10-28 23:57:34 +00:00
}
double hexshiftat(cell *c) {
2018-08-09 17:28:53 +00:00
if(binarytiling) return 0;
if(ctof(c) && S7==6 && S3 == 4 && BITRUNCATED) return hexshift + 2*M_PI/S7;
if(ctof(c) && (S7==8 || S7 == 4) && S3 == 3 && BITRUNCATED) return hexshift + 2*M_PI/S7;
2017-10-28 23:57:34 +00:00
if(hexshift && ctof(c)) return hexshift;
return 0;
2015-08-08 13:57:52 +00:00
}
2019-02-26 20:11:50 +00:00
#define FACE3 (DIM == 3 ? M_PI/2 : 0)
transmatrix ddspin(cell *c, int d, ld bonus) {
if(DIM == 3 && d < c->type) return rspintox(tC0(calc_relative_matrix(c->move(d), c, C0))) * cspin(2, 0, bonus);
return spin(displayspin(c, d) + bonus - hexshiftat(c));
2015-08-08 13:57:52 +00:00
}
transmatrix iddspin(cell *c, int d, ld bonus) {
if(DIM == 3 && d < c->type) return cspin(0, 2, bonus) * spintox(tC0(calc_relative_matrix(c->move(d), c, C0)));
return spin(hexshiftat(c) - displayspin(c, d) + bonus);
2017-10-09 09:46:49 +00:00
}
2017-07-10 18:47:38 +00:00
void drawPlayerEffects(const transmatrix& V, cell *c, bool onplayer) {
if(!onplayer && !items[itOrbEmpathy]) return;
if(items[itOrbShield] > (shmup::on ? 0 : ORBBASE)) drawShield(V, itOrbShield);
if(items[itOrbShell] > (shmup::on ? 0 : ORBBASE)) drawShield(V, itOrbShell);
2017-07-04 13:38:33 +00:00
2017-07-10 18:47:38 +00:00
if(items[itOrbSpeed]) drawSpeed(V);
2017-07-04 13:38:33 +00:00
2017-07-10 18:47:38 +00:00
if(onplayer && (items[itOrbSword] || items[itOrbSword2])) {
using namespace sword;
2015-08-08 13:57:52 +00:00
2019-03-09 16:38:23 +00:00
if(shmup::on && DIM == 2) {
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
if(items[itOrbSword])
queuepoly(V*spin(shmup::pc[multi::cpid]->swordangle), (peace::on ? shMagicShovel : shMagicSword), darkena(iinf[itOrbSword].color, 0, 0xC0 + 0x30 * sintick(200)));
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(items[itOrbSword2])
queuepoly(V*spin(shmup::pc[multi::cpid]->swordangle+M_PI), (peace::on ? shMagicShovel : shMagicSword), darkena(iinf[itOrbSword2].color, 0, 0xC0 + 0x30 * sintick(200)));
#endif
2017-07-10 18:47:38 +00:00
}
2019-03-09 16:38:23 +00:00
else if(shmup::on && DIM == 3) {
#if CAP_SHAPES
if(items[itOrbSword])
queuepoly(V*shmup::swordmatrix[multi::cpid] * cspin(2, 0, M_PI/2) * cspin(1,2, ticks / 150.), (peace::on ? shMagicShovel : shMagicSword), darkena(iinf[itOrbSword].color, 0, 0xC0 + 0x30 * sintick(200)));
if(items[itOrbSword2])
queuepoly(V*shmup::swordmatrix[multi::cpid] * cspin(2, 0, -M_PI/2) * cspin(1,2, ticks / 150.), (peace::on ? shMagicShovel : shMagicSword), darkena(iinf[itOrbSword2].color, 0, 0xC0 + 0x30 * sintick(200)));
#endif
}
2017-07-10 18:47:38 +00:00
2015-08-08 13:57:52 +00:00
else {
2017-07-10 18:47:38 +00:00
int& ang = angle[multi::cpid];
ang %= sword_angles;
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE || CAP_SHAPES
transmatrix Vnow = gmatrix[c] * rgpushxto0(inverse(gmatrix[c]) * tC0(V)) * ddspin(c,0,M_PI); // (IRREGULAR ? ddspin(c,0,M_PI) : spin(-hexshiftat(c)));
2019-02-17 17:41:40 +00:00
#endif
2017-07-10 18:47:38 +00:00
#if CAP_QUEUE
if(!euclid) for(int a=0; a<sword_angles; a++) {
int dda = sword_angles + (-1-2*a);
2017-07-10 18:47:38 +00:00
if(a == ang && items[itOrbSword]) continue;
if(PURE && a%3 != ang%3) continue;
if((a+sword_angles/2)%sword_angles == ang && items[itOrbSword2]) continue;
bool longer = sword::pos(cwt.at, a-1) != sword::pos(cwt.at, a+1);
color_t col = darkena(0xC0C0C0, 0, 0xFF);
queueline(Vnow*xspinpush0(dda * M_PI / sword_angles, PURE ? 0.6 * scalefactor : longer ? 0.36 : 0.4), Vnow*xspinpush0(dda * M_PI/sword_angles, PURE ? 0.7 * scalefactor : longer ? 0.44 : 0.42), col, 1);
2017-07-10 18:47:38 +00:00
}
#endif
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
if(items[itOrbSword])
queuepoly(Vnow*spin(M_PI+(-1-2*ang)*M_PI/sword_angles), (peace::on ? shMagicShovel : shMagicSword), darkena(iinf[itOrbSword].color, 0, 0x80 + 0x70 * sintick(200)));
2017-07-10 18:47:38 +00:00
if(items[itOrbSword2])
queuepoly(Vnow*spin((-1-2*ang)*M_PI/sword_angles), (peace::on ? shMagicShovel : shMagicSword), darkena(iinf[itOrbSword2].color, 0, 0x80 + 0x70 * sintick(200)));
#endif
2015-08-08 13:57:52 +00:00
}
}
2017-10-28 08:04:28 +00:00
if(onplayer && items[itOrbSafety]) drawSafety(V, c->type);
2017-07-04 13:38:33 +00:00
2017-07-10 18:47:38 +00:00
if(onplayer && items[itOrbFlash]) drawFlash(V);
if(onplayer && items[itOrbLove]) drawLove(V, 0); // displaydir(c, cwt.spin));
2017-07-04 13:38:33 +00:00
2017-07-10 18:47:38 +00:00
if(items[itOrbWinter])
drawWinter(V, 0); // displaydir(c, cwt.spin));
if(onplayer && items[itOrbLightning]) drawLightning(V);
if(safetyat > 0) {
int tim = ticks - safetyat;
if(tim > 2500) safetyat = 0;
for(int u=tim; u<=2500; u++) {
if((u-tim)%250) continue;
ld rad = hexf * u / 250;
color_t col = darkena(iinf[itOrbSafety].color, 0, 0xFF);
2018-08-01 09:07:22 +00:00
PRING(a)
curvepoint(V*xspinpush0(a * M_PI / S42, rad));
2018-08-28 12:27:23 +00:00
queuecurve(col, 0, PPR::LINE);
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
void drawStunStars(const transmatrix& V, int t) {
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
for(int i=0; i<3*t; i++) {
transmatrix V2 = V * spin(M_PI * 2 * i / (3*t) + ptick(200));
2018-08-28 12:27:23 +00:00
queuepolyat(V2, shFlailBall, 0xFFFFFFFF, PPR::STUNSTARS);
2017-07-10 18:47:38 +00:00
}
#endif
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
namespace tortoise {
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
// small is 0 or 2
void draw(const transmatrix& V, int bits, int small, int stuntime) {
2017-03-23 10:53:57 +00:00
#if CAP_SHAPES
color_t eyecolor = getBit(bits, tfEyeHue) ? 0xFF0000 : 0xC0C0C0;
color_t shellcolor = getBit(bits, tfShellHue) ? 0x00C040 : 0xA06000;
color_t scutecolor = getBit(bits, tfScuteHue) ? 0x00C040 : 0xA06000;
color_t skincolor = getBit(bits, tfSkinHue) ? 0x00C040 : 0xA06000;
2017-07-10 18:47:38 +00:00
if(getBit(bits, tfShellSat)) shellcolor = gradient(shellcolor, 0xB0B0B0, 0, .5, 1);
if(getBit(bits, tfScuteSat)) scutecolor = gradient(scutecolor, 0xB0B0B0, 0, .5, 1);
if(getBit(bits, tfSkinSat)) skincolor = gradient(skincolor, 0xB0B0B0, 0, .5, 1);
if(getBit(bits, tfShellDark)) shellcolor = gradient(shellcolor, 0, 0, .5, 1);
if(getBit(bits, tfSkinDark)) skincolor = gradient(skincolor, 0, 0, .5, 1);
for(int i=0; i<12; i++) {
color_t col =
2017-07-10 18:47:38 +00:00
i == 0 ? shellcolor:
i < 8 ? scutecolor :
skincolor;
int b = getBit(bits, i);
int d = darkena(col, 0, 0xFF);
if(i >= 1 && i <= 7) if(b) { d = darkena(col, 1, 0xFF); b = 0; }
if(i >= 8 && i <= 11 && stuntime >= 3) continue;
2017-07-10 18:47:38 +00:00
queuepoly(V, shTortoise[i][b+small], d);
if((i >= 5 && i <= 7) || (i >= 9 && i <= 10))
queuepoly(V * Mirror, shTortoise[i][b+small], d);
if(i == 8) {
for(int k=0; k<stuntime; k++) {
eyecolor &= 0xFEFEFE;
eyecolor >>= 1;
}
queuepoly(V, shTortoise[12][b+small], darkena(eyecolor, 0, 0xFF));
queuepoly(V * Mirror, shTortoise[12][b+small], darkena(eyecolor, 0, 0xFF));
}
}
#endif
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
int getMatchColor(int bits) {
int mcol = 1;
double d = tortoise::getScent(bits);
if(d > 0) mcol = 0xFFFFFF;
else if(d < 0) mcol = 0;
int dd = 0xFF * (atan(fabs(d)/2) / (M_PI/2));
return gradient(0x487830, mcol, 0, dd, 0xFF);
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
};
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
double footfun(double d) {
d -= floor(d);
return
d < .25 ? d :
d < .75 ? .5-d :
d-1;
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
bool ivoryz;
void animallegs(const transmatrix& V, eMonster mo, color_t col, double footphase) {
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
footphase /= SCALE;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
bool dog = mo == moRunDog;
bool bug = mo == moBug0 || mo == moMetalBeast;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(bug) footphase *= 2.5;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
double rightfoot = footfun(footphase / .4 / 2) / 4 * 2;
double leftfoot = footfun(footphase / .4 / 2 - (bug ? .5 : dog ? .1 : .25)) / 4 * 2;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(bug) rightfoot /= 2.5, leftfoot /= 2.5;
2015-08-08 13:57:52 +00:00
rightfoot *= SCALE;
leftfoot *= SCALE;
2017-07-10 18:47:38 +00:00
if(!footphase) rightfoot = leftfoot = 0;
2015-08-08 13:57:52 +00:00
2019-02-27 00:06:52 +00:00
transmatrix VAML = mmscale(V, geom3::ALEG);
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
hpcshape* sh[6][4] = {
{&shDogFrontPaw, &shDogRearPaw, &shDogFrontLeg, &shDogRearLeg},
{&shWolfFrontPaw, &shWolfRearPaw, &shWolfFrontLeg, &shWolfRearLeg},
{&shReptileFrontFoot, &shReptileRearFoot, &shReptileFrontLeg, &shReptileRearLeg},
{&shBugLeg, NULL, NULL, NULL},
{&shTrylobiteFrontClaw, &shTrylobiteRearClaw, &shTrylobiteFrontLeg, &shTrylobiteRearLeg},
{&shBullFrontHoof, &shBullRearHoof, &shBullFrontHoof, &shBullRearHoof},
};
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
hpcshape **x = sh[mo == moRagingBull ? 5 : mo == moBug0 ? 3 : mo == moMetalBeast ? 4 : mo == moRunDog ? 0 : mo == moReptile ? 2 : 1];
2015-08-08 13:57:52 +00:00
2019-03-01 17:51:16 +00:00
const transmatrix VL = (DIM == 2 ? V : mmscale(V, geom3::ALEG0));
2017-03-23 10:53:57 +00:00
if(x[0]) queuepolyat(VL * xpush(rightfoot), *x[0], col, PPR::MONSTER_FOOT);
if(x[0]) queuepolyat(VL * Mirror * xpush(leftfoot), *x[0], col, PPR::MONSTER_FOOT);
if(x[1]) queuepolyat(VL * xpush(-rightfoot), *x[1], col, PPR::MONSTER_FOOT);
if(x[1]) queuepolyat(VL * Mirror * xpush(-leftfoot), *x[1], col, PPR::MONSTER_FOOT);
2015-08-08 13:57:52 +00:00
2018-08-28 12:27:23 +00:00
if(x[2]) queuepolyat(VAML * xpush(rightfoot/2), *x[2], col, PPR::MONSTER_FOOT);
if(x[2]) queuepolyat(VAML * Mirror * xpush(leftfoot/2), *x[2], col, PPR::MONSTER_FOOT);
if(x[3]) queuepolyat(VAML * xpush(-rightfoot/2), *x[3], col, PPR::MONSTER_FOOT);
if(x[3]) queuepolyat(VAML * Mirror * xpush(-leftfoot/2), *x[3], col, PPR::MONSTER_FOOT);
#endif
2015-08-08 13:57:52 +00:00
}
bool noshadow;
#if CAP_SHAPES
void ShadowV(const transmatrix& V, const hpcshape& bp, PPR prio) {
2017-07-10 18:47:38 +00:00
if(mmspatial) {
if(model_needs_depth() || noshadow)
2017-07-10 18:47:38 +00:00
return; // shadows break the depth testing
dynamicval<color_t> p(poly_outline, OUTLINE_TRANS);
2017-07-10 18:47:38 +00:00
queuepolyat(V, bp, SHADOW_MON, prio);
2015-08-08 13:57:52 +00:00
}
}
#endif
2015-08-08 13:57:52 +00:00
#if CAP_SHAPES
transmatrix otherbodyparts(const transmatrix& V, color_t col, eMonster who, double footphase) {
2017-07-10 18:47:38 +00:00
#define VFOOT (DIM == 2 ? V : mmscale(V, geom3::LEG0))
2017-07-10 18:47:38 +00:00
#define VLEG mmscale(V, geom3::LEG)
#define VGROIN mmscale(V, geom3::GROIN)
#define VBODY mmscale(V, geom3::BODY)
#define VBODY1 mmscale(V, geom3::BODY1)
#define VBODY2 mmscale(V, geom3::BODY2)
#define VBODY3 mmscale(V, geom3::BODY3)
2017-07-10 18:47:38 +00:00
#define VNECK mmscale(V, geom3::NECK)
2019-02-27 00:15:40 +00:00
#define VHEAD mmscale(V, geom3::HEAD)
#define VHEAD1 mmscale(V, geom3::HEAD1)
#define VHEAD2 mmscale(V, geom3::HEAD2)
2017-07-10 18:47:38 +00:00
#define VALEGS V
#define VABODY mmscale(V, geom3::ABODY)
#define VAHEAD mmscale(V, geom3::AHEAD)
2017-07-10 18:47:38 +00:00
#define VFISH V
2019-03-04 16:56:49 +00:00
#define VBIRD ((DIM == 3 || (where && bird_disruption(where))) ? V : mmscale(V, geom3::BIRD + .05 * sintick(1000, (int) (size_t(where))/1000.)))
2017-07-10 18:47:38 +00:00
#define VGHOST mmscale(V, geom3::GHOST)
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
#define VSLIMEEYE mscale(V, geom3::FLATEYE)
2017-07-10 18:47:38 +00:00
// if(!mmspatial && !footphase && who != moSkeleton) return;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
footphase /= SCALE;
double rightfoot = footfun(footphase / .4 / 2.5) / 4 * 2.5 * SCALE;
const double wobble = -1;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
// todo
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(detaillevel >= 2) {
transmatrix VL = mmscale(V, geom3::LEG1);
queuepoly(VL * xpush(rightfoot*3/4), shHumanLeg, col);
queuepoly(VL * Mirror * xpush(-rightfoot*3/4), shHumanLeg, col);
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(true) {
transmatrix VL = mmscale(V, geom3::LEG);
queuepoly(VL * xpush(rightfoot/2), shHumanLeg, col);
queuepoly(VL * Mirror * xpush(-rightfoot/2), shHumanLeg, col);
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(detaillevel >= 2) {
transmatrix VL = mmscale(V, geom3::LEG3);
queuepoly(VL * xpush(rightfoot/4), shHumanLeg, col);
queuepoly(VL * Mirror * xpush(-rightfoot/4), shHumanLeg, col);
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(who == moWaterElemental) {
double fishtail = footfun(footphase / .4) / 4 * 1.5;
queuepoly(VFOOT * xpush(fishtail), shFishTail, watercolor(100));
}
else if(who == moSkeleton) {
queuepoly(VFOOT * xpush(rightfoot), shSkeletalFoot, col);
queuepoly(VFOOT * Mirror * xpush(-rightfoot), shSkeletalFoot, col);
return spin(rightfoot * wobble);
2017-07-10 18:47:38 +00:00
}
else if(isTroll(who) || who == moMonkey || who == moYeti || who == moRatling || who == moRatlingAvenger || who == moGoblin) {
queuepoly(VFOOT * xpush(rightfoot), shYetiFoot, col);
queuepoly(VFOOT * Mirror * xpush(-rightfoot), shYetiFoot, col);
2015-08-08 13:57:52 +00:00
}
else {
2017-07-10 18:47:38 +00:00
queuepoly(VFOOT * xpush(rightfoot), shHumanFoot, col);
queuepoly(VFOOT * Mirror * xpush(-rightfoot), shHumanFoot, col);
2015-08-08 13:57:52 +00:00
}
if(!mmspatial) return spin(rightfoot * wobble);
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(detaillevel >= 2 && who != moZombie)
queuepoly(mmscale(V, geom3::NECK1), shHumanNeck, col);
if(detaillevel >= 1) {
queuepoly(VGROIN, shHumanGroin, col);
if(who != moZombie) queuepoly(VNECK, shHumanNeck, col);
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
if(detaillevel >= 2) {
queuepoly(mmscale(V, geom3::GROIN1), shHumanGroin, col);
if(who != moZombie) queuepoly(mmscale(V, geom3::NECK3), shHumanNeck, col);
2016-08-26 09:58:03 +00:00
}
return spin(rightfoot * wobble);
return Id;
2016-08-26 09:58:03 +00:00
}
#endif
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
bool drawstar(cell *c) {
for(int t=0; t<c->type; t++)
if(c->move(t) && c->move(t)->wall != waSulphur && c->move(t)->wall != waSulphurC &&
c->move(t)->wall != waBarrier)
2017-07-10 18:47:38 +00:00
return false;
return true;
}
bool drawing_usershape_on(cell *c, mapeditor::eShapegroup sg) {
#if CAP_EDIT
2018-08-28 12:09:36 +00:00
return c && c == mapeditor::drawcell && mapeditor::drawcellShapeGroup() == sg;
#else
return false;
#endif
}
2019-02-28 15:24:46 +00:00
hyperpoint makeradar(transmatrix V) {
hyperpoint h = tC0(V);
using namespace hyperpoint_vec;
h = h * (hdist0(h) / sightranges[geometry] / hypot_d(3, h));
return h;
}
color_t kind_outline(eItem it) {
int k = itemclass(it);
if(k == IC_TREASURE)
return OUTLINE_TREASURE;
else if(k == IC_ORB)
return OUTLINE_ORB;
else
return OUTLINE_OTHER;
}
bool drawItemType(eItem it, cell *c, const transmatrix& V, color_t icol, int pticks, bool hidden) {
#if !CAP_SHAPES
return it;
#else
2019-02-17 17:41:40 +00:00
char xch = iinf[it].glyph;
auto sinptick = [c, pticks] (int period) { return c ? sintick(period) : sin(animation_factor * pticks / period);};
auto spinptick = [c, pticks] (int period, ld phase=0) { return c ? spintick(period, phase) : spin((animation_factor * pticks + phase) / period); };
2017-10-28 08:04:28 +00:00
int ct6 = c ? ctof(c) : 1;
2017-07-10 18:47:38 +00:00
hpcshape *xsh =
(it == itPirate || it == itKraken) ? &shPirateX :
(it == itBuggy || it == itBuggy2) ? &shPirateX :
it == itHolyGrail ? &shGrail :
isElementalShard(it) ? &shElementalShard :
(it == itBombEgg || it == itTrollEgg) ? &shEgg :
2017-10-10 12:24:39 +00:00
it == itHunting ? &shTriangle :
2017-07-10 18:47:38 +00:00
it == itDodeca ? &shDodeca :
xch == '*' ? &shGem[ct6] :
2017-10-06 22:34:10 +00:00
xch == '(' ? &shKnife :
2018-05-07 18:13:56 +00:00
it == itShard ? &shMFloor.b[0] :
2017-07-10 18:47:38 +00:00
it == itTreat ? &shTreat :
it == itSlime ? &shEgg :
xch == '%' ? &shDaisy : xch == '$' ? &shStar : xch == ';' ? &shTriangle :
xch == '!' ? &shTriangle : it == itBone ? &shNecro : it == itStatue ? &shStatue :
it == itIvory ? &shFigurine :
xch == '?' ? &shBookCover :
it == itKey ? &shKey :
it == itRevolver ? &shGun :
NULL;
2019-02-28 15:24:46 +00:00
if(c && doHighlight())
poly_outline = kind_outline(it);
if(DIM == 3 && c == viewctr.at->c7) return false;
2019-02-28 15:24:46 +00:00
#if MAXMDIM >= 4
if(c && DIM == 3) radarpoints.emplace_back(radarpoint{makeradar(V), iinf[it].glyph, icol, kind_outline(it)});
#endif
2016-08-26 09:58:03 +00:00
transmatrix Vit = V;
2019-02-26 13:35:04 +00:00
if(DIM == 3 && c) Vit = rgpushxto0(tC0(V));
// V * cspin(0, 2, ptick(618, 0));
if(c && conformal::includeHistory && conformal::infindhistory.count(c)) poly_outline = OUTLINE_DEAD;
2017-07-10 18:47:38 +00:00
if(!mmitem && it) return true;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
else if(it == itSavedPrincess) {
drawMonsterType(moPrincess, c, V, icol, 0);
return false;
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(it == itStrongWind) {
queuepoly(Vit * spinptick(750), shFan, darkena(icol, 0, 255));
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
else if(it == itWarning) {
queuepoly(Vit * spinptick(750), shTriangle, darkena(icol, 0, 255));
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
else if(it == itBabyTortoise) {
int bits = c ? tortoise::babymap[c] : tortoise::last;
int over = c && c->monst == moTortoise;
tortoise::draw(Vit * spinptick(5000) * ypush(crossf*.15), bits, over ? 4 : 2, 0);
// queuepoly(Vit, shHeptaMarker, darkena(tortoise::getMatchColor(bits), 0, 0xC0));
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(it == itCompass) {
transmatrix V2;
2019-02-17 17:28:20 +00:00
#if CAP_CRYSTAL
if(geometry == gCrystal) {
if(crystal::compass_probability <= 0) return true;
if(cwt.at->land == laCamelot && celldistAltRelative(cwt.at) < 0) crystal::used_compass_inside = true;
2018-12-03 22:03:35 +00:00
V2 = V * spin(crystal::compass_angle() + M_PI);
}
2019-02-17 17:28:20 +00:00
else
#endif
if(1) {
2018-12-03 22:03:35 +00:00
cell *c1 = c ? findcompass(c) : NULL;
if(c1) {
transmatrix P = ggmatrix(c1);
hyperpoint P1 = tC0(P);
if(isPlayerOn(c)) {
queuechr(P1, 2*vid.fsize, 'X', 0x10100 * int(128 + 100 * sintick(150)));
// queuestr(V, 1, its(compassDist(c)), 0x10101 * int(128 - 100 * sin(ticks / 150.)), 1);
queuestr(P1, vid.fsize, its(-compassDist(c)), 0x10101 * int(128 - 100 * sintick(150)));
addauraspecial(P1, 0xFF0000, 0);
2018-12-03 22:03:35 +00:00
}
V2 = V * rspintox(inverse(V) * P1);
2017-08-06 12:50:16 +00:00
}
2018-12-03 22:03:35 +00:00
else V2 = V;
2017-08-06 12:50:16 +00:00
}
if(DIM == 3) {
queuepoly(Vit, shRing, 0xFFFFFFFF);
V2 = V2 * cspin(1, 2, M_PI * sintick(100) / 39);
queuepoly(V2, shCompass3, 0xFF0000FF);
queuepoly(V2 * pispin, shCompass3, 0x000000FF);
}
else {
if(c) V2 = V2 * spin(M_PI * sintick(100) / 30);
queuepoly(V2, shCompass1, 0xFF8080FF);
queuepoly(V2, shCompass2, 0xFFFFFFFF);
queuepoly(V2, shCompass3, 0xFF0000FF);
queuepoly(V2 * pispin, shCompass3, 0x000000FF);
}
2017-07-10 18:47:38 +00:00
xsh = NULL;
}
2017-07-10 18:47:38 +00:00
else if(it == itPalace) {
transmatrix V2 = Vit * spin(ticks / 1500.);
draw_floorshape(c, V2, shMFloor3, 0xFFD500FF);
draw_floorshape(c, V2, shMFloor4, darkena(icol, 0, 0xFF));
2017-07-10 18:47:38 +00:00
queuepoly(V2, shGem[ct6], 0xFFD500FF);
xsh = NULL;
2017-03-23 10:53:57 +00:00
}
else if(mapeditor::drawUserShape(V, mapeditor::sgItem, it, darkena(icol, 0, 0xFF), c)) ;
2017-07-10 18:47:38 +00:00
else if(it == itRose) {
for(int u=0; u<4; u++)
queuepoly(Vit * spinptick(1500) * spin(2*M_PI / 3 / 4 * u), shRose, darkena(icol, 0, hidden ? 0x30 : 0xA0));
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(it == itBarrow && c) {
for(int i = 0; i<c->landparam; i++)
queuepolyat(Vit * spin(2 * M_PI * i / c->landparam) * xpush(.15) * spinptick(1500), *xsh, darkena(icol, 0, hidden ? 0x40 :
2017-07-10 18:47:38 +00:00
(highwall(c) && wmspatial) ? 0x60 : 0xFF),
2018-08-28 12:27:23 +00:00
PPR::HIDDEN);
2017-03-23 10:53:57 +00:00
// queuepoly(Vit*spin(M_PI+(1-2*ang)*2*M_PI/S84), shMagicSword, darkena(0xC00000, 0, 0x80 + 0x70 * sin(ticks / 200.0)));
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
else if(xsh) {
if(it == itFireShard) icol = firecolor(100);
if(it == itWaterShard) icol = watercolor(100) >> 8;
if(it == itZebra) icol = 0xFFFFFF;
if(it == itLotus) icol = 0x101010;
2018-01-04 14:46:06 +00:00
if(it == itSwitch) icol = minf[active_switch()].color;
2017-07-10 18:47:38 +00:00
transmatrix V2 = Vit * spinptick(1500);
2017-03-23 10:53:57 +00:00
if(xsh == &shBookCover && mmitem) {
if(DIM == 3)
queuepoly(V2 * cpush(2, 1e-3), shBook, 0x805020FF);
else
queuepoly(V2, shBook, 0x805020FF);
}
2017-09-30 09:46:41 +00:00
2018-08-28 12:27:23 +00:00
PPR pr = PPR::ITEM;
2017-09-30 09:46:41 +00:00
int alpha = hidden ? (it == itKraken ? 0xC0 : 0x40) : 0xF0;
2018-08-28 12:27:23 +00:00
if(c && c->wall == waIcewall) pr = PPR::HIDDEN, alpha = 0x80;
2016-01-02 10:09:13 +00:00
2017-09-30 09:46:41 +00:00
queuepolyat(V2, *xsh, darkena(icol, 0, alpha), pr);
2017-07-10 18:47:38 +00:00
if(it == itZebra)
queuepolyat(Vit * spinptick(1500, .5/(ct6+6)), *xsh, darkena(0x202020, 0, hidden ? 0x40 : 0xF0), PPR::ITEMb);
2016-08-26 09:58:03 +00:00
}
2017-07-12 16:03:53 +00:00
else if(xch == 'o' || it == itInventory) {
2017-07-10 18:47:38 +00:00
if(it == itOrbFire) icol = firecolor(100);
2018-08-28 12:27:23 +00:00
PPR prio = PPR::ITEM;
bool inice = c && c->wall == waIcewall;
2018-08-28 12:27:23 +00:00
if(inice) prio = PPR::HIDDEN;
2017-10-17 11:13:36 +00:00
int icol1 = icol;
2017-07-10 18:47:38 +00:00
if(it == itOrbFire) icol = firecolor(200);
if(it == itOrbFriend || it == itOrbDiscord) icol = 0xC0C0C0;
if(it == itOrbFrog) icol = 0xFF0000;
if(it == itOrbDash) icol = 0xFF0000;
if(it == itOrbFreedom) icol = 0xC0FF00;
if(it == itOrbAir) icol = 0xFFFFFF;
if(it == itOrbUndeath) icol = minf[moFriendlyGhost].color;
if(it == itOrbRecall) icol = 0x101010;
if(it == itOrbSlaying) icol = 0xFF0000;
color_t col = darkena(icol, 0, int(0x80 + 0x70 * sinptick(300)));
2017-10-17 11:13:36 +00:00
if(it == itOrbFish)
queuepolyat(Vit * spinptick(1500), shFishTail, col, PPR::ITEM_BELOW);
2017-10-17 11:13:36 +00:00
queuepolyat(Vit, shDisk, darkena(icol1, 0, inice ? 0x80 : hidden ? 0x20 : 0xC0), prio);
2017-07-10 18:47:38 +00:00
hpcshape& sh =
2017-07-16 21:00:55 +00:00
it == itOrbLove ? shLoveRing :
2017-07-10 18:47:38 +00:00
isRangedOrb(it) ? shTargetRing :
isOffensiveOrb(it) ? shSawRing :
isFriendOrb(it) ? shPeaceRing :
isUtilityOrb(it) ? shGearRing :
isDirectionalOrb(it) ? shSpearRing :
among(it, itOrb37, itOrbGravity) ? shHeptaRing :
2017-07-10 18:47:38 +00:00
shRing;
queuepolyat(Vit * spinptick(1500), sh, col, prio);
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
else if(it) return true;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
return false;
#endif
2016-08-26 09:58:03 +00:00
}
#if CAP_SHAPES
2017-10-04 19:26:26 +00:00
void drawTerraWarrior(const transmatrix& V, int t, int hp, double footphase) {
2017-09-30 09:46:41 +00:00
ShadowV(V, shPBody);
color_t col = linf[laTerracotta].color;
2017-10-04 19:26:26 +00:00
int bcol = darkena(false ? 0xC0B23E : col, 0, 0xFF);
const transmatrix VBS = otherbodyparts(V, bcol, moDesertman, footphase);
queuepoly(VBODY * VBS, shPBody, bcol);
if(!peace::on) queuepoly(VBODY * VBS * Mirror, shPSword, darkena(0xC0C0C0, 0, 0xFF));
queuepoly(VBODY1 * VBS, shTerraArmor1, darkena(t > 0 ? 0x4040FF : col, 0, 0xFF));
if(hp >= 4) queuepoly(VBODY2 * VBS, shTerraArmor2, darkena(t > 1 ? 0xC00000 : col, 0, 0xFF));
if(hp >= 2) queuepoly(VBODY3 * VBS, shTerraArmor3, darkena(t > 2 ? 0x612600 : col, 0, 0xFF));
2017-10-04 19:26:26 +00:00
queuepoly(VHEAD, shTerraHead, darkena(t > 4 ? 0x202020 : t > 3 ? 0x504040 : col, 0, 0xFF));
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shPFace, bcol);
2017-09-30 09:46:41 +00:00
}
#endif
2017-09-30 09:46:41 +00:00
bool drawMonsterType(eMonster m, cell *where, const transmatrix& V, color_t col, double footphase) {
2015-08-08 13:57:52 +00:00
2019-02-28 15:24:46 +00:00
#if MAXMDIM >= 4
if(DIM == 3 && m != moPlayer)
radarpoints.emplace_back(radarpoint{makeradar(V), minf[m].glyph, col, isFriendly(m) ? 0x00FF00FF : 0xFF0000FF });
#endif
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
char xch = minf[m].glyph;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(m == moTortoise && where && where->stuntime >= 3)
drawStunStars(V, where->stuntime-2);
else if (m == moTortoise || m == moPlayer || (where && !where->stuntime)) ;
else if(where && !(isMetalBeast(m) && where->stuntime == 1))
drawStunStars(V, where->stuntime);
2017-07-10 18:47:38 +00:00
if(m == moTortoise) {
int bits = where ? tortoise::getb(where) : tortoise::last;
tortoise::draw(V, bits, 0, where ? where->stuntime : 0);
if(tortoise::seek() && !tortoise::diff(bits) && where)
queuepoly(V, shRing, darkena(0xFFFFFF, 0, 0x80 + 0x70 * sintick(200)));
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moPlayer) {
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
charstyle& cs = getcs();
if(mapeditor::drawplayer && !mapeditor::drawUserShape(V, mapeditor::sgPlayer, cs.charid, cs.skincolor, where)) {
2017-07-10 18:47:38 +00:00
if(cs.charid >= 8) {
if(!mmspatial && !footphase)
queuepoly(VALEGS, shWolfLegs, fc(150, cs.dresscolor, 4));
else {
ShadowV(V, shWolfBody);
animallegs(VALEGS, moWolf, fc(500, cs.dresscolor, 4), footphase);
}
queuepoly(VABODY, shWolfBody, fc(0, cs.skincolor, 0));
queuepoly(VAHEAD, shFamiliarHead, fc(500, cs.haircolor, 2));
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
color_t col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.swordcolor, 3);
2017-07-10 18:47:38 +00:00
queuepoly(VAHEAD, shFamiliarEye, col);
queuepoly(VAHEAD * Mirror, shFamiliarEye, col);
}
if(knighted)
queuepoly(VABODY, shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
if(tortoise::seek())
tortoise::draw(VABODY, tortoise::seekbits, 4, 0);
2017-07-10 18:47:38 +00:00
}
else if(cs.charid >= 6) {
if(!mmspatial && !footphase)
queuepoly(VABODY, shDogBody, fc(0, cs.skincolor, 0));
else {
ShadowV(V, shDogTorso);
animallegs(VALEGS, moRunDog, fc(500, cs.dresscolor, 4), footphase);
queuepoly(VABODY, shDogTorso, fc(0, cs.skincolor, 0));
}
queuepoly(VAHEAD, shDogHead, fc(150, cs.haircolor, 2));
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
color_t col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.swordcolor, 3);
2017-07-10 18:47:38 +00:00
queuepoly(VAHEAD, shWolf1, col);
queuepoly(VAHEAD, shWolf2, col);
}
color_t colnose = items[itOrbDiscord] ? watercolor(0) : fc(314, 0xFF, 3);
queuepoly(VAHEAD, shWolf3, colnose);
if(knighted)
queuepoly(VABODY, shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
if(tortoise::seek())
tortoise::draw(VABODY, tortoise::seekbits, 4, 0);
2017-07-10 18:47:38 +00:00
}
else if(cs.charid >= 4) {
if(!mmspatial && !footphase)
queuepoly(VALEGS, shCatLegs, fc(500, cs.dresscolor, 4));
else {
ShadowV(V, shCatBody);
animallegs(VALEGS, moRunDog, fc(500, cs.dresscolor, 4), footphase);
}
queuepoly(VABODY, shCatBody, fc(0, cs.skincolor, 0));
queuepoly(VAHEAD, shCatHead, fc(150, cs.haircolor, 2));
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
color_t col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.swordcolor, 3);
2017-07-10 18:47:38 +00:00
queuepoly(VAHEAD * xpush(.04), shWolf1, col);
queuepoly(VAHEAD * xpush(.04), shWolf2, col);
}
if(knighted)
queuepoly(VABODY, shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
if(tortoise::seek())
tortoise::draw(VABODY, tortoise::seekbits, 4, 0);
2017-07-10 18:47:38 +00:00
}
else {
2015-08-08 13:57:52 +00:00
const transmatrix VBS = otherbodyparts(V, fc(0, cs.skincolor, 0), items[itOrbFish] ? moWaterElemental : moPlayer, footphase);
2015-08-08 13:57:52 +00:00
queuepoly(VBODY * VBS, (cs.charid&1) ? shFemaleBody : shPBody, fc(0, cs.skincolor, 0));
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
ShadowV(V, (cs.charid&1) ? shFemaleBody : shPBody);
2017-06-18 16:51:46 +00:00
2017-07-10 18:47:38 +00:00
if(cs.charid&1)
queuepoly(VBODY1 * VBS, shFemaleDress, fc(500, cs.dresscolor, 4));
2017-06-18 16:51:46 +00:00
2017-07-10 18:47:38 +00:00
if(cs.charid == 2)
queuepoly(VBODY2 * VBS, shPrinceDress, fc(400, cs.dresscolor, 5));
2017-07-10 18:47:38 +00:00
if(cs.charid == 3)
queuepoly(VBODY2 * VBS, shPrincessDress, fc(400, cs.dresscolor2, 5));
2017-10-12 10:05:12 +00:00
if(items[itOrbSide3])
queuepoly(VBODY * VBS, (cs.charid&1) ? shFerocityF : shFerocityM, fc(0, cs.skincolor, 0));
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(items[itOrbHorns]) {
queuepoly(VBODY * VBS, shBullHead, items[itOrbDiscord] ? watercolor(0) : 0xFF000030);
queuepoly(VBODY * VBS, shBullHorn, items[itOrbDiscord] ? watercolor(0) : 0xFF000040);
queuepoly(VBODY * VBS * Mirror, shBullHorn, items[itOrbDiscord] ? watercolor(0) : 0xFF000040);
2017-07-10 18:47:38 +00:00
}
2017-10-12 10:05:12 +00:00
if(items[itOrbSide1] && !shmup::on)
queuepoly(VBODY * VBS * spin(-M_PI/24), cs.charid >= 2 ? shSabre : shPSword, fc(314, cs.swordcolor, 3)); // 3 not colored
2017-10-12 10:05:12 +00:00
transmatrix VWPN = cs.lefthanded ? VBODY * VBS * Mirror : VBODY * VBS;
2018-11-10 17:32:55 +00:00
2019-01-07 03:55:21 +00:00
if(peace::on) ;
else if(racing::on) {
2019-01-24 13:50:36 +00:00
#if CAP_RACING
2019-01-07 03:55:21 +00:00
if(racing::trophy[multi::cpid])
queuepoly(VWPN, shTrophy, racing::trophy[multi::cpid]);
2019-01-24 13:50:36 +00:00
#endif
2019-01-07 03:55:21 +00:00
}
2017-07-10 18:47:38 +00:00
else if(items[itOrbThorns])
2018-11-10 17:32:55 +00:00
queuepoly(VWPN, shHedgehogBladePlayer, items[itOrbDiscord] ? watercolor(0) : 0x00FF00FF);
2017-07-10 18:47:38 +00:00
else if(!shmup::on && items[itOrbDiscord])
2018-11-10 17:32:55 +00:00
queuepoly(VWPN, cs.charid >= 2 ? shSabre : shPSword, watercolor(0));
2017-07-10 18:47:38 +00:00
else if(items[itRevolver])
2018-11-10 17:32:55 +00:00
queuepoly(VWPN, shGunInHand, fc(314, cs.swordcolor, 3)); // 3 not colored
2018-01-03 21:32:34 +00:00
else if(items[itOrbSlaying]) {
2018-11-10 17:32:55 +00:00
queuepoly(VWPN, shFlailTrunk, fc(314, cs.swordcolor, 3));
queuepoly(VWPN, shHammerHead, fc(314, cs.swordcolor, 3));
2018-01-03 21:32:34 +00:00
}
2017-07-10 18:47:38 +00:00
else if(!shmup::on)
2018-11-10 17:32:55 +00:00
queuepoly(VWPN, cs.charid >= 2 ? shSabre : shPSword, fc(314, cs.swordcolor, 3)); // 3 not colored
2017-07-10 18:47:38 +00:00
else if(shmup::curtime >= shmup::getPlayer()->nextshot)
2018-11-10 17:32:55 +00:00
queuepoly(VWPN, shPKnife, fc(314, cs.swordcolor, 3)); // 3 not colored
2017-07-10 18:47:38 +00:00
if(items[itOrbBeauty]) {
if(cs.charid&1)
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shFlowerHair, darkena(iinf[itOrbBeauty].color, 0, 0xFF));
2017-07-10 18:47:38 +00:00
else
2018-11-10 17:32:55 +00:00
queuepoly(VWPN, shFlowerHand, darkena(iinf[itOrbBeauty].color, 0, 0xFF));
2017-07-10 18:47:38 +00:00
}
if(where && where->land == laWildWest) {
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shWestHat1, darkena(cs.swordcolor, 1, 0XFF));
queuepoly(VHEAD2, shWestHat2, darkena(cs.swordcolor, 0, 0XFF));
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(cheater && !autocheat) {
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, (cs.charid&1) ? shGoatHead : shDemon, darkena(0xFFFF00, 0, 0xFF));
2017-07-10 18:47:38 +00:00
// queuepoly(V, shHood, darkena(0xFF00, 1, 0xFF));
}
else {
queuepoly(VHEAD, shPFace, fc(500, cs.skincolor, 1));
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, (cs.charid&1) ? shFemaleHair : shPHead, fc(150, cs.haircolor, 2));
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(knighted)
queuepoly(VBS, shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(tortoise::seek())
tortoise::draw(VBS * ypush(-crossf*.25), tortoise::seekbits, 4, 0);
}
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
else if(mapeditor::drawUserShape(V, mapeditor::sgMonster, m, darkena(col, 0, 0xFF), where)) {
return false;
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
else if(isMimic(m) || m == moShadow || m == moIllusion) {
charstyle& cs = getcs();
if(mapeditor::drawUserShape(V, mapeditor::sgPlayer, cs.charid, darkena(col, 0, 0x80), where)) return false;
2017-07-10 18:47:38 +00:00
if(cs.charid >= 8) {
queuepoly(VABODY, shWolfBody, darkena(col, 0, 0xC0));
ShadowV(V, shWolfBody);
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, darkena(col, 0, 0xC0), footphase);
else
queuepoly(VABODY, shWolfLegs, darkena(col, 0, 0xC0));
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
queuepoly(VABODY, shFamiliarHead, darkena(col, 0, 0xC0));
queuepoly(VAHEAD, shFamiliarEye, darkena(col, 0, 0xC0));
queuepoly(VAHEAD * Mirror, shFamiliarEye, darkena(col, 0, 0xC0));
}
else if(cs.charid >= 6) {
ShadowV(V, shDogBody);
queuepoly(VAHEAD, shDogHead, darkena(col, 0, 0xC0));
if(mmspatial || footphase) {
animallegs(VALEGS, moRunDog, darkena(col, 0, 0xC0), footphase);
queuepoly(VABODY, shDogTorso, darkena(col, 0, 0xC0));
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else
queuepoly(VABODY, shDogBody, darkena(col, 0, 0xC0));
queuepoly(VABODY, shWolf1, darkena(col, 1, 0xC0));
queuepoly(VABODY, shWolf2, darkena(col, 1, 0xC0));
queuepoly(VABODY, shWolf3, darkena(col, 1, 0xC0));
}
else if(cs.charid >= 4) {
ShadowV(V, shCatBody);
queuepoly(VABODY, shCatBody, darkena(col, 0, 0xC0));
queuepoly(VAHEAD, shCatHead, darkena(col, 0, 0xC0));
if(mmspatial || footphase)
animallegs(VALEGS, moRunDog, darkena(col, 0, 0xC0), footphase);
else
queuepoly(VALEGS, shCatLegs, darkena(col, 0, 0xC0));
queuepoly(VAHEAD * xpush(.04), shWolf1, darkena(col, 1, 0xC0));
queuepoly(VAHEAD * xpush(.04), shWolf2, darkena(col, 1, 0xC0));
}
else {
const transmatrix VBS = otherbodyparts(V, darkena(col, 0, 0x40), m, footphase);
queuepoly(VBODY * VBS, (cs.charid&1) ? shFemaleBody : shPBody, darkena(col, 0, 0X80));
2017-03-23 10:53:57 +00:00
2017-10-12 09:43:47 +00:00
if(!shmup::on) {
bool emp = items[itOrbEmpathy] && m != moShadow;
if(items[itOrbThorns] && emp)
queuepoly(VBODY * VBS, shHedgehogBladePlayer, darkena(col, 0, 0x40));
2017-10-12 10:05:12 +00:00
if(items[itOrbSide1] && !shmup::on)
queuepoly(VBODY * VBS * spin(-M_PI/24), cs.charid >= 2 ? shSabre : shPSword, darkena(col, 0, 0x40));
if(items[itOrbSide3] && emp)
queuepoly(VBODY * VBS, (cs.charid&1) ? shFerocityF : shFerocityM, darkena(col, 0, 0x40));
2017-10-12 10:05:12 +00:00
queuepoly(VBODY * VBS, (cs.charid >= 2 ? shSabre : shPSword), darkena(col, 0, 0XC0));
2017-10-12 09:43:47 +00:00
}
2017-07-22 23:33:27 +00:00
else if(!where || shmup::curtime >= shmup::getPlayer()->nextshot)
queuepoly(VBODY * VBS, shPKnife, darkena(col, 0, 0XC0));
if(knighted)
queuepoly(VBODY3 * VBS, shKnightCloak, darkena(col, 1, 0xC0));
2017-07-10 18:47:38 +00:00
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, (cs.charid&1) ? shFemaleHair : shPHead, darkena(col, 1, 0XC0));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, darkena(col, 0, 0XC0));
if(cs.charid&1)
queuepoly(VBODY1 * VBS, shFemaleDress, darkena(col, 1, 0XC0));
2017-07-10 18:47:38 +00:00
if(cs.charid == 2)
queuepoly(VBODY2 * VBS, shPrinceDress, darkena(col, 1, 0XC0));
2017-07-10 18:47:38 +00:00
if(cs.charid == 3)
queuepoly(VBODY2 * VBS, shPrincessDress, darkena(col, 1, 0XC0));
2017-03-23 10:53:57 +00:00
}
}
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
else if(m == moBullet) {
ShadowV(V, shKnife);
queuepoly(VBODY * spin(-M_PI/4), shKnife, getcs().swordcolor);
}
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
else if(m == moKnight || m == moKnightMoved) {
ShadowV(V, shPBody);
const transmatrix VBS = otherbodyparts(V, darkena(0xC0C0A0, 0, 0xC0), m, footphase);
queuepoly(VBODY * VBS, shPBody, darkena(0xC0C0A0, 0, 0xC0));
2019-01-07 03:56:06 +00:00
if(!racing::on)
queuepoly(VBODY * VBS, shPSword, darkena(0xFFFF00, 0, 0xFF));
queuepoly(VBODY1 * VBS, shKnightArmor, darkena(0xD0D0D0, 1, 0xFF));
color_t col;
2018-08-17 11:29:00 +00:00
if(!eubinary && where && where->master->alt)
2017-07-10 18:47:38 +00:00
col = cloakcolor(roundTableRadius(where));
else
col = cloakcolor(newRoundTableRadius());
queuepoly(VBODY2 * VBS, shKnightCloak, darkena(col, 1, 0xFF));
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shPHead, darkena(0x703800, 1, 0XFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
return false;
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moGolem || m == moGolemMoved) {
ShadowV(V, shPBody);
const transmatrix VBS = otherbodyparts(V, darkena(col, 1, 0XC0), m, footphase);
queuepoly(VBODY * VBS, shPBody, darkena(col, 0, 0XC0));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shGolemhead, darkena(col, 1, 0XFF));
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
else if(isPrincess(m) || m == moFalsePrincess || m == moRoseLady || m == moRoseBeauty) {
bool girl = princessgender() == GEN_F;
bool evil = !isPrincess(m);
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
int facecolor = evil ? 0xC0B090FF : 0xD0C080FF;
ShadowV(V, girl ? shFemaleBody : shPBody);
const transmatrix VBS = otherbodyparts(V, facecolor, m, footphase);
queuepoly(VBODY * VBS, girl ? shFemaleBody : shPBody, facecolor);
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(m == moPrincessArmed)
queuepoly(VBODY * VBS * Mirror, vid.cs.charid < 2 ? shSabre : shPSword, 0xFFFFFFFF);
2017-07-10 18:47:38 +00:00
if((m == moFalsePrincess || m == moRoseBeauty) && where && where->cpdist == 1)
queuepoly(VBODY * VBS, shPKnife, 0xFFFFFFFF);
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(m == moRoseLady) {
queuepoly(VBODY * VBS, shPKnife, 0xFFFFFFFF);
queuepoly(VBODY * VBS * Mirror, shPKnife, 0xFFFFFFFF);
2017-07-10 18:47:38 +00:00
}
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(girl) {
queuepoly(VBODY1 * VBS, shFemaleDress, evil ? 0xC000C0FF : 0x00C000FF);
2017-07-10 18:47:38 +00:00
if(vid.cs.charid < 2)
queuepoly(VBODY2 * VBS, shPrincessDress, evil ? 0xC040C0C0 : 0x8080FFC0);
2017-07-10 18:47:38 +00:00
}
else {
if(vid.cs.charid < 2)
queuepoly(VBODY1 * VBS, shPrinceDress, evil ? 0x802080FF : 0x404040FF);
2017-07-10 18:47:38 +00:00
}
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(m == moRoseLady) {
// queuepoly(V, girl ? shGoatHead : shDemon, 0x800000FF);
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, girl ? shFemaleHair : shPHead, evil ? 0x500050FF : 0x332A22FF);
2017-07-10 18:47:38 +00:00
}
else if(m == moRoseBeauty) {
if(girl) {
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shBeautyHair, 0xF0A0D0FF);
queuepoly(VHEAD1, shFlowerHair, 0xC00000FF);
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
else {
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shPHead, 0xF0A0D0FF);
queuepoly(VBS, shFlowerHand, 0xC00000FF);
queuepoly(VBODY2 * VBS, shSuspenders, 0xC00000FF);
2017-07-10 18:47:38 +00:00
}
}
else {
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, girl ? shFemaleHair : shPHead,
2017-07-10 18:47:38 +00:00
evil ? 0xC00000FF : 0x332A22FF);
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, facecolor);
2016-08-26 09:58:03 +00:00
}
2017-10-08 09:12:03 +00:00
else if(m == moWolf || m == moRedFox || m == moWolfMoved || m == moLavaWolf) {
2017-07-10 18:47:38 +00:00
ShadowV(V, shWolfBody);
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, darkena(col, 0, 0xFF), footphase);
else
queuepoly(VALEGS, shWolfLegs, darkena(col, 0, 0xFF));
queuepoly(VABODY, shWolfBody, darkena(col, 0, 0xFF));
2017-10-14 17:52:02 +00:00
if(m == moRedFox) {
queuepoly(VABODY, shFoxTail1, darkena(col, 0, 0xFF));
queuepoly(VABODY, shFoxTail2, darkena(0xFFFFFF, 0, 0xFF));
}
2017-07-10 18:47:38 +00:00
queuepoly(VAHEAD, shWolfHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shWolfEyes, darkena(col, 3, 0xFF));
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
else if(isBull(m)) {
ShadowV(V, shBullBody);
int hoofcol = darkena(gradient(0, col, 0, .65, 1), 0, 0xFF);
if(mmspatial || footphase)
animallegs(VALEGS, moRagingBull, hoofcol, footphase);
queuepoly(VABODY, shBullBody, darkena(gradient(0, col, 0, .80, 1), 0, 0xFF));
queuepoly(VAHEAD, shBullHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shBullHorn, darkena(0xFFFFFF, 0, 0xFF));
queuepoly(VAHEAD * Mirror, shBullHorn, darkena(0xFFFFFF, 0, 0xFF));
}
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
else if(m == moReptile) {
ShadowV(V, shReptileBody);
animallegs(VALEGS, moReptile, darkena(col, 0, 0xFF), footphase);
queuepoly(VABODY, shReptileBody, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shReptileHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shReptileEye, darkena(col, 3, 0xFF));
queuepoly(VAHEAD * Mirror, shReptileEye, darkena(col, 3, 0xFF));
queuepoly(VABODY, shReptileTail, darkena(col, 2, 0xFF));
2017-03-23 10:53:57 +00:00
}
2017-10-08 09:12:03 +00:00
else if(m == moSalamander) {
ShadowV(V, shReptileBody);
animallegs(VALEGS, moReptile, darkena(0xD00000, 1, 0xFF), footphase);
queuepoly(VABODY, shReptileBody, darkena(0xD00000, 0, 0xFF));
queuepoly(VAHEAD, shReptileHead, darkena(0xD00000, 1, 0xFF));
queuepoly(VAHEAD, shReptileEye, darkena(0xD00000, 0, 0xFF));
queuepoly(VAHEAD * Mirror, shReptileEye, darkena(0xD00000, 0, 0xFF));
queuepoly(VABODY, shReptileTail, darkena(0xD08000, 0, 0xFF));
}
2017-07-10 18:47:38 +00:00
else if(m == moVineBeast) {
ShadowV(V, shWolfBody);
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, 0x00FF00FF, footphase);
else
queuepoly(VALEGS, shWolfLegs, 0x00FF00FF);
queuepoly(VABODY, shWolfBody, darkena(col, 1, 0xFF));
queuepoly(VAHEAD, shWolfHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, shWolfEyes, 0xFF0000FF);
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moMouse || m == moMouseMoved) {
queuepoly(VALEGS, shMouse, darkena(col, 0, 0xFF));
queuepoly(VALEGS, shMouseLegs, darkena(col, 1, 0xFF));
queuepoly(VALEGS, shMouseEyes, 0xFF);
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(isBug(m)) {
ShadowV(V, shBugBody);
if(!mmspatial && !footphase)
queuepoly(VABODY, shBugBody, darkena(col, 0, 0xFF));
else {
animallegs(VALEGS, moBug0, darkena(col, 0, 0xFF), footphase);
queuepoly(VABODY, shBugAntenna, darkena(col, 1, 0xFF));
}
queuepoly(VABODY, shBugArmor, darkena(col, 1, 0xFF));
2017-03-23 10:53:57 +00:00
}
2018-12-23 16:41:12 +00:00
else if(among(m, moRunDog, moHunterDog, moHunterGuard, moHunterChanging, moFallingDog)) {
2017-07-10 18:47:38 +00:00
if(!mmspatial && !footphase)
queuepoly(VABODY, shDogBody, darkena(col, 0, 0xFF));
else {
ShadowV(V, shDogTorso);
queuepoly(VABODY, shDogTorso, darkena(col, 0, 0xFF));
2018-12-23 16:41:12 +00:00
animallegs(VALEGS, moRunDog, m == moFallingDog ? 0xFFFFFFFF : darkena(col, 0, 0xFF), footphase);
2017-07-10 18:47:38 +00:00
}
queuepoly(VAHEAD, shDogHead, darkena(col, 0, 0xFF));
2017-09-30 09:46:41 +00:00
{
dynamicval<color_t> dp(poly_outline);
int eyecolor = 0x202020;
bool redeyes = false;
if(m == moHunterDog) eyecolor = 0xFF0000, redeyes = true;
if(m == moHunterGuard) eyecolor = 0xFF6000, redeyes = true;
2017-10-15 22:15:54 +00:00
if(m == moHunterChanging) eyecolor = 0xFFFF00, redeyes = true;
int eyes = darkena(eyecolor, 0, 0xFF);
2017-09-30 09:46:41 +00:00
2018-08-01 09:25:07 +00:00
if(redeyes) poly_outline = eyes;
queuepoly(VAHEAD, shWolf1, eyes).flags |= POLY_FORCEWIDE;
queuepoly(VAHEAD, shWolf2, eyes).flags |= POLY_FORCEWIDE;
2017-09-30 09:46:41 +00:00
}
queuepoly(VAHEAD, shWolf3, darkena(m == moRunDog ? 0x202020 : 0x000000, 0, 0xFF));
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moOrangeDog) {
if(!mmspatial && !footphase)
queuepoly(VABODY, shDogBody, darkena(0xFFFFFF, 0, 0xFF));
else {
ShadowV(V, shDogTorso);
queuepoly(VABODY, shDogTorso, darkena(0xFFFFFF, 0, 0xFF));
animallegs(VALEGS, moRunDog, darkena(0xFFFFFF, 0, 0xFF), footphase);
}
queuepoly(VAHEAD, shDogHead, darkena(0xFFFFFF, 0, 0xFF));
queuepoly(VABODY, shDogStripes, darkena(0x303030, 0, 0xFF));
queuepoly(VAHEAD, shWolf1, darkena(0x202020, 0, 0xFF));
queuepoly(VAHEAD, shWolf2, darkena(0x202020, 0, 0xFF));
queuepoly(VAHEAD, shWolf3, darkena(0x202020, 0, 0xFF));
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moShark || m == moGreaterShark || m == moCShark)
queuepoly(VFISH, shShark, darkena(col, 0, 0xFF));
else if(m == moEagle || m == moParrot || m == moBomberbird || m == moAlbatross ||
2017-09-30 09:46:41 +00:00
m == moTameBomberbird || m == moWindCrow || m == moTameBomberbirdMoved ||
2019-01-03 16:21:24 +00:00
m == moSandBird || m == moAcidBird) {
2017-07-10 18:47:38 +00:00
ShadowV(V, shEagle);
if(m == moParrot && DIM == 3)
queuepolyat(VBIRD, shEagle, darkena(col, 0, 0xFF), PPR::SUPERLINE);
else
queuepoly(VBIRD, shEagle, darkena(col, 0, 0xFF));
2017-03-23 10:53:57 +00:00
}
2019-01-03 16:21:24 +00:00
else if(among(m, moSparrowhawk, moWestHawk)) {
2017-07-10 18:47:38 +00:00
ShadowV(V, shHawk);
queuepoly(VBIRD, shHawk, darkena(col, 0, 0xFF));
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moButterfly) {
transmatrix Vwing = Id;
Vwing[1][1] = .85 + .15 * sintick(100);
2017-07-10 18:47:38 +00:00
ShadowV(V * Vwing, shButterflyWing);
queuepoly(VBIRD * Vwing, shButterflyWing, darkena(col, 0, 0xFF));
queuepoly(VBIRD, shButterflyBody, darkena(col, 2, 0xFF));
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moGadfly) {
transmatrix Vwing = Id;
Vwing[1][1] = .85 + .15 * sintick(100);
2017-07-10 18:47:38 +00:00
ShadowV(V * Vwing, shGadflyWing);
queuepoly(VBIRD * Vwing, shGadflyWing, darkena(col, 0, 0xFF));
queuepoly(VBIRD, shGadflyBody, darkena(col, 1, 0xFF));
queuepoly(VBIRD, shGadflyEye, darkena(col, 2, 0xFF));
queuepoly(VBIRD * Mirror, shGadflyEye, darkena(col, 2, 0xFF));
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moVampire || m == moBat) {
2017-07-16 21:00:55 +00:00
// vampires have no shadow and no mirror images
if(m == moBat) ShadowV(V, shBatWings);
if(m == moBat || !inmirrorcount) {
queuepoly(VBIRD, shBatWings, darkena(0x303030, 0, 0xFF));
queuepoly(VBIRD, shBatBody, darkena(0x606060, 0, 0xFF));
}
2017-07-10 18:47:38 +00:00
/* queuepoly(V, shBatMouth, darkena(0xC00000, 0, 0xFF));
queuepoly(V, shBatFang, darkena(0xFFC0C0, 0, 0xFF));
queuepoly(V*Mirror, shBatFang, darkena(0xFFC0C0, 0, 0xFF));
queuepoly(V, shBatEye, darkena(00000000, 0, 0xFF));
queuepoly(V*Mirror, shBatEye, darkena(00000000, 0, 0xFF)); */
}
2017-07-10 18:47:38 +00:00
else if(m == moGargoyle) {
ShadowV(V, shGargoyleWings);
queuepoly(VBIRD, shGargoyleWings, darkena(col, 0, 0xD0));
queuepoly(VBIRD, shGargoyleBody, darkena(col, 0, 0xFF));
}
2017-07-10 18:47:38 +00:00
else if(m == moZombie) {
int c = darkena(col, where && where->land == laHalloween ? 1 : 0, 0xFF);
const transmatrix VBS = otherbodyparts(V, c, m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shPBody);
queuepoly(VBODY * VBS, shPBody, c);
}
2017-09-30 09:46:41 +00:00
else if(m == moTerraWarrior)
2017-10-04 19:26:26 +00:00
drawTerraWarrior(V, 7, (where ? where->hitpoints : 7), footphase);
2018-12-16 23:04:59 +00:00
else if(m == moVariantWarrior) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBS, shPBody, darkena(0xFFD500, 0, 0xF0));
if(!peace::on) queuepoly(VBS, shPSword, 0xFFFF00FF);
queuepoly(VHEAD, shHood, 0x008000FF);
}
2017-07-10 18:47:38 +00:00
else if(m == moDesertman) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shPBody);
queuepoly(VBS, shPBody, darkena(col, 0, 0xC0));
if(!peace::on) queuepoly(VBS, shPSword, 0xFFFF00FF);
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shHood, 0xD0D000C0);
}
2018-01-04 14:46:06 +00:00
/* else if(isSwitch(m)) {
2017-12-30 14:12:15 +00:00
otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, shPBody);
queuepoly(VBODY, shPBody, darkena(col, 0, 0xC0));
if(!peace::on) queuepoly(VBODY, shPSword, 0xFFFF00FF);
queuepoly(VHEAD, shHood, darkena(col, 0, 0xFF));
2018-01-04 14:46:06 +00:00
} */
else if(m == moMonk) {
const transmatrix VBS = otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
2018-01-04 14:46:06 +00:00
ShadowV(V, shRaiderBody);
queuepoly(VBODY * VBS, shRaiderBody, darkena(col, 0, 0xFF));
queuepoly(VBODY1 * VBS, shRaiderShirt, darkena(col, 2, 0xFF));
if(!peace::on) queuepoly(VBODY * VBS, shPKnife, 0xFFC0C0C0);
queuepoly(VBODY2 * VBS, shRaiderArmor, darkena(col, 1, 0xFF));
queuepolyat(VBODY3 * VBS, shRatCape2, darkena(col, 2, 0xFF), PPR::MONSTER_ARMOR0);
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
}
else if(m == moCrusher) {
const transmatrix VBS = otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
2018-01-04 14:46:06 +00:00
ShadowV(V, shRaiderBody);
queuepoly(VBODY * VBS, shRaiderBody, darkena(col, 0, 0xFF));
queuepoly(VBODY1 * VBS, shRaiderShirt, darkena(col, 2, 0xFF));
queuepoly(VBODY2 * VBS, shRaiderArmor, darkena(col, 1, 0xFF));
queuepoly(VBODY * VBS, shFlailTrunk, darkena(col, 1, 0XFF));
queuepoly(VBODY1 * VBS, shHammerHead, darkena(col, 0, 0XFF));
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
}
else if(m == moPair) {
const transmatrix VBS = otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
2018-01-04 14:46:06 +00:00
ShadowV(V, shRaiderBody);
queuepoly(VBODY * VBS, shRaiderBody, darkena(col, 0, 0xFF));
queuepoly(VBODY1 * VBS, shRaiderShirt, darkena(col, 2, 0xFF));
queuepoly(VBODY2 * VBS, shRaiderArmor, darkena(col, 1, 0xFF));
queuepoly(VBODY * VBS, shPickAxe, darkena(0xA0A0A0, 0, 0XFF));
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
}
else if(m == moAltDemon || m == moHexDemon) {
const transmatrix VBS = otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
2018-01-04 14:46:06 +00:00
ShadowV(V, shRaiderBody);
queuepoly(VBODY * VBS, shRaiderBody, darkena(col, 0, 0xFF));
queuepoly(VBODY1 * VBS, shRaiderShirt, darkena(col, 2, 0xFF));
queuepoly(VBODY2 * VBS, shRaiderArmor, darkena(col, 1, 0xFF));
if(!peace::on) queuepoly(VBODY * VBS, shPSword, 0xFFD0D0D0);
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xC0C0A0, 0, 0XFF));
2018-01-02 10:15:42 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moPalace || m == moFatGuard || m == moVizier || m == moSkeleton) {
if(m == moSkeleton) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(0xFFFFFF, 0, 0xFF), moSkeleton, footphase);
queuepoly(VBS, shSkeletonBody, darkena(0xFFFFFF, 0, 0xFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shSkull, darkena(0xFFFFFF, 0, 0xFF));
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shSkullEyes, darkena(0, 0, 0xFF));
2017-07-10 18:47:38 +00:00
ShadowV(V, shSkeletonBody);
queuepoly(VBS, shSabre, 0xFFFFFFFF);
2017-07-10 18:47:38 +00:00
}
else {
ShadowV(V, shPBody);
const transmatrix VBS = otherbodyparts(V, darkena(0xFFD500, 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
if(m == moFatGuard) {
queuepoly(VBODY * VBS, shFatBody, darkena(0xC06000, 0, 0xFF));
2017-07-10 18:47:38 +00:00
col = 0xFFFFFF;
if(!where || where->hitpoints >= 3)
queuepoly(VBODY1 * VBS, shKnightCloak, darkena(0xFFC0C0, 1, 0xFF));
2017-07-10 18:47:38 +00:00
}
else {
queuepoly(VBODY * VBS, shPBody, darkena(0xFFD500, 0, 0xFF));
queuepoly(VBODY1 * VBS, shKnightArmor, m == moVizier ? 0xC000C0FF :
2017-07-10 18:47:38 +00:00
darkena(0x00C000, 1, 0xFF));
if(where && where->hitpoints >= 3)
queuepoly(VBODY2 * VBS, shKnightCloak, m == moVizier ? 0x800080Ff :
2017-07-10 18:47:38 +00:00
darkena(0x00FF00, 1, 0xFF));
}
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shTurban1, darkena(col, 1, 0xFF));
2017-07-10 18:47:38 +00:00
if(!where || where->hitpoints >= 2)
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD2, shTurban2, darkena(col, 0, 0xFF));
queuepoly(VBODY * VBS, shSabre, 0xFFFFFFFF);
2017-07-10 18:47:38 +00:00
}
drawStunStars(V, where ? where->stuntime : 0);
}
2017-07-10 18:47:38 +00:00
else if(m == moCrystalSage) {
const transmatrix VBS = VBODY * otherbodyparts(V, 0xFFFFFFFF, m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shPBody);
queuepoly(VBS, shPBody, 0xFFFFFFFF);
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shPHead, 0xFFFFFFFF);
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, 0xFFFFFFFF);
}
2017-07-10 18:47:38 +00:00
else if(m == moHedge) {
ShadowV(V, shPBody);
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
queuepoly(VBS, shPBody, darkena(col, 0, 0xFF));
queuepoly(VBS, shHedgehogBlade, 0xC0C0C0FF);
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shPHead, 0x804000FF);
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, 0xF09000FF);
}
2017-07-10 18:47:38 +00:00
else if(m == moYeti || m == moMonkey) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shPBody);
queuepoly(VBS, shYeti, darkena(col, 0, 0xC0));
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shPHead, darkena(col, 0, 0xFF));
}
2017-07-10 18:47:38 +00:00
else if(m == moResearcher) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shPBody);
queuepoly(VBS, shPBody, darkena(0xFFFF00, 0, 0xC0));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shAztecHead, darkena(col, 0, 0xFF));
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shAztecCap, darkena(0xC000C0, 0, 0xFF));
}
2017-07-10 18:47:38 +00:00
else if(m == moFamiliar) {
/* queuepoly(V, shFemaleBody, darkena(0xC0B070, 0, 0xFF));
queuepoly(V, shPFace, darkena(0xC0B070, 0, 0XFF));
queuepoly(V, shWizardCape1, 0x601000FF);
queuepoly(V, shWizardCape2, 0x781800FF);
queuepoly(V, shWizardHat1, 0x902000FF);
queuepoly(V, shWizardHat2, 0xA82800FF); */
2017-07-10 18:47:38 +00:00
// queuepoly(V, shCatBody, darkena(0x601000, 0, 0xFF));
// queuepoly(V, shGargoyleBody, darkena(0x903000, 0, 0xFF));
ShadowV(V, shWolfBody);
queuepoly(VABODY, shWolfBody, darkena(0xA03000, 0, 0xFF));
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, darkena(0xC04000, 0, 0xFF), footphase);
else
queuepoly(VALEGS, shWolfLegs, darkena(0xC04000, 0, 0xFF));
queuepoly(VAHEAD, shFamiliarHead, darkena(0xC04000, 0, 0xFF));
// queuepoly(V, shCatLegs, darkena(0x902000, 0, 0xFF));
if(true) {
queuepoly(VAHEAD, shFamiliarEye, darkena(0xFFFF000, 0, 0xFF));
queuepoly(VAHEAD * Mirror, shFamiliarEye, darkena(0xFFFF000, 0, 0xFF));
}
}
2017-07-10 18:47:38 +00:00
else if(m == moRanger) {
ShadowV(V, shPBody);
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
queuepoly(VBS, shPBody, darkena(col, 0, 0xC0));
if(!peace::on) queuepoly(VBS, shPSword, darkena(col, 0, 0xFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shArmor, darkena(col, 1, 0xFF));
}
2017-07-22 23:33:27 +00:00
else if(m == moNarciss) {
ShadowV(V, shPBody);
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
queuepoly(VBS, shFlowerHand, darkena(col, 0, 0xFF));
queuepoly(VBS, shPBody, 0xFFE080FF);
if(!peace::on) queuepoly(VBS, shPKnife, 0xC0C0C0FF);
2017-07-22 23:33:27 +00:00
queuepoly(VHEAD, shPFace, 0xFFE080FF);
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shPHead, 0x806A00FF);
2017-07-22 23:33:27 +00:00
}
else if(m == moMirrorSpirit) {
ShadowV(V, shPBody);
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0x90), m, footphase);
queuepoly(VBS, shPBody, darkena(col, 0, 0x90));
if(!peace::on) queuepoly(VBS * Mirror, shPSword, darkena(col, 0, 0xD0));
2017-07-22 23:33:27 +00:00
queuepoly(VHEAD, shPHead, darkena(col, 1, 0x90));
queuepoly(VHEAD, shPFace, darkena(col, 1, 0x90));
queuepoly(VHEAD, shArmor, darkena(col, 0, 0xC0));
}
2017-10-08 09:12:03 +00:00
else if(m == moJiangshi) {
2017-10-06 22:34:10 +00:00
ShadowV(V, shJiangShi);
auto z2 = geom3::lev_to_factor(abs(sin(footphase * M_PI * 2)) * geom3::human_height);
auto V0 = V;
auto V = mmscale(V0, z2);
otherbodyparts(V, darkena(col, 0, 0xFF), m, m == moJiangshi ? 0 : footphase);
queuepoly(VBODY, shJiangShi, darkena(col, 0, 0xFF));
queuepoly(VBODY1, shJiangShiDress, darkena(0x202020, 0, 0xFF));
2017-10-06 22:34:10 +00:00
queuepoly(VHEAD, shTerraHead, darkena(0x101010, 0, 0xFF));
queuepoly(VHEAD, shPFace, darkena(col, 0, 0xFF));
queuepoly(VHEAD, shJiangShiCap1, darkena(0x800000, 0, 0xFF));
queuepoly(VHEAD, shJiangShiCap2, darkena(0x400000, 0, 0xFF));
2017-09-30 09:46:41 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moGhost || m == moSeep || m == moFriendlyGhost) {
if(m == moFriendlyGhost) col = fghostcolor(where);
2017-07-10 18:47:38 +00:00
queuepoly(VGHOST, shGhost, darkena(col, 0, m == moFriendlyGhost ? 0xC0 : 0x80));
queuepoly(VGHOST, shEyes, 0xFF);
}
else if(m == moVineSpirit) {
queuepoly(VGHOST, shGhost, 0xD0D0D0C0);
queuepoly(VGHOST, shEyes, 0xFF0000FF);
}
else if(m == moFireFairy) {
col = firecolor(0);
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shFemaleBody);
queuepoly(VBS, shFemaleBody, darkena(col, 0, 0XC0));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shWitchHair, darkena(col, 1, 0xFF));
queuepoly(VHEAD, shPFace, darkena(col, 0, 0XFF));
}
else if(m == moSlime) {
queuepoly(VFISH, shSlime, darkena(col, 0, 0x80));
queuepoly(VSLIMEEYE, shEyes, 0xFF);
}
2018-01-04 14:46:06 +00:00
else if(isSwitch(m)) {
queuepoly(VFISH, shJelly, darkena(col, 0, 0xD0));
2018-08-28 12:27:23 +00:00
queuepolyat(VBODY, shJelly, darkena(col, 0, 0xD0), PPR::MONSTER_BODY);
queuepolyat(VHEAD, shJelly, darkena(col, 0, 0xD0), PPR::MONSTER_HEAD);
queuepolyat(VHEAD, shEyes, 0xFF, PPR::MONSTER_HEAD);
2018-01-04 14:46:06 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moKrakenH) {
queuepoly(VFISH, shKrakenHead, darkena(col, 0, 0xD0));
queuepoly(VFISH, shKrakenEye, 0xFFFFFFC0);
queuepoly(VFISH, shKrakenEye2, 0xC0);
queuepoly(VFISH * Mirror, shKrakenEye, 0xFFFFFFC0);
queuepoly(VFISH * Mirror, shKrakenEye2, 0xC0);
}
else if(m == moKrakenT) {
queuepoly(VFISH, shSeaTentacle, darkena(col, 0, 0xD0));
}
else if(m == moCultist || m == moPyroCultist || m == moCultistLeader) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shPBody);
queuepoly(VBS, shPBody, darkena(col, 0, 0xC0));
if(!peace::on) queuepoly(VBS, shPSword, darkena(col, 2, 0xFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shHood, darkena(col, 1, 0xFF));
}
else if(m == moPirate) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shPBody);
queuepoly(VBS, shPBody, darkena(0x404040, 0, 0xFF));
queuepoly(VBS, shPirateHook, darkena(0xD0D0D0, 0, 0xFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, darkena(0xFFFF80, 0, 0xFF));
queuepoly(VHEAD1, shEyepatch, darkena(0, 0, 0xC0));
queuepoly(VHEAD2, shPirateHood, darkena(col, 0, 0xFF));
2017-07-10 18:47:38 +00:00
}
else if(m == moRatling || m == moRatlingAvenger) {
const transmatrix VBS = otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shYeti);
queuepoly(VLEG, shRatTail, darkena(col, 0, 0xFF));
queuepoly(VBODY * VBS, shYeti, darkena(col, 1, 0xFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shRatHead, darkena(col, 0, 0xFF));
float t = sintick(1000, where ? where->cpdist*M_PI : 0);
2017-07-10 18:47:38 +00:00
int eyecol = t > 0.92 ? 0xFF0000 : 0;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shWolf1, darkena(eyecol, 0, 0xFF));
queuepoly(VHEAD, shWolf2, darkena(eyecol, 0, 0xFF));
queuepoly(VHEAD, shWolf3, darkena(0x202020, 0, 0xFF));
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(m == moRatlingAvenger) {
queuepoly(VBODY1 * VBS, shRatCape2, 0x484848FF);
queuepoly(VHEAD, shRatCape1, 0x303030FF);
2016-08-26 09:58:03 +00:00
}
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moViking) {
const transmatrix VBS = otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shPBody);
queuepoly(VBODY * VBS, shPBody, darkena(0xE00000, 0, 0xFF));
if(!peace::on) queuepoly(VBODY * VBS, shPSword, darkena(0xD0D0D0, 0, 0xFF));
queuepoly(VBODY1 * VBS, shKnightCloak, darkena(0x404040, 0, 0xFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shVikingHelmet, darkena(0xC0C0C0, 0, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xFFFF80, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moOutlaw) {
const transmatrix VBS = otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
2017-03-23 10:53:57 +00:00
ShadowV(V, shPBody);
queuepoly(VBODY * VBS, shPBody, darkena(col, 0, 0xFF));
queuepoly(VBODY1 * VBS, shKnightCloak, darkena(col, 1, 0xFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shWestHat1, darkena(col, 2, 0XFF));
queuepoly(VHEAD, shWestHat2, darkena(col, 1, 0XFF));
queuepoly(VHEAD, shPFace, darkena(0xFFFF80, 0, 0xFF));
queuepoly(VBODY * VBS, shGunInHand, darkena(col, 1, 0XFF));
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moNecromancer) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
2017-03-23 10:53:57 +00:00
ShadowV(V, shPBody);
queuepoly(VBS, shPBody, 0xC00000C0);
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shHood, darkena(col, 1, 0xFF));
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moDraugr) {
const transmatrix VBS = VBODY * otherbodyparts(V, 0x483828D0, m, footphase);
queuepoly(VBS, shPBody, 0x483828D0);
queuepoly(VBS, shPSword, 0xFFFFD0A0);
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPHead, 0x483828D0);
// queuepoly(V, shSkull, 0xC06020D0);
//queuepoly(V, shSkullEyes, 0x000000D0);
// queuepoly(V, shWightCloak, 0xC0A080A0);
int b = where ? where->cpdist : 0;
b--;
if(b < 0) b = 0;
if(b > 6) b = 6;
queuepoly(VHEAD1, shWightCloak, 0x605040A0 + 0x10101000 * b);
2017-03-23 10:53:57 +00:00
}
2017-09-30 09:46:41 +00:00
else if(m == moVoidBeast) {
const transmatrix VBS = VBODY * otherbodyparts(V, 0x080808D0, m, footphase);
queuepoly(VBS, shPBody, 0x080808D0);
2017-09-30 09:46:41 +00:00
queuepoly(VHEAD, shPHead, 0x080808D0);
queuepoly(VHEAD, shWightCloak, 0xFF0000A0);
}
2017-07-10 18:47:38 +00:00
else if(m == moGoblin) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shYeti);
queuepoly(VBS, shYeti, darkena(col, 0, 0xC0));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shArmor, darkena(col, 1, 0XFF));
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moLancer || m == moFlailer || m == moMiner) {
transmatrix V2 = V;
if(m == moLancer)
V2 = V * spin((where && where->type == 6) ? -M_PI/3 : -M_PI/2 );
transmatrix Vh = mmscale(V2, geom3::HEAD);
transmatrix Vb = mmscale(V2, geom3::BODY);
Vb = Vb * otherbodyparts(V2, darkena(col, 1, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V2, shPBody);
queuepoly(Vb, shPBody, darkena(col, 0, 0xC0));
queuepoly(Vh, m == moFlailer ? shArmor : shHood, darkena(col, 1, 0XFF));
if(m == moMiner)
queuepoly(Vb, shPickAxe, darkena(0xC0C0C0, 0, 0XFF));
if(m == moLancer)
queuepoly(Vb, shPike, darkena(col, 0, 0XFF));
if(m == moFlailer) {
queuepoly(Vb, shFlailBall, darkena(col, 0, 0XFF));
queuepoly(Vb, shFlailChain, darkena(col, 1, 0XFF));
queuepoly(Vb, shFlailTrunk, darkena(col, 0, 0XFF));
}
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moTroll) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shYeti);
queuepoly(VBS, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD1, shPHead, darkena(col, 1, 0XFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, darkena(col, 2, 0XFF));
}
else if(m == moFjordTroll || m == moForestTroll || m == moStormTroll) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shYeti);
queuepoly(VBS, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD1, shPHead, darkena(col, 1, 0XFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, darkena(col, 2, 0XFF));
}
else if(m == moDarkTroll) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shYeti);
queuepoly(VBS, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD1, shPHead, darkena(col, 1, 0XFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, 0xFFFFFF80);
}
else if(m == moRedTroll) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shYeti);
queuepoly(VBS, shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD1, shPHead, darkena(0xFF8000, 0, 0XFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, 0xFFFFFF80);
}
else if(m == moEarthElemental) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, shWaterElemental);
queuepoly(VBS, shWaterElemental, darkena(col, 0, 0xC0));
queuepoly(VHEAD1, shFemaleHair, darkena(col, 0, 0XFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, 0xF0000080);
}
else if(m == moWaterElemental) {
const transmatrix VBS = VBODY * otherbodyparts(V, watercolor(50), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shWaterElemental);
queuepoly(VBS, shWaterElemental, watercolor(0));
queuepoly(VHEAD1, shFemaleHair, watercolor(100));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, watercolor(200));
}
else if(m == moFireElemental) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(firecolor(50), 0, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shWaterElemental);
queuepoly(VBS, shWaterElemental, darkena(firecolor(0), 0, 0xFF));
queuepoly(VHEAD1, shFemaleHair, darkena(firecolor(100), 0, 0xFF));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, darkena(firecolor(200), 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moAirElemental) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0x40), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shWaterElemental);
queuepoly(VBS, shWaterElemental, darkena(col, 0, 0x80));
queuepoly(VHEAD1, shFemaleHair, darkena(col, 0, 0x80));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shPFace, darkena(col, 0, 0x80));
}
else if((xch == 'd' || xch == 'D') && m != moDragonHead && m != moDragonTail) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
queuepoly(VBS, shPBody, darkena(col, 1, 0xC0));
2017-07-10 18:47:38 +00:00
ShadowV(V, shPBody);
int acol = col;
if(xch == 'D') acol = 0xD0D0D0;
queuepoly(VHEAD, shDemon, darkena(acol, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
2017-12-30 14:12:15 +00:00
else if(isMagneticPole(m)) {
if(m == moNorthPole)
2018-08-28 12:27:23 +00:00
queuepolyat(VBODY * spin(M_PI), shTentacle, 0x000000C0, PPR::TENTACLE1);
queuepolyat(VBODY, shDisk, darkena(col, 0, 0xFF), PPR::MONSTER_BODY);
2017-12-30 14:12:15 +00:00
}
2018-10-25 00:43:14 +00:00
else if(isMetalBeast(m) || m == moBrownBug) {
2017-07-10 18:47:38 +00:00
ShadowV(V, shTrylobite);
if(!mmspatial)
queuepoly(VABODY, shTrylobite, darkena(col, 1, 0xC0));
2017-03-23 10:53:57 +00:00
else {
2017-07-10 18:47:38 +00:00
queuepoly(VABODY, shTrylobiteBody, darkena(col, 1, 0xFF));
animallegs(VALEGS, moMetalBeast, darkena(col, 1, 0xFF), footphase);
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
int acol = col;
queuepoly(VAHEAD, shTrylobiteHead, darkena(acol, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
2017-09-30 09:46:41 +00:00
else if(m == moEvilGolem || m == moIceGolem) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 2, 0xC0), m, footphase);
2017-07-10 18:47:38 +00:00
ShadowV(V, shPBody);
queuepoly(VBS, shPBody, darkena(col, 0, 0XC0));
2017-07-10 18:47:38 +00:00
queuepoly(VHEAD, shGolemhead, darkena(col, 1, 0XFF));
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
else if(isWitch(m)) {
const transmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
2017-07-10 18:47:38 +00:00
int cc = 0xFF;
if(m == moWitchGhost) cc = 0x85 + 120 * sintick(160);
2017-07-10 18:47:38 +00:00
if(m == moWitchWinter && where) drawWinter(V, 0);
if(m == moWitchFlash && where) drawFlash(V);
if(m == moWitchSpeed && where) drawSpeed(V);
if(m == moWitchFire) col = firecolor(0);
ShadowV(V, shFemaleBody);
queuepoly(VBS, shFemaleBody, darkena(col, 0, cc));
2017-07-10 18:47:38 +00:00
// queuepoly(cV2, ct, shPSword, darkena(col, 0, 0XFF));
// queuepoly(V, shHood, darkena(col, 0, 0XC0));
if(m == moWitchFire) col = firecolor(100);
2019-02-27 00:15:40 +00:00
queuepoly(VHEAD1, shWitchHair, darkena(col, 1, cc));
2017-07-10 18:47:38 +00:00
if(m == moWitchFire) col = firecolor(200);
queuepoly(VHEAD, shPFace, darkena(col, 0, cc));
if(m == moWitchFire) col = firecolor(300);
queuepoly(VBS, shWitchDress, darkena(col, 1, 0XC0));
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
// just for the HUD glyphs...
else if(isIvy(m) || isMutantIvy(m) || m == moFriendlyIvy) {
queuepoly(V, shILeaf[0], darkena(col, 0, 0xFF));
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moWorm || m == moWormwait || m == moHexSnake) {
queuepoly(V, shWormHead, darkena(col, 0, 0xFF));
2018-08-28 12:27:23 +00:00
queuepolyat(V, shEyes, 0xFF, PPR::ONTENTACLE_EYES);
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moDragonHead) {
queuepoly(V, shDragonHead, darkena(col, 0, 0xFF));
2018-08-28 12:27:23 +00:00
queuepolyat(V, shEyes, 0xFF, PPR::ONTENTACLE_EYES);
2017-07-10 18:47:38 +00:00
int noscolor = 0xFF0000FF;
queuepoly(V, shDragonNostril, noscolor);
queuepoly(V * Mirror, shDragonNostril, noscolor);
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(m == moDragonTail) {
queuepoly(V, shDragonSegment, darkena(col, 0, 0xFF));
}
else if(m == moTentacle || m == moTentaclewait || m == moTentacleEscaping) {
queuepoly(V, shTentHead, darkena(col, 0, 0xFF));
2018-08-28 12:27:23 +00:00
ShadowV(V, shTentHead, PPR::GIANTSHADOW);
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
else return true;
return false;
#else
return true;
#endif
2017-07-10 18:47:38 +00:00
}
2016-01-02 10:09:13 +00:00
bool drawMonsterTypeDH(eMonster m, cell *where, const transmatrix& V, color_t col, bool dh, ld footphase) {
dynamicval<color_t> p(poly_outline, poly_outline);
2017-07-10 18:47:38 +00:00
if(dh) {
poly_outline = OUTLINE_DEAD;
darken++;
}
bool b = drawMonsterType(m,where,V,col, footphase);
if(dh) {
darken--;
}
return b;
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
transmatrix playerV;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
bool applyAnimation(cell *c, transmatrix& V, double& footphase, int layer) {
if(!animations[layer].count(c)) return false;
animation& a = animations[layer][c];
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
int td = ticks - a.ltick;
ld aspd = td / 1000.0 * exp(vid.mspeed);
2018-01-25 16:19:50 +00:00
ld R;
again:
if(a.attacking == 1)
R = hdist(tC0(a.attackat), tC0(a.wherenow));
else
R = hdist0(tC0(a.wherenow));
2017-07-10 18:47:38 +00:00
aspd *= (1+R+(shmup::on?1:0));
if(R < aspd || std::isnan(R) || std::isnan(aspd) || R > 10) {
2018-01-25 16:19:50 +00:00
if(a.attacking == 1) { a.attacking = 2; goto again; }
2017-07-10 18:47:38 +00:00
animations[layer].erase(c);
return false;
}
else {
2018-01-25 16:19:50 +00:00
if(a.attacking == 1)
a.wherenow = a.wherenow * rspintox(tC0(inverse(a.wherenow) * a.attackat));
else
a.wherenow = a.wherenow * rspintox(tC0(inverse(a.wherenow)));
2017-07-10 18:47:38 +00:00
a.wherenow = a.wherenow * xpush(aspd);
fixmatrix(a.wherenow);
2018-01-25 16:19:50 +00:00
a.footphase += a.attacking == 2 ? -aspd : aspd;
2017-07-10 18:47:38 +00:00
footphase = a.footphase;
V = V * a.wherenow;
2018-01-25 16:19:50 +00:00
if(a.attacking == 2) V = V * pispin;
2019-02-26 20:11:50 +00:00
if(DIM == 3) V = V * cspin(0, 2, M_PI/2);
2017-07-10 18:47:38 +00:00
a.ltick = ticks;
return true;
}
}
2016-01-02 10:09:13 +00:00
2017-07-22 23:33:27 +00:00
double chainAngle(cell *c, transmatrix& V, cell *c2, double dft, const transmatrix &Vwhere) {
if(!gmatrix0.count(c2)) return dft;
2017-07-10 18:47:38 +00:00
hyperpoint h = C0;
if(animations[LAYER_BIG].count(c2)) h = animations[LAYER_BIG][c2].wherenow * h;
h = inverse(V) * Vwhere * calc_relative_matrix(c2, c, C0) * h;
2017-07-10 18:47:38 +00:00
return atan2(h[1], h[0]);
}
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
// equivalent to V = V * spin(-chainAngle(c,V,c2,dft));
bool chainAnimation(cell *c, transmatrix& V, cell *c2, int i, ld bonus, const transmatrix &Vwhere, ld& length) {
2017-07-22 23:33:27 +00:00
if(!gmatrix0.count(c2)) {
V = V * ddspin(c,i,bonus);
2018-01-11 22:18:02 +00:00
length = cellgfxdist(c,i);
2017-07-10 18:47:38 +00:00
return false;
}
hyperpoint h = C0;
if(animations[LAYER_BIG].count(c2)) h = animations[LAYER_BIG][c2].wherenow * h;
h = inverse(V) * Vwhere * calc_relative_matrix(c2, c, C0) * h;
2018-01-11 22:18:02 +00:00
length = hdist0(h);
2017-07-10 18:47:38 +00:00
V = V * rspintox(h);
return true;
}
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
// push down the queue after q-th element, `down` absolute units down,
// based on cell c and transmatrix V
// do change the zoom factor? do change the priorities?
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
int cellcolor(cell *c) {
if(isPlayerOn(c) || isFriendly(c)) return OUTLINE_FRIEND;
if(noHighlight(c->monst)) return OUTLINE_NONE;
if(c->monst) return OUTLINE_ENEMY;
if(c->wall == waMirror) return c->land == laMirror ? OUTLINE_TREASURE : OUTLINE_ORB;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(c->item) {
int k = itemclass(c->item);
if(k == IC_TREASURE)
return OUTLINE_TREASURE;
else if(k == IC_ORB)
return OUTLINE_ORB;
else
return OUTLINE_OTHER;
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
return OUTLINE_NONE;
}
2017-03-23 10:53:57 +00:00
2018-01-11 22:18:02 +00:00
int taildist(cell *c) {
int s = 0;
while(s < 1000 && c->mondir != NODIR && isWorm(c->monst)) {
s++; c = c->move(c->mondir);
2018-01-11 22:18:02 +00:00
}
return s;
}
int last_wormsegment = -1;
vector<vector< function<void()> > > wormsegments;
2018-01-11 22:18:02 +00:00
void add_segment(int d, const function<void()>& s) {
2018-06-22 12:47:24 +00:00
if(isize(wormsegments) <= d) wormsegments.resize(d+1);
2018-01-11 22:18:02 +00:00
last_wormsegment = max(last_wormsegment, d);
wormsegments[d].push_back(s);
}
void drawWormSegments() {
for(int i=0; i<=last_wormsegment; i++) {
for(auto& f: wormsegments[i]) f();
wormsegments[i].clear();
}
last_wormsegment = -1;
}
2018-01-30 23:10:44 +00:00
bool dont_face_pc = false;
bool drawMonster(const transmatrix& Vparam, int ct, cell *c, color_t col) {
#if CAP_SHAPES
2015-08-08 13:57:52 +00:00
bool darkhistory = conformal::includeHistory && conformal::inkillhistory.count(c);
2017-07-10 18:47:38 +00:00
if(doHighlight())
poly_outline =
(isPlayerOn(c) || isFriendly(c)) ? OUTLINE_FRIEND :
noHighlight(c->monst) ? OUTLINE_NONE :
OUTLINE_ENEMY;
bool nospins = false, nospinb = false;
double footphaseb = 0, footphase = 0;
transmatrix Vs = Vparam; nospins = applyAnimation(c, Vs, footphase, LAYER_SMALL);
2017-07-22 23:33:27 +00:00
transmatrix Vb = Vparam; nospinb = applyAnimation(c, Vb, footphaseb, LAYER_BIG);
2017-07-10 18:47:38 +00:00
// nospin = true;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
eMonster m = c->monst;
if(isIvy(c) || isWorm(c) || isMutantIvy(c) || c->monst == moFriendlyIvy) {
if(isWorm(c) && DIM == 3) return false;
2017-07-10 18:47:38 +00:00
if((m == moHexSnake || m == moHexSnakeTail) && c->hitpoints == 2) {
int d = c->mondir;
if(d == NODIR)
forCellIdEx(c2, i, c)
if(among(c2->monst, moHexSnakeTail, moHexSnake) && c2->mondir == c->c.spin(i))
d = i;
if(d == NODIR) { d = hrand(c->type); createMov(c, d); }
2017-12-05 18:43:45 +00:00
int c1 = nestcolors[pattern_threecolor(c)];
int c2 = nestcolors[pattern_threecolor(c->move(d))];
2017-12-05 18:43:45 +00:00
col = (c1 + c2); // sum works because they are dark and should be brightened
}
2017-07-10 18:47:38 +00:00
if(isDragon(c->monst) && c->stuntime == 0) col = 0xFF6000;
transmatrix Vb0 = Vb;
2019-02-26 21:08:33 +00:00
if(c->mondir != NODIR && DIM == 3) {
queueline(tC0(Vparam), Vparam * tC0(calc_relative_matrix(c->move(c->mondir), c, C0)), (col << 8) + 0xFF, 0);
}
else if(c->mondir != NODIR) {
2017-07-10 18:47:38 +00:00
if(mmmon) {
2018-01-11 22:18:02 +00:00
ld length;
// cell *c2 = c->move(c->mondir);
2018-01-11 22:18:02 +00:00
if(nospinb)
chainAnimation(c, Vb, c->move(c->mondir), c->mondir, 0, Vparam, length);
2018-01-11 22:18:02 +00:00
else {
2017-07-10 18:47:38 +00:00
Vb = Vb * ddspin(c, c->mondir);
2018-01-11 22:18:02 +00:00
length = cellgfxdist(c, c->mondir);
}
if(mapeditor::drawUserShape(Vb, mapeditor::sgMonster, c->monst, (col << 8) + 0xFF, c))
return false;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(isIvy(c) || isMutantIvy(c) || c->monst == moFriendlyIvy)
queuepoly(Vb, shIBranch, (col << 8) + 0xFF);
2018-01-11 22:18:02 +00:00
/* else if(c->monst < moTentacle && wormstyle == 0) {
2018-08-28 12:27:23 +00:00
ShadowV(Vb, shTentacleX, PPR::GIANTSHADOW);
2017-07-10 18:47:38 +00:00
queuepoly(mmscale(Vb, geom3::ABODY), shTentacleX, 0xFF);
queuepoly(mmscale(Vb, geom3::ABODY), shTentacle, (col << 8) + 0xFF);
2018-01-11 22:18:02 +00:00
} */
// else if(c->monst < moTentacle) {
// }
2017-07-10 18:47:38 +00:00
else if(c->monst == moDragonHead || c->monst == moDragonTail) {
char part = dragon::bodypart(c, dragon::findhead(c));
if(part != '2') {
queuepoly(mmscale(Vb, geom3::ABODY), shDragonSegment, darkena(col, 0, 0xFF));
2018-08-28 12:27:23 +00:00
ShadowV(Vb, shDragonSegment, PPR::GIANTSHADOW);
2017-07-10 18:47:38 +00:00
}
}
else {
if(c->monst == moTentacleGhost) {
hyperpoint V0 = conformal::on ? tC0(Vs) : inverse(cwtV) * tC0(Vs);
hyperpoint V1 = spintox(V0) * V0;
Vs = cwtV * rspintox(V0) * rpushxto0(V1) * pispin;
drawMonsterType(moGhost, c, Vs, darkena(col, 0, 0xFF), footphase);
col = minf[moTentacletail].color;
}
2018-01-11 22:18:02 +00:00
/*
2017-07-10 18:47:38 +00:00
queuepoly(mmscale(Vb, geom3::ABODY), shTentacleX, 0xFFFFFFFF);
queuepoly(mmscale(Vb, geom3::ABODY), shTentacle, (col << 8) + 0xFF);
2018-08-28 12:27:23 +00:00
ShadowV(Vb, shTentacleX, PPR::GIANTSHADOW);
2018-01-11 22:18:02 +00:00
*/
bool hexsnake = c->monst == moHexSnake || c->monst == moHexSnakeTail;
bool thead = c->monst == moTentacle || c->monst == moTentaclewait || c->monst == moTentacleEscaping;
hpcshape& sh = hexsnake ? shWormSegment : shSmallWormSegment;
ld wav = hexsnake ? 0 :
c->monst < moTentacle ? 1/1.5 :
1;
color_t col0 = col;
2018-01-11 22:18:02 +00:00
if(c->monst == moWorm || c->monst == moWormwait)
col0 = minf[moWormtail].color;
else if(thead)
col0 = minf[moTentacletail].color;
add_segment(taildist(c), [=] () {
for(int i=11; i>=0; i--) {
if(i < 3 && (c->monst == moTentacle || c->monst == moTentaclewait)) continue;
transmatrix Vbx = Vb * spin(sin(M_PI * i / 6.) * wav / (i+.1)) * xpush(length * (i) / 12.0);
// transmatrix Vbx2 = Vnext * xpush(length2 * i / 6.0);
// Vbx = Vbx * rspintox(inverse(Vbx) * Vbx2 * C0) * pispin;
2018-08-28 12:27:23 +00:00
ShadowV(Vbx, sh, PPR::GIANTSHADOW);
2018-01-11 22:18:02 +00:00
queuepoly(mmscale(Vbx, geom3::ABODY), sh, (col0 << 8) + 0xFF);
}
});
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
else {
int hdir = displayspin(c, c->mondir);
color_t col = darkena(0x606020, 0, 0xFF);
2017-07-10 18:47:38 +00:00
for(int u=-1; u<=1; u++)
queueline(Vparam*xspinpush0(hdir+M_PI/2, u*crossf/5), Vparam*xspinpush(hdir, crossf)*xspinpush0(hdir+M_PI/2, u*crossf/5), col, 2 + vid.linequality);
2017-07-10 18:47:38 +00:00
}
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(mmmon) {
if(isIvy(c) || isMutantIvy(c) || c->monst == moFriendlyIvy) {
2019-02-26 21:08:33 +00:00
if(DIM == 3) {
hyperpoint V0 = tC0(Vb);
transmatrix Vs = rspintox(V0) * xpush(hdist0(V0)) * cspin(0, 2, -M_PI/2);
queuepoly(Vs, shILeaf[1], darkena(col, 0, 0xFF));
}
else {
queuepoly(mmscale(Vb, geom3::ABODY), shILeaf[ctof(c)], darkena(col, 0, 0xFF));
ShadowV(Vb, shILeaf[ctof(c)], PPR::GIANTSHADOW);
}
2017-07-10 18:47:38 +00:00
}
else if(m == moWorm || m == moWormwait || m == moHexSnake) {
Vb = Vb * pispin;
transmatrix Vbh = mmscale(Vb, geom3::AHEAD);
queuepoly(Vbh, shWormHead, darkena(col, 0, 0xFF));
2018-08-28 12:27:23 +00:00
queuepolyat(Vbh, shEyes, 0xFF, PPR::ONTENTACLE_EYES);
ShadowV(Vb, shWormHead, PPR::GIANTSHADOW);
2017-07-10 18:47:38 +00:00
}
else if(m == moDragonHead) {
transmatrix Vbh = mmscale(Vb, geom3::AHEAD);
2018-08-28 12:27:23 +00:00
ShadowV(Vb, shDragonHead, PPR::GIANTSHADOW);
2017-07-10 18:47:38 +00:00
queuepoly(Vbh, shDragonHead, darkena(col, c->hitpoints?0:1, 0xFF));
2018-08-28 12:27:23 +00:00
queuepolyat(Vbh/* * pispin */, shEyes, 0xFF, PPR::ONTENTACLE_EYES);
2017-07-10 18:47:38 +00:00
int noscolor = (c->hitpoints == 1 && c->stuntime ==1) ? 0xFF0000FF : 0xFF;
queuepoly(Vbh, shDragonNostril, noscolor);
queuepoly(Vbh * Mirror, shDragonNostril, noscolor);
}
else if(m == moTentacle || m == moTentaclewait || m == moTentacleEscaping) {
Vb = Vb * pispin;
transmatrix Vbh = mmscale(Vb, geom3::AHEAD);
queuepoly(Vbh, shTentHead, darkena(col, 0, 0xFF));
2018-08-28 12:27:23 +00:00
ShadowV(Vb, shTentHead, PPR::GIANTSHADOW);
2017-07-10 18:47:38 +00:00
}
else if(m == moDragonTail) {
cell *c2 = NULL;
for(int i=0; i<c->type; i++)
if(c->move(i) && isDragon(c->move(i)->monst) && c->move(i)->mondir == c->c.spin(i))
c2 = c->move(i);
2017-07-10 18:47:38 +00:00
int nd = neighborId(c, c2);
char part = dragon::bodypart(c, dragon::findhead(c));
if(part == 't') {
if(nospinb) {
2018-01-11 22:18:02 +00:00
ld length;
chainAnimation(c, Vb, c2, nd, 0, Vparam, length);
2017-07-10 18:47:38 +00:00
Vb = Vb * pispin;
}
else {
Vb = Vb0 * ddspin(c, nd, M_PI);
2017-07-10 18:47:38 +00:00
}
transmatrix Vbb = mmscale(Vb, geom3::ABODY);
queuepoly(Vbb, shDragonTail, darkena(col, c->hitpoints?0:1, 0xFF));
2018-08-28 12:27:23 +00:00
ShadowV(Vb, shDragonTail, PPR::GIANTSHADOW);
2017-07-10 18:47:38 +00:00
}
else if(true) {
if(nospinb) {
2018-01-11 22:18:02 +00:00
ld length;
chainAnimation(c, Vb, c2, nd, 0, Vparam, length);
2017-07-10 18:47:38 +00:00
Vb = Vb * pispin;
double ang = chainAngle(c, Vb, c->move(c->mondir), displayspin(c, c->mondir) - displayspin(c, nd), Vparam);
2017-07-10 18:47:38 +00:00
ang /= 2;
Vb = Vb * spin(M_PI-ang);
}
else {
ld hdir0 = displayspin(c, nd) + M_PI;
ld hdir1 = displayspin(c, c->mondir);
while(hdir1 > hdir0 + M_PI) hdir1 -= 2*M_PI;
while(hdir1 < hdir0 - M_PI) hdir1 += 2*M_PI;
Vb = Vb0 * spin((hdir0 + hdir1)/2 + M_PI);
2017-07-10 18:47:38 +00:00
}
transmatrix Vbb = mmscale(Vb, geom3::ABODY);
if(part == 'l' || part == '2') {
queuepoly(Vbb, shDragonLegs, darkena(col, c->hitpoints?0:1, 0xFF));
}
queuepoly(Vbb, shDragonWings, darkena(col, c->hitpoints?0:1, 0xFF));
}
}
2017-12-05 18:43:45 +00:00
else {
2018-01-11 22:18:02 +00:00
if(c->monst == moTentacletail && c->mondir == NODIR) {
queuepoly(Vb, shWormSegment, darkena(col, 0, 0xFF));
}
else if(c->mondir == NODIR) {
bool hexsnake = c->monst == moHexSnake || c->monst == moHexSnakeTail;
cell *c2 = NULL;
for(int i=0; i<c->type; i++)
if(c->move(i) && isWorm(c->move(i)->monst) && c->move(i)->mondir == c->c.spin(i))
c2 = c->move(i);
2018-01-11 22:18:02 +00:00
int nd = neighborId(c, c2);
if(nospinb) {
ld length;
chainAnimation(c, Vb, c2, nd, 0, Vparam, length);
Vb = Vb * pispin;
}
else {
Vb = Vb0 * ddspin(c, nd, M_PI);
2018-01-11 22:18:02 +00:00
}
transmatrix Vbb = mmscale(Vb, geom3::ABODY) * pispin;
hpcshape& sh = hexsnake ? shWormTail : shSmallWormTail;
queuepoly(Vbb, sh, darkena(col, 0, 0xFF));
2018-08-28 12:27:23 +00:00
ShadowV(Vb, sh, PPR::GIANTSHADOW);
2018-01-11 22:18:02 +00:00
}
2017-12-05 18:43:45 +00:00
}
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(!mmmon) return true;
}
else if(isMimic(c)) {
2017-07-22 23:33:27 +00:00
int xdir = 0, copies = 1;
if(c->wall == waMirrorWall) {
xdir = mirror::mirrordir(c);
copies = 2;
if(xdir == -1) copies = 6, xdir = 0;
2017-07-10 18:47:38 +00:00
}
for(auto& m: mirror::mirrors) if(c == m.second.at)
2017-07-22 23:33:27 +00:00
for(int d=0; d<copies; d++) {
multi::cpid = m.first;
auto cw = m.second;
if(d&1) cw = cw.mirrorat(xdir);
2018-03-24 11:59:01 +00:00
if(d>=2) cw += 2;
if(d>=4) cw += 2;
2017-07-22 23:33:27 +00:00
transmatrix Vs = Vparam;
bool mirr = cw.mirrored;
2018-08-21 13:37:59 +00:00
transmatrix T = Id;
nospins = applyAnimation(cwt.at, T, footphase, LAYER_SMALL);
if(nospins)
Vs = Vs * ddspin(c, cw.spin, 0) * iddspin(cwt.at, cwt.spin, 0) * T;
else
Vs = Vs * ddspin(c, cw.spin, 0);
2017-07-22 23:33:27 +00:00
if(mirr) Vs = Vs * Mirror;
if(inmirrorcount&1) mirr = !mirr;
col = mirrorcolor(geometry == gElliptic ? det(Vs) < 0 : mirr);
2018-08-21 13:37:59 +00:00
if(!mouseout() && !nospins) {
hyperpoint P2 = Vs * inverse(cwtV) * mouseh;
queuechr(P2, 10, 'x', 0xFF00);
}
2017-07-22 23:33:27 +00:00
if(!nospins && flipplayer) Vs = Vs * pispin;
if(mmmon) {
drawMonsterType(moMimic, c, Vs, col, footphase);
drawPlayerEffects(Vs, c, false);
}
2017-07-10 18:47:38 +00:00
}
return !mmmon;
}
else if(c->monst && !mmmon) return true;
// illusions face randomly
else if(c->monst == moIllusion) {
multi::cpid = 0;
drawMonsterType(c->monst, c, Vs, col, footphase);
drawPlayerEffects(Vs, c, false);
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
// wolves face the heat
else if(c->monst == moWolf && c->cpdist > 1) {
if(!nospins) {
int d = 0;
double bheat = -999;
for(int i=0; i<c->type; i++) if(c->move(i) && HEAT(c->move(i)) > bheat) {
bheat = HEAT(c->move(i));
2017-07-10 18:47:38 +00:00
d = i;
2015-08-08 13:57:52 +00:00
}
2019-02-26 20:11:50 +00:00
Vs = Vs * ddspin(c, d, FACE3);
2017-07-10 18:47:38 +00:00
}
return drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase);
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
else if(c->monst == moKrakenT) {
if(c->hitpoints == 0) col = 0x404040;
if(nospinb) {
2018-01-11 22:18:02 +00:00
ld length;
chainAnimation(c, Vb, c->move(c->mondir), c->mondir, 0, Vparam, length);
2017-07-10 18:47:38 +00:00
Vb = Vb * pispin;
Vb = Vb * xpush(tentacle_length - cellgfxdist(c, c->mondir));
}
else if(NONSTDVAR) {
transmatrix T = calc_relative_matrix(c->move(c->mondir), c, c->mondir);
Vb = Vb * T * rspintox(tC0(inverse(T))) * xpush(tentacle_length);
2017-07-10 18:47:38 +00:00
}
2018-04-03 21:39:18 +00:00
else {
Vb = Vb * ddspin(c, c->mondir, M_PI);
Vb = Vb * xpush(tentacle_length - cellgfxdist(c, c->mondir));
2018-04-03 21:39:18 +00:00
}
// if(ctof(c) && !masterless) Vb = Vb * xpush(hexhexdist - hcrossf);
// return (!BITRUNCATED) ? tessf * gp::scale : (c->type == 6 && (i&1)) ? hexhexdist : crossf;
2017-07-10 18:47:38 +00:00
return drawMonsterTypeDH(m, c, Vb, col, darkhistory, footphase);
}
2015-08-08 13:57:52 +00:00
// golems, knights, and hyperbugs don't face the player (mondir-controlled)
// also whatever in the lineview mode, and whatever in the quotient geometry
else if(isFriendly(c) || isBug(c) || (c->monst && conformal::on) || c->monst == moKrakenH || (isBull(c->monst) && c->mondir != NODIR) || c->monst == moButterfly || isMagneticPole(c->monst) ||
isSwitch(c->monst) || c->monst == moPair || (c->monst && (quotient || euwrap || dont_face_pc))) {
if(c->monst == moKrakenH) Vs = Vb, nospins = nospinb;
if(!nospins && c->mondir < c->type) Vs = Vs * ddspin(c, c->mondir, M_PI);
if(c->monst == moPair) Vs = Vs * xpush(-.12);
if(isFriendly(c)) drawPlayerEffects(Vs, c, false);
return drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase);
}
2017-07-10 18:47:38 +00:00
else if(c->monst) {
// other monsters face the player
if(!nospins) {
2019-02-26 20:11:50 +00:00
if(DIM == 2) {
hyperpoint V0 = inverse(cwtV) * tC0(Vs);
hyperpoint V1 = spintox(V0) * V0;
Vs = cwtV * rspintox(V0) * rpushxto0(V1) * pispin;
}
else {
hyperpoint V0 = inverse(cwtV) * tC0(Vs);
Vs = cwtV * rspintox(V0) * xpush(hdist0(V0)) * cspin(0, 2, -M_PI/2);
// cwtV * rgpushxto0(inverse(cwtV) * tC0(Vs));
}
2017-10-15 22:15:54 +00:00
if(c->monst == moHunterChanging)
2019-03-04 19:10:49 +00:00
Vs = Vs * cspin(DIM-2, DIM-1, M_PI);
2017-07-10 18:47:38 +00:00
}
if(c->monst == moShadow)
multi::cpid = c->hitpoints;
return drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase);
}
2017-08-06 12:50:16 +00:00
for(int i=0; i<numplayers(); i++) if(c == playerpos(i) && !shmup::on && mapeditor::drawplayer &&
!(hardcore && !canmove)) {
2017-07-10 18:47:38 +00:00
if(!nospins) {
Vs = playerV;
if(multi::players > 1 ? multi::flipped[i] : flipplayer) Vs = Vs * pispin;
}
else {
bool mirr = multi::players > 1 ? multi::player[i].mirrored : cwt.mirrored;
if(mirr) Vs = Vs * Mirror;
}
2017-07-10 18:47:38 +00:00
shmup::cpid = i;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
drawPlayerEffects(Vs, c, true);
if(!mmmon) return true;
if(hide_player()) {
}
else if(isWorm(m)) {
2017-07-10 18:47:38 +00:00
ld depth = geom3::factor_to_lev(wormhead(c) == c ? geom3::AHEAD : geom3::ABODY);
footphase = 0;
int q = ptds.size();
drawMonsterType(moPlayer, c, Vs, col, footphase);
pushdown(c, q, Vs, -depth, true, false);
}
else if(mmmon)
drawMonsterType(moPlayer, c, Vs, col, footphase);
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
return false;
2019-02-17 17:41:40 +00:00
#else
return false;
#endif
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
double downspin;
cell *straightDownSeek;
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
#define AURA 180
2017-03-23 10:53:57 +00:00
array<array<int,4>,AURA+1> aurac;
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
bool haveaura() {
2017-11-13 00:28:56 +00:00
if(!(vid.aurastr>0 && !svg::in && (auraNOGL || vid.usingGL))) return false;
2019-02-22 19:58:40 +00:00
if(DIM == 3) return false;
2018-03-26 17:06:47 +00:00
if(sphere && mdAzimuthalEqui()) return true;
if(among(pmodel, mdJoukowsky, mdJoukowskyInverted) && hyperbolic && conformal::model_transition < 1)
return true;
2017-11-13 00:28:56 +00:00
return pmodel == mdDisk && (!sphere || vid.alpha > 10) && !euclid;
2017-07-10 18:47:38 +00:00
}
2017-08-06 12:50:16 +00:00
vector<pair<int, int> > auraspecials;
int auramemo;
2017-07-10 18:47:38 +00:00
void clearaura() {
if(!haveaura()) return;
for(int a=0; a<AURA; a++) for(int b=0; b<4; b++)
aurac[a][b] = 0;
2017-08-06 12:50:16 +00:00
auraspecials.clear();
auramemo = 128 * 128 / vid.aurastr;
}
void apply_joukowsky_aura(hyperpoint& h) {
bool joukowsky = among(pmodel, mdJoukowskyInverted, mdJoukowsky) && hyperbolic && conformal::model_transition < 1;
if(joukowsky) {
hyperpoint ret;
applymodel(h, ret);
h = ret;
}
}
void addauraspecial(hyperpoint h, color_t col, int dir) {
2017-08-06 12:50:16 +00:00
if(!haveaura()) return;
apply_joukowsky_aura(h);
2018-12-22 21:39:33 +00:00
int r = int(2*AURA + dir + atan2(h[1], h[0]) * AURA / 2 / M_PI) % AURA;
2017-08-06 12:50:16 +00:00
auraspecials.emplace_back(r, col);
2017-07-10 18:47:38 +00:00
}
2016-01-02 10:09:13 +00:00
void addaura(hyperpoint h, color_t col, int fd) {
2017-07-10 18:47:38 +00:00
if(!haveaura()) return;
apply_joukowsky_aura(h);
int r = int(2*AURA + atan2(h[1], h[0]) * AURA / 2 / M_PI) % AURA;
2017-08-06 12:50:16 +00:00
aurac[r][3] += auramemo << fd;
2017-07-10 18:47:38 +00:00
col = darkened(col);
aurac[r][0] += (col>>16)&255;
aurac[r][1] += (col>>8)&255;
aurac[r][2] += (col>>0)&255;
}
void sumaura(int v) {
int auc[AURA];
for(int t=0; t<AURA; t++) auc[t] = aurac[t][v];
int val = 0;
if(vid.aurasmoothen < 1) vid.aurasmoothen = 1;
if(vid.aurasmoothen > AURA) vid.aurasmoothen = AURA;
int SMO = vid.aurasmoothen;
for(int t=0; t<SMO; t++) val += auc[t];
for(int t=0; t<AURA; t++) {
int tt = (t + SMO/2) % AURA;
aurac[tt][v] = val;
val -= auc[t];
val += auc[(t+SMO) % AURA];
}
aurac[AURA][v] = aurac[0][v];
}
2018-02-11 18:08:17 +00:00
vector<glhr::colored_vertex> auravertices;
2018-02-10 17:21:19 +00:00
2017-07-10 18:47:38 +00:00
void drawaura() {
if(!haveaura()) return;
if(vid.stereo_mode) return;
double rad = current_display->radius;
2018-03-26 17:06:47 +00:00
if(sphere && !mdAzimuthalEqui()) rad /= sqrt(vid.alpha*vid.alpha - 1);
2017-07-10 18:47:38 +00:00
for(int v=0; v<4; v++) sumaura(v);
2017-08-06 12:50:16 +00:00
for(auto& p: auraspecials) {
int r = p.first;
aurac[r][3] = auramemo;
for(int k=0; k<3; k++) aurac[r][k] = (p.second >> (16-8*k)) & 255;
2017-08-06 12:50:16 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-22 23:33:27 +00:00
#if CAP_SDL || CAP_GL
2017-07-10 18:47:38 +00:00
double bak[3];
bak[0] = ((backcolor>>16)&255)/255.;
bak[1] = ((backcolor>>8)&255)/255.;
bak[2] = ((backcolor>>0)&255)/255.;
2017-07-22 23:33:27 +00:00
#endif
2017-07-10 18:47:38 +00:00
2017-07-22 23:33:27 +00:00
#if CAP_SDL
2017-07-10 18:47:38 +00:00
if(!vid.usingGL) {
SDL_LockSurface(s);
for(int y=0; y<vid.yres; y++)
for(int x=0; x<vid.xres; x++) {
ld hx = (x * 1. - current_display->xcenter) / rad;
ld hy = (y * 1. - current_display->ycenter) / rad / vid.stretch;
2017-07-10 18:47:38 +00:00
if(vid.camera_angle) camrotate(hx, hy);
ld fac = sqrt(hx*hx+hy*hy);
if(fac < 1) continue;
ld dd = log((fac - .99999) / .00001);
ld cmul = 1 - dd/10.;
if(cmul>1) cmul=1;
if(cmul<0) cmul=0;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
ld alpha = AURA * atan2(hx,hy) / (2 * M_PI);
if(alpha<0) alpha += AURA;
if(alpha >= AURA) alpha -= AURA;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
int rm = int(alpha);
double fr = alpha-rm;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(rm<0 || rm >= AURA) continue;
color_t& p = qpixel(s, x, y);
2017-07-10 18:47:38 +00:00
for(int c=0; c<3; c++) {
double c1 = aurac[rm][2-c] / (aurac[rm][3]+.1);
double c2 = aurac[rm+1][2-c] / (aurac[rm+1][3]+.1);
const ld one = 1;
part(p, c) = int(255 * min(one, bak[2-c] + cmul * ((c1 + fr * (c2-c1) - bak[2-c]))));
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
}
SDL_UnlockSurface(s);
return;
}
#endif
2016-08-26 09:58:03 +00:00
2017-07-22 23:33:27 +00:00
#if CAP_GL
2017-07-10 18:47:38 +00:00
setcameraangle(true);
float cx[AURA+1][11][5];
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
double facs[11] = {1, 1.01, 1.02, 1.04, 1.08, 1.70, 1.95, 1.5, 2, 6, 10};
double cmul[11] = {1, .8, .7, .6, .5, .16, .12, .08, .07, .06, 0};
double d2[11] = {0, 2, 4, 6.5, 7, 7.5, 8, 8.5, 9, 9.5, 10};
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
for(int d=0; d<11; d++) {
double dd = d2[d];
cmul[d] = (1- dd/10.);
facs[d] = .99999 + .00001 * exp(dd);
}
facs[10] = 10;
2018-02-08 23:29:20 +00:00
cmul[1] = cmul[0];
bool inversion = vid.alpha <= -1 || pmodel == mdJoukowsky;
bool joukowsky = among(pmodel, mdJoukowskyInverted, mdJoukowsky) && hyperbolic && conformal::model_transition < 1;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
for(int r=0; r<=AURA; r++) for(int z=0; z<11; z++) {
float rr = (M_PI * 2 * r) / AURA;
float rad0 = inversion ? rad / facs[z] : rad * facs[z];
2017-07-10 18:47:38 +00:00
int rm = r % AURA;
ld c = cos(rr);
ld s = sin(rr);
if(joukowsky) {
ld c1 = c, s1 = s;
2018-10-28 01:12:04 +00:00
if(inversion)
conformal::apply_orientation(s1, c1);
else
conformal::apply_orientation(c1, s1);
ld& mt = conformal::model_transition;
ld mt2 = 1 - mt;
ld m = sqrt(c1*c1 + s1*s1 / mt2 / mt2);
m *= 2;
if(inversion) rad0 /= m;
else rad0 *= m;
}
cx[r][z][0] = rad0 * c;
cx[r][z][1] = rad0 * s * vid.stretch;
2017-07-10 18:47:38 +00:00
for(int u=0; u<3; u++)
cx[r][z][u+2] = bak[u] + (aurac[rm][u] / (aurac[rm][3]+.1) - bak[u]) * cmul[z];
}
2018-02-11 18:08:17 +00:00
auravertices.clear();
2017-07-10 18:47:38 +00:00
for(int r=0; r<AURA; r++) for(int z=0;z<10;z++) {
2018-02-11 18:08:17 +00:00
for(int c=0; c<6; c++) {
int br = (c == 1 || c == 3 || c == 5) ? r+1 : r;
int bz = (c == 2 || c == 4 || c == 5) ? z+1 : z;
auravertices.emplace_back(
cx[br][bz][0], cx[br][bz][1], cx[br][bz][2], cx[br][bz][3], cx[br][bz][4]
);
2017-07-10 18:47:38 +00:00
}
}
2018-09-04 21:27:27 +00:00
glflush();
2018-12-17 10:33:52 +00:00
current_display->set_projection(0, false);
2019-01-10 02:06:36 +00:00
setcameraangle(true);
2018-11-17 16:59:57 +00:00
glhr::switch_mode(glhr::gmVarColored, glhr::shader_projection::standard);
glhr::id_modelview();
2018-02-11 18:08:17 +00:00
glhr::prepare(auravertices);
glhr::set_depthtest(false);
2018-06-22 12:47:24 +00:00
glDrawArrays(GL_TRIANGLES, 0, isize(auravertices));
2018-02-11 18:08:17 +00:00
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
setcameraangle(false);
#endif
}
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
// int fnt[100][7];
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
bool bugsNearby(cell *c, int dist = 2) {
if(!(havewhat&HF_BUG)) return false;
if(isBug(c)) return true;
if(dist) for(int t=0; t<c->type; t++) if(c->move(t) && bugsNearby(c->move(t), dist-1)) return true;
2017-07-10 18:47:38 +00:00
return false;
}
2017-03-23 10:53:57 +00:00
2018-11-08 15:21:33 +00:00
colortable minecolors = {
2017-07-10 18:47:38 +00:00
0xFFFFFF, 0xF0, 0xF060, 0xF00000,
0x60, 0x600000, 0x00C0C0, 0x000000
};
2017-03-23 10:53:57 +00:00
2018-11-08 15:21:33 +00:00
colortable distcolors = {
2017-07-10 18:47:38 +00:00
0xFFFFFF, 0xF0, 0xF060, 0xF00000,
0xA0A000, 0xA000A0, 0x00A0A0, 0xFFD500
};
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
const char* minetexts[8] = {
"No mines next to you.",
"A mine is next to you!",
"Two mines next to you!",
"Three mines next to you!",
"Four mines next to you!",
"Five mines next to you!",
"Six mines next to you!",
"Seven mines next to you!"
};
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
int countMinesAround(cell *c) {
int mines = 0;
for(int i=0; i<c->type; i++)
if(c->move(i) && c->move(i)->wall == waMineMine)
2017-07-10 18:47:38 +00:00
mines++;
return mines;
}
2017-03-23 10:53:57 +00:00
transmatrix applyPatterndir(cell *c, const patterns::patterninfo& si) {
if(NONSTDVAR || binarytiling) return Id;
transmatrix V = ddspin(c, si.dir, M_PI);
2018-08-20 00:03:43 +00:00
if(si.reflect) V = V * Mirror;
return V * iddspin(c, 0, M_PI);
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
transmatrix applyDowndir(cell *c, const cellfunction& cf) {
return ddspin(c, patterns::downdir(c, cf), M_PI);
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
#if CAP_SHAPES
void set_towerfloor(cell *c, const cellfunction& cf = coastvalEdge) {
if(weirdhyperbolic || sphere) {
2018-05-07 18:13:56 +00:00
set_floor(shFloor);
return;
}
2017-07-10 18:47:38 +00:00
int j = -1;
2015-08-08 13:57:52 +00:00
if(masterless) j = 10;
else if(cf(c) > 1) {
2017-07-10 18:47:38 +00:00
int i = towerval(c, cf);
if(i == 4) j = 0;
if(i == 5) j = 1;
if(i == 6) j = 2;
if(i == 8) j = 3;
if(i == 9) j = 4;
if(i == 10) j = 5;
if(i == 13) j = 6;
if(PURE) {
2017-07-10 18:47:38 +00:00
if(i == 7) j = 7;
if(i == 11) j = 8;
if(i == 15) j = 9;
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(j >= 0)
2018-05-07 18:13:56 +00:00
set_floor(applyDowndir(c, cf), shTower[j]);
2017-07-10 18:47:38 +00:00
else if(c->wall != waLadder)
2018-05-07 18:13:56 +00:00
set_floor(shMFloor);
2017-07-10 18:47:38 +00:00
}
2016-01-02 10:09:13 +00:00
2018-05-07 18:13:56 +00:00
void set_zebrafloor(cell *c) {
if(masterless) { set_floor(shTower[10]); return; }
if(weirdhyperbolic) {
2018-05-07 18:13:56 +00:00
set_floor(shFloor); return;
}
auto si = patterns::getpatterninfo(c, patterns::PAT_ZEBRA, patterns::SPF_SYM0123);
2017-07-10 18:47:38 +00:00
int j;
if(PURE) j = 4;
else if(si.id >=4 && si.id < 16) j = 2;
else if(si.id >= 16 && si.id < 28) j = 1;
else if(si.id >= 28 && si.id < 40) j = 3;
2017-07-10 18:47:38 +00:00
else j = 0;
2018-05-07 18:13:56 +00:00
set_floor(applyPatterndir(c, si), shZebra[j]);
2017-07-10 18:47:38 +00:00
}
2016-01-02 10:09:13 +00:00
2018-05-07 18:13:56 +00:00
void set_maywarp_floor(cell *c);
2016-01-02 10:09:13 +00:00
int chasmg;
void set_reptile_floor(cell *c, const transmatrix& V, color_t col, bool nodetails = false) {
2016-08-26 09:58:03 +00:00
2017-12-27 21:10:50 +00:00
auto si =
euclid6 ?
patterns::getpatterninfo(c, patterns::PAT_COLORING, patterns::SPF_CHANGEROT)
2017-12-27 21:10:50 +00:00
:
patterns::getpatterninfo(c, patterns::PAT_ZEBRA, patterns::SPF_SYM0123);
2017-07-10 18:47:38 +00:00
int j;
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
if(!wmescher) j = 4;
else if(!BITRUNCATED) j = 0;
else if(si.id < 4) j = 0;
else if(si.id >=4 && si.id < 16) j = 1;
else if(si.id >= 16 && si.id < 28) j = 2;
else if(si.id >= 28 && si.id < 40) j = 3;
2017-07-10 18:47:38 +00:00
else j = 4;
2017-12-27 21:10:50 +00:00
if(euclid6) j = 0;
transmatrix D = applyPatterndir(c, si);
2017-07-10 18:47:38 +00:00
2018-05-07 18:13:56 +00:00
if(wmescher)
set_floor(D, shReptile[j][0]);
else set_maywarp_floor(c);
2017-03-23 10:53:57 +00:00
2018-05-07 18:13:56 +00:00
if(nodetails) return;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
int dcol = 0;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
int ecol = -1;
if(isReptile(c->wall)) {
unsigned char wp = c->wparam;
if(wp == 1)
ecol = 0xFFFF00;
else if(wp <= 5)
ecol = 0xFF0000;
else
ecol = 0;
if(ecol) ecol = gradient(0, ecol, -1, sintick(30), 1);
2017-07-10 18:47:38 +00:00
}
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(ecol == -1 || ecol == 0) dcol = darkena(col, 1, 0xFF);
else dcol = darkena(ecol, 0, 0x80);
2016-08-26 09:58:03 +00:00
dynamicval<color_t> p(poly_outline,
2017-07-10 18:47:38 +00:00
doHighlight() && ecol != -1 && ecol != 0 ? OUTLINE_ENEMY : OUTLINE_DEFAULT);
if(!chasmg) {
if(wmescher)
2018-05-07 18:13:56 +00:00
queuepoly(V*D, shReptile[j][1], dcol);
2017-07-10 18:47:38 +00:00
else
2018-05-07 18:13:56 +00:00
draw_floorshape(c, V, shMFloor, dcol);
2017-07-10 18:47:38 +00:00
}
if(ecol != -1) {
2018-05-07 18:13:56 +00:00
queuepoly(V*D, shReptile[j][2], (ecol << 8) + 0xFF);
queuepoly(V*D, shReptile[j][3], (ecol << 8) + 0xFF);
2017-07-10 18:47:38 +00:00
}
}
void draw_reptile(cell *c, const transmatrix &V, color_t col) {
2018-05-07 18:13:56 +00:00
auto qfib = qfi;
set_reptile_floor(c, V, col, chasmg == 2);
draw_qfi(c, V, col);
qfi = qfib;
}
void set_emeraldfloor(cell *c) {
if(!masterless && BITRUNCATED) {
auto si = patterns::getpatterninfo(c, patterns::PAT_EMERALD, patterns::SPF_SYM0123);
int j = -1;
if(si.id == 8) j = 0;
else if(si.id == 12) j = 1;
else if(si.id == 16) j = 2;
else if(si.id == 20) j = 3;
else if(si.id == 28) j = 4;
else if(si.id == 36) j = 5;
if(j >= 0) {
2018-05-07 18:13:56 +00:00
set_floor(applyPatterndir(c, si), shEmeraldFloor[j]);
return;
}
2017-07-10 18:47:38 +00:00
}
2018-05-07 18:13:56 +00:00
set_floor(shCaveFloor);
2017-07-10 18:47:38 +00:00
}
void viewBuggyCells(cell *c, transmatrix V) {
2018-06-22 12:47:24 +00:00
for(int i=0; i<isize(buggycells); i++)
2017-07-10 18:47:38 +00:00
if(c == buggycells[i]) {
queuepoly(V, shPirateX, 0xC000C080);
return;
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
2018-06-22 12:47:24 +00:00
for(int i=0; i<isize(buggycells); i++) {
2017-07-10 18:47:38 +00:00
cell *c1 = buggycells[i];
cell *cf = cwt.at;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
while(cf != c1) {
cf = pathTowards(cf, c1);
if(cf == c) {
queuepoly(V, shMineMark[1], 0xC000C0D0);
return;
}
2015-08-08 13:57:52 +00:00
}
}
}
2017-07-10 18:47:38 +00:00
void drawMovementArrows(cell *c, transmatrix V) {
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(viewdists) return;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
for(int d=0; d<8; d++) {
movedir md = vectodir(spin(-d * M_PI/4) * tC0(pushone()));
int u = md.d;
2018-03-24 11:59:01 +00:00
cellwalker xc = cwt + u + wstep;
if(xc.at == c) {
transmatrix fixrot = sphereflip * rgpushxto0(sphereflip * tC0(V));
2017-07-10 18:47:38 +00:00
// make it more transparent
color_t col = getcs().uicolor;
2017-07-10 18:47:38 +00:00
col -= (col & 0xFF) >> 1;
poly_outline = OUTLINE_DEFAULT;
queuepoly(fixrot * spin(-d * M_PI/4), shArrow, col);
2017-03-23 10:53:57 +00:00
2018-12-16 23:04:59 +00:00
if((c->type & 1) && (isStunnable(c->monst) || isPushable(c->wall))) {
2017-07-10 18:47:38 +00:00
transmatrix Centered = rgpushxto0(tC0(cwtV));
int sd = md.subdir;
queuepoly(inverse(Centered) * rgpushxto0(Centered * tC0(V)) * rspintox(Centered*tC0(V)) * spin(-sd * M_PI/S7) * xpush(0.2), shArrow, col);
}
else if(!confusingGeometry()) break;
2017-07-10 18:47:38 +00:00
}
}
2017-03-23 10:53:57 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
int celldistAltPlus(cell *c) { return 1000000 + celldistAlt(c); }
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
bool drawstaratvec(double dx, double dy) {
return dx*dx+dy*dy > .05;
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
int reptilecolor(cell *c) {
2018-08-30 14:08:05 +00:00
int i;
2017-07-10 18:47:38 +00:00
2018-08-30 14:08:05 +00:00
if(archimedean)
i = c->master->rval0 & 3;
else {
i = zebra40(c);
2018-08-30 14:08:05 +00:00
if(!masterless) {
if(i >= 4 && i < 16) i = 0;
else if(i >= 16 && i < 28) i = 1;
else if(i >= 28 && i < 40) i = 2;
else i = 3;
}
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
color_t fcoltab[4] = {0xe3bb97, 0xc2d1b0, 0xebe5cb, 0xA0A0A0};
2017-07-10 18:47:38 +00:00
return fcoltab[i];
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
ld wavefun(ld x) {
return sin(x);
/* x /= (2*M_PI);
x -= (int) x;
if(x > .5) return (x-.5) * 2;
else return 0; */
}
2017-03-23 10:53:57 +00:00
2018-11-08 15:21:33 +00:00
colortable nestcolors = { 0x800000, 0x008000, 0x000080, 0x404040, 0x700070, 0x007070, 0x707000, 0x606060 };
2017-12-05 18:43:45 +00:00
2018-11-08 20:56:06 +00:00
color_t floorcolors[landtypes];
void init_floorcolors() {
for(int i=0; i<landtypes; i++)
floorcolors[i] = linf[i].color;
floorcolors[laDesert] = 0xEDC9AF;
floorcolors[laKraken] = 0x20A020;
floorcolors[laDocks] = 0x202020;
floorcolors[laCA] = 0x404040;
floorcolors[laMotion] = 0xF0F000;
floorcolors[laGraveyard] = 0x107010;
floorcolors[laWineyard] = 0x006000;
floorcolors[laLivefjord] = 0x306030;
floorcolors[laMinefield] = 0x80A080;
floorcolors[laCaribbean] = 0x006000;
floorcolors[laAlchemist] = 0x202020;
floorcolors[laRlyeh] = 0x004080;
floorcolors[laHell] = 0xC00000;
floorcolors[laCrossroads] = 0xFF0000;
floorcolors[laJungle] = 0x008000;
floorcolors[laZebra] = 0xE0E0E0;
floorcolors[laCaves] = 0x202020;
floorcolors[laEmerald] = 0x202020;
floorcolors[laDeadCaves] = 0x202020;
floorcolors[laPalace] = 0x806020;
floorcolors[laHunting] = 0x40E0D0 / 2;
floorcolors[laBlizzard] = 0x5050C0;
floorcolors[laCocytus] = 0x80C0FF;
floorcolors[laIce] = 0x8080FF;
floorcolors[laCamelot] = 0xA0A0A0;
floorcolors[laOvergrown] = 0x00C020;
floorcolors[laClearing] = 0x60E080;
floorcolors[laHaunted] = 0x609F60;
floorcolors[laMirror] = floorcolors[laMirrorWall] = floorcolors[laMirrorOld] = 0x808080;
}
color_t magma_color(int id) {
if(id == 95/4-1) return 0x200000;
else if(id == 95/4) return 0x100000;
else if(id < 48/4) return gradient(0xF0F000, 0xF00000, 0, id, 48/4);
else if(id < 96/4) return gradient(0xF00000, 0x400000, 48/4, id, 95/4-2);
else return winf[waMagma].color;
}
void setcolors(cell *c, color_t& wcol, color_t& fcol) {
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
wcol = fcol = winf[c->wall].color;
2017-03-23 10:53:57 +00:00
if(c->wall == waMineMine)
wcol = fcol = winf[waMineUnknown].color;
2017-08-06 12:50:16 +00:00
// water colors
if(isWateryOrBoat(c) || c->wall == waReptileBridge) {
2017-07-10 18:47:38 +00:00
if(c->land == laOcean)
2019-02-17 17:33:15 +00:00
fcol =
#if CAP_FIELD
(c->landparam > 25 && !chaosmode) ? (
0x90 + 8 * sintick(1000, windmap::windcodes[windmap::getId(c)] / 256.)
) :
#endif
0x1010C0 + int(32 * sintick(500, (chaosmode ? c->CHAOSPARAM : c->landparam)*.75/M_PI));
2017-07-10 18:47:38 +00:00
else if(c->land == laOceanWall)
fcol = 0x2020FF;
2019-01-11 01:25:54 +00:00
else if(c->land == laVariant)
fcol = 0x002090 + 15 * sintick(300, 0);
2017-07-10 18:47:38 +00:00
else if(c->land == laKraken) {
fcol = 0x0000A0;
2018-05-04 10:49:50 +00:00
int mafcol = (kraken_pseudohept(c) ? 64 : 8);
2017-07-10 18:47:38 +00:00
/* bool nearshore = false;
for(int i=0; i<c->type; i++)
if(c->move(i)->wall != waSea && c->move(i)->wall != waBoat)
2017-07-10 18:47:38 +00:00
nearshore = true;
if(nearshore) mafcol += 30; */
fcol = fcol + mafcol * (4+sintick(500, ((eubinary||c->master->alt) ? celldistAlt(c) : 0)*.75/M_PI))/5;
2017-03-23 10:53:57 +00:00
}
2017-12-29 00:10:47 +00:00
else if(c->land == laDocks) {
fcol = 0x0000A0;
}
2017-07-10 18:47:38 +00:00
else if(c->land == laAlchemist)
fcol = 0x900090;
else if(c->land == laWhirlpool)
fcol = 0x0000C0 + int(32 * sintick(200, ((eubinary||c->master->alt) ? celldistAlt(c) : 0)*.75/M_PI));
2017-07-10 18:47:38 +00:00
else if(c->land == laLivefjord)
fcol = 0x000080;
else if(isWarped(c->land))
fcol = 0x0000C0 + int((warptype(c)?30:-30) * sintick(600));
2017-07-10 18:47:38 +00:00
else
fcol = wcol;
}
2017-03-23 10:53:57 +00:00
2017-08-06 12:50:16 +00:00
// floor colors for all the lands
else switch(c->land) {
case laBurial: case laTrollheim: case laBarrier: case laOceanWall:
case laCrossroads2: case laCrossroads3: case laCrossroads4: case laCrossroads5:
case laRose: case laPower: case laWildWest: case laHalloween: case laRedRock:
2017-10-30 08:05:16 +00:00
case laDragon: case laStorms: case laTerracotta: case laMercuryRiver:
2018-11-08 20:56:06 +00:00
case laDesert: case laKraken: case laDocks: case laCA:
case laMotion: case laGraveyard: case laWineyard: case laLivefjord:
case laRlyeh: case laHell: case laCrossroads: case laJungle:
case laAlchemist:
fcol = floorcolors[c->land]; break;
2017-10-30 08:05:16 +00:00
2019-02-17 17:37:23 +00:00
#if CAP_COMPLEX2
2018-12-16 23:04:59 +00:00
case laVariant: {
int b = getBits(c);
fcol = 0x404040;
for(int a=0; a<21; a++)
if((b >> a) & 1)
fcol += variant_features[a].color_change;
if(c->wall == waAncientGrave)
wcol = 0x080808;
else if(c->wall == waFreshGrave)
wcol = 0x202020;
break;
}
2019-02-17 17:37:23 +00:00
#endif
2018-12-16 23:04:59 +00:00
2018-01-03 20:51:11 +00:00
case laRuins:
2018-01-04 17:26:38 +00:00
fcol = pseudohept(c) ? 0xC0E0C0 : 0x40A040;
break;
2017-10-30 08:05:16 +00:00
case laDual:
2018-11-08 20:56:06 +00:00
fcol = floorcolors[c->land];
2017-10-30 08:05:16 +00:00
if(c->landparam == 2) fcol = 0x40FF00;
if(c->landparam == 3) fcol = 0xC0FF00;
break;
2017-08-06 12:50:16 +00:00
2018-11-11 15:40:22 +00:00
#if CAP_COMPLEX2
2018-11-08 20:56:06 +00:00
case laBrownian: {
using brownian::level;
fcol = wcol =
2018-12-25 11:49:54 +00:00
/*
2018-11-08 20:56:06 +00:00
c->landparam == 0 ? 0x0000F0 :
c->landparam < level ? gradient(0x002000, 0xFFFFFF, 1, c->landparam, level-1) :
c->landparam < 2 * level ? 0xFFFF80 :
c->landparam < 3 * level ? 0xFF8000 :
2018-12-25 11:49:54 +00:00
0xC00000; */
c->landparam == 0 ? 0x0000F0 : brownian::get_color(c->landparam);
2018-05-04 00:43:25 +00:00
break;
2018-10-25 00:43:14 +00:00
}
2018-11-11 15:40:22 +00:00
#endif
2018-05-04 00:43:25 +00:00
2019-02-17 17:33:15 +00:00
#if CAP_FIELD
2017-10-08 09:12:03 +00:00
case laVolcano: {
int id = lavatide(c, -1)/4;
if(c->wall == waMagma)
fcol = wcol = magma_color(id);
2017-10-14 23:16:01 +00:00
else if(c->wall == waNone) {
2017-10-08 09:12:03 +00:00
fcol = wcol = 0x404040;
if(id == 255/4) fcol = 0xA0A040;
if(id == 255/4-1) fcol = 0x606040;
}
/* {
if(id/4 == 255/4) fd = 0;
if(id/4 == 95/4-1 || id/4 == 255/4-1) fd = 1;
} */
break;
}
2019-02-17 17:33:15 +00:00
#endif
2017-08-06 12:50:16 +00:00
case laMinefield:
2018-11-08 20:56:06 +00:00
fcol = floorcolors[c->land];
2017-08-06 12:50:16 +00:00
if(c->wall == waMineMine && ((cmode & sm::MAP) || !canmove))
fcol = wcol = 0xFF4040;
break;
case laCaribbean:
if(c->wall != waCIsland && c->wall != waCIsland2)
2018-11-08 20:56:06 +00:00
fcol = floorcolors[c->land];
2017-08-06 12:50:16 +00:00
break;
case laReptile:
fcol = reptilecolor(c);
break;
2018-11-08 20:56:06 +00:00
2017-08-06 12:50:16 +00:00
case laCaves: case laEmerald: case laDeadCaves:
fcol = 0x202020;
if(c->land == laEmerald)
if(c->wall == waCavefloor || c->wall == waCavewall) {
fcol = wcol = gradient(winf[waCavefloor].color, 0xFF00, 0, 0.5, 1);
if(c->wall == waCavewall) wcol = 0xC0FFC0;
}
break;
case laMirror: case laMirrorWall: case laMirrorOld:
2018-11-08 20:56:06 +00:00
if(c->land == laMirrorWall) fcol = floorcolors[laMirror];
else fcol = floorcolors[c->land];
2017-08-06 12:50:16 +00:00
break;
case laDryForest:
fcol = gradient(0x008000, 0x800000, 0, c->landparam, 10);
break;
case laMountain:
2018-08-17 11:29:00 +00:00
if(eubinary || sphere || c->master->alt)
2017-08-06 12:50:16 +00:00
fcol = celldistAlt(c) & 1 ? 0x604020 : 0x302010;
else fcol = 0;
if(c->wall == waPlatform) wcol = 0xF0F0A0;
break;
case laCanvas:
fcol = c->landparam;
2019-02-26 13:56:07 +00:00
if(c->wall == waWaxWall) wcol = c->landparam;
2017-08-06 12:50:16 +00:00
break;
case laPalace:
2018-11-08 20:56:06 +00:00
fcol = floorcolors[c->land];
2017-08-06 12:50:16 +00:00
if(c->wall == waClosedGate || c->wall == waOpenGate)
fcol = wcol;
break;
case laElementalWall:
fcol = (linf[c->barleft].color>>1) + (linf[c->barright].color>>1);
break;
case laZebra:
2018-11-08 20:56:06 +00:00
fcol = floorcolors[c->land];
2017-08-06 12:50:16 +00:00
if(c->wall == waTrapdoor) fcol = 0x808080;
break;
case laHive:
fcol = linf[c->land].color;
if(c->wall == waWaxWall) wcol = c->landparam;
if(items[itOrbInvis] && c->wall == waNone && c->landparam)
fcol = gradient(fcol, 0xFF0000, 0, c->landparam, 100);
if(c->bardir == NOBARRIERS && c->barleft)
fcol = minf[moBug0+c->barright].color;
break;
2017-12-30 14:12:15 +00:00
case laSwitch:
fcol = minf[passive_switch].color;
break;
2017-08-06 12:50:16 +00:00
case laTortoise:
fcol = tortoise::getMatchColor(getBits(c));
if(c->wall == waBigTree) wcol = 0x709000;
else if(c->wall == waSmallTree) wcol = 0x905000;
break;
case laOvergrown: case laClearing:
2018-11-08 20:56:06 +00:00
fcol = floorcolors[c->land];
2017-08-06 12:50:16 +00:00
if(c->wall == waSmallTree) wcol = 0x008060;
else if(c->wall == waBigTree) wcol = 0x0080C0;
break;
case laTemple: {
2018-08-09 17:28:53 +00:00
int d = showoff ? 0 : (eubinary||c->master->alt) ? celldistAlt(c) : 99;
2017-08-06 12:50:16 +00:00
if(chaosmode)
fcol = 0x405090;
else if(d % TEMPLE_EACH == 0)
fcol = gradient(0x304080, winf[waColumn].color, 0, 0.5, 1);
// else if(c->type == 7)
// wcol = 0x707070;
else if(d% 2 == -1)
fcol = 0x304080;
else
fcol = 0x405090;
break;
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
2017-08-06 12:50:16 +00:00
case laWhirlwind:
if(c->land == laWhirlwind) {
color_t wcol[4] = {0x404040, 0x404080, 0x2050A0, 0x5050C0};
2017-08-06 12:50:16 +00:00
fcol = wcol[whirlwind::fzebra3(c)];
}
break;
2017-09-30 09:46:41 +00:00
2017-10-10 12:24:39 +00:00
case laHunting:
2018-11-08 20:56:06 +00:00
fcol = floorcolors[c->land];
2017-09-30 09:46:41 +00:00
if(pseudohept(c)) fcol = fcol * 3/4;
break;
2017-08-06 12:50:16 +00:00
case laIvoryTower:
fcol = 0x10101 * (32 + (c->landparam&1) * 32) - 0x000010;
break;
2018-12-21 13:41:23 +00:00
case laWestWall:
2018-12-25 18:25:09 +00:00
fcol = 0x10101 * ((c->landparam&1) * 32) + floorcolors[c->land];
2018-12-21 13:41:23 +00:00
break;
2017-08-06 12:50:16 +00:00
case laDungeon: {
int lp = c->landparam % 5;
// xcol = (c->landparam&1) ? 0xD00000 : 0x00D000;
int lps[5] = { 0x402000, 0x302000, 0x202000, 0x282000, 0x382000 };
fcol = lps[lp];
if(c->wall == waClosedGate)
fcol = wcol = 0xC0C0C0;
if(c->wall == waOpenGate)
fcol = wcol = 0x404040;
if(c->wall == waPlatform)
fcol = wcol = 0xDFB520;
break;
2017-07-10 18:47:38 +00:00
}
2017-08-06 12:50:16 +00:00
case laEndorian: {
int clev = pd_from->land == laEndorian ? edgeDepth(pd_from) : 0;
2017-08-06 12:50:16 +00:00
// xcol = (c->landparam&1) ? 0xD00000 : 0x00D000;
fcol = 0x10101 * (32 + (c->landparam&1) * 32) - 0x000010;
int ed = edgeDepth(c);
2018-04-11 11:16:40 +00:00
int sr = get_sightrange_ambush();
if(clev == UNKNOWN || ed == UNKNOWN)
fcol = 0x0000D0;
else {
while(ed > clev + sr) ed -= 2;
while(ed < clev - sr) ed += 2;
fcol = gradient(fcol, 0x0000D0, clev-sr, edgeDepth(c), clev+sr);
}
2017-08-06 12:50:16 +00:00
if(c->wall == waTrunk) fcol = winf[waTrunk].color;
if(c->wall == waCanopy || c->wall == waSolidBranch || c->wall == waWeakBranch) {
fcol = winf[waCanopy].color;
if(c->landparam & 1) fcol = gradient(0, fcol, 0, .75, 1);
}
break;
}
2019-02-17 17:33:15 +00:00
#if CAP_FIELD
2017-08-06 12:50:16 +00:00
case laPrairie:
if(prairie::isriver(c)) {
fcol = ((c->LHU.fi.rval & 1) ? 0x402000: 0x503000);
}
else {
fcol = 0x004000 + 0x001000 * c->LHU.fi.walldist;
fcol += 0x10000 * (255 - 511 / (1 + max((int) c->LHU.fi.flowerdist, 1)));
// fcol += 0x1 * (511 / (1 + max((int) c->LHU.fi.walldist2, 1)));
}
break;
2019-02-17 17:33:15 +00:00
#endif
2017-08-06 12:50:16 +00:00
case laCamelot: {
2018-08-17 11:29:00 +00:00
int d = showoff ? 0 : ((eubinary||c->master->alt) ? celldistAltRelative(c) : 0);
2017-08-06 12:50:16 +00:00
#if CAP_TOUR
if(!tour::on) camelotcheat = false;
if(camelotcheat)
fcol = (d&1) ? 0xC0C0C0 : 0x606060;
else
#endif
if(d < 0) {
2018-11-08 20:56:06 +00:00
fcol = floorcolors[c->land];
2017-08-06 12:50:16 +00:00
}
else {
// a nice floor pattern
int v = emeraldval(c);
int v0 = (v&~3);
bool sw = (v&1);
if(v0 == 8 || v0 == 12 || v0 == 20 || v0 == 40 || v0 == 36 || v0 == 24)
sw = !sw;
if(sw)
fcol = 0xC0C0C0;
else
fcol = 0xA0A0A0;
}
break;
}
2017-09-30 09:46:41 +00:00
case laIce: case laCocytus: case laBlizzard:
2017-08-06 12:50:16 +00:00
if(isIcyWall(c)) {
float h = HEAT(c);
2018-11-08 20:56:06 +00:00
eLand l = c->land;
2017-09-30 09:46:41 +00:00
2018-11-08 20:56:06 +00:00
color_t colorN04 = l == laCocytus ? 0x4080FF : 0x4040FF;
color_t colorN10 = 0x0000FF;
2018-11-08 20:56:06 +00:00
color_t color0 = floorcolors[c->land];
color_t color02 = 0xFFFFFF;
color_t color06 = 0xFF0000;
color_t color08 = 0xFFFF00;
2017-09-30 09:46:41 +00:00
if(h < -1)
wcol = colorN10;
else if(h < -0.4)
wcol = gradient(colorN04, colorN10 , -0.4, h, -1);
2017-08-06 12:50:16 +00:00
else if(h < 0)
2017-09-30 09:46:41 +00:00
wcol = gradient(color0, colorN04, 0, h, -0.4);
2017-08-06 12:50:16 +00:00
else if(h < 0.2)
2017-09-30 09:46:41 +00:00
wcol = gradient(color0, color02, 0, h, 0.2);
2017-08-06 12:50:16 +00:00
// else if(h < 0.4)
// wcol = gradient(0xFFFFFF, 0xFFFF00, 0.2, h, 0.4);
else if(h < 0.6)
2017-09-30 09:46:41 +00:00
wcol = gradient(color02, color06, 0.2, h, 0.6);
2017-08-06 12:50:16 +00:00
else if(h < 0.8)
2017-09-30 09:46:41 +00:00
wcol = gradient(color06, color08, 0.6, h, 0.8);
2017-08-06 12:50:16 +00:00
else
2017-09-30 09:46:41 +00:00
wcol = color08;
2017-08-06 12:50:16 +00:00
if(c->wall == waFrozenLake)
fcol = wcol;
else
fcol = (wcol & 0xFEFEFE) >> 1;
if(c->wall == waLake)
fcol = wcol = (wcol & 0xFCFCFC) >> 2;
}
break;
case laOcean:
if(chaosmode)
fcol = gradient(0xD0A090, 0xD0D020, 0, c->CHAOSPARAM, 30);
else
fcol = gradient(0xD0D090, 0xD0D020, -1, sin((double) c->landparam), 1);
break;
2017-12-05 18:43:45 +00:00
case laSnakeNest: {
int fv = pattern_threecolor(c);
fcol = nestcolors[fv&7];
if(realred(c->wall))
wcol = fcol * (4 + snakelevel(c)) / 4;
break;
}
2017-08-06 12:50:16 +00:00
default:
if(isElemental(c->land)) fcol = linf[c->land].color;
if(isWarped(c->land)) {
2017-12-18 20:15:03 +00:00
fcol = warptype(c) ? 0x80C080 : 0xA06020;
2017-08-06 12:50:16 +00:00
if(c->wall == waSmallTree) wcol = 0x608000;
}
if(isHaunted(c->land)) {
int itcolor = 0;
for(int i=0; i<c->type; i++) if(c->move(i) && c->move(i)->item)
2017-08-06 12:50:16 +00:00
itcolor = 1;
if(c->item) itcolor |= 2;
2018-11-08 20:56:06 +00:00
fcol = floorcolors[laHaunted] + 0x202020 * itcolor;
2017-08-06 12:50:16 +00:00
forCellEx(c2, c) if(c2->monst == moFriendlyGhost)
fcol = gradient(fcol, fghostcolor(c2), 0, .25, 1);
2017-07-10 18:47:38 +00:00
2017-08-06 12:50:16 +00:00
if(c->monst == moFriendlyGhost)
fcol = gradient(fcol, fghostcolor(c), 0, .5, 1);
2017-08-06 12:50:16 +00:00
if(c->wall == waSmallTree) wcol = 0x004000;
else if(c->wall == waBigTree) wcol = 0x008000;
}
}
/* if(c->land == laCaribbean && (c->wall == waCIsland || c->wall == waCIsland2))
fcol = wcol = winf[c->wall].color; */
switch(c->wall) {
case waSulphur: case waSulphurC: case waPlatform: case waMercury: case waDock:
case waAncientGrave: case waFreshGrave: case waThumperOn: case waThumperOff: case waBonfireOff:
2018-12-16 23:04:59 +00:00
case waRoundTable: case waExplosiveBarrel:
// floors become fcol
fcol = wcol;
break;
case waDeadTroll2: case waPetrifiedBridge: case waPetrified: {
eMonster m = eMonster(c->wparam);
if(c->wall == waPetrified || c->wall == waPetrifiedBridge)
wcol = gradient(wcol, minf[m].color, 0, .2, 1);
if(c->wall == waPetrified || isTroll(m)) if(!(m == moForestTroll && c->land == laOvergrown))
wcol = gradient(wcol, minf[m].color, 0, .4, 1);
break;
}
case waFloorA: case waFloorB: // isAlch
if(c->item && !(conformal::includeHistory && conformal::infindhistory.count(c)))
fcol = wcol = iinf[c->item].color;
else
fcol = wcol;
break;
case waBoat:
if(wmascii) wcol = 0xC06000;
break;
case waEternalFire:
fcol = wcol = weakfirecolor(1500);
break;
case waFire: case waPartialFire: case waBurningDock:
fcol = wcol = firecolor(100);
break;
case waDeadfloor: case waCavefloor:
fcol = wcol;
break;
case waNone:
if(c->land == laNone)
wcol = fcol = 0x101010;
if(c->land == laHive)
wcol = fcol;
break;
2017-08-06 12:50:16 +00:00
case waMineUnknown: case waMineMine:
if(mineMarkedSafe(c))
fcol = wcol = gradient(wcol, 0x40FF40, 0, 0.2, 1);
else if(mineMarked(c))
fcol = wcol = gradient(wcol, 0xFF4040, -1, sintick(100), 1);
// fallthrough
case waMineOpen:
fcol = wcol;
if(wmblack || wmascii) fcol >>= 1, wcol >>= 1;
break;
case waCavewall:
if(c->land != laEmerald) fcol = winf[waCavefloor].color;
break;
2019-02-28 16:06:53 +00:00
case waEditStatue:
if(c->land == laCanvas) wcol = c->landparam;
else wcol = (0x125628 * c->wparam) & 0xFFFFFF;
default:
break;
}
2017-09-30 09:46:41 +00:00
2017-10-08 09:12:03 +00:00
/* if(false && isAlch2(c, true)) {
int id = lavatide(c, -1);
2017-09-30 09:46:41 +00:00
if(id < 96)
wcol = gradient(0x800000, 0xFF0000, 0, id, 96);
else
wcol = gradient(0x00FF00, 0xFFFF00, 96, id, 255);
fcol = wcol;
2017-10-08 09:12:03 +00:00
} */
if(DIM == 2) {
int rd = rosedist(c);
if(rd == 1)
wcol = gradient(0x804060, wcol, 0,1,3),
fcol = gradient(0x804060, fcol, 0,1,3);
if(rd == 2)
wcol = gradient(0x804060, wcol, 0,2,3),
fcol = gradient(0x804060, fcol, 0,2,3);
}
2017-04-08 15:18:29 +00:00
if(items[itRevolver] && !shmup::on) {
bool inrange = c->mpdist <= GUNRANGE;
if(inrange) {
inrange = false;
for(int i=0; i<numplayers(); i++) for(cell *c1: gun_targets(playerpos(i))) if(c1 == c) inrange = true;
}
if(!inrange)
fcol = gradient(fcol, 0, 0, 25, 100),
wcol = gradient(wcol, 0, 0, 25, 100);
}
2017-07-10 18:47:38 +00:00
if(highwall(c) && !wmspatial)
fcol = wcol;
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
if(wmascii && (c->wall == waNone || isWatery(c))) wcol = fcol;
if(!wmspatial && snakelevel(c) && !realred(c->wall)) fcol = wcol;
if(c->wall == waGlass && !wmspatial) fcol = wcol;
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
bool noAdjacentChasms(cell *c) {
forCellEx(c2, c) if(c2->wall == waChasm) return false;
return true;
2015-08-08 13:57:52 +00:00
}
// does the current geometry allow nice duals
bool has_nice_dual() {
2019-02-17 17:28:20 +00:00
#if CAP_IRR
if(IRREGULAR) return irr::bitruncations_performed > 0;
2019-02-17 17:28:20 +00:00
#endif
#if CAP_ARCM
if(archimedean) return geosupport_football() >= 2;
2019-02-17 17:28:20 +00:00
#endif
if(binarytiling) return false;
if(BITRUNCATED) return true;
if(a4) return false;
if((S7 & 1) == 0) return true;
if(PURE) return false;
2019-02-17 17:28:20 +00:00
#if CAP_GP
2018-04-09 15:40:12 +00:00
return (gp::param.first + gp::param.second * 2) % 3 == 0;
2019-02-17 17:28:20 +00:00
#else
return false;
#endif
}
// does the current geometry allow nice duals
bool is_nice_dual(cell *c) {
return c->land == laDual && has_nice_dual();
}
bool use_swapped_duals() {
return (masterless && !a4) || GOLDBERG;
}
int wavephase;
#if CAP_SHAPES
void floorShadow(cell *c, const transmatrix& V, color_t col) {
if(model_needs_depth() || noshadow)
2017-07-10 18:47:38 +00:00
return; // shadows break the depth testing
dynamicval<color_t> p(poly_outline, OUTLINE_TRANS);
2018-05-07 18:13:56 +00:00
if(qfi.shape) {
2018-08-28 12:27:23 +00:00
queuepolyat(V * qfi.spin * shadowmulmatrix, *qfi.shape, col, PPR::WALLSHADOW);
2017-07-10 18:47:38 +00:00
}
else if(qfi.usershape >= 0)
2018-08-28 12:27:23 +00:00
mapeditor::drawUserShape(V * qfi.spin * shadowmulmatrix, mapeditor::sgFloor, qfi.usershape, col, c, PPR::WALLSHADOW);
2018-08-18 16:01:41 +00:00
else
2018-08-28 12:27:23 +00:00
draw_shapevec(c, V, qfi.fshape->shadow, col, PPR::WALLSHADOW);
2017-07-10 18:47:38 +00:00
}
2018-05-07 18:13:56 +00:00
void set_maywarp_floor(cell *c) {
bool warp = isWarped(c);
2018-08-20 13:24:44 +00:00
if(warp && !shmup::on && geosupport_football() == 2) {
if(!stdhyperbolic) {
set_floor(shTriheptaFloor);
return;
}
auto si = patterns::getpatterninfo(c, patterns::PAT_TYPES, 0);
2018-05-07 18:13:56 +00:00
if(si.id == 0 || si.id == 1)
set_floor(shTriheptaFloor);
else if(si.id >= 14)
2018-05-07 18:13:56 +00:00
set_floor(shFloor);
else
set_floor(applyPatterndir(c, si), shTriheptaSpecial[si.id]);
2017-07-10 18:47:38 +00:00
}
2018-05-07 18:13:56 +00:00
else if(is_nice_dual(c)) set_floor(shBigTriangle);
else set_floor(shFloor);
2017-07-10 18:47:38 +00:00
}
void escherSidewall(cell *c, int sidepar, const transmatrix& V, color_t col) {
2017-07-10 18:47:38 +00:00
if(sidepar >= SIDE_SLEV && sidepar <= SIDE_SLEV+2) {
int sl = sidepar - SIDE_SLEV;
for(int z=1; z<=4; z++) if(z == 1 || (z == 4 && detaillevel == 2))
2018-08-28 12:27:23 +00:00
draw_qfi(c, mscale(V, zgrad0(geom3::slev * sl, geom3::slev * (sl+1), z, 4)), col, PPR::REDWALL-4+z+4*sl);
2017-07-10 18:47:38 +00:00
}
else if(sidepar == SIDE_WALL) {
const int layers = 2 << detaillevel;
for(int z=1; z<layers; z++)
2018-08-28 12:27:23 +00:00
draw_qfi(c, mscale(V, zgrad0(0, geom3::actual_wall_height(), z, layers)), col, PPR::WALL3+z-layers);
2017-07-10 18:47:38 +00:00
}
else if(sidepar == SIDE_LAKE) {
const int layers = 1 << (detaillevel-1);
if(detaillevel) for(int z=0; z<layers; z++)
2018-08-28 12:27:23 +00:00
draw_qfi(c, mscale(V, zgrad0(-geom3::lake_top, 0, z, layers)), col, PPR::FLOOR+z-layers);
2017-07-10 18:47:38 +00:00
}
else if(sidepar == SIDE_LTOB) {
const int layers = 1 << (detaillevel-1);
if(detaillevel) for(int z=0; z<layers; z++)
2018-08-28 12:27:23 +00:00
draw_qfi(c, mscale(V, zgrad0(-geom3::lake_bottom, -geom3::lake_top, z, layers)), col, PPR::INLAKEWALL+z-layers);
2017-07-10 18:47:38 +00:00
}
else if(sidepar == SIDE_BTOI) {
const int layers = 1 << detaillevel;
2018-08-28 12:27:23 +00:00
draw_qfi(c, mscale(V, geom3::INFDEEP), col, PPR::MINUSINF);
2017-07-10 18:47:38 +00:00
for(int z=1; z<layers; z++)
2018-08-28 12:27:23 +00:00
draw_qfi(c, mscale(V, zgrad0(-geom3::lake_bottom, -geom3::lake_top, -z, 1)), col, PPR::LAKEBOTTOM+z-layers);
2017-07-10 18:47:38 +00:00
}
}
bool placeSidewall(cell *c, int i, int sidepar, const transmatrix& V, color_t col) {
2018-05-07 18:13:56 +00:00
if(!qfi.fshape || !qfi.fshape->is_plain || !validsidepar[sidepar] || qfi.usershape >= 0) {
2018-05-07 18:13:56 +00:00
escherSidewall(c, sidepar, V, col);
return true;
2017-10-29 22:54:26 +00:00
}
2018-05-07 18:13:56 +00:00
if(qfi.fshape == &shBigTriangle && pseudohept(c->move(i))) return false;
if(qfi.fshape == &shTriheptaFloor && !pseudohept(c) && !pseudohept(c->move(i))) return false;
2018-05-07 18:13:56 +00:00
PPR prio;
2018-08-28 12:27:23 +00:00
/* if(mirr) prio = PPR::GLASS - 2;
else */ if(sidepar == SIDE_WALL) prio = PPR::WALL3 - 2;
else if(sidepar == SIDE_WTS3) prio = PPR::WALL3 - 2;
else if(sidepar == SIDE_LAKE) prio = PPR::LAKEWALL;
else if(sidepar == SIDE_LTOB) prio = PPR::INLAKEWALL;
else if(sidepar == SIDE_BTOI) prio = PPR::BELOWBOTTOM;
else prio = PPR::REDWALL-2+4*(sidepar-SIDE_SLEV);
2017-07-10 18:47:38 +00:00
transmatrix V2 = V * ddspin(c, i);
2018-07-16 18:05:23 +00:00
if(binarytiling || archimedean || NONSTDVAR) {
2019-02-17 17:28:20 +00:00
#if CAP_ARCM
if(archimedean && !PURE)
i = (i + arcm::parent_index_of(c->master)/DUALMUL + MODFIXER) % c->type;
2019-02-17 17:28:20 +00:00
#endif
2018-05-07 18:13:56 +00:00
draw_shapevec(c, V2, qfi.fshape->gpside[sidepar][i], col, prio);
return false;
}
2017-10-29 22:54:26 +00:00
2018-05-07 18:13:56 +00:00
queuepolyat(V2, qfi.fshape->side[sidepar][pseudohept(c)], col, prio);
return false;
2016-01-02 10:09:13 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
bool openorsafe(cell *c) {
return c->wall == waMineOpen || mineMarkedSafe(c);
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
#define Dark(x) darkena(x,0,0xFF)
2017-03-23 10:53:57 +00:00
color_t stdgridcolor = 0x202020FF;
2017-07-10 18:47:38 +00:00
int gridcolor(cell *c1, cell *c2) {
2017-07-12 17:50:39 +00:00
if(cmode & sm::DRAW) return Dark(forecolor);
2017-07-10 18:47:38 +00:00
if(!c2)
return 0x202020 >> darken;
int rd1 = rosedist(c1), rd2 = rosedist(c2);
if(rd1 != rd2) {
int r = rd1+rd2;
if(r == 1) return Dark(0x802020);
if(r == 3) return Dark(0xC02020);
if(r == 2) return Dark(0xF02020);
2017-06-18 16:51:46 +00:00
}
2017-07-10 18:47:38 +00:00
if(chasmgraph(c1) != chasmgraph(c2))
return Dark(0x808080);
if(c1->land == laAlchemist && c2->land == laAlchemist && c1->wall != c2->wall && !c1->item && !c2->item)
2017-07-10 18:47:38 +00:00
return Dark(0xC020C0);
if((c1->land == laWhirlpool || c2->land == laWhirlpool) && (celldistAlt(c1) != celldistAlt(c2)))
return Dark(0x2020A0);
if(c1->land == laMinefield && c2->land == laMinefield && (openorsafe(c1) != openorsafe(c2)))
return Dark(0xA0A0A0);
if(!darken) return stdgridcolor;
2017-07-10 18:47:38 +00:00
return Dark(0x202020);
2017-06-18 16:51:46 +00:00
}
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
void pushdown(cell *c, int& q, const transmatrix &V, double down, bool rezoom, bool repriority) {
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
// since we might be changing priorities, we have to make sure that we are sorting correctly
if(down > 0 && repriority) {
int qq = q+1;
2018-06-22 12:47:24 +00:00
while(qq < isize(ptds))
if(qq > q && ptds[qq]->prio < ptds[qq-1]->prio) {
2017-07-10 18:47:38 +00:00
swap(ptds[qq], ptds[qq-1]);
qq--;
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
else qq++;
2017-06-18 16:51:46 +00:00
}
2018-06-22 12:47:24 +00:00
while(q < isize(ptds)) {
auto pp = dynamic_cast<dqi_poly*> (&*ptds[q++]);
if(!pp) continue;
auto& ptd = *pp;
double z2;
2017-07-10 18:47:38 +00:00
double z = zlevel(tC0(ptd.V));
double lev = geom3::factor_to_lev(z);
double nlev = lev - down;
double xyscale = rezoom ? geom3::scale_at_lev(lev) / geom3::scale_at_lev(nlev) : 1;
z2 = geom3::lev_to_factor(nlev);
double zscale = z2 / z;
// xyscale = xyscale + (zscale-xyscale) * (1+sin(ticks / 1000.0)) / 2;
ptd.V = xyzscale( V, xyscale*zscale, zscale)
* inverse(V) * ptd.V;
2017-07-10 18:47:38 +00:00
if(!repriority) ;
else if(nlev < -geom3::lake_bottom-1e-3) {
ptd.prio = PPR::BELOWBOTTOM_FALLANIM;
if(c->wall != waChasm)
ptd.color = 0; // disappear!
}
else if(nlev < -geom3::lake_top-1e-3)
ptd.prio = PPR::INLAKEWALL_FALLANIM;
else if(nlev < 0)
ptd.prio = PPR::LAKEWALL_FALLANIM;
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
// 1 : (floor, water); 2 : (water, bottom); 4 : (bottom, inf)
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
int shallow(cell *c) {
if(cellUnstable(c)) return 0;
else if(
c->wall == waReptile) return 1;
else if(c->wall == waReptileBridge ||
c->wall == waGargoyleFloor ||
c->wall == waGargoyleBridge ||
c->wall == waTempFloor ||
c->wall == waTempBridge ||
c->wall == waPetrifiedBridge ||
2017-07-10 18:47:38 +00:00
c->wall == waFrozenLake)
return 5;
return 7;
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
bool allemptynear(cell *c) {
if(c->wall) return false;
forCellEx(c2, c) if(c2->wall) return false;
return true;
2016-01-02 10:09:13 +00:00
}
static const int trapcol[4] = {0x904040, 0xA02020, 0xD00000, 0x303030};
static const int terracol[8] = {0xD000, 0xE25050, 0xD0D0D0, 0x606060, 0x303030, 0x181818, 0x0080, 0x8080};
2017-10-28 23:57:34 +00:00
2018-03-25 16:33:21 +00:00
bool bright;
2017-10-17 10:54:59 +00:00
// how much to darken
int getfd(cell *c) {
2018-03-25 16:33:21 +00:00
if(bright) return 0;
2017-10-17 10:54:59 +00:00
switch(c->land) {
case laRedRock:
case laReptile:
case laCanvas:
return 0;
2017-12-05 18:43:45 +00:00
case laSnakeNest:
return realred(c->wall) ? 0 : 1;
2017-10-17 10:54:59 +00:00
case laTerracotta:
case laMercuryRiver:
2017-10-17 18:41:17 +00:00
return (c->wall == waMercury && wmspatial) ? 0 : 1;
2017-10-17 10:54:59 +00:00
case laKraken:
2017-12-29 00:10:47 +00:00
case laDocks:
2017-10-17 10:54:59 +00:00
case laBurial:
case laIvoryTower:
case laDungeon:
case laMountain:
case laEndorian:
case laCaribbean:
case laWhirlwind:
case laRose:
case laWarpSea:
case laTortoise:
case laDragon:
case laHalloween:
case laHunting:
case laOcean:
case laLivefjord:
case laWhirlpool:
case laAlchemist:
case laIce:
case laGraveyard:
case laBlizzard:
case laRlyeh:
case laTemple:
case laWineyard:
case laDeadCaves:
case laPalace:
case laCA:
2017-10-30 08:05:16 +00:00
case laDual:
2018-10-25 00:43:14 +00:00
case laBrownian:
2017-10-17 10:54:59 +00:00
return 1;
2019-01-11 01:25:54 +00:00
case laVariant:
if(isWateryOrBoat(c)) return 1;
return 2;
2017-10-17 10:54:59 +00:00
case laTrollheim:
default:
return 2;
}
}
2017-12-05 18:43:45 +00:00
int getSnakelevColor(cell *c, int i, int last, int fd, color_t wcol) {
color_t col;
2017-12-05 18:43:45 +00:00
if(c->wall == waTower)
col = 0xD0D0D0-i*0x101010;
else if(c->land == laSnakeNest)
return darkena(nestcolors[pattern_threecolor(c)] * (5 + i) / 4, 0, 0xFF);
2019-02-17 17:37:23 +00:00
#if CAP_COMPLEX2
2018-12-25 11:49:54 +00:00
else if(c->land == laBrownian)
col = brownian::get_color(c->landparam % brownian::level + (i+1) * brownian::level);
2019-02-17 17:37:23 +00:00
#endif
2017-12-05 18:43:45 +00:00
else if(i == last-1)
col = wcol;
else
col = winf[waRed1+i].color;
return darkena(col, fd, 0xFF);
}
#if CAP_SHAPES
void draw_wall(cell *c, const transmatrix& V, color_t wcol, color_t& zcol, int ct6, int fd) {
zcol = wcol;
color_t wcol0 = wcol;
int starcol = wcol;
if(c->wall == waWarpGate) starcol = 0;
if(c->wall == waVinePlant) starcol = 0x60C000;
color_t wcol2 = gradient(0, wcol0, 0, .8, 1);
if(c->wall == waClosedGate) {
int hdir = 0;
for(int i=0; i<c->type; i++) if(c->move(i)->wall == waClosedGate)
hdir = i;
transmatrix V2 = mscale(V, wmspatial?geom3::WALL:1) * ddspin(c, hdir, M_PI);
2018-08-28 12:27:23 +00:00
queuepolyat(V2, shPalaceGate, darkena(wcol, 0, 0xFF), wmspatial?PPR::WALL3A:PPR::WALL);
starcol = 0;
}
hpcshape& shThisWall = isGrave(c->wall) ? shCross : shWall[ct6];
if(conegraph(c)) {
const int layers = 2 << detaillevel;
for(int z=1; z<layers; z++) {
2018-04-23 10:34:14 +00:00
double zg = zgrad0(0, geom3::actual_wall_height(), z, layers);
2018-05-07 18:13:56 +00:00
draw_qfi(c, xyzscale(V, zg*(layers-z)/layers, zg),
2018-08-28 12:27:23 +00:00
darkena(gradient(0, wcol, -layers, z, layers), 0, 0xFF), PPR::WALL3+z-layers+2);
}
2018-05-07 18:13:56 +00:00
floorShadow(c, V, SHADOW_WALL);
}
else if(true) {
if(!wmspatial) {
if(starcol) queuepoly(V, shThisWall, darkena(starcol, 0, 0xFF));
}
else {
transmatrix Vdepth = mscale(V, geom3::WALL);
int alpha = 0xFF;
if(c->wall == waIcewall)
alpha = 0xC0;
if(starcol && !(wmescher && c->wall == waPlatform))
2018-08-28 12:27:23 +00:00
queuepolyat(Vdepth, shThisWall, darkena(starcol, 0, 0xFF), PPR::WALL3A);
2018-08-28 12:27:23 +00:00
draw_qfi(c, Vdepth, darkena(wcol0, fd, alpha), PPR::WALL3);
2018-05-07 18:13:56 +00:00
floorShadow(c, V, SHADOW_WALL);
if(c->wall == waCamelot) {
forCellIdEx(c2, i, c) {
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_SLEV, V, darkena(wcol2, fd, alpha))) break;
}
forCellIdEx(c2, i, c) {
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_SLEV+1, V, darkena(wcol2, fd, alpha))) break;
}
forCellIdEx(c2, i, c) {
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_SLEV+2, V, darkena(wcol2, fd, alpha))) break;
}
forCellIdEx(c2, i, c) {
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_WTS3, V, darkena(wcol2, fd, alpha))) break;
}
}
else {
forCellIdEx(c2, i, c)
if(!highwall(c2) || conegraph(c2)) {
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_WALL, V, darkena(wcol2, fd, alpha))) break;
}
}
}
}
}
2019-02-17 17:41:40 +00:00
#endif
2018-05-07 18:13:56 +00:00
bool just_gmatrix;
int colorhash(color_t i) {
return (i * 0x471211 + i*i*0x124159 + i*i*i*0x982165) & 0xFFFFFF;
}
void draw_gravity_particles(cell *c, const transmatrix V) {
int u = (int)(size_t)(c);
u = ((u * 137) + (u % 1000) * 51) % 1000;
2019-01-03 02:20:02 +00:00
int tt = ticks + u; fractick(ticks, 900);
ld r0 = (tt % 900) / 1100.;
ld r1 = (tt % 900 + 200) / 1100.;
const color_t grav_normal_color = 0x808080FF;
const color_t antigrav_color = 0xF04040FF;
const color_t levitate_color = 0x40F040FF;
ld lev = 2;
if(spatial_graphics) {
switch(gravity_state) {
case gsNormal:
for(int i=0; i<6; i++) {
transmatrix T = V * spin(i*degree*60) * xpush(crossf/3);
queueline(mmscale(T, 1 + (1-r0) * (lev-1)) * C0, mmscale(T, 1 + (1-r1) * (lev - 1)) * C0, grav_normal_color);
}
break;
case gsAnti:
for(int i=0; i<6; i++) {
transmatrix T = V * spin(i*degree*60) * xpush(crossf/3);
queueline(mmscale(T, 1 + r0 * (lev-1)) * C0, mmscale(T, 1 + r1 * (lev-1)) * C0, antigrav_color);
}
break;
case gsLevitation:
for(int i=0; i<6; i++) {
transmatrix T0 = V * spin(i*degree*60 + tt/60. * degree) * xpush(crossf/3);
transmatrix T1 = V * spin(i*degree*60 + (tt/60. + 30) * degree) * xpush(crossf/3);
queueline(mmscale(T0, (lev+1)/2) * C0, mmscale(T1, (lev+1)/2) * C0, levitate_color);
}
break;
}
}
else {
switch(gravity_state) {
case gsNormal:
for(int i=0; i<6; i++) {
transmatrix T0 = V * spin(i*degree*60) * xpush(crossf/3 * (1-r0));
transmatrix T1 = V * spin(i*degree*60) * xpush(crossf/3 * (1-r1));
queueline(T0 * C0, T1 * C0, grav_normal_color);
}
break;
case gsAnti:
for(int i=0; i<6; i++) {
transmatrix T0 = V * spin(i*degree*60) * xpush(crossf/3 * r0);
transmatrix T1 = V * spin(i*degree*60) * xpush(crossf/3 * r1);
queueline(T0 * C0, T1 * C0, antigrav_color);
}
break;
case gsLevitation:
for(int i=0; i<6; i++) {
transmatrix T0 = V * spin(i*degree*60 + tt/60. * degree) * xpush(crossf/3);
transmatrix T1 = V * spin(i*degree*60 + (tt/60. + 30) * degree) * xpush(crossf/3);
queueline(T0 * C0, T1 * C0, levitate_color);
}
break;
}
}
}
bool isWall3(cell *c, color_t& wcol) {
if(isWorm(c)) { wcol = minf[c->monst].color; return true; }
if(isWall(c)) return true;
if(c->wall == waChasm && c->land == laMemory) { wcol = 0x606000; return true; }
if(c->wall == waInvisibleFloor) return false;
// if(chasmgraph(c)) return true;
2019-03-02 00:19:52 +00:00
if(among(c->wall, waMirror, waCloud, waMineUnknown, waMineMine)) return true;
return false;
}
bool isSulphuric(eWall w) { return among(w, waSulphur, waSulphurC); }
2019-03-10 17:38:55 +00:00
// 'land color', but a bit twisted for Alchemist Lab
color_t lcolor(cell *c) {
if(isAlch(c->wall) && !c->item) return winf[c->wall].color;
return floorcolors[c->land];
}
color_t transcolor(cell *c, cell *c2, color_t wcol) {
2019-03-10 17:38:55 +00:00
color_t dummy;
if(isWall3(c2, dummy)) return 0;
if(c->land != c2->land) {
2019-03-10 17:38:55 +00:00
if(c>c2) return 0;
if(c->land == laBarrier) return darkena(lcolor(c2), 0, 0x40);
if(c2->land == laBarrier) return darkena(lcolor(c), 0, 0x40);
return darkena(gradient(lcolor(c), lcolor(c2), 0, 1, 2), 0, 0x40);
}
2019-03-10 17:38:55 +00:00
if(isAlch(c) && !c->item && (c2->item || !isAlch(c2))) return darkena(winf[c->wall].color, 0, 0x40);
if(c->wall == c2->wall) return 0;
if(isFire(c) && !isFire(c2)) return darkena(wcol, 0, 0x30);
2019-03-10 17:38:55 +00:00
if(c->wall == waChasm && c2->wall != waChasm) return 0x606060A0;
if(isWateryOrBoat(c) && !isWateryOrBoat(c2)) return 0x0000C060;
if(isSulphuric(c->wall) && !isSulphuric(c2->wall)) return darkena(winf[c->wall].color, 0, 0x40);
if(c->wall == waFloorA && c2->wall == waFloorB && !c->item && !c2->item) return darkena(0xFF00FF, 0, 0x80);
if(realred(c->wall) && realred(c2->wall) && c->wall != c2->wall) {
int l = snakelevel(c) - snakelevel(c2);
if(l > 0) return darkena(floorcolors[laRedRock], 0, 0x30 * l);
}
if(c->wall == waMagma && c2->wall != waMagma) return darkena(magma_color(lavatide(c, -1)/4), 0, 0x80);
return 0;
}
2019-02-27 15:29:30 +00:00
// how much should be the d-th wall darkened in 3D
int get_darkval(int d) {
2019-03-02 23:43:31 +00:00
const int darkval_hbt[9] = {0,2,2,0,6,6,8,8,0};
const int darkval_s12[12] = {0,1,2,3,4,5,0,1,2,3,4,5};
const int darkval_e6[6] = {0,4,6,0,4,6};
const int darkval_e12[12] = {0,4,6,0,4,6,0,4,6,0,4,6};
const int darkval_e14[14] = {0,0,0,4,6,4,6,0,0,0,6,4,6,4};
2019-03-13 12:12:49 +00:00
const int darkval_hh[14] = {0,0,0,1,1,1,2,2,2,3,3,3,1,0};
const int darkval_hrec[7] = {0,0,2,4,2,4,0};
2019-03-02 23:43:31 +00:00
if(sphere) return darkval_s12[d];
if(euclid && S7 == 6) return darkval_e6[d];
if(euclid && S7 == 12) return darkval_e12[d];
if(euclid && S7 == 14) return darkval_e14[d];
2019-03-13 12:12:49 +00:00
if(geometry == gHoroHex) return darkval_hh[d];
if(geometry == gHoroRec) return darkval_hrec[d];
2019-03-02 23:43:31 +00:00
if(binarytiling) return darkval_hbt[d];
if(hyperbolic && S7 == 6) return darkval_e6[d];
if(hyperbolic && S7 == 12) return darkval_s12[d];
return 0;
2019-02-27 15:29:30 +00:00
}
2019-03-04 17:02:38 +00:00
void drawBoat(cell *c, const transmatrix* Vboat, transmatrix& Vboat0, transmatrix& V) {
double footphase;
if(c == cwt.at && hide_player()) return;
2019-03-04 17:02:38 +00:00
bool magical = items[itOrbWater] && (isPlayerOn(c) || (isFriendly(c) && items[itOrbEmpathy]));
int outcol = magical ? watercolor(0) : 0xC06000FF;
int incol = magical ? 0x0060C0FF : 0x804000FF;
bool nospin = false;
if(DIM == 3) {
Vboat0 = V;
nospin = c->wall == waBoat && applyAnimation(c, Vboat0, footphase, LAYER_BOAT);
if(!nospin) Vboat0 = rgpushxto0(tC0(V));
else Vboat0 = cspin(0, 2, M_PI) * Vboat0;
queuepolyat(mscale(Vboat0, scalefactor/2), shBoatOuter, outcol, PPR::BOATLEV2);
queuepolyat(mscale(Vboat0, scalefactor/2-0.01), shBoatInner, incol, PPR::BOATLEV2);
return;
}
Vboat = &(Vboat0 = *Vboat);
if(wmspatial && c->wall == waBoat) {
nospin = c->wall == waBoat && applyAnimation(c, Vboat0, footphase, LAYER_BOAT);
if(!nospin) Vboat0 = Vboat0 * ddspin(c, c->mondir, M_PI);
queuepolyat(Vboat0, shBoatOuter, outcol, PPR::BOATLEV);
Vboat = &(Vboat0 = V);
}
if(c->wall == waBoat) {
nospin = applyAnimation(c, Vboat0, footphase, LAYER_BOAT);
}
if(!nospin)
Vboat0 = Vboat0 * ddspin(c, c->mondir, M_PI);
else {
transmatrix Vx;
if(applyAnimation(c, Vx, footphase, LAYER_SMALL))
animations[LAYER_SMALL][c].footphase = 0;
}
if(wmspatial)
queuepolyat(mscale(Vboat0, (geom3::LAKE+1)/2), shBoatOuter, outcol, PPR::BOATLEV2);
queuepoly(Vboat0, shBoatOuter, outcol);
queuepoly(Vboat0, shBoatInner, incol);
}
2017-07-10 18:47:38 +00:00
void drawcell(cell *c, transmatrix V, int spinv, bool mirrored) {
2016-01-02 10:09:13 +00:00
2018-11-01 17:59:25 +00:00
cells_drawn++;
2018-01-05 16:30:03 +00:00
#if CAP_TEXTURE
2017-12-21 13:05:07 +00:00
if(texture::saving) {
2018-03-17 20:12:46 +00:00
texture::config.apply(c, V, 0xFFFFFFFF);
2018-06-17 10:03:05 +00:00
draw_qfi(c, V, 0xFFFFFFFF);
2017-12-21 13:05:07 +00:00
return;
}
if((cmode & sm::DRAW) && texture::config.tstate == texture::tsActive && !mouseout() && c)
mapeditor::draw_texture_ghosts(c, V);
2018-01-05 16:30:03 +00:00
#endif
2018-05-07 18:13:56 +00:00
2017-07-16 21:00:55 +00:00
bool orig = false;
if(!inmirrorcount) {
transmatrix& gm = gmatrix[c];
orig =
2019-02-21 17:46:53 +00:00
gm[DIM][DIM] == 0 ? true :
euwrap ? hypot(gm[0][DIM], gm[1][DIM]) >= hypot(V[0][DIM], V[1][DIM]) :
sphereflipped() ? fabs(gm[DIM][DIM]-1) <= fabs(V[DIM][DIM]-1) :
fabs(gm[DIM][DIM]-1) >= fabs(V[DIM][DIM]-1) - 1e-8;
2016-01-02 10:09:13 +00:00
2017-07-16 21:00:55 +00:00
if(orig) gm = V;
}
2018-05-07 18:13:56 +00:00
if(just_gmatrix) return;
#if CAP_SHAPES
2018-05-07 18:13:56 +00:00
set_floor(shFloor);
2019-02-17 17:41:40 +00:00
#endif
2018-05-07 18:13:56 +00:00
ivoryz = isGravityLand(c->land);
2016-08-26 09:58:03 +00:00
// if(behindsphere(V)) return;
2017-03-23 10:53:57 +00:00
if(callhandlers(false, hooks_drawcell, c, V)) return;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
ld dist0 = hdist0(tC0(V)) - 1e-6;
2018-11-01 17:59:25 +00:00
if(vid.use_smart_range) detaillevel = 2;
else if(dist0 < geom3::highdetail) detaillevel = 2;
2017-07-10 18:47:38 +00:00
else if(dist0 < geom3::middetail) detaillevel = 1;
else detaillevel = 0;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
#ifdef BUILDZEBRA
if(c->type == 6 && c->tmp > 0) {
int i = c->tmp;
zebra(cellwalker(c, i&15), 1, i>>4, "", 0);
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
c->item = eItem(c->heat / 4);
buildAutomatonRule(c);
#endif
2016-01-02 10:09:13 +00:00
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
viewBuggyCells(c,V);
2019-02-17 17:41:40 +00:00
#endif
2016-01-02 10:09:13 +00:00
2019-03-13 14:26:22 +00:00
if(conformal::on || inHighQual || DIM == 3 || sightrange_bonus > gamerange_bonus) checkTide(c);
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
// save the player's view center
if(isPlayerOn(c) && !shmup::on) {
playerfound = true;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(multi::players > 1) {
for(int i=0; i<numplayers(); i++)
if(playerpos(i) == c) {
2019-02-26 20:11:50 +00:00
playerV = V * ddspin(c, multi::player[i].spin, FACE3);
2017-07-10 18:47:38 +00:00
if(multi::player[i].mirrored) playerV = playerV * Mirror;
if(multi::player[i].mirrored == mirrored)
multi::whereis[i] = playerV;
}
}
else {
2019-02-26 20:11:50 +00:00
playerV = V * ddspin(c, cwt.spin, FACE3);
2017-07-10 18:47:38 +00:00
if(cwt.mirrored) playerV = playerV * Mirror;
if(orig) cwtV = playerV;
}
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
if(1) {
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
hyperpoint VC0 = tC0(V);
2017-03-23 10:53:57 +00:00
2017-07-16 21:00:55 +00:00
if(inmirrorcount) ;
2019-02-26 11:28:54 +00:00
else if(DIM == 3) ;
2017-07-16 21:00:55 +00:00
else if(intval(mouseh, VC0) < modist) {
2017-07-10 18:47:38 +00:00
modist2 = modist; mouseover2 = mouseover;
modist = intval(mouseh, VC0);
mouseover = c;
}
else if(intval(mouseh, VC0) < modist2) {
modist2 = intval(mouseh, VC0);
mouseover2 = c;
}
2017-07-16 21:00:55 +00:00
if(!euclid) {
2019-02-21 17:46:53 +00:00
double dfc = euclid ? intval(VC0, C0) : VC0[DIM];
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(dfc < centdist) {
centdist = dfc;
centerover = cellwalker(c, spinv, mirrored);
2017-07-10 18:47:38 +00:00
}
}
int orbrange = (items[itRevolver] ? 3 : 2);
if(c->cpdist <= orbrange) if(multi::players > 1 || multi::alwaysuse)
for(int i=0; i<multi::players; i++) if(multi::playerActive(i)) {
double dfc = intval(VC0, tC0(multi::crosscenter[i]));
if(dfc < multi::ccdist[i] && celldistance(playerpos(i), c) <= orbrange) {
multi::ccdist[i] = dfc;
multi::ccat[i] = c;
}
}
2017-03-23 10:53:57 +00:00
2017-07-16 21:00:55 +00:00
if(inmirror(c)) {
if(inmirrorcount >= 10) return;
cellwalker cw(c, 0, mirrored);
cw = mirror::reflect(cw);
int cmc = (cw.mirrored == mirrored) ? 2 : 1;
inmirrorcount += cmc;
if(cw.mirrored != mirrored) V = V * Mirror;
if(cw.spin) V = V * spin(2*M_PI*cw.spin/cw.at->type);
drawcell(cw.at, V, 0, cw.mirrored);
2017-07-16 21:00:55 +00:00
inmirrorcount -= cmc;
return;
}
// color_t col = 0xFFFFFF - 0x20 * c->maxdist - 0x2000 * c->cpdist;
2016-08-26 09:58:03 +00:00
if(!buggyGeneration && c->mpdist > 8 && !cheater && !autocheat) return; // not yet generated
/* if(!buggyGeneration && c->mpdist > 7 && !cheater) {
int cd = c->mpdist;
string label = its(cd);
int dc = distcolors[cd&7];
queuestr(V, (cd > 9 ? .6 : 1) * .2, label, 0xFF000000 + dc, 1);
} */
2017-07-10 18:47:38 +00:00
#if CAP_SHAPES
2017-07-12 17:50:39 +00:00
if(c->land == laNone && (cmode & sm::MAP)) {
2017-07-10 18:47:38 +00:00
queuepoly(V, shTriangle, 0xFF0000FF);
}
#endif
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
char ch = winf[c->wall].glyph;
color_t wcol, fcol, asciicol;
2017-07-10 18:47:38 +00:00
setcolors(c, wcol, fcol);
2017-07-16 21:00:55 +00:00
if(inmirror(c)) {
// for debugging
if(c->land == laMirrored) fcol = 0x008000;
if(c->land == laMirrorWall2) fcol = 0x800000;
if(c->land == laMirrored2) fcol = 0x000080;
}
for(int k=0; k<inmirrorcount; k++)
wcol = gradient(wcol, 0xC0C0FF, 0, 0.2, 1),
fcol = gradient(fcol, 0xC0C0FF, 0, 0.2, 1);
2017-07-10 18:47:38 +00:00
// addaura(tC0(V), wcol);
color_t zcol = fcol;
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
if(peace::on && peace::hint && c->land != laTortoise) {
int d =
(c->land == laCamelot || (c->land == laCaribbean && celldistAlt(c) <= 0) || (c->land == laPalace && celldistAlt(c))) ? celldistAlt(c):
celldist(c);
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
int dc =
0x10101 * (127 + int(127 * sintick(200, d*.75/M_PI)));
2017-07-10 18:47:38 +00:00
wcol = gradient(wcol, dc, 0, .3, 1);
fcol = gradient(fcol, dc, 0, .3, 1);
}
2017-07-16 21:00:55 +00:00
if(c->land == laMirrored || c->land == laMirrorWall2 || c->land == laMirrored2) {
string label = its(c->landparam);
queuestr(V, 1 * .2, label, 0xFFFFFFFF, 1);
}
2018-09-21 17:51:13 +00:00
if(viewdists) do_viewdist(c, V, wcol, fcol);
2018-09-13 18:38:06 +00:00
2017-11-06 20:18:40 +00:00
if(cmode & sm::TORUSCONFIG) {
using namespace torusconfig;
string label;
bool small;
if(tmflags() & TF_SINGLE) {
int cd = torus_cx * dx + torus_cy * newdy;
cd %= newqty; if(cd<0) cd += newqty;
label = its(cd);
small = cd;
}
else {
small = true;
label = its(torus_cx) + "," + its(torus_cy);
}
queuestr(V, small ? .2 : .6, label, small ? 0xFFFFFFD0 : 0xFFFF0040, 1);
2017-11-06 20:18:40 +00:00
}
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
asciicol = wcol;
2017-03-23 10:53:57 +00:00
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
if(c->land == laNone && c->wall == waNone)
queuepoly(V, shTriangle, 0xFFFF0000);
#endif
2017-07-10 18:47:38 +00:00
if(c->wall == waThumperOn) {
ld ds = fractick(160);
2017-07-10 18:47:38 +00:00
for(int u=0; u<5; u++) {
ld rad = hexf * (.3 * (u + ds));
2017-07-10 18:47:38 +00:00
int tcol = darkena(gradient(forecolor, backcolor, 0, rad, 1.5 * hexf), 0, 0xFF);
2018-08-01 09:07:22 +00:00
PRING(a)
curvepoint(V*xspinpush0(a * M_PI / S42, rad));
2018-08-28 12:27:23 +00:00
queuecurve(tcol, 0, PPR::LINE);
2017-03-23 10:53:57 +00:00
}
}
2017-07-10 18:47:38 +00:00
// bool dothept = false;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
/* if(pseudohept(c) && vid.darkhepta) {
col = gradient(0, col, 0, 0.75, 1);
} */
eItem it = c->item;
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
bool hidden = itemHidden(c);
bool hiddens = itemHiddenFromSight(c);
if(conformal::includeHistory && conformal::infindhistory.count(c)) {
2017-07-10 18:47:38 +00:00
hidden = true;
hiddens = false;
2016-01-02 10:09:13 +00:00
}
2017-07-12 17:50:39 +00:00
if(hiddens && !(cmode & sm::MAP))
2017-07-10 18:47:38 +00:00
it = itNone;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
int icol = 0, moncol = 0xFF00FF;
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
if(it)
ch = iinf[it].glyph, asciicol = icol = iinf[it].color;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(c->monst) {
ch = minf[c->monst].glyph, moncol = minf[c->monst].color;
if(c->monst == moMutant) {
// root coloring
if(c->stuntime != mutantphase)
moncol =
gradient(0xC00030, 0x008000, 0, (c->stuntime-mutantphase) & 15, 15);
}
if(isMetalBeast(c->monst) && c->stuntime)
moncol >>= 1;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(c->monst == moSlime) {
moncol = winf[c->wall].color;
moncol |= (moncol>>1);
}
asciicol = moncol;
2017-07-10 18:47:38 +00:00
if(isDragon(c->monst) || isKraken(c->monst)) if(!c->hitpoints)
asciicol = 0x505050;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(c->monst == moTortoise)
asciicol = tortoise::getMatchColor(tortoise::getb(c));
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(c->monst != moMutant) for(int k=0; k<c->stuntime; k++)
asciicol = ((asciicol & 0xFEFEFE) >> 1) + 0x101010;
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
if(c->cpdist == 0 && mapeditor::drawplayer) {
ch = '@';
if(!mmitem) asciicol = moncol = cheater ? 0xFF3030 : 0xD0D0D0;
2016-08-26 09:58:03 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(c->ligon) {
int tim = ticks - lightat;
if(tim > 1000) tim = 800;
if(elec::havecharge && tim > 400) tim = 400;
for(int t=0; t<c->type; t++) if(c->move(t) && c->move(t)->ligon) {
ld hdir = displayspin(c, t);
2017-07-10 18:47:38 +00:00
int lcol = darkena(gradient(iinf[itOrbLightning].color, 0, 0, tim, 1100), 0, 0xFF);
queueline(V*xspinpush0(ticks * M_PI / S42, hexf/2), V*xspinpush0(hdir, crossf), lcol, 2 + vid.linequality);
2015-08-08 13:57:52 +00:00
}
}
2017-10-28 08:04:28 +00:00
int ctype = c->type;
#if CAP_SHAPES
2017-10-28 08:04:28 +00:00
int ct6 = ctof(c);
2019-02-17 17:41:40 +00:00
#endif
2017-07-10 18:47:38 +00:00
bool error = false;
2017-03-23 10:53:57 +00:00
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
chasmg = chasmgraph(c);
#endif
2015-08-08 13:57:52 +00:00
2017-10-17 10:54:59 +00:00
int fd = getfd(c);
#if CAP_SHAPES
int flooralpha = 255;
2019-02-17 17:41:40 +00:00
#endif
2019-01-14 21:56:52 +00:00
#if CAP_EDIT && CAP_TEXTURE
if((cmode & sm::DRAW) && mapeditor::drawcell && mapeditor::drawcellShapeGroup() == mapeditor::sgFloor && texture::config.tstate != texture::tsActive)
flooralpha = 0xC0;
#endif
2017-10-08 09:12:03 +00:00
if(c->wall == waMagma) fd = 0;
2017-07-10 18:47:38 +00:00
poly_outline = OUTLINE_DEFAULT;
2018-08-28 12:27:23 +00:00
2017-07-10 18:47:38 +00:00
int sl = snakelevel(c);
2017-03-23 10:53:57 +00:00
2019-02-17 17:41:40 +00:00
transmatrix Vd0, Vboat0;
2017-07-10 18:47:38 +00:00
const transmatrix *Vdp =
(!wmspatial) ? &V :
sl ? &(Vd0= mscale(V, geom3::SLEV[sl])) :
highwall(c) ? &(Vd0= mscale(V, (1+geom3::WALL)/2)) :
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
(chasmg==1) ? &(Vd0 = mscale(V, geom3::LAKE)) :
#endif
2017-07-10 18:47:38 +00:00
&V;
2016-08-26 09:58:03 +00:00
#if CAP_SHAPES
2019-02-17 17:41:40 +00:00
transmatrix Vf0;
2017-07-10 18:47:38 +00:00
const transmatrix& Vf = (chasmg && wmspatial) ? (Vf0=mscale(V, geom3::BOTTOM)) : V;
2019-02-17 17:41:40 +00:00
#endif
2017-07-10 18:47:38 +00:00
const transmatrix *Vboat = &(*Vdp);
2018-07-09 18:38:20 +00:00
shmup::drawMonster(V, c, Vboat, Vboat0, Vdp);
2017-06-18 16:51:46 +00:00
2018-08-28 12:27:23 +00:00
poly_outline = OUTLINE_DEFAULT;
2019-02-17 17:41:40 +00:00
if(!wmascii) {
2017-03-23 10:53:57 +00:00
#if CAP_EDIT
auto si = patterns::getpatterninfo0(c);
if(drawing_usershape_on(c, mapeditor::sgFloor))
mapeditor::drawtrans = V * applyPatterndir(c, si);
#endif
#if CAP_SHAPES
2019-02-17 17:41:40 +00:00
// floor
bool eoh = euclid || !BITRUNCATED;
2017-07-10 18:47:38 +00:00
if(c->wall == waChasm) {
zcol = 0;
int rd = rosedist(c);
if(rd == 1)
2018-05-07 18:13:56 +00:00
draw_floorshape(c, V, shRoseFloor, 0x80406020);
if(rd == 2)
2018-05-07 18:13:56 +00:00
draw_floorshape(c, V, shRoseFloor, 0x80406040);
2017-07-10 18:47:38 +00:00
if(c->land == laZebra) fd++;
if(c->land == laHalloween && !wmblack) {
transmatrix Vdepth = wmspatial ? mscale(V, geom3::BOTTOM) : V;
2018-09-10 15:56:37 +00:00
draw_floorshape(c, Vdepth, shFullFloor, darkena(firecolor(0, 10), 0, 0xDF), PPR::LAKEBOTTOM);
2017-07-10 18:47:38 +00:00
}
}
2018-05-07 18:13:56 +00:00
2017-07-22 23:33:27 +00:00
#if CAP_EDIT
else if(mapeditor::haveUserShape(mapeditor::sgFloor, si.id)) {
qfi.usershape = si.id;
qfi.spin = applyPatterndir(c, si);
}
#endif
2018-05-07 18:13:56 +00:00
else if(patterns::whichShape == '7')
set_floor(shBigHepta);
2017-07-10 18:47:38 +00:00
2018-05-07 18:13:56 +00:00
else if(patterns::whichShape == '8')
set_floor(shTriheptaFloor);
2017-07-10 18:47:38 +00:00
2018-05-07 18:13:56 +00:00
else if(patterns::whichShape == '6')
set_floor(shBigTriangle);
2018-06-25 21:01:39 +00:00
else if(among(patterns::whichShape, '9', '^'))
2018-06-25 21:01:39 +00:00
set_floor(shFullFloor);
2017-12-09 01:20:10 +00:00
#if CAP_TEXTURE
2018-03-17 20:12:46 +00:00
else if(texture::config.apply(c, Vf, darkena(fcol, fd, 0xFF))) ;
#endif
2017-07-10 18:47:38 +00:00
2017-07-16 21:00:55 +00:00
else if(c->land == laMirrorWall) {
int d = mirror::mirrordir(c);
bool onleft = c->type == 7;
if(c->type == 7 && c->barleft == laMirror)
onleft = !onleft;
if(c->type == 6 && d != -1 && c->move(d)->barleft == laMirror)
2017-07-16 21:00:55 +00:00
onleft = !onleft;
if(PURE) onleft = !onleft;
2017-07-16 21:00:55 +00:00
if(d == -1) {
for(d=0; d<c->type; d++)
if(c->move(d) && c->modmove(d+1) && c->move(d)->land == laMirrorWall && c->modmove(d+1)->land == laMirrorWall)
2017-07-16 21:00:55 +00:00
break;
qfi.spin = ddspin(c, d, 0);
transmatrix V2 = V * qfi.spin;
if(!wmblack) for(int d=0; d<c->type; d++) {
2017-07-16 21:00:55 +00:00
inmirrorcount+=d;
2018-08-28 12:27:23 +00:00
queuepolyat(V2 * spin(d*M_PI/S3), shHalfFloor[2], darkena(fcol, fd, 0xFF), PPR::FLOORa);
2017-07-16 21:00:55 +00:00
inmirrorcount-=d;
2017-07-22 23:33:27 +00:00
}
if(wmspatial) {
const int layers = 2 << detaillevel;
for(int z=1; z<layers; z++)
2018-08-28 12:27:23 +00:00
queuepolyat(mscale(V2, zgrad0(0, geom3::actual_wall_height(), z, layers)), shHalfMirror[2], 0xC0C0C080, PPR::WALL3+z-layers);
2017-07-16 21:00:55 +00:00
}
2017-07-22 23:33:27 +00:00
else
2018-08-28 12:27:23 +00:00
queuepolyat(V2, shHalfMirror[2], 0xC0C0C080, PPR::WALL3);
2017-07-16 21:00:55 +00:00
}
else {
qfi.spin = ddspin(c, d, M_PI);
2017-07-16 21:00:55 +00:00
transmatrix V2 = V * qfi.spin;
2017-07-22 23:33:27 +00:00
if(!wmblack) {
inmirrorcount++;
2018-08-28 12:27:23 +00:00
queuepolyat(mirrorif(V2, !onleft), shHalfFloor[ct6], darkena(fcol, fd, 0xFF), PPR::FLOORa);
2017-07-22 23:33:27 +00:00
inmirrorcount--;
2018-08-28 12:27:23 +00:00
queuepolyat(mirrorif(V2, onleft), shHalfFloor[ct6], darkena(fcol, fd, 0xFF), PPR::FLOORa);
2017-07-22 23:33:27 +00:00
}
if(wmspatial) {
const int layers = 2 << detaillevel;
for(int z=1; z<layers; z++)
2018-08-28 12:27:23 +00:00
queuepolyat(mscale(V2, zgrad0(0, geom3::actual_wall_height(), z, layers)), shHalfMirror[ct6], 0xC0C0C080, PPR::WALL3+z-layers);
2017-07-22 23:33:27 +00:00
}
else
2018-08-28 12:27:23 +00:00
queuepolyat(V2, shHalfMirror[ct6], 0xC0C0C080, PPR::WALL3);
2017-07-16 21:00:55 +00:00
}
}
2017-07-10 18:47:38 +00:00
else if(c->land == laWineyard && cellHalfvine(c)) {
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
int i =-1;
for(int t=0;t<6; t++) if(c->move(t) && c->move(t)->wall == c->wall)
2017-07-10 18:47:38 +00:00
i = t;
2016-08-26 09:58:03 +00:00
qfi.spin = ddspin(c, i, M_PI/S3);
2017-07-10 18:47:38 +00:00
transmatrix V2 = V * qfi.spin;
if(wmspatial && wmescher) {
2018-05-07 18:13:56 +00:00
set_floor(shSemiFeatherFloor[0]);
2017-07-10 18:47:38 +00:00
int dk = 1;
int vcol = winf[waVinePlant].color;
2018-08-28 12:27:23 +00:00
draw_qfi(c, mscale(V2, geom3::WALL), darkena(vcol, dk, 0xFF), PPR::WALL3A);
2018-05-07 18:13:56 +00:00
escherSidewall(c, SIDE_WALL, V2, darkena(gradient(0, vcol, 0, .8, 1), dk, 0xFF));
queuepoly(V2, shSemiFeatherFloor[1], darkena(fcol, dk, 0xFF));
set_floor(shFeatherFloor);
2017-07-10 18:47:38 +00:00
}
else if(wmspatial) {
2018-05-07 18:13:56 +00:00
floorshape& shar = *(wmplain ? (floorshape*)&shFloor : (floorshape*)&shFeatherFloor);
2017-07-10 18:47:38 +00:00
2018-05-07 18:13:56 +00:00
set_floor(shar);
2017-07-10 18:47:38 +00:00
int vcol = winf[waVinePlant].color;
int vcol2 = gradient(0, vcol, 0, .8, 1);
transmatrix Vdepth = mscale(V2, geom3::WALL);
2015-08-08 13:57:52 +00:00
2018-08-28 12:27:23 +00:00
queuepolyat(Vdepth, shSemiFloor[0], darkena(vcol, fd, 0xFF), PPR::WALL3A);
{dynamicval<color_t> p(poly_outline, OUTLINE_TRANS); queuepolyat(V2 * spin(M_PI*2/3), shSemiFloorShadow, SHADOW_WALL, PPR::WALLSHADOW); }
2018-08-28 12:27:23 +00:00
queuepolyat(V2, shSemiFloorSide[SIDE_WALL], darkena(vcol, fd, 0xFF), PPR::WALL3A-2+away(V2));
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(validsidepar[SIDE_WALL]) forCellIdEx(c2, j, c) {
int dis = i-j;
dis %= 6;
if(dis<0) dis += 6;
if(dis != 1 && dis != 5) continue;
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, j, SIDE_WALL, V, darkena(vcol2, fd, 0xFF))) break;
2017-07-10 18:47:38 +00:00
}
}
2018-05-07 18:13:56 +00:00
else {
2017-07-10 18:47:38 +00:00
hpcshape *shar = shSemiFeatherFloor;
if(wmblack) shar = shSemiBFloor;
if(wmplain) shar = shSemiFloor;
2018-05-07 18:13:56 +00:00
queuepoly(V2, shar[0], darkena(winf[waVinePlant].color, fd, 0xFF));
2017-07-10 18:47:38 +00:00
2018-05-07 18:13:56 +00:00
set_floor(qfi.spin, shar[1]);
2017-07-10 18:47:38 +00:00
}
}
2015-08-08 13:57:52 +00:00
2018-05-07 18:13:56 +00:00
else if(c->land == laReptile || c->wall == waReptile)
set_reptile_floor(c, Vf, fcol);
2017-07-10 18:47:38 +00:00
else if(wmblack == 1 && c->wall == waMineOpen && vid.grid)
;
else if(wmblack) {
2018-05-07 18:13:56 +00:00
set_floor(shBFloor[ct6]);
2017-07-10 18:47:38 +00:00
int rd = rosedist(c);
if(rd == 1)
2018-05-07 18:13:56 +00:00
queuepoly(Vf, shHeptaMarker, darkena(fcol, 0, 0x80));
2017-07-10 18:47:38 +00:00
else if(rd == 2)
2018-05-07 18:13:56 +00:00
queuepoly(Vf, shHeptaMarker, darkena(fcol, 0, 0x40));
2017-07-10 18:47:38 +00:00
}
2018-05-07 18:13:56 +00:00
else if(isWarped(c) || is_nice_dual(c))
set_maywarp_floor(c);
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
else if(wmplain) {
2018-05-07 18:13:56 +00:00
set_floor(shFloor);
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
else if(randomPatternsMode && c->land != laBarrier && !isWarped(c->land)) {
int j = (randompattern[c->land]/5) % 15;
int k = randompattern[c->land] % RPV_MODULO;
int k7 = randompattern[c->land] % 7;
2018-05-07 18:13:56 +00:00
if(k == RPV_ZEBRA && k7 < 2) set_zebrafloor(c);
else if(k == RPV_EMERALD && k7 == 0) set_emeraldfloor(c);
else if(k == RPV_CYCLE && k7 < 4) set_towerfloor(c, celldist);
2017-07-10 18:47:38 +00:00
else switch(j) {
2018-05-07 18:13:56 +00:00
case 0: set_floor(shCloudFloor); break;
case 1: set_floor(shFeatherFloor); break;
case 2: set_floor(shStarFloor); break;
case 3: set_floor(shTriFloor); break;
case 4: set_floor(shSStarFloor); break;
case 5: set_floor(shOverFloor); break;
case 6: set_floor(shFeatherFloor); break;
case 7: set_floor(shDemonFloor); break;
case 8: set_floor(shCrossFloor); break;
case 9: set_floor(shMFloor); break;
case 10: set_floor(shCaveFloor); break;
case 11: set_floor(shPowerFloor); break;
case 12: set_floor(shDesertFloor); break;
case 13: set_floor(shChargedFloor); break;
case 14: set_floor(shLavaFloor); break;
2017-07-10 18:47:38 +00:00
}
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
// else if(c->land == laPrairie && !eoh && allemptynear(c) && fieldpattern::getflowerdist(c) <= 1)
// queuepoly(Vf, shLeafFloor[ct6], darkena(fcol, fd, 0xFF));
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
/* else if(c->land == laPrairie && prairie::isriver(c))
set_towerfloor(Vf, c, darkena(fcol, fd, 0xFF),
2017-07-10 18:47:38 +00:00
prairie::isleft(c) ? river::towerleft : river::towerright); */
else switch(c->land) {
case laPrairie:
2018-05-07 18:13:56 +00:00
case laAlchemist:
set_floor(shCloudFloor);
break;
2018-05-07 18:13:56 +00:00
case laJungle:
case laWineyard:
2018-05-07 18:13:56 +00:00
set_floor(shFeatherFloor);
break;
case laZebra:
2018-05-07 18:13:56 +00:00
set_zebrafloor(c);
break;
case laMountain:
2018-05-07 18:13:56 +00:00
set_towerfloor(c, euclid ? celldist : c->master->alt ? celldistAltPlus : celldist);
break;
case laEmerald:
2018-05-07 18:13:56 +00:00
set_emeraldfloor(c);
break;
case laRlyeh:
case laTemple:
2018-05-07 18:13:56 +00:00
set_floor(shTriFloor);
break;
case laVolcano:
2018-12-25 18:25:09 +00:00
case laVariant:
2018-05-07 18:13:56 +00:00
set_floor(shLavaFloor);
break;
case laRose:
2018-05-07 18:13:56 +00:00
set_floor(shRoseFloor);
break;
case laTortoise:
2018-05-07 18:13:56 +00:00
set_floor(shTurtleFloor);
break;
case laBurial: case laRuins:
2018-05-07 18:13:56 +00:00
set_floor(shBarrowFloor);
break;
case laTrollheim:
2018-05-07 18:13:56 +00:00
set_floor(shTrollFloor);
break;
2018-05-07 18:13:56 +00:00
/*case laMountain:
2018-05-07 18:13:56 +00:00
set_floor(FEATHERFLOOR);
break; */
case laGraveyard:
2018-05-07 18:13:56 +00:00
set_floor(shCrossFloor);
break;
case laMotion:
2018-05-07 18:13:56 +00:00
set_floor(shMFloor);
break;
case laWhirlwind:
2018-05-07 18:13:56 +00:00
case laEFire: case laEAir: case laEWater: case laEEarth: case laElementalWall:
set_floor(shNewFloor);
break;
case laHell:
2018-05-07 18:13:56 +00:00
set_floor(shDemonFloor);
break;
case laIce: case laBlizzard:
2018-05-07 18:13:56 +00:00
set_floor(shStarFloor);
break;
case laSwitch:
2018-05-07 18:13:56 +00:00
set_floor(shSwitchFloor);
if(ctof(c) && STDVAR && !archimedean && !binarytiling) for(int i=0; i<c->type; i++)
queuepoly(Vf * ddspin(c, i, M_PI/S7) * xpush(rhexf), shSwitchDisk, darkena(minf[active_switch()].color, fd, 0xFF));
break;
case laStorms:
2018-05-07 18:13:56 +00:00
set_floor(shChargedFloor);
break;
case laWildWest:
2018-05-07 18:13:56 +00:00
set_floor(shSStarFloor);
break;
case laPower:
2018-05-07 18:13:56 +00:00
set_floor(shPowerFloor);
break;
2018-05-07 18:13:56 +00:00
case laCaves:
2018-05-07 18:13:56 +00:00
case laLivefjord:
case laDeadCaves:
set_floor(shCaveFloor);
break;
2018-05-07 18:13:56 +00:00
case laDryForest:
2019-03-16 21:17:48 +00:00
set_floor(DIM == 3 ? shFeatherFloor : shDesertFloor);
break;
case laDesert:
2018-05-07 18:13:56 +00:00
case laRedRock: case laSnakeNest:
case laCocytus:
set_floor(shDesertFloor);
break;
2015-08-08 13:57:52 +00:00
case laBull:
2018-05-07 18:13:56 +00:00
set_floor(shButterflyFloor);
break;
case laCaribbean: case laOcean: case laOceanWall: case laWhirlpool:
2018-05-07 18:13:56 +00:00
set_floor(shCloudFloor);
break;
case laKraken:
case laDocks:
2018-05-07 18:13:56 +00:00
set_floor(shFullFloor);
break;
2018-05-07 18:13:56 +00:00
case laPalace: case laTerracotta:
set_floor(shPalaceFloor);
break;
2018-05-07 18:13:56 +00:00
case laDragon:
set_floor(shDragonFloor);
break;
2018-05-07 18:13:56 +00:00
case laOvergrown: case laClearing: case laHauntedWall: case laHaunted: case laHauntedBorder:
set_floor(shOverFloor);
break;
2018-05-07 18:13:56 +00:00
case laMercuryRiver: {
if(eoh)
2018-05-07 18:13:56 +00:00
set_floor(shFloor);
2017-10-17 10:54:59 +00:00
else {
int bridgedir = -1;
if(c->type == 6) {
for(int i=1; i<c->type; i+=2)
if(pseudohept(c->modmove(i-1)) && c->modmove(i-1)->land == laMercuryRiver)
if(pseudohept(c->modmove(i+1)) && c->modmove(i+1)->land == laMercuryRiver)
bridgedir = i;
}
if(bridgedir == -1)
2018-05-07 18:13:56 +00:00
set_floor(shPalaceFloor);
else {
transmatrix bspin = ddspin(c, bridgedir);
2018-05-07 18:13:56 +00:00
set_floor(bspin, shMercuryBridge[0]);
// only needed in one direction
if(c < c->move(bridgedir)) {
bspin = Vf * bspin;
queuepoly(bspin, shMercuryBridge[1], darkena(fcol, fd+1, 0xFF));
if(wmspatial) {
2018-08-28 12:27:23 +00:00
queuepolyat(mscale(bspin, geom3::LAKE), shMercuryBridge[1], darkena(gradient(0, winf[waMercury].color, 0, 0.8,1), 0, 0x80), PPR::LAKELEV);
queuepolyat(mscale(bspin, geom3::BOTTOM), shMercuryBridge[1], darkena(0x202020, 0, 0xFF), PPR::LAKEBOTTOM);
}
2017-10-17 18:41:17 +00:00
}
2017-10-17 10:54:59 +00:00
}
}
2018-02-11 22:30:00 +00:00
break;
2017-10-17 10:54:59 +00:00
}
case laHive:
if(c->wall != waFloorB && c->wall != waFloorA && c->wall != waMirror && c->wall != waCloud) {
2018-05-07 18:13:56 +00:00
fd = 1;
set_floor(shFloor);
if(c->wall != waMirror && c->wall != waCloud)
2018-08-28 12:27:23 +00:00
draw_floorshape(c, V, shMFloor, darkena(fcol, 2, 0xFF), PPR::FLOORa);
if(c->wall != waMirror && c->wall != waCloud)
2018-08-28 12:27:23 +00:00
draw_floorshape(c, V, shMFloor2, darkena(fcol, fcol==wcol ? 1 : 2, 0xFF), PPR::FLOORb);
}
else
2018-05-07 18:13:56 +00:00
set_floor(shFloor);
break;
case laEndorian:
if(c->wall == waTrunk)
2018-05-07 18:13:56 +00:00
set_floor(shFloor);
else if(c->wall == waCanopy || c->wall == waSolidBranch || c->wall == waWeakBranch)
2018-05-07 18:13:56 +00:00
set_floor(shFeatherFloor);
else
2018-05-07 18:13:56 +00:00
set_towerfloor(c);
break;
2018-12-25 18:25:09 +00:00
case laIvoryTower: case laDungeon: case laWestWall:
2018-05-07 18:13:56 +00:00
set_towerfloor(c);
break;
2018-10-25 00:43:14 +00:00
case laBrownian:
if(among(c->wall, waSea, waBoat))
set_floor(shCloudFloor);
else
2018-12-25 18:25:09 +00:00
set_floor(shTrollFloor);
2018-10-25 00:43:14 +00:00
break;
2017-07-10 18:47:38 +00:00
default:
2018-05-07 18:13:56 +00:00
set_floor(shFloor);
2017-07-10 18:47:38 +00:00
}
2018-05-07 18:13:56 +00:00
// actually draw the floor
if(chasmg == 2) ;
else if(chasmg && wmspatial && detaillevel == 0) {
2018-08-28 12:27:23 +00:00
draw_qfi(c, (*Vdp), darkena(fcol, fd, 0x80), PPR::LAKELEV);
}
2018-05-07 18:13:56 +00:00
else if(chasmg && wmspatial) {
color_t col = c->land == laCocytus ? 0x080808FF : 0x101010FF;
2018-05-07 18:13:56 +00:00
if(qfi.fshape == &shCloudFloor)
set_floor(shCloudSeabed);
2018-12-25 18:25:09 +00:00
else if(qfi.fshape == &shLavaFloor)
set_floor(shLavaSeabed);
2018-05-07 18:13:56 +00:00
else if(qfi.fshape == &shFloor)
set_floor(shFullFloor);
else if(qfi.fshape == &shCaveFloor)
set_floor(shCaveSeabed);
2018-08-28 12:27:23 +00:00
draw_qfi(c, mscale(V, geom3::BOTTOM), col, PPR::LAKEBOTTOM);
2018-05-07 18:13:56 +00:00
int fd0 = fd ? fd-1 : 0;
2018-08-28 12:27:23 +00:00
draw_qfi(c, (*Vdp), darkena(fcol, fd0, 0x80), PPR::LAKELEV);
2018-05-07 18:13:56 +00:00
}
else {
if(patterns::whichShape == '^') poly_outline = darkena(fcol, fd, flooralpha);
draw_qfi(c, V, darkena(fcol, fd, flooralpha));
2018-05-07 18:13:56 +00:00
}
2017-07-10 18:47:38 +00:00
// walls
2017-07-22 23:33:27 +00:00
#if CAP_EDIT
2017-07-10 18:47:38 +00:00
if(patterns::displaycodes) {
2017-12-19 13:35:34 +00:00
auto si = patterns::getpatterninfo0(c);
2017-07-10 18:47:38 +00:00
2018-08-20 13:24:44 +00:00
for(int i=(si.dir + MODFIXER) % si.symmetries; i<c->type; i += si.symmetries) {
queuepoly(V * ddspin(c, i) * (si.reflect?Mirror:Id), shAsymmetric, darkena(0x000000, 0, 0xC0));
2017-12-14 01:53:29 +00:00
si.dir += si.symmetries;
}
2017-07-10 18:47:38 +00:00
string label = its(si.id & 255);
color_t col = forecolor ^ colorhash(si.id >> 8);
queuestr(V, .5, label, 0xFF000000 + col);
2017-07-10 18:47:38 +00:00
}
#endif
2017-07-12 17:50:39 +00:00
if((cmode & sm::NUMBER) && (dialog::editingDetail())) {
color_t col =
2017-07-10 18:47:38 +00:00
dist0 < geom3::highdetail ? 0xFF80FF80 :
dist0 >= geom3::middetail ? 0xFFFF8080 :
0XFFFFFF80;
#if 1
queuepoly(V, shHeptaMarker, darkena(col & 0xFFFFFF, 0, 0xFF));
#else
char buf[64];
sprintf(buf, "%3.1f", float(dist0));
queuestr(V, .6, buf, col);
#endif
}
if(realred(c->wall) && !wmspatial) {
int s = snakelevel(c);
2018-05-07 18:13:56 +00:00
if(s >= 1) draw_floorshape(c, V, shRedRockFloor[0], getSnakelevColor(c, 0, 7, fd, wcol));
if(s >= 2) draw_floorshape(c, V, shRedRockFloor[1], getSnakelevColor(c, 1, 7, fd, wcol));
if(s >= 3) draw_floorshape(c, V, shRedRockFloor[2], getSnakelevColor(c, 2, 7, fd, wcol));
2017-07-10 18:47:38 +00:00
}
if(c->wall == waTower && !wmspatial) {
2018-05-07 18:13:56 +00:00
fcol = 0xE8E8E8;
set_floor(shMFloor);
2017-07-10 18:47:38 +00:00
}
if(pseudohept(c) && (
c->land == laRedRock ||
vid.darkhepta ||
(c->land == laClearing && !BITRUNCATED))) {
2017-07-10 18:47:38 +00:00
queuepoly((*Vdp), shHeptaMarker, wmblack ? 0x80808080 : 0x00000080);
}
2015-08-08 13:57:52 +00:00
if(conformal::includeHistory && conformal::inmovehistory.count(c))
2017-07-10 18:47:38 +00:00
queuepoly((*Vdp), shHeptaMarker, 0x000000C0);
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
char xch = winf[c->wall].glyph;
2019-02-22 20:13:33 +00:00
if(DIM == 3) {
color_t dummy;
if(isWall3(c, wcol)) {
int d = (wcol & 0xF0F0F0) >> 4;
2019-02-22 20:13:33 +00:00
2019-02-24 21:12:32 +00:00
for(int a=0; a<c->type; a++)
if(c->move(a) && !isWall3(c->move(a), dummy)) {
2019-03-12 01:40:15 +00:00
if(a < 4 && among(geometry, gHoroTris, gBinary3) && celldistAlt(c) >= celldistAlt(viewctr.at->c7)) continue;
if(a < 2 && among(geometry, gHoroRec) && celldistAlt(c) >= celldistAlt(viewctr.at->c7)) continue;
2019-03-11 17:46:34 +00:00
if(qfi.fshape && wmescher) {
auto& poly = queuepoly(V, shWall3D[a], darkena(wcol - d * get_darkval(a), 0, 0xFF));
poly.tinf = &qfi.fshape->tinf3;
2019-02-22 20:13:33 +00:00
}
2019-03-11 17:46:34 +00:00
else
queuepoly(V, shPlainWall3D[a], darkena(wcol - d * get_darkval(a), 0, 0xFF));
2019-02-22 20:13:33 +00:00
}
}
else {
for(int a=0; a<c->type; a++) if(c->move(a)) {
color_t t = transcolor(c, c->move(a), wcol);
2019-03-10 17:38:55 +00:00
if(t) {
t = t - get_darkval(a) * ((t & 0xF0F0F000) >> 4);
2019-03-11 17:46:34 +00:00
auto& poly = queuepolyat(V, shPlainWall3D[a], t, PPR::TRANSPARENT);
2019-03-10 17:38:55 +00:00
poly.subprio = celldistance(c, viewctr.at->c7) + celldistance(c->move(a), viewctr.at->c7);
}
2019-02-26 19:19:19 +00:00
}
if(among(c->wall, waBoat, waStrandedBoat)) drawBoat(c, &V, V, V);
else if(isFire(c)) {
static int r = 0;
r += ticks - lastt;
int each = 5 + last_firelimit;
while(r >= each) {
drawParticleSpeed(c, wcol, 75 + rand() % 75);
r -= each;
}
firelimit++;
}
else if(c->wall == waMineOpen) {
int mines = countMinesAround(c);
queuepoly(rgpushxto0(tC0(V)), shMineMark[0], darkena(minecolors[mines], 0, 0xFF));
}
else if(winf[c->wall].glyph == '.' || among(c->wall, waFloorA, waFloorB, waChasm) || isWatery(c) || isSulphuric(c->wall)) ;
else
queuepoly(rgpushxto0(tC0(V)), chasmgraph(c) ? shSawRing : shRing, darkena(wcol, 0, 0xFF));
2019-02-26 19:19:19 +00:00
}
int rd = rosedist(c);
if(rd == 1)
queuepoly(rgpushxto0(tC0(V)), shLoveRing, darkena(0x804060, 0, 0xFF));
if(rd == 2)
queuepoly(rgpushxto0(tC0(V)), shLoveRing, darkena(0x402030, 0, 0xFF));
2019-02-22 20:13:33 +00:00
}
else switch(c->wall) {
case waBigBush:
if(detaillevel >= 2)
2018-08-28 12:27:23 +00:00
queuepolyat(mmscale(V, zgrad0(0, geom3::slev, 1, 2)), shHeptaMarker, darkena(wcol, 0, 0xFF), PPR::REDWALL);
if(detaillevel >= 1)
2018-08-28 12:27:23 +00:00
queuepolyat(mmscale(V, geom3::SLEV[1]) * pispin, shWeakBranch, darkena(wcol, 0, 0xFF), PPR::REDWALL+1);
if(detaillevel >= 2)
2018-08-28 12:27:23 +00:00
queuepolyat(mmscale(V, zgrad0(0, geom3::slev, 3, 2)), shHeptaMarker, darkena(wcol, 0, 0xFF), PPR::REDWALL+2);
queuepolyat(mmscale(V, geom3::SLEV[2]), shSolidBranch, darkena(wcol, 0, 0xFF), PPR::REDWALL+3);
break;
case waSmallBush:
if(detaillevel >= 2)
2018-08-28 12:27:23 +00:00
queuepolyat(mmscale(V, zgrad0(0, geom3::slev, 1, 2)), shHeptaMarker, darkena(wcol, 0, 0xFF), PPR::REDWALL);
if(detaillevel >= 1)
2018-08-28 12:27:23 +00:00
queuepolyat(mmscale(V, geom3::SLEV[1]) * pispin, shWeakBranch, darkena(wcol, 0, 0xFF), PPR::REDWALL+1);
if(detaillevel >= 2)
2018-08-28 12:27:23 +00:00
queuepolyat(mmscale(V, zgrad0(0, geom3::slev, 3, 2)), shHeptaMarker, darkena(wcol, 0, 0xFF), PPR::REDWALL+2);
queuepolyat(mmscale(V, geom3::SLEV[2]), shWeakBranch, darkena(wcol, 0, 0xFF), PPR::REDWALL+3);
break;
case waSolidBranch:
queuepoly(V, shSolidBranch, darkena(wcol, 0, 0xFF));
break;
case waWeakBranch:
queuepoly(V, shWeakBranch, darkena(wcol, 0, 0xFF));
break;
2017-09-30 09:46:41 +00:00
case waLadder:
if(euclid) {
2018-05-07 18:13:56 +00:00
draw_floorshape(c, V, shMFloor, 0x804000FF);
draw_floorshape(c, V, shMFloor2, 0x000000FF);
}
else {
2018-08-28 12:27:23 +00:00
draw_floorshape(c, V, shFloor, 0x804000FF, PPR::FLOOR+1);
draw_floorshape(c, V, shMFloor, 0x000000FF, PPR::FLOOR+2);
}
break;
case waReptileBridge: {
2017-07-10 18:47:38 +00:00
Vboat = &(Vboat0 = V);
dynamicval<qfloorinfo> qfi2(qfi, qfi);
color_t col = reptilecolor(c);
chasmg = 0;
2018-05-07 18:13:56 +00:00
set_reptile_floor(c, V, col);
draw_qfi(c, V, col);
forCellIdEx(c2, i, c) if(chasmgraph(c2))
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_LAKE, V, darkena(gradient(0, col, 0, .8, 1), fd, 0xFF))) break;
chasmg = 1;
break;
2017-07-10 18:47:38 +00:00
}
case waTerraWarrior:
drawTerraWarrior(V, randterra ? (c->landparam & 7) : (5 - (c->landparam & 7)), 7, 0);
break;
2019-03-04 17:02:38 +00:00
case waBoat: case waStrandedBoat:
drawBoat(c, Vboat, Vboat0, V);
break;
case waBigStatue: {
transmatrix V2 = V;
double footphase;
applyAnimation(c, V2, footphase, LAYER_BOAT);
queuepolyat(V2, shStatue,
darkena(winf[c->wall].color, 0, 0xFF),
2018-08-28 12:27:23 +00:00
PPR::BIGSTATUE
);
break;
2017-07-10 18:47:38 +00:00
}
case waSulphurC: {
if(drawstar(c)) {
zcol = wcol;
if(wmspatial)
2018-08-28 12:27:23 +00:00
queuepolyat(mscale(V, geom3::HELLSPIKE), shGiantStar[ct6], darkena(wcol, 0, 0x40), PPR::HELLSPIKE);
else
queuepoly(V, shGiantStar[ct6], darkena(wcol, 0, 0xFF));
}
break;
2017-07-10 18:47:38 +00:00
}
case waTrapdoor:
if(c->land == laZebra) break;
/* fallthrough */
case waClosePlate: case waOpenPlate: {
transmatrix V2 = V;
2018-12-16 23:04:59 +00:00
if(wmescher && geosupport_football() == 2 && pseudohept(c) && c->land == laPalace) V2 = V * spin(M_PI / c->type);
2018-05-07 18:13:56 +00:00
draw_floorshape(c, V2, shMFloor, darkena(winf[c->wall].color, 0, 0xFF));
draw_floorshape(c, V2, shMFloor2, (!wmblack) ? darkena(fcol, 1, 0xFF) : darkena(0,1,0xFF));
break;
}
case waFrozenLake: case waLake: case waCamelotMoat:
case waSea: case waOpenGate: case waBubble: case waDock:
2018-10-25 00:43:14 +00:00
case waSulphur: case waMercury:
break;
case waNone:
if(c->land == laBrownian) goto wa_default;
break;
case waRose: {
zcol = wcol;
wcol <<= 1;
if(c->cpdist > 5)
wcol = 0xC0C0C0;
else if(rosephase == 7)
wcol = 0xFF0000;
else
wcol = gradient(wcol, 0xC00000, 0, rosephase, 6);
queuepoly(V, shThorns, 0xC080C0FF);
for(int u=0; u<4; u+=2)
queuepoly(V * spin(2*M_PI / 3 / 4 * u), shRose, darkena(wcol, 0, 0xC0));
break;
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
case waRoundTable:
if(wmspatial) goto wa_default;
break;
case waMirrorWall:
break;
case waGlass:
if(wmspatial) {
color_t col = winf[waGlass].color;
int dcol = darkena(col, 0, 0x80);
transmatrix Vdepth = mscale((*Vdp), geom3::WALL);
2018-08-28 12:27:23 +00:00
draw_floorshape(c, Vdepth, shMFloor, dcol, PPR::WALL); // GLASS
2018-05-07 18:13:56 +00:00
dynamicval<qfloorinfo> dq(qfi, qfi);
set_floor(shMFloor);
if(validsidepar[SIDE_WALL]) forCellIdEx(c2, i, c)
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_WALL, (*Vdp), dcol)) break;
}
break;
case waFan:
queuepoly(V * spintick(PURE ? -1000 : -500, 1/12.), shFan, darkena(wcol, 0, 0xFF));
break;
case waArrowTrap:
if(c->wparam >= 1)
queuepoly(V, shDisk, darkena(trapcol[c->wparam&3], 0, 0xFF));
if(isCentralTrap(c)) arrowtraps.push_back(c);
break;
2017-07-16 21:00:55 +00:00
2018-12-16 23:04:59 +00:00
case waFireTrap:
draw_floorshape(c, V, shMFloor, darkena(0xC00000, 0, 0xFF));
draw_floorshape(c, V, shMFloor2, darkena(0x600000, 0, 0xFF));
if(c->wparam >= 1)
queuepoly(V, shDisk, darkena(trapcol[c->wparam&3], 0, 0xFF));
break;
case waGiantRug:
queuepoly(V, shBigCarpet1, darkena(0xC09F00, 0, 0xFF));
queuepoly(V, shBigCarpet2, darkena(0x600000, 0, 0xFF));
queuepoly(V, shBigCarpet3, darkena(0xC09F00, 0, 0xFF));
break;
2017-07-10 18:47:38 +00:00
case waBarrier:
if(c->land == laOceanWall && wmescher && wmspatial) {
const int layers = 2 << detaillevel;
dynamicval<const hpcshape*> ds(qfi.shape, &shCircleFloor);
dynamicval<transmatrix> dss(qfi.spin, Id);
for(int z=1; z<layers; z++) {
2018-04-23 10:34:14 +00:00
double zg = zgrad0(-geom3::lake_top, geom3::actual_wall_height(), z, layers);
2018-05-07 18:13:56 +00:00
draw_qfi(c, xyzscale(V, zg*(layers-z)/layers, zg),
2018-08-28 12:27:23 +00:00
darkena(gradient(0, wcol, -layers, z, layers), 0, 0xFF), PPR::WALL3+z-layers+2);
}
}
else goto wa_default;
break;
2017-07-10 18:47:38 +00:00
case waMineOpen: {
int mines = countMinesAround(c);
if(mines)
2018-11-08 15:21:33 +00:00
queuepoly(V, shMineMark[ct6], darkena(minecolors[mines], 0, 0xFF));
break;
}
2019-02-28 16:06:53 +00:00
case waEditStatue:
if(!mapeditor::drawUserShape(V * ddspin(c, c->mondir), mapeditor::sgWall, c->wparam, darkena(wcol, fd, 0xFF), c))
queuepoly(V, shTriangle, darkena(wcol, fd, 0xFF));
break;
default: {
wa_default:
if(sl && wmspatial) {
2018-08-28 12:27:23 +00:00
draw_qfi(c, (*Vdp), darkena(wcol, fd, 0xFF), PPR::REDWALL-4+4*sl);
2018-05-07 18:13:56 +00:00
floorShadow(c, V, SHADOW_SL * sl);
for(int s=0; s<sl; s++)
forCellIdEx(c2, i, c) {
int sl2 = snakelevel(c2);
if(s >= sl2)
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_SLEV+s, V, getSnakelevColor(c, s, sl, fd, wcol))) break;
}
2017-07-10 18:47:38 +00:00
}
else if(highwall(c))
draw_wall(c, V, wcol, zcol, ct6, fd);
else if(xch == '%') {
if(doHighlight())
poly_outline = (c->land == laMirror) ? OUTLINE_TREASURE : OUTLINE_ORB;
2017-07-10 18:47:38 +00:00
if(wmspatial) {
color_t col = winf[c->wall].color;
int dcol = darkena(col, 0, 0xC0);
transmatrix Vdepth = mscale((*Vdp), geom3::WALL);
2018-08-28 12:27:23 +00:00
draw_floorshape(c, Vdepth, shMFloor, dcol, PPR::WALL); // GLASS
2018-05-07 18:13:56 +00:00
dynamicval<qfloorinfo> dq(qfi, qfi);
set_floor(shMFloor);
if(validsidepar[SIDE_WALL]) forCellIdEx(c2, i, c)
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_WALL, (*Vdp), dcol)) break;
2017-07-10 18:47:38 +00:00
}
2017-10-06 22:34:10 +00:00
else {
queuepoly(V, shMirror, darkena(wcol, 0, 0xC0));
2017-07-10 18:47:38 +00:00
}
poly_outline = OUTLINE_DEFAULT;
2017-07-10 18:47:38 +00:00
}
2018-12-16 23:04:59 +00:00
else if(c->wall == waExplosiveBarrel) {
const int layers = 2 << detaillevel;
for(int z=1; z<=layers; z++) {
double zg = zgrad0(0, geom3::actual_wall_height(), z, layers);
queuepolyat(xyzscale(V, zg, zg), shBarrel, darkena((z&1) ? 0xFF0000 : 0xC00000, 0, 0xFF), PPR(PPR::REDWALLm+z));
}
}
else if(isFire(c) || isThumper(c) || c->wall == waBonfireOff) {
auto V2 = V;
if(hasTimeout(c)) V2 = V2 * spintick(c->land == laPower ? 5000 : 500);
queuepoly(V2, shStar, darkena(wcol, 0, 0xF0));
if(isFire(c) && rand() % 300 < ticks - lastt)
drawParticle(c, wcol, 75);
}
else if(xch != '.' && xch != '+' && xch != '>' && xch != ':'&& xch != '-' && xch != ';' && xch != ',' && xch != '&')
error = true;
2017-07-10 18:47:38 +00:00
}
}
2019-02-17 17:41:40 +00:00
#else
error = true;
#endif
2017-07-10 18:47:38 +00:00
}
2017-09-30 09:46:41 +00:00
else {
if(c->wall == waArrowTrap)
asciicol = trapcol[c->wparam & 3];
2018-12-16 23:04:59 +00:00
if(c->wall == waFireTrap)
asciicol = trapcol[c->wparam & 3];
if(c->wall == waTerraWarrior)
asciicol = terracol[c->landparam & 7];
if(c->wall == waMineOpen) {
int mines = countMinesAround(c);
if(ch == '.') {
if(mines == 0) ch = ' ';
else ch = '0' + mines, asciicol = minecolors[mines];
}
else if(ch == '@') asciicol = minecolors[mines];
}
if(!(it || c->monst || c->cpdist == 0)) error = true;
}
2017-07-10 18:47:38 +00:00
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
int sha = shallow(c);
2017-04-08 15:18:29 +00:00
2019-02-27 17:00:09 +00:00
if(wmspatial && sha && DIM == 2) {
color_t col = (highwall(c) || c->wall == waTower) ? wcol : fcol;
2017-07-10 18:47:38 +00:00
if(!chasmg) {
2017-10-09 09:46:49 +00:00
#define D(v) darkena(gradient(0, col, 0, v * (sphere ? spherity(V * cellrelmatrix(c,i)) : 1), 1), fd, 0xFF)
// #define D(v) darkena(col, fd, 0xFF)
2017-10-09 09:46:49 +00:00
2017-07-10 18:47:38 +00:00
if(sha & 1) {
forCellIdEx(c2, i, c) if(chasmgraph(c2))
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_LAKE, V, D(.8))) break;
2017-07-10 18:47:38 +00:00
}
if(sha & 2) {
forCellIdEx(c2, i, c) if(chasmgraph(c2))
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_LTOB, V, D(.7))) break;
2017-07-10 18:47:38 +00:00
}
if(sha & 4) {
bool dbot = true;
forCellIdEx(c2, i, c) if(chasmgraph(c2) == 2) {
if(dbot) dbot = false,
2018-08-28 12:27:23 +00:00
draw_qfi(c, mscale(V, geom3::BOTTOM), 0x080808FF, PPR::LAKEBOTTOM);
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_BTOI, V, D(.6))) break;
2017-07-10 18:47:38 +00:00
}
2017-10-09 09:46:49 +00:00
#undef D
2017-07-10 18:47:38 +00:00
}
}
// wall between lake and chasm -- no Escher here
if(chasmg == 1) forCellIdEx(c2, i, c) if(chasmgraph(c2) == 2) {
dynamicval<qfloorinfo> qfib(qfi, qfi);
set_floor(shFullFloor);
2018-05-07 18:13:56 +00:00
placeSidewall(c, i, SIDE_LAKE, V, 0x202030FF);
placeSidewall(c, i, SIDE_LTOB, V, 0x181820FF);
placeSidewall(c, i, SIDE_BTOI, V, 0x101010FF);
2017-07-10 18:47:38 +00:00
}
2017-04-08 15:18:29 +00:00
}
2019-02-17 17:41:40 +00:00
2017-07-10 18:47:38 +00:00
if(chasmg) {
2018-06-22 12:47:24 +00:00
int q = isize(ptds);
int maxtime = euclid || sphere ? 20000 : 1500;
2017-07-10 18:47:38 +00:00
if(fallanims.count(c)) {
fallanim& fa = fallanims[c];
bool erase = true;
if(fa.t_floor) {
int t = (ticks - fa.t_floor);
if(t <= maxtime) {
2017-07-10 18:47:38 +00:00
erase = false;
if(fa.walltype == waNone)
2018-08-28 12:27:23 +00:00
draw_qfi(c, V, darkena(fcol, fd, 0xFF), PPR::FLOOR);
2017-07-10 18:47:38 +00:00
else {
color_t wcol2, fcol2;
2017-07-10 18:47:38 +00:00
eWall w = c->wall; int p = c->wparam;
c->wall = fa.walltype; c->wparam = fa.m;
setcolors(c, wcol2, fcol2);
int starcol = c->wall == waVinePlant ? 0x60C000 : wcol2;
c->wall = w; c->wparam = p;
2018-08-28 12:27:23 +00:00
draw_qfi(c, mscale(V, geom3::WALL), darkena(starcol, fd, 0xFF), PPR::WALL3);
queuepolyat(mscale(V, geom3::WALL), shWall[ct6], darkena(wcol2, 0, 0xFF), PPR::WALL3A);
2017-07-10 18:47:38 +00:00
forCellIdEx(c2, i, c)
2018-05-07 18:13:56 +00:00
if(placeSidewall(c, i, SIDE_WALL, V, darkena(wcol2, 1, 0xFF))) break;
2017-07-10 18:47:38 +00:00
}
pushdown(c, q, V, t*t / 1000000. + t / 1000., true, true);
}
}
if(fa.t_mon) {
2017-08-06 12:50:16 +00:00
dynamicval<int> d(multi::cpid, fa.pid);
2017-07-10 18:47:38 +00:00
int t = (ticks - fa.t_mon);
if(t <= maxtime) {
2017-07-10 18:47:38 +00:00
erase = false;
c->stuntime = 0;
transmatrix V2 = V;
double footphase = t / 200.0;
applyAnimation(c, V2, footphase, LAYER_SMALL);
drawMonsterType(fa.m, c, V2, minf[fa.m].color, footphase);
pushdown(c, q, V2, t*t / 1000000. + t / 1000., true, true);
}
}
if(erase) fallanims.erase(c);
}
}
2019-02-17 17:41:40 +00:00
#endif
2015-08-08 13:57:52 +00:00
if(it) {
2018-12-21 13:41:23 +00:00
if((c->land == laWhirlwind || c->item == itBabyTortoise || c->land == laWestWall) && c->wall != waBoat) {
double footphase = 0;
Vboat = &(Vboat0 = *Vboat);
applyAnimation(c, Vboat0, footphase, LAYER_BOAT);
2018-03-24 14:20:53 +00:00
}
if(cellHalfvine(c)) {
int i =-1;
for(int t=0;t<6; t++) if(c->move(t) && c->move(t)->wall == c->wall)
i = t;
2017-07-10 18:47:38 +00:00
Vboat = &(Vboat0 = *Vboat * ddspin(c, i) * xpush(-.13));
}
error |= drawItemType(it, c, *Vboat, icol, ticks, hidden);
}
2017-07-10 18:47:38 +00:00
if(true) {
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
int q = ptds.size();
#endif
2017-10-28 08:04:28 +00:00
error |= drawMonster(V, ctype, c, moncol);
#if CAP_SHAPES
2018-06-22 12:47:24 +00:00
if(Vboat != &V && Vboat != &Vboat0 && q != isize(ptds))
2017-07-10 18:47:38 +00:00
pushdown(c, q, V, -geom3::factor_to_lev(zlevel(tC0((*Vboat)))),
!isMultitile(c->monst), false);
#endif
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
if(!shmup::on && sword::at(c)) {
2018-08-28 12:27:23 +00:00
queuepolyat(V, shDisk, 0xC0404040, PPR::SWORDMARK);
2017-03-23 10:53:57 +00:00
}
#endif
#if CAP_TEXTURE
if(!texture::using_aura())
#endif
addaura(tC0(V), zcol, fd);
2015-08-08 13:57:52 +00:00
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
int ad = airdist(c);
if(ad == 1 || ad == 2) {
2017-07-10 18:47:38 +00:00
for(int i=0; i<c->type; i++) {
cell *c2 = c->move(i);
2017-07-10 18:47:38 +00:00
if(airdist(c2) < airdist(c)) {
2019-02-26 13:08:51 +00:00
ld airdir = calcAirdir(c2); // printf("airdir = %d\n", airdir);
transmatrix V0 = ddspin(c, i, M_PI);
2017-07-10 18:47:38 +00:00
2019-02-26 13:08:51 +00:00
double ph = ptick(PURE?150:75) + airdir;
2017-07-10 18:47:38 +00:00
int aircol = 0x8080FF00 | int(32 + 32 * -cos(ph));
double ph0 = ph/2;
ph0 -= floor(ph0/M_PI)*M_PI;
2017-07-10 18:47:38 +00:00
poly_outline = OUTLINE_TRANS;
queuepoly((*Vdp)*V0*xpush(hexf*-cos(ph0)), shDisk, aircol);
poly_outline = OUTLINE_DEFAULT;
}
}
}
#endif
#if CAP_SHAPES
if(c->land == laBlizzard) {
if(vid.backeffects) {
if(c->cpdist <= getDistLimit())
set_blizzard_frame(c, frameid);
}
else {
forCellIdEx(c2, i, c) if(againstWind(c, c2))
queuepoly(V * ddspin(c, i) * xpush(cellgfxdist(c, i)/2), shWindArrow, 0x8080FF80);
}
}
#endif
if(items[itOrbGravity] && c->cpdist <= 5)
draw_gravity_particles(c, V);
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
if(c->land == laWhirlwind) {
whirlwind::calcdirs(c);
for(int i=0; i<whirlwind::qdirs; i++) {
ld hdir0 = displayspin(c, whirlwind::dfrom[i]) + M_PI;
ld hdir1 = displayspin(c, whirlwind::dto[i]);
2017-07-10 18:47:38 +00:00
double ph1 = fractick(PURE ? 150 : 75);
2017-07-10 18:47:38 +00:00
int aircol = 0xC0C0FF40;
if(hdir1 < hdir0-M_PI) hdir1 += 2 * M_PI;
if(hdir1 >= hdir0+M_PI) hdir1 -= 2 * M_PI;
2017-07-10 18:47:38 +00:00
ld hdir = (hdir1*ph1+hdir0*(1-ph1));
2017-07-10 18:47:38 +00:00
transmatrix V0 = spin(hdir);
2017-07-10 18:47:38 +00:00
double ldist =
cellgfxdist(c, whirlwind::dfrom[i]) * (1-ph1)/2 +
cellgfxdist(c, whirlwind::dto[i]) * ph1/2;
// PURE ? crossf : c->type == 6 ? .2840 : 0.3399;
2017-07-10 18:47:38 +00:00
poly_outline = OUTLINE_TRANS;
queuepoly((*Vdp)*V0*xpush(ldist*(2*ph1-1)), shDisk, aircol);
poly_outline = OUTLINE_DEFAULT;
}
2017-07-10 18:47:38 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
2017-07-04 13:38:33 +00:00
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
2017-07-10 18:47:38 +00:00
if(error) {
2019-02-22 20:13:33 +00:00
queuechr(V, 1, ch, darkenedby(asciicol, darken), 2);
}
2017-07-10 18:47:38 +00:00
if(vid.grid) {
dynamicval<ld> lw(vid.linewidth, vid.linewidth);
2018-08-28 17:05:57 +00:00
vid.linewidth *= scalefactor;
2017-07-10 18:47:38 +00:00
// sphere: 0.3948
// sphere heptagonal: 0.5739
// sphere trihepta: 0.3467
// hyper trihepta: 0.2849
// hyper heptagonal: 0.6150
// hyper: 0.3798
2017-10-29 16:12:40 +00:00
int prec = sphere ? 3 : 1;
2018-08-01 09:07:22 +00:00
prec += vid.linequality;
2017-10-29 16:12:40 +00:00
2019-02-17 17:28:20 +00:00
if(0);
#if MAXMDIM == 4
2019-02-25 18:11:04 +00:00
else if(DIM == 3) {
for(int t=0; t<c->type; t++) {
if(!c->move(t)) continue;
2019-03-02 23:43:31 +00:00
if(binarytiling && !among(t, 5, 6, 8)) continue;
if(!binarytiling && c->move(t) < c) continue;
2019-03-08 21:41:26 +00:00
dynamicval<color_t> g(poly_outline, gridcolor(c, c->move(t)));
2019-03-11 17:46:34 +00:00
queuepoly(V, shWireframe3D[t], 0);
2019-02-25 18:11:04 +00:00
}
}
#endif
2019-02-17 17:28:20 +00:00
#if CAP_BT
else if(binarytiling && DIM == 2) {
2018-08-14 08:18:44 +00:00
ld yx = log(2) / 2;
ld yy = yx;
ld xx = 1 / sqrt(2)/2;
queueline(V * get_horopoint(-yy, xx), V * get_horopoint(yy, 2*xx), gridcolor(c, c->move(binary::bd_right)), prec);
2018-08-14 08:18:44 +00:00
auto horizontal = [&] (ld y, ld x1, ld x2, int steps, int dir) {
if(vid.linequality > 0) steps <<= vid.linequality;
if(vid.linequality < 0) steps >>= -vid.linequality;
for(int i=0; i<=steps; i++) curvepoint(V * get_horopoint(y, x1 + (x2-x1) * i / steps));
2018-10-28 02:06:31 +00:00
queuecurve(gridcolor(c, c->move(dir)), 0, PPR::LINE);
2018-08-14 08:18:44 +00:00
};
horizontal(yy, 2*xx, xx, 4, binary::bd_up_right);
horizontal(yy, xx, -xx, 8, binary::bd_up);
horizontal(yy, -xx, -2*xx, 4, binary::bd_up_left);
}
#endif
else if(isWarped(c) && has_nice_dual()) {
if(pseudohept(c)) for(int t=0; t<c->type; t++)
queueline(V * get_warp_corner(c, t%c->type),
V * get_warp_corner(c, (t+1)%c->type),
gridcolor(c, c->move(t)), prec);
2017-07-10 18:47:38 +00:00
}
else {
for(int t=0; t<c->type; t++)
if(c->move(t) && c->move(t) < c)
2018-08-27 17:38:11 +00:00
queueline(V * get_corner_position(c, t%c->type),
V * get_corner_position(c, (t+1)%c->type),
gridcolor(c, c->move(t)), prec);
2017-07-10 18:47:38 +00:00
}
}
2019-02-17 17:28:20 +00:00
#endif
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(!euclid) {
bool usethis = false;
double spd = 1;
2018-12-21 13:41:23 +00:00
int side = 0;
2017-07-10 18:47:38 +00:00
2019-02-17 17:28:20 +00:00
if(0);
#if CAP_BT
else if(binarytiling && conformal::do_rotate >= 2) {
2018-08-09 17:28:53 +00:00
if(!straightDownSeek || c->master->distance < straightDownSeek->master->distance) {
usethis = true;
spd = 1;
}
}
2019-02-17 17:28:20 +00:00
#endif
2018-08-09 17:28:53 +00:00
else if(isGravityLand(cwt.at->land) && cwt.at->land != laMountain) {
2018-12-21 13:41:23 +00:00
if(cwt.at->land == laDungeon) side = 2;
if(cwt.at->land == laWestWall) side = 1;
2018-03-25 13:07:11 +00:00
if(conformal::do_rotate >= 1)
2017-07-10 18:47:38 +00:00
if(!straightDownSeek || edgeDepth(c) < edgeDepth(straightDownSeek)) {
usethis = true;
spd = cwt.at->landparam / 10.;
2017-07-10 18:47:38 +00:00
}
}
2018-03-25 13:07:11 +00:00
else if(c->master->alt && cwt.at->master->alt &&
((cwt.at->land == laMountain && conformal::do_rotate >= 1)||
2018-03-25 13:07:11 +00:00
(conformal::do_rotate >= 2 &&
(cwt.at->land == laTemple || cwt.at->land == laWhirlpool ||
(cheater && (cwt.at->land == laClearing || cwt.at->land == laCaribbean ||
cwt.at->land == laCamelot || cwt.at->land == laPalace)))
2017-07-10 18:47:38 +00:00
))
&& c->land == cwt.at->land && c->master->alt->alt == cwt.at->master->alt->alt) {
2017-07-10 18:47:38 +00:00
if(!straightDownSeek || !straightDownSeek->master->alt || celldistAlt(c) < celldistAlt(straightDownSeek)) {
usethis = true;
spd = .5;
2018-12-21 13:41:23 +00:00
if(cwt.at->land == laMountain) side = 2;
2017-07-10 18:47:38 +00:00
}
}
2015-08-08 13:57:52 +00:00
else if(conformal::do_rotate >= 2 && cwt.at->land == laOcean && cwt.at->landparam < 25) {
2017-07-10 18:47:38 +00:00
if(!straightDownSeek || coastval(c, laOcean) < coastval(straightDownSeek, laOcean)) {
usethis = true;
spd = cwt.at->landparam / 10;
2017-07-10 18:47:38 +00:00
}
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(usethis) {
straightDownSeek = c;
downspin = atan2(VC0[1], VC0[0]);
2018-12-21 13:41:23 +00:00
downspin += (side-1) * M_PI/2;
2018-11-08 17:18:25 +00:00
downspin += conformal::rotation * degree;
2017-07-10 18:47:38 +00:00
while(downspin < -M_PI) downspin += 2*M_PI;
while(downspin > +M_PI) downspin -= 2*M_PI;
downspin = downspin * min(spd, (double)1);
}
}
2017-07-10 18:47:38 +00:00
if(!inHighQual) {
2017-07-22 23:33:27 +00:00
#if CAP_EDIT
2017-07-12 17:50:39 +00:00
if((cmode & sm::MAP) && lmouseover && darken == 0 &&
2019-02-26 13:56:07 +00:00
(DIM == 3 || !mouseout()) &&
(patterns::whichPattern ? patterns::getpatterninfo0(c).id == patterns::getpatterninfo0(lmouseover).id : c == lmouseover)) {
2019-02-26 13:56:07 +00:00
queuecircleat(c, .78, 0x00FFFFFF);
}
2017-07-22 23:33:27 +00:00
2017-10-28 08:04:28 +00:00
mapeditor::drawGhosts(c, V, ctype);
2017-07-10 18:47:38 +00:00
#endif
}
2017-07-10 18:47:38 +00:00
if(vid.grid && c->bardir != NODIR && c->bardir != NOBARRIERS && c->land != laHauntedWall &&
2017-07-10 18:47:38 +00:00
c->barleft != NOWALLSEP_USED) {
color_t col = darkena(0x505050, 0, 0xFF);
2018-08-01 09:07:22 +00:00
queueline(tC0(V), V*tC0(heptmove[c->bardir]), col, 2 + vid.linequality);
queueline(tC0(V), V*tC0(hexmove[c->bardir]), col, 2 + vid.linequality);
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
2017-07-22 23:33:27 +00:00
#if CAP_MODEL
2017-07-10 18:47:38 +00:00
netgen::buildVertexInfo(c, V);
#endif
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
}
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
struct flashdata {
int t;
int size;
cell *where;
double angle;
2019-02-26 19:18:57 +00:00
double angle2;
2017-07-10 18:47:38 +00:00
int spd; // 0 for flashes, >0 for particles
color_t color;
flashdata(int _t, int _s, cell *_w, color_t col, int sped) {
2017-07-10 18:47:38 +00:00
t=_t; size=_s; where=_w; color = col;
angle = rand() % 1000; spd = sped;
2019-02-26 19:18:57 +00:00
if(DIM == 3) angle2 = acos((rand() % 1000 - 499.5) / 500);
2017-07-10 18:47:38 +00:00
}
};
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
vector<flashdata> flashes;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
void drawFlash(cell *c) {
flashes.push_back(flashdata(ticks, 1000, c, iinf[itOrbFlash].color, 0));
}
void drawBigFlash(cell *c) {
flashes.push_back(flashdata(ticks, 2000, c, 0xC0FF00, 0));
}
2019-02-26 19:19:19 +00:00
void drawParticleSpeed(cell *c, color_t col, int speed) {
2017-07-10 18:47:38 +00:00
if(vid.particles && !confusingGeometry())
2019-02-26 19:19:19 +00:00
flashes.push_back(flashdata(ticks, rand() % 16, c, col, speed));
}
void drawParticle(cell *c, color_t col, int maxspeed) {
drawParticleSpeed(c, col, 1 + rand() % maxspeed);
2017-07-10 18:47:38 +00:00
}
void drawParticles(cell *c, color_t col, int qty, int maxspeed) {
2017-07-10 18:47:38 +00:00
if(vid.particles)
while(qty--) drawParticle(c,col, maxspeed);
}
void drawFireParticles(cell *c, int qty, int maxspeed) {
if(vid.particles)
for(int i=0; i<qty; i++)
drawParticle(c, firegradient(i / (qty-1.)), maxspeed);
}
void fallingFloorAnimation(cell *c, eWall w, eMonster m) {
if(!wmspatial) return;
fallanim& fa = fallanims[c];
fa.t_floor = ticks;
fa.walltype = w; fa.m = m;
// drawParticles(c, darkenedby(linf[c->land].color, 1), 4, 50);
}
2017-08-06 12:50:16 +00:00
void fallingMonsterAnimation(cell *c, eMonster m, int id) {
2017-07-10 18:47:38 +00:00
if(!mmspatial) return;
fallanim& fa = fallanims[c];
fa.t_mon = ticks;
fa.m = m;
2017-08-06 12:50:16 +00:00
fa.pid = id;
2017-07-10 18:47:38 +00:00
// drawParticles(c, darkenedby(linf[c->land].color, 1), 4, 50);
}
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
void queuecircleat(cell *c, double rad, color_t col) {
2017-07-10 18:47:38 +00:00
if(!c) return;
if(!gmatrix.count(c)) return;
2019-02-26 10:57:50 +00:00
if(DIM == 3) {
dynamicval<color_t> p(poly_outline, col);
for(int i=0; i<c->type; i++) {
2019-03-11 17:46:34 +00:00
queuepolyat(gmatrix[c], shWireframe3D[i], 0, PPR::SUPERLINE);
}
2019-02-26 10:57:50 +00:00
return;
}
#if CAP_SHAPES
if(vid.stereo_mode || sphere) {
dynamicval<color_t> p(poly_outline, col);
queuepolyat(gmatrix[c] * spintick(100), shGem[1], 0, PPR::LINE);
return;
}
2019-02-17 17:41:40 +00:00
#endif
2017-07-10 18:47:38 +00:00
queuecircle(gmatrix[c], rad, col);
if(!wmspatial) return;
if(highwall(c))
queuecircle(mscale(gmatrix[c], geom3::WALL), rad, col);
int sl;
if((sl = snakelevel(c))) {
queuecircle(mscale(gmatrix[c], geom3::SLEV[sl]), rad, col);
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
if(chasmgraph(c))
queuecircle(mscale(gmatrix[c], geom3::LAKE), rad, col);
2016-08-26 09:58:03 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
#define G(x) x && gmatrix.count(x)
#define IG(x) if(G(x))
#define Gm(x) gmatrix[x]
#define Gm0(x) tC0(gmatrix[x])
2016-01-02 10:09:13 +00:00
2017-07-22 23:33:27 +00:00
#if ISMOBILE==1
2017-07-10 18:47:38 +00:00
#define MOBON (clicked)
#else
#define MOBON true
2016-01-02 10:09:13 +00:00
#endif
2019-02-26 13:56:07 +00:00
cell *forwardcell() {
2019-02-26 11:28:54 +00:00
movedir md = vectodir(move_destination_vec(6));
cellwalker xc = cwt + md.d + wstep;
return xc.at;
}
2017-07-10 18:47:38 +00:00
void drawMarkers() {
2017-03-23 10:53:57 +00:00
if(!(cmode & sm::NORMAL)) return;
2018-01-02 10:15:42 +00:00
2018-09-06 20:34:35 +00:00
callhooks(hooks_markers);
#if CAP_SHAPES
2018-08-18 15:38:07 +00:00
viewmat();
2019-02-17 17:41:40 +00:00
#endif
2018-08-18 15:38:07 +00:00
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
2018-01-02 10:15:42 +00:00
for(cell *c1: crush_now)
queuecircleat(c1, .8, darkena(minf[moCrusher].color, 0, 0xFF));
2019-02-17 17:41:40 +00:00
#endif
2017-07-10 18:47:38 +00:00
if(!inHighQual) {
2016-01-02 10:09:13 +00:00
bool ok = !ISPANDORA || mousepressed;
2019-02-17 17:41:40 +00:00
ignore(ok);
2017-07-10 18:47:38 +00:00
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
2017-07-10 18:47:38 +00:00
if(G(dragon::target) && haveMount()) {
queuechr(Gm0(dragon::target), 2*vid.fsize, 'X',
gradient(0, iinf[itOrbDomination].color, -1, sintick(dragon::whichturn == turncount ? 75 : 150), 1));
2017-07-10 18:47:38 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
/* for(int i=0; i<12; i++) if(c->type == 5 && c->master == &dodecahedron[i])
queuechr(xc, yc, sc, 4*vid.fsize, 'A'+i, iinf[itOrbDomination].color); */
if(1) {
using namespace yendor;
if(yii < isize(yi) && !yi[yii].found) {
cell *keycell = NULL;
int i;
for(i=0; i<YDIST; i++)
if(yi[yii].path[i]->cpdist <= get_sightrange_ambush()) {
keycell = yi[yii].path[i];
break;
}
if(keycell) {
for(; i<YDIST; i++) {
cell *c = yi[yii].path[i];
if(inscreenrange(c))
keycell = c;
}
hyperpoint H = tC0(ggmatrix(keycell));
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
queuechr(H, 2*vid.fsize, 'X', 0x10101 * int(128 + 100 * sintick(150)));
queuestr(H, vid.fsize, its(celldistance(cwt.at, yi[yii].key())), 0x10101 * int(128 - 100 * sintick(150)));
2019-02-17 17:41:40 +00:00
#endif
addauraspecial(H, iinf[itOrbYendor].color, 0);
2017-08-06 12:50:16 +00:00
}
}
2017-07-10 18:47:38 +00:00
}
2018-11-17 18:30:50 +00:00
#if CAP_RACING
racing::markers();
2018-11-17 18:30:50 +00:00
#endif
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
2019-02-26 11:29:11 +00:00
if(lmouseover && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON && DIM == 2) {
2017-07-10 18:47:38 +00:00
queuecircleat(lmouseover, .8, darkena(lmouseover->cpdist > 1 ? 0x00FFFF : 0xFF0000, 0, 0xFF));
}
2019-02-26 11:29:11 +00:00
if(global_pushto && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON && DIM == 2) {
2017-07-10 18:47:38 +00:00
queuecircleat(global_pushto, .6, darkena(0xFFD500, 0, 0xFF));
}
2019-02-17 17:41:40 +00:00
#endif
2019-02-17 17:41:40 +00:00
#if CAP_SDLJOY && CAP_QUEUE
2019-02-26 11:29:11 +00:00
if(joydir.d >= 0 && DIM == 2)
queuecircleat(cwt.at->modmove(joydir.d+cwt.spin), .78 - .02 * sintick(199),
2017-07-10 18:47:38 +00:00
darkena(0x00FF00, 0, 0xFF));
2017-07-22 23:33:27 +00:00
#endif
2016-01-02 10:09:13 +00:00
2017-07-22 23:33:27 +00:00
bool m = true;
2019-02-17 17:41:40 +00:00
ignore(m);
2017-07-22 23:33:27 +00:00
#if CAP_MODEL
m = netgen::mode == 0;
#endif
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
if(centerover.at && !playermoved && m && !anims::any_animation() && DIM == 2)
2018-09-10 15:58:36 +00:00
queuecircleat(centerover.at, .70 - .06 * sintick(200),
darkena(int(175 + 25 * sintick(200)), 0, 0xFF));
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
if(multi::players > 1 || multi::alwaysuse) for(int i=0; i<numplayers(); i++) {
multi::cpid = i;
if(multi::players == 1) multi::player[i] = cwt;
cell *ctgt = multi::multiPlayerTarget(i);
queuecircleat(ctgt, .40 - .06 * sintick(200, i / numplayers()), getcs().uicolor);
2017-07-10 18:47:38 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
// process mouse
#if CAP_SHAPES
2019-02-26 10:57:50 +00:00
if((vid.axes == 4 || (vid.axes == 1 && !mousing)) && !shmup::on && DIM == 2) {
2017-07-10 18:47:38 +00:00
if(multi::players == 1) {
forCellIdAll(c2, d, cwt.at) IG(c2) drawMovementArrows(c2, confusingGeometry() ? Gm(cwt.at) * calc_relative_matrix(c2, cwt.at, d) : Gm(c2));
2017-07-10 18:47:38 +00:00
}
else if(multi::players > 1) for(int p=0; p<multi::players; p++) {
if(multi::playerActive(p) && (vid.axes == 4 || !drawstaratvec(multi::mdx[p], multi::mdy[p])))
forCellIdAll(c2, d, multi::player[p].at) IG(c2) {
2017-07-10 18:47:38 +00:00
multi::cpid = p;
dynamicval<transmatrix> ttm(cwtV, multi::whereis[p]);
dynamicval<cellwalker> tcw(cwt, multi::player[p]);
drawMovementArrows(c2, confusingGeometry() ? Gm(cwt.at) * calc_relative_matrix(c2, cwt.at, d) : Gm(c2));
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
}
}
2019-02-26 10:57:50 +00:00
if(DIM == 3 && !inHighQual && !shmup::on && vid.axes && playermoved) {
2019-02-26 13:56:07 +00:00
cell *c = forwardcell();
2019-02-26 11:28:54 +00:00
IG(c) queuecircleat(c, .8, getcs().uicolor);
2019-02-26 10:57:50 +00:00
}
if(DIM == 3 && !inHighQual && !playermoved) {
cell *c = mouseover;
IG(c) queuecircleat(c, .8, 0xFFFFFFFF);
}
2019-02-17 17:41:40 +00:00
#endif
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
monsterToSummon = moNone;
orbToTarget = itNone;
2016-08-26 09:58:03 +00:00
2017-07-12 17:50:39 +00:00
if(mouseover && targetclick) {
2017-07-10 18:47:38 +00:00
shmup::cpid = 0;
orbToTarget = targetRangedOrb(mouseover, roCheck);
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
2017-07-10 18:47:38 +00:00
if(orbToTarget == itOrbSummon) {
monsterToSummon = summonedAt(mouseover);
queuechr(mousex, mousey, 0, vid.fsize, minf[monsterToSummon].glyph, minf[monsterToSummon].color);
queuecircleat(mouseover, 0.6, darkena(minf[monsterToSummon].color, 0, 0xFF));
}
else if(orbToTarget) {
queuechr(mousex, mousey, 0, vid.fsize, '@', iinf[orbToTarget].color);
queuecircleat(mouseover, 0.6, darkena(iinf[orbToTarget].color, 0, 0xFF));
}
2019-02-17 17:41:40 +00:00
#endif
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
if(orbToTarget && rand() % 200 < ticks - lastt) {
if(orbToTarget == itOrbDragon)
drawFireParticles(mouseover, 2);
else if(orbToTarget == itOrbSummon) {
drawParticles(mouseover, iinf[orbToTarget].color, 1);
drawParticles(mouseover, minf[monsterToSummon].color, 1);
}
else {
drawParticles(mouseover, iinf[orbToTarget].color, 2);
}
}
if(items[itOrbAir] && mouseover->cpdist > 1) {
cell *c1 = mouseover;
for(int it=0; it<10; it++) {
int di;
cell *c2 = blowoff_destination(c1, di);
if(!c2) break;
transmatrix T1 = ggmatrix(c1);
transmatrix T2 = ggmatrix(c2);
transmatrix T = T1 * rspintox(inverse(T1)*T2*C0) * xpush(hdist(T1*C0, T2*C0) * fractick(50, 0));
color_t aircol = (orbToTarget == itOrbAir ? 0x8080FF40 : 0x8080FF20);
queuepoly(T, shDisk, aircol);
c1 = c2;
}
}
2019-02-17 17:41:40 +00:00
#endif
2017-07-10 18:47:38 +00:00
}
}
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
void drawFlashes() {
#if CAP_QUEUE
2018-06-22 12:47:24 +00:00
for(int k=0; k<isize(flashes); k++) {
2017-07-10 18:47:38 +00:00
flashdata& f = flashes[k];
2018-04-14 08:27:49 +00:00
transmatrix V;
if(f.spd) try { V = gmatrix.at(f.where); } catch(out_of_range&) {
2018-06-22 12:47:24 +00:00
f = flashes[isize(flashes)-1];
2018-01-20 16:52:59 +00:00
flashes.pop_back(); k--;
continue;
2018-04-14 08:27:49 +00:00
}
else V = ggmatrix(f.where);
2018-04-14 08:27:49 +00:00
2017-07-10 18:47:38 +00:00
int tim = ticks - f.t;
bool kill = tim > f.size;
if(f.spd) {
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
kill = tim > 300;
2019-02-26 19:18:57 +00:00
int partcol = darkena(f.color, 0, DIM == 3 ? 255 : max(255 - tim*255/300, 0));
2017-07-10 18:47:38 +00:00
poly_outline = OUTLINE_DEFAULT;
2019-02-26 19:18:57 +00:00
ld t = f.spd * tim * scalefactor / 50000.;
transmatrix T =
DIM == 2 ? V * spin(f.angle) * xpush(t) :
V * cspin(0, 1, f.angle) * cspin(0, 2, f.angle2) * cpush(2, t);
queuepoly(T, shParticle[f.size], partcol);
#endif
2017-07-10 18:47:38 +00:00
}
else if(f.size == 1000) {
for(int u=0; u<=tim; u++) {
if((u-tim)%50) continue;
if(u < tim-150) continue;
ld rad = u * 3 / 1000.;
rad = rad * (5-rad) / 2;
rad *= hexf;
int flashcol = f.color;
if(u > 500) flashcol = gradient(flashcol, 0, 500, u, 1100);
flashcol = darkena(flashcol, 0, 0xFF);
PRING(a) curvepoint(V*xspinpush0(a * M_PI / S42, rad));
2018-08-28 12:27:23 +00:00
queuecurve(flashcol, 0x8080808, PPR::LINE);
2017-07-10 18:47:38 +00:00
}
}
else if(f.size == 2000) {
for(int u=0; u<=tim; u++) {
if((u-tim)%50) continue;
if(u < tim-250) continue;
ld rad = u * 3 / 2000.;
rad = rad * (5-rad) * 1.25;
rad *= hexf;
int flashcol = f.color;
if(u > 1000) flashcol = gradient(flashcol, 0, 1000, u, 2200);
flashcol = darkena(flashcol, 0, 0xFF);
PRING(a) curvepoint(V*xspinpush0(a * M_PI / S42, rad));
2018-08-28 12:27:23 +00:00
queuecurve(flashcol, 0x8080808, PPR::LINE);
2017-07-10 18:47:38 +00:00
}
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(kill) {
2018-06-22 12:47:24 +00:00
f = flashes[isize(flashes)-1];
2017-07-10 18:47:38 +00:00
flashes.pop_back(); k--;
}
2016-08-26 09:58:03 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
bool allowIncreasedSight() {
2018-04-18 18:53:52 +00:00
if(cheater || autocheat) return true;
if(peace::on) return true;
#if CAP_TOUR
if(tour::on) return true;
#endif
if(randomPatternsMode) return true;
2019-01-11 14:01:36 +00:00
if(racing::on) return true;
if(quotient || !hyperbolic || archimedean) return true;
return false;
}
2018-04-11 13:49:49 +00:00
bool allowChangeRange() {
if(cheater || peace::on || randomPatternsMode) return true;
#if CAP_TOUR
if(tour::on) return true;
#endif
2019-01-11 14:01:36 +00:00
if(racing::on) return true;
2018-04-11 13:49:49 +00:00
if(sightrange_bonus >= 0) return true;
if(archimedean) return true;
2018-04-11 13:49:49 +00:00
return false;
}
purehookset hooks_drawmap;
2017-12-16 08:03:50 +00:00
transmatrix cview() {
sphereflip = Id;
2019-02-27 13:13:04 +00:00
if(DIM == 3 && !shmup::on && vid.yshift) return cpush(2, vid.yshift) * View;
if(DIM == 3) return View;
2019-02-21 17:46:53 +00:00
if(sphereflipped()) sphereflip[DIM][DIM] = -1;
2017-12-16 08:03:50 +00:00
return ypush(vid.yshift) * sphereflip * View;
}
2018-08-19 20:53:51 +00:00
void precise_mouseover() {
2019-02-26 13:56:07 +00:00
if(DIM == 3) {
mouseover2 = mouseover = viewctr.at->c7;
ld best = HUGE_VAL;
hyperpoint h = cpush(2, 1) * C0;
forCellEx(c1, mouseover2) {
ld dist = hdist(tC0(ggmatrix(c1)), h);
if(dist < best) mouseover = c1, best = dist;
}
return;
}
2018-08-19 20:53:51 +00:00
if(!mouseover) return;
cell *omouseover = mouseover;
for(int loop = 0; loop < 10; loop++) {
bool found = false;
if(!gmatrix.count(mouseover)) return;
hyperpoint r_mouseh = inverse(gmatrix[mouseover]) * mouseh;
for(int i=0; i<mouseover->type; i++) {
hyperpoint h1 = get_corner_position(mouseover, (i+mouseover->type-1) % mouseover->type);
hyperpoint h2 = get_corner_position(mouseover, i);
using namespace hyperpoint_vec;
hyperpoint hx = r_mouseh - h1;
h2 = h2 - h1;
ld z = h2[1] * hx[0] - h2[0] * hx[1];
ld z0 = h2[1] * h1[0] - h2[0] * h1[1];
if(z * z0 > 0) {
mouseover2 = mouseover;
mouseover = mouseover->move(i);
found = true;
break;
}
}
if(!found) return;
}
// probably some error... just return the original
mouseover = omouseover;
}
2017-07-10 18:47:38 +00:00
void drawthemap() {
last_firelimit = firelimit;
firelimit = 0;
2017-07-04 13:38:33 +00:00
2019-02-28 15:24:46 +00:00
radarpoints.clear();
callhooks(hooks_drawmap);
2017-07-10 18:47:38 +00:00
frameid++;
2018-11-01 17:59:25 +00:00
cells_drawn = 0;
2017-09-30 09:46:41 +00:00
wavephase = (-(ticks / 100)) & 7;
2015-08-08 13:57:52 +00:00
if(sightrange_bonus > 0 && !allowIncreasedSight())
sightrange_bonus = 0;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
profile_frame();
profile_start(0);
swap(gmatrix0, gmatrix);
gmatrix.clear();
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
wmspatial = vid.wallmode == 4 || vid.wallmode == 5;
wmescher = vid.wallmode == 3 || vid.wallmode == 5;
wmplain = vid.wallmode == 2 || vid.wallmode == 4;
wmascii = vid.wallmode == 0;
wmblack = vid.wallmode == 1;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
mmitem = vid.monmode >= 1;
mmmon = vid.monmode >= 2;
mmhigh = vid.monmode == 3 || vid.monmode == 5;
mmspatial = vid.monmode == 4 || vid.monmode == 5;
spatial_graphics = wmspatial || mmspatial;
2019-02-17 17:45:42 +00:00
#if CAP_RUG
if(rug::rugged && !rug::spatial_rug) spatial_graphics = false;
2019-02-17 17:45:42 +00:00
#endif
if(non_spatial_model())
spatial_graphics = false;
if(pmodel == mdDisk && abs(vid.alpha) < 1e-6) spatial_graphics = false;
if(!spatial_graphics) wmspatial = mmspatial = false;
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
DEBB(DF_GRAPH, (debugfile,"draw the map\n"));
for(int m=0; m<motypes; m++) if(isPrincess(eMonster(m)))
minf[m].name = princessgender() ? "Princess" : "Prince";
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
iinf[itSavedPrincess].name = minf[moPrincess].name;
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
for(int i=0; i<NUM_GS; i++) {
genderswitch_t& g = genderswitch[i];
if(g.gender != princessgender()) continue;
minf[g.m].help = g.desc;
minf[g.m].name = g.name;
2016-01-02 10:09:13 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(mapeditor::autochoose) mapeditor::ew = mapeditor::ewsearch;
mapeditor::ewsearch.dist = 1e30;
modist = 1e20; mouseover = NULL;
modist2 = 1e20; mouseover2 = NULL;
2017-07-12 16:03:53 +00:00
2018-04-11 11:16:40 +00:00
compute_graphical_distance();
2017-07-10 18:47:38 +00:00
centdist = 1e20;
if(!masterless) centerover.at = NULL;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
for(int i=0; i<multi::players; i++) {
multi::ccdist[i] = 1e20; multi::ccat[i] = NULL;
}
2015-08-08 13:57:52 +00:00
2017-08-06 12:50:16 +00:00
straightDownSeek = NULL; downspin = 0;
2017-07-22 23:33:27 +00:00
#if ISMOBILE
2017-07-10 18:47:38 +00:00
mouseovers = XLAT("No info about this...");
#endif
if(mouseout())
modist = -5;
playerfound = false;
// playerfoundL = false;
// playerfoundR = false;
2017-10-04 19:26:26 +00:00
arrowtraps.clear();
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
profile_start(1);
currentmap->draw();
2018-01-11 22:18:02 +00:00
drawWormSegments();
2017-09-30 09:46:41 +00:00
drawBlizzards();
2017-10-04 19:26:26 +00:00
drawArrowTraps();
2018-08-19 20:53:51 +00:00
precise_mouseover();
2017-07-16 21:00:55 +00:00
ivoryz = false;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
linepatterns::drawAll();
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
callhooks(hooks_frame);
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
profile_stop(1);
profile_start(4);
drawMarkers();
profile_stop(4);
drawFlashes();
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(multi::players > 1 && !shmup::on) {
if(shmup::centerplayer != -1)
cwtV = multi::whereis[shmup::centerplayer];
else {
hyperpoint h;
for(int i=0; i<3; i++) h[i] = 0;
for(int p=0; p<multi::players; p++) if(multi::playerActive(p)) {
hyperpoint h1 = tC0(multi::whereis[p]);
for(int i=0; i<3; i++) h[i] += h1[i];
}
h = mid(h, h);
cwtV = rgpushxto0(h);
}
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
if(shmup::on) {
if(shmup::players == 1)
cwtV = shmup::pc[0]->pat;
else if(shmup::centerplayer != -1)
cwtV = shmup::pc[shmup::centerplayer]->pat;
else {
2017-07-10 18:47:38 +00:00
hyperpoint h;
for(int i=0; i<3; i++) h[i] = 0;
for(int p=0; p<multi::players; p++) {
hyperpoint h1 = tC0(shmup::pc[p]->pat);
for(int i=0; i<3; i++) h[i] += h1[i];
}
h = mid(h, h);
cwtV = rgpushxto0(h);
}
}
2017-07-10 18:47:38 +00:00
2017-07-22 23:33:27 +00:00
#if CAP_SDL
2017-07-10 18:47:38 +00:00
Uint8 *keystate = SDL_GetKeyState(NULL);
lmouseover = mouseover;
bool useRangedOrb = (!(vid.shifttarget & 1) && haveRangedOrb() && lmouseover && lmouseover->cpdist > 1) || (keystate[SDLK_RSHIFT] | keystate[SDLK_LSHIFT]);
if(!useRangedOrb && !(cmode & sm::MAP) && !(cmode & sm::DRAW) && DEFAULTCONTROL && !mouseout()) {
dynamicval<eGravity> gs(gravity_state, gravity_state);
2017-07-10 18:47:38 +00:00
void calcMousedest();
calcMousedest();
cellwalker cw = cwt; bool f = flipplayer;
items[itWarning]+=2;
bool recorduse[ittypes];
for(int i=0; i<ittypes; i++) recorduse[i] = orbused[i];
movepcto(mousedest.d, mousedest.subdir, true);
for(int i=0; i<ittypes; i++) orbused[i] = recorduse[i];
items[itWarning] -= 2;
2017-07-22 23:33:27 +00:00
if(cw.spin != cwt.spin) mirror::act(-mousedest.d, mirror::SPINSINGLE);
2017-07-10 18:47:38 +00:00
cwt = cw; flipplayer = f;
lmouseover = mousedest.d >= 0 ? cwt.at->modmove(cwt.spin + mousedest.d) : cwt.at;
2017-07-10 18:47:38 +00:00
}
#endif
profile_stop(0);
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
void drawmovestar(double dx, double dy) {
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(viewdists) return;
2019-02-26 10:57:50 +00:00
if(DIM == 3) return;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
DEBB(DF_GRAPH, (debugfile,"draw movestar\n"));
if(!playerfound) return;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(shmup::on) return;
2017-07-22 23:33:27 +00:00
#if CAP_RUG
2017-07-10 18:47:38 +00:00
if(rug::rugged && multi::players == 1 && !multi::alwaysuse) return;
#endif
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
hyperpoint H = tC0(cwtV);
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
transmatrix Centered = Id;
2016-08-26 09:58:03 +00:00
if(masterless)
2019-02-21 17:46:53 +00:00
Centered = eupush(H);
2017-07-10 18:47:38 +00:00
else if(R > 1e-9) Centered = rgpushxto0(H);
2016-01-02 10:09:13 +00:00
2019-02-22 19:58:40 +00:00
Centered = Centered * rgpushxto0(hpxy(dx*5, dy*5));
2017-07-10 18:47:38 +00:00
if(multi::cpid >= 0) multi::crosscenter[multi::cpid] = Centered;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
int rax = vid.axes;
if(rax == 1) rax = drawstaratvec(dx, dy) ? 2 : 0;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
if(rax == 0 || vid.axes == 4) return;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
int starcol = getcs().uicolor;
2019-02-17 17:41:40 +00:00
ignore(starcol);
if(0);
#if CAP_SHAPES
2019-02-17 17:41:40 +00:00
else if(vid.axes == 3)
2017-07-10 18:47:38 +00:00
queuepoly(Centered, shMovestar, starcol);
#endif
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
else for(int d=0; d<8; d++) {
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
color_t col = starcol;
2017-07-22 23:33:27 +00:00
#if ISPANDORA
2017-07-10 18:47:38 +00:00
if(leftclick && (d == 2 || d == 6 || d == 1 || d == 7)) col &= 0xFFFFFF3F;
if(rightclick && (d == 2 || d == 6 || d == 3 || d == 5)) col &= 0xFFFFFF3F;
if(!leftclick && !rightclick && (d&1)) col &= 0xFFFFFF3F;
2015-08-08 13:57:52 +00:00
#endif
2017-07-10 18:47:38 +00:00
// EUCLIDEAN
2018-08-19 14:28:36 +00:00
queueline(tC0(Centered), Centered * xspinpush0(d * M_PI / 4, euclid ? 0.5 : d==0?.7:d==2?.5:.2), col, 3 + vid.linequality);
#endif
2016-01-02 10:09:13 +00:00
}
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
// old style joystick control
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
bool dronemode;
2015-08-08 13:57:52 +00:00
purehookset hooks_calcparam;
2017-07-10 18:47:38 +00:00
void calcparam() {
auto cd = current_display;
2017-07-10 18:47:38 +00:00
DEBB(DF_GRAPH, (debugfile,"calc param\n"));
2018-11-17 23:35:50 +00:00
cd->xtop = vid.xres * cd->xmin;
cd->ytop = vid.yres * cd->ymin;
cd->xsize = vid.xres * (cd->xmax - cd->xmin);
cd->ysize = vid.yres * (cd->ymax - cd->ymin);
cd->xcenter = cd->xtop + cd->xsize / 2;
cd->ycenter = cd->ytop + cd->ysize / 2;
if(vid.scale > -1e-2 && vid.scale < 1e-2) vid.scale = 1;
2018-11-17 23:35:50 +00:00
ld realradius = min(cd->xsize / 2, cd->ysize / 2);
2017-12-14 01:53:29 +00:00
cd->scrsize = realradius - (inHighQual ? 0 : ISANDROID ? 2 : ISIOS ? 40 : 40);
2017-03-23 10:53:57 +00:00
2018-11-27 01:33:10 +00:00
current_display->sidescreen = false;
2015-08-08 13:57:52 +00:00
2018-02-20 21:11:39 +00:00
if(vid.xres < vid.yres - 2 * vid.fsize && !inHighQual) {
cd->ycenter = vid.yres - cd->scrsize - vid.fsize;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else {
if(vid.xres > vid.yres * 4/3+16 && (cmode & sm::SIDE))
2018-11-27 01:33:10 +00:00
current_display->sidescreen = true;
2017-07-22 23:33:27 +00:00
#if CAP_TOUR
2017-07-10 18:47:38 +00:00
if(tour::on && (tour::slides[tour::currentslide].flags & tour::SIDESCREEN))
2018-11-27 01:33:10 +00:00
current_display->sidescreen = true;
2017-03-23 10:53:57 +00:00
#endif
2018-11-27 01:33:10 +00:00
if(current_display->sidescreen) cd->xcenter = vid.yres/2;
2017-03-23 10:53:57 +00:00
}
2018-02-20 21:11:39 +00:00
cd->radius = vid.scale * cd->scrsize;
if(DIM == 3) cd->radius = cd->scrsize;
realradius = min(realradius, cd->radius);
2017-03-23 10:53:57 +00:00
if(dronemode) { cd->ycenter -= cd->radius; cd->ycenter += vid.fsize/2; cd->ycenter += vid.fsize/2; cd->radius *= 2; }
2017-12-25 09:26:50 +00:00
cd->xcenter += cd->scrsize * vid.xposition;
cd->ycenter += cd->scrsize * vid.yposition;
2017-03-23 10:53:57 +00:00
cd->tanfov = tan(vid.fov * degree / 2);
2018-02-03 12:41:49 +00:00
if(pmodel)
cd->scrdist = vid.xres / 2 / cd->tanfov;
2018-02-03 12:41:49 +00:00
else
cd->scrdist = cd->radius;
cd->scrdist_text = cd->scrdist;
callhooks(hooks_calcparam);
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2018-01-25 16:22:04 +00:00
function<void()> wrap_drawfullmap = drawfullmap;
bool force_sphere_outline = false;
2018-11-08 15:28:17 +00:00
void drawfullmap() {
DEBB(DF_GRAPH, (debugfile,"draw full map\n"));
ptds.clear();
/*
if(conformal::on) {
char ch = 'A';
for(auto& v: conformal::v) {
queuepoly(ggmatrix(v->base) * v->at, shTriangle, 0x306090C0);
queuechr(ggmatrix(v->base) * v->at * C0, 10, ch++, 0xFF0000);
}
}
*/
#if CAP_QUEUE
2018-11-08 15:28:17 +00:00
draw_boundary(0);
draw_boundary(1);
draw_model_elements();
2019-02-17 17:41:40 +00:00
#endif
2018-11-08 15:28:17 +00:00
/* if(vid.wallmode < 2 && !euclid && !patterns::whichShape) {
2018-06-22 12:47:24 +00:00
int ls = isize(lines);
2017-07-10 18:47:38 +00:00
if(ISMOBILE) ls /= 10;
for(int t=0; t<ls; t++) queueline(View * lines[t].P1, View * lines[t].P2, lines[t].col >> (darken+1));
} */
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
clearaura();
if(!nomap) drawthemap();
2017-07-10 18:47:38 +00:00
if(!inHighQual) {
2017-07-12 17:50:39 +00:00
if((cmode & sm::NORMAL) && !rug::rugged) {
2017-07-10 18:47:38 +00:00
if(multi::players > 1) {
transmatrix bcwtV = cwtV;
for(int i=0; i<multi::players; i++) if(multi::playerActive(i))
cwtV = multi::whereis[i], multi::cpid = i, drawmovestar(multi::mdx[i], multi::mdy[i]);
cwtV = bcwtV;
}
else if(multi::alwaysuse)
drawmovestar(multi::mdx[0], multi::mdy[0]);
else
drawmovestar(0, 0);
}
2017-07-22 23:33:27 +00:00
#if CAP_EDIT
2017-07-12 17:50:39 +00:00
if(cmode & sm::DRAW) mapeditor::drawGrid();
2017-07-22 23:33:27 +00:00
#endif
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
profile_start(2);
2017-07-16 21:00:55 +00:00
2017-07-10 18:47:38 +00:00
drawaura();
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
2017-07-10 18:47:38 +00:00
drawqueue();
2019-02-17 17:41:40 +00:00
#endif
2017-07-10 18:47:38 +00:00
profile_stop(2);
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
void gamescreen(int _darken) {
2017-11-07 15:42:31 +00:00
2019-03-09 15:20:06 +00:00
if(subscreens::split([=] () {
2018-11-17 18:30:50 +00:00
calcparam();
current_display->set_projection(0, false);
current_display->set_viewport(0);
compute_graphical_distance();
gamescreen(_darken);
})) {
2019-03-09 15:20:06 +00:00
if(racing::on) return;
// create the gmatrix
current_display->set_projection(0, false);
2019-03-09 15:20:06 +00:00
current_display->set_viewport(0);
View = subscreens::player_displays[0].view_matrix;
viewctr = subscreens::player_displays[0].view_center;
just_gmatrix = true;
currentmap->draw();
just_gmatrix = false;
return;
}
2018-11-17 18:30:50 +00:00
2018-11-27 01:33:10 +00:00
if((cmode & sm::MAYDARK) && !current_display->sidescreen) {
_darken += 2;
2017-11-07 15:42:31 +00:00
}
2017-07-10 18:47:38 +00:00
darken = _darken;
if(conformal::includeHistory) conformal::restore();
2017-07-04 13:38:33 +00:00
2018-09-10 15:58:36 +00:00
anims::apply();
2017-07-22 23:33:27 +00:00
#if CAP_RUG
2017-07-10 18:47:38 +00:00
if(rug::rugged) {
rug::actDraw();
2017-07-22 23:33:27 +00:00
} else
2017-07-04 13:38:33 +00:00
#endif
2018-01-25 16:22:04 +00:00
wrap_drawfullmap();
2018-09-10 15:58:36 +00:00
anims::rollback();
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(conformal::includeHistory) conformal::restoreBack();
2017-04-08 15:18:29 +00:00
2017-07-10 18:47:38 +00:00
poly_outline = OUTLINE_DEFAULT;
2017-07-22 23:33:27 +00:00
#if ISMOBILE
2017-07-10 18:47:38 +00:00
buttonclicked = false;
if((cmode & sm::NORMAL) && vid.stereo_mode != sLR) {
2017-07-10 18:47:38 +00:00
if(andmode == 0 && shmup::on) {
using namespace shmupballs;
calc();
drawCircle(xmove, yb, rad, OUTLINE_FORE);
drawCircle(xmove, yb, rad/2, OUTLINE_FORE);
drawCircle(xfire, yb, rad, 0xFF0000FF);
drawCircle(xfire, yb, rad/2, 0xFF0000FF);
}
2017-07-10 18:47:38 +00:00
else {
if(!haveMobileCompass()) displayabutton(-1, +1, XLAT(andmode == 0 && useRangedOrb ? "FIRE" : "MOVE"), andmode == 0 ? BTON : BTOFF);
2018-02-03 19:04:19 +00:00
displayabutton(+1, +1, rug::rugged ? "RUG" : XLAT(andmode == 1 ? "BACK" : "DRAG"), andmode == 1 ? BTON : BTOFF);
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
displayabutton(-1, -1, XLAT("INFO"), andmode == 12 ? BTON : BTOFF);
displayabutton(+1, -1, XLAT("MENU"), andmode == 3 ? BTON : BTOFF);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
#endif
2017-07-10 18:47:38 +00:00
darken = 0;
2017-12-25 09:26:50 +00:00
2018-01-05 16:30:03 +00:00
#if CAP_TEXTURE
2018-03-17 20:12:46 +00:00
if(texture::config.tstate == texture::tsAdjusting)
texture::config.drawRawTexture();
2018-01-05 16:30:03 +00:00
#endif
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
2018-03-02 12:03:50 +00:00
bool nohelp;
2017-07-10 18:47:38 +00:00
void normalscreen() {
help = "@";
2017-07-12 16:03:53 +00:00
mouseovers = XLAT("Press F1 or right click for help");
2017-07-22 23:33:27 +00:00
#if CAP_TOUR
2017-07-12 16:03:53 +00:00
if(tour::on) mouseovers = tour::tourhelp;
#endif
2019-03-09 00:35:55 +00:00
if(DIM == 3 || !outofmap(mouseh)) getcstat = '-';
2017-07-12 17:50:39 +00:00
cmode = sm::NORMAL | sm::DOTOUR | sm::CENTER;
if(viewdists && show_distance_lists) cmode |= sm::SIDE | sm::MAYDARK;
2017-07-10 18:47:38 +00:00
gamescreen(hiliteclick && mmmon ? 1 : 0); drawStats();
2017-08-06 12:50:16 +00:00
if(nomenukey || ISMOBILE)
2017-07-10 18:47:38 +00:00
;
2017-07-22 23:33:27 +00:00
#if CAP_TOUR
2017-07-10 18:47:38 +00:00
else if(tour::on)
displayButton(vid.xres-8, vid.yres-vid.fsize, XLAT("(ESC) tour menu"), SDLK_ESCAPE, 16);
else
2015-08-08 13:57:52 +00:00
#endif
2017-07-10 18:47:38 +00:00
displayButton(vid.xres-8, vid.yres-vid.fsize, XLAT("(v) menu"), 'v', 16);
keyhandler = handleKeyNormal;
2017-07-12 16:03:53 +00:00
if(!playerfound && !anims::any_on())
displayButton(current_display->xcenter, current_display->ycenter, XLAT(mousing ? "find the player" : "press SPACE to find the player"), ' ', 8);
2017-07-12 16:03:53 +00:00
describeMouseover();
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
vector< function<void()> > screens = { normalscreen };
2015-08-08 13:57:52 +00:00
2017-07-12 17:50:39 +00:00
int cmode;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
void drawscreen() {
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
if(vid.xres == 0 || vid.yres == 0) return;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
DEBB(DF_GRAPH, (debugfile,"drawscreen\n"));
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
calcparam();
2017-11-06 18:24:02 +00:00
// rug::setVidParam();
2017-07-22 23:33:27 +00:00
#if CAP_GL
2017-07-10 18:47:38 +00:00
if(vid.usingGL) setGLProjection();
#endif
2017-07-22 23:33:27 +00:00
#if CAP_SDL
2017-07-10 18:47:38 +00:00
// SDL_LockSurface(s);
// unsigned char *b = (unsigned char*) s->pixels;
// int n = vid.xres * vid.yres * 4;
// while(n) *b >>= 1, b++, n--;
// memset(s->pixels, 0, vid.xres * vid.yres * 4);
2017-07-22 23:33:27 +00:00
#if CAP_GL
if(!vid.usingGL)
#endif
SDL_FillRect(s, NULL, backcolor);
2017-03-23 10:53:57 +00:00
#endif
2017-07-10 18:47:38 +00:00
// displaynum(vx,100, 0, 24, 0xc0c0c0, celldist(cwt.at), ":");
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
lgetcstat = getcstat;
getcstat = 0; inslider = false;
2017-03-23 10:53:57 +00:00
2017-07-12 16:03:53 +00:00
mouseovers = " ";
2017-07-12 17:50:39 +00:00
cmode = 0;
keyhandler = [] (int sym, int uni) {};
#if CAP_SDL
joyhandler = [] (SDL_Event& ev) { return false; };
#endif
2018-06-22 12:47:24 +00:00
if(!isize(screens)) pushScreen(normalscreen);
2017-07-10 18:47:38 +00:00
screens.back()();
2016-01-02 10:09:13 +00:00
2017-07-22 23:33:27 +00:00
#if !ISMOBILE
color_t col = linf[cwt.at->land].color;
if(cwt.at->land == laRedRock) col = 0xC00000;
2018-03-02 12:03:50 +00:00
if(!nohelp)
displayfr(vid.xres/2, vid.fsize, 2, vid.fsize, mouseovers, col, 8);
2017-07-12 16:03:53 +00:00
#endif
2017-07-10 18:47:38 +00:00
drawmessages();
2017-03-23 10:53:57 +00:00
2017-07-12 17:50:39 +00:00
bool normal = cmode & sm::NORMAL;
if((havewhat&HF_BUG) && darken == 0 && normal) for(int k=0; k<3; k++)
2017-07-10 18:47:38 +00:00
displayfr(vid.xres/2 + vid.fsize * 5 * (k-1), vid.fsize*2, 2, vid.fsize,
its(hive::bugcount[k]), minf[moBug0+k].color, 8);
bool minefieldNearby = false;
int mines[4], tmines=0;
for(int p=0; p<numplayers(); p++) {
mines[p] = 0;
cell *c = playerpos(p);
if(!c) continue;
for(int i=0; i<c->type; i++) if(c->move(i)) {
if(c->move(i)->land == laMinefield)
2017-07-10 18:47:38 +00:00
minefieldNearby = true;
if(c->move(i)->wall == waMineMine) {
2017-07-10 18:47:38 +00:00
bool ep = false;
if(!ep) mines[p]++, tmines++;
2016-01-02 10:09:13 +00:00
}
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
}
2016-01-02 10:09:13 +00:00
if((minefieldNearby || tmines) && !items[itOrbAether] && !last_gravity_state && darken == 0 && normal) {
2017-07-10 18:47:38 +00:00
string s;
if(tmines > 7) tmines = 7;
color_t col = minecolors[tmines];
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(tmines == 7) seenSevenMines = true;
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
for(int p=0; p<numplayers(); p++) if(multi::playerActive(p))
displayfr(vid.xres * (p+.5) / numplayers(),
current_display->ycenter - current_display->radius * 3/4, 2,
2017-07-10 18:47:38 +00:00
vid.fsize,
XLAT(minetexts[mines[p]]), minecolors[mines[p]], 8);
2015-08-08 13:57:52 +00:00
if(minefieldNearby && !shmup::on && cwt.at->land != laMinefield && cwt.peek()->land != laMinefield) {
displayfr(vid.xres/2, current_display->ycenter - current_display->radius * 3/4 - vid.fsize*3/2, 2,
2017-07-10 18:47:38 +00:00
vid.fsize,
XLAT("WARNING: you are entering a minefield!"),
col, 8);
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
// SDL_UnlockSurface(s);
2017-03-23 10:53:57 +00:00
2018-09-04 21:27:27 +00:00
glflush();
2017-07-10 18:47:38 +00:00
DEBT("swapbuffers");
2017-07-22 23:33:27 +00:00
#if CAP_SDL
#if CAP_GL
2017-07-10 18:47:38 +00:00
if(vid.usingGL) SDL_GL_SwapBuffers(); else
2015-08-08 13:57:52 +00:00
#endif
2017-07-10 18:47:38 +00:00
SDL_UpdateRect(s, 0, 0, vid.xres, vid.yres);
2015-08-08 13:57:52 +00:00
#endif
2017-07-10 18:47:38 +00:00
//printf("\ec");
}
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
void restartGraph() {
DEBB(DF_INIT, (debugfile,"restartGraph\n"));
View = Id;
if(!autocheat) linepatterns::clearAll();
2017-07-10 18:47:38 +00:00
if(currentmap) {
if(masterless) {
centerover = vec_to_cellwalker(0);
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
else {
viewctr.at = currentmap->getOrigin();
2017-07-10 18:47:38 +00:00
viewctr.spin = 0;
viewctr.mirrored = false;
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
if(sphere) View = spin(-M_PI/2);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
2018-01-13 18:20:46 +00:00
void clearAnimations() {
for(int i=0; i<ANIMLAYERS; i++) animations[i].clear();
flashes.clear();
fallanims.clear();
}
2017-07-10 18:47:38 +00:00
auto graphcm = addHook(clearmemory, 0, [] () {
2016-08-26 09:58:03 +00:00
DEBB(DF_INIT, (debugfile,"clear graph memory\n"));
mouseover = centerover.at = lmouseover = NULL;
2017-03-23 10:53:57 +00:00
gmatrix.clear(); gmatrix0.clear();
2018-01-13 18:20:46 +00:00
clearAnimations();
2017-07-10 18:47:38 +00:00
});
2017-03-23 10:53:57 +00:00
void resetGeometry() {
2019-03-11 17:46:34 +00:00
if(DIM == 3 && !floor_textures)
make_floor_textures();
2017-03-23 10:53:57 +00:00
precalc();
2019-02-17 17:33:15 +00:00
#if CAP_FIELD
if(hyperbolic && &currfp != &fieldpattern::fp_invalid) currfp.analyze();
2019-02-17 17:33:15 +00:00
#endif
2017-07-22 23:33:27 +00:00
#if CAP_GL
2017-03-23 10:53:57 +00:00
resetGL();
2017-07-04 13:38:33 +00:00
#endif
2017-03-23 10:53:57 +00:00
}
//=== animation
map<cell*, animation> animations[ANIMLAYERS];
int revhint(cell *c, int hint) {
if(hint >= 0 && hint < c->type) return c->c.spin(hint);
else return hint;
}
bool compute_relamatrix(cell *src, cell *tgt, int direction_hint, transmatrix& T) {
if(confusingGeometry()) {
T = calc_relative_matrix(src, tgt, revhint(src, direction_hint));
}
else {
if(gmatrix.count(src) && gmatrix.count(tgt))
T = inverse(gmatrix[tgt]) * gmatrix[src];
else
return false;
2017-03-23 10:53:57 +00:00
}
return true;
2017-03-23 10:53:57 +00:00
}
void animateMovement(cell *src, cell *tgt, int layer, int direction_hint) {
2018-01-25 16:19:50 +00:00
if(vid.mspeed >= 5) return; // no animations!
transmatrix T;
if(!compute_relamatrix(src, tgt, direction_hint, T)) return;
animation& a = animations[layer][tgt];
if(animations[layer].count(src)) {
a = animations[layer][src];
a.wherenow = T * a.wherenow;
animations[layer].erase(src);
a.attacking = 0;
}
else {
a.ltick = ticks;
a.wherenow = T;
a.footphase = 0;
2018-01-25 16:19:50 +00:00
}
}
void animateAttack(cell *src, cell *tgt, int layer, int direction_hint) {
if(vid.mspeed >= 5) return; // no animations!
transmatrix T;
if(!compute_relamatrix(src, tgt, direction_hint, T)) return;
bool newanim = !animations[layer].count(src);
animation& a = animations[layer][src];
a.attacking = 1;
a.attackat = rspintox(tC0(inverse(T))) * xpush(hdist0(T*C0) / 3);
if(newanim) a.wherenow = Id, a.ltick = ticks, a.footphase = 0;
}
2017-03-23 10:53:57 +00:00
vector<pair<cell*, animation> > animstack;
void indAnimateMovement(cell *src, cell *tgt, int layer, int direction_hint) {
2017-03-23 10:53:57 +00:00
if(vid.mspeed >= 5) return; // no animations!
if(animations[layer].count(tgt)) {
animation res = animations[layer][tgt];
animations[layer].erase(tgt);
animateMovement(src, tgt, layer, direction_hint);
2017-03-23 10:53:57 +00:00
if(animations[layer].count(tgt))
animstack.push_back(make_pair(tgt, animations[layer][tgt]));
animations[layer][tgt] = res;
}
else {
animateMovement(src, tgt, layer, direction_hint);
2017-03-23 10:53:57 +00:00
if(animations[layer].count(tgt)) {
animstack.push_back(make_pair(tgt, animations[layer][tgt]));
animations[layer].erase(tgt);
}
}
}
void commitAnimations(int layer) {
2018-06-22 12:47:24 +00:00
for(int i=0; i<isize(animstack); i++)
2017-03-23 10:53:57 +00:00
animations[layer][animstack[i].first] = animstack[i].second;
animstack.clear();
}
void animateReplacement(cell *a, cell *b, int layer, int direction_hinta, int direction_hintb) {
2017-03-23 10:53:57 +00:00
if(vid.mspeed >= 5) return; // no animations!
static cell c1;
gmatrix[&c1] = gmatrix[b];
if(animations[layer].count(b)) animations[layer][&c1] = animations[layer][b];
animateMovement(a, b, layer, direction_hinta);
animateMovement(&c1, a, layer, direction_hintb);
2017-03-23 10:53:57 +00:00
}
2017-08-06 12:50:16 +00:00
void drawBug(const cellwalker& cw, color_t col) {
#if CAP_SHAPES
2017-08-06 12:50:16 +00:00
initquickqueue();
transmatrix V = ggmatrix(cw.at);
if(cw.spin) V = V * ddspin(cw.at, cw.spin, M_PI);
2017-08-06 12:50:16 +00:00
queuepoly(V, shBugBody, col);
quickqueue();
#endif
2017-08-06 12:50:16 +00:00
}
cell *viewcenter() {
if(masterless) return centerover.at;
else return viewctr.at->c7;
2017-08-06 12:50:16 +00:00
}
bool inscreenrange(cell *c) {
2018-04-06 21:28:58 +00:00
if(sphere) return true;
2018-04-11 11:16:40 +00:00
if(euclid) return celldistance(viewcenter(), c) <= get_sightrange_ambush();
return heptdistance(viewcenter(), c) <= 8;
2017-08-06 12:50:16 +00:00
}
}