1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-09-05 20:07:58 +00:00

split graph.cpp into 6 files: graph, graph-player, graph-wall, graph-item, graph-monster, and animations

This commit is contained in:
Zeno Rogue
2025-08-17 12:17:20 +02:00
parent ebaeb9d7f0
commit fc81777e29
8 changed files with 4378 additions and 4333 deletions

542
animations.cpp Normal file
View File

@@ -0,0 +1,542 @@
// Hyperbolic Rogue -- animations (movement, attack, hug, fall animations, flashes, particles, etc.)
// Copyright (C) 2011-2025 Zeno Rogue, see 'hyper.cpp' for details
#include "hyper.h"
namespace hr {
//=== animation
#if HDR
struct animation {
int ltick;
double footphase;
transmatrix wherenow;
int attacking; /** 0 = no attack animation, 1 = first phase, 2 = second phase, 3 = hugging */
transmatrix attackat;
bool mirrored;
eItem thrown_item; /** for thrown items */
eMonster thrown_monster; /** for thrown monsters */
};
// we need separate animation layers for Orb of Domination and Tentacle+Ghost,
// and also to mark Boats
#define ANIMLAYERS 4
#define LAYER_BIG 0 // for worms and krakens
#define LAYER_SMALL 1 // for others
#define LAYER_BOAT 2 // mark that a boat has moved
#define LAYER_THROW 3 // for thrown items
#endif
EX array<map<cell*, animation>, ANIMLAYERS> animations;
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) {
if(vid.mspeed >= 5) return; // no animations!
LATE ( animateMovement(m, layer); )
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;
}
EX void animate_item_throw(cell *from, cell *to, eItem it, eMonster mo IS(moNone)) {
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;
a.thrown_monster = mo;
}
}
EX void animateAttackOrHug(const movei& m, int layer, int phase, ld ratio, ld delta) {
LATE( animateAttack(m, layer); )
if(vid.mspeed >= 5) return; // no animations!
transmatrix T = iadj(m);
bool newanim = !animations[layer].count(m.s);
animation& a = animations[layer][m.s];
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;
}
EX void animateAttack(const movei& m, int layer) {
animateAttackOrHug(m, layer, 1, 1/3., 0);
}
EX void animateCorrectAttack(const movei& m, int layer, eMonster who) {
if(among(who, moPlayer, moMimic, moIllusion, moShadow) && (getcs().charid/2) == pshSpaceship) {
animate_item_throw(m.s, m.t, itNone, moBullet);
return;
}
animateAttackOrHug(m, layer, 1, 1/3., 0);
}
EX void animateHug(const movei& m, int layer) {
animateAttackOrHug(m, layer, 3, 0.5, ((getcs().charid/2) == pshRatling ? -0.15 : -0.0713828) * cgi.scalefactor);
}
vector<pair<cell*, animation> > animstack;
EX void indAnimateMovement(const movei& m, int layer) {
if(vid.mspeed >= 5) return; // no animations!
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;
}
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);
}
}
}
EX void commitAnimations(int layer) {
LATE( commitAnimations(layer); )
for(int i=0; i<isize(animstack); i++)
animations[layer][animstack[i].first] = animstack[i].second;
animstack.clear();
}
EX bool applyAnimation(cell *c, shiftmatrix& V, double& footphase, int layer) {
if(!animations[layer].count(c)) return false;
animation& a = animations[layer][c];
int td = ticks - a.ltick;
ld aspd = td / 1000.0 * exp(vid.mspeed);
ld R;
again:
auto TC0 = cgi.emb->anim_tile_center();
if(among(a.attacking, 1, 3))
R = hdist(a.attackat * TC0, a.wherenow * TC0);
else
R = hdist(a.wherenow * TC0, TC0);
aspd *= (1+R+(shmup::on?1:0));
if(a.attacking == 3 && aspd > R) aspd = R;
if((R < aspd || std::isnan(R) || std::isnan(aspd) || R > 10) && a.attacking != 3) {
if(a.attacking == 1) { a.attacking = 2; goto again; }
animations[layer].erase(c);
return false;
}
else {
transmatrix T = inverse(a.wherenow);
ld z = cgi.emb->anim_center_z();
if(z) T = lzpush(-z) * T;
hyperpoint wnow;
if(a.attacking == 1 || a.attacking == 3)
wnow = T * a.attackat * TC0;
else
wnow = T * TC0;
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;
}
a.footphase += a.attacking == 2 ? -aspd : aspd;
if(a.attacking == 3 && aspd >= R) {
a.footphase = 0;
hyperpoint h1 = a.wherenow * C0;
a.wherenow = rgpushxto0(h1) * lrspintox(h1);
}
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);
}
if(a.mirrored) V = V * lmirror();
if(a.attacking == 2) V = V * lpispin();
a.ltick = ticks;
return true;
}
}
EX double chainAngle(cell *c, shiftmatrix& V, cell *c2, double dft, const shiftmatrix &Vwhere) {
if(cgi.emb->no_spin()) return 0;
if(!gmatrix0.count(c2)) return dft;
hyperpoint h = cgi.emb->anim_tile_center();
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();
if(z) h = lzpush(-z) * h;
return atan2(h[1], h[0]);
}
// equivalent to V = V * spin(-chainAngle(c,V,c2,dft));
EX bool chainAnimation(cell *c, cell *c2, shiftmatrix& V, const shiftmatrix &Vwhere, ld& length) {
if(cgi.emb->no_spin()) return false;
hyperpoint h = cgi.emb->anim_tile_center();
if(animations[LAYER_BIG].count(c2)) h = animations[LAYER_BIG][c2].wherenow * h;
h = inverse_shift(V, Vwhere) * h;
length = hdist(h, tile_center());
ld z = cgi.emb->center_z();
if(z) h = lzpush(-z) * h;
V = V * rspintox(h);
return true;
}
struct fallanim {
int t_mon, t_floor, pid;
eWall walltype;
eMonster m;
fallanim() { t_floor = 0; t_mon = 0; pid = 0; walltype = waNone; }
};
map<cell*, fallanim> fallanims;
#if HDR
struct flashdata {
int t;
int size;
cell *where;
transmatrix angle_matrix;
ld bubblesize;
int spd; // 0 for flashes, >0 for particles
color_t color;
string text;
flashdata(int _t, int _s, cell *_w, color_t col, int sped) {
t=_t; size=_s; where=_w; color = col;
spd = sped;
angle_matrix = random_spin();
}
};
#endif
EX vector<flashdata> flashes;
auto ahgf = addHook(hooks_removecells, 1, [] () {
eliminate_if(flashes, [] (flashdata& f) { return is_cell_removed(f.where); });
});
EX void drawBubble(cell *c, color_t col, string s, ld size) {
LATE( drawBubble(c, col, s, size); )
auto fd = flashdata(ticks, 1000, c, col, 0);
fd.text = s;
fd.bubblesize = size;
flashes.push_back(fd);
}
EX void drawFlash(cell *c) {
flashes.push_back(flashdata(ticks, 1000, c, iinf[itOrbFlash].color, 0));
}
EX void drawBigFlash(cell *c) {
flashes.push_back(flashdata(ticks, 2000, c, 0xC0FF00, 0));
}
EX void drawParticleSpeed(cell *c, color_t col, int speed) {
LATE( drawParticleSpeed(c, col, speed); )
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);
}
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);
ld angle = -atan2(tC0(currentmap->adj(c, dir)));
angle += TAU * (rand() % 100 - rand() % 100) / 100 / c->type;
fd.angle_matrix = spin(angle);
flashes.push_back(fd);
}
}
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;
LATE( fallingFloorAnimation(c, w, m); )
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;
LATE( fallingMonsterAnimation(c, m, id); )
fallanim& fa = fallanims[c];
fa.t_mon = ticks;
fa.m = m;
fa.pid = id;
// drawParticles(c, darkenedby(linf[c->land].color, 1), 4, 50);
}
void celldrawer::draw_fallanims() {
poly_outline = OUTLINE_NONE;
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[SIDE::FLOOR], darkena(fcol, fd, 0xFF), PPR::WALL);
else if(fa.walltype == waNone) {
draw_qfi(c, V, darkena(fcol, fd, 0xFF), PPR::FLOOR);
}
else {
celldrawer ddalt;
eWall w = c->wall; int p = c->wparam;
c->wall = fa.walltype; c->wparam = fa.m;
ddalt.c = c;
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::WALL_TOP);
queuepolyat(orthogonal_move_fol(V, cgi.WALL), cgi.shWall[ct6], darkena(ddalt.wcol, 0, 0xFF), PPR::WALL_DECO);
forCellIdEx(c2, i, c)
if(placeSidewall(c, i, SIDE::WALL, V, darkena(ddalt.wcol, 1, 0xFF))) break;
}
pushdown(c, q, V, t*t / 1000000. + t / 1000., true, true);
}
}
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;
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);
}
}
if(erase) fallanims.erase(c);
}
}
#if CAP_QUEUE
always_false static_bubbles;
EX void draw_flash(struct flashdata& f, const shiftmatrix& V, bool& kill) {
int tim = ticks - f.t;
if(tim <= f.size && !f.spd) kill = false;
if(f.text != "") {
if(static_bubbles) {
tim = 0; kill = false;
}
color_t col = f.color;
dynamicval<color_t> p(poly_outline, poly_outline);
int r = 2;
apply_neon(col, r);
if(GDIM == 3 || sphere)
queuestr(V, (1 - tim * 1. / f.size) * f.bubblesize * mapfontscale / 100, f.text, col, r);
else if(!kill) {
shiftpoint h = tC0(V);
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.bubblesize * mapfontscale / 100, f.text, col, r);
}
}
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);
}
}
else if(f.spd) {
#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 = V * f.angle_matrix * (GDIM == 2 ? xpush(t) : 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)
queueball(V * lzpush(cgi.GROIN1), rad, flashcol, itDiamond);
else
#endif
{
PRING(a) curvepoint(xspinpush0(a * cgi.S_step, rad));
queuecurve(V, flashcol, 0x8080808, PPR::LINE);
}
}
}
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)
queueball(V * lzpush(cgi.GROIN1), rad, flashcol, itRuby);
else
#endif
{
PRING(a) curvepoint(xspinpush0(a * cgi.S_step, rad));
queuecurve(V, flashcol, 0x8080808, PPR::LINE);
}
}
}
}
#endif
EX void drawFlashes() {
#if CAP_QUEUE
for(int k=0; k<isize(flashes); k++) {
bool kill = true;
flashdata& f = flashes[k];
bool copies = false;
for (const shiftmatrix& V : hr::span_at(current_display->all_drawn_copies, f.where)) {
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;
}
}
}
if(f.t > ticks - 800 && !copies) {
kill = false;
}
if(kill) {
f = flashes[isize(flashes)-1];
flashes.pop_back(); k--;
}
}
#endif
}
EX void clearAnimations() {
for(int i=0; i<ANIMLAYERS; i++) animations[i].clear();
flashes.clear();
fallanims.clear();
}
EX ld animation_factor = 1;
EX int animation_lcm = 0;
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;
}
EX ld fractick(int period, ld phase IS(0)) {
ld t = ptick(period, phase) / TAU;
t -= floor(t);
if(t<0) t++;
return t;
}
EX ld sintick(int period, ld phase IS(0)) {
return sin(ptick(period, phase));
}
EX transmatrix spintick(int period, ld phase IS(0)) {
return spin(ptick(period, phase));
}
auto animcm = addHook(hooks_gamedata, 0, [] (gamedata* gd) {
gd->store(animations);
gd->store(flashes);
gd->store(fallanims);
});
}

