mirror of
				https://github.com/zenorogue/hyperrogue.git
				synced 2025-10-31 14:02:59 +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
	 Zeno Rogue
					Zeno Rogue