1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2024-12-27 18:40:35 +00:00
hyperrogue/graph.cpp

6141 lines
205 KiB
C++
Raw Normal View History

// Hyperbolic Rogue -- main graphics file
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
2015-08-08 13:57:52 +00:00
/** \file graph.cpp
* \brief Drawing cells, monsters, items, etc.
*/
2016-08-26 09:58:03 +00:00
#include "hyper.h"
namespace hr {
2019-10-25 10:44:41 +00:00
EX int last_firelimit;
EX int firelimit;
2019-08-09 19:00:52 +00:00
EX int inmirrorcount = 0;
2017-07-16 21:00:55 +00:00
/** wall optimization: do not draw things beyond walls */
EX bool wallopt;
2019-12-06 10:45:19 +00:00
EX bool in_wallopt() { return wallopt || racing::on; }
2019-08-09 21:24:33 +00:00
EX bool spatial_graphics;
EX bool wmspatial, wmescher, wmplain, wmblack, wmascii, wmascii3;
2019-08-09 21:24:33 +00:00
EX bool mmspatial, mmhigh, mmmon, mmitem;
2017-03-23 10:53:57 +00:00
2020-11-01 20:20:54 +00:00
EX ld panini_alpha = 0;
2021-03-21 10:28:10 +00:00
EX ld stereo_alpha = 0;
2020-11-01 20:20:54 +00:00
2019-08-09 19:00:52 +00:00
EX int detaillevel = 0;
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX bool first_cell_to_draw = true;
2019-04-15 21:29:07 +00:00
2019-08-09 19:00:52 +00:00
EX bool in_perspective() {
return models::is_perspective(pconf.model);
2020-11-08 11:09:42 +00:00
}
EX bool in_perspective_v() {
return models::is_perspective(vpconf.model);
2019-07-28 09:07:21 +00:00
}
2019-08-09 19:00:52 +00:00
EX bool hide_player() {
2019-08-15 13:05:43 +00:00
return GDIM == 3 && playermoved && vid.yshift == 0 && vid.sspeed > -5 && in_perspective() && (first_cell_to_draw || elliptic) && (WDIM == 3 || vid.camera == 0) && !inmirrorcount
2019-06-01 14:59:04 +00:00
#if CAP_RACING
&& !(racing::on && !racing::use_standard_centering() && !racing::player_relative)
2019-06-01 14:59:04 +00:00
#endif
;
}
EX transmatrix ddspin180(cell *c, int dir) { return ddspin(c, dir, M_PI); }
EX transmatrix iddspin180(cell *c, int dir) { return iddspin(c, dir, M_PI); }
2022-03-27 10:42:44 +00:00
#if HDR
template<class T>
class span {
T *begin_ = nullptr;
T *end_ = nullptr;
public:
explicit span() = default;
explicit span(T *p, int n) : begin_(p), end_(p + n) {}
T *begin() const { return begin_; }
T *end() const { return end_; }
};
template<class Map, class Key>
hr::span<const shiftmatrix> span_at(const Map& map, const Key& key) {
auto it = map.find(key);
return (it == map.end()) ? hr::span<const shiftmatrix>() : hr::span<const shiftmatrix>(it->second.data(), it->second.size());
}
2022-03-27 10:42:44 +00:00
#endif
EX hookset<bool(int sym, int uni)> hooks_handleKey;
EX hookset<bool(cell *c, const shiftmatrix& V)> hooks_drawcell;
2019-08-09 19:00:52 +00:00
EX purehookset hooks_frame, hooks_markers;
2017-07-10 18:47:38 +00:00
2019-08-09 19:00:52 +00:00
EX ld animation_factor = 1;
EX int animation_lcm = 0;
2019-08-09 19:00:52 +00:00
EX ld ptick(int period, ld phase IS(0)) {
if(animation_lcm) animation_lcm = animation_lcm * (period / gcd(animation_lcm, period));
return (ticks * animation_factor * vid.ispeed) / period + phase * TAU;
}
2019-08-09 19:00:52 +00:00
EX ld fractick(int period, ld phase IS(0)) {
ld t = ptick(period, phase) / TAU;
t -= floor(t);
if(t<0) t++;
return t;
}
2019-08-09 19:00:52 +00:00
EX ld sintick(int period, ld phase IS(0)) {
return sin(ptick(period, phase));
}
2019-08-09 19:00:52 +00:00
EX transmatrix spintick(int period, ld phase IS(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
2019-08-09 19:00:52 +00:00
EX bool inHighQual; // taking high quality screenshot
2019-08-09 20:07:03 +00:00
EX bool auraNOGL; // aura without GL
2015-08-08 13:57:52 +00:00
//
int axestate;
2019-08-09 19:00:52 +00:00
EX int ticks;
EX int frameid;
2015-08-08 13:57:52 +00:00
EX bool camelotcheat;
2019-08-09 19:00:52 +00:00
EX bool nomap;
2017-04-08 15:18:29 +00:00
2019-08-09 20:07:03 +00:00
EX eItem orbToTarget;
EX eMonster monsterToSummon;
2017-03-23 10:53:57 +00:00
2019-08-09 20:07:03 +00:00
EX int sightrange_bonus = 0;
2017-03-23 10:53:57 +00:00
2019-08-09 19:00:52 +00:00
EX string mouseovers;
2015-08-08 13:57:52 +00:00
2019-08-09 19:00:52 +00:00
EX int darken = 0;
2015-08-08 13:57:52 +00:00
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;
2019-08-09 19:00:52 +00:00
EX bool doHighlight() {
2020-09-15 18:39:15 +00:00
return mmhigh;
2016-01-02 10:09:13 +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 TAU * dir / c->type;
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
2020-05-29 00:42:54 +00:00
/** @brief used to alternate colors depending on distance to something. In chessboard-patterned geometries, also use a third step */
EX int flip_dark(int f, int a0, int a1) {
if(geosupport_chessboard()) {
f = gmod(f, 3);
return a0 + (a1-a0) * f / 2;
}
else
return (f&1) ? a1 : a0;
}
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;
2021-03-06 10:46:13 +00:00
for(cell *pc: player_positions())
if(items[itOrbFish] && isWatery(pc) && z != 3) return watercolor(ph);
2017-07-10 18:47:38 +00:00
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
2019-08-09 20:07:03 +00:00
EX int lightat, safetyat;
2019-08-09 19:00:52 +00:00
EX void drawLightning() { lightat = ticks; }
EX void drawSafety() { safetyat = ticks; }
2015-08-08 13:57:52 +00:00
EX void drawShield(const shiftmatrix& 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 ? cgi.hexf : cgi.hexf - .1;
2017-07-10 18:47:38 +00:00
int mt = sphere ? 7 : 5;
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2019-08-15 13:05:43 +00:00
if(GDIM == 3)
2023-02-04 00:58:13 +00:00
queueball(V * lzpush(cgi.GROIN1), cgi.human_height / 2, darkena(col, 0, 0xFF), itOrbShield);
2019-06-01 14:59:04 +00:00
#else
if(1) ;
#endif
2019-05-11 13:04:54 +00:00
else {
for(ld a=0; a<=cgi.S84*mt+1e-6; a+=pow(.5, vid.linequality))
curvepoint(xspinpush0(a * cgi.S_step, d + sin(ds + 90._deg*a/mt)*.1));
queuecurve(V, darkena(col, 0, 0xFF), 0x8080808, PPR::LINE);
2019-05-11 13:04:54 +00:00
}
#endif
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2022-08-27 23:37:49 +00:00
void drawSpeed(const shiftmatrix& V, ld scale=1) {
#if CAP_CURVE
ld ds = ptick(10);
color_t col = darkena(iinf[itOrbSpeed].color, 0, 0xFF);
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2023-02-04 00:58:13 +00:00
if(GDIM == 3) queueball(V * lzpush(cgi.GROIN1), cgi.human_height * 0.55, col, itOrbSpeed);
2019-06-01 14:59:04 +00:00
else
#endif
for(int b=0; b<cgi.S84; b+=cgi.S14) {
2018-08-01 09:07:22 +00:00
PRING(a)
curvepoint(xspinpush0((ds+b+a) * cgi.S_step, cgi.hexf*a/cgi.S84*scale));
queuecurve(V, col, 0x8080808, PPR::LINE);
2017-03-23 10:53:57 +00:00
}
#endif
2015-08-08 13:57:52 +00:00
}
void drawSafety(const shiftmatrix& V, int ct) {
#if CAP_QUEUE
ld ds = ptick(50);
color_t col = darkena(iinf[itOrbSafety].color, 0, 0xFF);
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2019-08-15 13:05:43 +00:00
if(GDIM == 3) {
2023-02-04 00:58:13 +00:00
queueball(V * lzpush(cgi.GROIN1), 2*cgi.hexf, col, itOrbSafety);
2019-06-01 14:59:04 +00:00
return;
}
#endif
for(int a=0; a<ct; a++)
queueline(V*xspinpush0((ds+a*cgi.S84/ct) * cgi.S_step, 2*cgi.hexf), V*xspinpush0((ds+(a+(ct-1)/2)*cgi.S84/ct) * cgi.S_step, 2*cgi.hexf), col, vid.linequality);
#endif
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
void drawFlash(const shiftmatrix& 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 = cgi.hexf * (2.5 + .5 * sin(ds+u*.3));
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2019-08-15 13:05:43 +00:00
if(GDIM == 3) {
2023-02-04 00:58:13 +00:00
queueball(V * lzpush(cgi.GROIN1), rad, col, itOrbFlash);
2019-05-11 13:04:54 +00:00
}
2019-06-01 14:59:04 +00:00
#else
if(1) ;
#endif
2019-05-11 13:04:54 +00:00
else {
PRING(a) curvepoint(xspinpush0(a * cgi.S_step, rad));
queuecurve(V, col, 0x8080808, PPR::LINE);
2019-05-11 13:04:54 +00:00
}
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
2019-10-25 10:44:41 +00:00
EX ld cheilevel(ld v) {
return cgi.FLOOR + (cgi.HEAD - cgi.FLOOR) * v;
2019-05-15 08:51:46 +00:00
}
2019-10-25 10:44:41 +00:00
EX transmatrix chei(const transmatrix V, int a, int b) {
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2019-08-15 13:05:43 +00:00
if(GDIM == 2) return V;
2023-02-04 00:58:13 +00:00
return V * lzpush(cheilevel((a+.5) / b));
2019-06-01 14:59:04 +00:00
#else
return V;
#endif
2019-05-11 13:04:54 +00:00
}
EX shiftmatrix chei(const shiftmatrix V, int a, int b) {
#if MAXMDIM >= 4
if(GDIM == 2) return V;
2023-02-04 00:58:13 +00:00
return V * lzpush(cheilevel((a+.5) / b));
#else
return V;
#endif
}
void drawLove(const shiftmatrix& 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++) {
shiftmatrix V1 = chei(V, u, 5);
2018-08-01 09:07:22 +00:00
PRING(a) {
double d = (1 + cos(a * cgi.S_step)) / 2;
double z = a; if(z>cgi.S42) z = cgi.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
ld rad = cgi.hexf * (2.5 + .5 * sin(ds+u*.3)) * d;
curvepoint(xspinpush0((cgi.S42+hdir+a-1) * cgi.S_step, rad));
2017-07-10 18:47:38 +00:00
}
queuecurve(V1, col, 0x8080808, PPR::LINE);
2017-03-23 10:53:57 +00:00
}
#endif
2015-08-08 13:57:52 +00:00
}
2021-05-21 23:26:59 +00:00
void drawWinter(const shiftmatrix& V, ld hdir, color_t col) {
#if CAP_QUEUE
float ds = ptick(300);
2021-05-21 23:26:59 +00:00
col = darkena(col, 0, 0xFF);
2017-07-10 18:47:38 +00:00
for(int u=0; u<20; u++) {
ld rad = sin(ds+u * TAU / 20) * M_PI / S7;
shiftmatrix V1 = chei(V, u, 20);
queueline(V1*xspinpush0(M_PI+hdir+rad, cgi.hexf*.5), V1*xspinpush0(M_PI+hdir+rad, cgi.hexf*3), col, 2 + vid.linequality);
2015-08-08 13:57:52 +00:00
}
#endif
2015-08-08 13:57:52 +00:00
}
void drawLightning(const shiftmatrix& V) {
#if CAP_QUEUE
float ds = ptick(600);
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, rad;
if(vid.flasheffects) {
leng = 0.5 / (0.1 + (rand() % 100) / 100.0);
rad = rand() % 1000;
}
else {
if(u % 5) leng = 1.25 + sintick(200, ld(u) * 1.25) * 0.25;
else leng = 2 + sintick(200, ld(u) * 1.25);
rad = (u + ds) * TAU / 20;
}
shiftmatrix V1 = chei(V, u, 20);
queueline(V1*xspinpush0(rad, cgi.hexf*0.3), V1*xspinpush0(rad, cgi.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
void drawCurse(const shiftmatrix& V, eItem it) {
2021-05-21 23:26:59 +00:00
#if CAP_QUEUE
float ds = ptick(450) + (it * 5.5); // Extra offset so both Gluttony and Repulsion are easily visible
color_t col = darkena(iinf[it].color, 0, 0xFF);
2021-05-21 23:26:59 +00:00
for(int u=0; u<20; u++) {
ld leng, rad;
if(vid.flasheffects) {
leng = 0.6 + 0.3 * randd();
rad = rand() % 1000;
}
else {
leng = 0.85 + sintick(150, ld(u) * 1.25) * 0.15;
rad = (u + ds) * TAU / 20;
}
2021-05-21 23:26:59 +00:00
shiftmatrix V1 = chei(V, u, 20);
queueline(V1*xspinpush0(rad, cgi.hexf*0.3), V1*xspinpush0(rad, cgi.hexf*leng), col, 2 + vid.linequality);
}
#endif
}
2019-08-15 13:05:43 +00:00
#define UNTRANS (GDIM == 3 ? 0x000000FF : 0)
2022-12-13 18:04:43 +00:00
EX transmatrix lpispin() {
return spin180();
2022-12-13 18:04:43 +00:00
}
EX void drawPlayerEffects(const shiftmatrix& V, const shiftmatrix& Vparam, cell *c, eMonster m) {
2022-01-08 17:53:57 +00:00
bool onplayer = m == moPlayer;
2017-07-10 18:47:38 +00:00
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);
if(items[itCurseGluttony]) drawCurse(V, itCurseGluttony);
if(items[itCurseRepulsion]) drawCurse(V, itCurseRepulsion);
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-08-19 08:55:02 +00:00
if(shmup::on && SWORDDIM == 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 ? cgi.shMagicShovel : cgi.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 ? cgi.shMagicShovel : cgi.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
2019-08-19 08:55:02 +00:00
else if(SWORDDIM == 3) {
2019-03-09 16:38:23 +00:00
#if CAP_SHAPES
shiftmatrix Vsword =
shmup::on ? V * shmup::swordmatrix[multi::cpid] * cspin90(2, 0)
: Vparam * rgpushxto0(inverse_shift(gmatrix[c], tC0(V))) * sword::dir[multi::cpid].T;
2019-03-09 16:38:23 +00:00
if(items[itOrbSword])
queuepoly(Vsword * cspin(1,2, ticks / 150.), (peace::on ? cgi.shMagicShovel : cgi.shMagicSword), darkena(iinf[itOrbSword].color, 0, 0xC0 + 0x30 * sintick(200)));
2019-03-09 16:38:23 +00:00
if(items[itOrbSword2])
2022-12-13 18:04:43 +00:00
queuepoly(Vsword * lpispin() * cspin(1,2, ticks / 150.), (peace::on ? cgi.shMagicShovel : cgi.shMagicSword), darkena(iinf[itOrbSword2].color, 0, 0xC0 + 0x30 * sintick(200)));
2019-03-09 16:38:23 +00:00
#endif
}
2017-07-10 18:47:38 +00:00
2015-08-08 13:57:52 +00:00
else {
int& ang = sword::dir[multi::cpid].angle;
2019-05-28 23:01:13 +00:00
ang %= sword::sword_angles;
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE || CAP_SHAPES
shiftmatrix Vnow = Vparam * rgpushxto0(inverse_shift(Vparam, tC0(V))) * ddspin180(c,0);
2019-02-17 17:41:40 +00:00
#endif
2019-05-28 23:01:13 +00:00
int adj = 1 - ((sword_angles/cwt.at->type)&1);
2017-07-10 18:47:38 +00:00
#if CAP_QUEUE
2022-12-08 18:38:06 +00:00
if(!euclid && !mhybrid) for(int a=0; a<sword_angles; a++) {
2017-07-10 18:47:38 +00:00
if(a == ang && items[itOrbSword]) continue;
if((a+sword_angles/2)%sword_angles == ang && items[itOrbSword2]) continue;
bool longer = sword::pos2(cwt.at, a-1) != sword::pos2(cwt.at, a+1);
2019-05-28 23:01:13 +00:00
if(sword_angles > 48 && !longer) continue;
color_t col = darkena(0xC0C0C0, 0, 0xFF);
ld l0 = PURE ? 0.6 * cgi.scalefactor : longer ? 0.36 : 0.4;
ld l1 = PURE ? 0.7 * cgi.scalefactor : longer ? 0.44 : 0.42;
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2023-02-04 00:58:13 +00:00
hyperpoint h0 = GDIM == 3 ? xpush(l0) * lzpush(cgi.FLOOR - cgi.human_height/50) * C0 : xpush0(l0);
hyperpoint h1 = GDIM == 3 ? xpush(l1) * lzpush(cgi.FLOOR - cgi.human_height/50) * C0 : xpush0(l1);
2019-06-01 14:59:04 +00:00
#else
hyperpoint h0 = xpush0(l0);
hyperpoint h1 = xpush0(l1);
#endif
shiftmatrix T = Vnow*spin((sword_angles + (-adj-2*a)) * M_PI / sword_angles);
queueline(T*h0, T*h1, col, 1, PPR::SUPERLINE);
2017-07-10 18:47:38 +00:00
}
#endif
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
if(items[itOrbSword])
2019-05-28 23:01:13 +00:00
queuepoly(Vnow*spin(M_PI+(-adj-2*ang)*M_PI/sword_angles), (peace::on ? cgi.shMagicShovel : cgi.shMagicSword), darkena(iinf[itOrbSword].color, 0, 0x80 + 0x70 * sintick(200)));
2017-07-10 18:47:38 +00:00
if(items[itOrbSword2])
2019-05-28 23:01:13 +00:00
queuepoly(Vnow*spin((-adj-2*ang)*M_PI/sword_angles), (peace::on ? cgi.shMagicShovel : cgi.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])
2021-05-21 23:26:59 +00:00
drawWinter(V, 0, iinf[itOrbWinter].color); // displaydir(c, cwt.spin));
if(items[itOrbFire])
drawWinter(V, 0, iinf[itOrbFire].color); // displaydir(c, cwt.spin));
if(items[itCurseWater])
drawWinter(V, 0, iinf[itCurseWater].color); // displaydir(c, cwt.spin));
2017-07-10 18:47:38 +00:00
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 = cgi.hexf * u / 250;
color_t col = darkena(iinf[itOrbSafety].color, 0, 0xFF);
2018-08-01 09:07:22 +00:00
PRING(a)
curvepoint(xspinpush0(a * cgi.S_step, rad));
queuecurve(V, 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
}
void drawStunStars(const shiftmatrix& V, int t) {
#if CAP_SHAPES
2017-07-10 18:47:38 +00:00
for(int i=0; i<3*t; i++) {
shiftmatrix V2 = V * spin(TAU * i / (3*t) + ptick(200));
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2023-02-04 00:58:13 +00:00
if(GDIM == 3) V2 = V2 * lzpush(cgi.HEAD);
2019-06-01 14:59:04 +00:00
#endif
queuepolyat(V2, cgi.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
2019-10-25 10:44:41 +00:00
EX 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 shiftmatrix& 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);
2020-03-02 02:06:15 +00:00
if(bits < 0) {
skincolor = 0xC00060;
shellcolor = 0xFF00FF;
scutecolor = 0x6000C0;
eyecolor = 0xFFFFFF;
}
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;
queuepoly(V, cgi.shTortoise[i][b+small], d);
2017-07-10 18:47:38 +00:00
if((i >= 5 && i <= 7) || (i >= 9 && i <= 10))
2022-12-13 18:04:43 +00:00
queuepoly(V * lmirror(), cgi.shTortoise[i][b+small], d);
2017-07-10 18:47:38 +00:00
if(i == 8) {
for(int k=0; k<stuntime; k++) {
eyecolor &= 0xFEFEFE;
eyecolor >>= 1;
}
queuepoly(V, cgi.shTortoise[12][b+small], darkena(eyecolor, 0, 0xFF));
2022-12-13 18:04:43 +00:00
queuepoly(V * lmirror(), cgi.shTortoise[12][b+small], darkena(eyecolor, 0, 0xFF));
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
2019-10-25 10:44:41 +00:00
EX int getMatchColor(int bits) {
2017-07-10 18:47:38 +00:00
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) / 90._deg);
2017-07-10 18:47:38 +00:00
return gradient(0x487830, mcol, 0, dd, 0xFF);
2017-03-23 10:53:57 +00:00
}
EX }
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
2019-08-09 21:08:42 +00:00
EX bool ivoryz;
/** Change the level of V. Takes ivoryz and all geometries into account */
EX transmatrix at_smart_lof(const transmatrix& V, ld lev) {
if(!mmspatial) return V;
if(ivoryz) return mzscale(V, lev);
return orthogonal_move_fol(V, lev);
}
EX shiftmatrix at_smart_lof(const shiftmatrix& V, ld lev) { return shiftless(at_smart_lof(V.T, lev), V.shift); }
void animallegs(const shiftmatrix& 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
2017-07-10 18:47:38 +00:00
hpcshape* sh[6][4] = {
{&cgi.shDogFrontPaw, &cgi.shDogRearPaw, &cgi.shDogFrontLeg, &cgi.shDogRearLeg},
{&cgi.shWolfFrontPaw, &cgi.shWolfRearPaw, &cgi.shWolfFrontLeg, &cgi.shWolfRearLeg},
{&cgi.shReptileFrontFoot, &cgi.shReptileRearFoot, &cgi.shReptileFrontLeg, &cgi.shReptileRearLeg},
{&cgi.shBugLeg, NULL, NULL, NULL},
{&cgi.shTrylobiteFrontClaw, &cgi.shTrylobiteRearClaw, &cgi.shTrylobiteFrontLeg, &cgi.shTrylobiteRearLeg},
{&cgi.shBullFrontHoof, &cgi.shBullRearHoof, &cgi.shBullFrontHoof, &cgi.shBullRearHoof},
2017-07-10 18:47:38 +00:00
};
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];
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
if(GDIM == 3 && !(embedded_plane && gproduct)) {
if(x[0]) queuepolyat(V * front_leg_move * cspin(0, 2, rightfoot / leg_length) * front_leg_move_inverse, *x[0], col, PPR::MONSTER_FOOT);
2022-12-13 18:04:43 +00:00
if(x[0]) queuepolyat(V * lmirror() * front_leg_move * cspin(0, 2, leftfoot / leg_length) * front_leg_move_inverse, *x[0], col, PPR::MONSTER_FOOT);
if(x[1]) queuepolyat(V * rear_leg_move * cspin(0, 2, -rightfoot / leg_length) * rear_leg_move_inverse, *x[1], col, PPR::MONSTER_FOOT);
2022-12-13 18:04:43 +00:00
if(x[1]) queuepolyat(V * lmirror() * rear_leg_move * cspin(0, 2, -leftfoot / leg_length) * rear_leg_move_inverse, *x[1], col, PPR::MONSTER_FOOT);
2019-05-16 15:22:04 +00:00
return;
}
2019-06-01 14:59:04 +00:00
#endif
2015-08-08 13:57:52 +00:00
const shiftmatrix VL = at_smart_lof(V, cgi.ALEG0);
const shiftmatrix VAML = at_smart_lof(V, cgi.ALEG);
2017-03-23 10:53:57 +00:00
if(x[0]) queuepolyat(VL * lxpush(rightfoot), *x[0], col, PPR::MONSTER_FOOT);
if(x[0]) queuepolyat(VL * lmirror() * lxpush(leftfoot), *x[0], col, PPR::MONSTER_FOOT);
if(x[1]) queuepolyat(VL * lxpush(-rightfoot), *x[1], col, PPR::MONSTER_FOOT);
if(x[1]) queuepolyat(VL * lmirror() * lxpush(-leftfoot), *x[1], col, PPR::MONSTER_FOOT);
2015-08-08 13:57:52 +00:00
if(x[2]) queuepolyat(VAML * lxpush(rightfoot/2), *x[2], col, PPR::MONSTER_FOOT);
if(x[2]) queuepolyat(VAML * lmirror() * lxpush(leftfoot/2), *x[2], col, PPR::MONSTER_FOOT);
if(x[3]) queuepolyat(VAML * lxpush(-rightfoot/2), *x[3], col, PPR::MONSTER_FOOT);
if(x[3]) queuepolyat(VAML * lmirror() * lxpush(-leftfoot/2), *x[3], col, PPR::MONSTER_FOOT);
#endif
2015-08-08 13:57:52 +00:00
}
2019-08-09 19:00:52 +00:00
EX bool noshadow;
#if CAP_SHAPES
EX void ShadowV(const shiftmatrix& V, const hpcshape& bp, PPR prio IS(PPR::MONSTER_SHADOW)) {
2019-05-13 07:34:22 +00:00
if(WDIM == 2 && GDIM == 3 && bp.shs != bp.she) {
if(noshadow) return;
auto& p = queuepolyat(V, bp, 0x18, PPR::TRANSPARENT_SHADOW);
2019-05-13 07:34:22 +00:00
p.outline = 0;
p.subprio = -100;
p.offset = bp.shs;
p.cnt = bp.she - bp.shs;
p.flags &=~ POLY_TRIANGLES;
p.tinf = NULL;
return;
}
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 shiftmatrix& V, color_t col, eMonster who, double footphase) {
2017-07-10 18:47:38 +00:00
2022-12-08 18:38:06 +00:00
#define VFOOT ((GDIM == 2 || mhybrid) ? V : at_smart_lof(V, cgi.LEG0))
#define VLEG at_smart_lof(V, cgi.LEG)
#define VGROIN at_smart_lof(V, cgi.GROIN)
#define VBODY at_smart_lof(V, cgi.BODY)
#define VBODY1 at_smart_lof(V, cgi.BODY1)
#define VBODY2 at_smart_lof(V, cgi.BODY2)
#define VBODY3 at_smart_lof(V, cgi.BODY3)
#define VNECK at_smart_lof(V, cgi.NECK)
#define VHEAD at_smart_lof(V, cgi.HEAD)
#define VHEAD1 at_smart_lof(V, cgi.HEAD1)
#define VHEAD2 at_smart_lof(V, cgi.HEAD2)
#define VHEAD3 at_smart_lof(V, cgi.HEAD3)
2017-07-10 18:47:38 +00:00
#define VALEGS V
#define VABODY at_smart_lof(V, cgi.ABODY)
#define VAHEAD at_smart_lof(V, cgi.AHEAD)
2017-07-10 18:47:38 +00:00
#define VFISH V
#define VBIRD ((GDIM == 3 || (where && bird_disruption(where))) ? (WDIM == 2 ? at_smart_lof(V, cgi.BIRD) : V) : at_smart_lof(V, cgi.BIRD + .05 * sintick(1000, static_cast<int>(reinterpret_cast<size_t>(where))/1000.)))
#define VGHOST at_smart_lof(V, cgi.GHOST)
2015-08-08 13:57:52 +00:00
#define VSLIMEEYE orthogonal_move_fol(V, cgi.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
2019-08-15 13:05:43 +00:00
if(detaillevel >= 2 && GDIM == 2) {
shiftmatrix VL = at_smart_lof(V, cgi.LEG1);
queuepoly(VL * xpush(rightfoot*3/4), cgi.shHumanLeg, col);
2022-12-13 18:04:43 +00:00
queuepoly(VL * lmirror() * xpush(-rightfoot*3/4), cgi.shHumanLeg, col);
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
2019-08-15 13:05:43 +00:00
if(GDIM == 2) {
shiftmatrix VL = at_smart_lof(V, cgi.LEG);
queuepoly(VL * xpush(rightfoot/2), cgi.shHumanLeg, col);
2022-12-13 18:04:43 +00:00
queuepoly(VL * lmirror() * xpush(-rightfoot/2), cgi.shHumanLeg, col);
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
2019-08-15 13:05:43 +00:00
if(detaillevel >= 2 && GDIM == 2) {
shiftmatrix VL = at_smart_lof(V, cgi.LEG3);
queuepoly(VL * xpush(rightfoot/4), cgi.shHumanLeg, col);
2022-12-13 18:04:43 +00:00
queuepoly(VL * lmirror() * xpush(-rightfoot/4), cgi.shHumanLeg, col);
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
shiftmatrix Tright, Tleft;
2023-01-26 23:27:10 +00:00
if(GDIM == 2 || mhybrid || cgi.emb->is_euc_in_product()) {
Tright = VFOOT * xpush(rightfoot);
2022-12-13 18:04:43 +00:00
Tleft = VFOOT * lmirror() * xpush(-rightfoot);
}
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
else {
shiftmatrix V1 = V;
2022-12-13 18:04:43 +00:00
if(WDIM == 2) V1 = V1 * lzpush(cgi.GROIN);
2023-01-26 23:27:10 +00:00
int zdir = cgi.emb->is_euc_in_nil() ? 1 : 2;
2022-12-13 18:04:43 +00:00
Tright = V1 * cspin(0, zdir, rightfoot/ leg_length);
Tleft = V1 * lmirror() * cspin(zdir, 0, rightfoot / leg_length);
Tright = V1; Tleft = V1 * lmirror();
if(WDIM == 2) Tleft = Tleft * lzpush(-cgi.GROIN), Tright = Tright * lzpush(-cgi.GROIN);
}
2019-06-01 14:59:04 +00:00
#endif
2019-08-15 13:05:43 +00:00
if(who == moWaterElemental && GDIM == 2) {
2017-07-10 18:47:38 +00:00
double fishtail = footfun(footphase / .4) / 4 * 1.5;
queuepoly(VFOOT * xpush(fishtail), cgi.shFishTail, watercolor(100));
2017-07-10 18:47:38 +00:00
}
else if(who == moSkeleton) {
queuepoly(Tright, cgi.shSkeletalFoot, col);
queuepoly(Tleft, cgi.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(Tright, cgi.shYetiFoot, col);
queuepoly(Tleft, cgi.shYetiFoot, col);
2015-08-08 13:57:52 +00:00
}
else {
queuepoly(Tright, cgi.shHumanFoot, col);
queuepoly(Tleft, cgi.shHumanFoot, col);
2015-08-08 13:57:52 +00:00
}
2019-08-15 13:05:43 +00:00
if(GDIM == 3 || !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(at_smart_lof(V, cgi.NECK1), cgi.shHumanNeck, col);
2017-07-10 18:47:38 +00:00
if(detaillevel >= 1) {
queuepoly(VGROIN, cgi.shHumanGroin, col);
if(who != moZombie) queuepoly(VNECK, cgi.shHumanNeck, col);
2015-08-08 13:57:52 +00:00
}
2017-07-10 18:47:38 +00:00
if(detaillevel >= 2) {
queuepoly(at_smart_lof(V, cgi.GROIN1), cgi.shHumanGroin, col);
if(who != moZombie) queuepoly(at_smart_lof(V, cgi.NECK3), cgi.shHumanNeck, col);
2016-08-26 09:58:03 +00:00
}
return spin(rightfoot * wobble);
2016-08-26 09:58:03 +00:00
}
#endif
2016-08-26 09:58:03 +00:00
2019-10-25 10:44:41 +00:00
EX bool drawstar(cell *c) {
2017-07-10 18:47:38 +00:00
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;
}
2019-10-25 10:44:41 +00:00
EX 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-10-25 10:44:41 +00:00
EX color_t kind_outline(eItem it) {
2019-02-28 15:24:46 +00:00
int k = itemclass(it);
if(k == IC_TREASURE)
return OUTLINE_TREASURE;
else if(k == IC_ORB)
return OUTLINE_ORB;
else
return OUTLINE_OTHER;
}
EX shiftmatrix face_the_player(const shiftmatrix V) {
2019-08-15 13:05:43 +00:00
if(GDIM == 2) return V;
2022-12-08 18:38:06 +00:00
if(mproduct) return orthogonal_move(V, cos(ptick(750)) * cgi.plevel / 16);
if(mhybrid) return V * zpush(cos(ptick(750)) * cgi.plevel / 16);
2019-08-26 07:06:39 +00:00
transmatrix dummy; /* used only in prod anyways */
2023-02-04 01:04:35 +00:00
if(embedded_plane && !cgi.emb->is_same_in_same()) return V;
if(nonisotropic) return shiftless(spin_towards(unshift(V), dummy, C0, 2, 0));
2021-02-04 20:29:11 +00:00
#if CAP_VR
if(vrhr::enabled) {
shiftpoint h = tC0(V);
hyperpoint uh = unshift(h);
2022-12-08 18:38:06 +00:00
return shiftless(cspin90(1, 2) * lrspintox(cspin90(2, 1) * uh) * xpush(hdist0(uh)) * cspin90(0, 2) * spin270());
2021-02-04 20:29:11 +00:00
}
#endif
return rgpushxto0(tC0(V));
}
2019-08-09 21:08:42 +00:00
EX hpcshape& orbshape(eOrbshape s) {
2022-10-06 11:10:57 +00:00
if(vid.orbmode == 0) return cgi.shRing;
switch(s) {
case osLove: return cgi.shLoveRing;
case osRanged: return cgi.shTargetRing;
2022-10-06 11:10:57 +00:00
case osOffensive: return cgi.shSawRing;
case osDirectional: return vid.orbmode == 2 ? cgi.shSawRing : cgi.shSpearRing;
case osFriend: return cgi.shPeaceRing;
case osUtility: return cgi.shGearRing;
2021-05-02 11:39:09 +00:00
case osPowerUtility: return cgi.shPowerGearRing;
case osWarping: return cgi.shHeptaRing;
2020-02-25 23:56:06 +00:00
case osFrog: return cgi.shFrogRing;
2021-05-02 11:39:09 +00:00
case osProtective: return cgi.shProtectiveRing;
case osTerraform: return cgi.shTerraRing;
case osMovement: return cgi.shMoveRing;
default: return cgi.shRing;
}
}
2021-05-09 00:25:05 +00:00
void queue_ring(const shiftmatrix& V, hpcshape& sh, color_t col, PPR p) {
queuepolyat(V, sh, col, p).outline = 0;
auto& p1 = queuepolyat(V, sh, col, p);
p1.cnt = cgi.orb_inner_ring;
p1.color = 0;
auto& p2 = queuepolyat(V, sh, col, p);
p2.color = 0;
p2.offset += cgi.orb_inner_ring;
p2.cnt -= cgi.orb_inner_ring + 1;
}
2020-02-29 19:30:15 +00:00
EX color_t orb_auxiliary_color(eItem it) {
if(it == itOrbFire) return firecolor(200);
2022-08-29 02:06:01 +00:00
if(it == itOrbWater) return 0x000060;
2020-02-29 19:30:15 +00:00
if(it == itOrbFriend || it == itOrbDiscord) return 0xC0C0C0;
if(it == itOrbFrog) return 0xFF0000;
if(it == itOrbImpact) return 0xFF0000;
2022-10-04 16:14:16 +00:00
if(it == itOrbPhasing) return 0xFF0000;
if(it == itOrbDash) return 0xFF0000;
2020-02-29 19:30:15 +00:00
if(it == itOrbFreedom) return 0xC0FF00;
if(it == itOrbPlague) return 0x409040;
if(it == itOrbChaos) return 0xFF00FF;
if(it == itOrbAir) return 0xFFFFFF;
if(it == itOrbUndeath) return minf[moFriendlyGhost].color;
if(it == itOrbRecall) return 0x101010;
if(it == itOrbLife) return 0x90B090;
2020-02-29 19:30:15 +00:00
if(it == itOrbSlaying) return 0xFF0000;
if(it == itOrbSide1) return 0x307080;
if(it == itOrbDigging) return 0x606060;
2022-08-27 19:43:22 +00:00
if(it == itOrbEnergy) return 0xFFFF80;
2020-02-29 19:30:15 +00:00
return iinf[it].color;
}
2022-10-04 16:14:16 +00:00
EX color_t orb_inner_color(eItem it) {
if(it == itOrbWater) return 0x0070C0;
if(it == itOrbEnergy) return 0x8B4513;
// if(it == itOrbDash) return 0xFF0000;
if(it == itOrbSide1) return 0x00FF00;
// if(it == itOrbPhasing) return 0xFF0000;
if(it == itOrbDigging) return 0x00FF00;
if(it == itOrbLife) return 0x306000;
return iinf[it].color;
}
EX void draw_ascii(const shiftmatrix& V, char glyph, color_t col, ld size) {
string s = s0 + glyph;
int id = isize(ptds);
if(WDIM == 2 && GDIM == 3)
2023-02-04 00:58:13 +00:00
queuestrn(V * lzpush(cgi.FLOOR - cgi.scalefactor * size / 4), size, s, darkenedby(col, darken), 0);
else
queuestrn(V, 1, s, darkenedby(col, darken), GDIM == 3 ? 0 : 2);
while(id < isize(ptds)) ptds[id++]->prio = PPR::MONSTER_BODY;
}
EX bool drawItemType(eItem it, cell *c, const shiftmatrix& V, color_t icol, int pticks, bool hidden) {
if(!it) return false;
2019-02-17 17:41:40 +00:00
char xch = iinf[it].glyph;
#if MAXMDIM >= 4
if(c && GDIM == 3)
addradar(V, xch, icol, kind_outline(it));
#endif
if(WDIM == 3 && c == centerover && in_perspective() && hdist0(tC0(V)) < cgi.orbsize * 0.25) return false;
if(!mmitem || !CAP_SHAPES) {
draw_ascii(V, iinf[it].glyph, icol, 1);
return true;
}
#if CAP_SHAPES
auto sinptick = [c, pticks] (int period) { return c ? sintick(period) : sin(animation_factor * vid.ispeed * pticks / period);};
auto spinptick = [c, pticks] (int period, ld phase) { return c ? spintick(period, phase) : spin((animation_factor * vid.ispeed * pticks) / period + phase * TAU); };
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) ? &cgi.shPirateX :
(it == itBuggy || it == itBuggy2) ? &cgi.shPirateX :
it == itHolyGrail ? &cgi.shGrail :
isElementalShard(it) ? &cgi.shElementalShard :
(it == itBombEgg || it == itTrollEgg || it == itCursed) ? &cgi.shEgg :
2022-08-25 08:38:22 +00:00
(it == itFrog || it == itWhirlpool) ? &cgi.shDisk :
it == itHunting ? &cgi.shTriangle :
2021-05-27 11:00:20 +00:00
(it == itDodeca || it == itDice) ? &cgi.shDodeca :
xch == '*' ? &cgi.shGem[ct6] :
xch == '(' ? &cgi.shKnife :
it == itShard ? &cgi.shMFloor.b[0] :
it == itTreat ? &cgi.shTreat :
it == itSlime ? &cgi.shEgg :
xch == '%' ? &cgi.shDaisy : xch == '$' ? &cgi.shStar : xch == ';' ? &cgi.shTriangle :
xch == '!' ? &cgi.shTriangle : it == itBone ? &cgi.shNecro : it == itStatue ? &cgi.shStatue :
2020-03-02 18:52:22 +00:00
among(it, itIvory, itEclectic) ? &cgi.shFigurine :
xch == '?' ? &cgi.shBookCover :
it == itKey ? &cgi.shKey :
it == itRevolver ? &cgi.shGun :
2017-07-10 18:47:38 +00:00
NULL;
2019-02-28 15:24:46 +00:00
if(c && doHighlight())
poly_outline = kind_outline(it);
shiftmatrix Vit = V;
2023-02-04 09:18:01 +00:00
if(embedded_plane && c && it != itBabyTortoise) Vit = orthogonal_move_fol(V, cgi.STUFF);
2022-12-08 18:38:06 +00:00
if(c && mproduct)
Vit = orthogonal_move(Vit, sin(ptick(750)) * cgi.plevel / 4);
2023-02-04 09:18:01 +00:00
else if(c && sl2 && !embedded_plane)
2019-08-24 12:49:49 +00:00
Vit = Vit * zpush(sin(ptick(750)) * cgi.plevel / 4);
2019-08-18 19:30:39 +00:00
else
if(GDIM == 3 && c && it != itBabyTortoise) Vit = face_the_player(Vit);
2019-02-26 13:35:04 +00:00
// V * cspin(0, 2, ptick(618, 0));
2021-06-17 07:39:08 +00:00
#if CAP_SHAPES
if(mapeditor::drawUserShape(Vit, mapeditor::sgItem, it, darkena(icol, 0, 0xFF), c)) return true;
#endif
if(c && history::includeHistory && history::infindhistory.count(c)) poly_outline = OUTLINE_DEAD;
2017-07-10 18:47:38 +00:00
else if(it == itSavedPrincess) {
drawMonsterType(moPrincess, c, V, icol, 0, icol);
return true;
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(it == itStrongWind) {
queuepoly(Vit * spinptick(750, 0), cgi.shFan, darkena(icol, 0, 255));
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2021-05-02 13:16:29 +00:00
else if(it == itFatigue) {
queuepoly(Vit * spinptick(750, 0), cgi.shFan, darkena(icol, 0, 255));
}
2017-07-10 18:47:38 +00:00
else if(it == itWarning) {
queuepoly(Vit * spinptick(750, 0), cgi.shTriangle, darkena(icol, 0, 255));
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2023-10-26 10:32:09 +00:00
else if(it == itCrossbow) {
queuepoly(Vit, cgi.shCrossbowIcon, getcs().bowcolor);
queuepoly(Vit, cgi.shCrossbowstringIcon, getcs().bowcolor2);
}
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, 0) * ypush(cgi.crossf*.15), bits, over ? 4 : 2, 0);
// queuepoly(Vit, cgi.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) {
shiftmatrix V2;
2019-02-17 17:28:20 +00:00
#if CAP_CRYSTAL
if(cryst) {
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) {
shiftmatrix P = ggmatrix(c1);
shiftpoint P1 = tC0(P);
2018-12-03 22:03:35 +00:00
if(isPlayerOn(c)) {
queuestr(P1, 2*vid.fsize, "X", 0x10100 * int(128 + 100 * sintick(150)));
2018-12-03 22:03:35 +00:00
// 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
}
2022-12-08 18:38:06 +00:00
V2 = V * lrspintox(inverse_shift(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
}
2019-08-15 13:05:43 +00:00
if(GDIM == 3) {
2021-05-09 00:25:05 +00:00
queue_ring(Vit, cgi.shRing, 0xFFFFFFFF, PPR::ITEM);
if(WDIM == 2) V2 = orthogonal_move_fol(V2, cgi.STUFF);
V2 = V2 * cspin(1, 2, M_PI * sintick(100) / 39);
queuepoly(V2, cgi.shCompass3, 0xFF0000FF);
2022-12-13 18:04:43 +00:00
queuepoly(V2 * lpispin(), cgi.shCompass3, 0x000000FF);
}
else {
if(c) V2 = V2 * spin(M_PI * sintick(100) / 30);
color_t hider = hidden ? 0xFFFFFF20 : 0xFFFFFFFF;
queuepoly(V2, cgi.shCompass1, 0xFF8080FF & hider);
queuepoly(V2, cgi.shCompass2, 0xFFFFFFFF & hider);
queuepoly(V2, cgi.shCompass3, 0xFF0000FF & hider);
2022-12-13 18:04:43 +00:00
queuepoly(V2 * lpispin(), cgi.shCompass3, 0x000000FF & hider);
}
2017-07-10 18:47:38 +00:00
xsh = NULL;
}
2017-07-10 18:47:38 +00:00
else if(it == itPalace) {
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2019-05-25 01:36:48 +00:00
if(GDIM == 3 && WDIM == 2) {
ld h = cgi.human_height;
2019-05-25 01:36:48 +00:00
dynamicval<qfloorinfo> qfi2(qfi, qfi);
shiftmatrix V2 = V * spin(pticks * vid.ispeed / 1500.);
2019-05-25 01:36:48 +00:00
/* divisors should be higher than in plate renderer */
qfi.fshape = &cgi.shMFloor2;
2023-02-04 00:58:13 +00:00
draw_shapevec(c, V2 * lzpush(-h/30), qfi.fshape->levels[0], 0xFFD500FF, PPR::WALL);
2019-05-25 01:36:48 +00:00
qfi.fshape = &cgi.shMFloor3;
2023-02-04 00:58:13 +00:00
draw_shapevec(c, V2 * lzpush(-h/25), qfi.fshape->levels[0], darkena(icol, 0, 0xFF), PPR::WALL);
2019-05-25 01:36:48 +00:00
qfi.fshape = &cgi.shMFloor4;
2023-02-04 00:58:13 +00:00
draw_shapevec(c, V2 * lzpush(-h/20), qfi.fshape->levels[0], 0xFFD500FF, PPR::WALL);
2019-05-25 01:36:48 +00:00
}
else if(WDIM == 3 && c) {
2021-03-30 09:33:35 +00:00
ld h = cgi.human_height;
shiftmatrix V2 = Vit * spin(pticks * vid.ispeed / 1500.);
2023-02-04 00:58:13 +00:00
draw_floorshape(c, V2 * lzpush(h/100), cgi.shMFloor3, 0xFFD500FF);
draw_floorshape(c, V2 * lzpush(h/50), cgi.shMFloor4, darkena(icol, 0, 0xFF));
queuepoly(V2, cgi.shGem[ct6], 0xFFD500FF);
2019-05-25 01:36:48 +00:00
}
else if(WDIM == 3 && !c) {
queuepoly(Vit, cgi.shGem[ct6], 0xFFD500FF);
}
2019-06-01 14:59:04 +00:00
else
#endif
{
color_t hider = hidden ? 0xFFFFFF20 : 0xFFFFFFFF;
shiftmatrix V2 = Vit * spin(pticks * vid.ispeed / 1500.);
draw_floorshape(c, V2, cgi.shMFloor3, 0xFFD500FF & hider);
draw_floorshape(c, V2, cgi.shMFloor4, darkena(icol, 0, 0xFF) & hider);
queuepoly(V2, cgi.shGem[ct6], 0xFFD500FF & hider);
2019-05-25 01:36:48 +00:00
}
2017-07-10 18:47:38 +00:00
xsh = NULL;
2017-03-23 10:53:57 +00:00
}
2017-07-10 18:47:38 +00:00
else if(it == itRose) {
for(int u=0; u<4; u++)
queuepoly(Vit * spinptick(1500, 0) * spin(30._deg * u), cgi.shRoseItem, 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(TAU * i / c->landparam) * xpush(.15) * spinptick(1500, 0), *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);
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
shiftmatrix V2 = Vit * spinptick(1500, 0);
2017-03-23 10:53:57 +00:00
if(xsh == &cgi.shBookCover && mmitem) {
2019-08-15 13:05:43 +00:00
if(GDIM == 3)
queuepoly(V2 * cpush(2, 1e-3), cgi.shBook, 0x805020FF);
else
queuepoly(V2, cgi.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) {
shiftmatrix Vx = Vit * spinptick(1500, .5/(ct6+6));
if(GDIM == 3)
Vx = Vx * cpush(2, -1e-3);
queuepolyat(Vx, *xsh, darkena(0x202020, 0, hidden ? 0x40 : 0xF0), PPR::ITEMb);
}
2016-08-26 09:58:03 +00:00
}
2021-05-02 13:16:29 +00:00
else if(xch == 'o' || xch == 'c' || 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;
color_t icol1 = icol;
2020-02-29 19:30:15 +00:00
icol = orb_auxiliary_color(it);
color_t col = darkena(icol, 0, int(0x80 + 0x70 * sinptick(300)));
2022-10-04 16:14:16 +00:00
2022-10-06 11:10:57 +00:00
if(it == itOrbFish && vid.orbmode == 2)
queuepolyat(Vit * spinptick(1500, 0), cgi.shFishTail, col, PPR::ITEM_BELOW);
2021-05-02 13:16:29 +00:00
if(xch == 'c')
queuepolyat(Vit * spinptick(500, 0), cgi.shMoonDisk, darkena(0x801080, 0, hidden ? 0x20 : 0xC0), prio);
2022-10-06 11:10:57 +00:00
else if(vid.orbmode < 2) {
icol1 = orb_inner_color(it);
queuepolyat(Vit, cgi.shDisk, darkena(icol1, 0, inice ? 0x80 : hidden ? 0x20 : 0xC0), prio);
}
else {
2022-10-04 16:14:16 +00:00
icol1 = orb_inner_color(it);
2022-08-26 19:34:04 +00:00
auto dark = darkena(icol1, 0, inice ? 0x80 : hidden ? 0x20 : (it == itOrbBeauty) ? 0xA0 : 0xC0);
2022-10-04 16:14:16 +00:00
auto dark1 = darkena(icol1, 0, inice ? 0x40 : hidden ? 0x10 : (it == itOrbBeauty) ? 0x50 : 0x60);
if(c && GDIM == 2) Vit = rgpushxto0(tC0(Vit));
auto Vit1 = Vit * spin90();
2022-10-04 16:14:16 +00:00
if (it == itOrbBeauty) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
2022-08-26 19:34:04 +00:00
for(int u=0; u<3; u++)
queuepolyat(Vit1 * spin(40._deg * u), cgi.shSmallRose, dark, prio);
2022-10-04 16:14:16 +00:00
}
else if (it == itOrbLife) {
2022-10-04 16:14:16 +00:00
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit1, cgi.shSmallPBody, dark, prio);
queuepolyat(Vit1, cgi.shDiskM, dark, prio);
2022-08-28 01:21:53 +00:00
}
else if (it == itOrbBull) {
2022-10-04 16:14:16 +00:00
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit1, cgi.shTinyBullBody, dark, prio);
queuepolyat(Vit1, cgi.shTinyBullHead, dark, prio);
queuepolyat(Vit1, cgi.shTinyBullHorn, dark, prio);
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1 * lmirror(), cgi.shTinyBullHorn, dark, prio);
2022-10-04 16:14:16 +00:00
}
else if (it == itOrbFrog && false) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit1, cgi.shSmallFrogBody, dark, prio);
queuepolyat(Vit1, cgi.shSmallFrogRearFoot, dark, prio);
queuepolyat(Vit1, cgi.shSmallFrogRearLeg, dark, prio);
queuepolyat(Vit1, cgi.shSmallFrogRearLeg2, dark, prio);
queuepolyat(Vit1, cgi.shSmallFrogFrontFoot, dark, prio);
queuepolyat(Vit1, cgi.shSmallFrogFrontLeg, dark, prio);
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1*lmirror(), cgi.shSmallFrogRearFoot, dark, prio);
queuepolyat(Vit1*lmirror(), cgi.shSmallFrogRearLeg, dark, prio);
queuepolyat(Vit1*lmirror(), cgi.shSmallFrogRearLeg2, dark, prio);
queuepolyat(Vit1*lmirror(), cgi.shSmallFrogFrontFoot, dark, prio);
queuepolyat(Vit1*lmirror(), cgi.shSmallFrogFrontLeg, dark, prio);
2022-08-28 01:21:53 +00:00
}
2022-10-04 16:14:16 +00:00
else if (it == itOrbSpeed) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
drawSpeed(Vit, 0.3);
}
2022-08-29 04:09:24 +00:00
else if (it == itOrbStunning) {
2022-10-04 16:14:16 +00:00
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit, cgi.shDiskM, dark, prio);
2022-08-29 04:09:24 +00:00
for (int i=0; i<5; i++) {
shiftmatrix V2 = Vit * spin(TAU * i / 5 + ptick(300));
2022-08-29 04:09:24 +00:00
queuepolyat(V2, cgi.shSmallFlailBall, dark, prio);
}
}
else if (it == itOrbDragon) {
2022-10-04 16:14:16 +00:00
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit1, cgi.shSmallDragonHead, dark, prio);
queuepolyat(Vit1, cgi.shSmallDragonNostril, 0xFF, prio);
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1*lmirror(), cgi.shSmallDragonNostril, 0xFF, prio);
2022-10-04 16:14:16 +00:00
queuepolyat(Vit1, cgi.shSmallDragonEyes, 0x60, prio);
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1*lmirror(), cgi.shSmallDragonEyes, 0x60, prio);
}
else if (it == itOrbDomination) {
queuepolyat(Vit1*MirrorX, cgi.shSmallWormHead, dark, prio);
queuepolyat(Vit1*MirrorX, cgi.shSmallWormEyes, 0x60, prio);
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1*MirrorX*lmirror(), cgi.shSmallWormEyes, 0x60, prio);
2022-10-04 16:14:16 +00:00
}
else if (it == itOrbMorph || it == itOrbChaos || it == itOrbPlague) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit1, cgi.shSmallTreat, dark, prio);
}
else if (it == itOrbWinter) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit1, cgi.shSnowflake, dark, prio);
}
else if (it == itOrbLuck)
2022-10-04 16:14:16 +00:00
queuepolyat(Vit1, cgi.shSmallerDodeca, dark, prio);
2022-08-30 02:03:12 +00:00
else if (it == itOrbAether) {
2022-10-04 16:14:16 +00:00
queuepolyat(Vit1, cgi.shHalfDisk, dark, prio);
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1*lmirror(), cgi.shHalfDisk, 0xFF, prio);
2022-10-04 16:14:16 +00:00
queuepolyat(Vit1*MirrorX, cgi.shHalfHumanoid, dark, prio);
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1*lmirror()*MirrorX, cgi.shHalfHumanoid, 0xFF, prio);
2022-08-30 02:03:12 +00:00
}
else if (it == itOrbFlash)
2022-10-04 16:14:16 +00:00
queuepolyat(Vit1, cgi.shFlash, dark, prio);
else if (it == itOrbMatter || it == itOrbStone) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit1, cgi.shDiskSq, dark, prio);
}
else if (it == itOrbSummon) {
2022-10-04 16:14:16 +00:00
queuepolyat(Vit1, cgi.shHeptagon, dark, prio);
queuepolyat(Vit1, cgi.shHeptagram, dark, prio);
}
else if (it == itOrbSafety) {
queuepolyat(Vit, cgi.shDisk, dark, prio);
dynamicval<color_t> p(poly_outline, dark);
queuepolyat(Vit1, cgi.shHeptagram, 0, prio);
}
2022-08-26 17:04:52 +00:00
else {
2022-10-04 16:14:16 +00:00
bool jump = (it == itOrbPhasing || it == itOrbDash || it == itOrbFrog);
auto shape = (it == itOrbFriend) ? &cgi.shTinyBird :
(it == itOrbSide1) ? &cgi.shSmallPSword :
(it == itOrbDigging) ? &cgi.shSmallPickAxe :
(it == itOrbSword || it == itOrbSword2) ? &cgi.shSmallSword :
(it == itOrbThorns) ? &cgi.shSmallHedgehogBlade :
2022-08-29 17:30:37 +00:00
(it == itOrbSide2 || it == itOrb37 || it == itOrbLava) ? &cgi.shDiskT :
(it == itOrbGravity) ? &cgi.shTinyArrow :
2022-10-04 16:14:16 +00:00
(it == itOrbFreedom || it == itOrbRecall) ? &cgi.shDiskSq :
(it == itOrbEnergy) ? &cgi.shHalfDisk :
2022-09-15 23:02:27 +00:00
(it == itOrbSpace) ? &cgi.shSmallPirateHook :
2022-09-17 02:24:01 +00:00
(it == itOrbChoice || it == itOrbMirror || it == itOrbMagnetism || it == itOrbEmpathy || it == itOrbDiscord) ? &cgi.shEccentricDisk :
(it == itOrbPsi || it == itOrbSide3) ? &cgi.shDiskS :
2022-08-29 17:05:40 +00:00
(it == itOrbPurity) ? &cgi.shSmallEgg :
2022-08-29 23:14:59 +00:00
(it == itOrbLightning) ? &cgi.shLightningBolt :
2022-09-16 19:13:20 +00:00
(it == itOrbShield) ? &cgi.shShield :
2022-09-13 17:49:10 +00:00
(it == itOrbTime) ? &cgi.shHourglass :
2022-09-17 02:24:01 +00:00
(it == itOrbAir) ? &cgi.shSmallFan :
(it == itOrbWoods) ? &cgi.shTreeIcon :
(it == itOrbNature) ? &cgi.shLeafIcon :
(it == itOrbIllusion || it == itOrbInvis || it == itOrbTeleport) ? &cgi.shHumanoid :
2022-10-04 16:14:16 +00:00
jump ? &cgi.shDiskSegment :
2022-08-29 17:30:37 +00:00
NULL;
queuepolyat(Vit, cgi.shDisk, dark, prio);
2022-10-06 06:49:26 +00:00
bool reversed = (shape == &cgi.shTreeIcon || shape == &cgi.shHumanoid || it == itOrbSword2);
2022-10-04 16:14:16 +00:00
bool left90 = (shape == &cgi.shLeafIcon || shape == &cgi.shLightningBolt);
2022-08-29 17:30:37 +00:00
if (shape)
queuepolyat(reversed ? Vit1 * MirrorX : left90 ? Vit1 * spin270() : Vit1, *shape, (it == itOrbInvis || it == itOrbTeleport) ? 0x20 : 0x80, prio);
if (it == itOrbSide1 || (shape == &cgi.shEccentricDisk && it != itOrbDiscord))
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1*lmirror(), *shape, 0x80, prio);
2022-10-04 16:14:16 +00:00
if (jump || it == itOrbEnergy)
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1*lmirror(), *shape, col, prio);
2022-08-28 18:10:12 +00:00
if (it == itOrbIntensity || it == itOrbImpact)
2022-10-04 16:14:16 +00:00
queuepolyat(Vit1, cgi.shDiskM, 0x80, prio);
if (it == itOrbHorns) {
2022-10-04 16:14:16 +00:00
queuepolyat(Vit1, cgi.shSmallBullHead, 0x80, prio);
queuepolyat(Vit1, cgi.shSmallBullHorn, 0x80, prio);
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1*lmirror(), cgi.shSmallBullHorn, 0x80, prio);
}
if (it == itOrbUndeath) {
2022-10-04 16:14:16 +00:00
dark = darkena(fghostcolor(c) /* minf[moFriendlyGhost].color */, 0, inice ? 0x80 : hidden ? 0x20 : 0xC0);
queuepolyat(Vit1, cgi.shMiniGhost, dark, prio);
queuepolyat(Vit1, cgi.shMiniEyes, 0xFF, prio);
}
if (it == itOrbSlaying) {
2022-10-04 16:14:16 +00:00
queuepolyat(Vit1, cgi.shSmallFlailTrunk, 0x80, prio);
queuepolyat(Vit1, cgi.shSmallHammerHead, 0x80, prio);
}
if (it == itOrbShell)
for(int i = 1; i<8; i++) {
2022-10-04 16:14:16 +00:00
queuepolyat(Vit1, cgi.shTortoise[i][2], 0x80, prio);
if (i>=5 && i<=7)
2022-12-13 18:04:43 +00:00
queuepolyat(Vit1*lmirror(), cgi.shTortoise[i][2], 0x80, prio);
}
2022-08-26 17:04:52 +00:00
}
}
2021-05-09 00:25:05 +00:00
queue_ring(Vit * spinptick(1500, 0), orbshape(iinf[it].orbshape), col, prio);
2016-08-26 09:58:03 +00:00
}
else {
draw_ascii(V, xch, icol, 1);
}
2015-08-08 13:57:52 +00:00
return true;
#endif
2016-08-26 09:58:03 +00:00
}
#if CAP_SHAPES
2019-06-16 13:24:48 +00:00
color_t skincolor = 0xD0C080FF;
void humanoid_eyes(const shiftmatrix& V, color_t ecol, color_t hcol = skincolor) {
2019-08-15 13:05:43 +00:00
if(GDIM == 3) {
2019-06-16 13:24:48 +00:00
queuepoly(VHEAD, cgi.shPHeadOnly, hcol);
queuepoly(VHEAD, cgi.shSkullEyes, ecol);
}
}
EX void drawTerraWarrior(const shiftmatrix& V, int t, int hp, double footphase) {
if(!mmmon) {
draw_ascii(V, 'T', gradient(0x202020, 0xFFFFFF, 0, t, 6), 1.5);
return;
}
ShadowV(V, cgi.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, cgi.shPBody, bcol);
2022-12-13 18:04:43 +00:00
if(!peace::on) queuepoly(VBODY * VBS * lmirror(), cgi.shPSword, darkena(0xC0C0C0, 0, 0xFF));
queuepoly(VBODY1 * VBS, cgi.shTerraArmor1, darkena(t > 0 ? 0x4040FF : col, 0, 0xFF));
if(hp >= 4) queuepoly(VBODY2 * VBS, cgi.shTerraArmor2, darkena(t > 1 ? 0xC00000 : col, 0, 0xFF));
if(hp >= 2) queuepoly(VBODY3 * VBS, cgi.shTerraArmor3, darkena(t > 2 ? 0x612600 : col, 0, 0xFF));
queuepoly(VHEAD, cgi.shTerraHead, darkena(t > 4 ? 0x202020 : t > 3 ? 0x504040 : col, 0, 0xFF));
queuepoly(VHEAD1, cgi.shPFace, bcol);
2019-06-16 13:24:48 +00:00
humanoid_eyes(V, 0x400000FF, darkena(col, 0, 0xFF));
2017-09-30 09:46:41 +00:00
}
#endif
2017-09-30 09:46:41 +00:00
EX void drawPlayer(eMonster m, cell *where, const shiftmatrix& V, color_t col, double footphase, bool stop IS(false)) {
2019-03-30 12:16:19 +00:00
charstyle& cs = getcs();
2020-10-15 14:33:52 +00:00
#if CAP_COMPLEX2
auto& knighted = camelot::knighted;
2020-10-15 14:33:52 +00:00
#else
const bool knighted = false;
#endif
2015-08-08 13:57:52 +00:00
if(GDIM == 3) {
addradar(V, '@', cs.uicolor >> 8, 0xFF00FF00);
}
2019-03-30 12:16:19 +00:00
if(mapeditor::drawplayer && !mapeditor::drawUserShape(V, mapeditor::sgPlayer, cs.charid, cs.skincolor, where)) {
2017-03-23 10:53:57 +00:00
2019-03-30 12:16:19 +00:00
if(cs.charid >= 8) {
/* famililar */
if(!mmspatial && !footphase) {
if(stop) return;
queuepoly(VALEGS, cgi.shWolfLegs, fc(150, cs.dresscolor, 4));
}
2019-03-30 12:16:19 +00:00
else {
ShadowV(V, cgi.shWolfBody);
if(stop) return;
2019-03-30 12:16:19 +00:00
animallegs(VALEGS, moWolf, fc(500, cs.dresscolor, 4), footphase);
}
queuepoly(VABODY, cgi.shWolfBody, fc(0, cs.skincolor, 0));
queuepoly(VAHEAD, cgi.shFamiliarHead, fc(500, cs.haircolor, 2));
2019-03-30 12:16:19 +00:00
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
color_t col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.eyecolor, 3);
queuepoly(VAHEAD, cgi.shFamiliarEye, col);
2022-12-13 18:04:43 +00:00
queuepoly(VAHEAD * lmirror(), cgi.shFamiliarEye, col);
2019-03-30 12:16:19 +00:00
}
2019-03-30 12:16:19 +00:00
if(knighted)
queuepoly(VABODY, cgi.shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
2019-03-30 12:16:19 +00:00
if(tortoise::seek())
tortoise::draw(VABODY, tortoise::seekbits, 4, 0);
}
else if(cs.charid >= 6) {
/* dog */
if(!mmspatial && !footphase) {
if(stop) return;
queuepoly(VABODY, cgi.shDogBody, fc(0, cs.skincolor, 0));
}
2019-03-30 12:16:19 +00:00
else {
ShadowV(V, cgi.shDogTorso);
if(stop) return;
2019-03-30 12:16:19 +00:00
animallegs(VALEGS, moRunDog, fc(500, cs.dresscolor, 4), footphase);
queuepoly(VABODY, cgi.shDogTorso, fc(0, cs.skincolor, 0));
2017-07-10 18:47:38 +00:00
}
queuepoly(VAHEAD, cgi.shDogHead, fc(150, cs.haircolor, 2));
2015-08-08 13:57:52 +00:00
2019-03-30 12:16:19 +00:00
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
color_t col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.eyecolor, 3);
queuepoly(VAHEAD, cgi.shWolf1, col);
queuepoly(VAHEAD, cgi.shWolf2, col);
2019-03-30 12:16:19 +00:00
}
2019-03-30 12:16:19 +00:00
color_t colnose = items[itOrbDiscord] ? watercolor(0) : fc(314, 0xFF, 3);
queuepoly(VAHEAD, cgi.shWolf3, colnose);
2019-03-30 12:16:19 +00:00
if(knighted)
queuepoly(VABODY, cgi.shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
2019-03-30 12:16:19 +00:00
if(tortoise::seek())
tortoise::draw(VABODY, tortoise::seekbits, 4, 0);
}
else if(cs.charid >= 4) {
/* cat */
if(!mmspatial && !footphase) {
if(stop) return;
queuepoly(VALEGS, cgi.shCatLegs, fc(500, cs.dresscolor, 4));
}
2017-07-10 18:47:38 +00:00
else {
ShadowV(V, cgi.shCatBody);
if(stop) return;
2019-03-30 12:16:19 +00:00
animallegs(VALEGS, moRunDog, fc(500, cs.dresscolor, 4), footphase);
}
queuepoly(VABODY, cgi.shCatBody, fc(0, cs.skincolor, 0));
queuepoly(VAHEAD, cgi.shCatHead, fc(150, cs.haircolor, 2));
2019-03-30 12:16:19 +00:00
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
color_t col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.eyecolor, 3);
queuepoly(VAHEAD * xpush(.04), cgi.shWolf1, col);
queuepoly(VAHEAD * xpush(.04), cgi.shWolf2, col);
2019-03-30 12:16:19 +00:00
}
2015-08-08 13:57:52 +00:00
2019-03-30 12:16:19 +00:00
if(knighted)
queuepoly(VABODY, cgi.shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
2015-08-08 13:57:52 +00:00
2019-03-30 12:16:19 +00:00
if(tortoise::seek())
tortoise::draw(VABODY, tortoise::seekbits, 4, 0);
}
else {
/* human */
ShadowV(V, (cs.charid&1) ? cgi.shFemaleBody : cgi.shPBody);
if(stop) return;
2019-03-30 12:16:19 +00:00
const transmatrix VBS = otherbodyparts(V, fc(0, cs.skincolor, 0), items[itOrbFish] ? moWaterElemental : moPlayer, footphase);
queuepoly(VBODY * VBS, (cs.charid&1) ? cgi.shFemaleBody : cgi.shPBody, fc(0, cs.skincolor, 0));
2019-03-30 12:16:19 +00:00
2017-07-10 18:47:38 +00:00
if(cs.charid&1)
queuepoly(VBODY1 * VBS, cgi.shFemaleDress, fc(500, cs.dresscolor, 4));
2019-03-30 12:16:19 +00:00
2017-07-10 18:47:38 +00:00
if(cs.charid == 2)
queuepoly(VBODY2 * VBS, cgi.shPrinceDress, fc(400, cs.dresscolor, 5));
2017-07-10 18:47:38 +00:00
if(cs.charid == 3)
queuepoly(VBODY2 * VBS, cgi.shPrincessDress, fc(400, cs.dresscolor2, 5));
2017-10-12 10:05:12 +00:00
if(items[itOrbSide3])
queuepoly(VBODY * VBS, (cs.charid&1) ? cgi.shFerocityF : cgi.shFerocityM, fc(0, cs.skincolor, 0));
2019-03-30 12:16:19 +00:00
2017-07-10 18:47:38 +00:00
if(items[itOrbHorns]) {
queuepoly(VBODY * VBS, cgi.shBullHead, items[itOrbDiscord] ? watercolor(0) : 0xFF000030);
queuepoly(VBODY * VBS, cgi.shBullHorn, items[itOrbDiscord] ? watercolor(0) : 0xFF000040);
2022-12-13 18:04:43 +00:00
queuepoly(VBODY * VBS * lmirror(), cgi.shBullHorn, items[itOrbDiscord] ? watercolor(0) : 0xFF000040);
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
2017-10-12 10:05:12 +00:00
if(items[itOrbSide1] && !shmup::on)
queuepoly(VBODY * VBS * spin(-15._deg), cs.charid >= 2 ? cgi.shSabre : cgi.shPSword, fc(314, cs.swordcolor, 3)); // 3 not colored
2017-10-12 10:05:12 +00:00
2022-12-13 18:04:43 +00:00
shiftmatrix VWPN = cs.lefthanded ? VBODY * VBS * lmirror() : 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-03-30 12:16:19 +00:00
#if CAP_RACING
2019-01-07 03:55:21 +00:00
if(racing::trophy[multi::cpid])
queuepoly(VWPN, cgi.shTrophy, racing::trophy[multi::cpid]);
2019-03-30 12:16:19 +00:00
#endif
2019-01-07 03:55:21 +00:00
}
2023-10-26 10:33:02 +00:00
else if(bow::crossbow_mode() && cs.charid < 4) {
queuepoly(VWPN, cgi.shCrossbow, fc(314, cs.bowcolor, 3));
int ti = items[itCrossbow];
if(shmup::on) {
ti = shmup::getPlayer()->nextshot - shmup::curtime;
if(ti <= 0) ti = 0;
else ti = 1 + ti / 500;
}
if(ti <= 1) queuepoly(VWPN, cgi.shCrossbowstringLoaded, fc(314, cs.bowcolor2, 3));
else if(ti <= 2) queuepoly(VWPN, cgi.shCrossbowstringSemiloaded, fc(314, cs.bowcolor2, 3));
2023-10-26 10:33:02 +00:00
else queuepoly(VWPN, cgi.shCrossbowstringUnloaded, fc(314, cs.bowcolor2, 3));
if(ti == 0) queuepoly(VWPN, cgi.shCrossbowBolt, fc(314, cs.swordcolor, 3));
2023-10-26 10:33:02 +00:00
}
2017-07-10 18:47:38 +00:00
else if(items[itOrbThorns])
queuepoly(VWPN, cgi.shHedgehogBladePlayer, items[itOrbDiscord] ? watercolor(0) : 0x00FF00FF);
2017-07-10 18:47:38 +00:00
else if(!shmup::on && items[itOrbDiscord])
queuepoly(VWPN, cs.charid >= 2 ? cgi.shSabre : cgi.shPSword, watercolor(0));
2017-07-10 18:47:38 +00:00
else if(items[itRevolver])
queuepoly(VWPN, cgi.shGunInHand, fc(314, cs.swordcolor, 3)); // 3 not colored
2018-01-03 21:32:34 +00:00
else if(items[itOrbSlaying]) {
queuepoly(VWPN, cgi.shFlailTrunk, fc(314, cs.swordcolor, 3));
queuepoly(VWPN, cgi.shHammerHead, fc(314, cs.swordcolor, 3));
2018-01-03 21:32:34 +00:00
}
2021-05-21 23:26:59 +00:00
else if(items[itCurseWeakness]) {
/* no weapon shown */
}
2017-07-10 18:47:38 +00:00
else if(!shmup::on)
queuepoly(VWPN, cs.charid >= 2 ? cgi.shSabre : cgi.shPSword, fc(314, cs.swordcolor, 3)); // 3 not colored
2017-07-10 18:47:38 +00:00
else if(shmup::curtime >= shmup::getPlayer()->nextshot)
queuepoly(VWPN, cgi.shPKnife, fc(314, cs.swordcolor, 3)); // 3 not colored
2017-07-10 18:47:38 +00:00
if(items[itOrbBeauty]) {
if(cs.charid&1)
queuepoly(VHEAD1, cgi.shFlowerHair, darkena(iinf[itOrbBeauty].color, 0, 0xFF));
2017-07-10 18:47:38 +00:00
else
queuepoly(VWPN, cgi.shFlowerHand, darkena(iinf[itOrbBeauty].color, 0, 0xFF));
2017-07-10 18:47:38 +00:00
}
if(where && where->land == laWildWest) {
queuepoly(VHEAD1, cgi.shWestHat1, darkena(cs.swordcolor, 1, 0XFF));
queuepoly(VHEAD2, cgi.shWestHat2, darkena(cs.swordcolor, 0, 0XFF));
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
2017-07-10 18:47:38 +00:00
if(cheater && !autocheat) {
queuepoly(VHEAD1, (cs.charid&1) ? cgi.shGoatHead : cgi.shDemon, darkena(0xFFFF00, 0, 0xFF));
2017-07-10 18:47:38 +00:00
// queuepoly(V, shHood, darkena(0xFF00, 1, 0xFF));
}
else {
queuepoly(VHEAD, cgi.shPFace, fc(500, cs.skincolor, 1));
queuepoly(VHEAD1, (cs.charid&1) ? cgi.shFemaleHair : cgi.shPHead, fc(150, cs.haircolor, 2));
2017-07-10 18:47:38 +00:00
}
humanoid_eyes(V, cs.eyecolor, cs.skincolor);
2019-03-30 12:16:19 +00:00
2017-07-10 18:47:38 +00:00
if(knighted)
queuepoly(VBODY * VBS, cgi.shKnightCloak, darkena(cloakcolor(knighted), 1, 0xFF));
2019-03-30 12:16:19 +00:00
2017-07-10 18:47:38 +00:00
if(tortoise::seek())
tortoise::draw(VBODY * VBS * ypush(-cgi.crossf*.25), tortoise::seekbits, 4, 0);
}
2019-03-30 12:16:19 +00:00
}
}
2019-08-09 19:00:52 +00:00
EX int wingphase(int period, int phase IS(0)) {
ld t = fractick(period, phase);
const int WINGS2 = WINGS * 2;
int ti = int(t * WINGS2) % WINGS2;
if(ti > WINGS) ti = WINGS2 - ti;
2019-06-18 14:09:54 +00:00
return ti;
}
transmatrix wingmatrix(int period, int phase = 0) {
ld t = fractick(period, phase) * TAU;
transmatrix Vwing = Id;
Vwing[1][1] = .85 + .15 * sin(t);
return Vwing;
}
void drawMimic(eMonster m, cell *where, const shiftmatrix& V, color_t col, double footphase) {
2019-03-30 12:16:19 +00:00
charstyle& cs = getcs();
2019-03-30 12:17:51 +00:00
if(mapeditor::drawUserShape(V, mapeditor::sgPlayer, cs.charid, darkena(col, 0, 0x80), where)) return;
2019-03-30 12:16:19 +00:00
if(cs.charid >= 8) {
queuepoly(VABODY, cgi.shWolfBody, darkena(col, 0, 0xC0));
ShadowV(V, cgi.shWolfBody);
2019-03-30 12:16:19 +00:00
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, darkena(col, 0, 0xC0), footphase);
else
queuepoly(VABODY, cgi.shWolfLegs, darkena(col, 0, 0xC0));
2019-03-30 12:16:19 +00:00
queuepoly(VABODY, cgi.shFamiliarHead, darkena(col, 0, 0xC0));
queuepoly(VAHEAD, cgi.shFamiliarEye, darkena(col, 0, 0xC0));
2022-12-13 18:04:43 +00:00
queuepoly(VAHEAD * lmirror(), cgi.shFamiliarEye, darkena(col, 0, 0xC0));
2019-03-30 12:16:19 +00:00
}
else if(cs.charid >= 6) {
ShadowV(V, cgi.shDogBody);
queuepoly(VAHEAD, cgi.shDogHead, darkena(col, 0, 0xC0));
2019-03-30 12:16:19 +00:00
if(mmspatial || footphase) {
animallegs(VALEGS, moRunDog, darkena(col, 0, 0xC0), footphase);
queuepoly(VABODY, cgi.shDogTorso, darkena(col, 0, 0xC0));
2019-03-30 12:16:19 +00:00
}
else
queuepoly(VABODY, cgi.shDogBody, darkena(col, 0, 0xC0));
queuepoly(VABODY, cgi.shWolf1, darkena(col, 1, 0xC0));
queuepoly(VABODY, cgi.shWolf2, darkena(col, 1, 0xC0));
queuepoly(VABODY, cgi.shWolf3, darkena(col, 1, 0xC0));
2019-03-30 12:16:19 +00:00
}
else if(cs.charid >= 4) {
ShadowV(V, cgi.shCatBody);
queuepoly(VABODY, cgi.shCatBody, darkena(col, 0, 0xC0));
queuepoly(VAHEAD, cgi.shCatHead, darkena(col, 0, 0xC0));
2019-03-30 12:16:19 +00:00
if(mmspatial || footphase)
animallegs(VALEGS, moRunDog, darkena(col, 0, 0xC0), footphase);
else
queuepoly(VALEGS, cgi.shCatLegs, darkena(col, 0, 0xC0));
queuepoly(VAHEAD * xpush(.04), cgi.shWolf1, darkena(col, 1, 0xC0));
queuepoly(VAHEAD * xpush(.04), cgi.shWolf2, darkena(col, 1, 0xC0));
2019-03-30 12:16:19 +00:00
}
else {
const transmatrix VBS = otherbodyparts(V, darkena(col, 0, 0x40), m, footphase);
queuepoly(VBODY * VBS, (cs.charid&1) ? cgi.shFemaleBody : cgi.shPBody, darkena(col, 0, 0X80));
if(bow::crossbow_mode() && cs.charid < 4) {
shiftmatrix VWPN = cs.lefthanded ? VBODY * VBS * lmirror() : VBODY * VBS;
color_t col1 = darkena(col, 0, 0x40);
queuepoly(VWPN, cgi.shCrossbow, col1);
int ti = items[itCrossbow];
if(shmup::on) {
ti = shmup::getPlayer()->nextshot - shmup::curtime;
if(ti <= 0) ti = 0;
else ti = 1 + ti / 500;
}
if(ti <= 1) queuepoly(VWPN, cgi.shCrossbowstringLoaded, col1);
else if(ti <= 2) queuepoly(VWPN, cgi.shCrossbowstringSemiloaded, col1);
else queuepoly(VWPN, cgi.shCrossbowstringUnloaded, col1);
if(ti == 0) queuepoly(VWPN, cgi.shCrossbowBolt, col1);
}
else if(!shmup::on) {
2019-03-30 12:16:19 +00:00
bool emp = items[itOrbEmpathy] && m != moShadow;
if(items[itOrbThorns] && emp)
queuepoly(VBODY * VBS, cgi.shHedgehogBladePlayer, darkena(col, 0, 0x40));
2019-03-30 12:16:19 +00:00
if(items[itOrbSide1] && !shmup::on)
queuepoly(VBODY * VBS * spin(-15._deg), cs.charid >= 2 ? cgi.shSabre : cgi.shPSword, darkena(col, 0, 0x40));
2019-03-30 12:16:19 +00:00
if(items[itOrbSide3] && emp)
queuepoly(VBODY * VBS, (cs.charid&1) ? cgi.shFerocityF : cgi.shFerocityM, darkena(col, 0, 0x40));
2016-01-02 10:09:13 +00:00
2022-12-13 18:04:43 +00:00
shiftmatrix VWPN = cs.lefthanded ? VBODY * VBS * lmirror() : VBODY * VBS;
queuepoly(VWPN, (cs.charid >= 2 ? cgi.shSabre : cgi.shPSword), darkena(col, 0, 0XC0));
2016-01-02 10:09:13 +00:00
}
2019-03-30 12:16:19 +00:00
else if(!where || shmup::curtime >= shmup::getPlayer()->nextshot)
queuepoly(VBODY * VBS, cgi.shPKnife, darkena(col, 0, 0XC0));
2019-03-30 12:16:19 +00:00
2020-10-15 14:33:52 +00:00
#if CAP_COMPLEX2
if(camelot::knighted)
queuepoly(VBODY3 * VBS, cgi.shKnightCloak, darkena(col, 1, 0xC0));
2020-10-15 14:33:52 +00:00
#endif
2019-03-30 12:16:19 +00:00
queuepoly(VHEAD1, (cs.charid&1) ? cgi.shFemaleHair : cgi.shPHead, darkena(col, 1, 0XC0));
queuepoly(VHEAD, cgi.shPFace, darkena(col, 0, 0XC0));
2019-03-30 12:16:19 +00:00
if(cs.charid&1)
queuepoly(VBODY1 * VBS, cgi.shFemaleDress, darkena(col, 1, 0XC0));
2019-03-30 12:16:19 +00:00
if(cs.charid == 2)
queuepoly(VBODY2 * VBS, cgi.shPrinceDress, darkena(col, 1, 0XC0));
2019-03-30 12:16:19 +00:00
if(cs.charid == 3)
queuepoly(VBODY2 * VBS, cgi.shPrincessDress, darkena(col, 1, 0XC0));
humanoid_eyes(V, 0xFF, darkena(col, 0, 0x40));
2015-08-08 13:57:52 +00:00
}
2019-03-30 12:16:19 +00:00
}
EX bool drawMonsterType(eMonster m, cell *where, const shiftmatrix& V1, color_t col, double footphase, color_t asciicol) {
2019-03-30 12:16:19 +00:00
#if MAXMDIM >= 4
2020-08-01 14:48:08 +00:00
if(GDIM == 3 && asciicol != NOCOLOR) {
addradar(V1, minf[m].glyph, asciicol, isFriendly(m) ? 0x00FF00FF : 0xFF0000FF);
2020-08-01 14:48:08 +00:00
}
2019-03-30 12:16:19 +00:00
#endif
char xch = minf[m].glyph;
shiftmatrix V = V1;
2022-12-08 18:38:06 +00:00
if(WDIM == 3 && (classflag(m) & CF_FACE_UP) && where && !mhybrid) V = V1 * cspin90(0, 2);
#if CAP_SHAPES
2020-03-02 02:06:15 +00:00
if(among(m, moTortoise, moWorldTurtle) && where && where->stuntime >= 3)
2019-03-30 12:16:19 +00:00
drawStunStars(V, where->stuntime-2);
else if (among(m, moTortoise, moWorldTurtle, moMutant) || m == moPlayer || (where && !where->stuntime)) ;
2019-03-30 12:16:19 +00:00
else if(where && !(isMetalBeast(m) && where->stuntime == 1))
drawStunStars(V, where->stuntime);
2017-07-10 18:47:38 +00:00
2019-03-30 12:16:19 +00:00
if(mapeditor::drawUserShape(V, mapeditor::sgMonster, m, darkena(col, 0, 0xFF), where))
return true;
#endif
if(!mmmon || !CAP_SHAPES) {
draw_ascii(V1, xch, asciicol, 1.5);
return true;
}
2015-08-08 13:57:52 +00:00
#if CAP_SHAPES
2019-03-30 12:16:19 +00:00
switch(m) {
case 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)
2021-05-09 00:25:05 +00:00
queue_ring(V, cgi.shRing, darkena(0xFFFFFF, 0, 0x80 + 0x70 * sintick(200)), PPR::ITEM);
return true;
2019-03-30 12:16:19 +00:00
}
2017-07-10 18:47:38 +00:00
2020-03-02 02:06:15 +00:00
case moWorldTurtle: {
tortoise::draw(V, -1, 0, where ? where->stuntime : 0);
return true;
2020-03-02 02:06:15 +00:00
}
2019-03-30 12:16:19 +00:00
case moPlayer:
drawPlayer(m, where, V, col, footphase);
return true;
2015-08-08 13:57:52 +00:00
2019-03-30 12:17:51 +00:00
case moMimic: case moShadow: case moIllusion:
2019-03-30 12:16:19 +00:00
drawMimic(m, where, V, col, footphase);
return true;
2019-03-30 12:16:19 +00:00
case moBullet:
ShadowV(V, cgi.shKnife);
queuepoly(VBODY * spin270(), cgi.shKnife, getcs().swordcolor);
return true;
2019-03-30 12:16:19 +00:00
case moKnight: case moKnightMoved: {
ShadowV(V, cgi.shPBody);
2019-03-30 12:16:19 +00:00
const transmatrix VBS = otherbodyparts(V, darkena(0xC0C0A0, 0, 0xC0), m, footphase);
queuepoly(VBODY * VBS, cgi.shPBody, darkena(0xC0C0A0, 0, 0xC0));
2019-03-30 12:16:19 +00:00
if(!racing::on)
queuepoly(VBODY * VBS, cgi.shPSword, darkena(0xFFFF00, 0, 0xFF));
queuepoly(VBODY1 * VBS, cgi.shKnightArmor, darkena(0xD0D0D0, 1, 0xFF));
2019-03-30 12:16:19 +00:00
color_t col;
if(!eubinary && where && where->master->alt)
col = cloakcolor(roundTableRadius(where));
else
col = cloakcolor(newRoundTableRadius());
queuepoly(VBODY2 * VBS, cgi.shKnightCloak, darkena(col, 1, 0xFF));
queuepoly(VHEAD1, cgi.shPHead, darkena(0x703800, 1, 0XFF));
queuepoly(VHEAD, cgi.shPFace, darkena(0xC0C0A0, 0, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
case moGolem: case moGolemMoved: {
ShadowV(V, cgi.shPBody);
2022-01-08 17:53:57 +00:00
const transmatrix VBS = otherbodyparts(V, darkena(col, 1, 0XC0), items[itOrbFish] && items[itOrbEmpathy] ? moWaterElemental : m, footphase);
queuepoly(VBODY * VBS, cgi.shPBody, darkena(col, 0, 0XC0));
queuepoly(VHEAD, cgi.shGolemhead, darkena(col, 1, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xC0C000FF, darkena(col, 0, 0xFF));
return true;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
case moEvilGolem: case moIceGolem: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 2, 0xC0), m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBS, cgi.shPBody, darkena(col, 0, 0XC0));
queuepoly(VHEAD, cgi.shGolemhead, darkena(col, 1, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xFF0000FF, darkena(col, 0, 0xFF));
return true;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
case moFalsePrincess: case moRoseLady: case moRoseBeauty: {
princess:
bool girl = princessgender() == GEN_F;
bool evil = !isPrincess(m);
2017-07-10 18:47:38 +00:00
2019-03-30 12:16:19 +00:00
int facecolor = evil ? 0xC0B090FF : 0xD0C080FF;
ShadowV(V, girl ? cgi.shFemaleBody : cgi.shPBody);
2022-01-08 17:53:57 +00:00
const transmatrix VBS = otherbodyparts(V, facecolor, !evil && items[itOrbFish] && items[itOrbEmpathy] ? moWaterElemental : m, footphase);
queuepoly(VBODY * VBS, girl ? cgi.shFemaleBody : cgi.shPBody, facecolor);
2016-01-02 10:09:13 +00:00
2019-03-30 12:16:19 +00:00
if(m == moPrincessArmed)
2022-12-13 18:04:43 +00:00
queuepoly(VBODY * VBS * lmirror(), vid.cs.charid < 2 ? cgi.shSabre : cgi.shPSword, 0xFFFFFFFF);
2019-03-30 12:16:19 +00:00
if((m == moFalsePrincess || m == moRoseBeauty) && where && where->cpdist == 1)
queuepoly(VBODY * VBS, cgi.shPKnife, 0xFFFFFFFF);
2016-01-02 10:09:13 +00:00
2019-03-30 12:16:19 +00:00
if(m == moRoseLady) {
queuepoly(VBODY * VBS, cgi.shPKnife, 0xFFFFFFFF);
2022-12-13 18:04:43 +00:00
queuepoly(VBODY * VBS * lmirror(), cgi.shPKnife, 0xFFFFFFFF);
2019-03-30 12:16:19 +00:00
}
2017-07-10 18:47:38 +00:00
2019-03-30 12:16:19 +00:00
if(girl) {
queuepoly(VBODY1 * VBS, cgi.shFemaleDress, evil ? 0xC000C0FF : 0x00C000FF);
2019-03-30 12:16:19 +00:00
if(vid.cs.charid < 2)
queuepoly(VBODY2 * VBS, cgi.shPrincessDress, (evil ? 0xC040C0C0 : 0x8080FFC0) | UNTRANS);
2019-03-30 12:16:19 +00:00
}
else {
if(vid.cs.charid < 2)
queuepoly(VBODY1 * VBS, cgi.shPrinceDress, evil ? 0x802080FF : 0x404040FF);
2019-03-30 12:16:19 +00:00
}
2017-07-10 18:47:38 +00:00
2019-03-30 12:16:19 +00:00
if(m == moRoseLady) {
// queuepoly(V, girl ? cgi.shGoatHead : cgi.shDemon, 0x800000FF);
// make her hair a bit darker to stand out in 3D
2019-08-15 13:05:43 +00:00
queuepoly(VHEAD1, girl ? cgi.shFemaleHair : cgi.shPHead, evil ? 0x500050FF : GDIM == 3 ? 0x666A64FF : 0x332A22FF);
2019-03-30 12:16:19 +00:00
}
else if(m == moRoseBeauty) {
if(girl) {
queuepoly(VHEAD1, cgi.shBeautyHair, 0xF0A0D0FF);
queuepoly(VHEAD2, cgi.shFlowerHair, 0xC00000FF);
2019-03-30 12:16:19 +00:00
}
else {
queuepoly(VHEAD1, cgi.shPHead, 0xF0A0D0FF);
2020-04-17 13:03:23 +00:00
queuepoly(VBODY * VBS, cgi.shFlowerHand, 0xC00000FF);
queuepoly(VBODY2 * VBS, cgi.shSuspenders, 0xC00000FF);
2019-03-30 12:16:19 +00:00
}
}
else {
queuepoly(VHEAD1, girl ? cgi.shFemaleHair : cgi.shPHead,
2019-03-30 12:16:19 +00:00
evil ? 0xC00000FF : 0x332A22FF);
}
queuepoly(VHEAD, cgi.shPFace, facecolor);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, evil ? 0x0000C0FF : 0x00C000FF, facecolor);
return true;
2019-03-30 12:16:19 +00:00
}
case moWolf: case moRedFox: case moWolfMoved: case moLavaWolf: {
ShadowV(V, cgi.shWolfBody);
2019-03-30 12:16:19 +00:00
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, darkena(col, 0, 0xFF), footphase);
else
queuepoly(VALEGS, cgi.shWolfLegs, darkena(col, 0, 0xFF));
queuepoly(VABODY, cgi.shWolfBody, darkena(col, 0, 0xFF));
2019-03-30 12:16:19 +00:00
if(m == moRedFox) {
queuepoly(VABODY, cgi.shFoxTail1, darkena(col, 0, 0xFF));
queuepoly(VABODY, cgi.shFoxTail2, darkena(0xFFFFFF, 0, 0xFF));
2019-03-30 12:16:19 +00:00
}
queuepoly(VAHEAD, cgi.shWolfHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, cgi.shWolfEyes, darkena(col, 3, 0xFF));
2019-08-15 13:05:43 +00:00
if(GDIM == 3) {
queuepoly(VAHEAD, cgi.shFamiliarEye, 0xFF);
2022-12-13 18:04:43 +00:00
queuepoly(VAHEAD * lmirror(), cgi.shFamiliarEye, 0xFF);
2019-05-11 13:11:55 +00:00
}
return true;
2019-03-30 12:16:19 +00:00
}
2017-07-10 18:47:38 +00:00
2019-03-30 12:16:19 +00:00
case moReptile: {
ShadowV(V, cgi.shReptileBody);
2019-03-30 12:16:19 +00:00
animallegs(VALEGS, moReptile, darkena(col, 0, 0xFF), footphase);
queuepoly(VABODY, cgi.shReptileBody, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, cgi.shReptileHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, cgi.shReptileEye, darkena(col, 3, 0xFF));
2022-12-13 18:04:43 +00:00
queuepoly(VAHEAD * lmirror(), cgi.shReptileEye, darkena(col, 3, 0xFF));
2019-09-28 12:18:23 +00:00
if(GDIM == 2) queuepoly(VABODY, cgi.shReptileTail, darkena(col, 2, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
2017-07-10 18:47:38 +00:00
2019-03-30 12:16:19 +00:00
case moSalamander: {
ShadowV(V, cgi.shReptileBody);
2019-03-30 12:16:19 +00:00
animallegs(VALEGS, moReptile, darkena(0xD00000, 1, 0xFF), footphase);
queuepoly(VABODY, cgi.shReptileBody, darkena(0xD00000, 0, 0xFF));
queuepoly(VAHEAD, cgi.shReptileHead, darkena(0xD00000, 1, 0xFF));
queuepoly(VAHEAD, cgi.shReptileEye, darkena(0xD00000, 0, 0xFF));
2022-12-13 18:04:43 +00:00
queuepoly(VAHEAD * lmirror(), cgi.shReptileEye, darkena(0xD00000, 0, 0xFF));
queuepoly(VABODY, cgi.shReptileTail, darkena(0xD08000, 0, 0xFF));
return true;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
2020-02-26 00:19:49 +00:00
case moFrog: case moPhaser: case moVaulter: {
ShadowV(V, cgi.shFrogBody);
const shiftmatrix VL = GDIM == 3 ? V : at_smart_lof(V, cgi.ALEG0);
2020-02-26 00:19:49 +00:00
color_t xcolor = darkena(0xFF0000, 1, 0xFF);
int alpha = (m == moPhaser ? 0xC0 : 0xFF);
if(footphase) {
2020-02-28 23:05:06 +00:00
queuepoly(VL, cgi.shFrogJumpFoot, darkena(col, 0, alpha));
2022-12-13 18:04:43 +00:00
queuepoly(VL * lmirror(), cgi.shFrogJumpFoot, darkena(col, 0, alpha));
2020-02-28 23:05:06 +00:00
queuepoly(VALEGS, cgi.shFrogJumpLeg, xcolor);
2022-12-13 18:04:43 +00:00
queuepoly(VALEGS * lmirror(), cgi.shFrogJumpLeg, xcolor);
2020-02-26 00:19:49 +00:00
}
else {
queuepoly(VL, cgi.shFrogRearFoot, darkena(col, 0, alpha));
2022-12-13 18:04:43 +00:00
queuepoly(VL * lmirror(), cgi.shFrogRearFoot, darkena(col, 0, alpha));
2020-02-26 00:19:49 +00:00
queuepoly(VALEGS, cgi.shFrogRearLeg, xcolor);
2022-12-13 18:04:43 +00:00
queuepoly(VALEGS * lmirror(), cgi.shFrogRearLeg, xcolor);
2020-02-26 00:19:49 +00:00
queuepoly(VALEGS, cgi.shFrogRearLeg2, xcolor);
2022-12-13 18:04:43 +00:00
queuepoly(VALEGS * lmirror(), cgi.shFrogRearLeg2, xcolor);
2020-02-26 00:19:49 +00:00
}
queuepoly(VL, cgi.shFrogFrontFoot, darkena(col, 0, alpha));
2022-12-13 18:04:43 +00:00
queuepoly(VL * lmirror(), cgi.shFrogFrontFoot, darkena(col, 0, alpha));
2020-02-26 00:19:49 +00:00
queuepoly(VALEGS, cgi.shFrogFrontLeg, xcolor);
2022-12-13 18:04:43 +00:00
queuepoly(VALEGS * lmirror(), cgi.shFrogFrontLeg, xcolor);
2020-02-26 00:19:49 +00:00
queuepoly(VABODY, cgi.shFrogBody, darkena(col, 0, alpha));
queuepoly(VABODY, cgi.shFrogEye, darkena(col, 3, alpha));
2022-12-13 18:04:43 +00:00
queuepoly(VABODY * lmirror(), cgi.shFrogEye, darkena(col, 3, alpha));
2020-02-26 00:19:49 +00:00
queuepoly(VABODY, cgi.shFrogStripe, xcolor);
return true;
2020-02-26 00:19:49 +00:00
}
2019-03-30 12:16:19 +00:00
case moVineBeast: {
ShadowV(V, cgi.shWolfBody);
2019-03-30 12:16:19 +00:00
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, 0x00FF00FF, footphase);
else
queuepoly(VALEGS, cgi.shWolfLegs, 0x00FF00FF);
queuepoly(VABODY, cgi.shWolfBody, darkena(col, 1, 0xFF));
queuepoly(VAHEAD, cgi.shWolfHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, cgi.shWolfEyes, 0xFF0000FF);
return true;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
case moMouse: case moMouseMoved: {
queuepoly(VALEGS, cgi.shMouse, darkena(col, 0, 0xFF));
queuepoly(VALEGS, cgi.shMouseLegs, darkena(col, 1, 0xFF));
queuepoly(VALEGS, cgi.shMouseEyes, 0xFF);
return true;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
case moRunDog: case moHunterDog: case moHunterGuard: case moHunterChanging: case moFallingDog: {
if(!mmspatial && !footphase)
queuepoly(VABODY, cgi.shDogBody, darkena(col, 0, 0xFF));
2019-03-30 12:16:19 +00:00
else {
ShadowV(V, cgi.shDogTorso);
queuepoly(VABODY, cgi.shDogTorso, darkena(col, 0, 0xFF));
2019-03-30 12:16:19 +00:00
animallegs(VALEGS, moRunDog, m == moFallingDog ? 0xFFFFFFFF : darkena(col, 0, 0xFF), footphase);
2016-08-26 09:58:03 +00:00
}
queuepoly(VAHEAD, cgi.shDogHead, darkena(col, 0, 0xFF));
2019-03-30 12:16:19 +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;
if(m == moHunterChanging) eyecolor = 0xFFFF00, redeyes = true;
int eyes = darkena(eyecolor, 0, 0xFF);
if(redeyes) poly_outline = eyes;
queuepoly(VAHEAD, cgi.shWolf1, eyes).flags |= POLY_FORCEWIDE;
queuepoly(VAHEAD, cgi.shWolf2, eyes).flags |= POLY_FORCEWIDE;
2019-03-30 12:16:19 +00:00
}
queuepoly(VAHEAD, cgi.shWolf3, darkena(m == moRunDog ? 0x202020 : 0x000000, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moOrangeDog: {
if(!mmspatial && !footphase)
queuepoly(VABODY, cgi.shDogBody, darkena(0xFFFFFF, 0, 0xFF));
2017-07-10 18:47:38 +00:00
else {
ShadowV(V, cgi.shDogTorso);
2019-06-01 17:50:17 +00:00
if(GDIM == 2) queuepoly(VABODY, cgi.shDogTorso, darkena(0xFFFFFF, 0, 0xFF));
2019-03-30 12:16:19 +00:00
animallegs(VALEGS, moRunDog, darkena(0xFFFFFF, 0, 0xFF), footphase);
2017-07-10 18:47:38 +00:00
}
queuepoly(VAHEAD, cgi.shDogHead, darkena(0xFFFFFF, 0, 0xFF));
2019-06-01 17:50:17 +00:00
queuepoly(VABODY, cgi.shDogStripes, GDIM == 2 ? darkena(0x303030, 0, 0xFF) : 0xFFFFFFFF);
queuepoly(VAHEAD, cgi.shWolf1, darkena(0x202020, 0, 0xFF));
queuepoly(VAHEAD, cgi.shWolf2, darkena(0x202020, 0, 0xFF));
queuepoly(VAHEAD, cgi.shWolf3, darkena(0x202020, 0, 0xFF));
return true;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
2020-03-02 02:06:15 +00:00
case moShark: case moGreaterShark: case moCShark:
queuepoly(VFISH, cgi.shShark, darkena(col, 0, 0xFF));
return true;
2020-02-26 00:19:49 +00:00
case moPike:
queuepoly(VFISH, cgi.shPikeBody, darkena(col, 0, 0xFF));
queuepoly(VFISH, cgi.shPikeEye, darkena(col, 2, 0xFF));
2022-12-13 18:04:43 +00:00
queuepoly(VFISH * lmirror(), cgi.shPikeEye, darkena(col, 2, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
2019-03-30 12:17:51 +00:00
case moEagle: case moParrot: case moBomberbird: case moAlbatross:
2019-03-30 12:16:19 +00:00
case moTameBomberbird: case moWindCrow: case moTameBomberbirdMoved:
2019-04-22 12:41:14 +00:00
case moSandBird: case moAcidBird: {
ShadowV(V, cgi.shEagle);
2019-08-15 13:05:43 +00:00
auto& sh = GDIM == 3 ? cgi.shAnimatedEagle[wingphase(200)] : cgi.shEagle;
if(m == moParrot && GDIM == 3)
2019-04-22 12:41:14 +00:00
queuepolyat(VBIRD, sh, darkena(col, 0, 0xFF), PPR::SUPERLINE);
2019-03-30 12:16:19 +00:00
else
2019-04-22 12:41:14 +00:00
queuepoly(VBIRD, sh, darkena(col, 0, 0xFF));
return true;
2019-04-22 12:41:14 +00:00
}
2019-03-30 12:16:19 +00:00
case moSparrowhawk: case moWestHawk: {
ShadowV(V, cgi.shHawk);
2019-08-15 13:05:43 +00:00
auto& sh = GDIM == 3 ? cgi.shAnimatedHawk[wingphase(200)] : cgi.shHawk;
queuepoly(VBIRD, sh, darkena(col, 0, 0xFF));
return true;
2016-08-26 09:58:03 +00:00
}
2019-03-30 12:16:19 +00:00
case moButterfly: {
transmatrix Vwing = wingmatrix(100);
ShadowV(V * Vwing, cgi.shButterflyWing);
2019-08-15 13:05:43 +00:00
if(GDIM == 2)
queuepoly(VBIRD * Vwing, cgi.shButterflyWing, darkena(col, 0, 0xFF));
else
queuepoly(VBIRD, cgi.shAnimatedButterfly[wingphase(100)], darkena(col, 0, 0xFF));
queuepoly(VBIRD, cgi.shButterflyBody, darkena(col, 2, 0xFF));
return true;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
case moGadfly: {
transmatrix Vwing = wingmatrix(100);
ShadowV(V * Vwing, cgi.shGadflyWing);
2019-08-15 13:05:43 +00:00
queuepoly(VBIRD * Vwing, GDIM == 2 ? cgi.shGadflyWing : cgi.shAnimatedGadfly[wingphase(100)], darkena(col, 0, 0xFF));
queuepoly(VBIRD, cgi.shGadflyBody, darkena(col, 1, 0xFF));
queuepoly(VBIRD, cgi.shGadflyEye, darkena(col, 2, 0xFF));
2022-12-13 18:04:43 +00:00
queuepoly(VBIRD * lmirror(), cgi.shGadflyEye, darkena(col, 2, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moVampire: case moBat: {
// vampires have no shadow and no mirror images
if(m == moBat) ShadowV(V, cgi.shBatWings);
2019-03-30 12:16:19 +00:00
if(m == moBat || !inmirrorcount) {
2019-08-15 13:05:43 +00:00
queuepoly(VBIRD, GDIM == 2 ? cgi.shBatWings : cgi.shAnimatedBat[wingphase(100)], darkena(0x303030, 0, 0xFF));
queuepoly(VBIRD, GDIM == 2 ? cgi.shBatBody : cgi.shAnimatedBat2[wingphase(100)], darkena(0x606060, 0, 0xFF));
}
/* queuepoly(V, cgi.shBatMouth, darkena(0xC00000, 0, 0xFF));
queuepoly(V, cgi.shBatFang, darkena(0xFFC0C0, 0, 0xFF));
2022-12-13 18:04:43 +00:00
queuepoly(V*lmirror(), cgi.shBatFang, darkena(0xFFC0C0, 0, 0xFF));
queuepoly(V, cgi.shBatEye, darkena(00000000, 0, 0xFF));
2022-12-13 18:04:43 +00:00
queuepoly(V*lmirror(), cgi.shBatEye, darkena(00000000, 0, 0xFF)); */
return true;
2019-03-30 12:16:19 +00:00
}
case moGargoyle: {
ShadowV(V, cgi.shGargoyleWings);
2019-08-15 13:05:43 +00:00
queuepoly(VBIRD, GDIM == 2 ? cgi.shGargoyleWings : cgi.shAnimatedGargoyle[wingphase(300)], darkena(col, 0, 0xD0));
queuepoly(VBIRD, GDIM == 2 ? cgi.shGargoyleBody : cgi.shAnimatedGargoyle2[wingphase(300)], darkena(col, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moZombie: {
int c = darkena(col, where && where->land == laHalloween ? 1 : 0, 0xFF);
const transmatrix VBS = otherbodyparts(V, c, m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBODY * VBS, cgi.shPBody, c);
return true;
2019-03-30 12:16:19 +00:00
}
case moTerraWarrior: {
drawTerraWarrior(V, 7, (where ? where->hitpoints : 7), footphase);
return true;
2019-03-30 12:16:19 +00:00
}
case moVariantWarrior: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBS, cgi.shPBody, darkena(0xFFD500, 0, 0xF0));
if(!peace::on) queuepoly(VBS, cgi.shPSword, 0xFFFF00FF);
queuepoly(VHEAD, cgi.shHood, 0x008000FF);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xFFFF00FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moDesertman: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBS, cgi.shPBody, darkena(col, 0, 0xC0));
if(!peace::on) queuepoly(VBS, cgi.shPSword, 0xFFFF00FF);
queuepoly(VHEAD, cgi.shHood, 0xD0D000C0 | UNTRANS);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x301800FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moMonk: {
const transmatrix VBS = otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, cgi.shRaiderBody);
queuepoly(VBODY * VBS, cgi.shRaiderBody, darkena(col, 0, 0xFF));
queuepoly(VBODY1 * VBS, cgi.shRaiderShirt, darkena(col, 2, 0xFF));
if(!peace::on) queuepoly(VBODY * VBS, cgi.shPKnife, 0xFFC0C0C0);
queuepoly(VBODY2 * VBS, cgi.shRaiderArmor, darkena(col, 1, 0xFF));
queuepolyat(VBODY3 * VBS, cgi.shRatCape2, darkena(col, 2, 0xFF), PPR::MONSTER_ARMOR0);
queuepoly(VHEAD1, cgi.shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, cgi.shPFace, darkena(0xC0C0A0, 0, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moCrusher: {
const transmatrix VBS = otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, cgi.shRaiderBody);
queuepoly(VBODY * VBS, cgi.shRaiderBody, darkena(col, 0, 0xFF));
queuepoly(VBODY1 * VBS, cgi.shRaiderShirt, darkena(col, 2, 0xFF));
queuepoly(VBODY2 * VBS, cgi.shRaiderArmor, darkena(col, 1, 0xFF));
queuepoly(VBODY * VBS, cgi.shFlailTrunk, darkena(col, 1, 0XFF));
queuepoly(VBODY1 * VBS, cgi.shHammerHead, darkena(col, 0, 0XFF));
queuepoly(VHEAD1, cgi.shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, cgi.shPFace, darkena(0xC0C0A0, 0, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moPair: {
const transmatrix VBS = otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, cgi.shRaiderBody);
queuepoly(VBODY * VBS, cgi.shRaiderBody, darkena(col, 0, 0xFF));
queuepoly(VBODY1 * VBS, cgi.shRaiderShirt, darkena(col, 2, 0xFF));
queuepoly(VBODY2 * VBS, cgi.shRaiderArmor, darkena(col, 1, 0xFF));
queuepoly(VBODY * VBS, cgi.shPickAxe, darkena(0xA0A0A0, 0, 0XFF));
queuepoly(VHEAD1, cgi.shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, cgi.shPFace, darkena(0xC0C0A0, 0, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moAltDemon: case moHexDemon: {
const transmatrix VBS = otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, cgi.shRaiderBody);
queuepoly(VBODY * VBS, cgi.shRaiderBody, darkena(col, 0, 0xFF));
queuepoly(VBODY1 * VBS, cgi.shRaiderShirt, darkena(col, 2, 0xFF));
queuepoly(VBODY2 * VBS, cgi.shRaiderArmor, darkena(col, 1, 0xFF));
if(!peace::on) queuepoly(VBODY * VBS, cgi.shPSword, 0xFFD0D0D0);
queuepoly(VHEAD1, cgi.shRaiderHelmet, darkena(col, 0, 0XFF));
queuepoly(VHEAD, cgi.shPFace, darkena(0xC0C0A0, 0, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moSkeleton: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(0xFFFFFF, 0, 0xFF), moSkeleton, footphase);
queuepoly(VBS, cgi.shSkeletonBody, darkena(0xFFFFFF, 0, 0xFF));
2019-08-15 13:05:43 +00:00
if(GDIM == 2) queuepoly(VHEAD, cgi.shSkull, darkena(0xFFFFFF, 0, 0xFF));
if(GDIM == 2) queuepoly(VHEAD1, cgi.shSkullEyes, 0x000000FF);
2019-05-13 12:22:20 +00:00
humanoid_eyes(V, 0x000000FF, 0xFFFFFFFF);
ShadowV(V, cgi.shSkeletonBody);
queuepoly(VBS, cgi.shSabre, 0xFFFFFFFF);
return true;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
case moPalace: case moFatGuard: case moVizier: {
ShadowV(V, cgi.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, cgi.shFatBody, darkena(0xC06000, 0, 0xFF));
2017-07-10 18:47:38 +00:00
col = 0xFFFFFF;
if(!where || where->hitpoints >= 3)
queuepoly(VBODY1 * VBS, cgi.shKnightCloak, darkena(0xFFC0C0, 1, 0xFF));
2017-07-10 18:47:38 +00:00
}
else {
queuepoly(VBODY * VBS, cgi.shPBody, darkena(0xFFD500, 0, 0xFF));
queuepoly(VBODY1 * VBS, cgi.shKnightArmor, m == moVizier ? 0xC000C0FF :
2017-07-10 18:47:38 +00:00
darkena(0x00C000, 1, 0xFF));
if(where && where->hitpoints >= 3)
queuepoly(VBODY2 * VBS, cgi.shKnightCloak, m == moVizier ? 0x800080Ff :
2017-07-10 18:47:38 +00:00
darkena(0x00FF00, 1, 0xFF));
}
queuepoly(VHEAD1, cgi.shTurban1, darkena(col, 1, 0xFF));
2017-07-10 18:47:38 +00:00
if(!where || where->hitpoints >= 2)
queuepoly(VHEAD2, cgi.shTurban2, darkena(col, 0, 0xFF));
queuepoly(VBODY * VBS, cgi.shSabre, 0xFFFFFFFF);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x301800FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moCrystalSage: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, 0xFFFFFFFF, m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBS, cgi.shPBody, 0xFFFFFFFF);
queuepoly(VHEAD1, cgi.shPHead, 0xFFFFFFFF);
queuepoly(VHEAD, cgi.shPFace, 0xFFFFFFFF);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xFFFFFFFF, 0xC0C0C0FF);
return true;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
case moHedge: {
ShadowV(V, cgi.shPBody);
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
queuepoly(VBS, cgi.shPBody, darkena(col, 0, 0xFF));
queuepoly(VBS, cgi.shHedgehogBlade, 0xC0C0C0FF);
queuepoly(VHEAD1, cgi.shPHead, 0x804000FF);
queuepoly(VHEAD, cgi.shPFace, 0xF09000FF);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x00D000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moYeti: case moMonkey: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBS, cgi.shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD1, cgi.shPHead, darkena(col, 0, 0xFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF, darkena(col, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moResearcher: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBS, cgi.shPBody, darkena(0xFFFF00, 0, 0xC0));
queuepoly(VHEAD, cgi.shAztecHead, darkena(col, 0, 0xFF));
queuepoly(VHEAD1, cgi.shAztecCap, darkena(0xC000C0, 0, 0xFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moFamiliar: {
ShadowV(V, cgi.shWolfBody);
queuepoly(VABODY, cgi.shWolfBody, darkena(0xA03000, 0, 0xFF));
2019-03-30 12:16:19 +00:00
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, darkena(0xC04000, 0, 0xFF), footphase);
else
queuepoly(VALEGS, cgi.shWolfLegs, darkena(0xC04000, 0, 0xFF));
2017-07-10 18:47:38 +00:00
queuepoly(VAHEAD, cgi.shFamiliarHead, darkena(0xC04000, 0, 0xFF));
// queuepoly(V, cgi.shCatLegs, darkena(0x902000, 0, 0xFF));
2019-03-30 12:16:19 +00:00
if(true) {
2021-07-12 02:56:29 +00:00
queuepoly(VAHEAD, cgi.shFamiliarEye, darkena(0xFFFF00, 0, 0xFF));
2022-12-13 18:04:43 +00:00
queuepoly(VAHEAD * lmirror(), cgi.shFamiliarEye, darkena(0xFFFF00, 0, 0xFF));
2019-03-30 12:16:19 +00:00
}
return true;
2019-03-30 12:16:19 +00:00
}
2019-03-30 12:16:19 +00:00
case moRanger: {
ShadowV(V, cgi.shPBody);
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
queuepoly(VBS, cgi.shPBody, darkena(col, 0, 0xC0));
if(!peace::on) queuepoly(VBS, cgi.shPSword, darkena(col, 0, 0xFF));
queuepoly(VHEAD, cgi.shArmor, darkena(col, 1, 0xFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moNarciss: {
ShadowV(V, cgi.shPBody);
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
queuepoly(VBS, cgi.shFlowerHand, darkena(col, 0, 0xFF));
queuepoly(VBS, cgi.shPBody, 0xFFE080FF);
if(!peace::on) queuepoly(VBS, cgi.shPKnife, 0xC0C0C0FF);
queuepoly(VHEAD, cgi.shPFace, 0xFFE080FF);
queuepoly(VHEAD1, cgi.shPHead, 0x806A00FF);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moMirrorSpirit: {
ShadowV(V, cgi.shPBody);
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0x90), m, footphase);
queuepoly(VBS, cgi.shPBody, darkena(col, 0, 0x90));
2022-12-13 18:04:43 +00:00
if(!peace::on) queuepoly(VBS * lmirror(), cgi.shPSword, darkena(col, 0, 0xD0));
queuepoly(VHEAD1, cgi.shPHead, darkena(col, 1, 0x90));
queuepoly(VHEAD2, cgi.shPFace, darkena(col, 1, 0x90));
queuepoly(VHEAD, cgi.shArmor, darkena(col, 0, 0xC0));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xFFFFFFFF, darkena(col, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moJiangshi: {
ShadowV(V, cgi.shJiangShi);
auto z2 = WDIM == 3 ? 0 : GDIM == 3 ? -abs(sin(footphase * TAU)) * cgi.human_height/3 : geom3::lev_to_factor(abs(sin(footphase * TAU)) * cgi.human_height);
2019-03-30 12:16:19 +00:00
auto V0 = V;
auto V = at_smart_lof(V0, z2);
2019-03-30 12:16:19 +00:00
otherbodyparts(V, darkena(col, 0, 0xFF), m, m == moJiangshi ? 0 : footphase);
queuepoly(VBODY, cgi.shJiangShi, darkena(col, 0, 0xFF));
queuepoly(VBODY1, cgi.shJiangShiDress, darkena(0x202020, 0, 0xFF));
queuepoly(VHEAD, cgi.shTerraHead, darkena(0x101010, 0, 0xFF));
queuepoly(VHEAD1, cgi.shPFace, darkena(col, 0, 0xFF));
queuepoly(VHEAD2, cgi.shJiangShiCap1, darkena(0x800000, 0, 0xFF));
queuepoly(VHEAD3, cgi.shJiangShiCap2, darkena(0x400000, 0, 0xFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF, darkena(col, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moGhost: case moSeep: case moFriendlyGhost: {
if(m == moFriendlyGhost) col = fghostcolor(where);
2019-08-15 13:05:43 +00:00
queuepolyat(VGHOST, cgi.shGhost, darkena(col, 0, m == moFriendlyGhost ? 0xC0 : 0x80), GDIM == 3 ? PPR::SUPERLINE : cgi.shGhost.prio);
queuepolyat(VGHOST, cgi.shGhostEyes, 0xFF, GDIM == 3 ? PPR::SUPERLINE : cgi.shEyes.prio);
return true;
2019-03-30 12:16:19 +00:00
}
case moVineSpirit: {
queuepoly(VGHOST, cgi.shGhost, 0xD0D0D0C0 | UNTRANS);
2019-08-15 13:05:43 +00:00
queuepolyat(VGHOST, cgi.shGhostEyes, 0xFF0000FF, GDIM == 3 ? PPR::SUPERLINE : cgi.shGhostEyes.prio);
return true;
2019-03-30 12:16:19 +00:00
}
case moFireFairy: {
col = firecolor(0);
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, cgi.shFemaleBody);
queuepoly(VBS, cgi.shFemaleBody, darkena(col, 0, 0XC0));
queuepoly(VHEAD, cgi.shWitchHair, darkena(col, 1, 0xFF));
queuepoly(VHEAD1, cgi.shPFace, darkena(col, 0, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, darkena(col, 1, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
2020-02-26 00:20:33 +00:00
case moRusalka: {
col = watercolor(0);
bool girl = princessgender() == GEN_F;
if(girl) {
const shiftmatrix VBS = VBODY * otherbodyparts(V, col, m, footphase);
2020-02-26 00:20:33 +00:00
ShadowV(V, cgi.shFemaleBody);
queuepoly(VBS, cgi.shFemaleBody, watercolor(100));
queuepoly(VHEAD1, cgi.shFemaleHair, watercolor(150));
// queuepoly(VHEAD2, cgi.shFlowerHair, watercolor(50));
// queuepoly(VHEAD, cgi.shWitchHair, watercolor(150));
queuepoly(VHEAD1, cgi.shPFace, watercolor(200));
queuepoly(VHEAD1, cgi.shWightCloak, watercolor(50) & 0xFFFFFF80);
humanoid_eyes(V, col | 0xFF);
}
else {
const shiftmatrix VBS = VBODY * otherbodyparts(V, col, m, footphase);
2020-02-26 00:20:33 +00:00
ShadowV(V, cgi.shPBody);
queuepoly(VBS, cgi.shPBody, watercolor(100));
queuepoly(VBS, cgi.shSuspenders, watercolor(150));
queuepoly(VHEAD1, cgi.shPHead, watercolor(50));
queuepoly(VHEAD1, cgi.shPFace, watercolor(200));
queuepoly(VHEAD1, cgi.shWightCloak, watercolor(50) & 0xFFFFFF80);
humanoid_eyes(V, col | 0xFF);
}
return true;
2020-02-26 00:20:33 +00:00
}
2019-03-30 12:16:19 +00:00
case moSlime: {
queuepoly(VFISH, cgi.shSlime, darkena(col, 0, 0x80));
queuepoly(VSLIMEEYE, cgi.shSlimeEyes, 0xFF);
return true;
2019-03-30 12:16:19 +00:00
}
2019-04-05 12:41:21 +00:00
2019-03-30 12:16:19 +00:00
case moKrakenH: {
queuepoly(VFISH, cgi.shKrakenHead, darkena(col, 0, 0xD0));
queuepoly(VFISH, cgi.shKrakenEye, 0xFFFFFFC0 | UNTRANS);
queuepoly(VFISH, cgi.shKrakenEye2, 0xC0);
2022-12-13 18:04:43 +00:00
queuepoly(VFISH * lmirror(), cgi.shKrakenEye, 0xFFFFFFC0 | UNTRANS);
queuepoly(VFISH * lmirror(), cgi.shKrakenEye2, 0xC0);
return true;
2019-03-30 12:16:19 +00:00
}
case moKrakenT: {
queuepoly(VFISH, cgi.shSeaTentacle, darkena(col, 0, 0xD0));
return true;
2019-03-30 12:16:19 +00:00
}
case moCultist: case moPyroCultist: case moCultistLeader: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBS, cgi.shPBody, darkena(col, 0, 0xC0));
if(!peace::on) queuepoly(VBS, cgi.shPSword, darkena(col, 2, 0xFF));
queuepoly(VHEAD, cgi.shHood, darkena(col, 1, 0xFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x00FF00FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moPirate: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBS, cgi.shPBody, darkena(0x404040, 0, 0xFF));
queuepoly(VBS, cgi.shPirateHook, darkena(0xD0D0D0, 0, 0xFF));
queuepoly(VHEAD, cgi.shPFace, darkena(0xFFFF80, 0, 0xFF));
queuepoly(VHEAD1, cgi.shEyepatch, darkena(0, 0, 0xC0));
queuepoly(VHEAD2, cgi.shPirateHood, darkena(col, 0, 0xFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moRatling: case moRatlingAvenger: {
const transmatrix VBS = otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, cgi.shYeti);
queuepoly(VLEG, cgi.shRatTail, darkena(col, 0, 0xFF));
queuepoly(VBODY * VBS, cgi.shYeti, darkena(col, 1, 0xFF));
2019-03-30 12:16:19 +00:00
float t = sintick(1000, where ? where->cpdist*M_PI : 0);
int eyecol = t > 0.92 ? 0xFF0000 : 0;
2019-08-15 13:05:43 +00:00
if(GDIM == 2) {
queuepoly(VHEAD, cgi.shRatHead, darkena(col, 0, 0xFF));
queuepoly(VHEAD, cgi.shWolf1, darkena(eyecol, 0, 0xFF));
queuepoly(VHEAD, cgi.shWolf2, darkena(eyecol, 0, 0xFF));
queuepoly(VHEAD, cgi.shWolf3, darkena(0x202020, 0, 0xFF));
if(m == moRatlingAvenger) queuepoly(VHEAD1, cgi.shRatCape1, 0x303030FF);
2019-05-12 18:11:17 +00:00
}
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2019-05-12 18:11:17 +00:00
else {
2023-02-04 00:58:13 +00:00
shiftmatrix V1 = V * lzpush(cgi.AHEAD - zc(0.4) - zc(0.98) + cgi.HEAD); // * cpush(0, cgi.scalefactor * (-0.1));
queuepoly(V1, cgi.shRatHead, darkena(col, 0, 0xFF));
2019-05-24 21:49:23 +00:00
/*
queuepoly(V1, cgi.shFamiliarEye, darkena(eyecol, 0, 0xFF));
2022-12-13 18:04:43 +00:00
queuepoly(V1 * lmirror(), cgi.shFamiliarEye, darkena(eyecol, 0, 0xFF));
queuepoly(V1, cgi.shWolfEyes, darkena(col, 3, 0xFF));
2019-05-24 21:49:23 +00:00
*/
queuepoly(V1, cgi.shRatEye1, darkena(eyecol, 0, 0xFF));
queuepoly(V1, cgi.shRatEye2, darkena(eyecol, 0, 0xFF));
queuepoly(V1, cgi.shRatEye3, darkena(0x202020, 0, 0xFF));
if(m == moRatlingAvenger) queuepoly(V1, cgi.shRatCape1, 0x303030FF);
2019-05-12 18:11:17 +00:00
}
2019-06-01 14:59:04 +00:00
#endif
2019-03-30 12:16:19 +00:00
if(m == moRatlingAvenger) {
queuepoly(VBODY1 * VBS, cgi.shRatCape2, 0x484848FF);
2019-03-30 12:16:19 +00:00
}
return true;
2019-03-30 12:16:19 +00:00
}
case moViking: {
const transmatrix VBS = otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBODY * VBS, cgi.shPBody, darkena(0xE00000, 0, 0xFF));
if(!peace::on) queuepoly(VBODY * VBS, cgi.shPSword, darkena(0xD0D0D0, 0, 0xFF));
queuepoly(VBODY1 * VBS, cgi.shKnightCloak, darkena(0x404040, 0, 0xFF));
queuepoly(VHEAD, cgi.shVikingHelmet, darkena(0xC0C0C0, 0, 0XFF));
queuepoly(VHEAD, cgi.shPFace, darkena(0xFFFF80, 0, 0xFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moOutlaw: {
const transmatrix VBS = otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBODY * VBS, cgi.shPBody, darkena(col, 0, 0xFF));
queuepoly(VBODY1 * VBS, cgi.shKnightCloak, darkena(col, 1, 0xFF));
queuepoly(VHEAD1, cgi.shWestHat1, darkena(col, 2, 0XFF));
queuepoly(VHEAD2, cgi.shWestHat2, darkena(col, 1, 0XFF));
queuepoly(VHEAD, cgi.shPFace, darkena(0xFFFF80, 0, 0xFF));
queuepoly(VBODY * VBS, cgi.shGunInHand, darkena(col, 1, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moNecromancer: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, cgi.shPBody);
queuepoly(VBS, cgi.shPBody, 0xC00000C0 | UNTRANS);
queuepoly(VHEAD, cgi.shHood, darkena(col, 1, 0xFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xFF0000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moDraugr: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, 0x483828D0 | UNTRANS, m, footphase);
queuepoly(VBS, cgi.shPBody, 0x483828D0 | UNTRANS);
queuepoly(VBS, cgi.shPSword, 0xFFFFD0A0 | UNTRANS);
queuepoly(VHEAD, cgi.shPHead, 0x483828D0 | UNTRANS);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xFF0000FF, 0x483828FF);
// queuepoly(V, cgi.shSkull, 0xC06020D0);
//queuepoly(V, cgi.shSkullEyes, 0x000000D0);
// queuepoly(V, cgi.shWightCloak, 0xC0A080A0);
2019-03-30 12:16:19 +00:00
int b = where ? where->cpdist : 0;
b--;
if(b < 0) b = 0;
if(b > 6) b = 6;
queuepoly(VHEAD1, cgi.shWightCloak, color_t(0x605040A0 | UNTRANS) + color_t(0x10101000 * b));
return true;
2019-03-30 12:16:19 +00:00
}
case moVoidBeast: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, 0x080808D0 | UNTRANS, m, footphase);
queuepoly(VBS, cgi.shPBody, 0x080808D0 | UNTRANS);
queuepoly(VHEAD, cgi.shPHead, 0x080808D0 | UNTRANS);
queuepoly(VHEAD, cgi.shWightCloak, 0xFF0000A0 | UNTRANS);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xFF0000FF, 0x080808FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moGoblin: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, cgi.shYeti);
queuepoly(VBS, cgi.shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD, cgi.shArmor, darkena(col, 1, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x800000FF, darkena(col, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moLancer: case moFlailer: case moMiner: {
shiftmatrix V2 = V;
2019-03-30 12:16:19 +00:00
if(m == moLancer)
V2 = V * spin((where && where->type == 6) ? -60._deg : -90._deg );
shiftmatrix Vh = at_smart_lof(V2, cgi.HEAD);
shiftmatrix Vb = at_smart_lof(V2, cgi.BODY);
2019-03-30 12:16:19 +00:00
Vb = Vb * otherbodyparts(V2, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V2, cgi.shPBody);
queuepoly(Vb, cgi.shPBody, darkena(col, 0, 0xC0));
queuepoly(Vh, m == moFlailer ? cgi.shArmor : cgi.shHood, darkena(col, 1, 0XFF));
2019-03-30 12:16:19 +00:00
if(m == moMiner)
queuepoly(Vb, cgi.shPickAxe, darkena(0xC0C0C0, 0, 0XFF));
2019-03-30 12:16:19 +00:00
if(m == moLancer)
queuepoly(Vb, cgi.shPike, darkena(col, 0, 0XFF));
2019-03-30 12:16:19 +00:00
if(m == moFlailer) {
queuepoly(Vb, cgi.shFlailBall, darkena(col, 0, 0XFF));
queuepoly(Vb, cgi.shFlailChain, darkena(col, 1, 0XFF));
queuepoly(Vb, cgi.shFlailTrunk, darkena(col, 0, 0XFF));
2019-03-30 12:16:19 +00:00
}
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
return true;
2019-03-30 12:16:19 +00:00
}
case moTroll: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, cgi.shYeti);
queuepoly(VBS, cgi.shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD1, cgi.shPHead, darkena(col, 1, 0XFF));
queuepoly(VHEAD, cgi.shPFace, darkena(col, 2, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x004000FF, darkena(col, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moFjordTroll: case moForestTroll: case moStormTroll: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, cgi.shYeti);
queuepoly(VBS, cgi.shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD1, cgi.shPHead, darkena(col, 1, 0XFF));
queuepoly(VHEAD, cgi.shPFace, darkena(col, 2, 0XFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x004000FF, darkena(col, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moDarkTroll: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, cgi.shYeti);
queuepoly(VBS, cgi.shYeti, darkena(col, 0, 0xC0));
queuepoly(VHEAD1, cgi.shPHead, darkena(col, 1, 0XFF));
queuepoly(VHEAD, cgi.shPFace, 0xFFFFFF80 | UNTRANS);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF, darkena(col, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moRedTroll: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xFF), m, footphase);
ShadowV(V, cgi.shYeti);
queuepoly(VBS, cgi.shYeti, darkena(0xFF8000, 0, 0XFF));
queuepoly(VHEAD1, cgi.shPHead, darkena(col, 0, 0xC0));
queuepoly(VHEAD, cgi.shPFace, 0xFFFFFF80 | UNTRANS);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF, darkena(col, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moEarthElemental: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, cgi.shWaterElemental);
queuepoly(VBS, cgi.shWaterElemental, darkena(col, 0, 0xC0));
queuepoly(VHEAD1, cgi.shFemaleHair, darkena(col, 0, 0XFF));
queuepoly(VHEAD, cgi.shPFace, 0xF0000080 | UNTRANS);
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xD0D000FF, darkena(col, 1, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moWaterElemental: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, watercolor(50), m, footphase);
ShadowV(V, cgi.shWaterElemental);
queuepoly(VBS, cgi.shWaterElemental, watercolor(0));
queuepoly(VHEAD1, cgi.shFemaleHair, watercolor(100));
queuepoly(VHEAD, cgi.shPFace, watercolor(200));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x0000FFFF, watercolor(150));
return true;
2019-03-30 12:16:19 +00:00
}
case moFireElemental: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(firecolor(50), 0, 0xFF), m, footphase);
ShadowV(V, cgi.shWaterElemental);
queuepoly(VBS, cgi.shWaterElemental, darkena(firecolor(0), 0, 0xFF));
queuepoly(VHEAD1, cgi.shFemaleHair, darkena(firecolor(100), 0, 0xFF));
queuepoly(VHEAD, cgi.shPFace, darkena(firecolor(200), 0, 0xFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, darkena(firecolor(200), 0, 0xFF), darkena(firecolor(50), 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moAirElemental: {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0x40), m, footphase);
ShadowV(V, cgi.shWaterElemental);
queuepoly(VBS, cgi.shWaterElemental, darkena(col, 0, 0x80));
queuepoly(VHEAD1, cgi.shFemaleHair, darkena(col, 0, 0x80));
queuepoly(VHEAD, cgi.shPFace, darkena(col, 0, 0x80));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xFFFFFFFF, darkena(col, 1, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moWorm: case moWormwait: case moHexSnake: {
queuepoly(V, cgi.shWormHead, darkena(col, 0, 0xFF));
queuepolyat(V, cgi.shWormEyes, 0xFF, PPR::ONTENTACLE_EYES);
return true;
2019-03-30 12:16:19 +00:00
}
case moDragonHead: {
queuepoly(V, cgi.shDragonHead, darkena(col, 0, 0xFF));
queuepolyat(V, cgi.shDragonEyes, 0xFF, PPR::ONTENTACLE_EYES);
2019-03-30 12:16:19 +00:00
int noscolor = 0xFF0000FF;
queuepoly(V, cgi.shDragonNostril, noscolor);
2022-12-13 18:04:43 +00:00
queuepoly(V * lmirror(), cgi.shDragonNostril, noscolor);
return true;
2019-03-30 12:16:19 +00:00
}
case moDragonTail: {
queuepoly(V, cgi.shDragonSegment, darkena(col, 0, 0xFF));
return true;
2019-03-30 12:16:19 +00:00
}
case moTentacle: case moTentaclewait: case moTentacleEscaping: {
queuepoly(V, cgi.shTentHead, darkena(col, 0, 0xFF));
ShadowV(V, cgi.shTentHead, PPR::GIANTSHADOW);
return true;
2019-03-30 12:16:19 +00:00
}
2019-03-30 16:51:37 +00:00
case moAsteroid: {
queuepoly(V, cgi.shAsteroid[1], darkena(col, 0, 0xFF));
return true;
2019-03-30 16:51:37 +00:00
}
2021-05-27 11:00:20 +00:00
2021-06-09 02:33:55 +00:00
#if CAP_COMPLEX2
2021-05-29 13:45:37 +00:00
case moAnimatedDie: case moAngryDie: {
2021-05-27 11:00:20 +00:00
if(where)
dice::draw_die(where, V, 1, darkena(col, 0, 0xFF));
else
queuepoly(V, cgi.shDodeca, darkena(col, 0, 0xFF));
return true;
}
2021-06-09 02:33:55 +00:00
#endif
2021-05-27 11:00:20 +00:00
2019-03-30 12:16:19 +00:00
default: ;
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
if(isPrincess(m)) goto princess;
else if(isBull(m)) {
ShadowV(V, cgi.shBullBody);
2019-03-30 12:16:19 +00:00
int hoofcol = darkena(gradient(0, col, 0, .65, 1), 0, 0xFF);
if(mmspatial || footphase)
animallegs(VALEGS, moRagingBull, hoofcol, footphase);
queuepoly(VABODY, cgi.shBullBody, darkena(gradient(0, col, 0, .80, 1), 0, 0xFF));
queuepoly(VAHEAD, cgi.shBullHead, darkena(col, 0, 0xFF));
queuepoly(VAHEAD, cgi.shBullHorn, darkena(0xFFFFFF, 0, 0xFF));
2022-12-13 18:04:43 +00:00
queuepoly(VAHEAD * lmirror(), cgi.shBullHorn, darkena(0xFFFFFF, 0, 0xFF));
2017-07-10 18:47:38 +00:00
}
2019-03-30 12:16:19 +00:00
else if(isBug(m)) {
ShadowV(V, cgi.shBugBody);
2019-03-30 12:16:19 +00:00
if(!mmspatial && !footphase)
queuepoly(VABODY, cgi.shBugBody, darkena(col, 0, 0xFF));
2019-03-30 12:16:19 +00:00
else {
animallegs(VALEGS, moBug0, darkena(col, 0, 0xFF), footphase);
queuepoly(VABODY, cgi.shBugAntenna, darkena(col, 1, 0xFF));
2019-03-30 12:16:19 +00:00
}
queuepoly(VABODY, cgi.shBugArmor, darkena(col, 1, 0xFF));
2017-07-10 18:47:38 +00:00
}
2018-01-04 14:46:06 +00:00
else if(isSwitch(m)) {
queuepoly(VFISH, cgi.shJelly, darkena(col, 0, 0xD0));
queuepolyat(VBODY, cgi.shJelly, darkena(col, 0, 0xD0), PPR::MONSTER_BODY);
queuepolyat(VHEAD, cgi.shJelly, darkena(col, 0, 0xD0), PPR::MONSTER_HEAD);
queuepolyat(VHEAD, cgi.shSlimeEyes, 0xFF, PPR::MONSTER_HEAD);
2018-01-04 14:46:06 +00:00
}
2019-03-30 12:16:19 +00:00
else if(isDemon(m)) {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 0, 0xC0), m, footphase);
queuepoly(VBS, cgi.shPBody, darkena(col, 1, 0xC0));
ShadowV(V, cgi.shPBody);
2017-07-10 18:47:38 +00:00
int acol = col;
if(xch == 'D') acol = 0xD0D0D0;
queuepoly(VHEAD, cgi.shDemon, darkena(acol, 0, 0xFF));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0xFF0000FF, 0xC00000FF);
2016-01-02 10:09:13 +00:00
}
2017-12-30 14:12:15 +00:00
else if(isMagneticPole(m)) {
if(m == moNorthPole)
queuepolyat(VBODY * spin180(), cgi.shTentacle, 0x000000C0, PPR::TENTACLE1);
queuepolyat(VBODY, cgi.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) {
ShadowV(V, cgi.shTrylobite);
2017-07-10 18:47:38 +00:00
if(!mmspatial)
queuepoly(VABODY, cgi.shTrylobite, darkena(col, 1, 0xC0));
2017-03-23 10:53:57 +00:00
else {
queuepoly(VABODY, cgi.shTrylobiteBody, darkena(col, 1, 0xFF));
2017-07-10 18:47:38 +00:00
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, cgi.shTrylobiteHead, darkena(acol, 0, 0xFF));
2016-01-02 10:09:13 +00:00
}
2021-05-17 12:18:45 +00:00
else if(m == moHexer) {
const shiftmatrix VBS = VBODY * otherbodyparts(V, darkena(col, 1, 0xFF), m, footphase);
ShadowV(V, cgi.shFemaleBody);
queuepoly(VBS, cgi.shFemaleBody, darkena(0x800080, 0, 0xFF));
queuepoly(VHEAD1, cgi.shWitchHair, darkena(0xFF00FF, 1, 0xFF));
queuepoly(VHEAD, cgi.shPFace, darkena(0xFFFFFF, 0, 0xFF));
queuepoly(VBS, cgi.shWitchDress, darkena(col, 1, 0XC0));
humanoid_eyes(V, 0xF000F0FF);
}
2017-07-10 18:47:38 +00:00
else if(isWitch(m)) {
const shiftmatrix 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);
2021-05-21 23:26:59 +00:00
if(m == moWitchWinter && where) drawWinter(V, 0, iinf[itOrbWinter].color);
2017-07-10 18:47:38 +00:00
if(m == moWitchFlash && where) drawFlash(V);
if(m == moWitchSpeed && where) drawSpeed(V);
if(m == moWitchFire) col = firecolor(0);
ShadowV(V, cgi.shFemaleBody);
queuepoly(VBS, cgi.shFemaleBody, darkena(col, 0, cc));
// queuepoly(cV2, ct, cgi.shPSword, darkena(col, 0, 0XFF));
// queuepoly(V, cgi.shHood, darkena(col, 0, 0XC0));
2017-07-10 18:47:38 +00:00
if(m == moWitchFire) col = firecolor(100);
queuepoly(VHEAD1, cgi.shWitchHair, darkena(col, 1, cc));
2017-07-10 18:47:38 +00:00
if(m == moWitchFire) col = firecolor(200);
queuepoly(VHEAD, cgi.shPFace, darkena(col, 0, cc));
2017-07-10 18:47:38 +00:00
if(m == moWitchFire) col = firecolor(300);
queuepoly(VBS, cgi.shWitchDress, darkena(col, 1, 0XC0));
2019-05-12 18:08:42 +00:00
humanoid_eyes(V, 0x000000FF);
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
// just for the HUD glyphs...
2019-03-30 12:16:19 +00:00
else if(isAnyIvy(m)) {
queuepoly(VBODY, cgi.shILeaf[0], darkena(col, 0, 0xFF));
2017-03-23 10:53:57 +00:00
}
2016-01-02 10:09:13 +00:00
else
draw_ascii(V1, minf[m].glyph, asciicol, 1.5);
2017-07-10 18:47:38 +00:00
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 shiftmatrix& V, color_t col, bool dh, ld footphase, color_t asciicol) {
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, asciicol);
2017-07-10 18:47:38 +00:00
if(dh) {
darken--;
}
return b;
}
2015-08-08 13:57:52 +00:00
EX shiftmatrix playerV;
2015-08-08 13:57:52 +00:00
EX bool applyAnimation(cell *c, shiftmatrix& V, double& footphase, int layer) {
2017-07-10 18:47:38 +00:00
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:
auto TC0 = cgi.emb->anim_tile_center();
2020-12-26 17:09:09 +00:00
if(among(a.attacking, 1, 3))
R = hdist(a.attackat * TC0, a.wherenow * TC0);
2018-01-25 16:19:50 +00:00
else
R = hdist(a.wherenow * TC0, TC0);
2017-07-10 18:47:38 +00:00
aspd *= (1+R+(shmup::on?1:0));
2020-12-26 17:09:09 +00:00
if(a.attacking == 3 && aspd > R) aspd = R;
2020-12-26 17:09:09 +00:00
if((R < aspd || std::isnan(R) || std::isnan(aspd) || R > 10) && a.attacking != 3) {
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 {
transmatrix T = inverse(a.wherenow);
ld z = cgi.emb->anim_center_z();
2023-01-26 23:27:10 +00:00
if(z) T = lzpush(-z) * T;
2019-08-18 20:27:55 +00:00
hyperpoint wnow;
2020-12-26 17:09:09 +00:00
if(a.attacking == 1 || a.attacking == 3)
wnow = T * a.attackat * TC0;
2018-01-25 16:19:50 +00:00
else
wnow = T * TC0;
2019-08-18 20:27:55 +00:00
shift_v_towards(T, shiftless(wnow), aspd, shift_method(smaAnimation));
if(z) T = lzpush(z) * T;
a.wherenow = inverse(T);
fixmatrix(a.wherenow);
if(cgflags & qAFFINE) {
transmatrix T = a.wherenow;
fixmatrix_euclid(T);
a.wherenow = inverse(T) * a.wherenow;
for(int i=0; i<MDIM; i++)
a.wherenow[i] = lerp(a.wherenow[i], Id[i], aspd / R);
a.wherenow = T * a.wherenow;
}
2018-01-25 16:19:50 +00:00
a.footphase += a.attacking == 2 ? -aspd : aspd;
2020-12-26 17:09:09 +00:00
if(a.attacking == 3 && aspd >= R) {
a.footphase = 0;
hyperpoint h1 = a.wherenow * C0;
a.wherenow = rgpushxto0(h1) * lrspintox(h1);
2020-12-26 17:09:09 +00:00
}
2017-07-10 18:47:38 +00:00
footphase = a.footphase;
V = V * a.wherenow * lrspintox(wnow);
if(cgi.emb->is_cylinder() && !gproduct) {
if(nil) {
V = V * lzpush(1);
V = V * spin270();
V = V * lzpush(-1);
}
else
V = V * cspin90(1, 0);
}
2022-12-13 18:04:43 +00:00
if(a.mirrored) V = V * lmirror();
if(a.attacking == 2) V = V * lpispin();
2017-07-10 18:47:38 +00:00
a.ltick = ticks;
return true;
}
}
2016-01-02 10:09:13 +00:00
double chainAngle(cell *c, shiftmatrix& V, cell *c2, double dft, const shiftmatrix &Vwhere) {
2023-02-04 09:19:13 +00:00
if(cgi.emb->no_spin()) return 0;
2017-07-22 23:33:27 +00:00
if(!gmatrix0.count(c2)) return dft;
hyperpoint h = cgi.emb->anim_tile_center();
2017-07-10 18:47:38 +00:00
if(animations[LAYER_BIG].count(c2)) h = animations[LAYER_BIG][c2].wherenow * h;
h = inverse_shift(V, Vwhere) * calc_relative_matrix(c2, c, C0) * h;
ld z = cgi.emb->anim_center_z();
2023-02-04 09:19:13 +00:00
if(z) h = lzpush(-z) * 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, cell *c2, shiftmatrix& V, const shiftmatrix &Vwhere, ld& length) {
2023-02-04 09:19:13 +00:00
if(cgi.emb->no_spin()) return false;
hyperpoint h = cgi.emb->anim_tile_center();
2017-07-10 18:47:38 +00:00
if(animations[LAYER_BIG].count(c2)) h = animations[LAYER_BIG][c2].wherenow * h;
h = inverse_shift(V, Vwhere) * h;
2023-02-04 09:19:13 +00:00
length = hdist(h, tile_center());
ld z = cgi.emb->center_z();
if(z) h = lzpush(-z) * 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
2019-08-09 21:39:36 +00:00
EX int cellcolor(cell *c) {
2017-07-10 18:47:38 +00:00
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
if(c->item && !itemHiddenFromSight(c)) {
2017-07-10 18:47:38 +00:00
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 && 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;
}
2019-08-09 19:00:52 +00:00
EX bool dont_face_pc = false;
2018-01-30 23:10:44 +00:00
2021-05-27 11:00:20 +00:00
EX shiftmatrix die_target;
EX bool drawMonster(const shiftmatrix& Vparam, int ct, cell *c, color_t col, color_t asciicol) {
#if CAP_SHAPES
2015-08-08 13:57:52 +00:00
bool darkhistory = history::includeHistory && history::inkillhistory.count(c);
2021-08-02 19:14:21 +00:00
color_t outline = OUTLINE_NONE;
if(doHighlight()) {
outline =
2017-07-10 18:47:38 +00:00
(isPlayerOn(c) || isFriendly(c)) ? OUTLINE_FRIEND :
noHighlight(c->monst) ? OUTLINE_NONE :
OUTLINE_ENEMY;
2021-08-02 19:14:21 +00:00
poly_outline = outline;
}
2017-07-10 18:47:38 +00:00
// highlight faraway enemies if that's needed
if (vid.faraway_highlight && c->cpdist >= 6 && vid.faraway_highlight <= get_threat_level(c)) {
// basic red-green oscillation
color_t r = ceil(255*abs(sintick(1000, 0.25)));
color_t g = ceil(255*abs(sintick(1000, 0.00)));
color_t hlc = (r<<16) + (g<<8);
hlc = gradient(col, hlc, 0, vid.faraway_highlight_color, 100);
if(c->cpdist == 6)
hlc = gradient(0, hlc, 0, 1, 1.5);
addauraspecial(tC0(Vparam), hlc, 0);
}
2017-07-10 18:47:38 +00:00
bool nospins = false, nospinb = false;
double footphaseb = 0, footphase = 0;
shiftmatrix Vs = Vparam; nospins = applyAnimation(c, Vs, footphase, LAYER_SMALL);
shiftmatrix 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;
2019-03-30 12:35:03 +00:00
bool half_elliptic = elliptic && GDIM == 3 && WDIM == 2;
bool mirrored = det(Vparam.T) > 0;
bool res = m;
2019-03-30 12:35:03 +00:00
if(!m) ;
else if(half_elliptic && mirrored != c->monmirror && !isMimic(m)) ;
2017-07-10 18:47:38 +00:00
2019-03-30 12:35:03 +00:00
else if(isAnyIvy(c) || isWorm(c)) {
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;
2019-08-15 13:05:43 +00:00
if(GDIM == 3)
addradar(Vparam, minf[m].glyph, asciicol, isFriendly(m) ? 0x00FF00FF : 0xFF0000FF);
2019-05-11 13:20:34 +00:00
shiftmatrix Vb0 = Vb;
2019-08-15 13:05:43 +00:00
if(c->mondir != NODIR && GDIM == 3 && isAnyIvy(c)) {
auto V1 = at_smart_lof(Vparam, cgi.ABODY);
auto V2 = at_smart_lof(Vparam * currentmap->adj(c, c->mondir), cgi.ABODY);
queueline(V1 * tile_center(), V2 * tile_center(), (col << 8) + 0xFF, 0);
2019-02-26 21:08:33 +00:00
}
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);
if(nospinb) {
chainAnimation(c, c2, Vb, Vparam * currentmap->adj(c, c->mondir), 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);
}
2023-02-04 09:19:13 +00:00
shiftmatrix Vbn = Vb;
if(cgi.emb->no_spin() && c->mondir != NODIR) {
Vbn = Vparam * currentmap->adj(c, c->mondir);
ld dummy;
applyAnimation(c->move(c->mondir), Vbn, dummy, LAYER_BIG);
}
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vb = Vb * lmirror();
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, cgi.shIBranch, (col << 8) + 0xFF);
2018-01-11 22:18:02 +00:00
/* else if(c->monst < moTentacle && wormstyle == 0) {
ShadowV(Vb, cgi.shTentacleX, PPR::GIANTSHADOW);
queuepoly(at_smart_lof(Vb, cgi.ABODY), cgi.shTentacleX, 0xFF);
queuepoly(at_smart_lof(Vb, cgi.ABODY), cgi.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(at_smart_lof(Vb, cgi.ABODY), cgi.shDragonSegment, darkena(col, 0, 0xFF));
ShadowV(Vb, cgi.shDragonSegment, PPR::GIANTSHADOW);
2017-07-10 18:47:38 +00:00
}
}
else {
if(c->monst == moTentacleGhost) {
hyperpoint V0 = history::on ? unshift(tC0(Vs)) : inverse_shift(cwtV, tC0(Vs));
2022-12-08 18:38:06 +00:00
hyperpoint V1 = lspintox(V0) * V0;
2022-12-13 18:04:43 +00:00
Vs = cwtV * lrspintox(V0) * rpushxto0(V1) * lpispin();
drawMonsterType(moGhost, c, Vs, col, footphase, asciicol);
2017-07-10 18:47:38 +00:00
col = minf[moTentacletail].color;
}
2018-01-11 22:18:02 +00:00
/*
queuepoly(at_smart_lof(Vb, cgi.ABODY), cgi.shTentacleX, 0xFFFFFFFF);
queuepoly(at_smart_lof(Vb, cgi.ABODY), cgi.shTentacle, (col << 8) + 0xFF);
ShadowV(Vb, cgi.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 ? cgi.shWormSegment : cgi.shSmallWormSegment;
2018-01-11 22:18:02 +00:00
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;
2021-08-02 19:14:21 +00:00
if(doHighlight())
poly_outline = outline;
shiftmatrix Vbx = Vb;
2023-02-04 09:19:13 +00:00
if(cgi.emb->no_spin()) {
hyperpoint h = inverse_shift(Vb, Vbn * tile_center());
h = cgi.emb->actual_to_intermediate(h);
h *= i / 12.;
Vbx = Vb * cgi.emb->intermediate_to_actual_translation(h);
}
else {
if(WDIM == 2) Vbx = Vbx * spin(sin(TAU * i / 12) * wav / (i+.1));
Vbx = Vbx * lxpush(length * (i) / 12.0);
}
// shiftmatrix Vbx2 = Vnext * xpush(length2 * i / 6.0);
2022-12-13 18:04:43 +00:00
// Vbx = Vbx * rspintox(inverse(Vbx) * Vbx2 * C0) * lpispin();
2018-08-28 12:27:23 +00:00
ShadowV(Vbx, sh, PPR::GIANTSHADOW);
queuepoly(at_smart_lof(Vbx, cgi.ABODY), sh, (col0 << 8) + 0xFF);
2018-01-11 22:18:02 +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
else {
shiftmatrix T = Vparam * ddspin(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(T*xspinpush0(90._deg, u*cgi.crossf/5), T*xspinpush(0, cgi.crossf)*xspinpush0(90._deg, u*cgi.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) {
2019-03-30 12:35:03 +00:00
if(isAnyIvy(c)) {
2022-12-08 18:38:06 +00:00
if(mhybrid) {
2019-08-19 08:33:07 +00:00
queuepoly(Vb, cgi.shILeaf[ctof(c)], darkena(col, 0, 0xFF));
for(int a=0; a<c->type-2; a++)
queuepoly(Vb * spin(a * TAU / (c->type-2)), cgi.shILeaf[2], darkena(col, 0, 0xFF));
2019-08-19 08:33:07 +00:00
}
else if(GDIM == 3) {
queuepoly(face_the_player(at_smart_lof(Vb, cgi.ABODY)), cgi.shILeaf[1], darkena(col, 0, 0xFF));
2019-02-26 21:08:33 +00:00
}
else {
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vb = Vb * lmirror();
queuepoly(at_smart_lof(Vb, cgi.ABODY), cgi.shILeaf[ctof(c)], darkena(col, 0, 0xFF));
ShadowV(Vb, cgi.shILeaf[ctof(c)], PPR::GIANTSHADOW);
2019-02-26 21:08:33 +00:00
}
2017-07-10 18:47:38 +00:00
}
else if(m == moWorm || m == moWormwait || m == moHexSnake) {
2022-12-13 18:04:43 +00:00
Vb = Vb * lpispin();
if(c->monmirror) Vb = Vb * lmirror();
shiftmatrix Vbh = at_smart_lof(Vb, cgi.AHEAD);
queuepoly(Vbh, cgi.shWormHead, darkena(col, 0, 0xFF));
queuepolyat(Vbh, cgi.shWormEyes, 0xFF, PPR::ONTENTACLE_EYES);
ShadowV(Vb, cgi.shWormHead, PPR::GIANTSHADOW);
2017-07-10 18:47:38 +00:00
}
else if(m == moDragonHead) {
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vb = Vb * lmirror();
shiftmatrix Vbh = at_smart_lof(Vb, cgi.AHEAD);
ShadowV(Vb, cgi.shDragonHead, PPR::GIANTSHADOW);
queuepoly(Vbh, cgi.shDragonHead, darkena(col, c->hitpoints?0:1, 0xFF));
2022-12-13 18:04:43 +00:00
queuepolyat(Vbh/* * lpispin() */, cgi.shDragonEyes, 0xFF, PPR::ONTENTACLE_EYES);
2017-07-10 18:47:38 +00:00
int noscolor = (c->hitpoints == 1 && c->stuntime ==1) ? 0xFF0000FF : 0xFF;
queuepoly(Vbh, cgi.shDragonNostril, noscolor);
2022-12-13 18:04:43 +00:00
queuepoly(Vbh * lmirror(), cgi.shDragonNostril, noscolor);
2017-07-10 18:47:38 +00:00
}
else if(m == moTentacle || m == moTentaclewait || m == moTentacleEscaping) {
2022-12-13 18:04:43 +00:00
Vb = Vb * lpispin();
if(c->monmirror) Vb = Vb * lmirror();
shiftmatrix Vbh = at_smart_lof(Vb, cgi.AHEAD);
queuepoly(Vbh, cgi.shTentHead, darkena(col, 0, 0xFF));
ShadowV(Vb, cgi.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, c2, Vb, Vparam * currentmap->adj(c, nd), length);
2022-12-13 18:04:43 +00:00
Vb = Vb * lpispin();
2017-07-10 18:47:38 +00:00
}
else {
Vb = Vb0 * ddspin180(c, nd);
2017-07-10 18:47:38 +00:00
}
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vb = Vb * lmirror();
shiftmatrix Vbb = at_smart_lof(Vb, cgi.ABODY);
queuepoly(Vbb, cgi.shDragonTail, darkena(col, c->hitpoints?0:1, 0xFF));
ShadowV(Vb, cgi.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, c2, Vb, Vparam * currentmap->adj(c, nd), length);
2022-12-13 18:04:43 +00:00
Vb = Vb * lpispin();
double ang = chainAngle(c, Vb, c->move(c->mondir), currentmap->spin_angle(c, c->mondir) - currentmap->spin_angle(c, nd), Vparam);
2017-07-10 18:47:38 +00:00
ang /= 2;
Vb = Vb * spin(M_PI-ang);
}
else {
/* todo what if no spin_angle */
ld hdir0 = currentmap->spin_angle(c, nd) + M_PI;
ld hdir1 = currentmap->spin_angle(c, c->mondir);
cyclefix(hdir1, hdir0);
Vb = Vb0 * spin((hdir0 + hdir1)/2 + M_PI);
2017-07-10 18:47:38 +00:00
}
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vb = Vb * lmirror();
shiftmatrix Vbb = at_smart_lof(Vb, cgi.ABODY);
2017-07-10 18:47:38 +00:00
if(part == 'l' || part == '2') {
queuepoly(Vbb, cgi.shDragonLegs, darkena(col, c->hitpoints?0:1, 0xFF));
2017-07-10 18:47:38 +00:00
}
queuepoly(Vbb, cgi.shDragonWings, darkena(col, c->hitpoints?0:1, 0xFF));
2017-07-10 18:47:38 +00:00
}
}
2017-12-05 18:43:45 +00:00
else {
2018-01-11 22:18:02 +00:00
if(c->monst == moTentacletail && c->mondir == NODIR) {
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vb = Vb * lmirror();
queuepoly(GDIM == 3 ? at_smart_lof(Vb, cgi.ABODY) : Vb, cgi.shWormSegment, darkena(col, 0, 0xFF));
2018-01-11 22:18:02 +00:00
}
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, c2, Vb, Vparam * currentmap->adj(c, nd), length);
2022-12-13 18:04:43 +00:00
Vb = Vb * lpispin();
2018-01-11 22:18:02 +00:00
}
else {
Vb = Vb0 * ddspin180(c, nd);
2018-01-11 22:18:02 +00:00
}
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vb = Vb * lmirror();
shiftmatrix Vbb = at_smart_lof(Vb, cgi.ABODY) * lpispin();
hpcshape& sh = hexsnake ? cgi.shWormTail : cgi.shSmallWormTail;
2018-01-11 22:18:02 +00:00
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
}
2021-04-15 15:55:46 +00:00
else res = res && drawMonsterType(c->monst, c, Vb, col, footphase, asciicol);
2017-07-10 18:47:38 +00:00
}
2017-07-10 18:47:38 +00:00
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;
shiftmatrix Vs = Vparam;
2017-07-22 23:33:27 +00:00
bool mirr = cw.mirrored;
if(mirrored != mirr && half_elliptic) continue;
shiftmatrix T = shiftless(Id);
2018-08-21 13:37:59 +00:00
nospins = applyAnimation(cwt.at, T, footphase, LAYER_SMALL);
if(nospins)
Vs = Vs * ddspin(c, cw.spin, 0) * iddspin(cwt.at, cwt.spin, 0) * unshift(T);
2018-08-21 13:37:59 +00:00
else
Vs = Vs * ddspin(c, cw.spin, 0);
2022-12-13 18:04:43 +00:00
if(mirr) Vs = Vs * lmirror();
2017-07-22 23:33:27 +00:00
if(inmirrorcount&1) mirr = !mirr;
col = mirrorcolor(geometry == gElliptic ? det(Vs.T) < 0 : mirr);
2019-05-11 13:21:48 +00:00
if(!mouseout() && !nospins && GDIM == 2) {
shiftpoint P2 = Vs * inverse_shift(inmirrorcount ? ocwtV : cwtV, mouseh);
queuestr(P2, 10, "x", 0xFF00);
2018-08-21 13:37:59 +00:00
}
2022-12-13 18:04:43 +00:00
if(!nospins && flipplayer) Vs = Vs * lpispin();
res = res && drawMonsterType(moMimic, c, Vs, col, footphase, asciicol);
drawPlayerEffects(Vs, Vparam, c, c->monst);
2017-07-10 18:47:38 +00:00
}
}
// illusions face randomly
else if(c->monst == moIllusion) {
multi::cpid = 0;
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vs = Vs * lmirror();
drawMonsterType(c->monst, c, Vs, col, footphase, asciicol);
drawPlayerEffects(Vs, Vparam, c, c->monst);
2017-07-10 18:47:38 +00:00
}
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
}
Vs = Vs * ddspin(c, d, 0);
2017-07-10 18:47:38 +00:00
}
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vs = Vs * lmirror();
return drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase, asciicol);
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(c->monst == moKrakenT) {
if(c->hitpoints == 0) col = 0x404040;
if(nospinb) {
2018-01-11 22:18:02 +00:00
ld length;
chainAnimation(c, c->move(c->mondir), Vb, Vparam * currentmap->adj(c, c->mondir), length);
2022-12-13 18:04:43 +00:00
Vb = Vb * lpispin();
Vb = Vb * xpush(cgi.tentacle_length - cellgfxdist(c, c->mondir));
}
else if(NONSTDVAR) {
transmatrix T = currentmap->adj(c, c->mondir);
2022-12-08 18:38:06 +00:00
Vb = Vb * T * lrspintox(tC0(iso_inverse(T))) * xpush(cgi.tentacle_length);
2017-07-10 18:47:38 +00:00
}
2018-04-03 21:39:18 +00:00
else {
Vb = Vb * ddspin180(c, c->mondir);
Vb = Vb * xpush(cgi.tentacle_length - cellgfxdist(c, c->mondir));
2018-04-03 21:39:18 +00:00
}
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vb = Vb * lmirror();
// if(ctof(c) && !masterless) Vb = Vb * xpush(hexhexdist - hcrossf);
// return (!BITRUNCATED) ? tessf * gp::scale : (c->type == 6 && (i&1)) ? hexhexdist : cgi.crossf;
res = res && drawMonsterTypeDH(m, c, Vb, col, darkhistory, footphase, asciicol);
2017-07-10 18:47:38 +00:00
}
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
2021-05-30 11:48:24 +00:00
else if(isDie(c->monst)) {
2021-05-27 11:00:20 +00:00
transmatrix U = inverse_shift(Vparam, Vs);
U = rgpushxto0(tC0(U));
die_target = Vparam;
res = res && drawMonsterTypeDH(m, c, Vparam * U, col, darkhistory, footphase, asciicol);
}
2019-11-27 00:01:20 +00:00
else if((hasFacing(c) && c->mondir != NODIR) || history::on || quotient || dont_face_pc) {
if(c->monst == moKrakenH) Vs = Vb, nospins = nospinb;
if(!nospins && c->mondir < c->type) Vs = Vs * ddspin180(c, c->mondir);
if(c->monst == moPair) Vs = Vs * xpush(-.12);
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vs = Vs * lmirror();
if(isFriendly(c)) drawPlayerEffects(Vs, Vparam, c, c->monst);
res = res && drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase, asciicol);
}
2019-03-30 12:35:03 +00:00
else {
2017-07-10 18:47:38 +00:00
// other monsters face the player
2019-08-20 17:29:03 +00:00
if(!nospins) {
shiftmatrix& where = (c->monst == moMirrorSpirit && inmirrorcount) ? ocwtV : cwtV;
2023-01-26 23:27:10 +00:00
if(cgi.emb->is_euc_in_product()) { }
2023-01-05 23:09:12 +00:00
else if(WDIM == 2 || mproduct) {
2022-12-08 18:38:06 +00:00
hyperpoint V0 = inverse_shift(Vs, where * tile_center());
2019-08-20 17:29:03 +00:00
ld z = 0;
2022-12-08 18:38:06 +00:00
if(gproduct) {
2019-08-20 17:29:03 +00:00
auto d = product_decompose(V0);
z = d.first;
V0 = d.second;
}
if(hypot_d(2, tC0(unshift(Vs))) > 1e-3) {
2022-12-08 18:38:06 +00:00
Vs = Vs * lrspintox(V0);
if(gproduct) Vs = orthogonal_move(Vs, z);
2019-08-20 17:29:03 +00:00
}
2019-02-26 20:11:50 +00:00
}
2019-08-24 12:49:49 +00:00
else if(!sl2) {
hyperpoint V0 = inverse_shift(Vs, tC0(where));
2022-12-08 18:38:06 +00:00
Vs = Vs * lrspintox(V0);
2019-02-26 20:11:50 +00:00
// cwtV * rgpushxto0(inverse(cwtV) * tC0(Vs));
}
2017-10-15 22:15:54 +00:00
if(c->monst == moHunterChanging)
2022-12-08 18:38:06 +00:00
Vs = Vs * (mhybrid ? spin180() : cspin180(WDIM-2, WDIM-1));
2017-07-10 18:47:38 +00:00
}
2022-12-13 18:04:43 +00:00
if(c->monmirror) Vs = Vs * lmirror();
2017-07-10 18:47:38 +00:00
if(c->monst == moShadow)
multi::cpid = c->hitpoints;
res = res && drawMonsterTypeDH(m, c, Vs, col, darkhistory, footphase, asciicol);
2017-07-10 18:47:38 +00:00
}
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)) {
bool mirr = multi::players > 1 ? multi::player[i].mirrored : cwt.mirrored;
if(half_elliptic && mirr != mirrored) continue;
2017-07-10 18:47:38 +00:00
if(!nospins) {
Vs = playerV;
2022-12-13 18:04:43 +00:00
if(multi::players > 1 ? multi::flipped[i] : flipplayer) Vs = Vs * lpispin();
2017-07-10 18:47:38 +00:00
}
else {
2022-12-13 18:04:43 +00:00
if(mirr) Vs = Vs * lmirror();
}
2019-09-06 06:17:38 +00:00
multi::cpid = i;
2015-08-08 13:57:52 +00:00
asciicol = getcs().uicolor >> 8;
drawPlayerEffects(Vs, Vparam, c, moPlayer);
if(inmirrorcount && !mouseout() && !nospins && GDIM == 2) {
hyperpoint h = inverse_shift(ocwtV, mouseh);
2022-12-13 18:04:43 +00:00
if(flipplayer) h = lpispin() * h;
shiftpoint P2 = Vs * h;
queuestr(P2, 10, "x", 0xFF00);
}
2017-07-10 18:47:38 +00:00
if(hide_player()) {
first_cell_to_draw = false;
if(WDIM == 2 && GDIM == 3) {
drawPlayer(moPlayer, c, Vs, col, footphase, true);
res = true;
}
}
else if(isWorm(c->monst)) {
ld depth = geom3::factor_to_lev(wormhead(c) == c ? cgi.AHEAD : cgi.ABODY);
2017-07-10 18:47:38 +00:00
footphase = 0;
int q = isize(ptds);
2021-04-15 15:55:58 +00:00
res = res | drawMonsterType(moPlayer, c, Vs, col, footphase, asciicol);
2017-07-10 18:47:38 +00:00
pushdown(c, q, Vs, -depth, true, false);
}
2021-04-15 15:55:58 +00:00
else res = res | drawMonsterType(moPlayer, c, Vs, col, footphase, asciicol);
2017-07-10 18:47:38 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
return res;
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +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
int haveaura_cached;
/** 0 = no aura, 1 = standard aura, 2 = Joukowsky aura */
EX int haveaura() {
if(!(vid.aurastr>0 && !svg::in && (auraNOGL || vid.usingGL))) return 0;
2020-12-30 13:52:58 +00:00
if(vrhr::active()) return 0;
if(sphere && mdAzimuthalEqui()) return 0;
if(among(pmodel, mdJoukowsky, mdJoukowskyInverted) && hyperbolic && pconf.model_transition < 1)
return 2;
if(pmodel == mdFisheye) return 1;
return pmodel == mdDisk && (!sphere || pconf.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;
EX void clearaura() {
haveaura_cached = haveaura();
if(!haveaura_cached) return;
2017-07-10 18:47:38 +00:00
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(shiftpoint& h) {
if(haveaura_cached == 2) {
hyperpoint ret;
applymodel(h, ret);
h.h = ret;
}
if(nonisotropic) {
h.h = lp_apply(inverse_exp(h, pfNO_DISTANCE));
}
}
EX void addauraspecial(shiftpoint h, color_t col, int dir) {
if(!haveaura_cached) return;
apply_joukowsky_aura(h);
int r = int(2*AURA + dir + atan2(h[1], h[0]) * AURA / TAU) % 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
EX void addaura(shiftpoint h, color_t col, int fd) {
if(!haveaura_cached) return;
apply_joukowsky_aura(h);
int r = gmod(atan2(h[1], h[0]) * AURA / TAU, 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
2020-07-03 12:42:33 +00:00
#if CAP_GL
2018-02-11 18:08:17 +00:00
vector<glhr::colored_vertex> auravertices;
2020-07-03 12:42:33 +00:00
#endif
2018-02-10 17:21:19 +00:00
2020-07-03 12:42:33 +00:00
EX void drawaura() {
DEBBI(DF_GRAPH, ("draw aura"));
2017-07-10 18:47:38 +00:00
if(!haveaura()) return;
if(vid.stereo_mode) return;
double rad = current_display->radius;
if(sphere && !mdAzimuthalEqui()) rad /= sqrt(pconf.alpha*pconf.alpha - 1);
if(hyperbolic && pmodel == mdFisheye) {
ld h = 1;
h /= pconf.fisheye_param;
ld nrad = h / sqrt(2 + h*h);
rad *= nrad;
}
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
ld bak[3];
2017-07-10 18:47:38 +00:00
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 / pconf.stretch;
2017-07-10 18:47:38 +00:00
2023-08-14 16:08:28 +00:00
if(!models::camera_straight) camrotate(hx, hy);
2017-07-10 18:47:38 +00:00
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
ld alpha = AURA * atan2(hx,hy) / TAU;
2017-07-10 18:47:38 +00:00
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);
ld 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++) {
ld c1 = aurac[rm][2-c] / (aurac[rm][3]+.1);
ld c2 = aurac[rm+1][2-c] / (aurac[rm+1][3]+.1);
2017-07-10 18:47:38 +00:00
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
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 = pconf.alpha <= -1 || pmodel == mdJoukowsky;
bool joukowsky = among(pmodel, mdJoukowskyInverted, mdJoukowsky) && hyperbolic && pconf.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 = (TAU * 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) {
hyperpoint v(c, s, 0, 1);
2018-10-28 01:12:04 +00:00
if(inversion)
models::ori_to_scr(v);
2018-10-28 01:12:04 +00:00
else
models::scr_to_ori(v);
ld c1 = v[0], s1 = v[1];
ld& mt = pconf.model_transition;
ld mt2 = 1 - mt;
ld m = sqrt(c1*c1 + s1*s1 / mt2 / mt2);
m *= 2;
if(inversion) rad0 /= m;
else rad0 *= m;
}
ld x = rad0 * c;
2020-11-05 17:26:37 +00:00
ld y = rad0 * s;
2023-08-14 16:08:28 +00:00
if(!models::camera_straight) {
hyperpoint p = hyperpoint(x, y, rad0, 1);
p = rot_inverse(pconf.cam()) * p;
2023-08-14 16:08:28 +00:00
x = p[0] * rad0 / p[2];
y = p[1] * rad0 / p[2];
}
cx[r][z][0] = x;
2020-11-05 17:26:37 +00:00
cx[r][z][1] = y * pconf.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();
2019-10-21 20:34:20 +00:00
current_display->next_shader_flags = GF_VARCOLOR;
dynamicval<eModel> m(pmodel, mdPixel);
current_display->set_all(0, 0);
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));
2017-07-10 18:47:38 +00:00
#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
2019-08-09 20:07:03 +00:00
EX colortable minecolors = {
2017-07-10 18:47:38 +00:00
0xFFFFFF, 0xF0, 0xF060, 0xF00000,
0x60, 0x600000, 0x00C0C0, 0x000000, 0x808080, 0xFFD500
2017-07-10 18:47:38 +00:00
};
2017-03-23 10:53:57 +00:00
2019-08-09 20:07:03 +00:00
EX 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
EX const char* minetexts[8] = {
2017-07-10 18:47:38 +00:00
"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
2019-08-09 20:07:03 +00:00
EX int countMinesAround(cell *c) {
2017-07-10 18:47:38 +00:00
int mines = 0;
for(cell *c2: adj_minefield_cells(c))
if(c2->wall == waMineMine)
2017-07-10 18:47:38 +00:00
mines++;
return mines;
}
2017-03-23 10:53:57 +00:00
2019-08-09 21:08:42 +00:00
EX transmatrix applyPatterndir(cell *c, const patterns::patterninfo& si) {
2023-01-26 23:27:10 +00:00
if(NONSTDVAR || bt::in() || cgi.emb->is_euc_in_noniso()) return Id;
transmatrix V = ddspin180(c, si.dir);
2022-12-13 18:04:43 +00:00
if(si.reflect) V = V * lmirror();
2019-11-27 00:01:20 +00:00
if(euclid) return V;
return V * iddspin180(c, 0);
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2019-10-25 10:44:41 +00:00
EX transmatrix applyDowndir(cell *c, const cellfunction& cf) {
return ddspin180(c, patterns::downdir(c, cf));
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
void draw_movement_arrows(cell *c, const transmatrix& V, int df) {
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
if(viewdists) return;
string keylist = "";
const ld keysize = .6;
color_t col = getcs().uicolor;
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 * 45._deg) * smalltangent());
cellwalker xc = cwt + md.d;
if(xc.spin != df) continue;
xc += wstep;
if(xc.at == c) {
transmatrix fixrot = sphereflip * rgpushxto0(sphereflip * tC0(V));
2017-07-10 18:47:38 +00:00
// make it more transparent
col -= (col & 0xFF) >> 1;
poly_outline = OUTLINE_DEFAULT;
char key = 0;
if(vid.axes >= 5)
key = (vid.axes == 5 ? keys_wasd : keys_vi)[d];
if(vid.axes >= 5) keylist += key;
else
queuepoly(shiftless(fixrot * spin(-d * 45._deg)), cgi.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))) {
transmatrix Centered = rgpushxto0(unshift(tC0(cwtV)));
2017-07-10 18:47:38 +00:00
int sd = md.subdir;
2022-12-08 18:38:06 +00:00
transmatrix T = iso_inverse(Centered) * rgpushxto0(Centered * tC0(V)) * lrspintox(Centered*tC0(V)) * spin(-sd * M_PI/S7) * xpush(0.2);
if(vid.axes >= 5)
queuestr(shiftless(T), keysize, s0 + key, col >> 8, 1);
else
queuepoly(shiftless(T), cgi.shArrow, col);
2017-07-10 18:47:38 +00:00
}
else if(!confusingGeometry()) break;
2017-07-10 18:47:38 +00:00
}
}
if(keylist != "") queuestr(shiftless(V), keysize, keylist, col >> 8, 1);
2017-03-23 10:53:57 +00:00
}
2019-10-25 10:44:41 +00:00
EX 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
2019-10-25 10:44:41 +00:00
EX color_t reptilecolor(cell *c) {
2018-08-30 14:08:05 +00:00
int i;
2017-07-10 18:47:38 +00:00
if(arcm::in())
2018-08-30 14:08:05 +00:00
i = c->master->rval0 & 3;
else {
i = zebra40(c);
2018-08-30 14:08:05 +00:00
2019-11-27 00:01:20 +00:00
if(!euclid) {
2018-08-30 14:08:05 +00:00
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
2019-10-25 10:44:41 +00:00
color_t reptilecolors[4] = {0xe3bb97, 0xc2d1b0, 0xebe5cb, 0xA0A0A0};
return reptilecolors[i];
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2017-07-10 18:47:38 +00:00
ld wavefun(ld x) {
return sin(x);
/* x /= TAU;
2017-07-10 18:47:38 +00:00
x -= (int) x;
if(x > .5) return (x-.5) * 2;
else return 0; */
}
2017-03-23 10:53:57 +00:00
2021-08-03 02:40:05 +00:00
// Color components in nestcolors must be less than 0x80 (for addition in drawMonster for Rock Snakes)
// and must be divisible by 4 (for brightening of raised cells in celldrawer::setcolors)
EX colortable nestcolors = { 0x7C0000, 0x007C00, 0x00007C, 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];
2019-09-05 07:01:47 +00:00
EX void init_floorcolors() {
2018-11-08 20:56:06 +00:00
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;
2021-05-23 13:53:52 +00:00
floorcolors[laCursed] = 0x481848;
2021-05-29 18:08:25 +00:00
floorcolors[laDice] = 0xC0C0FF;
2018-11-08 20:56:06 +00:00
2021-05-23 13:53:52 +00:00
floorcolors[laMirror] = floorcolors[laMirrorWall] = floorcolors[laMirrorOld] = 0x808080;
2018-11-08 20:56:06 +00:00
}
2019-10-25 10:44:41 +00:00
EX 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;
}
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
2019-08-09 19:00:52 +00:00
EX 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(arcm::in()) return geosupport_football() >= 2;
2019-02-17 17:28:20 +00:00
#endif
if(bt::in()) 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
2019-08-09 19:00:52 +00:00
EX bool is_nice_dual(cell *c) {
return c->land == laDual && has_nice_dual();
}
2019-08-09 19:00:52 +00:00
EX bool use_swapped_duals() {
2019-11-27 00:01:20 +00:00
return (euclid && !a4) || GOLDBERG;
}
#if CAP_SHAPES
EX void floorShadow(cell *c, const shiftmatrix& 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) {
queuepolyat(V * qfi.spin * cgi.shadowmulmatrix, *qfi.shape, col, PPR::WALLSHADOW);
2017-07-10 18:47:38 +00:00
}
else if(qfi.usershape >= 0)
mapeditor::drawUserShape(V * qfi.spin * cgi.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
}
2019-10-25 10:44:41 +00:00
EX bool use_warp_graphics() {
if(shmup::on) return false;
if(geosupport_football() != 2) return false;
2021-04-11 20:15:40 +00:00
if(ls::chaoticity() >= 75) return false;
return true;
}
EX void escherSidewall(cell *c, int sidepar, const shiftmatrix& 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))
draw_qfi(c, orthogonal_move_fol(V, zgrad0(cgi.slev * sl, cgi.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++)
draw_qfi(c, orthogonal_move_fol(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++)
draw_qfi(c, orthogonal_move_fol(V, zgrad0(-vid.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++)
draw_qfi(c, orthogonal_move_fol(V, zgrad0(-vid.lake_bottom, -vid.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;
draw_qfi(c, orthogonal_move_fol(V, cgi.INFDEEP), col, PPR::MINUSINF);
2017-07-10 18:47:38 +00:00
for(int z=1; z<layers; z++)
draw_qfi(c, orthogonal_move_fol(V, zgrad0(-vid.lake_bottom, -vid.lake_top, -z, 1)), col, PPR::LAKEBOTTOM+z-layers);
2017-07-10 18:47:38 +00:00
}
}
EX bool placeSidewall(cell *c, int i, int sidepar, const shiftmatrix& V, color_t col) {
2018-05-07 18:13:56 +00:00
if(!qfi.fshape || !qfi.fshape->is_plain || !cgi.validsidepar[sidepar] || qfi.usershape >= 0) if(GDIM == 2) {
2018-05-07 18:13:56 +00:00
escherSidewall(c, sidepar, V, col);
return true;
2017-10-29 22:54:26 +00:00
}
2019-05-10 00:52:04 +00:00
if(!qfi.fshape) return true;
2018-05-07 18:13:56 +00:00
if(qfi.fshape == &cgi.shBigTriangle && pseudohept(c->move(i))) return false;
if(qfi.fshape == &cgi.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;
2020-02-26 00:15:30 +00:00
else if(sidepar == SIDE_ASHA) prio = PPR::ASHALLOW;
else if(sidepar == SIDE_BSHA) prio = PPR::BSHALLOW;
2018-08-28 12:27:23 +00:00
else prio = PPR::REDWALL-2+4*(sidepar-SIDE_SLEV);
if((col & 255) < 255) prio = PPR::TRANSPARENT_WALL;
2017-07-10 18:47:38 +00:00
2023-01-26 23:27:10 +00:00
if(cgi.emb->is_in_noniso()) {
2022-12-13 18:04:43 +00:00
draw_shapevec(c, V, qfi.fshape->gpside[sidepar][i], col, prio);
return false;
}
2019-07-25 10:24:02 +00:00
dynamicval<bool> ncor(approx_nearcorner, true);
shiftmatrix V2 = V * ddspin_side(c, i);
2022-12-13 22:27:04 +00:00
if(NONSTDVAR || !standard_tiling()) {
2019-02-17 17:28:20 +00:00
#if CAP_ARCM
if(arcm::in() && !PURE)
2020-01-18 15:03:32 +00:00
i = gmod(i + arcm::parent_index_of(c->master)/DUALMUL, c->type);
2019-02-17 17:28:20 +00:00
#endif
if(currentmap->strict_tree_rules()) {
i = rulegen::get_arb_dir(c, i);
}
if(sidepar >= SIDEPARS) {
println(hlog, "ERROR: sidepar >= SIDEPARS", make_pair(sidepar, SIDEPARS));
return false;
}
if(i >= isize(qfi.fshape->gpside[sidepar])) {
println(hlog, "ERROR: i >= gpside[sidepar]", make_tuple(sidepar, i, isize(qfi.fshape->gpside[sidepar])));
return false;
}
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
queuepolyat(V2, qfi.fshape->side[sidepar][shvid(c)], col, prio);
2018-05-07 18:13:56 +00:00
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) {
2020-10-15 14:33:52 +00:00
#if CAP_COMPLEX2
return c->wall == waMineOpen || mine::marked_safe(c);
2020-10-15 14:33:52 +00:00
#else
return false;
#endif
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
2019-08-09 21:39:36 +00:00
EX color_t stdgridcolor = 0x202020FF;
2019-10-25 10:44:41 +00:00
EX int gridcolor(cell *c1, cell *c2) {
if(cmode & sm::DRAW && !mapeditor::drawing_tool) 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
}
2019-05-21 23:13:54 +00:00
if(chasmgraph(c1) != chasmgraph(c2) && c1->land != laAsteroids && c2->land != laAsteroids)
2017-07-10 18:47:38 +00:00
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
EX void pushdown(cell *c, int& q, const shiftmatrix &V, double down, bool rezoom, bool repriority) {
2019-05-08 18:40:41 +00:00
2019-05-10 01:16:40 +00:00
#if MAXMDIM >= 4
2019-05-08 18:40:41 +00:00
if(GDIM == 3) {
for(int i=q; i<isize(ptds); i++) {
auto pp = dynamic_cast<dqi_poly*> (&*ptds[q++]);
if(!pp) continue;
auto& ptd = *pp;
2023-01-05 23:09:12 +00:00
ptd.V = ptd.V * lzpush(+down);
2019-05-08 18:40:41 +00:00
}
return;
}
2019-05-10 01:16:40 +00:00
#endif
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.T));
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.T = xyzscale( V.T, xyscale*zscale, zscale)
2020-09-16 03:57:05 +00:00
* z_inverse(V.T) * unshift(ptd.V, V.shift);
2017-07-10 18:47:38 +00:00
if(!repriority) ;
else if(nlev < -vid.lake_bottom-1e-3) {
ptd.prio = PPR::BELOWBOTTOM_FALLANIM;
if(c->wall != waChasm)
ptd.color = 0; // disappear!
}
else if(nlev < -vid.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
2019-10-25 10:44:41 +00:00
EX int shallow(cell *c) {
2017-07-10 18:47:38 +00:00
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
}
2019-08-09 19:00:52 +00:00
EX bool bright;
EX int canvasdark;
2018-03-25 16:33:21 +00:00
2017-10-17 10:54:59 +00:00
// how much to darken
2019-10-25 10:44:41 +00:00
EX int getfd(cell *c) {
2018-03-25 16:33:21 +00:00
if(bright) return 0;
2020-02-26 00:40:40 +00:00
if(among(c->land, laAlchemist, laHell, laVariant, laEclectic) && WDIM == 2 && GDIM == 3) return 0;
2017-10-17 10:54:59 +00:00
switch(c->land) {
case laCanvas:
return min(2,max(0,canvasdark));
2017-10-17 10:54:59 +00:00
case laRedRock:
case laReptile:
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
2019-08-10 08:57:14 +00:00
EX bool just_gmatrix;
2019-10-25 10:44:41 +00:00
EX int colorhash(color_t i) {
return (i * 0x471211 + i*i*0x124159 + i*i*i*0x982165) & 0xFFFFFF;
}
2019-08-10 08:57:14 +00:00
EX bool isWall3(cell *c, color_t& wcol) {
if(isWall(c)) return true;
2023-03-28 20:54:46 +00:00
if(c->wall == waChasm && c->land == laMemory && (anyshiftclick || !(cgflags & qFRACTAL))) { 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;
}
EX bool isWall3(cell *c) { color_t dummy; return isWall3(c, dummy); }
2019-10-25 10:44:41 +00:00
EX 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];
}
2019-10-25 10:44:41 +00:00
EX 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 && c->land != laNone && c2->land != laNone) {
2019-03-10 17:38:55 +00:00
if(c>c2) return 0;
if(c->land == laBarrier) return darkena3(lcolor(c2), 0, 0x40);
if(c2->land == laBarrier) return darkena3(lcolor(c), 0, 0x40);
return darkena3(gradient(lcolor(c), lcolor(c2), 0, 1, 2), 0, 0x40);
}
2019-07-30 11:01:41 +00:00
if(sol && c->land == laWineyard && c2->master->distance < c->master->distance)
return 0x00800040;
if(isAlch(c) && !c->item && (c2->item || !isAlch(c2))) return darkena3(winf[c->wall].color, 0, 0x40);
if(c->wall == c2->wall) return 0;
if(isFire(c) && !isFire(c2)) return darkena3(wcol, 0, 0x30);
if(c->wall == waLadder) return darkena3(wcol, 0, 0x30);
if(c->land == laZebra && c2->land == laZebra && c2->wall == waTrapdoor) return 0x202020A0;
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 darkena3(winf[c->wall].color, 0, 0x40);
2019-03-23 21:25:35 +00:00
if(among(c->wall, waCanopy, waSolidBranch, waWeakBranch) && !among(c2->wall, waCanopy, waSolidBranch, waWeakBranch)) return 0x00C00060;
if(c->wall == waFloorA && c2->wall == waFloorB && !c->item && !c2->item) return darkena3(0xFF00FF, 0, 0x80);
if(realred(c->wall) || realred(c2->wall)) {
int l = snakelevel(c) - snakelevel(c2);
if(l > 0) return darkena3(floorcolors[laRedRock], 0, 0x30 * l);
}
if(among(c->wall, waRubble, waDeadfloor2) && !snakelevel(c2)) return darkena3(winf[c->wall].color, 0, 0x40);
if(c->wall == waMagma && c2->wall != waMagma) return darkena3(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
2019-10-25 10:44:41 +00:00
EX int get_darkval(cell *c, int d) {
2022-12-08 18:38:06 +00:00
if(mhybrid) {
2019-08-18 19:30:52 +00:00
return d >= c->type - 2 ? 4 : 0;
}
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};
const int darkval_sol[8] = {0,2,4,4,0,2,4,4};
const int darkval_arnold[12] = {0,2,0,2,4,5,0,2,0,2,4,5};
const int darkval_kite[12] = {0, 2, 0, 2, 4, 4, 6, 6, 6, 6, 6, 6};
2019-08-06 19:01:37 +00:00
const int darkval_nil[8] = {6,6,0,3,6,6,0,3};
2019-10-01 03:03:46 +00:00
const int darkval_nih[11] = {0,2,0,2,4,6,6,6,6,6,6};
2022-02-17 20:00:10 +00:00
#if MAXMDIM >= 4
2021-11-07 08:07:51 +00:00
if(among(variation, eVariation::dual_subcubes, eVariation::bch, eVariation::bch_oct, eVariation::coxeter)) {
int v = reg3::get_face_vertex_count(c, d);
2021-07-07 16:26:14 +00:00
return v-3;
}
2022-02-17 20:00:10 +00:00
#endif
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];
if(kite::in()) return darkval_kite[d];
if(asonov::in()) return darkval_arnold[d];
if(sol) return darkval_sol[d];
2019-10-01 03:03:46 +00:00
if(nih) return darkval_nih[d];
if(bt::in()) return darkval_hbt[d];
2019-03-02 23:43:31 +00:00
if(hyperbolic && S7 == 6) return darkval_e6[d];
if(hyperbolic && S7 == 12) return darkval_s12[d];
2019-08-06 19:01:37 +00:00
if(nil) return darkval_nil[d];
2019-03-02 23:43:31 +00:00
return 0;
2019-02-27 15:29:30 +00:00
}
EX ld mousedist(shiftmatrix T) {
if(GDIM == 2) return hdist(mouseh, tC0(T));
shiftpoint T1 = orthogonal_move_fol(T, cgi.FLOOR) * tile_center();
2019-05-09 15:02:50 +00:00
hyperpoint h1;
applymodel(T1, h1);
if(mouseaim_sensitivity) return sqhypot_d(2, h1) + (point_behind(T1) ? 1e10 : 0);
h1 = h1 - point2((mousex - current_display->xcenter) / current_display->radius, (mousey - current_display->ycenter) / current_display->radius);
return sqhypot_d(2, h1) + (point_behind(T1) ? 1e10 : 0);
2019-05-09 15:02:50 +00:00
}
2021-03-09 15:04:53 +00:00
vector<vector<hyperpoint>> clipping_plane_sets;
2019-08-09 19:00:52 +00:00
EX int noclipped;
2019-05-11 13:06:48 +00:00
2020-05-25 00:27:23 +00:00
EX bool frustum_culling = true;
2021-03-09 15:04:53 +00:00
EX ld threshold, xyz_threshold;
EX bool clip_checked = false;
2019-05-11 13:06:48 +00:00
void make_clipping_planes() {
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2021-03-09 15:04:53 +00:00
clip_checked = false;
2022-12-08 18:38:06 +00:00
if(!frustum_culling || PIU(sphere) || experimental || vid.stereo_mode == sODS || panini_alpha || stereo_alpha || gproduct || embedded_plane) return;
2020-11-19 17:20:06 +00:00
2021-03-09 15:04:53 +00:00
if(WDIM == 3 && pmodel == mdPerspective && !nonisotropic && !in_s2xe())
threshold = sin_auto(cgi.corner_bonus), xyz_threshold = 0, clip_checked = true;
else if(pmodel == mdGeodesic && sn::in())
threshold = .6, xyz_threshold = 3, clip_checked = true;
else if(pmodel == mdGeodesic && nil)
threshold = 2, xyz_threshold = 3, clip_checked = true;
else return;
2021-07-07 16:26:40 +00:00
2021-03-09 15:04:53 +00:00
clipping_plane_sets.clear();
auto add_clipping_plane_txy = [] (transmatrix T, const transmatrix& nlp, ld x1, ld y1, ld x2, ld y2) {
2019-05-11 13:06:48 +00:00
ld z1 = 1, z2 = 1;
hyperpoint sx = point3(y1 * z2 - y2 * z1, z1 * x2 - z2 * x1, x1 * y2 - x2 * y1);
sx /= hypot_d(3, sx);
sx[3] = 0;
2021-03-09 15:04:53 +00:00
sx = T * sx;
if(nisot::local_perspective_used) sx = ortho_inverse(nlp) * sx;
2021-03-09 15:04:53 +00:00
clipping_plane_sets.back().push_back(sx);
};
2021-03-09 15:22:22 +00:00
#if CAP_VR
2021-03-09 15:04:53 +00:00
auto add_clipping_plane_proj = [&] (transmatrix T, const transmatrix& nlp, const transmatrix& iproj, ld x1, ld y1, ld x2, ld y2) {
hyperpoint h1 = iproj * point31(x1, y1, .5);
hyperpoint h2 = iproj * point31(x2, y2, .5);
h1 /= h1[2]; h2 /= h2[2];
add_clipping_plane_txy(T, nlp, h1[0], h1[1], h2[0], h2[1]);
};
2021-03-09 15:22:22 +00:00
#endif
2021-03-09 15:04:53 +00:00
auto clipping_planes_screen = [&] (const transmatrix& T, const transmatrix& nlp) {
ld tx = current_display->tanfov;
ld ty = tx * current_display->ysize / current_display->xsize;
clipping_plane_sets.push_back({});
add_clipping_plane_txy(T, nlp, +tx, +ty, -tx, +ty);
add_clipping_plane_txy(T, nlp, -tx, +ty, -tx, -ty);
add_clipping_plane_txy(T, nlp, -tx, -ty, +tx, -ty);
add_clipping_plane_txy(T, nlp, +tx, -ty, +tx, +ty);
2019-05-11 13:06:48 +00:00
};
2021-03-09 15:04:53 +00:00
bool stdview = true;
#if CAP_VR
if(vrhr::active()) {
for(auto p: vrhr::frusta) {
if(p.screen)
clipping_planes_screen(inverse(p.pre), p.nlp);
else {
auto iproj = inverse(p.proj);
auto ipre = inverse(p.pre);
clipping_plane_sets.push_back({});
add_clipping_plane_proj(ipre, p.nlp, iproj, 1, 1, 0, 1);
add_clipping_plane_proj(ipre, p.nlp, iproj, 0, 1, 0, 0);
add_clipping_plane_proj(ipre, p.nlp, iproj, 0, 0, 1, 0);
add_clipping_plane_proj(ipre, p.nlp, iproj, 1, 0, 1, 1);
}
stdview = false;
}
}
#endif
if(stdview) clipping_planes_screen(Id, NLP);
2019-06-01 14:59:04 +00:00
#endif
2019-05-11 13:06:48 +00:00
}
2021-03-09 15:04:53 +00:00
bool clipped_by(const hyperpoint& H, const vector<hyperpoint>& v) {
for(auto& cpoint: v) if((H|cpoint) < -threshold) return true;
return false;
}
bool clipped_by(const hyperpoint& H, const vector<vector<hyperpoint>>& vv) {
for(auto& cps: vv) if(!clipped_by(H, cps)) return false;
return true;
}
2019-10-25 10:44:41 +00:00
bool celldrawer::cell_clipped() {
2021-03-09 15:04:53 +00:00
if(!clip_checked) return false;
hyperpoint H = unshift(tC0(V));
if(xyz_threshold && abs(H[0]) <= xyz_threshold && abs(H[1]) <= xyz_threshold && abs(H[2]) <= xyz_threshold) {
2019-10-25 10:44:41 +00:00
noclipped++;
2021-03-09 15:04:53 +00:00
return false;
2019-10-25 10:44:41 +00:00
}
2021-03-09 15:04:53 +00:00
if(clipped_by(H, clipping_plane_sets)) {
drawcell_in_radar();
return true;
2019-10-25 10:44:41 +00:00
}
2021-03-09 15:04:53 +00:00
noclipped++;
2019-10-25 10:44:41 +00:00
return false;
}
2020-01-02 15:51:32 +00:00
EX ld precise_width = .5;
int grid_depth = 0;
2020-01-25 23:48:56 +00:00
EX bool fat_edges = false;
EX void gridline(const shiftmatrix& V1, const hyperpoint h1, const shiftmatrix& V2, const hyperpoint h2, color_t col, int prec) {
transmatrix U2 = unshift(V2, V1.shift);
int c1 = safe_classify_ideals(h1);
int c2 = safe_classify_ideals(h2);
ld d = (c1 <= 0 || c2 <= 0) ? 99 : hdist(V1.T*h1, U2*h2);
2020-01-25 23:48:56 +00:00
2020-01-29 17:18:51 +00:00
#if MAXMDIM >= 4
2023-06-10 09:05:57 +00:00
if(GDIM == 3 && fat_edges) {
if(nonisotropic) {
auto nV1 = V1 * rgpushxto0(h1);
hyperpoint U2 = inverse_shift(nV1, V2*rgpushxto0(h2)) * C0;
auto& p = cgi.get_pipe_noniso(U2, vid.linewidth, ePipeEnd::ball);
queuepoly(nV1, p, col);
return;
}
shiftmatrix T = V1 * rgpushxto0(h1);
transmatrix S = rspintox(inverse_shift(T, V2) * h2);
2022-07-14 09:28:24 +00:00
transmatrix U = rspintoc(inverse_shift(T*S, shiftless(C0)), 2, 1);
2023-06-10 09:05:57 +00:00
auto& p = queuepoly(T * S * U, cgi.get_pipe_iso(d, vid.linewidth, ePipeEnd::ball), col);
2020-04-15 14:21:30 +00:00
p.intester = xpush0(d/2);
2020-01-25 23:48:56 +00:00
return;
}
2020-01-29 17:18:51 +00:00
#endif
2020-01-25 23:48:56 +00:00
while(d > precise_width && d < 100 && grid_depth < 10) {
if(V1.shift != V2.shift || !eqmatrix(V1.T, V2.T, 1e-6)) { gridline(V1, h1, V1, inverse_shift(V1, V2) * h2, col, prec); return; }
hyperpoint h;
if(c1 <= 0 && c2 <= 0) {
h = closest_to_zero(h1, h2);
if(safe_classify_ideals(h) <= 0) return;
h = normalize(h);
}
else if(c2 <= 0) {
dynamicval<int> dw(grid_depth, 99);
for(ld a=0; a<ideal_limit; a+=precise_width)
gridline(V1, towards_inf(h1, h2, a), V1, towards_inf(h1, h2, a+precise_width), col, prec);
return;
}
else if(c1 <= 0) {
dynamicval<int> dw(grid_depth, 99);
for(ld a=0; a<ideal_limit; a+=precise_width)
gridline(V1, towards_inf(h2, h1, a), V1, towards_inf(h2, h1, a+precise_width), col, prec);
return;
}
else h = midz(h1, h2);
grid_depth++;
2020-01-02 15:51:32 +00:00
gridline(V1, h1, V1, h, col, prec);
gridline(V1, h, V1, h2, col, prec);
grid_depth--;
2020-01-02 15:51:32 +00:00
return;
}
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
if(WDIM == 2 && GDIM == 3) {
ld eps = cgi.human_height/100;
queueline(V1*orthogonal_move(h1,cgi.FLOOR+eps), V2*orthogonal_move(h2,cgi.FLOOR+eps), col, prec);
queueline(V1*orthogonal_move(h1,cgi.WALL-eps), V2*orthogonal_move(h2,cgi.WALL-eps), col, prec);
}
else
2019-06-01 14:59:04 +00:00
#endif
2019-05-21 23:13:02 +00:00
queueline(V1*h1, V2*h2, col, prec);
2019-05-11 21:53:16 +00:00
}
EX void gridline(const shiftmatrix& V, const hyperpoint h1, const hyperpoint h2, color_t col, int prec) {
2019-05-21 23:13:02 +00:00
gridline(V, h1, V, h2, col, prec);
}
EX subcellshape& generate_subcellshape_if_needed(cell *c, int id) {
if(isize(cgi.subshapes) <= id) cgi.subshapes.resize(id+1);
auto& ss = cgi.subshapes[id];
if(!ss.faces.empty()) return ss;
2022-12-08 18:38:06 +00:00
cell *c1 = mhybrid ? hybrid::get_where(c).first : c;
2022-12-08 18:38:06 +00:00
if(mhybrid || WDIM == 2) for(int i=0; i<c1->type; i++) {
hyperpoint w;
auto f = [&] {
/* mirror image of C0 in the axis h1-h2 */
hyperpoint h1 = get_corner_position(c1, i);
hyperpoint h2 = get_corner_position(c1, i+1);
transmatrix T = gpushxto0(h1);
2022-12-08 18:38:06 +00:00
T = lspintox(T * h2) * T;
w = T * C0;
w[1] = -w[1];
w = iso_inverse(T) * w;
};
2022-12-08 18:38:06 +00:00
if(mproduct) PIU(f());
else f();
ss.walltester.push_back(w);
}
2021-09-16 19:30:26 +00:00
2022-12-08 18:38:06 +00:00
if(mhybrid || WDIM == 2) {
2021-09-16 19:30:26 +00:00
ss.walltester.push_back(C0);
ss.walltester.push_back(C0);
}
for(int i=0; i<c1->type; i++)
ss.faces.push_back({hybrid::get_corner(c1, i, 0, -1), hybrid::get_corner(c1, i, 0, +1), hybrid::get_corner(c1, i, 1, +1), hybrid::get_corner(c1, i, 1, -1)});
for(int a: {0,1}) {
vector<hyperpoint> l;
int z = a ? 1 : -1;
hyperpoint ctr = zpush0(z * cgi.plevel/2);
for(int i=0; i<c1->type; i++)
2022-12-08 18:38:06 +00:00
if(mproduct || WDIM == 2)
l.push_back(hybrid::get_corner(c1, i, 0, z));
else {
l.push_back(ctr);
l.push_back(hybrid::get_corner(c1, i+1, 0, z));
l.push_back(hybrid::get_corner(c1, i, 1, z));
l.push_back(ctr);
l.push_back(hybrid::get_corner(c1, i, 1, z));
l.push_back(hybrid::get_corner(c1, i, 0, z));
}
if(a == 0) std::reverse(l.begin()+1, l.end());
ss.faces.push_back(l);
}
ss.compute_hept();
return ss;
}
int hrmap::wall_offset(cell *c) {
int id = currentmap->full_shvid(c);
2022-12-08 18:38:06 +00:00
if(WDIM == 3 && !mhybrid && !reg3::in()) return 0;
2021-07-09 15:09:04 +00:00
if(isize(cgi.walloffsets) <= id) cgi.walloffsets.resize(id+1, {-1, nullptr});
auto &wop = cgi.walloffsets[id];
int &wo = wop.first;
if(!wop.second) wop.second = c;
if(wo == -1) {
auto& ss = generate_subcellshape_if_needed(c, id);
wo = isize(cgi.shWall3D);
if(!cgi.wallstart.empty()) cgi.wallstart.pop_back();
cgi.reserve_wall3d(wo + isize(ss.faces));
for(int i=0; i<isize(ss.faces); i++) {
cgi.make_wall(wo + i, ss.faces[i]);
cgi.walltester[wo + i] = ss.walltester[i];
}
cgi.wallstart.push_back(isize(cgi.raywall));
cgi.compute_cornerbonus();
cgi.extra_vertices();
}
return wo;
}
EX void queue_transparent_wall(const shiftmatrix& V, hpcshape& sh, color_t color) {
auto& poly = queuepolyat(V, sh, color, PPR::TRANSPARENT_WALL);
shiftpoint h = V * sh.intester;
2019-07-28 09:07:21 +00:00
if(in_perspective())
poly.subprio = int(hdist0(h) * 100000);
else {
hyperpoint h2;
applymodel(h, h2);
poly.subprio = int(h2[2] * 100000);
}
}
2019-06-01 14:59:04 +00:00
#if MAXMDIM >= 4
2019-10-25 10:44:41 +00:00
EX int ceiling_category(cell *c) {
2019-05-20 11:40:56 +00:00
switch(c->land) {
case laNone:
case laMemory:
case laMirrorWall2:
case laMirrored:
case laMirrored2:
case landtypes:
return 0;
/* starry levels */
case laIce:
case laCrossroads:
case laCrossroads2:
case laCrossroads3:
case laCrossroads4:
case laCrossroads5:
case laJungle:
case laGraveyard:
case laMotion:
case laRedRock:
case laZebra:
case laHunting:
case laEAir:
case laStorms:
case laMountain:
case laHaunted:
case laHauntedWall:
case laHauntedBorder:
case laWhirlwind:
case laBurial:
case laHalloween:
case laReptile:
case laVolcano:
case laBlizzard:
case laDual:
case laWestWall:
case laAsteroids:
return 1;
2019-06-01 17:26:02 +00:00
case laPower:
2019-05-20 11:40:56 +00:00
case laWineyard:
case laDesert:
case laAlchemist:
case laDryForest:
case laCaribbean:
case laMinefield:
case laOcean:
case laWhirlpool:
case laLivefjord:
case laEWater:
case laOceanWall:
case laWildWest:
case laOvergrown:
case laClearing:
case laRose:
case laWarpCoast:
case laWarpSea:
case laEndorian:
case laTortoise:
case laPrairie:
2019-06-01 15:01:04 +00:00
case laDragon:
2019-05-20 11:40:56 +00:00
case laSnakeNest:
case laDocks:
case laKraken:
case laBrownian:
2019-06-01 15:01:04 +00:00
case laHell:
2019-06-13 15:25:11 +00:00
case laVariant:
2020-05-15 23:06:47 +00:00
case laFrog:
case laWet:
2019-05-20 11:40:56 +00:00
return 2;
case laBarrier:
case laCaves:
case laMirror:
case laMirrorOld:
case laCocytus:
case laEmerald:
case laDeadCaves:
case laHive:
case laCamelot:
case laIvoryTower:
case laEFire:
case laEEarth:
case laElementalWall:
case laTrollheim:
case laDungeon:
case laBull:
case laCA:
case laMirrorWall:
case laTerracotta:
case laMercuryRiver:
case laMagnetic:
case laSwitch:
2020-05-15 23:06:47 +00:00
case laEclectic:
2019-05-20 11:40:56 +00:00
return 3;
case laCanvas:
if(canvas_default_wall == waInvisibleFloor) return 0;
return 3;
2019-05-20 11:40:56 +00:00
2019-05-24 23:36:49 +00:00
case laPalace:
2019-05-25 01:37:27 +00:00
case laPrincessQuest:
2019-05-20 11:40:56 +00:00
default:
return 4;
case laRuins:
return 6;
case laTemple:
case laRlyeh:
return 7;
2019-05-20 11:40:56 +00:00
}
}
2019-11-03 13:19:11 +00:00
#endif
EX void set_detail_level(const shiftmatrix& V) {
2019-10-25 10:44:41 +00:00
ld dist0 = hdist0(tC0(V)) - 1e-6;
if(vid.use_smart_range) detaillevel = 2;
else if(dist0 < vid.highdetail) detaillevel = 2;
else if(dist0 < vid.middetail) detaillevel = 1;
else detaillevel = 0;
2019-08-09 23:56:00 +00:00
2019-10-25 10:44:41 +00:00
if((cmode & sm::NUMBER) && (dialog::editingDetail())) {
color_t col =
dist0 < vid.highdetail ? 0xFF80FF80 :
dist0 >= vid.middetail ? 0xFFFF8080 :
0XFFFFFF80;
queuepoly(V, cgi.shHeptaMarker, darkena(col & 0xFFFFFF, 0, 0xFF));
2019-06-01 15:01:04 +00:00
}
}
2019-10-25 10:44:41 +00:00
struct flashdata {
int t;
int size;
cell *where;
double angle;
double angle2;
int spd; // 0 for flashes, >0 for particles
color_t color;
2019-12-25 21:01:57 +00:00
string text;
2019-10-25 10:44:41 +00:00
flashdata(int _t, int _s, cell *_w, color_t col, int sped) {
t=_t; size=_s; where=_w; color = col;
angle = rand() % 1000; spd = sped;
if(GDIM == 3) angle2 = acos((rand() % 1000 - 499.5) / 500);
2019-06-01 15:01:04 +00:00
}
2019-10-25 10:44:41 +00:00
};
2019-06-01 15:01:04 +00:00
2019-10-25 10:44:41 +00:00
vector<flashdata> flashes;
2019-05-28 23:06:01 +00:00
auto ahgf = addHook(hooks_removecells, 1, [] () {
eliminate_if(flashes, [] (flashdata& f) { return is_cell_removed(f.where); });
});
2019-12-25 21:01:57 +00:00
EX void drawBubble(cell *c, color_t col, string s, ld size) {
2020-02-29 16:58:59 +00:00
LATE( drawBubble(c, col, s, size); )
2019-12-26 08:40:28 +00:00
auto fd = flashdata(ticks, 1000, c, col, 0);
2019-12-25 21:01:57 +00:00
fd.text = s;
fd.angle = size;
flashes.push_back(fd);
}
2019-10-25 10:44:41 +00:00
EX void drawFlash(cell *c) {
flashes.push_back(flashdata(ticks, 1000, c, iinf[itOrbFlash].color, 0));
}
2019-10-25 10:44:41 +00:00
EX void drawBigFlash(cell *c) {
flashes.push_back(flashdata(ticks, 2000, c, 0xC0FF00, 0));
2019-05-20 11:40:56 +00:00
}
2019-10-25 10:44:41 +00:00
EX void drawParticleSpeed(cell *c, color_t col, int speed) {
2020-02-29 16:58:59 +00:00
LATE( drawParticleSpeed(c, col, speed); )
2019-10-25 10:44:41 +00:00
if(vid.particles && !confusingGeometry())
flashes.push_back(flashdata(ticks, rand() % 16, c, col, speed));
}
EX void drawParticle(cell *c, color_t col, int maxspeed IS(100)) {
drawParticleSpeed(c, col, 1 + rand() % maxspeed);
}
2020-02-29 19:20:08 +00:00
EX void drawDirectionalParticle(cell *c, int dir, color_t col, int maxspeed IS(100)) {
LATE( drawDirectionalParticle(c, dir, col, maxspeed); )
if(vid.particles && !confusingGeometry()) {
int speed = 1 + rand() % maxspeed;
auto fd = flashdata(ticks, rand() % 16, c, col, speed);
fd.angle = -atan2(tC0(currentmap->adj(c, dir)));
fd.angle += TAU * (rand() % 100 - rand() % 100) / 100 / c->type;
2020-02-29 19:20:08 +00:00
flashes.push_back(fd);
}
}
2019-10-25 10:44:41 +00:00
EX void drawParticles(cell *c, color_t col, int qty, int maxspeed IS(100)) {
if(vid.particles)
while(qty--) drawParticle(c,col, maxspeed);
}
EX void drawFireParticles(cell *c, int qty, int maxspeed IS(100)) {
if(vid.particles)
for(int i=0; i<qty; i++)
drawParticle(c, firegradient(i / (qty-1.)), maxspeed);
}
EX void fallingFloorAnimation(cell *c, eWall w IS(waNone), eMonster m IS(moNone)) {
if(!wmspatial) return;
2020-02-29 16:58:59 +00:00
LATE( fallingFloorAnimation(c, w, m); )
2019-10-25 10:44:41 +00:00
fallanim& fa = fallanims[c];
fa.t_floor = ticks;
fa.walltype = w; fa.m = m;
// drawParticles(c, darkenedby(linf[c->land].color, 1), 4, 50);
}
EX void fallingMonsterAnimation(cell *c, eMonster m, int id IS(multi::cpid)) {
if(!mmspatial) return;
2020-02-29 16:58:59 +00:00
LATE( fallingMonsterAnimation(c, m, id); )
2019-10-25 10:44:41 +00:00
fallanim& fa = fallanims[c];
fa.t_mon = ticks;
fa.m = m;
fa.pid = id;
// drawParticles(c, darkenedby(linf[c->land].color, 1), 4, 50);
2019-05-16 15:41:37 +00:00
}
2017-03-23 10:53:57 +00:00
2019-10-25 10:44:41 +00:00
void celldrawer::draw_fallanims() {
2019-11-03 13:19:56 +00:00
poly_outline = OUTLINE_NONE;
2019-10-25 10:44:41 +00:00
if(fallanims.count(c)) {
int q = isize(ptds);
int maxtime = euclid || sphere ? 20000 : 1500;
fallanim& fa = fallanims[c];
bool erase = true;
if(fa.t_floor) {
int t = (ticks - fa.t_floor);
if(t <= maxtime) {
erase = false;
if(GDIM == 3)
draw_shapevec(c, V, qfi.fshape->levels[0], darkena(fcol, fd, 0xFF), PPR::WALL);
else if(fa.walltype == waNone) {
draw_qfi(c, V, darkena(fcol, fd, 0xFF), PPR::FLOOR);
2017-07-10 18:47:38 +00:00
}
2019-10-25 10:44:41 +00:00
else {
celldrawer ddalt;
eWall w = c->wall; int p = c->wparam;
c->wall = fa.walltype; c->wparam = fa.m;
ddalt.c = c;
2019-10-25 10:44:41 +00:00
ddalt.setcolors();
int starcol = c->wall == waVinePlant ? 0x60C000 : ddalt.wcol;
c->wall = w; c->wparam = p;
draw_qfi(c, orthogonal_move_fol(V, cgi.WALL), darkena(starcol, fd, 0xFF), PPR::WALL3);
queuepolyat(orthogonal_move_fol(V, cgi.WALL), cgi.shWall[ct6], darkena(ddalt.wcol, 0, 0xFF), PPR::WALL3A);
2019-10-25 10:44:41 +00:00
forCellIdEx(c2, i, c)
if(placeSidewall(c, i, SIDE_WALL, V, darkena(ddalt.wcol, 1, 0xFF))) break;
2017-07-10 18:47:38 +00:00
}
2019-10-25 10:44:41 +00:00
pushdown(c, q, V, t*t / 1000000. + t / 1000., true, true);
2017-07-10 18:47:38 +00:00
}
}
2019-10-25 10:44:41 +00:00
if(fa.t_mon) {
dynamicval<int> d(multi::cpid, fa.pid);
int t = (ticks - fa.t_mon);
if(t <= maxtime) {
erase = false;
c->stuntime = 0;
shiftmatrix V2 = V;
2019-10-25 10:44:41 +00:00
double footphase = t / 200.0;
applyAnimation(c, V2, footphase, LAYER_SMALL);
drawMonsterType(fa.m, c, V2, minf[fa.m].color, footphase, NOCOLOR);
pushdown(c, q, V2, t*t / 1000000. + t / 1000., true, true);
2017-07-10 18:47:38 +00:00
}
}
2019-10-25 10:44:41 +00:00
if(erase) fallanims.erase(c);
}
}
2017-07-10 18:47:38 +00:00
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
EX void queuecircleat1(cell *c, const shiftmatrix& V, double rad, color_t col) {
2019-05-08 16:33:08 +00:00
if(WDIM == 3) {
2019-02-26 10:57:50 +00:00
dynamicval<color_t> p(poly_outline, col);
int ofs = currentmap->wall_offset(c);
for(int i=0; i<c->type; i++) {
queuepolyat(V, cgi.shWireframe3D[ofs + i], 0, PPR::SUPERLINE);
}
2019-02-26 10:57:50 +00:00
return;
}
if(spatial_graphics || GDIM == 3) {
vector<hyperpoint> corners(c->type+1);
for(int i=0; i<c->type; i++) corners[i] = get_corner_position(c, i, 3 / rad);
corners[c->type] = corners[0];
for(int i=0; i<c->type; i++) {
queueline(V * orthogonal_move_fol(corners[i], cgi.FLOOR), V * orthogonal_move_fol(corners[i+1], cgi.FLOOR), col, 2, PPR::SUPERLINE);
queueline(V * orthogonal_move_fol(corners[i], cgi.WALL), V * orthogonal_move_fol(corners[i+1], cgi.WALL), col, 2, PPR::SUPERLINE);
queueline(V * orthogonal_move_fol(corners[i], cgi.FLOOR), V * orthogonal_move_fol(corners[i], cgi.WALL), col, 2, PPR::SUPERLINE);
}
return;
}
#if CAP_SHAPES
if(vid.stereo_mode || sphere) {
dynamicval<color_t> p(poly_outline, col);
queuepolyat(V * spintick(100), cgi.shGem[1], 0, PPR::LINE);
return;
}
2019-02-17 17:41:40 +00:00
#endif
queuecircle(V, rad, col);
2017-07-10 18:47:38 +00:00
if(!wmspatial) return;
if(highwall(c))
queuecircle(orthogonal_move_fol(V, cgi.WALL), rad, col);
2017-07-10 18:47:38 +00:00
int sl;
if((sl = snakelevel(c))) {
queuecircle(orthogonal_move_fol(V, cgi.SLEV[sl]), rad, col);
2016-01-02 10:09:13 +00:00
}
2017-07-10 18:47:38 +00:00
if(chasmgraph(c))
queuecircle(orthogonal_move_fol(V, cgi.LAKE), rad, col);
2016-08-26 09:58:03 +00:00
}
EX void queuecircleat(cell *c, double rad, color_t col) {
if(!c) return;
for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, c))
queuecircleat1(c, V, rad, col);
}
#endif
2016-01-02 10:09:13 +00:00
#if ISMOBILE
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-08-09 20:07:03 +00:00
EX cell *forwardcell() {
2020-11-19 17:20:06 +00:00
#if CAP_VR
if(vrhr::active()) {
2020-11-19 17:20:06 +00:00
return vrhr::forward_cell;
}
#endif
2019-02-26 11:28:54 +00:00
movedir md = vectodir(move_destination_vec(6));
cellwalker xc = cwt + md.d + wstep;
return xc.at;
}
EX bool draw_centerover = true;
EX bool should_draw_mouse_cursor() {
if(!mousing || inHighQual) return false;
if(outofmap(mouseh.h)) return false;
if(rug::rugged && !rug::renderonce) return true;
return false;
}
2019-08-09 20:07:03 +00:00
EX void drawMarkers() {
2017-03-23 10:53:57 +00:00
if(!(cmode & sm::NORMAL)) return;
if(should_draw_mouse_cursor()) {
2021-03-06 10:46:13 +00:00
for(int i: player_indices()) {
queueline(ggmatrix(playerpos(i)) * (WDIM == 2 && GDIM == 3 ? zpush0(cgi.WALL) : C0), mouseh, 0xFF00FF, grid_prec() + 1);
}
}
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
if(haveMount())
for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, dragon::target)) {
queuestr(V, 1, "X",
gradient(0, iinf[itOrbDomination].color, -1, sintick(dragon::whichturn == turncount ? 75 : 150), 1));
}
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])
queuestr(xc, yc, sc, 4*vid.fsize, s0+('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];
}
if(keycell) {
for(; i<YDIST; i++) {
cell *c = yi[yii].path[i];
if(inscreenrange(c))
keycell = c;
}
shiftpoint H = tC0(ggmatrix(keycell));
2019-02-17 17:41:40 +00:00
#if CAP_QUEUE
queuestr(H, 2*vid.fsize, "X", 0x10101 * int(128 + 100 * sintick(150)));
int cd = celldistance(yi[yii].key(), cwt.at);
if(cd == DISTANCE_UNKNOWN) for(int i2 = 0; i2<YDIST; i2++) {
int cd2 = celldistance(cwt.at, yi[yii].path[i2]);
if(cd2 != DISTANCE_UNKNOWN) {
cd = cd2 + (YDIST-1-i2);
println(hlog, "i2 = ", i2, " cd = ", celldistance(cwt.at, keycell));
}
}
queuestr(H, vid.fsize, its(cd), 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-05-08 16:33:08 +00:00
if(lmouseover && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON && WDIM == 2) {
2020-12-29 03:42:09 +00:00
cell *at = lmouseover;
#if CAP_VR
if(vrhr::active() && vrhr::forward_cell)
2020-12-29 03:42:09 +00:00
at = vrhr::forward_cell;
#endif
queuecircleat(at, .8, darkena(lmouseover->cpdist > 1 ? 0x00FFFF : 0xFF0000, 0, 0xFF));
2017-07-10 18:47:38 +00:00
}
2019-12-26 09:29:41 +00:00
if(global_pushto && vid.drawmousecircle && ok && DEFAULTCONTROL && MOBON && WDIM == 2) {
queuecircleat(global_pushto, .6, darkena(0xFFD500, 0, 0xFF));
2017-07-10 18:47:38 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
2019-02-17 17:41:40 +00:00
#if CAP_SDLJOY && CAP_QUEUE
2019-05-08 16:33:08 +00:00
if(joydir.d >= 0 && WDIM == 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 && !playermoved && m && !anims::any_animation() && WDIM == 2 && draw_centerover)
queuecircleat(centerover, .70 - .06 * sintick(200),
2018-09-10 15:58:36 +00:00
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
if((vid.axes >= 4 || (vid.axes == 1 && !mousing)) && !shmup::on && GDIM == 2) {
2017-07-10 18:47:38 +00:00
if(multi::players == 1) {
forCellIdAll(c2, d, cwt.at) if(gmatrix.count(cwt.at)) draw_movement_arrows(c2, unshift(gmatrix[cwt.at]) * currentmap->adj(cwt.at, d), d);
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) if(gmatrix.count(cwt.at)) {
2017-07-10 18:47:38 +00:00
multi::cpid = p;
dynamicval<shiftmatrix> ttm(cwtV, multi::whereis[p]);
2017-07-10 18:47:38 +00:00
dynamicval<cellwalker> tcw(cwt, multi::player[p]);
draw_movement_arrows(c2, unshift(gmatrix[cwt.at]) * currentmap->adj(cwt.at, d), d);
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(GDIM == 3 && !inHighQual && !shmup::on && vid.axes3 && playermoved) {
2019-02-26 13:56:07 +00:00
cell *c = forwardcell();
2020-11-19 17:20:06 +00:00
if(c) queuecircleat(c, .8, getcs().uicolor);
2019-02-26 10:57:50 +00:00
}
2019-02-17 17:41:40 +00:00
#endif
2022-12-08 18:38:06 +00:00
if(mhybrid && !shmup::on) {
2019-08-19 08:55:02 +00:00
using namespace sword;
int& ang = sword::dir[multi::cpid].angle;
ang %= sword_angles;
int adj = 1 - ((sword_angles/cwt.at->type)&1);
if(items[itOrbSword])
for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, cwt.at))
queuestr(V * spin(M_PI+(-adj-2*ang)*M_PI/sword_angles) * xpush0(cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword].color);
if(items[itOrbSword2])
for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, cwt.at))
queuestr(V * spin((-adj-2*ang)*M_PI/sword_angles) * xpush0(-cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword2].color);
2019-08-19 08:55:02 +00:00
}
if(SWORDDIM == 3 && !shmup::on) {
if(items[itOrbSword])
for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, cwt.at))
queuestr(V * sword::dir[multi::cpid].T * xpush0(cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword].color);
if(items[itOrbSword2])
for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, cwt.at))
queuestr(V * sword::dir[multi::cpid].T * xpush0(-cgi.sword_size), vid.fsize*2, "+", iinf[itOrbSword2].color);
}
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) {
2019-09-06 06:17:38 +00:00
multi::cpid = 0;
2017-07-10 18:47:38 +00:00
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);
queuestr(mousex, mousey, 0, vid.fsize, s0+minf[monsterToSummon].glyph, minf[monsterToSummon].color);
2017-07-10 18:47:38 +00:00
queuecircleat(mouseover, 0.6, darkena(minf[monsterToSummon].color, 0, 0xFF));
}
else if(orbToTarget) {
queuestr(mousex, mousey, 0, vid.fsize, "@", iinf[orbToTarget].color);
2017-07-10 18:47:38 +00:00
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;
2019-11-22 17:48:51 +00:00
auto mib = blowoff_destination(c1, di);
if(!mib.proper()) break;
auto& c2 = mib.t;
shiftmatrix T1 = ggmatrix(c1);
shiftmatrix T2 = ggmatrix(c2);
2022-12-08 18:38:06 +00:00
shiftmatrix T = T1 * lrspintox(inverse_shift(T1,T2*C0)) * xpush(hdist(T1*C0, T2*C0) * fractick(50, 0));
color_t aircol = (orbToTarget == itOrbAir ? 0x8080FF40 : 0x8080FF20);
queuepoly(T, cgi.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++) {
bool kill = true;
2017-07-10 18:47:38 +00:00
flashdata& f = flashes[k];
2019-12-08 18:33:17 +00:00
bool copies = false;
for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, f.where)) {
2019-12-08 18:33:17 +00:00
copies = true;
draw_flash(f, V, kill);
}
forCellIdEx(c2, id, f.where) {
if(!copies) {
for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, c2)) {
draw_flash(f, V * currentmap->iadj(f.where, id), kill);
copies = true;
}
}
2017-07-10 18:47:38 +00:00
}
2020-04-06 07:48:34 +00:00
if(f.t > ticks - 800 && !copies) {
kill = false;
}
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
2019-12-08 18:33:17 +00:00
#if CAP_QUEUE
2020-01-02 15:54:28 +00:00
always_false static_bubbles;
EX void draw_flash(struct flashdata& f, const shiftmatrix& V, bool& kill) {
2019-12-08 18:33:17 +00:00
int tim = ticks - f.t;
2020-01-02 15:54:28 +00:00
2019-12-08 18:33:17 +00:00
if(tim <= f.size && !f.spd) kill = false;
2019-12-25 21:01:57 +00:00
if(f.text != "") {
2020-01-02 15:54:28 +00:00
if(static_bubbles) {
tim = 0; kill = false;
}
2019-12-26 22:32:27 +00:00
color_t col = f.color;
dynamicval<color_t> p(poly_outline, poly_outline);
int r = 2;
apply_neon(col, r);
2019-12-25 22:10:36 +00:00
if(GDIM == 3 || sphere)
2019-12-26 22:32:27 +00:00
queuestr(V, (1 - tim * 1. / f.size) * f.angle, f.text, col, r);
2019-12-25 22:10:36 +00:00
else if(!kill) {
shiftpoint h = tC0(V);
2019-12-25 22:10:36 +00:00
if(hdist0(h) > .1) {
transmatrix V2 = rspintox(h.h) * xpush(hdist0(h.h) * (1 / (1 - tim * 1. / f.size)));
queuestr(shiftless(V2, h.shift), f.angle, f.text, col, r);
2019-12-25 22:10:36 +00:00
}
}
2020-01-02 15:54:28 +00:00
if(static_bubbles) {
ld rad[25];
for(int a=0; a<24; a++) rad[a] = (0.5 + randd() * .3 + 0.5 * (a&1)) / (2.8 + celldistance(f.where, cwt.at) * .2);
rad[24] = rad[0];
for(int a=0; a<24; a++) curvepoint(xspinpush0(TAU * a / 24, rad[a]));
queuecurve(V, 0xFF, 0xFF0000FF, PPR::SUPERLINE);
2020-01-02 15:54:28 +00:00
}
2019-12-25 21:01:57 +00:00
}
else if(f.spd) {
2019-12-08 18:33:17 +00:00
#if CAP_SHAPES
if(tim <= 300) kill = false;
int partcol = darkena(f.color, 0, GDIM == 3 ? 255 : max(255 - tim*255/300, 0));
poly_outline = OUTLINE_DEFAULT;
ld t = f.spd * tim * cgi.scalefactor / 50000.;
shiftmatrix T =
2019-12-08 18:33:17 +00:00
GDIM == 2 ? V * spin(f.angle) * xpush(t) :
V * cspin(0, 1, f.angle) * cspin(0, 2, f.angle2) * cpush(2, t);
queuepoly(T, cgi.shParticle[f.size], partcol);
#endif
}
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 *= cgi.hexf;
int flashcol = f.color;
if(u > 500) flashcol = gradient(flashcol, 0, 500, u, 1100);
flashcol = darkena(flashcol, 0, 0xFF);
#if MAXMDIM >= 4
if(GDIM == 3)
2023-02-04 00:58:13 +00:00
queueball(V * lzpush(cgi.GROIN1), rad, flashcol, itDiamond);
2019-12-08 18:33:17 +00:00
else
#endif
{
PRING(a) curvepoint(xspinpush0(a * cgi.S_step, rad));
queuecurve(V, flashcol, 0x8080808, PPR::LINE);
2019-12-08 18:33:17 +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 *= cgi.hexf;
int flashcol = f.color;
if(u > 1000) flashcol = gradient(flashcol, 0, 1000, u, 2200);
flashcol = darkena(flashcol, 0, 0xFF);
#if MAXMDIM >= 4
if(GDIM == 3)
2023-02-04 00:58:13 +00:00
queueball(V * lzpush(cgi.GROIN1), rad, flashcol, itRuby);
2019-12-08 18:33:17 +00:00
else
#endif
{
PRING(a) curvepoint(xspinpush0(a * cgi.S_step, rad));
queuecurve(V, flashcol, 0x8080808, PPR::LINE);
2019-12-08 18:33:17 +00:00
}
}
}
}
#endif
2019-08-09 20:07:03 +00:00
EX 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 || arcm::in() || arb::in()) return true;
2019-05-08 16:33:08 +00:00
if(WDIM == 3) return true;
if(!canmove) return true;
return false;
}
2019-08-09 20:07:03 +00:00
EX bool allowChangeRange() {
2018-04-11 13:49:49 +00:00
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;
if(arcm::in() || arb::in()) return true;
2019-05-08 16:33:08 +00:00
if(WDIM == 3) return true;
2018-04-11 13:49:49 +00:00
return false;
}
2019-08-09 20:07:03 +00:00
EX purehookset hooks_drawmap;
2019-08-09 20:07:03 +00:00
EX transmatrix actual_view_transform;
2019-04-29 01:34:21 +00:00
2019-08-20 16:02:03 +00:00
EX ld wall_radar(cell *c, transmatrix T, transmatrix LPe, ld max) {
if(!in_perspective() || !vid.use_wall_radar) return max;
2019-08-26 07:06:39 +00:00
transmatrix ori;
2022-12-08 18:38:06 +00:00
if(gproduct) ori = ortho_inverse(LPe);
2019-05-11 21:52:18 +00:00
ld step = max / 20;
2019-04-29 01:34:21 +00:00
ld fixed_yshift = 0;
for(int i=0; i<20; i++) {
T = shift_object(T, ori, ztangent(-step), shift_method(smaWallRadar));
2019-11-14 18:33:55 +00:00
virtualRebase(c, T);
2019-04-29 01:34:21 +00:00
color_t col;
if(isWall3(c, col) || (WDIM == 2 && GDIM == 3 && tC0(T)[2] > cgi.FLOOR)) {
T = shift_object(T, ori, ztangent(step), shift_method(smaWallRadar));
2019-04-29 01:34:21 +00:00
step /= 2; i = 17;
if(step < 1e-3) break;
}
else fixed_yshift += step;
}
return fixed_yshift;
}
/** if this is set to ON, just transform non-isotropic spaces according to View, and apply NLP to view */
EX bool nonisotropic_weird_transforms;
EX void decide_lpu() {
nisot::local_perspective_used = gproduct;
}
EX void make_actual_view() {
decide_lpu();
if(!nisot::local_perspective_used) NLP = Id;
2017-12-16 08:03:50 +00:00
sphereflip = Id;
2022-12-04 13:48:12 +00:00
sphere_flipped = flip_sphere();
if(sphere_flipped) sphereflip[LDIM][LDIM] = -1;
2019-05-11 21:52:18 +00:00
actual_view_transform = sphereflip;
if(vid.yshift && WDIM == 2) actual_view_transform = ypush(vid.yshift) * actual_view_transform;
2019-05-10 01:16:40 +00:00
#if MAXMDIM >= 4
2019-05-11 21:52:18 +00:00
if(GDIM == 3) {
ld max = WDIM == 2 ? vid.camera : vid.yshift;
2019-08-20 16:02:03 +00:00
if(max) {
2020-09-16 03:57:05 +00:00
transmatrix Start = view_inverse(actual_view_transform * View);
ld d = wall_radar(centerover, Start, NLP, max);
2020-09-16 03:57:05 +00:00
actual_view_transform = get_shift_view_of(ztangent(d), actual_view_transform * View) * view_inverse(View);
2019-08-20 16:02:03 +00:00
}
hyperpoint h = tC0(view_inverse(actual_view_transform * View));
2023-01-07 10:45:40 +00:00
2023-01-26 23:27:10 +00:00
camera_level = cgi.emb->get_logical_z(h);
camera_sign = cgi.FLOOR > cgi.WALL;
2019-05-11 21:52:18 +00:00
}
if((nonisotropic || (hyperbolic && bt::in() && !nisot::geodesic_movement)) && !nonisotropic_weird_transforms) {
transmatrix T = actual_view_transform * View;
2020-09-16 03:57:05 +00:00
transmatrix T2 = eupush( tC0(view_inverse(T)) );
NLP = T * T2;
2020-09-16 03:57:05 +00:00
actual_view_transform = ortho_inverse(NLP) * actual_view_transform;
nisot::local_perspective_used = true;
}
2019-05-10 01:16:40 +00:00
#endif
cgi.emb->set_radar_transform();
Viewbase = View;
}
EX shiftmatrix cview(ld base_shift IS(0)) {
return shiftless(actual_view_transform * View, base_shift);
2017-12-16 08:03:50 +00:00
}
EX int point_direction;
2022-10-27 08:33:47 +00:00
EX int through_wall(cell *c, hyperpoint at) {
2022-10-27 07:45:03 +00:00
ld dist = hdist0(at);
int nei = -1;
2022-10-27 08:33:47 +00:00
for(int i=0; i<c->type; i++) {
ld dist1 = hdist0(currentmap->ray_iadj(c, i) * at);
2022-10-27 07:45:03 +00:00
if(dist1 < dist) nei = i, dist = dist1;
}
return nei;
}
2019-08-09 20:07:03 +00:00
EX void precise_mouseover() {
2022-10-27 07:45:03 +00:00
if(WDIM == 3 && (cmode & (sm::EDIT_INSIDE_WALLS | sm::EDIT_BEFORE_WALLS))) {
transmatrix T = view_inverse(View);
transmatrix ori = Id;
2022-12-08 18:38:06 +00:00
if(gproduct) ori = ortho_inverse(NLP);
2022-10-27 07:45:03 +00:00
ld step = 0.2;
cell *c = centerover;
for(int i=0; i<100; i++) {
apply_shift_object(T, ori, ztangent(step));
2022-10-27 08:33:47 +00:00
int pd = through_wall(c, T * C0);
2022-10-27 07:45:03 +00:00
if(pd != -1) {
color_t col;
cell *c1 = c->cmove(pd);
if(isWall3(c1, col)) {
mouseover = c;
mouseover2 = c1;
point_direction = pd;
if(cmode & sm::EDIT_INSIDE_WALLS) {
swap(mouseover, mouseover2);
2022-10-27 08:12:14 +00:00
}
else {
2022-10-27 07:45:03 +00:00
point_direction =c->c.spin(pd);
}
return;
}
else {
T = currentmap->iadj(c, pd) * T;
c = c1;
}
}
}
}
2019-05-09 15:02:50 +00:00
if(WDIM == 3) {
mouseover2 = mouseover = centerover;
2019-02-26 13:56:07 +00:00
ld best = HUGE_VAL;
shiftpoint h = shiftless(direct_exp(lp_iapply(ztangent(0.01))));
point_direction = -1;
2019-09-09 17:22:43 +00:00
shiftmatrix cov = ggmatrix(mouseover2);
forCellIdEx(c1, i, mouseover2) {
shiftpoint h1 = tC0(cov * currentmap->adj(mouseover2, i));
ld dist = geo_dist(h, h1) - geo_dist(shiftless(C0), h1);
if(dist < best) mouseover = c1, best = dist, point_direction = i;
2019-02-26 13:56:07 +00:00
}
return;
}
2018-08-19 20:53:51 +00:00
if(!mouseover) return;
2019-05-09 15:02:50 +00:00
if(GDIM == 3) return;
2018-08-19 20:53:51 +00:00
cell *omouseover = mouseover;
for(int loop = 0; loop < 10; loop++) {
bool found = false;
if(!gmatrix.count(mouseover)) return;
hyperpoint r_mouseh = inverse_shift(gmatrix[mouseover], mouseh);
2018-08-19 20:53:51 +00:00
for(int i=0; i<mouseover->type; i++) {
2020-09-21 10:00:25 +00:00
hyperpoint h1 = get_corner_position(mouseover, gmod(i-1, mouseover->type));
2018-08-19 20:53:51 +00:00
hyperpoint h2 = get_corner_position(mouseover, i);
2020-09-21 10:00:25 +00:00
if(det3(build_matrix(h1, h2, C0, C0)) * det3(build_matrix(h1, h2, r_mouseh, C0)) < 0) {
2018-08-19 20:53:51 +00:00
mouseover2 = mouseover;
mouseover = mouseover->move(i);
found = true;
break;
}
}
if(!found) return;
}
// probably some error... just return the original
mouseover = omouseover;
}
EX transmatrix Viewbase;
2019-10-23 21:10:50 +00:00
EX bool no_wall_rendering;
2019-10-21 22:37:57 +00:00
2022-03-27 12:32:29 +00:00
EX bool set_multi = false;
EX hyperpoint multi_point;
EX void center_multiplayer_map(const vector<hyperpoint>& hs) {
hyperpoint h = Hypc;
for(auto h1: hs) h += h1;
h /= isize(hs);
2023-01-26 23:27:10 +00:00
h = cgi.emb->normalize_flat(h);
2022-03-27 12:32:29 +00:00
cwtV = shiftless(rgpushxto0(h));
if(isize(hs) == 2) {
set_multi = true;
multi_point = hs[1];
}
}
2019-08-09 19:00:52 +00:00
EX void drawthemap() {
2019-05-27 05:19:04 +00:00
check_cgi();
cgi.require_shapes();
2019-05-12 23:57:40 +00:00
DEBBI(DF_GRAPH, ("draw the map"));
last_firelimit = firelimit;
firelimit = 0;
2017-07-04 13:38:33 +00:00
make_clipping_planes();
current_display->radarpoints.clear();
current_display->radarlines.clear();
callhooks(hooks_drawmap);
2017-07-10 18:47:38 +00:00
frameid++;
2018-11-01 17:59:25 +00:00
cells_drawn = 0;
cells_generated = 0;
2019-05-11 13:06:48 +00:00
noclipped = 0;
first_cell_to_draw = true;
if(sightrange_bonus > 0 && !allowIncreasedSight())
sightrange_bonus = 0;
2015-08-08 13:57:52 +00:00
2017-07-10 18:47:38 +00:00
swap(gmatrix0, gmatrix);
gmatrix.clear();
current_display->all_drawn_copies.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 || vid.wallmode == 6;
wmascii3 = vid.wallmode == 6;
2017-07-10 18:47:38 +00:00
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;
2020-09-15 18:39:15 +00:00
mmspatial = vid.monmode >= 3;
mmhigh = vid.highlightmode >= 1;
if(hiliteclick) mmhigh = !mmhigh;
spatial_graphics = wmspatial || mmspatial;
2019-08-15 13:05:43 +00:00
spatial_graphics = spatial_graphics && GDIM == 2;
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(pconf.alpha) < 1e-6) spatial_graphics = false;
if(!spatial_graphics) wmspatial = mmspatial = false;
2019-08-15 13:05:43 +00:00
if(GDIM == 3) wmspatial = mmspatial = true;
2016-01-02 10:09:13 +00:00
2017-07-10 18:47:38 +00:00
for(int m=0; m<motypes; m++) if(isPrincess(eMonster(m)))
minf[m].name = princessgender() ? "Princess" : "Prince";
2019-10-21 22:37:57 +00:00
2020-03-21 18:05:17 +00:00
#if CAP_RAY
2019-10-25 12:50:48 +00:00
ray::in_use = ray::requested();
2019-11-03 13:19:11 +00:00
#endif
2019-10-25 12:50:48 +00:00
no_wall_rendering = ray::in_use;
// ray::comparison_mode = true;
if(ray::comparison_mode) no_wall_rendering = false;
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
for(int i=0; i<multi::players; i++) {
multi::ccdist[i] = 1e20; multi::ccat[i] = NULL;
}
2015-08-08 13:57:52 +00:00
2019-12-27 10:14:25 +00:00
downseek.reset();
2017-08-06 12:50:16 +00:00
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
2019-05-09 15:02:50 +00:00
if(mouseout() && !mousepan)
2017-07-10 18:47:38 +00:00
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
make_actual_view();
currentmap->draw_all();
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
drawMarkers();
drawFlashes();
2015-08-08 13:57:52 +00:00
2020-04-17 18:04:33 +00:00
mapeditor::draw_dtshapes();
2022-03-27 12:32:29 +00:00
set_multi = false;
2020-04-17 18:04:33 +00:00
2017-07-10 18:47:38 +00:00
if(multi::players > 1 && !shmup::on) {
if(multi::split_screen)
cwtV = multi::whereis[subscreens::current_player];
else if(multi::centerplayer != -1)
2019-09-06 06:17:38 +00:00
cwtV = multi::whereis[multi::centerplayer];
2017-07-10 18:47:38 +00:00
else {
2022-03-27 12:32:29 +00:00
vector<hyperpoint> pts;
for(int p=0; p<multi::players; p++) if(multi::playerActive(p))
2022-12-08 18:38:06 +00:00
pts.push_back(unshift(multi::whereis[p] * tile_center()));
2022-03-27 12:32:29 +00:00
center_multiplayer_map(pts);
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(shmup::on) {
if(multi::split_screen)
cwtV = shmup::pc[subscreens::current_player]->pat;
else if(multi::players == 1)
2017-07-10 18:47:38 +00:00
cwtV = shmup::pc[0]->pat;
else if(multi::centerplayer != -1)
2019-09-06 06:17:38 +00:00
cwtV = shmup::pc[multi::centerplayer]->pat;
else {
2022-03-27 12:32:29 +00:00
vector<hyperpoint> pts;
for(int p=0; p<multi::players; p++)
2022-12-08 18:38:06 +00:00
pts.push_back(unshift(shmup::pc[p]->pat * tile_center()));
2022-03-27 12:32:29 +00:00
center_multiplayer_map(pts);
}
}
2017-07-10 18:47:38 +00:00
2017-07-22 23:33:27 +00:00
#if CAP_SDL
const Uint8 *keystate = SDL12_GetKeyState(NULL);
2017-07-10 18:47:38 +00:00
lmouseover = mouseover;
2021-05-27 10:59:21 +00:00
lmouseover_distant = lmouseover;
bool useRangedOrb = (!(vid.shifttarget & 1) && haveRangedOrb() && lmouseover && lmouseover->cpdist > 1) || (keystate[SDL12(SDLK_RSHIFT, SDL_SCANCODE_RSHIFT)] | keystate[SDL12(SDLK_LSHIFT, SDL_SCANCODE_LSHIFT)]);
if(!useRangedOrb && !(cmode & sm::MAP) && !(cmode & sm::DRAW) && DEFAULTCONTROL && !mouseout() && !dual::state) {
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;
movepcto(mousedest.d, mousedest.subdir, true);
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
2017-03-23 10:53:57 +00:00
}
2019-08-09 19:00:52 +00:00
EX void drawmovestar(double dx, double dy) {
2017-03-23 10:53:57 +00:00
2019-05-12 23:57:40 +00:00
DEBBI(DF_GRAPH, ("draw movestar"));
2017-07-10 18:47:38 +00:00
if(viewdists) return;
2019-08-15 13:05:43 +00:00
if(GDIM == 3) return;
2016-08-26 09:58:03 +00:00
2017-07-10 18:47:38 +00:00
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
2022-12-08 18:38:06 +00:00
shiftpoint H = cwtV * tile_center();
2017-07-10 18:47:38 +00:00
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
shiftmatrix Centered;
2016-08-26 09:58:03 +00:00
2019-11-27 00:01:20 +00:00
if(euclid)
Centered = shiftless(eupush(H.h));
2017-07-10 18:47:38 +00:00
else if(R > 1e-9) Centered = rgpushxto0(H);
else Centered = shiftless(Id);
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
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)
queuepoly(Centered, cgi.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
queueline(tC0(Centered), Centered * xspinpush0(d * 45._deg, cgi.scalefactor/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
EX bool dronemode;
2015-08-08 13:57:52 +00:00
purehookset hooks_calcparam;
2019-09-09 13:31:49 +00:00
EX int corner_centering;
EX bool permaside;
2022-09-24 07:12:23 +00:00
EX bool old_center;
2023-03-23 08:29:46 +00:00
EX ld min_scale = 1e-6;
2023-05-15 00:39:46 +00:00
EX int forced_center_down = ISANDROID ? 2 : ISIOS ? 40 : 40;
2019-08-09 19:00:52 +00:00
EX void calcparam() {
2019-05-12 23:57:40 +00:00
DEBBI(DF_GRAPH, ("calc param"));
auto cd = current_display;
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;
2023-03-23 08:29:46 +00:00
if(abs(pconf.scale) < min_scale) pconf.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
2023-05-15 00:39:46 +00:00
cd->scrsize = realradius;
if(!inHighQual) cd->scrsize -= forced_center_down;
2017-03-23 10:53:57 +00:00
current_display->sidescreen = permaside;
2015-08-08 13:57:52 +00:00
2022-09-24 07:12:23 +00:00
if(vid.xres < vid.yres - 2 * vid.fsize && !inHighQual && (old_center || !in_perspective())) {
2020-04-06 06:46:32 +00:00
cd->ycenter = lerp(vid.fsize + cd->scrsize, vid.yres - cd->scrsize - vid.fsize, .8);
2015-08-08 13:57:52 +00:00
}
2017-03-23 10:53:57 +00:00
else {
bool ok = !vrhr::active();
2022-07-12 12:41:54 +00:00
if(vid.xres > vid.yres * 4/3+16 && (cmode & sm::SIDE) && ok && !((cmode & sm::MAYDARK) && centered_menus))
2018-11-27 01:33:10 +00:00
current_display->sidescreen = true;
2017-07-22 23:33:27 +00:00
#if CAP_TOUR
2020-11-19 17:20:06 +00:00
if(tour::on && (tour::slides[tour::currentslide].flags & tour::SIDESCREEN) && ok)
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 = pconf.scale * cd->scrsize;
2019-08-15 13:05:43 +00:00
if(GDIM == 3 && in_perspective()) cd->radius = cd->scrsize;
realradius = min(realradius, cd->radius);
2017-03-23 10:53:57 +00:00
ld aradius = sphere ? cd->radius / (pconf.alpha - 1) : cd->radius;
2021-05-09 00:25:41 +00:00
#if MAXMDIM >= 4
if(euclid && rots::drawing_underlying) aradius *= 2.5;
2021-05-09 00:25:41 +00:00
#endif
2019-09-09 13:31:49 +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
2019-09-09 13:31:49 +00:00
if(corner_centering) {
cd->ycenter = cd->ytop + cd->ysize - vid.fsize - aradius;
if(corner_centering == 1)
cd->xcenter = cd->xtop + vid.fsize + aradius;
if(corner_centering == 2)
cd->xcenter = cd->xtop + cd->xsize - vid.fsize - aradius;
}
cd->xcenter += cd->scrsize * pconf.xposition;
cd->ycenter += cd->scrsize * pconf.yposition;
2019-09-09 13:31:49 +00:00
2020-11-01 20:20:54 +00:00
ld fov = vid.fov * degree / 2;
2021-03-21 10:28:10 +00:00
cd->tanfov = sin(fov) / (cos(fov) + (panini_alpha ? panini_alpha : stereo_alpha));
2018-02-03 12:41:49 +00:00
callhooks(hooks_calcparam);
2019-04-23 13:03:17 +00:00
reset_projection();
2017-07-10 18:47:38 +00:00
}
2017-03-23 10:53:57 +00:00
2020-04-10 22:41:57 +00:00
EX function<void()> wrap_drawfullmap = drawfullmap;
2018-01-25 16:22:04 +00:00
bool force_sphere_outline = false;
2019-08-09 19:00:52 +00:00
EX void drawfullmap() {
2018-11-08 15:28:17 +00:00
2019-05-12 23:57:40 +00:00
DEBBI(DF_GRAPH, ("draw full map"));
2018-11-08 15:28:17 +00:00
2019-05-27 05:19:04 +00:00
check_cgi();
cgi.require_shapes();
2018-11-08 15:28:17 +00:00
ptds.clear();
/*
if(models::on) {
2018-11-08 15:28:17 +00:00
char ch = 'A';
for(auto& v: history::v) {
queuepoly(ggmatrix(v->base) * v->at, cgi.shTriangle, 0x306090C0);
queuestr(ggmatrix(v->base) * v->at * C0, 10, s0+(ch++), 0xFF0000);
2018-11-08 15:28:17 +00:00
}
}
*/
#if CAP_QUEUE
2018-11-08 15:28:17 +00:00
draw_boundary(0);
draw_boundary(1);
draw_model_elements();
2020-07-03 12:48:36 +00:00
#if MAXMDIM >= 4 && CAP_GL
2019-06-01 15:01:04 +00:00
prepare_sky();
#endif
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();
else callhooks(hooks_frame);
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) {
auto bcwtV = cwtV;
2017-07-10 18:47:38 +00:00
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-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-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
2019-06-28 08:22:48 +00:00
#if ISMOBILE
extern bool wclick;
#endif
2020-09-11 09:14:32 +00:00
EX bool just_refreshing;
EX int menu_darkening = 2;
2022-07-12 12:41:54 +00:00
EX bool centered_menus = false;
EX void gamescreen() {
if(cmode & sm::NOSCR) {
emptyscreen();
return;
}
2017-11-07 15:42:31 +00:00
2020-09-11 09:14:32 +00:00
if(just_refreshing) return;
2019-03-09 15:20:06 +00:00
if(subscreens::split([=] () {
2018-11-17 18:30:50 +00:00
calcparam();
compute_graphical_distance();
gamescreen();
})) {
2019-03-09 15:20:06 +00:00
if(racing::on) return;
// create the gmatrix
View = subscreens::player_displays[0].view_matrix;
centerover = subscreens::player_displays[0].precise_center;
2019-03-09 15:20:06 +00:00
just_gmatrix = true;
currentmap->draw_all();
2019-03-09 15:20:06 +00:00
just_gmatrix = false;
return;
}
2019-05-28 23:09:38 +00:00
auto gx = vid.xres;
auto gy = vid.yres;
2019-05-28 23:09:38 +00:00
if(dual::split([=] () {
vid.xres = gx;
vid.yres = gy;
dual::in_subscreen([=] () { gamescreen(); });
2019-05-28 23:09:38 +00:00
})) {
calcparam();
return;
}
calcparam();
darken = 0;
if(!inHighQual && !vrhr::active()) {
if((cmode & sm::MAYDARK) && !current_display->sidescreen)
darken += menu_darkening;
else if(cmode & sm::DARKEN)
darken += menu_darkening;
}
if(vid.highlightmode == (hiliteclick ? 0 : 2))
darken++;
if(darken >= 8) {
emptyscreen();
return;
2017-11-07 15:42:31 +00:00
}
if(history::includeHistory) history::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) {
2022-09-24 07:12:34 +00:00
if(!nomap) 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
if(history::includeHistory) history::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 && !inHighQual) {
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 {
2021-05-23 12:33:25 +00:00
if(!haveMobileCompass()) displayabutton(-1, +1, andmode == 0 && useRangedOrb ? XLAT("FIRE") : andmode == 0 && WDIM == 3 && wclick ? XLAT("WAIT") : XLAT("MOVE"), andmode == 0 ? BTON : BTOFF);
displayabutton(+1, +1, rug::rugged ? XLAT("RUG") :andmode == 1 ? XLAT("BACK") : GDIM == 3 ? XLAT("CAM") : XLAT("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
2020-11-19 17:20:06 +00:00
#if CAP_VR
2021-02-06 18:11:53 +00:00
vrhr::size_and_draw_ui_box();
2020-11-19 17:20:06 +00:00
#endif
2017-07-10 18:47:38 +00:00
}
2015-08-08 13:57:52 +00:00
EX void emptyscreen() {
ptds.clear();
ray::in_use = false;
drawqueue();
}
2023-03-25 08:24:47 +00:00
EX int nohelp;
2020-09-11 09:16:59 +00:00
EX bool no_find_player;
2018-03-02 12:03:50 +00:00
2019-08-09 19:00:52 +00:00
EX void normalscreen() {
2017-07-10 18:47:38 +00:00
help = "@";
2017-07-12 16:03:53 +00:00
2019-12-25 10:53:50 +00:00
mouseovers = standard_help();
2017-07-12 16:03:53 +00:00
2017-07-22 23:33:27 +00:00
#if CAP_TOUR
2022-06-16 21:13:54 +00:00
if(tour::on) mouseovers = (tour::slides[tour::currentslide].flags & tour::NOTITLE) ? "" : tour::tourhelp;
2017-07-12 16:03:53 +00:00
#endif
if(GDIM == 3 || !outofmap(mouseh.h)) 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;
gamescreen(); 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() && !sphere && !no_find_player && mapeditor::drawplayer)
2021-05-23 12:33:25 +00:00
displayButton(current_display->xcenter, current_display->ycenter, mousing ? XLAT("find the player") : XLAT("press SPACE to find the player"), ' ', 8);
2022-03-27 17:12:10 +00:00
if(!mapeditor::drawplayer && playermoved && !no_find_player)
displayButton(current_display->xcenter, current_display->ycenter, XLAT("move the camera with arrow keys and Home/End"), PSEUDOKEY_NOHINT, 8);
2017-07-12 16:03:53 +00:00
describeMouseover();
2016-01-02 10:09:13 +00:00
}
2019-08-09 23:15:41 +00:00
EX vector< function<void()> > screens = { normalscreen };
#if HDR
template<class T> void pushScreen(const T& x) { screens.push_back(x); }
inline void popScreen() { if(isize(screens)>1) screens.pop_back(); }
inline void popScreenAll() { while(isize(screens)>1) popScreen(); }
typedef void (*cfunction)();
2019-08-09 23:15:41 +00:00
#endif
2015-08-08 13:57:52 +00:00
EX cfunction current_screen_cfunction() {
auto tgt = screens.back().target<cfunction>();
if(!tgt) return nullptr;
return *tgt;
}
2019-08-10 08:57:14 +00:00
#if HDR
namespace sm {
static constexpr int NORMAL = 1;
static constexpr int MISSION = 2;
static constexpr int HELP = 4;
static constexpr int MAP = 8;
static constexpr int DRAW = 16;
static constexpr int NUMBER = 32;
static constexpr int SHMUPCONFIG = 64;
static constexpr int OVERVIEW = 128;
static constexpr int SIDE = 256;
static constexpr int DOTOUR = 512;
static constexpr int CENTER = 1024;
static constexpr int ZOOMABLE = 4096;
static constexpr int TORUSCONFIG = 8192;
static constexpr int MAYDARK = 16384;
static constexpr int DIALOG_STRICT_X = 32768; // do not interpret dialog clicks outside of the X region
static constexpr int EXPANSION = (1<<16);
static constexpr int HEXEDIT = (1<<17);
static constexpr int VR_MENU = (1<<18); // always show the menu in VR
static constexpr int SHOWCURSOR = (1<<19); // despite MAP/DRAW always show the cursor, no panning
static constexpr int PANNING = (1<<20); // smooth scrolling works
static constexpr int DARKEN = (1<<21); // darken the game background
static constexpr int NOSCR = (1<<22); // do not show the game background
static constexpr int AUTO_VALUES = (1<<23); // automatic place for values
static constexpr int NARROW_LINES = (1<<24); // do make the lines narrower if we needed to reduce width
static constexpr int EDIT_BEFORE_WALLS = (1<<25); // mouseover targets before walls
static constexpr int EDIT_INSIDE_WALLS = (1<<26); // mouseover targets inside walls
static constexpr int DIALOG_WIDE = (1<<27); // make dialogs wide
static constexpr int MOUSEAIM = (1<<28); // mouse aiming active here
}
2019-08-10 08:57:14 +00:00
#endif
2019-08-09 20:07:03 +00:00
EX int cmode;
2015-08-08 13:57:52 +00:00
2022-05-17 07:43:45 +00:00
EX bool dont_display_minecount = false;
2022-07-23 13:36:27 +00:00
EX color_t titlecolor;
2019-08-09 19:00:52 +00:00
EX void drawscreen() {
2015-08-08 13:57:52 +00:00
2019-05-12 23:57:40 +00:00
DEBBI(DF_GRAPH, ("drawscreen"));
#if CAP_GL
2020-11-08 10:49:19 +00:00
GLWRAP;
#endif
2016-08-26 09:58:03 +00:00
2019-05-12 23:57:40 +00:00
if(vid.xres == 0 || vid.yres == 0) return;
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
#if CAP_XGD
if(!vid.usingGL) {
gdpush(5); gdpush(backcolor);
}
#endif
#if CAP_VR
vrhr::clear();
#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
// 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
2019-08-09 19:00:52 +00:00
// displaynum(vx,100, 0, 24, 0xc0c0c0, celldist(cwt.at), ":");
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;
2022-07-23 13:36:27 +00:00
if(titlecolor) col = titlecolor;
2023-03-25 08:24:47 +00:00
if(nohelp != 1)
2018-03-02 12:03:50 +00:00
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[MAXPLAYER], tmines=0;
2017-07-10 18:47:38 +00:00
for(int p=0; p<numplayers(); p++) {
mines[p] = 0;
cell *c = playerpos(p);
if(!c) continue;
for(cell *c2: adj_minefield_cells(c)) {
if(c2->land == laMinefield)
2017-07-10 18:47:38 +00:00
minefieldNearby = true;
if(c2->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 > 9) tmines = 9;
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
2022-05-17 07:43:45 +00:00
if(!dont_display_minecount) for(int p: player_indices())
2017-07-10 18:47:38 +00:00
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,
mines[p] > 7 ? its(mines[p]) : XLAT(minetexts[mines[p]]), minecolors[mines[p]], 8);
2015-08-08 13:57:52 +00:00
2022-05-17 07:43:45 +00:00
if(minefieldNearby && !shmup::on && cwt.at->land != laMinefield && cwt.peek()->land != laMinefield && !dont_display_minecount) {
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();
2019-05-12 23:57:40 +00:00
DEBB(DF_GRAPH, ("swapbuffers"));
2020-11-19 17:20:06 +00:00
#if CAP_VR
vrhr::submit();
#endif
#if CAP_SDL
present_screen();
#endif
2021-02-04 20:30:08 +00:00
2021-02-06 18:12:04 +00:00
#if CAP_VR
2021-02-04 20:30:08 +00:00
vrhr::handoff();
2021-02-06 18:12:04 +00:00
#endif
2017-07-10 18:47:38 +00:00
//printf("\ec");
}
2015-08-08 13:57:52 +00:00
2019-08-09 19:00:52 +00:00
EX void restartGraph() {
2019-05-12 23:57:40 +00:00
DEBBI(DF_INIT, ("restartGraph"));
2017-07-10 18:47:38 +00:00
if(!autocheat) linepatterns::clearAll();
2022-12-17 16:37:26 +00:00
if(currentmap) resetview();
2017-03-23 10:53:57 +00:00
}
2015-08-08 13:57:52 +00:00
2019-08-09 19:00:52 +00:00
EX void clearAnimations() {
2018-01-13 18:20:46 +00:00
for(int i=0; i<ANIMLAYERS; i++) animations[i].clear();
flashes.clear();
fallanims.clear();
}
auto graphcm = addHook(hooks_clearmemory, 0, [] () {
2019-05-12 23:57:40 +00:00
DEBBI(DF_MEMORY, ("clear graph memory"));
mouseover = centerover = lmouseover = NULL;
gmatrix.clear(); gmatrix0.clear(); current_display->all_drawn_copies.clear();
2018-01-13 18:20:46 +00:00
clearAnimations();
2019-05-28 23:09:38 +00:00
})
+ addHook(hooks_gamedata, 0, [] (gamedata* gd) {
gd->store(mouseover);
gd->store(lmouseover);
gd->store(animations);
gd->store(flashes);
gd->store(fallanims);
gd->store(current_display->radar_transform);
2019-05-30 14:12:38 +00:00
gd->store(actual_view_transform);
2017-07-10 18:47:38 +00:00
});
2017-03-23 10:53:57 +00:00
//=== animation
2019-08-09 21:08:42 +00:00
#if HDR
struct animation {
int ltick;
double footphase;
transmatrix wherenow;
2020-12-26 17:09:09 +00:00
int attacking; /** 0 = no attack animation, 1 = first phase, 2 = second phase, 3 = hugging */
2019-08-09 21:08:42 +00:00
transmatrix attackat;
bool mirrored;
2021-06-06 08:35:55 +00:00
eItem thrown_item; /** for thrown items */
2019-08-09 21:08:42 +00:00
};
// we need separate animation layers for Orb of Domination and Tentacle+Ghost,
// and also to mark Boats
2021-06-06 08:35:55 +00:00
#define ANIMLAYERS 4
2019-08-09 21:08:42 +00:00
#define LAYER_BIG 0 // for worms and krakens
#define LAYER_SMALL 1 // for others
#define LAYER_BOAT 2 // mark that a boat has moved
2021-06-06 08:35:55 +00:00
#define LAYER_THROW 3 // for thrown items
2019-08-09 21:08:42 +00:00
#endif
EX array<map<cell*, animation>, ANIMLAYERS> animations;
2017-03-23 10:53:57 +00:00
2019-08-09 20:37:11 +00:00
EX int revhint(cell *c, int hint) {
if(hint >= 0 && hint < c->type) return c->c.spin(hint);
else return hint;
}
EX transmatrix adj(const movei& m) {
if(m.proper()) return currentmap->adj(m.s, m.d);
else return currentmap->relative_matrix(m.t, m.s, C0);
}
EX transmatrix iadj(const movei& m) {
if(m.proper()) return currentmap->iadj(m.s, m.d);
else return currentmap->relative_matrix(m.s, m.t, C0);
}
EX void animateMovement(const movei& m, int layer) {
2018-01-25 16:19:50 +00:00
if(vid.mspeed >= 5) return; // no animations!
2020-02-29 16:58:59 +00:00
LATE ( animateMovement(m, layer); )
2019-11-22 15:37:32 +00:00
transmatrix T = iadj(m);
bool found_s = animations[layer].count(m.s);
animation& a = animations[layer][m.t];
if(found_s) {
a = animations[layer][m.s];
a.wherenow = T * a.wherenow;
if(m.s != m.t)
animations[layer].erase(m.s);
a.attacking = 0;
}
else {
a.ltick = ticks;
a.wherenow = T;
a.footphase = 0;
a.mirrored = false;
}
if(m.proper() && m.s->c.mirror(m.d))
a.mirrored = !a.mirrored;
}
2021-06-06 08:35:55 +00:00
EX void animate_item_throw(cell *from, cell *to, eItem it) {
bool steps = false;
again:
if(from != to) {
forCellIdEx(c1, i, from) if(celldistance(c1, to) < celldistance(from, to)) {
animateMovement(movei(from, i), LAYER_THROW);
from = c1;
steps = true;
goto again;
}
}
if(steps) {
animation& a = animations[LAYER_THROW][to];
a.thrown_item = it;
}
}
2020-12-26 17:09:09 +00:00
EX void animateAttackOrHug(const movei& m, int layer, int phase, ld ratio, ld delta) {
2020-02-29 16:58:59 +00:00
LATE( animateAttack(m, layer); )
if(vid.mspeed >= 5) return; // no animations!
2019-11-22 15:46:37 +00:00
transmatrix T = iadj(m);
bool newanim = !animations[layer].count(m.s);
animation& a = animations[layer][m.s];
2020-12-26 17:09:09 +00:00
a.attacking = phase;
auto TC0 = tile_center();
a.attackat = lrspintox(iso_inverse(T) * TC0) * lxpush(hdist(TC0, T*TC0) * ratio + delta);
if(newanim) a.wherenow = Id, a.ltick = ticks, a.footphase = 0;
}
2020-12-26 17:09:09 +00:00
EX void animateAttack(const movei& m, int layer) {
animateAttackOrHug(m, layer, 1, 1/3., 0);
}
EX void animateHug(const movei& m, int layer) {
animateAttackOrHug(m, layer, 3, 0.5, -0.0713828 * cgi.scalefactor);
}
2017-03-23 10:53:57 +00:00
vector<pair<cell*, animation> > animstack;
EX void indAnimateMovement(const movei& m, int layer) {
2017-03-23 10:53:57 +00:00
if(vid.mspeed >= 5) return; // no animations!
2020-02-29 16:58:59 +00:00
LATE( indAnimateMovement(m, layer); )
if(animations[layer].count(m.t)) {
animation res = animations[layer][m.t];
animations[layer].erase(m.t);
animateMovement(m, layer);
if(animations[layer].count(m.t))
animstack.push_back(make_pair(m.t, animations[layer][m.t]));
animations[layer][m.t] = res;
2017-03-23 10:53:57 +00:00
}
else {
animateMovement(m, layer);
if(animations[layer].count(m.t)) {
animstack.push_back(make_pair(m.t, animations[layer][m.t]));
animations[layer].erase(m.t);
2017-03-23 10:53:57 +00:00
}
}
}
2019-08-09 19:00:52 +00:00
EX void commitAnimations(int layer) {
2020-02-29 16:58:59 +00:00
LATE( commitAnimations(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();
}
2019-08-09 20:07:03 +00:00
EX void drawBug(const cellwalker& cw, color_t col) {
#if CAP_SHAPES
2017-08-06 12:50:16 +00:00
initquickqueue();
shiftmatrix V = ggmatrix(cw.at);
if(cw.spin) V = V * ddspin180(cw.at, cw.spin);
queuepoly(V, cgi.shBugBody, col);
2017-08-06 12:50:16 +00:00
quickqueue();
#endif
2017-08-06 12:50:16 +00:00
}
2019-08-09 19:00:52 +00:00
EX bool inscreenrange(cell *c) {
2018-04-06 21:28:58 +00:00
if(sphere) return true;
if(euclid) return celldistance(centerover, c) <= get_sightrange_ambush();
2019-08-06 10:00:46 +00:00
if(nonisotropic) return gmatrix.count(c);
2019-10-12 12:40:33 +00:00
if(geometry == gCrystal344) return gmatrix.count(c);
return heptdistance(centerover, c) <= 8;
2017-08-06 12:50:16 +00:00
}
#if MAXMDIM >= 4
auto hooksw = addHook(hooks_swapdim, 100, [] { clearAnimations(); gmatrix.clear(); gmatrix0.clear(); current_display->all_drawn_copies.clear(); });
#endif
}