508
graph-item.cpp Normal file
View File

@@ -0,0 +1,508 @@
// Hyperbolic Rogue -- item graphics file
// Copyright (C) 2011-2025 Zeno Rogue, see 'hyper.cpp' for details
#include "hyper.h"
namespace hr {
EX shiftmatrix face_the_player(const shiftmatrix V) {
if(GDIM == 2) return V;
if(mproduct) return bobbing ? orthogonal_move(V, cos(ptick(750)) * cgi.plevel / 16) : V;
if(mhybrid) return bobbing ? V * zpush(cos(ptick(750)) * cgi.plevel / 16) : V;
transmatrix dummy; /* used only in prod anyways */
if(embedded_plane && !cgi.emb->is_same_in_same()) return V;
if(nonisotropic) return shiftless(spin_towards(unshift(V), dummy, C0, 2, 0));
#if CAP_VR
if(vrhr::enabled) {
shiftpoint h = tC0(V);
hyperpoint uh = unshift(h);
return shiftless(cspin90(1, 2) * rspintox(cspin90(2, 1) * uh) * xpush(hdist0(uh)) * cspin90(0, 2) * spin270());
}
#endif
return rgpushxto0(tC0(V));
}
EX hpcshape& orbshape(eOrbshape s) {
if(vid.orbmode == 0) return cgi.shRing;
switch(s) {
case osLove: return cgi.shLoveRing;
case osRanged: return cgi.shTargetRing;
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;
case osPowerUtility: return cgi.shPowerGearRing;
case osWarping: return cgi.shHeptaRing;
case osFrog: return cgi.shFrogRing;
case osProtective: return cgi.shProtectiveRing;
case osTerraform: return cgi.shTerraRing;
case osMovement: return cgi.shMoveRing;
default: return cgi.shRing;
}
}
EX 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;
}
EX color_t orb_auxiliary_color(eItem it) {
if(it == itOrbFire) return firecolor(200);
if(it == itOrbWater) return 0x000060;
if(it == itOrbFriend || it == itOrbDiscord) return 0xC0C0C0;
if(it == itOrbFrog) return 0xFF0000;
if(it == itOrbImpact) return 0xFF0000;
if(it == itOrbPhasing) return 0xFF0000;
if(it == itOrbDash) return 0xFF0000;
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;
if(it == itOrbSlaying) return 0xFF0000;
if(it == itOrbSide1) return 0x307080;
if(it == itOrbDigging) return 0x606060;
if(it == itOrbEnergy) return 0xFFFF80;
return iinf[it].color;
}
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 bool drawItemType(eItem it, cell *c, const shiftmatrix& V, color_t icol, int pticks, bool hidden) {
if(!it) return false;
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_or_zh(V, iinf[it].glyph, iinf[it].name, icol, 1, 0.5);
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); };
int ct6 = c ? ctof(c) : 1;
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 :
(it == itFrog || it == itWhirlpool) ? &cgi.shDisk :
it == itHunting ? &cgi.shTriangle :
(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 :
among(it, itIvory, itEclectic) ? &cgi.shFigurine :
xch == '?' ? &cgi.shBookCover :
it == itKey ? &cgi.shKey :
it == itRevolver ? &cgi.shGun :
NULL;
if(c && doHighlight())
poly_outline = kind_outline(it);
shiftmatrix Vit = V;
if(embedded_plane && c && it != itBabyTortoise) Vit = orthogonal_move_fol(V, cgi.STUFF);
if(c && mproduct)
Vit = orthogonal_move(Vit, sin(ptick(750)) * cgi.plevel / 4);
else if(c && sl2 && !embedded_plane)
Vit = Vit * zpush(sin(ptick(750)) * cgi.plevel / 4);
else
if(GDIM == 3 && c && it != itBabyTortoise) Vit = face_the_player(Vit);
// V * cspin(0, 2, ptick(618, 0));
#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;
else if(it == itSavedPrincess) {
drawMonsterType(moPrincess, c, V, icol, 0, icol);
return true;
}
else if(it == itStrongWind) {
queuepoly(Vit * spinptick(750, 0), cgi.shFan, darkena(icol, 0, 255));
}
else if(it == itFatigue) {
queuepoly(Vit * spinptick(750, 0), cgi.shFan, darkena(icol, 0, 255));
}
else if(it == itWarning) {
queuepoly(Vit * spinptick(750, 0), cgi.shTriangle, darkena(icol, 0, 255));
}
else if(it == itCrossbow) {
queuepoly(Vit, cgi.shCrossbowIcon, getcs().bowcolor);
queuepoly(Vit, cgi.shCrossbowstringIcon, getcs().bowcolor2);
}
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));
}
else if(it == itCompass) {
shiftmatrix V2;
#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;
V2 = V * spin(crystal::compass_angle() + M_PI);
}
else
#endif
if(1) {
shiftpoint P1;
if(mark_compass(c, P1)) {
V2 = V * lrspintox(inverse_shift(V, P1));
}
else V2 = V;
}
if(GDIM == 3) {
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);
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);
queuepoly(V2 * lpispin(), cgi.shCompass3, 0x000000FF & hider);
}
xsh = NULL;
}
else if(it == itPalace) {
#if MAXMDIM >= 4
if(GDIM == 3 && WDIM == 2) {
ld h = cgi.human_height;
dynamicval<qfloorinfo> qfi2(qfi, qfi);
shiftmatrix V2 = V * spin(pticks * vid.ispeed / 1500.);
/* divisors should be higher than in plate renderer */
qfi.fshape = &cgi.shMFloor2;
draw_shapevec(c, V2 * lzpush(-h/30), qfi.fshape->levels[SIDE::FLOOR], 0xFFD500FF, PPR::WALL);
qfi.fshape = &cgi.shMFloor3;
draw_shapevec(c, V2 * lzpush(-h/25), qfi.fshape->levels[SIDE::FLOOR], darkena(icol, 0, 0xFF), PPR::WALL);
qfi.fshape = &cgi.shMFloor4;
draw_shapevec(c, V2 * lzpush(-h/20), qfi.fshape->levels[SIDE::FLOOR], 0xFFD500FF, PPR::WALL);
}
else if(WDIM == 3 && c) {
ld h = cgi.human_height;
shiftmatrix V2 = Vit * spin(pticks * vid.ispeed / 1500.);
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);
}
else if(WDIM == 3 && !c) {
queuepoly(Vit, cgi.shGem[ct6], 0xFFD500FF);
}
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);
}
xsh = NULL;
}
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));
}
else if(it == itBarrow && c) {
for(int i = 0; i<c->landparam; i++)
queuepolyat(Vit * spin(TAU * i / c->landparam) * xpush(.15 * cgi.scalefactor) * spinptick(1500, 0), *xsh, darkena(icol, 0, hidden ? 0x40 :
(highwall(c) && wmspatial) ? 0x60 : 0xFF),
PPR::HIDDEN);
}
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;
if(it == itSwitch) icol = minf[active_switch()].color;
shiftmatrix V2 = Vit * spinptick(1500, 0);
if(xsh == &cgi.shBookCover && mmitem) {
if(GDIM == 3)
queuepoly(V2 * cpush(2, 1e-3), cgi.shBook, 0x805020FF);
else
queuepoly(V2, cgi.shBook, 0x805020FF);
}
PPR pr = PPR::ITEM;
int alpha = hidden ? (it == itKraken ? 0xC0 : 0x40) : 0xF0;
if(c && c->wall == waIcewall) pr = PPR::HIDDEN, alpha = 0x80;
queuepolyat(V2, *xsh, darkena(icol, 0, alpha), pr);
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);
}
}
else if(xch == 'o' || xch == 'c' || it == itInventory) {
if(it == itOrbFire) icol = firecolor(100);
PPR prio = PPR::ITEM;
bool inice = c && c->wall == waIcewall;
if(inice) prio = PPR::HIDDEN;
color_t icol1 = icol;
icol = orb_auxiliary_color(it);
color_t col = darkena(icol, 0, int(0x80 + 0x70 * sinptick(300)));
if(it == itOrbFish && vid.orbmode == 2)
queuepolyat(Vit * spinptick(1500, 0), cgi.shFishTail, col, PPR::ITEM_BELOW);
if(xch == 'c')
queuepolyat(Vit * spinptick(500, 0), cgi.shMoonDisk, darkena(0x801080, 0, hidden ? 0x20 : 0xC0), prio);
else if(vid.orbmode < 2) {
icol1 = orb_inner_color(it);
queuepolyat(Vit, cgi.shDisk, darkena(icol1, 0, inice ? 0x80 : hidden ? 0x20 : 0xC0), prio);
}
else {
icol1 = orb_inner_color(it);
auto dark = darkena(icol1, 0, inice ? 0x80 : hidden ? 0x20 : (it == itOrbBeauty) ? 0xA0 : 0xC0);
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();
if (it == itOrbBeauty) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
for(int u=0; u<3; u++)
queuepolyat(Vit1 * spin(40._deg * u), cgi.shSmallRose, dark, prio);
}
else if (it == itOrbLife) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit1, cgi.shSmallPBody, dark, prio);
queuepolyat(Vit1, cgi.shDiskM, dark, prio);
}
else if (it == itOrbBull) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit1, cgi.shTinyBullBody, dark, prio);
queuepolyat(Vit1, cgi.shTinyBullHead, dark, prio);
queuepolyat(Vit1, cgi.shTinyBullHorn, dark, prio);
queuepolyat(Vit1 * lmirror(), cgi.shTinyBullHorn, dark, prio);
}
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);
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);
}
else if (it == itOrbSpeed) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
drawSpeed(Vit, 0.3);
}
else if (it == itOrbStunning) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit, cgi.shDiskM, dark, prio);
for (int i=0; i<5; i++) {
shiftmatrix V2 = Vit * spin(TAU * i / 5 + ptick(300));
queuepolyat(V2, cgi.shSmallFlailBall, dark, prio);
}
}
else if (it == itOrbDragon) {
queuepolyat(Vit, cgi.shDisk, dark1, prio);
queuepolyat(Vit1, cgi.shSmallDragonHead, dark, prio);
queuepolyat(Vit1, cgi.shSmallDragonNostril, 0xFF, prio);
queuepolyat(Vit1*lmirror(), cgi.shSmallDragonNostril, 0xFF, prio);
queuepolyat(Vit1, cgi.shSmallDragonEyes, 0x60, prio);
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);
queuepolyat(Vit1*MirrorX*lmirror(), cgi.shSmallWormEyes, 0x60, prio);
}
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)
queuepolyat(Vit1, cgi.shSmallerDodeca, dark, prio);
else if (it == itOrbAether) {
queuepolyat(Vit1, cgi.shHalfDisk, dark, prio);
queuepolyat(Vit1*lmirror(), cgi.shHalfDisk, 0xFF, prio);
queuepolyat(Vit1*MirrorX, cgi.shHalfHumanoid, dark, prio);
queuepolyat(Vit1*lmirror()*MirrorX, cgi.shHalfHumanoid, 0xFF, prio);
}
else if (it == itOrbFlash)
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) {
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);
}
else {
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 :
(it == itOrbSide2 || it == itOrb37 || it == itOrbLava) ? &cgi.shDiskT :
(it == itOrbGravity) ? &cgi.shTinyArrow :
(it == itOrbFreedom || it == itOrbRecall) ? &cgi.shDiskSq :
(it == itOrbEnergy) ? &cgi.shHalfDisk :
(it == itOrbSpace) ? &cgi.shSmallPirateHook :
(it == itOrbChoice || it == itOrbMirror || it == itOrbMagnetism || it == itOrbEmpathy || it == itOrbDiscord) ? &cgi.shEccentricDisk :
(it == itOrbPsi || it == itOrbSide3) ? &cgi.shDiskS :
(it == itOrbPurity) ? &cgi.shSmallEgg :
(it == itOrbLightning) ? &cgi.shLightningBolt :
(it == itOrbShield) ? &cgi.shShield :
(it == itOrbTime) ? &cgi.shHourglass :
(it == itOrbAir) ? &cgi.shSmallFan :
(it == itOrbWoods) ? &cgi.shTreeIcon :
(it == itOrbNature) ? &cgi.shLeafIcon :
(it == itOrbIllusion || it == itOrbInvis || it == itOrbTeleport) ? &cgi.shHumanoid :
jump ? &cgi.shDiskSegment :
NULL;
queuepolyat(Vit, cgi.shDisk, dark, prio);
bool reversed = (shape == &cgi.shTreeIcon || shape == &cgi.shHumanoid || it == itOrbSword2);
bool left90 = (shape == &cgi.shLeafIcon || shape == &cgi.shLightningBolt);
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))
queuepolyat(Vit1*lmirror(), *shape, 0x80, prio);
if (jump || it == itOrbEnergy)
queuepolyat(Vit1*lmirror(), *shape, col, prio);
if (it == itOrbIntensity || it == itOrbImpact)
queuepolyat(Vit1, cgi.shDiskM, 0x80, prio);
if (it == itOrbHorns) {
queuepolyat(Vit1, cgi.shSmallBullHead, 0x80, prio);
queuepolyat(Vit1, cgi.shSmallBullHorn, 0x80, prio);
queuepolyat(Vit1*lmirror(), cgi.shSmallBullHorn, 0x80, prio);
}
if (it == itOrbUndeath) {
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) {
queuepolyat(Vit1, cgi.shSmallFlailTrunk, 0x80, prio);
queuepolyat(Vit1, cgi.shSmallHammerHead, 0x80, prio);
}
if (it == itOrbShell)
for(int i = 1; i<8; i++) {
queuepolyat(Vit1, cgi.shTortoise[i][2], 0x80, prio);
if (i>=5 && i<=7)
queuepolyat(Vit1*lmirror(), cgi.shTortoise[i][2], 0x80, prio);
}
}
}
queue_ring(Vit * spinptick(1500, 0), orbshape(iinf[it].orbshape), col, prio);
}
else {
draw_ascii_or_zh(V, xch, iinf[it].name, icol, 1, 0.5);
}
return true;
#endif
}
EX void queue_goal_text(shiftpoint P1, ld sizemul, const string& s, color_t color) {
#if CAP_VR
if(vrhr::enabled) {
auto e = inverse_exp(P1);
e = e * 3 / hypot_d(GDIM, e);
auto T = face_the_player(shiftless(rgpushxto0(direct_exp(e))));
queuestrn(T, sizemul * mapfontscale / 100, s, color);
return;
}
#endif
queuestr(P1, vid.fsize * sizemul, s, color);
}
EX bool mark_compass(cell *c, shiftpoint& P1) {
cell *c1 = c ? findcompass(c) : NULL;
if(!c1) return false;
shiftmatrix P = ggmatrix(c1);
P1 = tC0(P);
if(isPlayerOn(c)) {
queue_goal_text(P1, 2, "X", 0x10100 * int(128 + 100 * sintick(150)));
// queuestr(V, 1, its(compassDist(c)), 0x10101 * int(128 - 100 * sin(ticks / 150.)), 1);
queue_goal_text(P1, 1, its(-compassDist(c)), 0x10101 * int(128 - 100 * sintick(150)));
addauraspecial(P1, 0xFF0000, 0);
addradar(P, 'X', iinf[itCompass].color, 0xFF, true);
}
return true;
}
}

