2019-08-10 11:43:24 +00:00
// Hyperbolic Rogue -- starting and ending games
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
2016-08-26 09:58:03 +00:00
2019-08-10 11:43:24 +00:00
/** \file system.cpp
* \ brief changing game modes , starting , closing , loading and saving games
*/
2016-08-26 09:58:03 +00:00
2019-09-05 07:15:40 +00:00
# include "hyper.h"
2018-06-10 23:58:31 +00:00
namespace hr {
2019-08-10 00:16:48 +00:00
# if HDR
2020-03-27 20:49:57 +00:00
/** \brief This namespace has constants used as parameters in functions such as restart_game and wrongmode. */
2019-08-10 00:16:48 +00:00
namespace rg {
static const char nothing = 0 ;
static const char peace = ' P ' ;
static const char inv = ' i ' ;
static const char chaos = ' C ' ;
static const char tactic = ' t ' ;
static const char tour = ' T ' ;
static const char yendor = ' y ' ;
static const char shmup = ' s ' ;
static const char randpattern = ' r ' ;
static const char princess = ' p ' ;
static const char daily = ' d ' ;
static const char daily_off = ' D ' ;
static const char racing = ' R ' ;
static const char dualmode = ' U ' ;
2019-09-12 14:26:48 +00:00
static const char heptagons = ' 7 ' ;
2019-08-10 00:16:48 +00:00
2020-03-27 20:49:57 +00:00
/** \brief wrongmode only -- marks 'global' achievements not related to the current mode */
2019-08-10 00:16:48 +00:00
static const char global = ' x ' ;
2020-03-27 20:49:57 +00:00
/** \brief wrongmode only -- change vid.scfg.players then restart_game(rg::nothing) instead */
2019-08-10 00:16:48 +00:00
static const char multi = ' m ' ;
2020-03-27 20:49:57 +00:00
/** \brief wrongmode only -- mark achievements for special geometries/variations */
2019-08-10 00:16:48 +00:00
static const char special_geometry = ' g ' ;
}
# endif
2020-03-27 20:51:21 +00:00
/** \brief is a game map currently loaded (it is false after hr::stop_game and true after hr::start_game) */
2019-08-09 19:00:52 +00:00
EX bool game_active ;
2018-06-10 22:58:38 +00:00
2020-03-27 20:51:21 +00:00
/** \brief God mode */
2019-08-09 20:07:03 +00:00
EX bool autocheat ;
2020-03-27 19:21:08 +00:00
2020-03-27 20:51:21 +00:00
/** \brief which wall should we fill the Canvas with */
2020-01-28 15:42:07 +00:00
EX eWall canvas_default_wall = waNone ;
2016-08-26 09:58:03 +00:00
2020-03-27 20:51:21 +00:00
/** \brief the number of Black Lotuses collected -- but updated only if we manage to escape */
2019-08-09 20:07:03 +00:00
EX int truelotus ;
2016-08-26 09:58:03 +00:00
2019-08-09 20:07:03 +00:00
EX int asteroids_generated , asteroid_orbs_generated ;
2019-03-30 16:51:37 +00:00
2019-08-09 20:07:03 +00:00
EX time_t timerstart , savetime ;
EX bool timerstopped ;
2019-09-05 10:00:55 +00:00
EX int savecount ;
2019-08-09 19:00:52 +00:00
EX bool doCross = false ;
2016-08-26 09:58:03 +00:00
2019-09-06 06:17:02 +00:00
EX bool gamegen_failure ;
2018-12-04 18:16:39 +00:00
2019-08-09 20:07:03 +00:00
EX eLand top_land ;
2018-03-29 22:20:33 +00:00
2020-03-27 20:51:21 +00:00
/** \brief a comparator for version number strings */
2019-09-06 06:17:02 +00:00
EX bool verless ( string v , string cmp ) {
2017-07-22 23:33:27 +00:00
if ( isdigit ( v [ 0 ] ) & & isdigit ( v [ 1 ] ) )
v = " A " + v ;
if ( isdigit ( cmp [ 0 ] ) & & isdigit ( cmp [ 1 ] ) )
cmp = " A " + cmp ;
2017-07-16 21:00:55 +00:00
return v < cmp ;
}
2020-03-27 20:51:21 +00:00
/** \brief should we set the starting land to specialland */
2018-04-15 11:19:03 +00:00
bool do_use_special_land ( ) {
return
! safety & &
2018-11-24 14:35:02 +00:00
( peace : : on | | tactic : : on | | geometry | | NONSTDVAR | | randomPatternsMode | | yendor : : on | | racing : : on ) ;
2018-04-15 11:19:03 +00:00
}
2020-03-27 20:51:21 +00:00
/** \brief Hooks for welcomeMessage. Return true to capture. */
2020-04-11 18:47:14 +00:00
EX hookset < bool ( ) > hooks_welcome_message ;
2018-07-09 17:55:56 +00:00
2020-03-27 20:51:21 +00:00
/** \brief Print the welcome message during the start of game. Depends on the current mode and other settings. */
2019-09-06 06:17:02 +00:00
EX void welcomeMessage ( ) {
2018-07-09 17:55:56 +00:00
if ( callhandlers ( false , hooks_welcome_message ) ) return ;
2017-08-06 12:50:16 +00:00
# if CAP_TOUR
else if ( tour : : on ) return ; // displayed by tour
# endif
else if ( princess : : challenge ) {
kills [ moVizier ] = 1 ;
princess : : forceMouse = true ;
if ( yendor : : everwon )
items [ itGreenStone ] = 99 ;
addMessage ( XLAT ( " Welcome to %the1 Challenge! " , moPrincess ) ) ;
addMessage ( XLAT ( " The more Hypersian Rugs you collect, the harder it is. " , moPrincess ) ) ;
2018-02-08 21:27:12 +00:00
}
2017-08-06 12:50:16 +00:00
else if ( randomPatternsMode )
addMessage ( XLAT ( " Welcome to the Random Pattern mode! " ) ) ;
else if ( tactic : : on )
addMessage ( XLAT ( " You are playing %the1 in the Pure Tactics mode. " , firstland ) ) ;
else if ( yendor : : on )
addMessage ( XLAT ( " Welcome to the Yendor Challenge %1! " , its ( yendor : : challenge ) ) ) ;
else if ( peace : : on ) ; // no welcome message
else if ( shmup : : on ) ; // welcome message given elsewhere
else if ( euclid )
addMessage ( XLAT ( " Welcome to the Euclidean mode! " ) ) ;
2018-12-25 22:59:47 +00:00
else if ( specialland = = laHalloween & & BITRUNCATED & & among ( geometry , gSphere , gElliptic ) )
2017-08-06 12:50:16 +00:00
addMessage ( XLAT ( " Welcome to Halloween! " ) ) ;
2019-08-27 19:43:17 +00:00
else if ( elliptic & & WDIM = = 2 )
2017-08-06 12:50:16 +00:00
addMessage ( XLAT ( " Good luck in the elliptic plane! " ) ) ;
2019-08-27 19:43:17 +00:00
else if ( elliptic )
addMessage ( XLAT ( " Good luck in the elliptic space! " ) ) ;
2017-08-06 12:50:16 +00:00
else if ( sphere )
addMessage ( XLAT ( " Welcome to Spherogue! " ) ) ;
2019-09-12 18:27:00 +00:00
else if ( in_s2xe ( ) )
addMessage ( XLAT ( " Welcome to Spherindrogue! " ) ) ;
else if ( in_h2xe ( ) )
addMessage ( XLAT ( " Welcome to Hyper-X-R-Rogue! " ) ) ;
2019-07-30 11:02:24 +00:00
else if ( sol )
addMessage ( XLAT ( " Welcome to SolvRogue! " ) ) ;
2019-08-27 19:43:17 +00:00
else if ( nil )
addMessage ( XLAT ( " Welcome to NilRogue! " ) ) ;
else if ( sl2 ) {
2020-07-24 00:30:50 +00:00
if ( cgi . psl_steps % hybrid : : csteps = = 0 )
addMessage ( XLAT ( " Welcome to PSL(2,R)-ogue! " ) ) ;
else
addMessage ( XLAT ( " Welcome to SL(2,R)-ogue! " ) ) ;
2019-08-27 19:43:17 +00:00
if ( hybrid : : underlying = = gNormal & & BITRUNCATED )
addMessage ( XLAT ( " Hint: this is more playable with pure {7,3} or pure {5,4} " ) ) ;
}
2018-12-25 22:59:47 +00:00
else if ( PURE & & geometry = = gNormal & & ! cheater )
addMessage ( XLAT ( " Welcome to the Heptagonal Mode! " ) ) ;
else if ( cheater | | autocheat )
addMessage ( XLAT ( " Welcome to HyperRogue! (cheat mode on) " ) ) ;
else
addMessage ( XLAT ( " Welcome to HyperRogue! " ) ) ;
2017-08-06 12:50:16 +00:00
2018-05-15 21:26:04 +00:00
if ( do_use_special_land ( ) | | firstland ! = laIce ) if ( ! daily : : on ) {
2018-04-15 11:19:03 +00:00
auto lv = land_validity ( specialland ) ;
if ( lv . flags & lv : : display_error_message )
addMessage ( XLAT ( lv . msg ) ) ;
}
2017-08-06 12:50:16 +00:00
# if ISMAC
addMessage ( XLAT ( " Press F1 or right-shift-click things for help. " ) ) ;
# elif !ISMOBILE
addMessage ( XLAT ( " Press F1 or right-click things for help. " ) ) ;
# endif
}
2017-10-30 21:47:18 +00:00
2020-03-27 20:51:21 +00:00
/** \brief These hooks are called at the start of initgame. */
2020-04-11 18:47:14 +00:00
EX hookset < void ( ) > hooks_initgame ;
2018-05-25 23:11:19 +00:00
2020-03-27 20:51:21 +00:00
/** \brief initialize the game */
2019-08-09 19:18:13 +00:00
EX void initgame ( ) {
2019-05-12 23:57:40 +00:00
DEBBI ( DF_INIT , ( " initGame " ) ) ;
2018-05-25 23:11:19 +00:00
callhooks ( hooks_initgame ) ;
2016-08-26 09:58:03 +00:00
2018-11-17 18:26:35 +00:00
if ( multi : : players < 1 | | multi : : players > MAXPLAYER )
multi : : players = 1 ;
multi : : whereto [ 0 ] . d = MD_UNDECIDED ;
multi : : cpid = 0 ;
2018-12-04 18:16:39 +00:00
2016-08-26 09:58:03 +00:00
yendor : : init ( 1 ) ;
if ( safety & & safetyseed ) {
shrand ( safetyseed ) ;
firstland = safetyland ;
}
2018-11-30 13:47:13 +00:00
# if CAP_RACING
2018-11-24 14:35:02 +00:00
if ( racing : : on ) racing : : apply_seed ( ) ;
2018-11-30 13:47:13 +00:00
# endif
2018-11-24 14:35:02 +00:00
2018-04-15 11:19:03 +00:00
bool use_special_land = do_use_special_land ( ) ;
2018-04-11 15:44:46 +00:00
if ( use_special_land ) firstland = specialland ;
2016-08-26 09:58:03 +00:00
if ( firstland = = laNone | | firstland = = laBarrier )
firstland = laCrossroads ;
2017-03-23 10:53:57 +00:00
if ( firstland = = laCrossroads5 & & ! tactic : : on ) firstland = laCrossroads2 ; // could not fit!
2016-08-26 09:58:03 +00:00
if ( firstland = = laOceanWall ) firstland = laOcean ;
if ( firstland = = laHauntedWall ) firstland = laGraveyard ;
2017-10-13 19:28:14 +00:00
if ( firstland = = laMercuryRiver ) firstland = laTerracotta ;
2017-03-23 10:53:57 +00:00
if ( firstland = = laMountain & & ! tactic : : on ) firstland = laJungle ;
2018-04-15 11:19:13 +00:00
if ( firstland = = laPrincessQuest ) firstland = laPalace ;
2020-03-02 02:06:27 +00:00
if ( firstland = = laMemory ) firstland = laIce ;
2018-08-04 20:36:21 +00:00
if ( ( isGravityLand ( firstland ) & & ! isCyclic ( firstland ) ) | | ( firstland = = laOcean & & ! safety & & ! yendor : : on ) )
2018-04-11 15:44:46 +00:00
firstland = weirdhyperbolic ? laCrossroads4 : laCrossroads ;
2016-08-26 09:58:03 +00:00
2019-07-03 03:02:26 +00:00
clear_euland ( specialland ) ;
2018-08-17 22:46:45 +00:00
cwt . at = currentmap - > gamestart ( ) ; cwt . spin = 0 ; cwt . mirrored = false ;
cwt . at - > land = firstland ;
2018-10-25 00:43:14 +00:00
if ( firstland = = laBrownian ) brownian : : init ( cwt . at ) ;
2016-08-26 09:58:03 +00:00
chaosAchieved = false ;
2019-12-27 13:02:21 +00:00
clearing : : direct = 0 ;
clearing : : imputed = 0 ;
2018-08-17 22:46:45 +00:00
if ( firstland = = laElementalWall ) cwt . at - > land = randomElementalLand ( ) ;
2016-08-26 09:58:03 +00:00
2019-06-13 16:13:32 +00:00
resetview ( ) ;
2018-08-17 22:46:45 +00:00
createMov ( cwt . at , 0 ) ;
2017-03-23 10:53:57 +00:00
2019-05-27 05:17:39 +00:00
pregen ( ) ;
2018-08-17 22:46:45 +00:00
setdist ( cwt . at , BARLEV , NULL ) ;
2016-08-26 09:58:03 +00:00
2019-05-16 14:57:14 +00:00
if ( ! use_special_land & & ! safety ) {
2019-05-15 16:21:32 +00:00
if ( firstland ! = ( princess : : challenge ? laPalace : laIce ) ) cheater + + ;
}
2017-07-04 13:38:33 +00:00
if ( ( tactic : : on | | yendor : : on | | peace : : on ) & & isCyclic ( firstland ) ) {
2019-12-08 18:17:28 +00:00
camelot : : anthraxBonus = items [ itHolyGrail ] ;
2018-08-17 22:46:45 +00:00
cwt . at - > move ( 0 ) - > land = firstland ;
if ( firstland = = laWhirlpool ) cwt . at - > move ( 0 ) - > wall = waSea ;
2016-08-26 09:58:03 +00:00
2018-08-17 22:46:45 +00:00
setdist ( cwt . at - > move ( 0 ) , BARLEV - 1 , cwt . at ) ;
2019-05-05 15:38:48 +00:00
if ( ! sphere & & ! eubinary & & ! quotient ) {
2018-08-17 22:46:45 +00:00
heptagon * h = createAlternateMap ( cwt . at , 2 , hsA ) ;
2017-10-30 21:47:18 +00:00
if ( ! h ) printf ( " FAIL \n " ) ;
}
2016-08-26 09:58:03 +00:00
}
if ( tactic : : on & & firstland = = laPower ) {
items [ itOrbSpeed ] = 30 ;
items [ itOrbWinter ] = 30 ;
items [ itOrbFlash ] = 30 ;
}
2020-04-12 18:38:17 +00:00
if ( firstland = = laCA )
items [ itOrbAether ] = 2 ;
2016-08-26 09:58:03 +00:00
if ( tactic : : on & & firstland = = laCaribbean ) {
2017-03-23 10:53:57 +00:00
if ( hiitemsMax ( itRedGem ) > = 25 ) items [ itRedGem ] = min ( hiitemsMax ( itRedGem ) , 50 ) ;
if ( hiitemsMax ( itFernFlower ) > = 25 ) items [ itFernFlower ] = min ( hiitemsMax ( itFernFlower ) , 50 ) ;
if ( hiitemsMax ( itWine ) > = 25 ) items [ itWine ] = min ( hiitemsMax ( itWine ) , 50 ) ;
2016-08-26 09:58:03 +00:00
}
yendor : : lastchallenge = yendor : : challenge ;
2018-11-17 18:26:35 +00:00
if ( shmup : : on ) shmup : : init ( ) ;
2016-08-26 09:58:03 +00:00
yendor : : init ( 2 ) ;
2018-11-17 18:30:50 +00:00
# if CAP_RACING
if ( racing : : on ) racing : : generate_track ( ) ;
# endif
2018-12-04 18:20:44 +00:00
if ( gamegen_failure ) return ;
2018-12-04 18:16:39 +00:00
2017-08-06 12:50:16 +00:00
if ( euclid & & specialland = = laPrincessQuest ) {
2019-12-08 09:59:09 +00:00
cell * c = euc : : at ( princess : : coords ( ) ) ;
2016-08-26 09:58:03 +00:00
princess : : generating = true ;
c - > land = laPalace ;
2018-04-03 21:34:47 +00:00
setdist ( c , 7 - getDistLimit ( ) - genrange_bonus , NULL ) ;
2016-08-26 09:58:03 +00:00
princess : : generating = false ;
}
2018-08-17 22:46:45 +00:00
if ( cwt . at - > land = = laCrossroads2 ) {
2019-08-26 13:55:30 +00:00
cell * c = cwt . at ;
2019-11-28 22:09:04 +00:00
if ( hybri ) { c = hybrid : : get_where ( c ) . first ; PIU ( c - > cmove ( 0 ) ) ; }
2019-08-26 13:55:30 +00:00
c - > landparam = 12 ;
c - > cmove ( 0 ) - > landparam = 44 ;
c - > cmove ( 0 ) - > land = laCrossroads2 ;
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
2019-05-28 23:01:13 +00:00
sword : : determine_sword_angles ( ) ;
2019-06-25 08:05:39 +00:00
for ( int i = 0 ; i < numplayers ( ) ; i + + )
sword : : dir [ i ] = sword : : initial ( cwt . at ) ;
2016-08-26 09:58:03 +00:00
2018-04-30 22:21:18 +00:00
# if CAP_DAILY
daily : : split ( ) ;
# endif
2016-08-26 09:58:03 +00:00
// extern int sightrange; sightrange = 9;
2018-08-17 22:46:45 +00:00
// cwt.at->land = laHell; items[itHell] = 10;
2018-04-03 21:34:47 +00:00
for ( int i = BARLEV ; i > = 7 - getDistLimit ( ) - genrange_bonus ; i - - ) {
2018-08-17 22:46:45 +00:00
setdist ( cwt . at , i , NULL ) ;
2018-03-29 22:20:33 +00:00
2017-04-04 09:13:15 +00:00
currentmap - > verify ( ) ;
2017-03-23 10:53:57 +00:00
}
2017-05-27 19:40:40 +00:00
if ( doCross ) {
for ( int i = 0 ; i < ittypes ; i + + ) if ( itemclass ( eItem ( i ) ) = = IC_TREASURE ) items [ i ] = 50 ;
for ( int i = 0 ; i < motypes ; i + + ) kills [ i ] = 30 ;
items [ itSavedPrincess ] = 0 ;
kills [ moPrincessMoved ] = 0 ;
kills [ moPrincessArmedMoved ] = 0 ;
kills [ moPlayer ] = 0 ;
}
2017-03-23 10:53:57 +00:00
if ( quotient & & generateAll ( firstland ) ) {
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( currentmap - > allcells ( ) ) ; i + + )
2017-04-04 09:13:15 +00:00
setdist ( currentmap - > allcells ( ) [ i ] , 8 , NULL ) ;
2017-03-23 10:53:57 +00:00
}
if ( multi : : players > 1 & & ! shmup : : on ) for ( int i = 0 ; i < numplayers ( ) ; i + + ) {
2018-08-17 22:46:45 +00:00
int idir = ( 3 * i ) % cwt . at - > type ;
multi : : player [ i ] . at = cwt . at - > move ( idir ) ;
2017-03-23 10:53:57 +00:00
// special case -- otherwise they land on a wall
if ( firstland = = laCrossroads2 & & i = = 1 )
2018-08-17 22:46:45 +00:00
multi : : player [ 1 ] . at = cwt . at ;
2017-03-23 10:53:57 +00:00
if ( firstland = = laCrossroads2 & & i = = 6 )
2018-08-17 22:46:45 +00:00
multi : : player [ 6 ] . at = createMov ( createMov ( cwt . at , 0 ) , 3 ) ;
setdist ( cwt . at - > move ( idir ) , 7 - getDistLimit ( ) - genrange_bonus , cwt . at ) ;
2017-03-23 10:53:57 +00:00
multi : : player [ i ] . spin = 0 ;
multi : : flipped [ i ] = true ;
multi : : whereto [ i ] . d = MD_UNDECIDED ;
2016-08-26 09:58:03 +00:00
}
yendor : : init ( 3 ) ;
2017-07-04 13:38:33 +00:00
peace : : simon : : init ( ) ;
2017-03-23 10:53:57 +00:00
multi : : revive_queue . clear ( ) ;
2017-07-22 23:33:27 +00:00
# if CAP_TOUR
2017-05-27 19:40:40 +00:00
if ( tour : : on ) tour : : presentation ( tour : : pmRestart ) ;
2017-04-08 15:18:29 +00:00
# endif
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if ( multi : : players > 1 & & ! shmup : : on ) {
for ( int i = 0 ; i < numplayers ( ) ; i + + )
makeEmpty ( playerpos ( i ) ) ;
}
else {
for ( int i = 0 ; i < numplayers ( ) ; i + + )
2018-08-17 22:46:45 +00:00
makeEmpty ( cwt . at ) ;
2017-03-23 10:53:57 +00:00
}
2016-08-26 09:58:03 +00:00
2019-04-10 00:34:17 +00:00
if ( specialland = = laMinefield & & bounded ) {
bfs ( ) ;
2019-04-04 12:36:45 +00:00
generate_mines ( ) ;
2019-04-10 00:34:17 +00:00
}
2019-04-04 12:36:45 +00:00
2017-07-16 21:00:55 +00:00
princess : : squeaked = false ;
2018-04-14 08:24:02 +00:00
clearing : : current_root = NULL ;
2017-07-16 21:00:55 +00:00
2016-08-26 09:58:03 +00:00
if ( ! safety ) {
usedSafety = false ;
timerstart = time ( NULL ) ; turncount = 0 ; rosewave = 0 ; rosephase = 0 ;
noiseuntil = 0 ;
sagephase = 0 ; hardcoreAt = 0 ;
timerstopped = false ;
savecount = 0 ; savetime = 0 ;
cheater = 0 ;
if ( autocheat ) cheater = 1 ;
2020-02-26 00:39:41 +00:00
if ( ! wfc : : use_eclectic ) cheater = 1 ;
2020-03-21 18:06:54 +00:00
if ( ! autocheat & & ! cheater & & geometry = = gNormal ) patterns : : whichShape = 0 ;
2016-08-26 09:58:03 +00:00
hauntedWarning = false ;
2018-12-24 14:10:52 +00:00
if ( ! autocheat ) {
timerghost = true ;
gen_wandering = true ;
}
2016-08-26 09:58:03 +00:00
truelotus = 0 ;
2019-03-30 16:51:37 +00:00
asteroids_generated = 0 ;
asteroid_orbs_generated = 0 ;
2020-01-28 16:57:32 +00:00
dpgen : : in = false ;
2016-08-26 09:58:03 +00:00
survivalist = true ;
2019-02-17 17:28:20 +00:00
# if CAP_CRYSTAL
2018-12-25 22:54:34 +00:00
crystal : : used_compass_inside = false ;
2019-02-17 17:28:20 +00:00
# endif
2020-03-06 13:22:36 +00:00
got_achievements = { } ;
2017-07-22 23:33:27 +00:00
# if CAP_INV
2017-07-04 13:38:33 +00:00
if ( inv : : on ) inv : : init ( ) ;
# endif
2019-12-08 18:17:28 +00:00
mine : : auto_teleport_charges ( ) ;
2018-04-14 07:34:33 +00:00
if ( ! use_special_land ) {
2016-08-26 09:58:03 +00:00
if ( firstland ! = ( princess : : challenge ? laPalace : laIce ) ) cheater + + ;
}
2017-08-06 12:50:16 +00:00
welcomeMessage ( ) ;
2016-08-26 09:58:03 +00:00
}
else {
usedSafety = true ;
safety = false ;
}
2017-03-23 10:53:57 +00:00
havewhat = hadwhat = 0 ; rosemap . clear ( ) ;
2016-08-26 09:58:03 +00:00
elec : : lightningfast = 0 ;
2017-03-23 10:53:57 +00:00
lastsafety = gold ( ) ;
2016-08-26 09:58:03 +00:00
bfs ( ) ;
2017-11-13 10:08:06 +00:00
checkmove ( ) ;
2018-03-25 13:07:11 +00:00
playermoved = true ;
2018-06-28 08:29:32 +00:00
if ( quotient | | sphere )
for ( cell * c : currentmap - > allcells ( ) ) setdist ( c , 8 , NULL ) ;
2018-04-03 21:34:47 +00:00
2019-02-09 11:23:45 +00:00
if ( ! allowChangeRange ( ) ) {
2018-11-01 17:59:25 +00:00
gamerange_bonus = genrange_bonus = 0 ;
if ( vid . use_smart_range = = 2 ) vid . use_smart_range = 1 ;
}
if ( ! allowIncreasedSight ( ) ) vid . use_smart_range = 0 ;
2016-08-26 09:58:03 +00:00
}
2017-03-23 10:53:57 +00:00
bool havesave = true ;
2017-07-22 23:33:27 +00:00
# if CAP_SAVE
2019-09-06 06:17:02 +00:00
2020-03-27 19:37:43 +00:00
/** \brief A namespace for loading and saving scores and saved games (system.cpp), and for displaying these scores (scores.cpp).
*
* Most ApplyBox functions are used both for saving savegames and scores to the logfile , loading savegames and scores from the logfile ,
* and loading highscore information from the logfile . The flags saving , loading , and loadingHi specify what is actually done .
*/
2020-03-27 19:05:58 +00:00
EX namespace scores {
2019-09-06 06:17:02 +00:00
# if HDR
2020-03-27 20:51:21 +00:00
/** \brief the amount of boxes reserved for each hr::score item */
2017-07-16 21:00:55 +00:00
# define MAXBOX 500
2020-03-27 20:51:21 +00:00
/** \brief currently used boxes in hr::score */
2020-03-27 19:21:08 +00:00
# define POSSCORE 387
2020-03-27 20:51:21 +00:00
/** \brief a struct to keep local score from an earlier game */
2016-08-26 09:58:03 +00:00
struct score {
2020-03-27 20:51:21 +00:00
/** \brief version used */
2016-08-26 09:58:03 +00:00
string ver ;
2020-03-27 20:51:21 +00:00
/** \brief all the data of the saved score, see applyBoxes() */
2016-08-26 09:58:03 +00:00
int box [ MAXBOX ] ;
} ;
2019-09-06 06:17:02 +00:00
# endif
2016-08-26 09:58:03 +00:00
2020-03-27 20:51:21 +00:00
/** \brief the current save */
2020-03-27 19:37:43 +00:00
EX score save ;
2020-03-27 20:51:21 +00:00
/** \brief the index of the next box */
2020-03-27 19:21:08 +00:00
EX int boxid ;
2020-03-27 20:51:21 +00:00
/** \brief see hr::applyBox */
2019-09-06 06:17:02 +00:00
EX bool saving , loading , loadingHi ;
2016-08-26 09:58:03 +00:00
2020-03-27 20:51:21 +00:00
/** \brief names of all the boxes */
2019-09-06 06:17:02 +00:00
EX string boxname [ MAXBOX ] ;
2020-03-27 20:51:21 +00:00
/** \brief 'fake' boxes should not appear when examining local scores */
2019-09-06 06:17:02 +00:00
EX bool fakebox [ MAXBOX ] ;
2020-03-27 20:51:21 +00:00
/** \brief does this box contain monster kills */
2019-09-06 06:17:02 +00:00
EX bool monsbox [ MAXBOX ] ;
2016-08-26 09:58:03 +00:00
2020-03-27 20:51:21 +00:00
/** \brief the next box should contain t */
2016-08-26 09:58:03 +00:00
void applyBox ( int & t ) {
2020-03-27 19:21:08 +00:00
if ( saving ) save . box [ boxid + + ] = t ;
else if ( loading ) t = save . box [ boxid + + ] ;
2016-08-26 09:58:03 +00:00
else boxid + + ;
}
2020-03-27 20:51:21 +00:00
/** \brief the next box should contain tb */
2020-04-17 13:04:27 +00:00
void applyBoxBignum ( bignum & tb , string name ) {
2019-12-25 10:53:50 +00:00
float tf ;
int ti ;
if ( saving ) tf = tb . approx_ld ( ) ;
if ( saving ) memcpy ( & ti , & tf , 4 ) ;
2020-04-17 13:04:27 +00:00
applyBoxNum ( ti , name ) ;
2019-12-25 10:53:50 +00:00
if ( loading ) memcpy ( & tf , & ti , 4 ) ;
if ( loading ) tb = bignum ( tf ) ;
}
2020-03-27 20:51:21 +00:00
/** \brief the next box should contain i, and possibly be named name */
2019-08-09 20:07:03 +00:00
EX void applyBoxNum ( int & i , string name IS ( " " ) ) {
2020-04-17 13:04:27 +00:00
fakebox [ boxid ] = ( name = = " " | | name [ 0 ] = = ' @ ' ) ;
2016-08-26 09:58:03 +00:00
boxname [ boxid ] = name ;
2017-03-23 10:53:57 +00:00
monsbox [ boxid ] = false ;
2016-08-26 09:58:03 +00:00
applyBox ( i ) ;
}
2020-03-27 20:51:21 +00:00
/** \brief the next box should contain b, and possibly be named name */
2016-08-26 09:58:03 +00:00
void applyBoxBool ( bool & b , string name = " " ) {
int i = b ;
applyBoxNum ( i , name ) ;
2017-03-23 10:53:57 +00:00
monsbox [ boxid ] = false ;
2016-08-26 09:58:03 +00:00
b = i ;
}
2020-03-27 20:51:21 +00:00
/** \brief Save i while saving, do nothing while loading. Use together with hr::scores::applyBoxLoad and boxid++ */
2016-08-26 09:58:03 +00:00
void applyBoxSave ( int i , string name = " " ) {
fakebox [ boxid ] = ( name = = " " ) ;
boxname [ boxid ] = name ;
applyBox ( i ) ;
}
2020-03-27 20:51:21 +00:00
/** \brief Load i while loading, do nothing while saving. Use together with hr::scores::applyBoxSave and boxid++ */
2016-08-26 09:58:03 +00:00
int applyBoxLoad ( string name = " " ) {
fakebox [ boxid ] = ( name = = " " ) ;
boxname [ boxid ] = name ;
int i = 0 ; applyBox ( i ) ;
return i ;
}
2020-03-27 20:51:21 +00:00
/** \brief the next box is the number of collected items it */
2016-08-26 09:58:03 +00:00
void applyBoxI ( eItem it , bool f = false ) {
boxname [ boxid ] = iinf [ it ] . name ;
fakebox [ boxid ] = f ;
2017-03-23 10:53:57 +00:00
monsbox [ boxid ] = false ;
2016-08-26 09:58:03 +00:00
if ( loadingHi ) {
2020-03-27 19:21:08 +00:00
updateHi ( it , save . box [ boxid + + ] ) ;
2016-08-26 09:58:03 +00:00
}
else applyBox ( items [ it ] ) ;
}
2017-07-16 21:00:55 +00:00
vector < eItem > invorb ;
void addinv ( eItem it ) {
invorb . push_back ( it ) ;
}
2020-03-27 20:51:21 +00:00
/** \brief Handle the information about orb it. Need to call list_invorb later */
2017-07-16 21:00:55 +00:00
void applyBoxOrb ( eItem it ) {
applyBoxI ( it , true ) ;
invorb . push_back ( it ) ;
}
2020-03-27 20:51:21 +00:00
/** \brief Handle the OSM information for all orbs that applyBoxOrb has been called for so far */
2017-07-16 21:00:55 +00:00
void list_invorb ( ) {
for ( eItem it : invorb ) {
2017-07-22 23:33:27 +00:00
# if CAP_INV
2017-07-16 21:00:55 +00:00
if ( true ) {
inv : : applyBox ( it ) ;
continue ;
}
# endif
int u = 0 ;
applyBoxNum ( u ) ;
}
invorb . clear ( ) ;
}
2020-03-27 20:51:21 +00:00
/** \brief handle the number of monsters of type m killed */
2016-08-26 09:58:03 +00:00
void applyBoxM ( eMonster m , bool f = false ) {
fakebox [ boxid ] = f ;
boxname [ boxid ] = minf [ m ] . name ;
2017-03-23 10:53:57 +00:00
monsbox [ boxid ] = true ;
2016-08-26 09:58:03 +00:00
applyBox ( kills [ m ] ) ;
}
2020-03-27 20:51:21 +00:00
/** \brief Call applyBox for all the required values. This will save the values if hr::scores::saving==true, load if hr::scores::loading==true, load into highscores if hr::scores::loadingHi==true */
2019-09-06 06:17:02 +00:00
EX void applyBoxes ( ) {
2017-07-16 21:00:55 +00:00
invorb . clear ( ) ;
2016-08-26 09:58:03 +00:00
eLand lostin = laNone ;
applyBoxSave ( ( int ) timerstart , " time elapsed " ) ;
time_t timer = time ( NULL ) ;
applyBoxSave ( ( int ) timer , " date " ) ;
applyBoxSave ( gold ( ) , " treasure collected " ) ;
applyBoxSave ( tkills ( ) , " total kills " ) ;
applyBoxNum ( turncount , " turn count " ) ;
applyBoxNum ( cellcount , " cells generated " ) ;
2017-03-23 10:53:57 +00:00
if ( loading ) timerstart = time ( NULL ) ;
2016-08-26 09:58:03 +00:00
for ( int i = 0 ; i < itOrbLightning ; i + + )
if ( i = = 0 ) items [ i ] = 0 , applyBoxI ( itFernFlower ) ;
else applyBoxI ( eItem ( i ) ) ;
for ( int i = 0 ; i < 43 ; i + + ) {
if ( loading ) kills [ i ] = 0 ;
bool fake =
2017-03-23 10:53:57 +00:00
i = = moLesserM | | i = = moNone | | i = = moWolfMoved | | i = = moTentacletail | |
i = = moIvyNext ;
2016-08-26 09:58:03 +00:00
if ( i = = moWormtail ) applyBoxM ( moCrystalSage ) ;
else if ( i = = moWormwait ) applyBoxM ( moFireFairy ) ;
else if ( i = = moTentacleEscaping ) applyBoxM ( moMiner ) ;
else if ( i = = moGolemMoved ) applyBoxM ( moIllusion ) ;
2017-07-16 21:00:55 +00:00
else if ( i = = moTentaclewait ) applyBoxOrb ( itOrbThorns ) ;
else if ( i = = moGreater ) applyBoxOrb ( itOrbDragon ) ;
else if ( i = = moGreaterM ) applyBoxOrb ( itOrbIllusion ) ;
2016-08-26 09:58:03 +00:00
else applyBoxM ( eMonster ( i ) , fake ) ;
}
if ( saving ) {
2017-03-23 10:53:57 +00:00
int totaltime = savetime ;
if ( ! timerstopped ) totaltime + = timer - timerstart ;
applyBoxSave ( ( int ) totaltime , " time played " ) ;
2016-08-26 09:58:03 +00:00
}
else if ( loading ) savetime = applyBoxLoad ( " time played " ) ;
2017-03-23 10:53:57 +00:00
else boxname [ boxid ] = " time played " , boxid + + ;
2016-08-26 09:58:03 +00:00
if ( saving ) savecount + + ;
applyBoxNum ( savecount , " number of saves " ) ;
if ( saving ) savecount - - ;
applyBoxNum ( cheater , " number of cheats " ) ;
2017-03-23 10:53:57 +00:00
fakebox [ boxid ] = true ;
2020-04-17 13:04:27 +00:00
if ( saving ) applyBoxSave ( items [ itOrbSafety ] ? safetyland : cwt . at - > land , " @safetyland " ) ;
else if ( loading ) firstland = safetyland = eLand ( applyBoxLoad ( " @safetyland " ) ) ;
2020-03-27 19:21:08 +00:00
else lostin = eLand ( save . box [ boxid + + ] ) ;
2016-08-26 09:58:03 +00:00
2017-07-16 21:00:55 +00:00
for ( int i = itOrbLightning ; i < 25 ; i + + ) applyBoxOrb ( eItem ( i ) ) ;
2016-08-26 09:58:03 +00:00
applyBoxI ( itRoyalJelly ) ;
applyBoxI ( itWine ) ;
applyBoxI ( itSilver ) ;
applyBoxI ( itEmerald ) ;
applyBoxI ( itPower ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbFire ) ;
applyBoxOrb ( itOrbInvis ) ;
applyBoxOrb ( itOrbAether ) ;
applyBoxOrb ( itOrbPsi ) ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moBug0 ) ;
applyBoxM ( moBug1 ) ;
applyBoxM ( moBug2 ) ;
applyBoxM ( moVineBeast ) ;
applyBoxM ( moVineSpirit ) ;
applyBoxM ( moLancer ) ;
applyBoxM ( moFlailer ) ;
applyBoxM ( moEarthElemental ) ;
applyBoxM ( moDarkTroll ) ;
applyBoxM ( moWitch ) ;
applyBoxM ( moWitchFire ) ;
applyBoxM ( moWitchFlash ) ;
applyBoxM ( moWitchGhost ) ;
applyBoxM ( moWitchSpeed ) ;
applyBoxM ( moEvilGolem ) ;
applyBoxM ( moWitchWinter ) ;
applyBoxI ( itHolyGrail ) ;
applyBoxI ( itGrimoire ) ;
applyBoxM ( moKnight ) ;
applyBoxM ( moCultistLeader ) ;
applyBoxM ( moPirate ) ;
applyBoxM ( moCShark ) ;
applyBoxM ( moParrot ) ;
applyBoxI ( itPirate ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbTime ) ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moHexSnake ) ;
applyBoxM ( moRedTroll ) ;
applyBoxI ( itRedGem ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbSpace ) ;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
int geo = geometry ;
2020-04-17 13:04:27 +00:00
applyBoxNum ( geo , " @geometry " ) ; geometry = eGeometry ( geo ) ;
2016-08-26 09:58:03 +00:00
applyBoxBool ( hardcore , " hardcore " ) ;
2020-04-17 13:04:27 +00:00
applyBoxNum ( hardcoreAt , " @hardcoreAt " ) ;
2016-08-26 09:58:03 +00:00
applyBoxBool ( shmup : : on , " shmup " ) ;
2017-08-06 12:50:16 +00:00
if ( saving ) applyBoxSave ( specialland , " euclid land " ) ;
else if ( loading ) specialland = eLand ( applyBoxLoad ( " euclid land " ) ) ;
2017-03-23 10:53:57 +00:00
else fakebox [ boxid + + ] = true ;
2016-08-26 09:58:03 +00:00
applyBoxI ( itCoast ) ;
applyBoxI ( itWhirlpool ) ;
applyBoxI ( itBombEgg ) ;
applyBoxM ( moBomberbird ) ;
applyBoxM ( moTameBomberbird ) ;
applyBoxM ( moAlbatross ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbFriend ) ;
applyBoxOrb ( itOrbAir ) ;
applyBoxOrb ( itOrbWater ) ;
2016-08-26 09:58:03 +00:00
applyBoxI ( itPalace ) ;
applyBoxI ( itFjord ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbFrog ) ;
applyBoxOrb ( itOrbDiscord ) ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moPalace ) ;
applyBoxM ( moFatGuard ) ;
applyBoxM ( moSkeleton ) ;
applyBoxM ( moVizier ) ;
applyBoxM ( moViking ) ;
applyBoxM ( moFjordTroll ) ;
applyBoxM ( moWaterElemental ) ;
applyBoxI ( itSavedPrincess ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbLove ) ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moPrincess ) ;
2017-03-23 10:53:57 +00:00
applyBoxM ( moPrincessMoved , false ) ; // live Princess for Safety
applyBoxM ( moPrincessArmedMoved , false ) ; // live Princess for Safety
2016-08-26 09:58:03 +00:00
applyBoxM ( moMouse ) ;
2020-04-17 13:04:27 +00:00
applyBoxNum ( princess : : saveArmedHP , " @saveArmedHP " ) ;
applyBoxNum ( princess : : saveHP , " @saveHP " ) ;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
applyBoxI ( itIvory ) ;
2016-08-26 09:58:03 +00:00
applyBoxI ( itElemental ) ;
applyBoxI ( itZebra ) ;
applyBoxI ( itFireShard ) ;
applyBoxI ( itWaterShard ) ;
applyBoxI ( itAirShard ) ;
applyBoxI ( itEarthShard ) ;
applyBoxM ( moAirElemental ) ;
applyBoxM ( moFireElemental ) ;
2017-03-23 10:53:57 +00:00
applyBoxM ( moFamiliar ) ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moGargoyle ) ;
applyBoxM ( moOrangeDog ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbSummon ) ;
applyBoxOrb ( itOrbMatter ) ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moForestTroll ) ;
applyBoxM ( moStormTroll ) ;
applyBoxM ( moOutlaw ) ;
applyBoxM ( moMutant ) ;
applyBoxM ( moMetalBeast ) ;
applyBoxM ( moMetalBeast2 ) ;
applyBoxI ( itMutant ) ;
applyBoxI ( itFulgurite ) ;
applyBoxI ( itBounty ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbLuck ) ;
applyBoxOrb ( itOrbStunning ) ;
2016-08-26 09:58:03 +00:00
2020-04-17 13:04:27 +00:00
applyBoxBool ( tactic : : on , " @tactic " ) ;
applyBoxNum ( elec : : lightningfast , " @lightningfast " ) ;
2016-08-26 09:58:03 +00:00
2020-03-27 19:21:08 +00:00
// if(save.box[boxid]) printf("lotus = %d (lost = %d)\n", save.box[boxid], isHaunted(lostin));
2016-08-26 09:58:03 +00:00
if ( loadingHi & & isHaunted ( lostin ) ) boxid + + ;
else applyBoxI ( itLotus ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbUndeath ) ;
2016-08-26 09:58:03 +00:00
applyBoxI ( itWindstone ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbEmpathy ) ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moWindCrow ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itMutant2 ) ;
applyBoxOrb ( itOrbFreedom ) ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moRedFox ) ;
2020-04-17 13:04:27 +00:00
applyBoxBool ( survivalist , " @survivalist " ) ;
2016-08-26 09:58:03 +00:00
if ( loadingHi ) applyBoxI ( itLotus ) ;
else applyBoxNum ( truelotus , " lotus/escape " ) ;
2018-08-28 15:17:34 +00:00
int v = int ( variation ) ;
applyBoxNum ( v , " variation " ) ;
variation = eVariation ( v ) ;
2016-08-26 09:58:03 +00:00
applyBoxI ( itRose ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbBeauty ) ;
2016-08-26 09:58:03 +00:00
applyBoxI ( itCoral ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrb37 ) ;
applyBoxOrb ( itOrbEnergy ) ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moRatling ) ;
applyBoxM ( moFalsePrincess ) ;
applyBoxM ( moRoseLady ) ;
applyBoxM ( moRoseBeauty ) ;
2019-05-27 05:17:39 +00:00
applyBoxNum ( chaosmode , " Chaos mode " ) ;
2017-03-23 10:53:57 +00:00
applyBoxNum ( multi : : players , " shmup players " ) ;
if ( multi : : players < 1 | | multi : : players > MAXPLAYER )
multi : : players = 1 ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moRatlingAvenger ) ;
// printf("applybox %d\n", shmup::players);
applyBoxI ( itApple ) ;
2017-03-23 10:53:57 +00:00
applyBoxM ( moSparrowhawk ) ;
applyBoxM ( moResearcher ) ;
2016-08-26 09:58:03 +00:00
applyBoxI ( itDragon ) ;
applyBoxM ( moDragonHead ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbDomination ) ;
2016-08-26 09:58:03 +00:00
applyBoxI ( itBabyTortoise ) ;
2020-04-17 13:04:27 +00:00
applyBoxNum ( tortoise : : seekbits , " @seekbits " ) ;
2016-08-26 09:58:03 +00:00
applyBoxM ( moTortoise ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbShell ) ;
2017-03-23 10:53:57 +00:00
2020-04-17 13:04:27 +00:00
applyBoxNum ( safetyseed , " @safetyseed " ) ;
2017-03-23 10:53:57 +00:00
// (+18)
for ( int i = 0 ; i < 6 ; i + + ) {
2020-04-17 13:04:27 +00:00
applyBoxNum ( multi : : treasures [ i ] , " @multi-treasures " + its ( i ) ) ;
applyBoxNum ( multi : : kills [ i ] , " @multi-kills " + its ( i ) ) ;
applyBoxNum ( multi : : deaths [ i ] , " @multi-deaths " + its ( i ) ) ;
2017-03-23 10:53:57 +00:00
}
// (+8)
applyBoxM ( moDragonTail ) ;
applyBoxI ( itKraken ) ;
applyBoxM ( moKrakenH ) ;
applyBoxM ( moKrakenT ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbSword ) ;
2017-03-23 10:53:57 +00:00
applyBoxI ( itBarrow ) ;
applyBoxM ( moDraugr ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbSword2 ) ;
2017-03-23 10:53:57 +00:00
applyBoxI ( itTrollEgg ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbStone ) ;
2017-03-23 10:53:57 +00:00
bool sph ;
sph = false ; applyBoxBool ( sph , " sphere " ) ; if ( sph ) geometry = gSphere ;
sph = false ; applyBoxBool ( sph , " elliptic " ) ; if ( sph ) geometry = gElliptic ;
2020-04-17 13:04:27 +00:00
applyBoxNum ( princess : : reviveAt , " @reviveAt " ) ;
2017-03-23 10:53:57 +00:00
applyBoxI ( itDodeca ) ;
applyBoxI ( itAmethyst ) ;
applyBoxI ( itSlime ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbNature ) ;
applyBoxOrb ( itOrbDash ) ;
addinv ( itOrbRecall ) ;
2017-03-23 10:53:57 +00:00
applyBoxM ( moBat ) ;
applyBoxM ( moReptile ) ;
applyBoxM ( moFriendlyIvy ) ;
applyBoxI ( itGreenGrass ) ;
applyBoxI ( itBull ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbHorns ) ;
applyBoxOrb ( itOrbBull ) ;
2017-03-23 10:53:57 +00:00
applyBoxM ( moSleepBull ) ;
applyBoxM ( moRagingBull ) ;
applyBoxM ( moHerdBull ) ;
applyBoxM ( moButterfly ) ;
applyBoxM ( moGadfly ) ;
2017-07-16 21:00:55 +00:00
// 10.0:
2020-04-17 13:04:27 +00:00
applyBoxNum ( hinttoshow , " @hinttoshow " ) ; // 258
2017-07-16 21:00:55 +00:00
addinv ( itOrbMirror ) ;
addinv ( itGreenStone ) ;
list_invorb ( ) ;
applyBoxBool ( inv : : on , " inventory " ) ; // 306
2017-07-22 23:33:27 +00:00
# if CAP_INV
2020-04-17 13:04:27 +00:00
applyBoxNum ( inv : : rseed , " @inv-rseed " ) ;
2017-07-16 21:00:55 +00:00
# else
{ int u ; applyBoxNum ( u ) ; }
# endif
2017-10-08 12:02:03 +00:00
// 10.1:
applyBoxI ( itLavaLily ) ;
2017-10-10 12:24:39 +00:00
applyBoxI ( itHunting ) ;
2017-10-08 12:02:03 +00:00
applyBoxI ( itBlizzard ) ;
applyBoxI ( itTerra ) ;
applyBoxOrb ( itOrbSide1 ) ;
applyBoxOrb ( itOrbSide2 ) ;
applyBoxOrb ( itOrbSide3 ) ;
applyBoxOrb ( itOrbLava ) ;
applyBoxOrb ( itOrbMorph ) ;
applyBoxM ( moHunterDog ) ;
applyBoxM ( moIceGolem ) ;
applyBoxM ( moVoidBeast ) ;
applyBoxM ( moJiangshi ) ;
applyBoxM ( moTerraWarrior ) ;
applyBoxM ( moSalamander ) ;
applyBoxM ( moLavaWolf ) ;
2018-01-04 12:32:11 +00:00
applyBoxOrb ( itOrbSlaying ) ;
applyBoxOrb ( itOrbMagnetism ) ;
applyBoxOrb ( itOrbPhasing ) ;
applyBoxI ( itDock ) ;
applyBoxI ( itGlowCrystal ) ;
applyBoxI ( itMagnet ) ;
applyBoxI ( itRuins ) ;
applyBoxI ( itSwitch ) ;
applyBoxM ( moNorthPole ) ;
applyBoxM ( moSouthPole ) ;
applyBoxM ( moSwitch1 ) ;
applyBoxM ( moSwitch2 ) ;
applyBoxM ( moAltDemon ) ;
applyBoxM ( moHexDemon ) ;
applyBoxM ( moPair ) ;
applyBoxM ( moCrusher ) ;
applyBoxM ( moMonk ) ;
2018-04-10 22:30:50 +00:00
2018-08-28 15:17:34 +00:00
bool v2 = false ;
2020-04-17 13:04:27 +00:00
applyBoxBool ( v2 , " @variation " ) ; if ( loading & & v2 ) variation = eVariation : : goldberg ;
applyBoxNum ( gp : : param . first , " @gp-first " ) ;
applyBoxNum ( gp : : param . second , " @gp-second " ) ;
2018-07-17 12:28:49 +00:00
2018-08-28 15:17:34 +00:00
v2 = false ; applyBoxBool ( v2 ) ; if ( loading & & v2 ) variation = eVariation : : irregular ;
2020-04-17 13:04:27 +00:00
applyBoxNum ( irr : : cellcount , " @irr-cellcount " ) ;
2017-10-08 12:02:03 +00:00
2018-07-23 03:19:06 +00:00
list_invorb ( ) ;
2020-04-17 13:04:27 +00:00
applyBoxNum ( irr : : bitruncations_performed , " @irr-bitruncations " ) ;
2019-01-03 01:14:49 +00:00
applyBoxI ( itVarTreasure ) ;
applyBoxI ( itBrownian ) ;
applyBoxI ( itWest ) ;
applyBoxM ( moAcidBird ) ;
applyBoxM ( moBrownBug ) ;
applyBoxM ( moVariantWarrior ) ;
applyBoxM ( moWestHawk ) ;
applyBoxM ( moFallingDog ) ;
applyBoxOrb ( itOrbIntensity ) ;
applyBoxOrb ( itOrbChoice ) ;
applyBoxOrb ( itOrbGravity ) ;
list_invorb ( ) ;
2019-04-08 11:51:23 +00:00
applyBoxM ( moNarciss ) ;
applyBoxM ( moMirrorSpirit ) ;
2019-12-25 10:53:50 +00:00
2020-04-17 13:04:27 +00:00
applyBoxNum ( clearing : : direct , " @clearing-direct " ) ;
applyBoxBignum ( clearing : : imputed , " @clearing-imputed " ) ;
2020-02-26 00:43:12 +00:00
applyBoxOrb ( itOrbImpact ) ;
applyBoxOrb ( itOrbChaos ) ;
2020-02-26 01:49:35 +00:00
applyBoxOrb ( itOrbPlague ) ;
2020-02-26 00:43:12 +00:00
applyBoxI ( itEclectic ) ;
applyBoxI ( itFrog ) ;
applyBoxI ( itWet ) ;
applyBoxM ( moFrog ) ;
applyBoxM ( moPhaser ) ;
applyBoxM ( moVaulter ) ;
applyBoxM ( moPike ) ;
applyBoxM ( moRusalka ) ;
2020-03-27 19:21:08 +00:00
list_invorb ( ) ;
2018-08-05 03:07:34 +00:00
2017-03-23 10:53:57 +00:00
if ( POSSCORE ! = boxid ) printf ( " ERROR: %d boxes \n " , boxid ) ;
2020-03-27 19:21:08 +00:00
if ( isize ( invorb ) ) { println ( hlog , " ERROR: Orbs not taken into account " ) ; exit ( 1 ) ; }
2016-08-26 09:58:03 +00:00
}
2020-03-27 20:51:21 +00:00
/** \brief save the current game values to save */
2019-09-06 06:17:02 +00:00
EX void saveBox ( ) {
2016-08-26 09:58:03 +00:00
boxid = 0 ; saving = true ; applyBoxes ( ) ; saving = false ;
}
2020-03-27 20:51:21 +00:00
/** \brief load the current game values from save */
2016-08-26 09:58:03 +00:00
void loadBox ( ) {
// have boxid
boxid = 0 ; loading = true ; applyBoxes ( ) ; loading = false ;
}
2020-03-27 20:51:21 +00:00
/** \brief load the current game values from save into the highscore tables */
2016-08-26 09:58:03 +00:00
void loadBoxHigh ( ) {
2020-03-27 19:21:08 +00:00
dynamicval < int > sp1 ( multi : : players , save . box [ 197 ] ) ;
dynamicval < eGeometry > sp2 ( geometry , ( eGeometry ) save . box [ 116 ] ) ;
dynamicval < bool > sp3 ( shmup : : on , save . box [ 119 ] ) ;
dynamicval < int > sp4 ( chaosmode , save . box [ 196 ] ) ;
dynamicval < eVariation > sp5 ( variation , ( eVariation ) save . box [ 186 ] ) ;
dynamicval < int > sp7 ( gp : : param . first , save . box [ 342 ] ) ;
dynamicval < int > sp8 ( gp : : param . second , save . box [ 343 ] ) ;
dynamicval < bool > spinv ( inv : : on , save . box [ 306 ] ) ;
2018-04-10 22:30:50 +00:00
2020-03-27 19:21:08 +00:00
if ( save . box [ 238 ] ) geometry = gSphere ;
if ( save . box [ 239 ] ) geometry = gElliptic ;
if ( save . box [ 341 ] ) variation = eVariation : : goldberg ;
if ( save . box [ 344 ] ) variation = eVariation : : irregular ;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if ( multi : : players < 1 | | multi : : players > MAXPLAYER )
multi : : players = 1 ;
if ( shmup : : on & & multi : : players = = 1 ) ;
2016-08-26 09:58:03 +00:00
else {
// have boxid
boxid = 0 ; loadingHi = true ; applyBoxes ( ) ; loadingHi = false ;
}
}
2020-03-27 19:05:58 +00:00
EX }
2016-08-26 09:58:03 +00:00
// certify that saves and achievements were received
// in an official version of HyperRogue
2019-09-06 06:17:02 +00:00
EX namespace anticheat {
EX int certify ( const string & s , int a , int b , int c , int d IS ( 0 ) ) ;
EX }
2017-07-22 23:33:27 +00:00
# if CAP_CERTIFY
2017-07-04 13:38:33 +00:00
# include "private/certify.cpp"
2019-09-06 06:17:02 +00:00
# endif
2016-08-26 09:58:03 +00:00
2019-09-06 06:17:02 +00:00
# if !CAP_CERTIFY
EX namespace anticheat {
EX bool tampered ;
2016-08-26 09:58:03 +00:00
void save ( FILE * f ) { }
2020-03-27 19:05:58 +00:00
bool load ( FILE * f , scores : : score & sc , const string & ver ) { return true ; }
2019-09-06 06:17:02 +00:00
int certify ( const string & s , int a , int b , int c , int d ) { return d ; }
2016-08-26 09:58:03 +00:00
int check ( int cv , const string & ver , const string & s , int a , int b , int c , int d = 0 ) { return cv = = d ; }
void nextid ( int & tid , const string & ver , int cert ) { tid + + ; }
2019-09-06 06:17:02 +00:00
EX }
2016-08-26 09:58:03 +00:00
# endif
long long saveposition = - 1 ;
2019-09-06 06:17:02 +00:00
EX void remove_emergency_save ( ) {
2017-07-22 23:33:27 +00:00
# if !ISWINDOWS
2017-03-23 10:53:57 +00:00
if ( saveposition > = 0 ) {
if ( truncate ( scorefile , saveposition ) ) { }
saveposition = - 1 ;
}
# endif
}
2019-08-09 19:18:13 +00:00
EX void saveStats ( bool emergency IS ( false ) ) {
2020-02-23 04:47:30 +00:00
DEBBI ( DF_INIT , ( " saveStats [ " , scorefile , " ] " ) ) ;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
if ( autocheat ) return ;
2017-07-22 23:33:27 +00:00
# if CAP_TOUR
2017-04-08 15:18:29 +00:00
if ( tour : : on ) return ;
# endif
2016-08-26 09:58:03 +00:00
if ( randomPatternsMode ) return ;
2019-05-29 14:50:36 +00:00
if ( dual : : state ) return ;
2019-12-14 10:42:16 +00:00
if ( arcm : : in ( ) ) return ;
2019-12-27 01:08:19 +00:00
if ( arb : : in ( ) ) return ;
2019-08-24 12:07:46 +00:00
if ( hybri ) return ;
2018-05-15 21:26:04 +00:00
if ( daily : : on ) return ;
2017-07-04 13:38:33 +00:00
if ( peace : : on ) return ;
2020-01-28 16:57:32 +00:00
if ( dpgen : : in ) return ;
2019-09-08 10:25:00 +00:00
if ( experimental ) return ;
2020-05-16 09:24:30 +00:00
if ( ginf [ geometry ] . xcode = = no_code ) return ;
2020-07-12 18:52:32 +00:00
if ( INVERSE ) return ;
2017-07-12 16:03:53 +00:00
if ( ! gold ( ) ) return ;
2017-03-23 10:53:57 +00:00
remove_emergency_save ( ) ;
2016-08-26 09:58:03 +00:00
FILE * f = fopen ( scorefile , " at " ) ;
if ( ! f ) {
// printf("Could not open the score file '%s'!\n", scorefile);
addMessage ( XLAT ( " Could not open the score file: " ) + scorefile ) ;
return ;
}
if ( emergency ) {
saveposition = ftell ( f ) ;
2017-03-23 10:53:57 +00:00
// if(!timerghost) addMessage(XLAT("Emergency save at ") + its(saveposition));
2016-08-26 09:58:03 +00:00
}
time_t timer ;
timer = time ( NULL ) ;
char sbuf [ 128 ] ; strftime ( sbuf , 128 , " %c " , localtime ( & timerstart ) ) ;
char buf [ 128 ] ; strftime ( buf , 128 , " %c " , localtime ( & timer ) ) ;
if ( ( tactic : : on | | yendor : : on ) & & ! items [ itOrbSafety ] & & ! cheater ) {
int t = ( int ) ( timer - timerstart ) ;
2019-08-20 11:07:16 +00:00
modecode_t xcode = modecode ( ) ;
2016-08-26 09:58:03 +00:00
if ( tactic : : on ) {
2018-04-11 15:44:46 +00:00
int score = items [ treasureType ( specialland ) ] ;
2016-08-26 09:58:03 +00:00
if ( score ) {
int c =
2018-04-11 15:44:46 +00:00
anticheat : : certify ( dnameof ( specialland ) , turncount , t , ( int ) timerstart ,
2019-08-20 11:02:29 +00:00
int ( xcode ) * 999 + tactic : : id + 256 * score + ( xcode > > 32 ) * 7 ) ;
2018-01-06 21:05:22 +00:00
fprintf ( f , " TACTICS %s %d %d %d %d %d %d %d %d date: %s \n " , VER ,
2018-04-11 15:44:46 +00:00
tactic : : id , specialland , score , turncount , t , int ( timerstart ) ,
2019-08-20 11:07:16 +00:00
c , int ( xcode ) , buf ) ;
2018-04-11 15:44:46 +00:00
tactic : : record ( specialland , score ) ;
2016-08-26 09:58:03 +00:00
anticheat : : nextid ( tactic : : id , VER , c ) ;
}
}
if ( yendor : : on )
2018-01-06 21:05:22 +00:00
fprintf ( f , " YENDOR %s %d %d %d %d %d %d %d %d date: %s \n " , VER ,
2016-08-26 09:58:03 +00:00
yendor : : lastchallenge , items [ itOrbYendor ] , yendor : : won , turncount , t , int ( timerstart ) ,
anticheat : : certify ( yendor : : won ? " WON " : " LOST " , turncount , t , ( int ) timerstart ,
2019-08-20 11:02:29 +00:00
int ( xcode ) * 999 + yendor : : lastchallenge + 256 * items [ itOrbYendor ] + ( xcode > > 32 ) * 7 ) ,
2019-08-20 11:07:16 +00:00
int ( xcode ) ,
2016-08-26 09:58:03 +00:00
buf ) ;
fclose ( f ) ;
return ;
}
fprintf ( f , " HyperRogue: game statistics (version " VER " ) \n " ) ;
if ( cheater )
fprintf ( f , " CHEATER! (cheated %d times) \n " , cheater ) ;
if ( true ) {
fprintf ( f , VER ) ;
if ( ! shmup : : on ) items [ itOrbLife ] = countMyGolems ( moGolem ) ;
if ( ! shmup : : on ) items [ itOrbFriend ] = countMyGolems ( moTameBomberbird ) ;
if ( ! shmup : : on ) kills [ moPrincessMoved ] = countMyGolems ( moPrincess ) ;
if ( ! shmup : : on ) kills [ moPrincessArmedMoved ] = countMyGolems ( moPrincessArmed ) ;
if ( ! shmup : : on ) princess : : saveHP = countMyGolemsHP ( moPrincess ) ;
if ( ! shmup : : on ) princess : : saveArmedHP = countMyGolemsHP ( moPrincessArmed ) ;
2020-03-27 19:05:58 +00:00
scores : : saveBox ( ) ;
2016-08-26 09:58:03 +00:00
2020-03-27 19:21:08 +00:00
for ( int i = 0 ; i < scores : : boxid ; i + + ) fprintf ( f , " %d " , scores : : save . box [ i ] ) ;
2016-08-26 09:58:03 +00:00
anticheat : : save ( f ) ;
fprintf ( f , " \n " ) ;
}
fprintf ( f , " Played on: %s - %s (%d %s) \n " , sbuf , buf , turncount ,
shmup : : on ? " knives " : " turns " ) ;
fprintf ( f , " Total wealth: %d \n " , gold ( ) ) ;
fprintf ( f , " Total enemies killed: %d \n " , tkills ( ) ) ;
fprintf ( f , " cells generated: %d \n " , cellcount ) ;
2017-03-23 10:53:57 +00:00
if ( pureHardcore ( ) ) fprintf ( f , " Pure hardcore mode \n " ) ;
2019-08-26 14:21:05 +00:00
if ( geometry ) fprintf ( f , " Geometry: %s/%s/%s \n " , gp : : operation_name ( ) . c_str ( ) , ginf [ geometry ] . tiling_name . c_str ( ) , ginf [ geometry ] . quotient_name . c_str ( ) ) ;
2016-08-26 09:58:03 +00:00
if ( chaosmode ) fprintf ( f , " Chaos mode \n " ) ;
2017-03-23 10:53:57 +00:00
if ( shmup : : on ) fprintf ( f , " Shoot-em up mode \n " ) ;
2017-07-04 13:38:33 +00:00
if ( inv : : on ) fprintf ( f , " Inventory mode \n " ) ;
2017-03-23 10:53:57 +00:00
if ( multi : : players > 1 ) fprintf ( f , " Multi-player (%d players) \n " , multi : : players ) ;
2016-08-26 09:58:03 +00:00
fprintf ( f , " Number of cells explored, by distance from the player: \n " ) ;
2017-03-23 10:53:57 +00:00
{ for ( int i = 0 ; i < 10 ; i + + ) fprintf ( f , " %d " , explore [ i ] ) ; } fprintf ( f , " \n " ) ;
2016-08-26 09:58:03 +00:00
if ( kills [ 0 ] ) fprintf ( f , " walls melted: %d \n " , kills [ 0 ] ) ;
2018-08-17 22:46:45 +00:00
fprintf ( f , " cells travelled: %d \n " , celldist ( cwt . at ) ) ;
2016-08-26 09:58:03 +00:00
fprintf ( f , " \n " ) ;
for ( int i = 0 ; i < ittypes ; i + + ) if ( items [ i ] )
fprintf ( f , " %4dx %s \n " , items [ i ] , iinf [ i ] . name ) ;
fprintf ( f , " \n " ) ;
for ( int i = 1 ; i < motypes ; i + + ) if ( kills [ i ] )
fprintf ( f , " %4dx %s \n " , kills [ i ] , minf [ i ] . name ) ;
fprintf ( f , " \n \n \n " ) ;
2020-05-03 18:56:41 +00:00
# if !ISMOBILE
2019-05-12 23:57:40 +00:00
DEBB ( DF_INIT , ( " Game statistics saved to " , scorefile ) ) ;
2020-03-27 18:57:57 +00:00
addMessage ( XLAT ( " Game statistics saved to %1 " , scorefile ) ) ;
2016-08-26 09:58:03 +00:00
# endif
fclose ( f ) ;
}
// load the save
2019-08-09 19:18:13 +00:00
EX void loadsave ( ) {
2017-04-08 15:18:29 +00:00
if ( autocheat ) return ;
2017-07-22 23:33:27 +00:00
# if CAP_TOUR
2017-04-08 15:18:29 +00:00
if ( tour : : on ) return ;
# endif
2019-05-12 23:57:40 +00:00
DEBBI ( DF_INIT , ( " loadSave " ) ) ;
2016-08-26 09:58:03 +00:00
FILE * f = fopen ( scorefile , " rt " ) ;
havesave = f ;
if ( ! f ) return ;
2020-03-27 19:05:58 +00:00
scores : : score sc ;
2016-08-26 09:58:03 +00:00
bool ok = false ;
bool tamper = false ;
2017-07-16 21:00:55 +00:00
int coh = counthints ( ) ;
2016-08-26 09:58:03 +00:00
while ( ! feof ( f ) ) {
char buf [ 120 ] ;
if ( fgets ( buf , 120 , f ) = = NULL ) break ;
if ( buf [ 0 ] = = ' H ' & & buf [ 1 ] = = ' y ' ) {
2017-03-23 10:53:57 +00:00
if ( fscanf ( f , " %s " , buf ) < = 0 ) break ;
sc . ver = buf ;
2017-07-16 21:00:55 +00:00
if ( sc . ver [ 1 ] ! = ' . ' ) sc . ver = ' 0 ' + sc . ver ;
if ( verless ( sc . ver , " 4.4 " ) | | sc . ver = = " CHEATER! " ) { ok = false ; continue ; }
2016-08-26 09:58:03 +00:00
ok = true ;
for ( int i = 0 ; i < MAXBOX ; i + + ) {
if ( fscanf ( f , " %d " , & sc . box [ i ] ) < = 0 ) {
2020-03-27 19:05:58 +00:00
scores : : boxid = i ;
2016-08-26 09:58:03 +00:00
tamper = anticheat : : load ( f , sc , sc . ver ) ;
2020-03-27 19:05:58 +00:00
using namespace scores ;
2020-03-27 19:21:08 +00:00
for ( int i = 0 ; i < boxid ; i + + ) save . box [ i ] = sc . box [ i ] ;
for ( int i = boxid ; i < MAXBOX ; i + + ) save . box [ i ] = 0 , sc . box [ i ] = 0 ;
2016-08-26 09:58:03 +00:00
2020-03-27 19:21:08 +00:00
if ( save . box [ 258 ] > = 0 & & save . box [ 258 ] < coh ) {
hints [ save . box [ 258 ] ] . last = save . box [ 1 ] ;
2017-07-16 21:00:55 +00:00
}
2016-08-26 09:58:03 +00:00
loadBoxHigh ( ) ;
break ;
}
}
}
if ( buf [ 0 ] = = ' A ' & & buf [ 1 ] = = ' C ' & & buf [ 2 ] = = ' H ' ) {
char buf1 [ 80 ] , buf2 [ 80 ] ;
sscanf ( buf , " %70s%70s " , buf1 , buf2 ) ;
if ( buf2 = = string ( " PRINCESS1 " ) ) princess : : everSaved = true ;
if ( buf2 = = string ( " YENDOR2 " ) ) yendor : : everwon = true ;
if ( buf2 = = string ( " CR4 " ) ) chaosUnlocked = true ;
}
if ( buf [ 0 ] = = ' T ' & & buf [ 1 ] = = ' A ' & & buf [ 2 ] = = ' C ' ) {
ok = false ;
char buf1 [ 80 ] , ver [ 10 ] ;
int tid , land , score , tc , t , ts , cert ;
2018-01-06 21:05:22 +00:00
int xc = - 1 ;
2020-05-15 09:52:51 +00:00
sscanf ( buf , " %70s%9s%d%d%d%d%d%d%d%d " ,
2018-01-06 21:05:22 +00:00
buf1 , ver , & tid , & land , & score , & tc , & t , & ts , & cert , & xc ) ;
2017-07-22 23:33:27 +00:00
eLand l2 = eLand ( land ) ;
if ( land = = laMirror & & verless ( ver , " 10.0 " ) ) l2 = laMirrorOld ;
2018-01-06 21:05:22 +00:00
if ( xc = = - 1 )
for ( xc = 0 ; xc < 32768 ; xc + + )
if ( anticheat : : check ( cert , ver , dnameof ( l2 ) , tc , t , ts , xc * 999 + unsigned ( tid ) + 256 * score ) )
break ;
2016-08-26 09:58:03 +00:00
2018-01-06 21:05:22 +00:00
if ( tid = = tactic : : id & & ( anticheat : : check ( cert , ver , dnameof ( l2 ) , tc , t , ts , xc * unsigned ( 999 ) + unsigned ( tid ) + 256 * score ) ) ) {
if ( score ! = 0
& & ! ( land = = laOcean & & verless ( ver , " 8.0f " ) )
2018-01-25 22:25:35 +00:00
& & ! ( land = = laTerracotta & & verless ( ver , " 10.3e " ) )
2020-03-11 09:27:33 +00:00
& & ! ( land = = laWildWest & & verless ( ver , " 11.3b " ) & & ! verless ( ver , " 11.3 " ) ) )
tactic : : record ( l2 , score , xc ) ;
2018-01-06 21:05:22 +00:00
anticheat : : nextid ( tactic : : id , ver , cert ) ;
}
2016-08-26 09:58:03 +00:00
}
if ( buf [ 0 ] = = ' Y ' & & buf [ 1 ] = = ' E ' & & buf [ 2 ] = = ' N ' ) {
char buf1 [ 80 ] , ver [ 10 ] ;
2018-01-06 21:05:22 +00:00
int cid , oy , won , tc , t , ts , cert = 0 , xc = - 1 ;
2020-05-15 09:52:51 +00:00
sscanf ( buf , " %70s%9s%d%d%d%d%d%d%d%d " ,
2018-01-06 21:05:22 +00:00
buf1 , ver , & cid , & oy , & won , & tc , & t , & ts , & cert , & xc ) ;
if ( xc = = - 1 )
for ( xc = 0 ; xc < 32768 ; xc + + )
if ( anticheat : : check ( cert , ver , won ? " WON " : " LOST " , tc , t , ts , xc * 999 + cid + 256 * oy ) )
break ;
if ( won ) if ( anticheat : : check ( cert , ver , won ? " WON " : " LOST " , tc , t , ts , xc * 999 + cid + 256 * oy ) ) {
2017-03-23 10:53:57 +00:00
if ( xc = = 19 & & cid = = 25 ) xc = 0 ;
2016-08-26 09:58:03 +00:00
if ( cid > 0 & & cid < YENDORLEVELS )
2017-07-16 21:00:55 +00:00
if ( ! ( verless ( ver , " 8.0f " ) & & oy > 1 & & cid = = 15 ) )
if ( ! ( verless ( ver , " 9.3b " ) & & oy > 1 & & ( cid = = 27 | | cid = = 28 ) ) )
2017-03-23 10:53:57 +00:00
{
2016-08-26 09:58:03 +00:00
yendor : : bestscore [ xc ] [ cid ] = max ( yendor : : bestscore [ xc ] [ cid ] , oy ) ;
}
}
}
}
fclose ( f ) ;
if ( ok & & sc . box [ 65 + 4 + itOrbSafety - itOrbLightning ] ) {
anticheat : : tampered = tamper ;
// printf("box = %d (%d)\n", sc.box[65 + 4 + itOrbSafety - itOrbLightning], boxid);
// printf("boxid = %d\n", boxid);
2020-03-27 19:05:58 +00:00
using namespace scores ;
2020-03-27 19:21:08 +00:00
for ( int i = 0 ; i < boxid ; i + + ) save . box [ i ] = sc . box [ i ] ;
for ( int i = boxid ; i < MAXBOX ; i + + ) save . box [ i ] = 0 ;
// for(int i=160; i<200; i++) printf("%d: %d ", i, save.box[i]);
2016-08-26 09:58:03 +00:00
loadBox ( ) ;
// printf("boxid = %d\n", boxid);
if ( items [ itHolyGrail ] ) {
items [ itHolyGrail ] - - ;
2019-12-08 18:17:28 +00:00
camelot : : knighted = newRoundTableRadius ( ) ;
2016-08-26 09:58:03 +00:00
items [ itHolyGrail ] + + ;
}
2019-12-08 18:17:28 +00:00
else camelot : : knighted = 0 ;
2016-08-26 09:58:03 +00:00
safety = true ;
if ( items [ itSavedPrincess ] < 0 ) items [ itSavedPrincess ] = 0 ;
addMessage ( XLAT ( " Game loaded. " ) ) ;
2017-08-06 12:50:16 +00:00
showstartmenu = false ;
// reset unsavable special modes just in case
peace : : on = false ;
randomPatternsMode = false ;
yendor : : on = false ;
tour : : on = false ;
2016-08-26 09:58:03 +00:00
}
}
# endif
2019-08-09 19:18:13 +00:00
EX void stop_game ( ) {
2018-06-10 22:58:38 +00:00
if ( ! game_active ) return ;
2019-05-28 23:09:38 +00:00
if ( dual : : split ( stop_game ) ) return ;
2019-05-12 23:57:40 +00:00
DEBBI ( DF_INIT , ( " stop_game " ) ) ;
2018-06-10 22:58:38 +00:00
achievement_final ( true ) ;
# if CAP_SAVE
saveStats ( ) ;
# endif
for ( int i = 0 ; i < ittypes ; i + + ) items [ i ] = 0 ;
lastkills = 0 ; for ( int i = 0 ; i < motypes ; i + + ) kills [ i ] = 0 ;
for ( int i = 0 ; i < 10 ; i + + ) explore [ i ] = 0 ;
for ( int i = 0 ; i < 10 ; i + + ) for ( int l = 0 ; l < landtypes ; l + + )
exploreland [ i ] [ l ] = 0 ;
for ( int i = 0 ; i < numplayers ( ) ; i + + )
if ( multi : : playerActive ( i ) )
multi : : deaths [ i ] + + ;
# if CAP_SAVE
anticheat : : tampered = false ;
# endif
achievementsReceived . clear ( ) ;
princess : : saved = false ;
princess : : nodungeon = false ;
princess : : reviveAt = 0 ;
princess : : forceVizier = false ;
princess : : forceMouse = false ;
2019-12-08 18:17:28 +00:00
camelot : : knighted = 0 ;
2018-06-10 22:58:38 +00:00
// items[itGreenStone] = 100;
clearMemory ( ) ;
game_active = false ;
2018-05-26 00:14:52 +00:00
# if CAP_DAILY
2018-06-12 22:41:02 +00:00
if ( daily : : on )
2018-05-25 23:29:25 +00:00
daily : : turnoff ( ) ;
2018-05-26 00:14:52 +00:00
# endif
2018-06-10 22:58:38 +00:00
}
2020-05-25 00:32:29 +00:00
EX eModel default_model ( ) {
2019-10-06 09:52:06 +00:00
if ( nonisotropic ) return mdGeodesic ;
if ( GDIM = = 3 ) return mdPerspective ;
return mdDisk ;
}
2020-01-16 15:39:18 +00:00
2020-04-11 18:40:12 +00:00
EX purehookset hooks_on_geometry_change ;
2020-01-16 15:39:18 +00:00
2019-08-09 19:18:13 +00:00
EX void set_geometry ( eGeometry target ) {
2019-10-06 09:52:06 +00:00
bool was_default = pmodel = = default_model ( ) ;
2020-04-11 18:40:12 +00:00
callhooks ( hooks_on_geometry_change ) ;
2018-08-28 15:17:34 +00:00
if ( geometry ! = target ) {
2019-08-15 13:05:43 +00:00
int old_DIM = GDIM ;
2018-08-28 15:17:34 +00:00
stop_game ( ) ;
ors : : reset ( ) ;
2019-09-12 11:52:16 +00:00
if ( among ( target , gProduct , gRotSpace ) ) {
if ( vid . always3 ) { vid . always3 = false ; geom3 : : apply_always3 ( ) ; }
2020-07-24 00:30:50 +00:00
if ( target = = gRotSpace ) hybrid : : csteps = 0 ;
2019-09-12 11:52:16 +00:00
hybrid : : configure ( target ) ;
}
2018-08-28 15:17:34 +00:00
geometry = target ;
2019-10-12 11:47:41 +00:00
if ( chaosmode & & bounded ) chaosmode = 0 ;
if ( chaosmode = = 1 & & walls_not_implemented ( ) ) chaosmode = 0 ;
2019-02-17 17:28:20 +00:00
# if CAP_IRR
2018-08-28 15:17:34 +00:00
if ( IRREGULAR ) variation = eVariation : : bitruncated ;
2019-02-17 17:28:20 +00:00
# endif
# if CAP_GP
2018-08-28 15:17:34 +00:00
if ( GOLDBERG & & gp : : param = = gp : : loc ( 1 , 1 ) & & S3 = = 3 ) {
variation = eVariation : : bitruncated ;
}
if ( GOLDBERG & & nonorientable ) {
if ( gp : : param . second & & gp : : param . second ! = gp : : param . first )
gp : : param . second = 0 ;
}
2019-02-17 17:28:20 +00:00
# endif
2019-12-27 00:52:14 +00:00
if ( DUAL & & geometry ! = gArchimedean & & ! hybri )
2018-08-30 00:11:43 +00:00
variation = ginf [ geometry ] . default_variation ;
2019-02-17 17:28:20 +00:00
# if CAP_BT
2019-12-27 00:52:28 +00:00
if ( bt : : in ( ) | | WDIM = = 3 | | kite : : in ( ) | | arb : : in ( ) ) if ( ! hybri ) variation = eVariation : : pure ;
2019-02-17 17:28:20 +00:00
# endif
2019-12-27 22:00:38 +00:00
if ( S3 > = OINF ) variation = eVariation : : pure ;
2020-07-12 22:56:00 +00:00
if ( INVERSE ) variation = gp : : variation_for ( gp : : param ) ;
2020-04-10 22:42:41 +00:00
if ( ginf [ target ] . default_variation = = eVariation : : pure & & geometry ! = gArchimedean )
variation = eVariation : : pure ;
2019-10-06 09:52:06 +00:00
if ( was_default ) pmodel = default_model ( ) ;
2019-08-25 17:16:29 +00:00
if ( nonisotropic & & old_DIM = = 2 & & vid . texture_step < 4 ) vid . texture_step = 4 ;
2020-05-29 08:47:18 +00:00
if ( WDIM = = 2 & & ( cgflags & qIDEAL ) & & vid . always3 & & vid . texture_step < 32 ) vid . texture_step = 32 ;
2019-08-18 21:12:36 +00:00
if ( prod ) { pmodel = mdPerspective ; if ( vid . texture_step < 4 ) vid . texture_step = 4 ; }
2019-10-12 11:13:09 +00:00
if ( WDIM = = 3 & & ( cgflags & qIDEAL ) & & vid . texture_step < 4 ) vid . texture_step = 4 ;
2019-08-27 19:42:59 +00:00
if ( sl2 ) nisot : : geodesic_movement = true ;
2020-07-24 00:30:50 +00:00
if ( rotspace ) {
check_cgi ( ) ; cgi . require_basics ( ) ;
hybrid : : csteps = cgi . psl_steps ;
}
2018-08-28 15:17:34 +00:00
}
}
2019-08-09 19:18:13 +00:00
EX void set_variation ( eVariation target ) {
2018-08-28 15:17:34 +00:00
if ( variation ! = target ) {
stop_game ( ) ;
2019-12-14 11:12:24 +00:00
if ( bt : : in ( ) | | sol | | kite : : in ( ) | | WDIM = = 3 ) if ( ! prod ) geometry = gNormal ;
2018-12-02 10:31:13 +00:00
auto & cd = ginf [ gCrystal ] ;
2019-08-22 10:14:39 +00:00
if ( target = = eVariation : : bitruncated & & cryst & & cd . sides = = 8 & & cd . vertex = = 4 ) {
2018-12-02 10:31:13 +00:00
cd . vertex = 3 ;
2019-02-06 15:34:22 +00:00
cd . tiling_name = " {8,3} " ;
2018-11-30 15:59:42 +00:00
target = eVariation : : pure ;
}
2018-08-28 15:17:34 +00:00
variation = target ;
}
}
2019-05-29 00:44:30 +00:00
void stop_tour ( ) {
while ( gamestack : : pushed ( ) ) {
gamestack : : pop ( ) ;
stop_game ( ) ;
}
}
2018-08-28 15:17:34 +00:00
2019-08-09 19:18:13 +00:00
EX void switch_game_mode ( char switchWhat ) {
2019-05-12 23:57:40 +00:00
DEBBI ( DF_INIT , ( " switch_game_mode " , switchWhat ) ) ;
2018-06-10 22:58:38 +00:00
switch ( switchWhat ) {
case rg : : peace :
peace : : on = ! peace : : on ;
tactic : : on = yendor : : on = princess : : challenge =
randomPatternsMode = inv : : on = false ;
2018-11-24 22:52:13 +00:00
racing : : on = false ;
2018-06-10 22:58:38 +00:00
break ;
2019-05-29 00:44:30 +00:00
case rg : : dualmode :
stop_tour ( ) ; tour : : on = false ;
racing : : on = false ;
yendor : : on = tactic : : on = princess : : challenge = false ;
if ( ! dual : : state ) dual : : enable ( ) ;
else dual : : disable ( ) ;
break ;
2018-06-10 22:58:38 +00:00
case rg : : inv :
inv : : on = ! inv : : on ;
2019-11-29 15:10:34 +00:00
if ( tactic : : on ) firstland = specialland = laIce ;
2018-06-10 22:58:38 +00:00
tactic : : on = yendor : : on = princess : : challenge =
2019-11-29 15:10:34 +00:00
peace : : on = false ;
2018-11-24 22:52:13 +00:00
racing : : on = false ;
2018-06-10 22:58:38 +00:00
break ;
case rg : : chaos :
if ( tactic : : on ) firstland = laIce ;
yendor : : on = tactic : : on = princess : : challenge = false ;
chaosmode = ! chaosmode ;
2018-12-04 22:08:56 +00:00
if ( bounded ) set_geometry ( gNormal ) ;
2018-11-24 22:52:13 +00:00
racing : : on = false ;
2018-06-10 22:58:38 +00:00
break ;
2017-07-22 23:33:27 +00:00
# if CAP_TOUR
2018-06-10 22:58:38 +00:00
case rg : : tour :
2019-05-29 00:44:30 +00:00
if ( tour : : on ) stop_tour ( ) ;
2018-06-10 22:58:38 +00:00
geometry = gNormal ;
yendor : : on = tactic : : on = princess : : challenge = peace : : on = inv : : on = false ;
2019-05-29 00:44:30 +00:00
dual : : disable ( ) ;
2018-08-28 15:17:34 +00:00
chaosmode = randomPatternsMode = false ;
variation = eVariation : : bitruncated ;
2019-05-06 23:08:49 +00:00
# if CAP_GP
2018-07-17 00:29:18 +00:00
gp : : param = gp : : loc ( 1 , 1 ) ;
2019-05-06 23:08:49 +00:00
# endif
2018-06-10 22:58:38 +00:00
shmup : : on = false ;
tour : : on = ! tour : : on ;
2018-11-24 22:52:13 +00:00
racing : : on = false ;
2018-06-10 22:58:38 +00:00
break ;
2017-04-08 15:18:29 +00:00
# endif
2018-06-10 22:58:38 +00:00
case rg : : yendor :
yendor : : on = ! yendor : : on ;
tactic : : on = false ;
peace : : on = false ;
inv : : on = false ;
princess : : challenge = false ;
randomPatternsMode = false ;
chaosmode = false ;
2018-11-24 22:52:13 +00:00
racing : : on = false ;
2018-06-10 22:58:38 +00:00
if ( ! yendor : : on ) firstland = laIce ;
2019-05-29 00:44:30 +00:00
dual : : disable ( ) ;
2018-06-10 22:58:38 +00:00
break ;
2018-11-30 13:47:13 +00:00
# if CAP_RACING
2018-11-24 22:52:13 +00:00
case rg : : racing :
racing : : on = ! racing : : on ;
shmup : : on = racing : : on ;
peace : : on = false ;
tour : : on = false ;
inv : : on = false ;
chaosmode = false ;
princess : : challenge = false ;
2019-05-29 00:44:30 +00:00
dual : : disable ( ) ;
2018-11-24 22:52:13 +00:00
break ;
2018-11-30 13:47:13 +00:00
# endif
2018-06-10 22:58:38 +00:00
case rg : : tactic :
tactic : : on = ! tactic : : on ;
yendor : : on = false ;
peace : : on = false ;
inv : : on = false ;
randomPatternsMode = false ;
princess : : challenge = false ;
2018-11-24 22:52:13 +00:00
racing : : on = false ;
2018-06-10 22:58:38 +00:00
chaosmode = false ;
if ( ! tactic : : on ) firstland = laIce ;
2019-05-29 00:44:30 +00:00
dual : : disable ( ) ;
2018-06-10 22:58:38 +00:00
break ;
case rg : : shmup :
shmup : : on = ! shmup : : on ;
princess : : challenge = false ;
2018-11-24 22:52:13 +00:00
if ( ! shmup : : on ) racing : : on = false ;
2018-06-10 22:58:38 +00:00
break ;
case rg : : randpattern :
randomPatternsMode = ! randomPatternsMode ;
tactic : : on = false ;
yendor : : on = false ;
peace : : on = false ;
princess : : challenge = false ;
break ;
case rg : : princess :
princess : : challenge = ! princess : : challenge ;
2019-11-29 15:10:34 +00:00
firstland = specialland = princess : : challenge ? laPalace : laIce ;
2018-06-10 22:58:38 +00:00
shmup : : on = false ;
tactic : : on = false ;
yendor : : on = false ;
chaosmode = false ;
inv : : on = false ;
2018-11-24 22:52:13 +00:00
racing : : on = false ;
2019-05-29 00:44:30 +00:00
dual : : disable ( ) ;
2018-06-10 22:58:38 +00:00
break ;
2018-05-01 17:38:42 +00:00
# if CAP_DAILY
2018-06-10 22:58:38 +00:00
case rg : : daily :
daily : : setup ( ) ;
break ;
case rg : : daily_off :
daily : : turnoff ( ) ;
break ;
2018-05-01 17:38:42 +00:00
# endif
2016-08-26 09:58:03 +00:00
}
2018-06-10 22:58:38 +00:00
}
2019-08-09 19:18:13 +00:00
EX void start_game ( ) {
2018-06-10 22:58:38 +00:00
if ( game_active ) return ;
2019-05-12 23:57:40 +00:00
DEBBI ( DF_INIT , ( " start_game " ) ) ;
2019-05-28 23:09:38 +00:00
if ( dual : : state = = 1 ) dual : : assign_landsides ( ) ;
if ( dual : : split ( start_game ) ) return ;
2018-12-04 18:20:44 +00:00
restart :
2018-06-10 22:58:38 +00:00
game_active = true ;
2018-12-04 18:20:44 +00:00
gamegen_failure = false ;
2019-06-06 17:37:17 +00:00
ignored_memory_warning = false ;
2019-05-26 16:04:02 +00:00
check_cgi ( ) ;
cgi . require_basics ( ) ;
2019-08-27 19:43:32 +00:00
arcm : : current . compute_geometry ( ) ;
2016-08-26 09:58:03 +00:00
initcells ( ) ;
2018-09-12 02:23:15 +00:00
expansion . reset ( ) ;
2016-08-26 09:58:03 +00:00
if ( randomPatternsMode ) {
for ( int i = 0 ; i < landtypes ; i + + ) {
randompattern [ i ] = hrandpos ( ) ;
// change probability 1/5 to 2/6
if ( hrand ( 5 ) = = 0 ) {
randompattern [ i ] - = ( randompattern [ i ] % 5 ) ;
}
}
2018-06-29 11:16:31 +00:00
if ( randomPatternsMode ) specialland = pickLandRPM ( laNone ) ;
2016-08-26 09:58:03 +00:00
clearMemoRPM ( ) ;
}
initgame ( ) ;
2018-12-04 18:20:44 +00:00
if ( gamegen_failure ) {
stop_game ( ) ;
goto restart ;
}
2016-08-26 09:58:03 +00:00
canmove = true ;
restartGraph ( ) ;
resetmusic ( ) ;
resetmusic ( ) ;
2018-08-20 00:04:49 +00:00
# if CAP_TEXTURE
texture : : config . remap ( ) ;
2018-11-17 18:30:50 +00:00
# endif
2019-03-09 15:20:06 +00:00
subscreens : : prepare ( ) ;
2016-08-26 09:58:03 +00:00
}
2018-06-10 22:58:38 +00:00
2019-08-09 19:18:13 +00:00
// popAllScreens + popAllGames + stop_game + switch_game_mode + start_game
EX void restart_game ( char switchWhat IS ( rg : : nothing ) ) {
2018-06-10 22:58:38 +00:00
popScreenAll ( ) ;
stop_game ( ) ;
switch_game_mode ( switchWhat ) ;
start_game ( ) ;
}
2019-08-09 19:18:13 +00:00
// stop_game + switch_game_mode
EX void stop_game_and_switch_mode ( char switchWhat IS ( rg : : nothing ) ) {
2018-06-10 22:58:38 +00:00
stop_game ( ) ;
switch_game_mode ( switchWhat ) ;
}
2020-04-11 18:40:12 +00:00
EX purehookset hooks_clearmemory ;
2017-07-10 18:47:38 +00:00
2019-08-09 19:18:13 +00:00
EX void clearMemory ( ) {
2020-04-11 18:40:12 +00:00
callhooks ( hooks_clearmemory ) ;
2017-07-10 18:47:38 +00:00
}
2019-09-05 07:01:47 +00:00
EX bool fixseed = false ;
EX int startseed = 0 ;
EX eLand firstland0 ;
2020-04-10 22:42:53 +00:00
EX purehookset hooks_initialize ;
2019-09-05 07:01:47 +00:00
EX void initAll ( ) {
2020-04-10 22:42:53 +00:00
callhooks ( hooks_initialize ) ;
2019-09-05 07:01:47 +00:00
init_floorcolors ( ) ;
showstartmenu = true ;
ca : : init ( ) ;
# if CAP_COMMANDLINE
arg : : read ( 1 ) ;
# endif
srand ( time ( NULL ) ) ;
shrand ( fixseed ? startseed : time ( NULL ) ) ;
achievement_init ( ) ; // not in ANDROID
firstland0 = firstland ;
// initlanguage();
initgraph ( ) ;
# if CAP_SAVE
loadsave ( ) ;
if ( IRREGULAR ) irr : : auto_creator ( ) ;
# endif
start_game ( ) ;
if ( ! shmup : : on ) {
restoreGolems ( items [ itOrbLife ] , moGolem ) ; items [ itOrbLife ] = 0 ;
restoreGolems ( items [ itOrbFriend ] , moTameBomberbird ) ; items [ itOrbFriend ] = 0 ;
restoreGolems ( kills [ moPrincessMoved ] , moPrincess , princess : : saveHP ) ; kills [ moPrincessMoved ] = 0 ;
restoreGolems ( kills [ moPrincessArmedMoved ] , moPrincessArmed , princess : : saveArmedHP ) ; kills [ moPrincessArmedMoved ] = 0 ;
}
firstland = firstland0 ;
polygonal : : solve ( ) ;
}
2020-04-11 18:40:12 +00:00
EX purehookset hooks_final_cleanup ;
2020-01-16 15:39:18 +00:00
2019-09-05 07:01:47 +00:00
EX void finishAll ( ) {
achievement_final ( ! items [ itOrbSafety ] ) ;
# if CAP_SAVE
saveStats ( ) ;
# endif
clearMemory ( ) ;
2020-05-03 18:56:41 +00:00
# if !ISMOBILE
2019-09-05 07:01:47 +00:00
cleargraph ( ) ;
# endif
achievement_close ( ) ;
2020-04-11 18:40:12 +00:00
callhooks ( hooks_final_cleanup ) ;
2019-09-05 07:01:47 +00:00
}
2020-04-11 18:40:12 +00:00
auto cgm = addHook ( hooks_clearmemory , 40 , [ ] ( ) {
2017-07-10 18:47:38 +00:00
pathq . clear ( ) ;
dcal . clear ( ) ;
clearshadow ( ) ;
2018-02-27 22:43:21 +00:00
for ( int i = 0 ; i < MAXPLAYER ; i + + ) lastmountpos [ i ] = NULL ;
2017-07-10 18:47:38 +00:00
seenSevenMines = false ;
recallCell = NULL ;
butterflies . clear ( ) ;
buggycells . clear ( ) ;
2018-01-02 10:15:42 +00:00
crush_next . clear ( ) ;
crush_now . clear ( ) ;
2019-01-28 20:47:47 +00:00
rosemap . clear ( ) ;
2019-04-08 14:16:16 +00:00
adj_memo . clear ( ) ;
2018-01-26 00:45:49 +00:00
} ) +
2019-05-28 23:09:38 +00:00
addHook ( hooks_gamedata , 0 , [ ] ( gamedata * gd ) {
gd - > store ( pathq ) ;
gd - > store ( dcal ) ;
gd - > store ( recallCell ) ;
gd - > store ( butterflies ) ;
gd - > store ( buggycells ) ;
gd - > store ( crush_now ) ;
gd - > store ( crush_next ) ;
gd - > store ( rosemap ) ;
2019-05-30 14:12:38 +00:00
gd - > store ( airmap ) ;
2019-05-28 23:09:38 +00:00
gd - > store ( adj_memo ) ;
gd - > store ( pd_from ) ;
gd - > store ( pd_range ) ;
gd - > store ( pathqm ) ;
gd - > store ( reachedfrom ) ;
2019-05-30 14:12:38 +00:00
gd - > store ( gravity_state ) ;
gd - > store ( last_gravity_state ) ;
gd - > store ( shpos ) ;
2019-05-30 14:17:17 +00:00
gd - > store ( cshpos ) ;
2019-11-26 23:43:30 +00:00
gd - > store ( gp : : do_adjm ) ;
2019-05-28 23:09:38 +00:00
} ) +
2018-01-26 00:45:49 +00:00
addHook ( hooks_removecells , 0 , [ ] ( ) {
eliminate_if ( crush_next , is_cell_removed ) ;
eliminate_if ( crush_now , is_cell_removed ) ;
eliminate_if ( buggycells , is_cell_removed ) ;
eliminate_if ( butterflies , [ ] ( pair < cell * , int > & p ) { return is_cell_removed ( p . first ) ; } ) ;
for ( int i = 0 ; i < SHSIZE ; i + + ) for ( int p = 0 ; p < MAXPLAYER ; p + + )
set_if_removed ( shpos [ p ] [ i ] , NULL ) ;
2020-02-23 01:51:27 +00:00
} ) ;
2018-06-10 23:58:31 +00:00
}