hyperrogue/hyper.h

971 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.0g"
#define VERNUM_HEX 0xAA07
#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 rotspace (geometry == gRotSpace)
#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 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
// achievements
#define LB_YENDOR_CHALLENGE 40
#define LB_PURE_TACTICS 41
#define NUMLEADER 90
#define LB_PURE_TACTICS_SHMUP 49
#define LB_PURE_TACTICS_COOP 50
#define LB_RACING 81
#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 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)
// 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,
BELOWBOTTOMm,
BELOWBOTTOM,
BELOWBOTTOMp,
BELOWBOTTOM_FALLANIM,
LAKEBOTTOM, HELLSPIKE,
INLAKEWALLm, INLAKEWALL, INLAKEWALLp,
INLAKEWALL_FALLANIM,
BSHALLOW, SHALLOW, ASHALLOW,
SUBLAKELEV, LAKELEV, BOATLEV, BOATLEV2, BOATLEV3,
LAKEWALLm, LAKEWALL, LAKEWALLp,
LAKEWALL_FALLANIM,
FLOOR_TOWER,
FLOOR,
FLOOR_DRAGON,
FLOORa, FLOORb, FLOORc, FLOORd,
LIZEYE,
BFLOOR,
GFLOORa, GFLOORb, GFLOORc,
WALLSHADOW,
STRUCT0, STRUCT1, STRUCT2, STRUCT3,
THORNS, WALL,
REDWALLm, REDWALLs, REDWALLp, REDWALL,
REDWALLm2, REDWALLs2, REDWALLp2, REDWALLt2,
REDWALLm3, REDWALLs3, REDWALLp3, REDWALLt3,
HEPTAMARK,
ITEM_BELOW,
ITEM, ITEMa, ITEMb,
BIGSTATUE,
WALL3m, WALL3s, WALL3p, WALL3, WALL3A,
// WALL3m, WALL3s, WALL3p, WALL3, WALL3A,
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;
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)...);
}
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(); }
#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