2001
graph-monster.cpp Normal file

File diff suppressed because it is too large Load Diff

742
graph-player.cpp Normal file
View File

@@ -0,0 +1,742 @@
// Hyperbolic Rogue -- player/mimic graphics file
// Copyright (C) 2011-2025 Zeno Rogue, see 'hyper.cpp' for details
#include "hyper.h"
namespace hr {
EX int lightat, safetyat;
EX void drawLightning() { lightat = ticks; }
EX void drawSafety() { safetyat = ticks; }
EX void drawShield(const shiftmatrix& V, eItem it) {
#if CAP_CURVE
float ds = ptick(300);
color_t col = iinf[it].color;
if(it == itOrbShield && items[itOrbTime] && !orbused[it])
col = (col & 0xFEFEFE) / 2;
if(sphere && cwt.at->land == laHalloween && !wmblack && !wmascii)
col = 0;
double d = it == itOrbShield ? cgi.hexf : cgi.hexf - .1;
int mt = sphere ? 7 : 5;
#if MAXMDIM >= 4
if(GDIM == 3)
queueball(V * lzpush(cgi.GROIN1), cgi.human_height / 2, darkena(col, 0, 0xFF), itOrbShield);
#else
if(1) ;
#endif
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);
}
#endif
}
EX void drawSpeed(const shiftmatrix& V, ld scale IS(1)) {
#if CAP_CURVE
ld ds = ptick(10);
color_t col = darkena(iinf[itOrbSpeed].color, 0, 0xFF);
#if MAXMDIM >= 4
if(GDIM == 3) queueball(V * lzpush(cgi.GROIN1), cgi.human_height * 0.55, col, itOrbSpeed);
else
#endif
for(int b=0; b<cgi.S84; b+=cgi.S14) {
PRING(a)
curvepoint(xspinpush0((ds+b+a) * cgi.S_step, cgi.hexf*a/cgi.S84*scale));
queuecurve(V, col, 0x8080808, PPR::LINE);
}
#endif
}
EX void drawSafety(const shiftmatrix& V, int ct) {
if(inHighQual) return;
#if CAP_QUEUE
ld ds = ptick(50);
color_t col = darkena(iinf[itOrbSafety].color, 0, 0xFF);
#if MAXMDIM >= 4
if(GDIM == 3) {
queueball(V * lzpush(cgi.GROIN1), 2*cgi.hexf, col, itOrbSafety);
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
}
EX void drawFlash(const shiftmatrix& V) {
#if CAP_CURVE
float ds = ptick(300);
color_t col = darkena(iinf[itOrbFlash].color, 0, 0xFF);
col &= ~1;
for(int u=0; u<5; u++) {
ld rad = cgi.hexf * (2.5 + .5 * sin(ds+u*.3));
#if MAXMDIM >= 4
if(GDIM == 3) {
queueball(V * lzpush(cgi.GROIN1), rad, col, itOrbFlash);
}
#else
if(1) ;
#endif
else {
PRING(a) curvepoint(xspinpush0(a * cgi.S_step, rad));
queuecurve(V, col, 0x8080808, PPR::LINE);
}
}
#endif
}
EX void drawLove(const shiftmatrix& V, int hdir) {
#if CAP_CURVE
float ds = ptick(300);
color_t col = darkena(iinf[itOrbLove].color, 0, 0xFF);
col &= ~1;
for(int u=0; u<5; u++) {
shiftmatrix V1 = chei(V, u, 5);
PRING(a) {
double d = (1 + cos(a * cgi.S_step)) / 2;
double z = a; if(z>cgi.S42) z = cgi.S84-z;
if(z <= 10) d += (10-z) * (10-z) * (10-z) / 3000.;
ld rad = cgi.hexf * (2.5 + .5 * sin(ds+u*.3)) * d;
curvepoint(xspinpush0((cgi.S42+hdir+a-1) * cgi.S_step, rad));
}
queuecurve(V1, col, 0x8080808, PPR::LINE);
}
#endif
}
EX void drawWinter(const shiftmatrix& V, ld hdir, color_t col) {
#if CAP_QUEUE
float ds = ptick(300);
col = darkena(col, 0, 0xFF);
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);
}
#endif
}
EX void drawLightning(const shiftmatrix& V) {
#if CAP_QUEUE
float ds = ptick(600);
color_t col = darkena(iinf[itOrbLightning].color, 0, 0xFF);
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);
}
#endif
}
EX void drawCurse(const shiftmatrix& V, eItem it) {
#if CAP_QUEUE
float ds = ptick(450) + (int(it) * 5.5); // Extra offset so both Gluttony and Repulsion are easily visible
color_t col = darkena(iinf[it].color, 0, 0xFF);
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;
}
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
}
EX void drawPlayerEffects(const shiftmatrix& V, const shiftmatrix& Vparam, cell *c, eMonster m) {
bool onplayer = m == moPlayer;
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);
if(items[itOrbSpeed]) drawSpeed(V, (items[itOrbSpeed] % 2) ? 1.1 : 0.8);
if(items[itCurseGluttony]) drawCurse(V, itCurseGluttony);
if(items[itCurseRepulsion]) drawCurse(V, itCurseRepulsion);
if(onplayer && (items[itOrbSword] || items[itOrbSword2])) {
using namespace sword;
if(shmup::on && SWORDDIM == 2) {
#if CAP_SHAPES
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)));
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
}
else if(SWORDDIM == 3) {
#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;
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)));
if(items[itOrbSword2])
queuepoly(Vsword * lpispin() * cspin(1,2, ticks / 150.), (peace::on ? cgi.shMagicShovel : cgi.shMagicSword), darkena(iinf[itOrbSword2].color, 0, 0xC0 + 0x30 * sintick(200)));
#endif
}
else {
int& ang = sword::dir[multi::cpid].angle;
ang %= sword::sword_angles;
#if CAP_QUEUE || CAP_SHAPES
shiftmatrix Vnow = Vparam * rgpushxto0(inverse_shift(Vparam, tC0(V))) * ddspin180(c,0);
#endif
int adj = 1 - ((sword_angles/cwt.at->type)&1);
#if CAP_QUEUE
if(!euclid && !mhybrid) for(int a=0; a<sword_angles; a++) {
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);
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;
#if MAXMDIM >= 4
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);
#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);
}
#endif
#if CAP_SHAPES
if(items[itOrbSword])
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)));
if(items[itOrbSword2])
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
}
}
if(onplayer && items[itOrbSafety]) drawSafety(V, c->type);
if(onplayer && items[itOrbFlash]) drawFlash(V);
if(onplayer && items[itOrbLove]) drawLove(V, 0); // displaydir(c, cwt.spin));
if(items[itOrbWinter])
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));
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);
PRING(a)
curvepoint(xspinpush0(a * cgi.S_step, rad));
queuecurve(V, col, 0, PPR::LINE);
}
}
}
#if HDR
struct playershape {
string name;
bool is_humanoid;
bool is_animal;
};
enum ePlayershape { pshRogue, pshPrincess, pshCat, pshDog, pshFamiliar, pshSpaceship, pshBunny, pshRatling, pshGUARD };
#endif
EX vector<playershape> playershapes = {
{"Rogue", true, false},
{"Princess", true, false},
{"cat", false, true},
{"dog", false, true},
{"Familiar", false, true},
{"spaceship", false, false},
{"bunny", false, true},
{"Ratling", true, false}
};
EX void drawPlayer_animal(eMonster m, cell *where, const shiftmatrix& V0, color_t col, double footphase, bool stop IS(false)) {
charstyle& cs = getcs();
auto id = ePlayershape(cs.charid >> 1);
auto V = V0;
if(id == pshBunny) {
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 * 2);
auto V0 = V;
V = at_smart_lof(V0, z2);
}
if(id == pshBunny) {
/* bunny */
if(!mmspatial && !footphase) {
if(stop) return;
queuepoly(VALEGS, cgi.shCatLegs, fc(500, cs.dresscolor, 4));
}
else {
ShadowV(V, cgi.shBunnyBody);
if(stop) return;
animallegs(VALEGS, moRunDog, fc(500, cs.dresscolor, 4), footphase);
}
queuepoly(VABODY, cgi.shBunnyTail, fc(0, cs.skincolor, 0));
queuepoly(VABODY, cgi.shBunnyBody, fc(0, cs.skincolor, 0));
queuepoly(VAHEAD, cgi.shBunnyHead, fc(150, cs.haircolor, 2));
}
else if(id == pshFamiliar) {
/* famililar */
if(!mmspatial && !footphase) {
if(stop) return;
queuepoly(VALEGS, cgi.shWolfLegs, fc(150, cs.dresscolor, 4));
}
else {
ShadowV(V, cgi.shWolfBody);
if(stop) return;
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));
}
else if(id == pshDog) {
/* dog */
if(!mmspatial && !footphase) {
if(stop) return;
queuepoly(VABODY, cgi.shDogBody, fc(0, cs.skincolor, 0));
}
else {
ShadowV(V, cgi.shDogTorso);
if(stop) return;
animallegs(VALEGS, moRunDog, fc(500, cs.dresscolor, 4), footphase);
queuepoly(VABODY, cgi.shDogTorso, fc(0, cs.skincolor, 0));
}
queuepoly(VAHEAD, cgi.shDogHead, fc(150, cs.haircolor, 2));
}
else if(id == pshCat) {
/* cat */
if(!mmspatial && !footphase) {
if(stop) return;
queuepoly(VALEGS, cgi.shCatLegs, fc(500, cs.dresscolor, 4));
}
else {
ShadowV(V, cgi.shCatBody);
if(stop) return;
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));
}
if(!shmup::on || shmup::curtime >= shmup::getPlayer()->nextshot) {
color_t col = items[itOrbDiscord] ? watercolor(0) : fc(314, cs.eyecolor, 3);
if(among(id, pshCat, pshBunny)) {
queuepoly(VAHEAD * xpush(.04), cgi.shWolf1, col);
queuepoly(VAHEAD * xpush(.04), cgi.shWolf2, col);
}
if(id == pshDog) {
queuepoly(VAHEAD, cgi.shWolf1, col);
queuepoly(VAHEAD, cgi.shWolf2, col);
}
if(id == pshFamiliar) {
queuepoly(VAHEAD, cgi.shFamiliarEye, col);
queuepoly(VAHEAD * lmirror(), cgi.shFamiliarEye, col);
}
}
if(id == pshBunny) {
queuepoly(VAHEAD, cgi.shBunnyEar, fc(150, cs.haircolor, 2));
queuepoly(VAHEAD * MirrorY, cgi.shBunnyEar, fc(150, cs.haircolor, 2));
}
if(id == pshDog) {
color_t colnose = items[itOrbDiscord] ? watercolor(0) : fc(314, 0xFF, 3);
queuepoly(VAHEAD, cgi.shWolf3, colnose);
}
#if CAP_COMPLEX2
if(camelot::knighted)
queuepoly(VABODY, cgi.shKnightCloak, darkena(cloakcolor(camelot::knighted), 1, 0xFF));
#endif
if(tortoise::seek())
tortoise::draw(VABODY, tortoise::seekbits, 4, 0);
}
EX void drawPlayer_humanoid(eMonster m, cell *where, const shiftmatrix& V, color_t col, double footphase, bool stop IS(false)) {
charstyle& cs = getcs();
auto id = ePlayershape(cs.charid >> 1);
auto& body = (id == pshRatling) ? cgi.shYeti : (cs.charid&1) ? cgi.shFemaleBody : cgi.shPBody;
ShadowV(V, body);
if(stop) return;
const transmatrix VBS = otherbodyparts(V, fc(0, (id == pshRatling) ? cs.dresscolor : cs.skincolor, 0), items[itOrbFish] ? moWaterElemental : moPlayer, footphase);
queuepoly(VBODY * VBS, body, fc(0, cs.skincolor, 0));
if(cs.charid&1)
queuepoly(VBODY1 * VBS, cgi.shFemaleDress, fc(500, cs.dresscolor, 4));
if(cs.charid == 2)
queuepoly(VBODY2 * VBS, cgi.shPrinceDress, fc(400, cs.dresscolor, 5));
if(cs.charid == 3)
queuepoly(VBODY2 * VBS, cgi.shPrincessDress, fc(400, cs.dresscolor2, 5));
if(items[itOrbSide3])
queuepoly(VBODY * VBS, (cs.charid&1) ? cgi.shFerocityF : cgi.shFerocityM, fc(0, cs.skincolor, 0));
if(items[itOrbHorns]) {
queuepoly(VBODY * VBS, cgi.shBullHead, items[itOrbDiscord] ? watercolor(0) : 0xFF000030);
queuepoly(VBODY * VBS, cgi.shBullHorn, items[itOrbDiscord] ? watercolor(0) : 0xFF000040);
queuepoly(VBODY * VBS * lmirror(), cgi.shBullHorn, items[itOrbDiscord] ? watercolor(0) : 0xFF000040);
}
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
shiftmatrix VWPN = cs.lefthanded ? VBODY * VBS * lmirror() : VBODY * VBS;
if(peace::on) ;
else if(racing::on) {
#if CAP_RACING
if(racing::trophy[multi::cpid])
queuepoly(VWPN, cgi.shTrophy, racing::trophy[multi::cpid]);
#endif
}
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;
}
shiftmatrix VWPN1 = VWPN, VWPN2 = VWPN;
if(GDIM == 3) { ld h = cgi.human_height; VWPN1 = VWPN * lzpush(-h/60); VWPN2 = VWPN * lzpush(-h/30); }
if(ti <= 1) queuepoly(VWPN1, cgi.shCrossbowstringLoaded, fc(314, cs.bowcolor2, 3));
else if(ti <= 2) queuepoly(VWPN1, cgi.shCrossbowstringSemiloaded, fc(314, cs.bowcolor2, 3));
else queuepoly(VWPN1, cgi.shCrossbowstringUnloaded, fc(314, cs.bowcolor2, 3));
if(ti == 0) queuepoly(VWPN2, cgi.shCrossbowBolt, fc(314, cs.swordcolor, 3));
}
else if(items[itOrbThorns])
queuepoly(VWPN, cgi.shHedgehogBladePlayer, items[itOrbDiscord] ? watercolor(0) : 0x00FF00FF);
else if(!shmup::on && items[itOrbDiscord])
queuepoly(VWPN, cs.charid >= 2 ? cgi.shSabre : cgi.shPSword, watercolor(0));
else if(items[itRevolver])
queuepoly(VWPN, cgi.shGunInHand, fc(314, cs.swordcolor, 3)); // 3 not colored
else if(items[itOrbSlaying]) {
queuepoly(VWPN, cgi.shFlailTrunk, items[itOrbDiscord] ? watercolor(0) : fc(314, cs.swordcolor, 3));
queuepoly(VWPN, cgi.shHammerHead, items[itOrbDiscord] ? watercolor(50) : fc(314, cs.swordcolor, 3));
}
else if(items[itCurseWeakness]) {
/* no weapon shown */
}
else if(!shmup::on)
queuepoly(VWPN, cs.charid >= 2 ? cgi.shSabre : cgi.shPSword, fc(314, cs.swordcolor, 3)); // 3 not colored
else if(shmup::curtime >= shmup::getPlayer()->nextshot)
queuepoly(VWPN, cgi.shPKnife, fc(314, cs.swordcolor, 3)); // 3 not colored
if(items[itOrbBeauty]) {
if(cs.charid&1)
queuepoly(VHEAD1, cgi.shFlowerHair, darkena(iinf[itOrbBeauty].color, 0, 0xFF));
else
queuepoly(VWPN, cgi.shFlowerHand, darkena(iinf[itOrbBeauty].color, 0, 0xFF));
}
if(where && where->land == laWildWest) {
queuepoly(VHEAD1, cgi.shWestHat1, darkena(cs.swordcolor, 1, 0XFF));
queuepoly(VHEAD2, cgi.shWestHat2, darkena(cs.swordcolor, 0, 0XFF));
}
if(cheater && !autocheat) {
queuepoly(VHEAD1, (cs.charid&1) ? cgi.shGoatHead : cgi.shDemon, darkena(0xFFFF00, 0, 0xFF));
// queuepoly(V, shHood, darkena(0xFF00, 1, 0xFF));
}
else if(id == pshRatling) {
queuepoly(VHEAD, cgi.shRatHead, fc(500, cs.haircolor, 1));
queuepoly(VHEAD, cgi.shWolf1, cs.eyecolor);
queuepoly(VHEAD, cgi.shWolf2, cs.eyecolor);
queuepoly(VHEAD, cgi.shWolf3, darkena(0x202020, 0, 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));
}
humanoid_eyes(V, cs.eyecolor, cs.skincolor);
#if CAP_COMPLEX2
if(camelot::knighted)
queuepoly(VBODY * VBS, id == pshRatling ? cgi.shRatCape1 : cgi.shKnightCloak, darkena(cloakcolor(camelot::knighted), 1, 0xFF));
#endif
if(tortoise::seek())
tortoise::draw(VBODY * VBS * ypush(-cgi.crossf*.25), tortoise::seekbits, 4, 0);
}
EX void drawPlayer(eMonster m, cell *where, const shiftmatrix& V, color_t col, double footphase, bool stop IS(false)) {
charstyle& cs = getcs();
if(GDIM == 3) {
addradar(V, '@', cs.uicolor >> 8, 0xFF00FF00);
}
if(mapeditor::drawplayer && !mapeditor::drawUserShape(V, mapeditor::sgPlayer, cs.charid, cs.skincolor, where)) {
auto id = ePlayershape(cs.charid >> 1);
if(playershapes[id].is_animal) drawPlayer_animal(m, where, V, col, footphase, stop);
else if(playershapes[id].is_humanoid) drawPlayer_humanoid(m, where, V, col, footphase, stop);
else if(cs.charid >= 10) {
ShadowV(V, cgi.shSpaceship);
queuepoly(VBODY, cgi.shSpaceshipBase, fc(150, cs.skincolor, 4));
queuepoly(VBODY, cgi.shSpaceshipCockpit, fc(150, cs.eyecolor, 4));
queuepoly(VBODY, cgi.shSpaceshipGun, fc(150, cs.dresscolor, 4));
queuepoly(VBODY, cgi.shSpaceshipEngine, fc(150, cs.haircolor, 4));
queuepoly(VBODY * lmirror(), cgi.shSpaceshipGun, fc(150, cs.dresscolor, 4));
queuepoly(VBODY * lmirror(), cgi.shSpaceshipEngine, fc(150, cs.haircolor, 4));
}
}
}
EX void drawMimic(eMonster m, cell *where, const shiftmatrix& V, color_t col, double footphase) {
charstyle& cs = getcs();
if(mapeditor::drawUserShape(V, mapeditor::sgPlayer, cs.charid, darkena(col, 0, 0x80), where)) return;
if(cs.charid >= 10) {
ShadowV(V, cgi.shSpaceship);
queuepoly(VBODY, cgi.shSpaceshipBase, darkena(col, 0, 0xC0));
queuepoly(VBODY, cgi.shSpaceshipCockpit, darkena(col, 0, 0xC0));
queuepoly(VBODY, cgi.shSpaceshipGun, darkena(col, 0, 0xC0));
queuepoly(VBODY, cgi.shSpaceshipEngine, darkena(col, 0, 0xC0));
queuepoly(VBODY * lmirror(), cgi.shSpaceshipGun, darkena(col, 0, 0xC0));
queuepoly(VBODY * lmirror(), cgi.shSpaceshipEngine, darkena(col, 0, 0xC0));
}
else if(cs.charid >= 8) {
queuepoly(VABODY, cgi.shWolfBody, darkena(col, 0, 0xC0));
ShadowV(V, cgi.shWolfBody);
if(mmspatial || footphase)
animallegs(VALEGS, moWolf, darkena(col, 0, 0xC0), footphase);
else
queuepoly(VABODY, cgi.shWolfLegs, darkena(col, 0, 0xC0));
queuepoly(VABODY, cgi.shFamiliarHead, darkena(col, 0, 0xC0));
queuepoly(VAHEAD, cgi.shFamiliarEye, darkena(col, 0, 0xC0));
queuepoly(VAHEAD * lmirror(), cgi.shFamiliarEye, darkena(col, 0, 0xC0));
}
else if(cs.charid >= 6) {
ShadowV(V, cgi.shDogBody);
queuepoly(VAHEAD, cgi.shDogHead, darkena(col, 0, 0xC0));
if(mmspatial || footphase) {
animallegs(VALEGS, moRunDog, darkena(col, 0, 0xC0), footphase);
queuepoly(VABODY, cgi.shDogTorso, darkena(col, 0, 0xC0));
}
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));
}
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));
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));
}
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;
}
shiftmatrix VWPN1 = VWPN, VWPN2 = VWPN;
if(GDIM == 3) { ld h = cgi.human_height; VWPN1 = VWPN * lzpush(-h/60); VWPN2 = VWPN * lzpush(-h/30); }
if(ti <= 1) queuepoly(VWPN1, cgi.shCrossbowstringLoaded, col1);
else if(ti <= 2) queuepoly(VWPN1, cgi.shCrossbowstringSemiloaded, col1);
else queuepoly(VWPN1, cgi.shCrossbowstringUnloaded, col1);
if(ti == 0) queuepoly(VWPN2, cgi.shCrossbowBolt, col1);
}
else if(!shmup::on) {
bool emp = items[itOrbEmpathy] && m != moShadow;
if(items[itOrbThorns] && emp)
queuepoly(VBODY * VBS, cgi.shHedgehogBladePlayer, darkena(col, 0, 0x40));
if(items[itOrbSide1] && !shmup::on)
queuepoly(VBODY * VBS * spin(-15._deg), cs.charid >= 2 ? cgi.shSabre : cgi.shPSword, darkena(col, 0, 0x40));
if(items[itOrbSide3] && emp)
queuepoly(VBODY * VBS, (cs.charid&1) ? cgi.shFerocityF : cgi.shFerocityM, darkena(col, 0, 0x40));
shiftmatrix VWPN = cs.lefthanded ? VBODY * VBS * lmirror() : VBODY * VBS;
queuepoly(VWPN, (cs.charid >= 2 ? cgi.shSabre : cgi.shPSword), darkena(col, 0, 0XC0));
}
else if(!where || shmup::curtime >= shmup::getPlayer()->nextshot)
queuepoly(VBODY * VBS, cgi.shPKnife, darkena(col, 0, 0XC0));
#if CAP_COMPLEX2
if(camelot::knighted)
queuepoly(VBODY3 * VBS, cgi.shKnightCloak, darkena(col, 1, 0xC0));
#endif
queuepoly(VHEAD1, (cs.charid&1) ? cgi.shFemaleHair : cgi.shPHead, darkena(col, 1, 0XC0));
queuepoly(VHEAD, cgi.shPFace, darkena(col, 0, 0XC0));
if(cs.charid&1)
queuepoly(VBODY1 * VBS, cgi.shFemaleDress, darkena(col, 1, 0XC0));
if(cs.charid == 2)
queuepoly(VBODY2 * VBS, cgi.shPrinceDress, darkena(col, 1, 0XC0));
if(cs.charid == 3)
queuepoly(VBODY2 * VBS, cgi.shPrincessDress, darkena(col, 1, 0XC0));
humanoid_eyes(V, 0xFF, darkena(col, 0, 0x40));
}
}
EX void draw_movement_arrows(cell *c, const transmatrix& V, int df) {
if(viewdists) return;
string keylist = "";
const ld keysize = .6;
color_t col = getcs().uicolor;
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));
// 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);
if((c->type & 1) && (isStunnable(c->monst) || isPushable(c->wall))) {
transmatrix Centered = rgpushxto0(unshift(tC0(cwtV)));
int sd = md.subdir;
if(keybd_subdir_enabled) sd = keybd_subdir;
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 * mapfontscale / 100, s0 + key, col >> 8, 1);
else
queuepoly(shiftless(T), cgi.shArrow, col);
}
else if(!confusingGeometry()) break;
}
}
if(keylist != "") queuestr(shiftless(V), keysize * mapfontscale / 100, keylist, col >> 8, 1);
}
EX void drawmovestar(double dx, double dy) {
DEBBI(DF_GRAPH, ("draw movestar"));
if(viewdists) return;
if(GDIM == 3) return;
if(!playerfound) return;
if(shmup::on) return;
#if CAP_RUG
if(rug::rugged && multi::players == 1 && !multi::alwaysuse) return;
#endif
shiftpoint H = cwtV * tile_center();
ld R = sqrt(H[0] * H[0] + H[1] * H[1]);
shiftmatrix Centered;
if(euclid)
Centered = shiftless(eupush(H.h));
else if(R > 1e-9) Centered = rgpushxto0(H);
else Centered = shiftless(Id);
Centered = Centered * rgpushxto0(hpxy(dx*5, dy*5));
if(multi::cpid >= 0) multi::crosscenter[multi::cpid] = Centered;
int rax = vid.axes;
if(rax == 1) rax = drawstaratvec(dx, dy) ? 2 : 0;
if(rax == 0 || vid.axes >= 4) return;
int starcol = getcs().uicolor;
ignore(starcol);
if(0);
#if CAP_SHAPES
else if(vid.axes == 3)
queuepoly(Centered, cgi.shMovestar, starcol);
#endif
else for(int d=0; d<8; d++) {
#if CAP_QUEUE
color_t col = starcol;
#if ISPANDORA
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;
#endif
queueline(tC0(Centered), Centered * xspinpush0(d * 45._deg, cgi.scalefactor/2), col, 3 + vid.linequality);
#endif
}
}
}

