2019-08-10 11:43:24 +00:00
// Hyperbolic Rogue -- main header file
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
2019-08-10 17:30:21 +00:00
/** \file hyper.h
2019-08-10 11:43:24 +00:00
* \ 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
*/
2017-10-29 13:19:51 +00:00
2019-09-05 07:07:04 +00:00
# ifndef _HYPER_H_
# define _HYPER_H_
2018-06-10 23:58:31 +00:00
// version numbers
2022-03-01 08:53:30 +00:00
# define VER "12.0l"
# define VERNUM_HEX 0xA90C
2018-06-10 23:58:31 +00:00
2019-09-05 07:07:04 +00:00
# include "sysconfig.h"
2018-06-17 03:32:09 +00:00
# include <stdarg.h>
2019-01-06 01:47:51 +00:00
# include "hyper_function.h"
2018-06-17 03:32:09 +00:00
2020-03-27 20:48:31 +00:00
/** \brief the main namespace of HyperRogue */
2018-06-10 23:58:31 +00:00
namespace hr {
2020-03-27 20:48:31 +00:00
/** \brief A helper structure that acts as a boolean which is always false. Helpful when disabling stuff with compiler flags. */
2019-02-17 17:50:56 +00:00
struct always_false {
2021-06-29 05:33:08 +00:00
operator bool ( ) const { return false ; }
bool operator = ( bool b ) const { return b ; }
2019-02-17 17:50:56 +00:00
} ;
2020-03-27 20:48:31 +00:00
/** \brief placate GCC's overzealous -Wunused-result */
2018-07-14 06:45:14 +00:00
template < class T >
void ignore ( T & & ) {
}
2020-03-27 20:48:31 +00:00
/** \brief a simple static_cast<void*> for use with printf("%p") */
2020-03-27 18:39:33 +00:00
inline const void * voidp ( const void * p ) {
2020-02-23 02:48:46 +00:00
return p ;
}
2020-03-27 20:48:31 +00:00
/** \brief Is the value of first parameter equal to one of the remaining parameters? */
2019-03-30 16:52:51 +00:00
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 . . . ) ; }
2018-06-17 16:32:06 +00:00
// 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 ;
2018-06-22 00:00:26 +00:00
using std : : move ;
using std : : make_tuple ;
2018-09-04 17:53:42 +00:00
using std : : unique_ptr ;
2018-06-18 01:53:39 +00:00
2018-06-18 01:43:23 +00:00
using std : : abs ;
2018-06-18 01:53:39 +00:00
using std : : isfinite ;
using std : : isnan ;
2018-10-23 15:03:58 +00:00
using std : : isinf ;
2018-06-18 01:53:39 +00:00
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 ;
2018-07-23 12:07:18 +00:00
# ifndef NO_STD_HYPOT
2018-07-23 03:18:03 +00:00
using std : : hypot ;
using std : : asinh ;
using std : : acosh ;
# endif
2018-06-10 23:58:31 +00:00
2021-03-21 21:56:38 +00:00
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 ( ) ) { }
} ;
2019-04-03 18:24:15 +00:00
2021-03-21 21:56:38 +00:00
struct hr_shortest_path_exception { } ;
2021-03-21 09:44:38 +00:00
2018-06-10 23:58:31 +00:00
// genus (in grammar)
# define GEN_M 0
# define GEN_F 1
# define GEN_N 2
# define GEN_O 3
2019-04-29 09:17:06 +00:00
// Add a message to the GUI.
// If multiple messages appear with the same spamtype != 0, the older ones disappear quickly
2018-06-10 23:58:31 +00:00
void addMessage ( string s , char spamtype = 0 ) ;
// geometry-dependent constants
2019-10-10 11:13:37 +00:00
# define cginf ginf[geometry]
# define S7 cginf.sides
# define S3 cginf.vertex
2019-12-14 11:05:01 +00:00
# define hyperbolic_37 (S7 == 7 && S3 == 3 && !bt::in() && !arcm::in())
# define hyperbolic_not37 ((S7 > 7 || S3 > 3 || bt::in() || arcm::in()) && hyperbolic)
2020-06-03 09:45:18 +00:00
# define weirdhyperbolic ((S7 > 7 || S3 > 3 || !STDVAR || bt::in() || arcm::in() || arb::in()) && hyperbolic)
2019-12-14 11:05:01 +00:00
# define stdhyperbolic (S7 == 7 && S3 == 3 && STDVAR && !bt::in() && !arcm::in())
2018-08-09 17:28:53 +00:00
2019-10-10 11:13:37 +00:00
# define cgflags cginf.flags
# define cryst (cgflags & qCRYSTAL)
2019-04-29 09:17:06 +00:00
2019-08-10 17:34:12 +00:00
/** convenience flag for geometries with major aspects missing */
2019-10-10 11:13:37 +00:00
# define experimental (cgflags & qEXPERIMENTAL)
2019-08-10 17:34:12 +00:00
2019-04-29 09:17:06 +00:00
// these geometries do not feature alternate structures for horocycles
2019-12-14 11:05:01 +00:00
# define eubinary (euclid || bt::in() || cryst || nil)
2018-06-10 23:58:31 +00:00
2019-10-10 11:13:37 +00:00
# define cgclass (cginf.cclass)
2018-06-10 23:58:31 +00:00
# define euclid (cgclass == gcEuclid)
# define sphere (cgclass == gcSphere)
2019-10-10 11:13:37 +00:00
# define sol (cgflags & qSOL)
# define nih (cgflags & qNIH)
2019-08-06 10:00:46 +00:00
# define nil (cgclass == gcNil)
2019-08-24 09:55:45 +00:00
# define sl2 (cgclass == gcSL2)
2019-08-17 21:28:41 +00:00
# define prod (cgclass == gcProduct)
2019-10-10 11:13:37 +00:00
# define hybri (cgflags & qHYBRID)
2020-05-08 18:26:30 +00:00
# define rotspace (geometry == gRotSpace)
2018-06-10 23:58:31 +00:00
# define hyperbolic (cgclass == gcHyperbolic)
2019-12-14 11:28:45 +00:00
# define nonisotropic (among(cgclass, gcSolNIH, gcNil, gcSL2))
2019-08-06 10:00:46 +00:00
# define translatable (euclid || nonisotropic)
2019-10-10 11:13:37 +00:00
# define nonorientable (cgflags & qNONORIENTABLE)
# define elliptic (cgflags & qELLIPTIC)
# define quotient (cgflags & qANYQ)
# define smallbounded (cgflags & qSMALL)
# define bounded (cgflags & qBOUNDED)
2018-06-10 23:58:31 +00:00
2019-04-29 09:17:06 +00:00
// Dry Forest burning, heat transfer, etc. are performed on the whole universe
# define doall (bounded)
2019-12-14 10:42:16 +00:00
# define sphere_narcm (sphere && !arcm::in())
2018-08-17 11:29:00 +00:00
2018-06-10 23:58:31 +00:00
# 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)
2018-11-30 13:45:19 +00:00
# define a38 (S3 == 3 && S7 == 8)
2018-06-10 23:58:31 +00:00
# define sphere4 (sphere && S7 == 4)
# define stdeuc (geometry == gNormal || geometry == gEuclid || geometry == gEuclidSquare)
2018-11-30 14:26:50 +00:00
# define smallsphere (sphere_narcm && S7 < 5)
# define bigsphere (sphere_narcm && S7 == 5)
2018-06-10 23:58:31 +00:00
# define S6 (S3*2)
# define MAX_S3 4
2018-11-27 15:17:20 +00:00
# define SG6 (S3==3?6:4)
# define SG3 (S3==3?3:2)
# define SG2 (S3==3?2:1)
2020-07-12 18:52:32 +00:00
# define GOLDBERG_INV (GOLDBERG || INVERSE)
# define INVERSE among(variation, eVariation::unrectified, eVariation::warped, eVariation::untruncated )
# define UNRECTIFIED (variation == eVariation::unrectified)
# define WARPED (variation == eVariation::warped)
# define UNTRUNCATED (variation == eVariation::untruncated)
2018-08-28 15:17:34 +00:00
# define GOLDBERG (variation == eVariation::goldberg)
# define IRREGULAR (variation == eVariation::irregular)
# define PURE (variation == eVariation::pure)
# define BITRUNCATED (variation == eVariation::bitruncated)
2018-08-30 00:11:43 +00:00
# define DUAL (variation == eVariation::dual)
2018-08-30 14:05:24 +00:00
# define DUALMUL (DUAL ? 2 : 1)
2018-08-28 15:17:34 +00:00
2019-10-10 11:13:37 +00:00
# define CHANGED_VARIATION (variation != cginf.default_variation)
2018-08-28 15:17:34 +00:00
# define STDVAR (PURE || BITRUNCATED)
# define NONSTDVAR (!STDVAR)
2020-07-12 22:54:25 +00:00
# define VALENCE current_valence()
2018-08-28 15:17:34 +00:00
2016-08-26 09:58:03 +00:00
# define NUMWITCH 7
// achievements
# define LB_YENDOR_CHALLENGE 40
# define LB_PURE_TACTICS 41
2021-05-31 18:33:34 +00:00
# define NUMLEADER 87
2016-08-26 09:58:03 +00:00
# define LB_PURE_TACTICS_SHMUP 49
# define LB_PURE_TACTICS_COOP 50
2019-01-11 01:19:28 +00:00
# define LB_RACING 81
2016-08-26 09:58:03 +00:00
2017-10-29 11:19:33 +00:00
# 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
2018-11-06 23:51:41 +00:00
typedef complex < ld > cld ;
2019-12-23 21:21:04 +00:00
typedef unsigned color_t ;
2019-05-29 14:27:24 +00:00
struct charstyle {
int charid ;
color_t skincolor , haircolor , dresscolor , swordcolor , dresscolor2 , uicolor , eyecolor ;
bool lefthanded ;
} ;
enum eStereo { sOFF , sAnaglyph , sLR , sODS } ;
2019-12-23 21:21:04 +00:00
enum eModel : int ;
2020-04-16 22:53:58 +00:00
/** 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 , camera_angle , fisheye_param , twopoint_param , stretch , ballangle , ballproj , euclid_to_sphere ;
ld clip_min , clip_max ;
ld model_orientation , halfplane_scale , model_orientation_yz ;
ld collignon_parameter ;
2020-09-15 17:17:07 +00:00
ld aitoff_parameter , miller_parameter , loximuthal_parameter , winkel_parameter ;
2020-11-01 19:10:08 +00:00
bool show_hyperboloid_flat ;
2020-04-16 22:53:58 +00:00
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 ;
2020-09-11 09:13:18 +00:00
ld rotational_nil ;
2020-12-27 16:11:19 +00:00
ld depth_scaling ;
ld hyperboloid_scaling ;
ld vr_angle , vr_zshift , vr_scale_factor ;
2020-04-16 22:53:58 +00:00
2021-10-01 06:03:16 +00:00
int back_and_front ; /* 0 = do not, 1 = do, 2 = only back */
2020-04-16 22:53:58 +00:00
projection_configuration ( ) {
formula = " z^2 " ; top_z = 5 ; model_transition = 1 ; spiral_angle = 70 ; spiral_x = 10 ; spiral_y = 7 ;
2020-09-11 09:13:18 +00:00
rotational_nil = 1 ;
2020-04-16 22:53:58 +00:00
right_spiral_multiplier = 1 ;
any_spiral_multiplier = 1 ;
sphere_spiral_multiplier = 2 ;
spiral_cone = 360 ;
use_atan = false ;
product_z_scale = 1 ;
2020-09-15 17:17:07 +00:00
aitoff_parameter = .5 ;
miller_parameter = .8 ;
loximuthal_parameter = 0 ;
winkel_parameter = .5 ;
2020-11-01 19:10:08 +00:00
show_hyperboloid_flat = true ;
2020-12-27 16:11:19 +00:00
depth_scaling = 1 ;
vr_angle = 0 ;
hyperboloid_scaling = 1 ;
vr_zshift = 0 ;
vr_scale_factor = 1 ;
2021-10-01 06:03:16 +00:00
back_and_front = 0 ;
2020-04-16 22:53:58 +00:00
}
} ;
2021-04-30 17:44:54 +00:00
enum eThreatLevel { tlNoThreat , tlSpam , tlNormal , tlHighThreat } ;
2019-05-29 14:27:24 +00:00
struct videopar {
2020-04-16 22:53:58 +00:00
projection_configuration projection_config , rug_config ;
ld yshift ;
ld sspeed , mspeed ;
ld binary_width , fixed_facing_dir ;
2019-05-29 14:27:24 +00:00
int mobilecompasssize ;
int radarsize ; // radar for 3D geometries
2019-07-31 15:32:44 +00:00
ld radarrange ;
2019-05-29 14:27:24 +00:00
int aurastr , aurasmoothen ;
bool fixed_facing ;
bool fixed_yz ;
bool use_wall_radar ;
int linequality ;
2021-02-06 00:39:18 +00:00
bool want_fullscreen ;
2019-05-29 14:27:24 +00:00
bool full ;
2021-02-06 00:39:18 +00:00
bool change_fullscr ;
bool relative_window_size ;
bool want_vsync ;
bool current_vsync ;
2019-05-29 14:27:24 +00:00
int graphglyph ; // graphical glyphs
bool darkhepta ;
int shifttarget ;
int xres , yres , framelimit ;
int xscr , yscr ;
2021-02-06 00:39:18 +00:00
int fullscreen_x , fullscreen_y ;
int window_x , window_y ;
ld window_rel_x , window_rel_y ;
2019-05-29 14:27:24 +00:00
bool grid ;
2019-12-25 22:04:07 +00:00
bool particles ;
2019-05-29 14:27:24 +00:00
2021-02-06 00:39:18 +00:00
bool relative_font ;
int fsize , abs_fsize , fontscale ;
2019-05-29 14:27:24 +00:00
int flashtime ;
2020-09-15 18:39:15 +00:00
int wallmode , monmode , axes , highlightmode ;
2019-06-25 08:30:31 +00:00
bool axes3 ;
2019-05-29 14:27:24 +00:00
bool revcontrol ;
int msgleft , msglimit ;
2021-02-06 00:39:18 +00:00
bool wantGL ;
int want_antialias ;
bool fineline ;
2019-05-29 14:27:24 +00:00
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 ;
2019-07-13 12:37:30 +00:00
ld multiplier_grid , multiplier_ring ;
2019-05-29 14:27:24 +00:00
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)
2020-08-20 14:12:04 +00:00
bool smart_area_based ; // based on area or length?
2019-05-29 14:27:24 +00:00
int cells_drawn_limit ;
int cells_generated_limit ; // limit on cells generated per frame
eStereo stereo_mode ;
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 ;
ld rock_wall_ratio ;
ld human_wall_ratio ;
2021-10-15 20:38:24 +00:00
bool pseudohedral ; // in 3D modes
2021-10-15 20:36:43 +00:00
ld depth_bonus ; // to fiix the placement of 3D models in pseudogonal -- not working currently
2019-05-29 14:27:24 +00:00
int tc_alpha , tc_depth , tc_camera ;
ld highdetail , middetail ;
bool gp_autoscale_heights ;
2019-06-01 17:58:07 +00:00
ld eye ;
bool auto_eye ;
2019-08-14 16:58:42 +00:00
2019-08-18 19:16:27 +00:00
ld plevel_factor ;
2019-12-25 22:04:07 +00:00
bool bubbles_special , bubbles_threshold , bubbles_all ;
2019-12-27 10:57:08 +00:00
int joysmooth ;
2021-04-30 17:44:54 +00:00
eThreatLevel faraway_highlight ; // draw attention to monsters on the horizon
int faraway_highlight_color ; // 0 = monster color, 100 = red-green oscillation
2019-05-29 14:27:24 +00:00
} ;
extern videopar vid ;
2020-11-01 16:37:51 +00:00
/** \brief How many dimensional is the gameplay. In the FPP mode of a 2D geometry, WDIM is 2 */
2019-10-10 11:13:37 +00:00
# define WDIM cginf.g.gameplay_dimension
2020-11-01 16:37:51 +00:00
/** \brief How many dimensional is the graphical representation. In the FPP mode of a 2D geometry, MDIM is 3 */
2019-10-10 11:13:37 +00:00
# define GDIM cginf.g.graphical_dimension
2020-11-01 16:37:51 +00:00
/** \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 */
2020-04-17 13:37:31 +00:00
# define MDIM (MAXMDIM == 3 ? 3 : cginf.g.homogeneous_dimension)
2020-11-01 16:37:51 +00:00
/** \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 4 x4 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 */
2019-08-17 21:28:41 +00:00
# define LDIM (MDIM-1)
2019-08-22 09:24:25 +00:00
# define cclass g.kind
2019-02-17 17:47:19 +00:00
2019-08-09 12:03:43 +00:00
# define self (*this)
2017-10-29 11:19:33 +00:00
# define BUGCOLORS 3
2021-04-11 20:15:40 +00:00
# define big_unlock (inv::on && !ls::any_chaos())
2019-06-17 08:13:08 +00:00
2017-10-29 11:19:33 +00:00
// land completion for shared unlocking
2019-06-17 08:13:08 +00:00
# define U5 (big_unlock ? 10 : 5)
2017-10-29 11:19:33 +00:00
// land completion for advanced unlocking
2019-06-17 08:13:08 +00:00
# define U10 (big_unlock ? 25 : 10)
2017-10-29 11:19:33 +00:00
// land completion
2019-06-17 08:13:08 +00:00
# define R10 (big_unlock ? 50 : 10)
2017-10-29 11:19:33 +00:00
// intermediate lands
2019-06-17 08:13:08 +00:00
# define R30 (big_unlock ? 100 : 30)
2017-10-29 11:19:33 +00:00
// advanced lands
2019-06-17 08:13:08 +00:00
# define R60 (big_unlock ? 200 : 60)
2017-10-29 11:19:33 +00:00
// advanced lands II
2019-06-17 08:13:08 +00:00
# define R90 (big_unlock ? 300 : 90)
2017-10-29 11:19:33 +00:00
// Crossroads IV
2019-06-17 08:13:08 +00:00
# define R200 (big_unlock ? 800 : 200)
2017-10-29 11:19:33 +00:00
// Crossroads V
2019-06-17 08:13:08 +00:00
# define R300 (big_unlock ? 1200 : 300)
2017-10-29 11:19:33 +00:00
// kill types for Dragon Chasms
2019-06-17 08:13:08 +00:00
# define R20 (big_unlock ? 30 : 20)
2017-10-29 11:19:33 +00:00
// kill count for Graveyard/Hive
2019-06-17 08:13:08 +00:00
# define R100 (big_unlock ? 500 : 100)
2017-10-29 11:19:33 +00:00
2018-06-22 12:47:24 +00:00
// 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 ( ) ; }
2017-10-29 11:19:33 +00:00
2020-01-18 15:59:16 +00:00
// 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 ) ;
}
} ;
2016-08-26 09:58:03 +00:00
// game forward declarations
namespace anticheat { extern bool tampered ; }
# define HRANDMAX 0x7FFFFFFF
2018-04-09 16:05:23 +00:00
2017-03-23 10:53:57 +00:00
struct movedir {
2019-04-29 11:41:24 +00:00
int d ;
// non-negative numbers denote 'rotate +d steps and act in this direction
// negative numbers have the following meanings (warning: not used consistently):
2017-03-23 10:53:57 +00:00
# define MD_WAIT (-1)
# define MD_DROP (-2)
# define MD_UNDECIDED (-3)
# define MD_USE_ORB (-4)
2019-04-29 11:41:24 +00:00
int subdir ; // for normal movement (0+): turn left or right
2019-08-10 11:43:24 +00:00
struct cell * tgt ; // for MD_USE_ORB: target cell
2017-03-23 10:53:57 +00:00
} ;
2016-08-26 09:58:03 +00:00
// shmup
2020-04-12 00:44:31 +00:00
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 ) ) ;
2020-09-11 09:17:12 +00:00
return prio ;
2020-04-12 00:44:31 +00:00
}
2020-09-11 09:17:12 +00:00
void del ( int prio ) {
map_ - > erase ( prio ) ;
}
2020-04-12 00:44:31 +00:00
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 ( ) > ;
2018-07-09 17:32:34 +00:00
2018-06-17 15:51:26 +00:00
static const int NOHINT = - 1 ;
2016-08-26 09:58:03 +00:00
2017-10-08 22:21:39 +00:00
typedef function < void ( ) > reaction_t ;
2017-12-14 11:10:40 +00:00
typedef function < bool ( ) > bool_reaction_t ;
2017-10-08 22:21:39 +00:00
2020-09-21 10:00:52 +00:00
void offer_choose_file ( reaction_t r ) ;
2017-05-27 19:40:40 +00:00
# define HELPFUN(x) (help_delegate = x, "HELPFUN")
2019-08-10 11:43:24 +00:00
typedef function < int ( struct cell * ) > cellfunction ;
2018-09-27 19:52:13 +00:00
2016-08-26 09:58:03 +00:00
// passable flags
# define SAGEMELT .1
2018-05-15 21:26:04 +00:00
# define PT(x, y) ((tactic::on || quotient == 2 || daily::on) ? (y) : inv::on ? min(2*(y),x) : (x))
2016-08-26 09:58:03 +00:00
# define ROCKSNAKELENGTH 50
2017-03-23 10:53:57 +00:00
# define WORMLENGTH 15
2016-08-26 09:58:03 +00:00
# define PRIZEMUL 7
# define INF 9999
2018-04-03 21:34:47 +00:00
# define INFD 60
2017-03-23 10:53:57 +00:00
# define PINFD 125
2018-01-25 22:52:57 +00:00
# ifndef BARLEV
2019-03-02 23:45:40 +00:00
# define BARLEV ((ISANDROID||ISIOS||ISFAKEMOBILE||getDistLimit()<7)?(getDistLimit()<4?8:9):10)
2018-01-25 22:52:57 +00:00
# endif
2016-08-26 09:58:03 +00:00
# define BUGLEV 15
// #define BARLEV 9
# define YDIST 101
2018-12-14 18:24:27 +00:00
# define MODECODES (1ll<<61)
2016-08-26 09:58:03 +00:00
# define GUNRANGE 3
// loops
# define fakecellloop(ct) for(cell *ct = (cell*)1; ct; ct=NULL)
2018-08-17 22:46:45 +00:00
# define forCellIdAll(ct, i, cf) fakecellloop(ct) for(int i=0; i<(cf)->type && (ct=(cf)->move(i),true); i++)
2016-08-26 09:58:03 +00:00
# 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
2018-01-03 20:49:14 +00:00
# define AF_NORMAL 0 // nothing special about this attack
2017-09-30 09:46:41 +00:00
# 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
2018-01-03 20:49:14 +00:00
# define AF_MUSTKILL Flag(23) // when TRUE, stunning attacks are not accepted by canAttack
2017-09-30 09:46:41 +00:00
# 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
2018-01-02 10:15:42 +00:00
# define AF_CRUSH Flag(31) // Crusher's delayed attack
2020-02-26 01:49:35 +00:00
# define AF_PLAGUE Flag(32) // Orb of Plague (do not check adjacency)
2020-03-02 19:35:15 +00:00
# define AF_PSI Flag(33) // Orb of the Mind
2021-05-02 13:16:29 +00:00
# define AF_WEAK Flag(34) // Curse of Weakness
2017-03-23 10:53:57 +00:00
2017-07-22 23:33:27 +00:00
# if CAP_SDL
2016-08-26 09:58:03 +00:00
2017-07-22 23:33:27 +00:00
# if CAP_PNG
2016-08-26 09:58:03 +00:00
# include "savepng.h"
# define IMAGEEXT ".png"
void IMAGESAVE ( SDL_Surface * s , const char * fname ) ;
2017-07-22 23:33:27 +00:00
# else
# define IMAGEEXT ".bmp"
# define IMAGESAVE SDL_SaveBMP
# endif
2016-08-26 09:58:03 +00:00
# endif
template < class T > struct dynamicval {
T & where ;
T backup ;
dynamicval ( T & wh , T val ) : where ( wh ) { backup = wh ; wh = val ; }
2017-07-04 13:38:33 +00:00
dynamicval ( T & wh ) : where ( wh ) { backup = wh ; }
2016-08-26 09:58:03 +00:00
~ dynamicval ( ) { where = backup ; }
} ;
2021-03-06 10:47:54 +00:00
struct finalizer {
reaction_t f ;
finalizer ( reaction_t r ) : f ( r ) { }
~ finalizer ( ) { f ( ) ; }
} ;
2019-08-09 21:48:28 +00:00
static const int MAXPLAYER = 7 ;
2021-04-02 13:51:48 +00:00
# define DEFAULTCONTROL (multi::players == 1 && !shmup::on && !multi::alwaysuse)
2017-05-31 16:33:50 +00:00
# define DEFAULTNOR(sym) (DEFAULTCONTROL || multi::notremapped(sym))
2017-03-23 10:53:57 +00:00
2017-07-22 23:33:27 +00:00
# define CAP_MENUSCALING (ISPANDORA || ISMOBILE)
2017-03-23 10:53:57 +00:00
2017-07-22 23:33:27 +00:00
# if CAP_MENUSCALING
2018-07-22 10:54:05 +00:00
# define displayfrZ dialog::zoom::displayfr
# define displayfrZH dialog::zoom::displayfr_highlight
2017-03-23 10:53:57 +00:00
# else
# define displayfrZ displayfr
2018-07-22 13:54:44 +00:00
# define displayfrZH dialog::zoom::displayfr_highlight
2017-03-23 10:53:57 +00:00
# endif
// just in case if I change my mind about when Orbs lose their power
# define ORBBASE 0
# define mmscale(V, x) (mmspatial ? (ivoryz ? mzscale(V,x) : mscale(V, x)) : (V))
# define SHADOW_WALL 0x60
# define SHADOW_SL 0x18
# define SHADOW_MON 0x30
// ranks:
2018-08-28 12:27:23 +00:00
enum class PPR {
2019-05-28 23:06:01 +00:00
ZERO , EUCLIDEAN_SKY , OUTCIRCLE , MOVESTAR ,
2018-08-28 12:27:23 +00:00
MINUSINF ,
BELOWBOTTOMm ,
BELOWBOTTOM ,
BELOWBOTTOMp ,
BELOWBOTTOM_FALLANIM ,
LAKEBOTTOM , HELLSPIKE ,
INLAKEWALLm , INLAKEWALL , INLAKEWALLp ,
INLAKEWALL_FALLANIM ,
2020-02-26 00:15:30 +00:00
BSHALLOW , SHALLOW , ASHALLOW ,
2018-08-28 12:27:23 +00:00
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 ,
2019-05-20 11:40:56 +00:00
PARTICLE , SWORDMARK , MAGICSWORD , MISSILE , SKY ,
2018-08-28 12:27:23 +00:00
MINEMARK , ARROW ,
MOBILE_ARROW ,
2019-02-27 00:16:07 +00:00
LINE ,
2019-03-04 17:00:51 +00:00
// in depth tested models transparent surfaces need to be depth sorted by HyperRogue
2019-05-13 11:18:55 +00:00
// and set to PPR::TRANSPARENT_* to draw them after all the opaque ones
TRANSPARENT_LAKE , TRANSPARENT_SHADOW , TRANSPARENT_WALL ,
2019-02-27 00:16:07 +00:00
// no depth testing for SUPERLINE and above
SUPERLINE , TEXT , CIRCLE ,
2018-08-28 12:27:23 +00:00
MAX ,
DEFAULT = - 1
2017-03-23 10:53:57 +00:00
} ;
2018-08-28 11:45:11 +00:00
2018-09-01 11:50:56 +00:00
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 ) ; }
2018-08-28 12:27:23 +00:00
2017-03-23 10:53:57 +00:00
# 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
2017-07-04 13:38:33 +00:00
# define OUTLINE_DEFAULT ((bordcolor << 8) + 0xFF)
# define OUTLINE_FORE ((forecolor << 8) + 0xFF)
# define OUTLINE_BACK ((backcolor << 8) + 0xFF)
2017-03-23 10:53:57 +00:00
2019-08-10 00:16:48 +00:00
enum orbAction { roMouse , roKeyboard , roCheck , roMouseForce , roMultiCheck , roMultiGo } ;
2017-03-23 10:53:57 +00:00
# define MODELCOUNT ((int) mdGUARD)
2020-04-16 22:53:58 +00:00
# define pconf vid.projection_config
2020-07-03 12:42:33 +00:00
# if CAP_RUG
2020-04-16 22:53:58 +00:00
# define vpconf (rug::rugged ? vid.rug_config : vid.projection_config)
2020-07-03 12:42:33 +00:00
# else
# define vpconf pconf
# endif
2020-04-16 22:53:58 +00:00
# define pmodel (pconf.model)
2017-11-13 10:26:21 +00:00
2019-08-02 20:09:19 +00:00
static const int DISTANCE_UNKNOWN = 127 ;
2017-06-18 16:51:46 +00:00
2020-04-12 00:44:31 +00:00
template < class T , class U > int addHook ( hookset < T > & m , int prio , U & & hook ) {
return m . add ( prio , static_cast < U & & > ( hook ) ) ;
2017-07-10 18:47:38 +00:00
}
2020-09-11 09:17:12 +00:00
template < class T > void delHook ( hookset < T > & m , int prio ) {
m . del ( prio ) ;
}
2020-04-12 00:44:31 +00:00
template < class T , class . . . U > void callhooks ( const hookset < T > & h , U & & . . . args ) {
return h . callhooks ( static_cast < U & & > ( args ) . . . ) ;
2017-07-10 18:47:38 +00:00
}
2020-04-12 00:44:31 +00:00
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 ) . . . ) ;
2017-07-10 18:47:38 +00:00
}
2019-08-10 00:16:48 +00:00
string XLAT ( string ) ;
2017-10-29 11:46:57 +00:00
2017-07-10 18:47:38 +00:00
# define GLERR(call) glError(call, __FILE__, __LINE__)
# define SHMUPTITLE "shoot'em up mode"
2018-12-06 10:43:10 +00:00
// check for a plain number key
2018-12-06 11:31:51 +00:00
# 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)
2017-07-12 16:03:53 +00:00
namespace scores { void load ( ) ; }
2020-05-03 18:56:41 +00:00
# if ISMOBILE
2017-07-12 16:03:53 +00:00
namespace leader { void showMenu ( ) ; void handleKey ( int sym , int uni ) ; }
# endif
2017-07-22 23:33:27 +00:00
int textwidth ( int siz , const string & str ) ;
# if CAP_GL
int gl_width ( int size , const char * s ) ;
# endif
2020-05-03 18:56:41 +00:00
# if ISMOBILE
2017-07-22 23:33:27 +00:00
extern int andmode ;
2018-07-22 13:54:44 +00:00
extern bool longclick ;
extern bool useRangedOrb ;
2017-07-22 23:33:27 +00:00
# endif
2017-08-06 12:50:16 +00:00
2017-10-29 11:46:57 +00:00
# ifndef GL
typedef float GLfloat ;
# endif
2019-02-22 19:58:40 +00:00
typedef array < GLfloat , 2 > glvec2 ;
2018-02-11 18:08:17 +00:00
typedef array < GLfloat , 3 > glvec3 ;
typedef array < GLfloat , 4 > glvec4 ;
2019-02-24 18:40:01 +00:00
2019-02-27 22:30:26 +00:00
# if MAXMDIM == 4
2019-02-24 18:40:01 +00:00
# define SHDIM 4
2019-02-22 19:58:40 +00:00
typedef glvec4 glvertex ;
2019-02-24 18:40:01 +00:00
# else
# define SHDIM 3
typedef glvec3 glvertex ;
# endif
2018-02-11 18:08:17 +00:00
2017-10-29 13:19:51 +00:00
extern int emeraldtable [ 100 ] [ 7 ] ;
2018-03-24 11:59:01 +00:00
// extern cell *cwpeek(cellwalker cw, int dir);
2017-10-29 13:19:51 +00:00
# define HAUNTED_RADIUS getDistLimit()
# define UNKNOWN 65535
# define GRAIL_FOUND 0x4000
# define GRAIL_RADIUS_MASK 0x3FFF
2019-08-09 21:24:33 +00:00
extern vector < cell * > dcal ;
2017-11-03 18:20:54 +00:00
// z to close to this limit => do not draw
# define BEHIND_LIMIT 1e-6
2017-11-03 18:31:42 +00:00
2018-01-26 00:45:49 +00:00
template < class T , class U > void eliminate_if ( vector < T > & data , U pred ) {
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( data ) ; i + + )
2018-01-26 00:45:49 +00:00
if ( pred ( data [ i ] ) )
data [ i ] = data . back ( ) , data . pop_back ( ) , i - - ;
}
2019-03-01 17:53:32 +00:00
template < class T > array < T , 4 > make_array ( T a , T b , T c , T d ) { array < T , 4 > x ; x [ 0 ] = a ; x [ 1 ] = b ; x [ 2 ] = c ; x [ 3 ] = d ; return x ; }
2018-02-11 18:08:17 +00:00
template < class T > array < T , 3 > make_array ( T a , T b , T c ) { array < T , 3 > x ; x [ 0 ] = a ; x [ 1 ] = b ; x [ 2 ] = c ; return x ; }
template < class T > array < T , 2 > make_array ( T a , T b ) { array < T , 2 > x ; x [ 0 ] = a ; x [ 1 ] = b ; return x ; }
2021-07-18 21:02:52 +00:00
// Find in a std::map or std::unordered_map, or return null.
template < class Map , class Key >
2021-07-18 21:33:25 +00:00
const typename Map : : mapped_type * at_or_null ( const Map & map , const Key & key ) {
2021-07-18 21:02:52 +00:00
auto it = map . find ( key ) ;
2021-07-18 21:33:25 +00:00
return ( it = = map . end ( ) ) ? nullptr : & it - > second ;
2021-07-18 21:02:52 +00:00
}
2018-04-30 22:21:18 +00:00
namespace daily {
extern bool on ;
extern int daily_id ;
void setup ( ) ;
void split ( ) ;
void gifts ( ) ;
2018-05-15 21:26:04 +00:00
void turnoff ( ) ;
void showMenu ( ) ;
int find_daily_lbid ( int id ) ;
2018-05-20 13:16:21 +00:00
bool prevent_spawn_treasure_on ( cell * c ) ;
2018-05-26 23:06:12 +00:00
void handleQuit ( int sev ) ;
void uploadscore ( bool really_final ) ;
2018-04-30 22:21:18 +00:00
}
2019-05-26 16:04:02 +00:00
# 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))
2018-08-09 17:28:53 +00:00
2018-12-21 13:43:38 +00:00
# define ONEMPTY if(d == 7 && passable(c, NULL, 0) && !safety && !reptilecheat)
2019-05-09 15:20:31 +00:00
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 ) ;
}
}
}
2019-03-11 17:46:34 +00:00
2020-03-31 18:06:18 +00:00
/** 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 ) {
for ( int i = 0 ; i < 200 ; i + + ) {
ld d = ( dmin + dmax ) / 2 ;
if ( dmin = = d | | dmax = = d ) break ;
if ( f ( d ) ) dmax = d ;
else dmin = d ;
}
return dmin ;
}
2019-08-09 19:00:52 +00:00
static const int max_vec = ( 1 < < 14 ) ;
extern bool needConfirmationEvenIfSaved ( ) ;
2019-07-31 11:21:41 +00:00
2019-12-23 21:21:04 +00:00
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 ;
}
2019-08-09 13:07:43 +00:00
}
2020-02-29 16:58:59 +00:00
/** 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 ; }
2019-12-14 10:53:55 +00:00
// assert macro
# ifdef NDEBUG
2020-12-24 23:03:27 +00:00
# define hassert(condition) if(!(condition)) __builtin_unreachable()
2019-12-14 10:53:55 +00:00
# else
2020-12-25 05:06:09 +00:00
# define hassert(condition) if(!(condition)) printf("%s:%d:%s: assertion failed: %s\n", __FILE__, __LINE__, __func__, #condition)
2019-12-14 10:53:55 +00:00
# endif
2019-08-09 19:00:52 +00:00
# define IS(z) = z
2019-08-09 13:07:43 +00:00
# include "autohdr.h"
2019-08-09 19:00:52 +00:00
# undef IS
# define IS(z)
2019-08-09 23:15:41 +00:00
# define EX
2019-08-09 19:00:52 +00:00
2019-09-05 07:07:04 +00:00
# endif