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:
542
animations.cpp
Normal file
542
animations.cpp
Normal 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
508
graph-item.cpp
Normal 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
2001
graph-monster.cpp
Normal file
File diff suppressed because it is too large
Load Diff
742
graph-player.cpp
Normal file
742
graph-player.cpp
Normal 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
555
graph-wall.cpp
Normal 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
|
||||
|
||||
}
|
@@ -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"
|
||||
|
20
util.cpp
20
util.cpp
@@ -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); }
|
||||
|
Reference in New Issue
Block a user