mirror of
				https://github.com/zenorogue/hyperrogue.git
				synced 2025-10-30 21:42:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			969 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			969 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Hyperbolic Rogue -- main header file
 | |
| // Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
 | |
| 
 | |
| /** \file hyper.h
 | |
|  *  \brief The main header file of HyperRogue
 | |
|  *
 | |
|  *  Contains general utility macros, various value macros, using clauses for standard library functions,
 | |
|  *  implementation of the basic connection_table, walker, cell and heptagon classes,
 | |
|  *  and general routines which did not fit elsewhere
 | |
|  */
 | |
| 
 | |
| #ifndef _HYPER_H_
 | |
| #define _HYPER_H_
 | |
| 
 | |
| // version numbers
 | |
| #define VER "13.0z"
 | |
| #define VERNUM_HEX 0xAA1A
 | |
| 
 | |
| #include "sysconfig.h"
 | |
| 
 | |
| #include <stdarg.h>
 | |
| #include "hyper_function.h"
 | |
| 
 | |
| /** \brief the main namespace of HyperRogue */
 | |
| namespace hr {
 | |
| 
 | |
| /** \brief A helper structure that acts as a boolean which is always false. Helpful when disabling stuff with compiler flags. */
 | |
| struct always_false {
 | |
|   operator bool() const { return false; }
 | |
|   bool operator = (bool b) const { return b; }
 | |
|   };
 | |
| 
 | |
| /** \brief placate GCC's overzealous -Wunused-result */
 | |
| template<class T>
 | |
| void ignore(T&&) {
 | |
|   }
 | |
| 
 | |
| /** \brief a simple static_cast<void*> for use with printf("%p") */
 | |
| inline const void *voidp(const void *p) {
 | |
|   return p;
 | |
|   }
 | |
| 
 | |
| /** \brief Is the value of first parameter equal to one of the remaining parameters? */
 | |
| template<class T, class V, class... U> bool among(T x, V y) { return x == y; }
 | |
| template<class T, class V, class... U> bool among(T x, V y, U... u) { return x==y || among(x,u...); }
 | |
| 
 | |
| // functions and types used from the standard library
 | |
| using std::vector;
 | |
| using std::map;
 | |
| using std::array;
 | |
| using std::sort;
 | |
| using std::multimap;
 | |
| using std::set;
 | |
| using std::string;
 | |
| using std::pair;
 | |
| using std::tuple;
 | |
| using std::shared_ptr;
 | |
| using std::make_shared;
 | |
| using std::min;
 | |
| using std::max;
 | |
| using std::make_pair;
 | |
| using std::tie;
 | |
| using std::queue;
 | |
| using std::swap;
 | |
| using std::complex;
 | |
| using std::reverse;
 | |
| using std::real;
 | |
| using std::imag;
 | |
| using std::stable_sort;
 | |
| using std::out_of_range;
 | |
| using std::get;
 | |
| using std::move;
 | |
| using std::make_tuple;
 | |
| using std::unique_ptr;
 | |
| 
 | |
| using std::abs;
 | |
| using std::isfinite;
 | |
| using std::isnan;
 | |
| using std::isinf;
 | |
| using std::log;
 | |
| using std::exp;
 | |
| using std::sin;
 | |
| using std::cos;
 | |
| using std::sinh;
 | |
| using std::asin;
 | |
| using std::acos;
 | |
| using std::tan;
 | |
| using std::atan;
 | |
| using std::atan2;
 | |
| using std::tanh;
 | |
| using std::sqrt;
 | |
| using std::pow;
 | |
| using std::floor;
 | |
| using std::ceil;
 | |
| #ifndef NO_STD_HYPOT
 | |
| using std::hypot;
 | |
| using std::asinh;
 | |
| using std::acosh;
 | |
| #endif
 | |
| 
 | |
| struct hr_exception : std::runtime_error {
 | |
|     explicit hr_exception() : std::runtime_error("hr_exception") {}
 | |
|     explicit hr_exception(const std::string& s) : std::runtime_error(s.c_str()) {}
 | |
| };
 | |
| 
 | |
| struct hr_shortest_path_exception { };
 | |
| 
 | |
| // genus (in grammar)
 | |
| #define GEN_M 0
 | |
| #define GEN_F 1
 | |
| #define GEN_N 2
 | |
| #define GEN_O 3
 | |
| #define GEN_BASE_MASK   7
 | |
| 
 | |
| #define GENF_ELISION    (1 << 3)
 | |
| #define GENF_PLURALONLY (1 << 4)
 | |
| #define GENF_PROPER     (1 << 5)
 | |
| 
 | |
| // Add a message to the GUI.
 | |
| // If multiple messages appear with the same spamtype != 0, the older ones disappear quickly
 | |
| void addMessage(string s, char spamtype = 0);
 | |
| 
 | |
| // geometry-dependent constants
 | |
| 
 | |
| #define cginf ginf[geometry]
 | |
| 
 | |
| #define S7 cginf.sides
 | |
| #define S3 cginf.vertex
 | |
| #define hyperbolic_37 (S7 == 7 && S3 == 3 && !bt::in() && !arcm::in())
 | |
| #define hyperbolic_not37 ((S7 > 7 || S3 > 3 || bt::in() || arcm::in()) && hyperbolic)
 | |
| #define weirdhyperbolic ((S7 > 7 || S3 > 3 || !STDVAR || bt::in() || arcm::in() || arb::in()) && hyperbolic)
 | |
| #define stdhyperbolic (S7 == 7 && S3 == 3 && STDVAR && !bt::in() && !arcm::in() && !arb::in())
 | |
| 
 | |
| #define cgflags cginf.flags 
 | |
| 
 | |
| #define cryst (cgflags & qCRYSTAL)
 | |
| 
 | |
| /** convenience flag for geometries with major aspects missing */
 | |
| #define experimental (cgflags & qEXPERIMENTAL)
 | |
| 
 | |
| // these geometries do not feature alternate structures for horocycles
 | |
| #define eubinary (euclid || bt::in() || cryst || nil)
 | |
| 
 | |
| #define cgclass (cginf.cclass)
 | |
| #define euclid (cgclass == gcEuclid)
 | |
| #define sphere (cgclass == gcSphere)
 | |
| #define sol (among(cgclass, gcSol, gcSolN))
 | |
| #define nih (among(cgclass, gcNIH, gcSolN))
 | |
| #define nil (cgclass == gcNil)
 | |
| #define sl2 (cgclass == gcSL2)
 | |
| #define gtwisted (geometry == gTwistedProduct)
 | |
| #define hyperbolic (cgclass == gcHyperbolic)
 | |
| #define nonisotropic (among(cgclass, gcSol, gcSolN, gcNIH, gcSL2, gcNil))
 | |
| #define translatable (euclid || nonisotropic)
 | |
| #define nonorientable (cgflags & qNONORIENTABLE)
 | |
| #define elliptic (cgflags & qELLIPTIC)
 | |
| #define quotient (cgflags & qANYQ)
 | |
| #define smallbounded ((cgflags & qSMALL) || disksize)
 | |
| #define closed_manifold (cgflags & qCLOSED)
 | |
| #define closed_or_bounded (closed_manifold || disksize)
 | |
| /** the actual map has hybrid geometry, not just the graphics */
 | |
| #define mhybrid (cgflags & qHYBRID)
 | |
| /** graphics based on a product geometry -- either embedded or actual hybrid product */
 | |
| #define gproduct (cgclass == gcProduct)
 | |
| 
 | |
| /** 2D geometry embedded in 3D */
 | |
| #define embedded_plane (WDIM == 2 && GDIM == 3)
 | |
| /** the actual map is product, not just the graphics */
 | |
| #define mproduct (gproduct && !embedded_plane)
 | |
| /** the actual map is twisted, not just the graphics */
 | |
| #define mtwisted (gtwisted && !embedded_plane)
 | |
| /** the actual map is product, not just the graphics */
 | |
| #define meuclid (geom3::mgclass() == gcEuclid)
 | |
| #define msphere (geom3::mgclass() == gcSphere)
 | |
| #define mhyperbolic (geom3::mgclass() == gcHyperbolic)
 | |
| 
 | |
| // Dry Forest burning, heat transfer, etc. are performed on the whole universe
 | |
| #define doall (closed_or_bounded)
 | |
| 
 | |
| #define sphere_narcm (sphere && !arcm::in())
 | |
| 
 | |
| #define a4 (S3 == 4)
 | |
| #define a45 (S3 == 4 && S7 == 5)
 | |
| #define a46 (S3 == 4 && S7 == 6)
 | |
| #define a47 (S3 == 4 && S7 == 7)
 | |
| #define a457 (S3 == 4 && S7 != 6)
 | |
| #define a467 (S3 == 4 && S7 >= 6)
 | |
| #define a38 (S3 == 3 && S7 == 8)
 | |
| #define sphere4 (sphere && S7 == 4)
 | |
| #define stdeuc (geometry == gNormal || geometry == gEuclid || geometry == gEuclidSquare)
 | |
| #define smallsphere (sphere_narcm && S7 < 5)
 | |
| #define bigsphere (sphere_narcm && S7 == 5)
 | |
| 
 | |
| #define S6 (S3*2)
 | |
| #define MAX_S3 4
 | |
| 
 | |
| #define SG6 (S3==3?6:4)
 | |
| #define SG3 (S3==3?3:2)
 | |
| #define SG2 (S3==3?2:1)
 | |
| 
 | |
| #define GOLDBERG_INV (GOLDBERG || INVERSE)
 | |
| 
 | |
| #define INVERSE among(variation, eVariation::unrectified, eVariation::warped, eVariation::untruncated )
 | |
| 
 | |
| #define aperiodic (cgflags & qAPERIODIC)
 | |
| #define UNRECTIFIED (variation == eVariation::unrectified)
 | |
| #define WARPED (variation == eVariation::warped)
 | |
| #define UNTRUNCATED (variation == eVariation::untruncated)
 | |
| 
 | |
| #define GOLDBERG (variation == eVariation::goldberg)
 | |
| #define IRREGULAR (variation == eVariation::irregular)
 | |
| #define PURE (variation == eVariation::pure)
 | |
| #define BITRUNCATED (variation == eVariation::bitruncated)
 | |
| #define DUAL (variation == eVariation::dual)
 | |
| #define DUALMUL (DUAL ? 2 : 1)
 | |
| 
 | |
| #define CHANGED_VARIATION (variation != cginf.default_variation)
 | |
| 
 | |
| #define STDVAR (PURE || BITRUNCATED)
 | |
| #define NONSTDVAR (!STDVAR)
 | |
| 
 | |
| #define VALENCE current_valence()
 | |
| 
 | |
| #define NUMWITCH 7
 | |
| 
 | |
| #if ISMOBILE || ISWEB || ISPANDORA || 1
 | |
| typedef double ld;
 | |
| #define LDF "%lf"
 | |
| #define PLDF "lf"
 | |
| #else
 | |
| typedef long double ld;
 | |
| #define LDF "%Lf"
 | |
| #define PLDF "Lf"
 | |
| #endif
 | |
| 
 | |
| typedef complex<ld> cld;
 | |
| 
 | |
| typedef unsigned color_t;
 | |
| 
 | |
| struct charstyle {
 | |
|   int charid;
 | |
|   color_t skincolor, haircolor, dresscolor, swordcolor, dresscolor2, uicolor, eyecolor, bowcolor, bowcolor2;
 | |
|   bool lefthanded;
 | |
|   };
 | |
| 
 | |
| enum eStereo { sOFF, sAnaglyph, sLR, sODS, sPanini, sStereographic, sEquirectangular, sCylindrical };
 | |
| 
 | |
| enum eModel : int;
 | |
| 
 | |
| /** configuration of the projection */
 | |
| struct projection_configuration {
 | |
|   eModel model;            /**< which projection, see classes.cpp */
 | |
|   ld xposition, yposition; /**< move the center to another position */
 | |
|   ld scale, alpha, fisheye_param, fisheye_alpha, twopoint_param, axial_angle, stretch, ballproj, euclid_to_sphere;
 | |
|   ld clip_min, clip_max;
 | |
|   ld halfplane_scale;  
 | |
|   ld collignon_parameter; 
 | |
|   ld offside, offside2;
 | |
|   ld aitoff_parameter, miller_parameter, loximuthal_parameter, winkel_parameter;
 | |
|   bool show_hyperboloid_flat;
 | |
|   bool small_hyperboloid;
 | |
|   bool collignon_reflected;
 | |
|   string formula;
 | |
|   eModel basic_model;
 | |
|   ld top_z;
 | |
|   ld model_transition;
 | |
|   ld spiral_angle;
 | |
|   ld spiral_x;
 | |
|   ld spiral_y;
 | |
|   bool use_atan;
 | |
|   ld right_spiral_multiplier;
 | |
|   ld any_spiral_multiplier;
 | |
|   ld sphere_spiral_multiplier;
 | |
|   ld spiral_cone;
 | |
|   ld skiprope;
 | |
|   ld product_z_scale;
 | |
|   ld rotational_nil;
 | |
|   
 | |
|   ld depth_scaling;
 | |
|   ld hyperboloid_scaling;
 | |
|   ld vr_angle, vr_zshift, vr_scale_factor;
 | |
|   bool dualfocus_autoscale;
 | |
| 
 | |
|   int back_and_front; /* 0 = do not, 1 = do, 2 = only back */
 | |
|   struct trans23 *ptr_model_orientation;
 | |
|   struct transmatrix *ptr_ball;
 | |
|   struct transmatrix *ptr_camera;
 | |
| 
 | |
|   projection_configuration();
 | |
| 
 | |
|   trans23& mori() { return *ptr_model_orientation; }
 | |
|   transmatrix& ball() { return *ptr_ball; }
 | |
|   transmatrix& cam() { return *ptr_camera; }
 | |
|   };
 | |
| 
 | |
| enum eThreatLevel { tlNoThreat, tlSpam, tlNormal, tlHighThreat };
 | |
| 
 | |
| constexpr ld use_the_default_value = -20.0625;
 | |
| 
 | |
| enum ePseudohedral { phOFF, phInscribed, phCircumscribed };
 | |
| 
 | |
| struct videopar {
 | |
|   projection_configuration projection_config, rug_config;
 | |
|   ld yshift;
 | |
|   ld sspeed, mspeed;
 | |
|   ld binary_width, fixed_facing_dir;
 | |
|   int mobilecompasssize;
 | |
|   int radarsize; // radar for 3D geometries
 | |
|   ld radarrange;
 | |
|   int aurastr, aurasmoothen;
 | |
|   bool fixed_facing;
 | |
|   bool fixed_yz;
 | |
|   bool use_wall_radar;
 | |
|   
 | |
|   int linequality;
 | |
| 
 | |
|   bool want_fullscreen;
 | |
|   bool full;
 | |
|   bool change_fullscr;
 | |
|   bool relative_window_size;
 | |
|   bool want_vsync;
 | |
|   bool current_vsync;
 | |
|   
 | |
|   int graphglyph; // graphical glyphs
 | |
|   bool darkhepta;
 | |
|   int shifttarget;
 | |
|   
 | |
|   int xres, yres, framelimit;
 | |
|   
 | |
|   int xscr, yscr;
 | |
|   
 | |
|   int fullscreen_x, fullscreen_y;
 | |
|   int window_x, window_y;
 | |
|   ld window_rel_x, window_rel_y;
 | |
|   
 | |
|   bool grid;
 | |
|   bool particles;
 | |
|   
 | |
|   bool relative_font;
 | |
|   int fsize, abs_fsize, fontscale;
 | |
|   int flashtime;
 | |
|   
 | |
|   int wallmode, monmode, axes, highlightmode, orbmode;
 | |
|   bool axes3;
 | |
|   bool revcontrol;
 | |
|   
 | |
|   int msgleft, msglimit;
 | |
| 
 | |
|   bool wantGL;
 | |
|   int want_antialias;
 | |
|   bool fineline;
 | |
|   
 | |
|   bool usingGL;
 | |
|   int antialias;
 | |
|   #define AA_NOGL      1
 | |
|   #define AA_VERSION   2
 | |
|   #define AA_LINES     4
 | |
|   #define AA_POLY      8
 | |
|   #define AA_FONT      32
 | |
|   #define AA_MULTI     64
 | |
|   #define AA_MULTI16   128 // not configurable
 | |
|   ld linewidth;
 | |
|   
 | |
|   ld multiplier_grid, multiplier_ring;
 | |
| 
 | |
|   int joyvalue, joyvalue2, joypanthreshold;
 | |
|   ld joypanspeed;
 | |
|   
 | |
|   charstyle cs;
 | |
|   
 | |
|   bool samegender; // same gender for the Princess?
 | |
|   int language;
 | |
|   
 | |
|   bool backeffects; // background particle effects
 | |
|   
 | |
|   int killreduction, itemreduction, portreduction;
 | |
|   
 | |
|   int steamscore;
 | |
|   bool drawmousecircle; // draw the circle around the mouse
 | |
|   bool skipstart;       // skip the start menu
 | |
|   bool quickmouse;      // quick mouse on the map
 | |
|   bool sloppy_3d;       // make 3D faster but ugly
 | |
|   int timeformat;       // time format used in the message log
 | |
|   
 | |
|   int use_smart_range;  // 0 = distance-based, 1 = model-based, 2 = model-based and generate
 | |
|   ld smart_range_detail;// minimum visible cell for modes 1 and 2
 | |
|   ld smart_range_detail_3;// minimum visible cell in 3D (for mode 2, there is no mode 1)
 | |
|   bool smart_area_based;// based on area or length?
 | |
|   int cells_drawn_limit;
 | |
|   int cells_generated_limit; // limit on cells generated per frame
 | |
|   
 | |
|   eStereo stereo_mode;
 | |
|   ld stereo_param;
 | |
|   ld ipd;
 | |
|   ld lr_eyewidth, anaglyph_eyewidth;
 | |
|   ld fov;
 | |
|   bool consider_shader_projection;
 | |
|   int desaturate;
 | |
|   int texture_step;
 | |
| 
 | |
|   
 | |
|   bool always3;  // always use the 3D engine
 | |
|   ld depth;      // world level below the plane
 | |
|   ld camera;     // camera level above the plane
 | |
|   ld wall_height, creature_scale, height_width;
 | |
|   ld lake_top, lake_bottom, lake_shallow, wall_height2, wall_height3;
 | |
|   ld lowsky_height, sky_height, star_height, infdeep_height, star_size, sun_size;
 | |
|   bool height_limits;
 | |
|   ld rock_wall_ratio;
 | |
|   ld human_wall_ratio;
 | |
|   ePseudohedral pseudohedral;
 | |
|   ld depth_bonus;   // to fiix the placement of 3D models in pseudogonal -- not working currently
 | |
| 
 | |
|   int tc_alpha, tc_depth, tc_camera;
 | |
|   ld highdetail, middetail;
 | |
|   bool gp_autoscale_heights;
 | |
|   
 | |
|   ld eye;
 | |
|   bool auto_eye;
 | |
| 
 | |
|   ld plevel_factor;
 | |
|   bool bubbles_special, bubbles_threshold, bubbles_all;
 | |
|   int joysmooth;
 | |
|   
 | |
|   eThreatLevel faraway_highlight; // draw attention to monsters on the horizon
 | |
|   int faraway_highlight_color; // 0 = monster color, 100 = red-green oscillation
 | |
| 
 | |
|   ld ispeed;
 | |
|   bool flasheffects;
 | |
|   };
 | |
| 
 | |
| extern videopar vid;
 | |
| 
 | |
| /** \brief How many dimensional is the gameplay. In the FPP mode of a 2D geometry, WDIM is 2 */
 | |
| #define WDIM cginf.g.gameplay_dimension
 | |
| /** \brief How many dimensional is the graphical representation. In the FPP mode of a 2D geometry, MDIM is 3 */
 | |
| #define GDIM cginf.g.graphical_dimension
 | |
| /** \brief How many dimensions of the matrix representation are used. It is usually 3 in 2D geometries (not FPP) and in product geometries, 4 in 3D geometries */
 | |
| #define MDIM (MAXMDIM == 3 ? 3 : cginf.g.homogeneous_dimension)
 | |
| /** \brief What dimension of matrices is used in loops (the 'extra' dimensions have values 0 or 1 as in Id)
 | |
|  *  Even if MDIM==3, it may be faster to keep 4x4 matrices and perform computations using them (rather than having another condition due to the variable loop size). 
 | |
|  *  The experiments on my computer show it to be the case, but the effect is not significant, and it may be different on another computer.
 | |
|  */
 | |
| #define MXDIM (CAP_MDIM_FIXED ? MAXMDIM : MDIM)
 | |
| /** \brief The 'homogeneous' dimension index */
 | |
| #define LDIM (MDIM-1)
 | |
| #define cclass g.kind
 | |
| 
 | |
| #define self (*this)
 | |
| 
 | |
| #define BUGCOLORS 3
 | |
| 
 | |
| #define big_unlock (inv::on && !ls::any_chaos())
 | |
| 
 | |
| // land completion for shared unlocking
 | |
| #define U5 (big_unlock ? 10 : 5)
 | |
| // land completion for advanced unlocking
 | |
| #define U10 (big_unlock ? 25 : 10)
 | |
| 
 | |
| // land completion
 | |
| #define R10 (big_unlock ? 50 : 10)
 | |
| // intermediate lands
 | |
| #define R30 (big_unlock ? 100 : 30)
 | |
| // advanced lands
 | |
| #define R60 (big_unlock ? 200 : 60)
 | |
| // advanced lands II
 | |
| #define R90 (big_unlock ? 300 : 90)
 | |
| // Crossroads IV
 | |
| #define R200 (big_unlock ? 800 : 200)
 | |
| // Crossroads V
 | |
| #define R300 (big_unlock ? 1200 : 300)
 | |
| // Thematic Crossroads
 | |
| #define R400 (big_unlock ? 1600 : 400)
 | |
| // Master Crossroads
 | |
| #define R500 (big_unlock ? 2000 : 500)
 | |
| // kill types for Dragon Chasms
 | |
| #define R20 (big_unlock ? 30 : 20)
 | |
| // kill count for Graveyard/Hive
 | |
| #define R100 (big_unlock ? 500 : 100)
 | |
| 
 | |
| // size casted to int, to prevent warnings and actual errors caused by the unsignedness of x.size()
 | |
| template<class T> int isize(const T& x) {return x.size(); }
 | |
| 
 | |
| // automatically growing vector
 | |
| template<class T> struct grow_vector : public vector<T> {
 | |
|   T& grow(size_t index) {
 | |
|     if(index >= this->size()) {
 | |
|       this->resize(index + 1);
 | |
|       }
 | |
|     return (vector<T>::operator[]) (index);
 | |
|     }
 | |
|   };
 | |
| 
 | |
| // game forward declarations
 | |
| 
 | |
| namespace anticheat { extern bool tampered; }
 | |
| #define HRANDMAX 0x7FFFFFFF
 | |
| 
 | |
| struct movedir { 
 | |
|   int d; 
 | |
|   // non-negative numbers denote 'rotate +d steps and act in this direction
 | |
|   // negative numbers have the following meanings (warning: not used consistently):
 | |
|   #define MD_WAIT (-1)
 | |
|   #define MD_DROP (-2)
 | |
|   #define MD_UNDECIDED (-3)
 | |
|   #define MD_USE_ORB (-4)
 | |
|   int subdir; // for normal movement (0+): turn left or right
 | |
|   struct cell *tgt;  // for MD_USE_ORB: target cell
 | |
|   };
 | |
| 
 | |
| // shmup
 | |
| 
 | |
| template<class T>
 | |
| class hookset {
 | |
|     std::map<int, std::function<T>> *map_ = nullptr;
 | |
| 
 | |
| public:
 | |
|     template<class U>
 | |
|     int add(int prio, U&& hook) {
 | |
|         if (map_ == nullptr) map_ = new std::map<int, std::function<T>>();
 | |
|         while (map_->count(prio)) {
 | |
|             prio++;
 | |
|         }
 | |
|         map_->emplace(prio, static_cast<U&&>(hook));
 | |
|         return prio;
 | |
|     }
 | |
| 
 | |
|     void del(int prio) {
 | |
|         map_->erase(prio);
 | |
|         }
 | |
| 
 | |
|     template<class... U>
 | |
|     void callhooks(U&&... args) const {
 | |
|         if (map_ == nullptr) return;
 | |
|         for (const auto& p : *map_) {
 | |
|             p.second(static_cast<U&&>(args)...);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     template<class V, class... U>
 | |
|     V callhandlers(V zero, U&&... args) const {
 | |
|         if (map_ == nullptr) return zero;
 | |
|         for (const auto& p : *map_) {
 | |
|             auto z = p.second(static_cast<U&&>(args)...);
 | |
|             if (z != zero) return z;
 | |
|         }
 | |
|         return zero;
 | |
|     }
 | |
| };
 | |
| 
 | |
| using purehookset = hookset<void()>;
 | |
| 
 | |
| static constexpr int NOHINT = -1;
 | |
| 
 | |
| typedef function<void()> reaction_t;
 | |
| typedef function<bool()> bool_reaction_t;
 | |
| 
 | |
| void offer_choose_file(reaction_t r);
 | |
| 
 | |
| #define HELPFUN(x) (help_delegate = x, "HELPFUN")
 | |
| 
 | |
| typedef function<int(struct cell*)> cellfunction;
 | |
| 
 | |
| // passable flags
 | |
| 
 | |
| #define SAGEMELT .1
 | |
| #define PT(x, y) rebalance_treasure(x, y, c->land)
 | |
| #define ROCKSNAKELENGTH 50
 | |
| #define WORMLENGTH 15
 | |
| #define PRIZEMUL 7
 | |
| 
 | |
| #define INF  9999
 | |
| #define INFD 60
 | |
| #define PINFD 125
 | |
| #ifndef BARLEV
 | |
| #define BARLEV ((ISANDROID||ISIOS||ISFAKEMOBILE||getDistLimit()<7)?(getDistLimit()<4?8:9):10)
 | |
| #endif
 | |
| #define BUGLEV 15
 | |
| // #define BARLEV 9
 | |
| 
 | |
| #define YDIST 101
 | |
| #define MODECODES (1ll<<61)
 | |
|   
 | |
| #define GUNRANGE 3
 | |
| 
 | |
| // loops
 | |
| 
 | |
| #define fakecellloop(ct) for(cell *ct = (cell*)1; ct; ct=NULL)
 | |
| 
 | |
| #define forCellIdAll(ct, i, cf) fakecellloop(ct) for(int i=0; i<(cf)->type && (ct=(cf)->move(i),true); i++) 
 | |
| #define forCellIdCM(ct, i, cf)  fakecellloop(ct) for(int i=0; i<(cf)->type && (ct=createMov((cf),i),true); i++) 
 | |
| #define forCellIdEx(ct, i, cf)  forCellIdAll(ct,i,cf) if(ct)
 | |
| 
 | |
| #define forCellEx(ct, cf) forCellIdEx(ct,forCellEx ## __LINE__,cf)
 | |
| #define forCellCM(ct, cf) forCellIdCM(ct,forCellCM ## __LINE__,cf)
 | |
| #define forCellAll(ct, cf) forCellIdCM(ct,forCellAll ## __LINE__,cf)
 | |
| 
 | |
| // canAttack/moveval flags
 | |
| 
 | |
| #define AF_NORMAL            0          // nothing special about this attack
 | |
| 
 | |
| #define AF_TOUGH             Flag(0)    // tough attacks: Hyperbugs
 | |
| #define AF_MAGIC             Flag(1)    // magical attacks: Flash
 | |
| #define AF_STAB              Flag(2)    // stabbing attacks (usually ignored except Hedgehogs)
 | |
| #define AF_LANCE             Flag(3)    // lance attacks (used by Lancers)
 | |
| #define AF_ONLY_ENEMY        Flag(4)    // only say YES if it is an enemy
 | |
| #define AF_ONLY_FRIEND       Flag(5)    // only say YES if it is a friend
 | |
| #define AF_ONLY_FBUG         Flag(6)    // only say YES if it is a bug_or friend
 | |
| #define AF_BACK              Flag(7)    // backward attacks (ignored except Viziers and Flailers)
 | |
| #define AF_APPROACH          Flag(8)    // approach attacks (ignored except Lancers)
 | |
| #define AF_IGNORE_UNARMED    Flag(9)    // ignore the UNARMED flag
 | |
| #define AF_NOSHIELD          Flag(10)   // ignore the shielded status
 | |
| #define AF_GETPLAYER         Flag(11)   // check for player (replace m2 with moPlayer for player position)
 | |
| #define AF_GUN               Flag(12)   // revolver attack
 | |
| #define AF_FAST              Flag(13)   // fast attack
 | |
| #define AF_EAT               Flag(17)   // eating attacks from Worm-likes
 | |
| 
 | |
| #define MF_NOATTACKS         Flag(14)   // don't do any attacks
 | |
| #define MF_PATHDIST          Flag(15)   // consider pathdist for moveval
 | |
| #define MF_ONLYEAGLE         Flag(16)   // do this only for Eagles
 | |
| #define MF_MOUNT             Flag(18)   // don't do 
 | |
| #define MF_NOFRIEND          Flag(19)   // don't do it for friends
 | |
| 
 | |
| #define AF_SWORD             Flag(20)   // big sword
 | |
| #define AF_SWORD_INTO        Flag(21)   // moving into big sword
 | |
| #define AF_MSG               Flag(22)   // produce a message
 | |
| #define AF_MUSTKILL          Flag(23)   // when TRUE, stunning attacks are not accepted by canAttack
 | |
| #define AF_NEXTTURN          Flag(24)   // next turn -- don't count shield at power 1
 | |
| #define AF_FALL              Flag(25)   // death by falling
 | |
| #define MF_STUNNED           Flag(26)   // edgeunstable: ignore ladders (as stunned monsters do)
 | |
| #define MF_IVY               Flag(27)   // edgeunstable: ignore ivy (ivy cannot climb ivy)
 | |
| #define AF_HORNS             Flag(28)   // spear attack (always has APPROACH too)
 | |
| #define AF_BULL              Flag(29)   // bull attack
 | |
| #define AF_SIDE              Flag(30)   // side attack
 | |
| #define AF_CRUSH             Flag(31)   // Crusher's delayed attack
 | |
| #define AF_PLAGUE            Flag(32)   // Orb of Plague (do not check adjacency)
 | |
| #define AF_PSI               Flag(33)   // Orb of the Mind
 | |
| #define AF_WEAK              Flag(34)   // Curse of Weakness
 | |
| #define AF_BOW               Flag(35)   // crossbow attack
 | |
| 
 | |
| #if CAP_SDL
 | |
| 
 | |
| #if CAP_PNG
 | |
| #include "savepng.h"
 | |
| #define IMAGEEXT ".png"
 | |
| void IMAGESAVE(SDL_Surface *s, const char *fname);
 | |
| #else
 | |
| #define IMAGEEXT ".bmp"
 | |
| #define IMAGESAVE SDL_SaveBMP
 | |
| #endif
 | |
| 
 | |
| #endif
 | |
| 
 | |
| template<class T> struct dynamicval {
 | |
|   T& where;
 | |
|   T backup;
 | |
|   dynamicval(T& wh, T val) : where(wh) { backup = wh; wh = val; }
 | |
|   dynamicval(T& wh) : where(wh) { backup = wh; }
 | |
|   ~dynamicval() { where = backup; }
 | |
|   };
 | |
| 
 | |
| struct finalizer {
 | |
|   reaction_t f;
 | |
|   finalizer(reaction_t r) : f(r) {}
 | |
|   ~finalizer() { f(); }
 | |
|   };
 | |
|   
 | |
| static constexpr int MAXPLAYER = 7;
 | |
| 
 | |
| #define DEFAULTCONTROL (multi::players == 1 && !shmup::on && !multi::alwaysuse)
 | |
| #define DEFAULTNOR(sym) (DEFAULTCONTROL || multi::notremapped(sym))
 | |
| 
 | |
| #define CAP_MENUSCALING (ISPANDORA || ISMOBILE)
 | |
| 
 | |
| #if CAP_MENUSCALING
 | |
| #define displayfrZ dialog::zoom::displayfr
 | |
| #define displayfrZH dialog::zoom::displayfr_highlight
 | |
| #else
 | |
| #define displayfrZ displayfr
 | |
| #define displayfrZH dialog::zoom::displayfr_highlight
 | |
| #endif
 | |
| 
 | |
| // just in case if I change my mind about when Orbs lose their power
 | |
| #define ORBBASE 0
 | |
| 
 | |
| #define SHADOW_WALL 0x60
 | |
| #define SHADOW_SL   0x18
 | |
| #define SHADOW_MON  0x30
 | |
| 
 | |
| // ranks:
 | |
| enum class PPR {
 | |
|   ZERO, EUCLIDEAN_SKY, OUTCIRCLE, MOVESTAR,
 | |
|   MINUSINF,
 | |
|   DEEP_ESCHER, DEEP_SIDE, DEEP_FALLANIM, DEEP_TOP, HELLSPIKE,
 | |
|   SHALLOW_ESCHER, SHALLOW_SIDE, SHALLOW_FALLANIM, SHALLOW_TOP,
 | |
|   WATERLEVEL_ESCHER, WATERLEVEL_SIDE, WATERLEVEL_TOP, BOATLEV, BOATLEV2, BOATLEV3,
 | |
|   FLOOR_ESCHER, FLOOR_SIDE, FLOOR_FALLANIM, FLOOR_TOWER, FLOOR, FLOOR_DRAGON,
 | |
|   FLOORa, FLOORb, FLOORc, FLOORd,
 | |
|   LIZEYE,
 | |
|   BFLOOR,
 | |
|   GFLOORa, GFLOORb, GFLOORc,
 | |
|   WALLSHADOW,
 | |
|   STRUCT0, STRUCT1, STRUCT2, STRUCT3,
 | |
|   THORNS, WALL,
 | |
|   RED1_ESCHER, RED1_SIDE, RED1_TOP,
 | |
|   RED2_ESCHER, RED2_SIDE, RED2_TOP,
 | |
|   RED3_ESCHER, RED3_SIDE, RED3_TOP,
 | |
|   HEPTAMARK,
 | |
|   ITEM_BELOW,
 | |
|   ITEM, ITEMa, ITEMb,
 | |
|   BIGSTATUE,
 | |
| 
 | |
|   WALL_ESCHER, WALL_SIDE, WALL_TOP, WALL_DECO,
 | |
| 
 | |
|   HIDDEN, GIANTSHADOW,
 | |
|   TENTACLE0, TENTACLE1,
 | |
|   ONTENTACLE, ONTENTACLE_EYES, ONTENTACLE_EYES2,
 | |
|   MONSTER_SHADOW,
 | |
|   MONSTER_FOOT, MONSTER_LEG, MONSTER_GROIN,
 | |
|   MONSTER_SUBWPN, MONSTER_WPN,
 | |
|   MONSTER_BODY, MONSTER_ARMOR0, MONSTER_ARMOR1,
 | |
|   MONSTER_CLOAK, MONSTER_NECK,
 | |
|   MONSTER_HEAD, MONSTER_FACE, MONSTER_EYE0, MONSTER_EYE1,
 | |
|   MONSTER_HAIR, MONSTER_HAT0, MONSTER_HAT1,
 | |
|   MONSTER_HOODCLOAK1, MONSTER_HOODCLOAK2,
 | |
|   STUNSTARS,
 | |
|   CARRIED, CARRIEDa, CARRIEDb,
 | |
|   PARTICLE, SWORDMARK, MAGICSWORD, MISSILE, SKY,
 | |
|   MINEMARK, ARROW,
 | |
|   MOBILE_ARROW,
 | |
|   LINE, 
 | |
|   // in depth tested models transparent surfaces need to be depth sorted by HyperRogue
 | |
|   // and set to PPR::TRANSPARENT_* to draw them after all the opaque ones
 | |
|   TRANSPARENT_LAKE, TRANSPARENT_SHADOW, TRANSPARENT_WALL,
 | |
|   // no depth testing for SUPERLINE and above
 | |
|   SUPERLINE, TEXT, CIRCLE,
 | |
|   MAX,
 | |
|   DEFAULT = -1
 | |
|   };
 | |
| 
 | |
| inline PPR operator + (PPR x, int y) { return PPR(int(x) + y); }
 | |
| inline PPR operator - (PPR x, int y) { return PPR(int(x) - y); }
 | |
| inline int operator - (PPR x, PPR y) { return int(x) - int(y); }
 | |
| 
 | |
| #define OUTLINE_NONE     0x000000FF
 | |
| #define OUTLINE_FRIEND   0x00FF00FF
 | |
| #define OUTLINE_ENEMY    0xFF0000FF
 | |
| #define OUTLINE_TREASURE 0xFFFF00FF
 | |
| #define OUTLINE_ORB      0xFF8000FF
 | |
| #define OUTLINE_OTHER    0xFFFFFFFF
 | |
| #define OUTLINE_DEAD     0x800000FF
 | |
| #define OUTLINE_TRANS    0
 | |
| #define OUTLINE_DEFAULT  ((bordcolor << 8) + 0xFF)
 | |
| #define OUTLINE_FORE     ((forecolor << 8) + 0xFF)
 | |
| #define OUTLINE_BACK     ((backcolor << 8) + 0xFF)
 | |
| 
 | |
| enum orbAction { roMouse, roKeyboard, roCheck, roMouseForce, roMultiCheck, roMultiGo };
 | |
| 
 | |
| #define MODELCOUNT ((int) mdGUARD)
 | |
| 
 | |
| #define pconf vid.projection_config
 | |
| #if CAP_RUG
 | |
| #define vpconf (rug::rugged ? vid.rug_config : vid.projection_config)
 | |
| #else
 | |
| #define vpconf pconf
 | |
| #endif
 | |
| #define pmodel (pconf.model)
 | |
| 
 | |
| static constexpr int DISTANCE_UNKNOWN = 127;
 | |
| static constexpr int DISTANCE_UNKNOWN_BIG = 99999999;
 | |
| 
 | |
| template<class T, class U> int addHook(hookset<T>& m, int prio, U&& hook) {
 | |
|   return m.add(prio, static_cast<U&&>(hook));
 | |
|   }
 | |
| 
 | |
| template<class T> void delHook(hookset<T>& m, int prio) {
 | |
|   m.del(prio);
 | |
|   }
 | |
| 
 | |
| template<class T, class... U> void callhooks(const hookset<T>& h, U&&... args) {
 | |
|   return h.callhooks(static_cast<U&&>(args)...);
 | |
|   }
 | |
| 
 | |
| template<class T, class V, class... U> V callhandlers(V zero, const hookset<T>& h, U&&... args) {
 | |
|   return h.callhandlers(zero, static_cast<U&&>(args)...);
 | |
|   }
 | |
| 
 | |
| void popScreen();
 | |
| 
 | |
| template<class T, class U> void hook_in_subscreen(hookset<T>& m, int prio, U&& hook) {
 | |
|   int v = m.add(prio, static_cast<U&&>(hook));
 | |
|   pushScreen([&m, v] {
 | |
|     delHook(m, v);
 | |
|     popScreen();
 | |
|     });
 | |
|   }
 | |
| 
 | |
| string XLAT(string);
 | |
| 
 | |
| #define GLERR(call) glError(call, __FILE__, __LINE__)
 | |
| 
 | |
| #define SHMUPTITLE "shoot'em up mode"
 | |
| 
 | |
| // check for a plain number key
 | |
| #define NUMBERKEY (interpret_as_direction(sym, uni) ? 0 : uni)
 | |
| #define DKEY (get_direction_key(sym, uni))
 | |
| #define DIRECTIONKEY (interpret_as_direction(sym, uni) ? uni : 0)
 | |
| 
 | |
| namespace scores { void load(); void load_only(); extern int which_mode; }
 | |
| 
 | |
| #if ISMOBILE
 | |
| namespace leader { void showMenu(); void handleKey(int sym, int uni); }
 | |
| #endif
 | |
| 
 | |
| int textwidth(int siz, const string &str);
 | |
| #if CAP_GL
 | |
| int gl_width(int size, const char *s);
 | |
| #endif
 | |
| 
 | |
| #if ISMOBILE
 | |
| extern int andmode;
 | |
| extern bool longclick;
 | |
| extern bool useRangedOrb;
 | |
| #endif
 | |
| 
 | |
| #ifndef GL
 | |
| typedef float GLfloat;
 | |
| #endif
 | |
| 
 | |
| typedef array<GLfloat, 2> glvec2;
 | |
| typedef array<GLfloat, 3> glvec3;
 | |
| typedef array<GLfloat, 4> glvec4;
 | |
| 
 | |
| #if MAXMDIM == 4
 | |
| #define SHDIM 4
 | |
| typedef glvec4 glvertex;
 | |
| #else
 | |
| #define SHDIM 3
 | |
| typedef glvec3 glvertex;
 | |
| #endif
 | |
| 
 | |
| extern int emeraldtable[100][7];
 | |
| 
 | |
| // extern cell *cwpeek(cellwalker cw, int dir);
 | |
| 
 | |
| #define HAUNTED_RADIUS getDistLimit()
 | |
| #define UNKNOWN 65535
 | |
| 
 | |
| #define GRAIL_FOUND 0x4000
 | |
| #define GRAIL_RADIUS_MASK 0x3FFF
 | |
| 
 | |
| extern vector<cell*> dcal;
 | |
| 
 | |
| // z to close to this limit => do not draw
 | |
| 
 | |
| #define BEHIND_LIMIT 1e-6
 | |
| 
 | |
| template<class T, class U> void eliminate_if(vector<T>& data, U pred) {
 | |
|   for(int i=0; i<isize(data); i++)
 | |
|     if(pred(data[i]))
 | |
|       data[i] = data.back(), data.pop_back(), i--;
 | |
|   }
 | |
| 
 | |
| template<class T> constexpr array<T, 4> make_array(T a, T b, T c, T d) { return array<T,4>{a,b,c,d}; }
 | |
| template<class T> constexpr array<T, 3> make_array(T a, T b, T c) { return array<T,3>{a,b,c}; }
 | |
| template<class T> constexpr array<T, 2> make_array(T a, T b) { return array<T,2>{a,b}; }
 | |
| 
 | |
| // Find in a std::map or std::unordered_map, or return null.
 | |
| template<class Map, class Key>
 | |
| const typename Map::mapped_type *at_or_null(const Map& map, const Key& key) {
 | |
|   auto it = map.find(key);
 | |
|   return (it == map.end()) ? nullptr : &it->second;
 | |
|   }
 | |
| 
 | |
| int gmod(int i, int j);
 | |
| 
 | |
| // vector::at(i) modulo its size (const version)
 | |
| template<class T> const T& atmod(const vector<T>& container, int index) {
 | |
|   return container[gmod(index, isize(container))];
 | |
|   }
 | |
| 
 | |
| // vector::at(i) modulo its size (non-const version)
 | |
| template<class T> T& atmod(vector<T>& container, int index) {
 | |
|   return container[gmod(index, isize(container))];
 | |
|   }
 | |
| 
 | |
| namespace daily {
 | |
|   extern bool on;
 | |
|   extern int historical;
 | |
|   extern int daily_id;
 | |
|   void setup();
 | |
|   void split();
 | |
|   void gifts();
 | |
|   void turnoff();
 | |
|   void showMenu();
 | |
|   int find_daily_lbid(int id);
 | |
|   bool prevent_spawn_treasure_on(cell *c);
 | |
|   void handleQuit(int sev);
 | |
|   void uploadscore(bool really_final);
 | |
|   }
 | |
| 
 | |
| #define RING(i) for(double i=0; i<=cgi.S84+1e-6; i+=SD3 * pow(.5, vid.linequality))
 | |
| #define REVRING(i) for(double i=cgi.S84; i>=-1e-6; i-=SD3 * pow(.5, vid.linequality))
 | |
| #define PRING(i) for(double i=0; i<=cgi.S84+1e-6; i+= pow(.5, vid.linequality))
 | |
| #define REVPRING(i) for(double i=cgi.S84; i>=-1e-6; i-=pow(.5, vid.linequality))
 | |
| 
 | |
| #define ONEMPTY if(d == 7 && passable(c, NULL, 0) && !safety && !reptilecheat)
 | |
| 
 | |
| template <class T> void texture_order(const T& f) {
 | |
|   const int STEP = vid.texture_step;
 | |
|   const ld STEP2 = STEP;
 | |
|   for(int y=0; y<STEP; y++)
 | |
|   for(int x=0; x<STEP; x++) {
 | |
|     ld x0 = x / STEP2;
 | |
|     ld y0 = y / STEP2;
 | |
|     ld b = 1 / STEP2;
 | |
| 
 | |
|      if(x+y < STEP) {
 | |
|        f(x0, y0); f(x0+b, y0); f(x0, y0+b);
 | |
|        }
 | |
|      if(x+y <= STEP && x && y) {
 | |
|        f(x0, y0); f(x0-b, y0); f(x0, y0-b);
 | |
|        }
 | |
|     }
 | |
|   }
 | |
| 
 | |
| /** find the smallest value of x in range [dmin..dmax] such that f(x) returns true */
 | |
| 
 | |
| template<class T> ld binsearch(ld dmin, ld dmax, const T& f, int iterations = 200) {
 | |
|   for(int i=0; i<iterations; i++) {
 | |
|     ld d = (dmin + dmax) / 2;
 | |
|     if(dmin == d || dmax == d) break;
 | |
|     if(f(d)) dmax = d;
 | |
|     else dmin = d;
 | |
|     }
 | |
|   return dmin;
 | |
|   } 
 | |
| 
 | |
| static constexpr int max_vec = (1<<14);
 | |
| extern bool needConfirmationEvenIfSaved();
 | |
| 
 | |
| typedef unsigned long long flagtype;
 | |
| #define Flag(i) (flagtype(1ull<<i))
 | |
| static inline void set_flag(flagtype& f, flagtype which, bool b) {
 | |
|   if(b) f |= which;
 | |
|   else f &= ~which;
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| /** this macro is used to delay performing the action in case if everything is rolled back */
 | |
| #define LATE(x) \
 | |
|   if(changes.on) { changes.at_commit([=] { x; }); return; }
 | |
| 
 | |
| // assert macro
 | |
| #ifdef NDEBUG
 | |
| #define hassert(condition) if(!(condition)) __builtin_unreachable()
 | |
| #else
 | |
| #define hassert(condition) if(!(condition)) printf("%s:%d:%s: assertion failed: %s\n", __FILE__, __LINE__, __func__, #condition)
 | |
| #endif
 | |
| 
 | |
| #define IS(z) = z
 | |
| #include "autohdr.h"
 | |
| #undef IS
 | |
| #define IS(z)
 | |
| #define EX
 | |
| 
 | |
| #endif
 | 