555
graph-wall.cpp Normal file
View File

@@ -0,0 +1,555 @@
// Hyperbolic Rogue -- terrain graphics file
// Copyright (C) 2011-2025 Zeno Rogue, see 'hyper.cpp' for details
#include "hyper.h"
namespace hr {
EX bool camelotcheat;
EX colortable minecolors = {
0xFFFFFF, 0xF0, 0xF060, 0xF00000,
0x60, 0x600000, 0x00C0C0, 0x000000, 0x808080, 0xFFD500
};
EX colortable distcolors = {
0xFFFFFF, 0xF0, 0xF060, 0xF00000,
0xA0A000, 0xA000A0, 0x00A0A0, 0xFFD500
};
EX const char* minetexts[8] = {
"No mines next to you.",
"A mine is next to you!",
"Two mines next to you!",
"Three mines next to you!",
"Four mines next to you!",
"Five mines next to you!",
"Six mines next to you!",
"Seven mines next to you!"
};
EX map<cell*, int> fake_minecount;
EX int countMinesAround(cell *c) {
if(fake_minecount.count(c)) return fake_minecount[c];
int mines = 0;
for(cell *c2: adj_minefield_cells(c))
if(c2->wall == waMineMine)
mines++;
return mines;
}
EX transmatrix applyPatterndir(cell *c, const patterns::patterninfo& si) {
if(NONSTDVAR || bt::in() || cgi.emb->is_euc_in_noniso()) return Id;
transmatrix V = ddspin180(c, si.dir);
if(si.reflect) V = V * lmirror();
if(euclid) return V;
return V * iddspin180(c, 0);
}
EX transmatrix applyDowndir(cell *c, const cellfunction& cf) {
return ddspin180(c, patterns::downdir(c, cf));
}
EX color_t reptilecolor(cell *c) {
int i;
if(arcm::in())
i = c->master->rval0 & 3;
else {
i = zebra40(c);
if(!euclid) {
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;
}
}
color_t reptilecolors[4] = {0xe3bb97, 0xc2d1b0, 0xebe5cb, 0xA0A0A0};
return reptilecolors[i];
}
// 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 };
color_t floorcolors[landtypes];
EX void init_floorcolors() {
for(int i=0; i<landtypes; i++)
floorcolors[i] = linf[i].color;
floorcolors[laDesert] = 0xEDC9AF;
floorcolors[laKraken] = 0x20A020;
floorcolors[laDocks] = 0x202020;
floorcolors[laCA] = 0x404040;
floorcolors[laMotion] = 0xF0F000;
floorcolors[laGraveyard] = 0x107010;
floorcolors[laWineyard] = 0x006000;
floorcolors[laLivefjord] = 0x306030;
floorcolors[laMinefield] = 0x80A080;
floorcolors[laCaribbean] = 0x006000;
floorcolors[laAlchemist] = 0x202020;
floorcolors[laRlyeh] = 0x004080;
floorcolors[laHell] = 0xC00000;
floorcolors[laCrossroads] = 0xFF0000;
floorcolors[laJungle] = 0x008000;
floorcolors[laZebra] = 0xE0E0E0;
floorcolors[laCaves] = 0x202020;
floorcolors[laEmerald] = 0x202020;
floorcolors[laDeadCaves] = 0x202020;
floorcolors[laPalace] = 0x806020;
floorcolors[laHunting] = 0x40E0D0 / 2;
floorcolors[laBlizzard] = 0x5050C0;
floorcolors[laCocytus] = 0x80C0FF;
floorcolors[laIce] = 0x8080FF;
floorcolors[laCamelot] = 0xA0A0A0;
floorcolors[laOvergrown] = 0x00C020;
floorcolors[laClearing] = 0x60E080;
floorcolors[laHaunted] = 0x609F60;
floorcolors[laCursed] = 0x481848;
floorcolors[laDice] = 0xC0C0FF;
floorcolors[laMirror] = floorcolors[laMirrorWall] = floorcolors[laMirrorOld] = 0x808080;
}
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;
}
bool noAdjacentChasms(cell *c) {
forCellEx(c2, c) if(c2->wall == waChasm) return false;
return true;
}
#if CAP_SHAPES
EX void floorShadow(cell *c, const shiftmatrix& V, color_t col) {
if(model_needs_depth() || noshadow)
return; // shadows break the depth testing
dynamicval<color_t> p(poly_outline, OUTLINE_TRANS);
if(qfi.shape) {
queuepolyat(V * qfi.spin * cgi.shadowmulmatrix, *qfi.shape, col, PPR::WALLSHADOW);
}
else if(qfi.usershape >= 0)
mapeditor::drawUserShape(V * qfi.spin * cgi.shadowmulmatrix, mapeditor::sgFloor, qfi.usershape, col, c, PPR::WALLSHADOW);
else
draw_shapevec(c, V, qfi.fshape->shadow, col, PPR::WALLSHADOW);
}
EX bool use_warp_graphics() {
if(shmup::on) return false;
if(geosupport_football() != 2) return false;
if(ls::chaoticity() >= 75) return false;
return true;
}
EX void escherSidewall(cell *c, SIDE sidepar, const shiftmatrix& V, color_t col) {
if(sidepar >= SIDE::RED1 && sidepar <= SIDE::RED3) {
int sl = int(sidepar) - int(SIDE::RED1);
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::RED1_ESCHER+3*sl);
}
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::WALL_ESCHER);
}
else if(sidepar == SIDE::FLOOR) {
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_ESCHER);
}
else if(sidepar == SIDE::WATERLEVEL) {
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_shallow, -vid.lake_top, z, layers)), col, PPR::WATERLEVEL_ESCHER);
}
else if(sidepar == SIDE::SHALLOW) {
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_shallow, z, layers)), col, PPR::SHALLOW_ESCHER);
}
else if(sidepar == SIDE::DEEP) {
const int layers = 1 << detaillevel;
draw_qfi(c, orthogonal_move_fol(V, cgi.INFDEEP), col, PPR::MINUSINF);
for(int z=layers-1; z>1; z--)
draw_qfi(c, orthogonal_move_fol(V, zgrad0(-vid.lake_bottom, -vid.lake_top, -z, 1)), col, PPR::DEEP_ESCHER);
}
}
EX bool use_escher(SIDE sidepar) {
return (!qfi.fshape || !qfi.fshape->is_plain || !cgi.validsidepar[sidepar] || qfi.usershape >= 0) && (GDIM == 2);
}
EX bool placeSidewall(cell *c, int i, SIDE sidepar, const shiftmatrix& V, color_t col) {
if(use_escher(sidepar)) {
escherSidewall(c, sidepar, V, col);
return true;
}
if(!qfi.fshape) return true;
if(qfi.fshape == &cgi.shBigTriangle && pseudohept(c->move(i))) return false;
if(qfi.fshape == &cgi.shTriheptaFloor && !pseudohept(c) && !pseudohept(c->move(i))) return false;
PPR prio = side_to_prio[sidepar];
if((col & 255) < 255) prio = PPR::TRANSPARENT_WALL;
dynamicval<bool> ncor(approx_nearcorner, true);
#if CAP_ARCM
if(arcm::in() && !PURE)
i = gmod(i + arcm::parent_index_of(c->master)/DUALMUL, c->type);
#endif
if(currentmap->strict_tree_rules()) {
i = rulegen::get_arb_dir(c, i);
}
if(int(sidepar) >= SIDEPARS) {
println(hlog, "ERROR: sidepar >= SIDEPARS: ", make_pair(int(sidepar), SIDEPARS));
return false;
}
if(i >= isize(qfi.fshape->side[sidepar])) {
println(hlog, "ERROR: i >= side[sidepar]", make_tuple(int(sidepar), i, isize(qfi.fshape->side[sidepar])));
return false;
}
draw_shapevec(c, V, qfi.fshape->side[sidepar][i], col, prio);
return false;
}
#endif
EX int mine_opacity = 255;
EX bool isWall3(cell *c, color_t& wcol) {
if(c->wall == waRose) { wcol = gradient(0, wcol, -5 - 5 * (7-rosephase), sintick(50 * (8 - rosephase)), 1); }
if(isWall(c)) return true;
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;
if(among(c->wall, waMirror, waCloud)) return true;
if(among(c->wall, waMineUnknown, waMineMine) && mine_opacity == 255) return true;
return false;
}
EX bool isWall3(cell *c) { color_t dummy; return isWall3(c, dummy); }
EX bool isSulphuric(eWall w) { return among(w, waSulphur, waSulphurC); }
// '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];
}
EX color_t transcolor(cell *c, cell *c2, color_t wcol) {
color_t dummy;
if(isWall3(c2, dummy)) return 0;
if(c->land != c2->land && c->land != laNone && c2->land != laNone) {
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);
}
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;
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);
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 = int(get_spatial_info(c).top) - int(get_spatial_info(c2).top);
if(l > 0) return darkena3(floorcolors[laRedRock], 0, 0x30 * l);
}
if(among(c->wall, waRubble, waDeadfloor2) && !among(get_spatial_info(c2).top, SIDE::RED1, SIDE::RED2, SIDE::RED3)) 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);
if(among(c->wall, waMineUnknown, waMineMine) && !among(c2->wall, waMineMine, waMineUnknown) && mine_opacity > 0 && mine_opacity < 255)
return 0xFFFFFF00 | mine_opacity;
return 0;
}
EX bool no_darken = false;
// how much should be the d-th wall darkened in 3D
EX int get_darkval(cell *c, int d) {
if(no_darken) return 0;
if(mhybrid) {
return d >= c->type - 2 ? 4 : 0;
}
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};
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};
const int darkval_nil[8] = {6,6,0,3,6,6,0,3};
const int darkval_nih[11] = {0,2,0,2,4,6,6,6,6,6,6};
const int darkval_ot[8] = {0,1,2,3,6,5,4,3};
#if MAXMDIM >= 4
if(among(variation, eVariation::dual_subcubes, eVariation::bch, eVariation::bch_oct, eVariation::coxeter)) {
int v = reg3::get_face_vertex_count(c, d);
return v-3;
}
#endif
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];
if(geometry == gHoroHex) return darkval_hh[d];
if(geometry == gHoroRec) return darkval_hrec[d];
if(geometry == gOctTet3) return darkval_ot[d + (shvid(c) == 2 ? 4 : 0)];
if(kite::in()) return darkval_kite[d];
if(asonov::in()) return darkval_arnold[d];
if(sol) return darkval_sol[d];
if(nih) return darkval_nih[d];
if(bt::in()) return darkval_hbt[d];
if(hyperbolic && S7 == 6) return darkval_e6[d];
if(hyperbolic && S7 == 12) return darkval_s12[d];
if(nil) return darkval_nil[d];
return 0;
}
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;
cell *c1 = mhybrid ? hybrid::get_where(c).first : c;
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);
T = lspintox(T * h2) * T;
w = T * C0;
w[1] = -w[1];
w = iso_inverse(T) * w;
};
if(mproduct) PIU(f());
else f();
ss.walltester.push_back(w);
}
if(mhybrid || WDIM == 2) {
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)});
ss.angle_of_zero = -PIU(atan2(currentmap->adj(c1, 0)*C0));
for(int a: {0,1}) {
vector<hyperpoint> l;
int z = a ? 1 : -1;
hyperpoint ctr = zpush0(z * cgi.plevel/2);
int qty = (mproduct || WDIM == 2) ? 1 : 3;
for(int i=0; i<c1->type; i++)
if(qty == 1)
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());
if(a == 1) std::rotate(l.begin(), l.begin()+qty, l.end());
ss.faces.push_back(l);
}
ss.compute_hept();
return ss;
}
int hrmap::wall_offset(cell *c) {
int id = currentmap->full_shvid(c);
if(WDIM == 3 && !mhybrid && !reg3::in() && geometry != gOctTet3) return 0;
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));
rk_shape = &ss;
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;
if(in_perspective())
poly.subprio = int(hdist0(h) * 100000);
else {
hyperpoint h2;
applymodel(h, h2);
poly.subprio = int(h2[2] * 100000);
}
}
#if MAXMDIM >= 4
EX int ceiling_category(cell *c) {
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;
case laPower:
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:
case laDragon:
case laSnakeNest:
case laDocks:
case laKraken:
case laBrownian:
case laHell:
case laVariant:
case laFrog:
case laWet:
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:
case laEclectic:
return 3;
case laCanvas:
if(canvas_default_wall == waInvisibleFloor) return 0;
return 3;
case laPalace:
case laPrincessQuest:
default:
return 4;
case laRuins:
return 6;
case laTemple:
case laRlyeh:
return 7;
}
}
#endif
}

4338
graph.cpp

File diff suppressed because it is too large Load Diff

View File

@@ -113,6 +113,11 @@
#include "hypgraph.cpp"
#include "textures.cpp"
#include "graph.cpp"
#include "graph-player.cpp"
#include "animations.cpp"
#include "graph-wall.cpp"
#include "graph-item.cpp"
#include "graph-monster.cpp"
#include "celldrawer.cpp"
#include "sky.cpp"
#include "blizzard.cpp"

View File

@@ -27,6 +27,26 @@ EX int SDL_GetTicks() {
#endif
#endif
#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());
}
#endif
EX long double sqr(long double x) { return x*x; }
EX ld round_nearest(ld x) { if(x > 0) return int(x+.5); else return -int(.5-x); }