1
0
mirror of https://github.com/zenorogue/hyperrogue.git synced 2025-08-26 15:32:19 +00:00
hyperrogue/graph-item.cpp

509 lines
20 KiB
C++

// 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;
}
}