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 {
2023-08-23 17:44:37 +00:00
static constexpr char nothing = 0 ;
static constexpr char peace = ' P ' ;
static constexpr char inv = ' i ' ;
static constexpr char chaos = ' C ' ;
static constexpr char tactic = ' t ' ;
static constexpr char tour = ' T ' ;
static constexpr char yendor = ' y ' ;
static constexpr char shmup = ' s ' ;
static constexpr char randpattern = ' r ' ;
static constexpr char princess = ' p ' ;
static constexpr char daily = ' d ' ;
static constexpr char daily_off = ' D ' ;
static constexpr char racing = ' R ' ;
static constexpr char dualmode = ' U ' ;
static constexpr char heptagons = ' 7 ' ;
2024-05-16 16:13:48 +00:00
2020-03-27 20:49:57 +00:00
/** \brief wrongmode only -- marks 'global' achievements not related to the current mode */
2024-05-16 16:13:48 +00:00
static constexpr char global = ' x ' ;
2020-03-27 20:49:57 +00:00
/** \brief wrongmode only -- change vid.scfg.players then restart_game(rg::nothing) instead */
2023-08-23 17:44:37 +00:00
static constexpr char multi = ' m ' ;
2022-02-02 02:01:31 +00:00
/** \brief wrongmode only -- mark achievements for special geometries/variations, this automatically marks the expected land_structure as lsSingle */
2023-08-23 17:44:37 +00:00
static constexpr char special_geometry = ' g ' ;
2022-02-02 02:01:31 +00:00
/** \brief wrongmode only -- mark achievements for special geometries/variations */
2023-08-23 17:44:37 +00:00
static constexpr char special_geometry_nicewalls = ' G ' ;
2023-12-15 09:59:17 +00:00
/** \brief wrongmode only -- specgeom returns 'fail' if the special geometry is not correct */
static constexpr char fail = ' X ' ;
2019-08-10 00:16:48 +00:00
}
# 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
2024-05-28 13:09:18 +00:00
EX time_t timerstart , savetime , tickstart ;
2019-08-09 20:07:03 +00:00
EX bool timerstopped ;
2019-09-05 10:00:55 +00:00
EX int savecount ;
2021-05-27 10:57:12 +00:00
EX int save_turns ;
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 ) {
2024-05-16 16:13:48 +00:00
if ( isdigit ( v [ 0 ] ) & & isdigit ( v [ 1 ] ) )
2017-07-22 23:33:27 +00:00
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 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 ;
2023-03-25 08:24:47 +00:00
if ( nohelp = = 1 ) return ;
2024-03-22 00:22:34 +00:00
if ( custom_welcome ! = " " ) addMessage ( custom_welcome ) ;
else if ( embedded_plane ) return IPF ( welcomeMessage ( ) ) ;
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 ) ) ;
2022-10-26 22:03:21 +00:00
}
else if ( intra : : in )
addMessage ( XLAT ( " Welcome to a portal map! Press 'o' to configure. " ) ) ;
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-28 11:16:56 +00:00
if ( hybrid : : csteps = = 0 )
addMessage ( XLAT ( " Welcome to CoverRogue! " ) ) ;
else if ( cgi . psl_steps % hybrid : : csteps = = 0 )
2020-07-24 00:30:50 +00:00
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) " ) ) ;
2023-03-28 21:09:23 +00:00
else if ( hat : : in ( ) )
addMessage ( XLAT ( " Welcome to HatRogue! " ) ) ;
2023-10-29 07:00:27 +00:00
else if ( bow : : crossbow_mode ( ) )
addMessage ( XLAT ( " Welcome to HyperRanger! " ) ) ;
2018-12-25 22:59:47 +00:00
else
addMessage ( XLAT ( " Welcome to HyperRogue! " ) ) ;
2017-08-06 12:50:16 +00:00
2021-04-11 20:15:40 +00:00
if ( ! safety & & ! 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
2023-03-25 08:24:47 +00:00
if ( nohelp = = 2 ) return ;
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
2021-04-07 16:32:54 +00:00
/** \brief These hooks are called at the end of initgame. */
EX hookset < void ( ) > hooks_post_initgame ;
2021-04-11 20:15:40 +00:00
EX bool ineligible_starting_land ;
2021-12-13 21:30:26 +00:00
EX int easy_specialland ;
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 " ) ) ;
2024-05-16 16:13:48 +00:00
callhooks ( hooks_initgame ) ;
2021-04-11 20:15:40 +00:00
if ( ! safety ) fix_land_structure_choice ( ) ;
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 ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
yendor : : init ( 1 ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
if ( safety & & safetyseed ) {
shrand ( safetyseed ) ;
firstland = safetyland ;
}
2018-11-30 13:47:13 +00:00
2021-04-11 20:15:40 +00:00
if ( ! safety ) {
firstland = specialland ;
2024-03-27 16:22:29 +00:00
ineligible_starting_land = ! landUnlockedIngame ( specialland ) ;
2021-04-11 20:15:40 +00:00
}
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
if ( firstland = = laNone | | firstland = = laBarrier )
firstland = laCrossroads ;
2021-12-13 21:30:26 +00:00
easy_specialland = 0 ;
2024-05-16 16:13:48 +00:00
if ( firstland = = laOceanWall ) firstland = laOcean ;
if ( firstland = = laHauntedWall ) firstland = laGraveyard ;
2024-02-22 19:21:08 +00:00
if ( firstland = = laHauntedBorder ) firstland = laGraveyard ;
2021-06-27 04:39:41 +00:00
if ( firstland = = laHaunted & & ! tactic : : on ) firstland = laGraveyard ;
2017-10-13 19:28:14 +00:00
if ( firstland = = laMercuryRiver ) firstland = laTerracotta ;
2023-09-18 12:34:46 +00:00
if ( firstland = = laMountain & & ! tactic : : on & & ! ls : : hv_structure ( ) ) 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 ;
2023-09-18 12:34:46 +00:00
if ( ! ls : : hv_structure ( ) )
2021-12-13 21:30:26 +00:00
if ( ( isGravityLand ( firstland ) & & ! isCyclic ( firstland ) ) | | ( firstland = = laOcean & & ! safety & & ! yendor : : on ) ) {
2018-04-11 15:44:46 +00:00
firstland = weirdhyperbolic ? laCrossroads4 : laCrossroads ;
2021-12-13 21:30:26 +00:00
easy_specialland = 3 ;
}
2024-05-16 16:13:48 +00:00
2021-07-26 01:01:07 +00:00
clear_euland ( firstland ) ;
2019-07-03 03:02:26 +00:00
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
2020-10-15 14:33:52 +00:00
# if CAP_COMPLEX2
2018-10-25 00:43:14 +00:00
if ( firstland = = laBrownian ) brownian : : init ( cwt . at ) ;
2020-10-15 14:33:52 +00:00
# endif
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
chaosAchieved = false ;
2019-12-27 13:02:21 +00:00
clearing : : direct = 0 ;
clearing : : imputed = 0 ;
2021-05-30 08:17:15 +00:00
rosephase = 0 ;
2022-11-03 18:36:54 +00:00
shmup : : count_pauses = 0 ;
2024-05-27 09:14:25 +00:00
illegal_moves = 0 ;
2019-12-27 13:02:21 +00:00
2024-03-14 18:52:22 +00:00
splitrocks = 0 ;
2018-08-17 22:46:45 +00:00
if ( firstland = = laElementalWall ) cwt . at - > land = randomElementalLand ( ) ;
2024-05-16 16:13:48 +00:00
resetview ( ) ;
2018-08-17 22:46:45 +00:00
createMov ( cwt . at , 0 ) ;
2024-05-16 16:13:48 +00:00
pregen ( ) ;
2018-08-17 22:46:45 +00:00
setdist ( cwt . at , BARLEV , NULL ) ;
2016-08-26 09:58:03 +00:00
2021-09-30 11:22:12 +00:00
if ( ! disable_bigstuff )
2021-04-23 18:23:49 +00:00
if ( isCyclic ( specialland ) | | specialland = = laCanvas ) {
2020-10-15 14:33:52 +00:00
# if CAP_COMPLEX2
2019-12-08 18:17:28 +00:00
camelot : : anthraxBonus = items [ itHolyGrail ] ;
2020-10-15 14:33:52 +00:00
# endif
2018-08-17 22:46:45 +00:00
cwt . at - > move ( 0 ) - > land = firstland ;
if ( firstland = = laWhirlpool ) cwt . at - > move ( 0 ) - > wall = waSea ;
2024-05-16 16:13:48 +00:00
2018-08-17 22:46:45 +00:00
setdist ( cwt . at - > move ( 0 ) , BARLEV - 1 , cwt . at ) ;
2021-04-25 09:30:10 +00:00
if ( horo_ok ( ) ) {
2024-05-16 16:13:48 +00:00
if ( specialland = = laCamelot )
2021-04-15 17:07:41 +00:00
start_camelot ( cwt . at ) ;
else {
2021-07-29 12:51:49 +00:00
heptagon * h = create_altmap ( cwt . at , 2 , hsA ) ;
2021-04-15 17:07:41 +00:00
if ( ! h ) printf ( " FAIL \n " ) ;
}
2017-10-30 21:47:18 +00:00
}
2016-08-26 09:58:03 +00:00
}
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
if ( tactic : : on & & firstland = = laPower ) {
items [ itOrbSpeed ] = 30 ;
items [ itOrbWinter ] = 30 ;
items [ itOrbFlash ] = 30 ;
}
2024-05-16 16:13:48 +00:00
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
}
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
yendor : : lastchallenge = yendor : : challenge ;
2024-05-16 16:13:48 +00:00
2018-11-17 18:26:35 +00:00
if ( shmup : : on ) shmup : : init ( ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
yendor : : init ( 2 ) ;
2024-05-16 16:13:48 +00:00
2018-11-17 18:30:50 +00:00
# if CAP_RACING
if ( racing : : on ) racing : : generate_track ( ) ;
# endif
2024-05-16 16:13:48 +00:00
2018-12-04 18:20:44 +00:00
if ( gamegen_failure ) return ;
2024-05-16 16:13:48 +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 ;
}
2023-12-15 08:54:27 +00:00
if ( cwt . at - > land = = laCrossroads2 & & ! ls : : hv_structure ( ) ) {
2019-08-26 13:55:30 +00:00
cell * c = cwt . at ;
2022-12-08 18:38:06 +00:00
if ( mhybrid ) { 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
}
2024-05-16 16:13:48 +00:00
2019-05-28 23:01:13 +00:00
sword : : determine_sword_angles ( ) ;
2024-05-16 16:13:48 +00:00
for ( int i = 0 ; i < numplayers ( ) ; i + + )
2019-06-25 08:05:39 +00:00
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
2024-05-16 16:13:48 +00:00
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
}
2024-05-16 16:13:48 +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 ;
}
2024-05-16 16:13:48 +00:00
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
}
2024-05-16 16:13:48 +00:00
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
}
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
yendor : : init ( 3 ) ;
2017-07-04 13:38:33 +00:00
peace : : simon : : init ( ) ;
2024-05-16 16:13:48 +00:00
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
2024-05-16 16:13:48 +00:00
2017-03-23 10:53:57 +00:00
if ( multi : : players > 1 & & ! shmup : : on ) {
2021-03-06 10:46:13 +00:00
for ( cell * pc : player_positions ( ) )
makeEmpty ( pc ) ;
2017-03-23 10:53:57 +00:00
}
else {
2021-03-06 10:46:13 +00:00
makeEmpty ( cwt . at ) ;
2017-03-23 10:53:57 +00:00
}
2024-05-16 16:13:48 +00:00
2022-05-21 11:08:42 +00:00
if ( specialland = = laMinefield & & closed_or_bounded ) {
2019-04-10 00:34:17 +00:00
bfs ( ) ;
2019-04-04 12:36:45 +00:00
generate_mines ( ) ;
2019-04-10 00:34:17 +00:00
}
2022-05-21 11:08:42 +00:00
if ( specialland = = laHalloween )
halloween : : generate ( ) ;
2021-04-15 16:21:05 +00:00
if ( in_lovasz ( ) ) {
2021-03-21 10:30:42 +00:00
cwt . at - > item = itOrbInvis ;
}
2024-05-16 16:13:48 +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 ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
if ( ! safety ) {
usedSafety = false ;
timerstart = time ( NULL ) ; turncount = 0 ; rosewave = 0 ; rosephase = 0 ;
2024-05-28 13:09:18 +00:00
tickstart = ticks ;
2016-08-26 09:58:03 +00:00
noiseuntil = 0 ;
sagephase = 0 ; hardcoreAt = 0 ;
timerstopped = false ;
savecount = 0 ; savetime = 0 ;
2023-12-12 19:26:08 +00:00
tortoise : : last21tort = 0 ;
2016-08-26 09:58:03 +00:00
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 ;
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
2020-10-15 14:33:52 +00:00
# if CAP_COMPLEX2
2019-12-08 18:17:28 +00:00
mine : : auto_teleport_charges ( ) ;
2020-10-15 14:33:52 +00:00
# endif
2017-08-06 12:50:16 +00:00
welcomeMessage ( ) ;
2016-08-26 09:58:03 +00:00
}
else {
usedSafety = true ;
safety = false ;
}
2024-05-16 16:13:48 +00:00
2017-03-23 10:53:57 +00:00
havewhat = hadwhat = 0 ; rosemap . clear ( ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
elec : : lightningfast = 0 ;
2024-05-16 16:13:48 +00:00
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 ;
2024-05-16 16:13:48 +00:00
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 ;
2024-04-22 22:07:35 +00:00
calcTidalPhase ( ) ;
callhooks ( hooks_post_initgame ) ;
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).
*
2024-05-16 16:13:48 +00:00
* Most ApplyBox functions are used both for saving savegames and scores to the logfile , loading savegames and scores from the logfile ,
2020-03-27 19:37:43 +00:00
* 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 */
2024-04-13 22:42:11 +00:00
# define POSSCORE 418
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 ] ;
2024-02-22 23:39:12 +00:00
/** \brief yasc message */
string yasc_message ;
2016-08-26 09:58:03 +00:00
} ;
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 ;
2024-05-16 16:13:48 +00:00
if ( loadingHi ) {
2020-08-02 00:04:18 +00:00
updateHi_for_code ( it , save . box [ boxid + + ] , saved_modecode ) ;
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 ) ;
2024-05-16 16:13:48 +00:00
}
2017-07-16 21:00:55 +00:00
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-08-02 00:04:18 +00:00
EX modecode_t saved_modecode ;
2023-10-27 20:44:40 +00:00
template < class T > void applyBoxEnum ( T & i , string name IS ( " " ) ) {
int ii = ( int ) i ;
applyBoxNum ( ii , name ) ;
i = ( T ) ii ;
}
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 ) ;
2024-05-16 16:13:48 +00:00
for ( int i = 0 ; i < itOrbLightning ; i + + )
2016-08-26 09:58:03 +00:00
if ( i = = 0 ) items [ i ] = 0 , applyBoxI ( itFernFlower ) ;
else applyBoxI ( eItem ( i ) ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
for ( int i = 0 ; i < 43 ; i + + ) {
if ( loading ) kills [ i ] = 0 ;
if ( i = = moWormtail ) applyBoxM ( moCrystalSage ) ;
else if ( i = = moWormwait ) applyBoxM ( moFireFairy ) ;
else if ( i = = moTentacleEscaping ) applyBoxM ( moMiner ) ;
2024-02-24 18:54:45 +00:00
else if ( i = = moREMOVED ) applyBoxI ( itFatigue ) ;
2016-08-26 09:58:03 +00:00
else if ( i = = moGolemMoved ) applyBoxM ( moIllusion ) ;
2024-02-24 23:50:15 +00:00
else if ( i = = moTentacletail ) applyBoxI ( itSnake ) ;
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 ) ;
2024-02-25 00:17:29 +00:00
else if ( i = = moLesserM ) applyBoxM ( moFriendlyGhost ) ;
2024-02-21 18:17:12 +00:00
else if ( i = = moWolfMoved ) applyBoxM ( moWorldTurtle ) ;
2024-02-22 11:33:49 +00:00
else if ( i = = moNone ) applyBoxNum ( kills [ i ] , " icewalls melted " ) ;
2024-02-25 00:17:29 +00:00
else applyBoxM ( eMonster ( i ) ) ;
2016-08-26 09:58:03 +00:00
}
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
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 + + ;
2024-05-16 16:13:48 +00:00
if ( saving ) savecount + + ;
applyBoxNum ( savecount , " number of saves " ) ;
2016-08-26 09:58:03 +00:00
if ( saving ) savecount - - ;
applyBoxNum ( cheater , " number of cheats " ) ;
2024-05-16 16:13:48 +00:00
2024-02-22 23:39:12 +00:00
fakebox [ boxid ] = false ;
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 + + ] ) ;
2024-05-16 16:13:48 +00:00
2017-07-16 21:00:55 +00:00
for ( int i = itOrbLightning ; i < 25 ; i + + ) applyBoxOrb ( eItem ( i ) ) ;
2024-05-16 16:13:48 +00:00
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 ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
applyBoxM ( moPirate ) ;
applyBoxM ( moCShark ) ;
applyBoxM ( moParrot ) ;
applyBoxI ( itPirate ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbTime ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
applyBoxM ( moHexSnake ) ;
applyBoxM ( moRedTroll ) ;
applyBoxI ( itRedGem ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbSpace ) ;
2024-05-16 16:13:48 +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 ;
2024-05-16 16:13:48 +00:00
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 ) ;
2024-05-16 16:13:48 +00:00
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 ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
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 " ) ;
2024-05-16 16:13:48 +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 ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
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 ) ;
2024-05-16 16:13:48 +00:00
2020-04-17 13:04:27 +00:00
applyBoxBool ( tactic : : on , " @tactic " ) ;
applyBoxNum ( elec : : lightningfast , " @lightningfast " ) ;
2024-05-16 16:13:48 +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 " ) ;
2024-05-16 16:13:48 +00:00
2023-10-27 20:44:40 +00:00
applyBoxEnum ( variation , " variation " ) ;
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 ) ;
2023-10-27 20:44:40 +00:00
applyBoxEnum ( land_structure , " land structure " ) ;
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 ) ;
2024-05-16 16:13:48 +00:00
// printf("applybox %d\n", shmup::players);
2016-08-26 09:58:03 +00:00
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 ) ;
2024-05-16 16:13:48 +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 ) ;
2024-05-16 16:13:48 +00:00
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 " ) ;
2024-05-16 16:13:48 +00:00
2017-03-23 10:53:57 +00:00
applyBoxI ( itDodeca ) ;
applyBoxI ( itAmethyst ) ;
applyBoxI ( itSlime ) ;
2017-07-16 21:00:55 +00:00
applyBoxOrb ( itOrbNature ) ;
2024-05-16 16:13:48 +00:00
applyBoxOrb ( itOrbDash ) ;
2017-07-16 21:00:55 +00:00
addinv ( itOrbRecall ) ;
2017-03-23 10:53:57 +00:00
applyBoxM ( moBat ) ;
applyBoxM ( moReptile ) ;
applyBoxM ( moFriendlyIvy ) ;
2024-05-16 16:13:48 +00:00
2017-03-23 10:53:57 +00:00
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 ( ) ;
2017-07-22 23:33:27 +00:00
# if CAP_INV
2022-09-29 13:12:56 +00:00
applyBoxBool ( inv : : on , " inventory " ) ; // 306
2020-04-17 13:04:27 +00:00
applyBoxNum ( inv : : rseed , " @inv-rseed " ) ;
2017-07-16 21:00:55 +00:00
# else
2022-09-29 13:12:56 +00:00
{ int u ; applyBoxNum ( u ) ; applyBoxNum ( u ) ; }
2017-07-16 21:00:55 +00:00
# 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 ) ;
2024-05-16 16:13:48 +00:00
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 ) ;
2024-05-16 16:13:48 +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 " ) ;
2024-05-16 16:13:48 +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 " ) ;
2024-05-16 16:13:48 +00:00
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 ( ) ;
2024-05-16 16:13:48 +00:00
2019-04-08 11:51:23 +00:00
applyBoxM ( moNarciss ) ;
2024-05-16 16:13:48 +00:00
applyBoxM ( moMirrorSpirit ) ;
2020-04-17 13:04:27 +00:00
applyBoxNum ( clearing : : direct , " @clearing-direct " ) ;
applyBoxBignum ( clearing : : imputed , " @clearing-imputed " ) ;
2024-05-16 16:13:48 +00:00
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
2020-08-02 00:04:18 +00:00
applyBoxNum ( saved_modecode , " modecode " ) ;
2021-04-11 20:15:40 +00:00
applyBoxBool ( ineligible_starting_land , " ineligible_starting_land " ) ;
2024-05-16 16:13:48 +00:00
2021-05-27 10:57:12 +00:00
applyBoxNum ( yasc_code , " YASC code " ) ;
applyBoxBool ( casual , " casual mode " ) ;
2024-05-16 16:13:48 +00:00
2021-05-30 11:38:32 +00:00
applyBoxI ( itCursed ) ;
applyBoxI ( itDice ) ;
applyBoxOrb ( itOrbPurity ) ;
applyBoxOrb ( itOrbWoods ) ;
applyBoxM ( moHexer ) ;
applyBoxM ( moAngryDie ) ;
applyBoxM ( moAnimatedDie ) ;
applyBoxI ( itCurseWeakness , true ) ;
applyBoxI ( itCurseFatigue , true ) ;
applyBoxI ( itCurseDraining , true ) ;
applyBoxI ( itCurseRepulsion , true ) ;
applyBoxI ( itCurseGluttony , true ) ;
applyBoxI ( itCurseWater , true ) ;
list_invorb ( ) ;
2020-08-02 00:04:18 +00:00
2023-10-27 20:46:11 +00:00
applyBoxEnum ( bow : : weapon , " weapon choice " ) ;
applyBoxEnum ( bow : : style , " crossbow style " ) ;
2024-02-18 04:48:58 +00:00
applyBoxOrb ( itOrbFish ) ;
list_invorb ( ) ;
2024-02-27 01:17:38 +00:00
applyBoxNum ( items [ itCrossbow ] ) ;
2024-02-28 05:30:33 +00:00
applyBoxNum ( items [ itRevolver ] ) ;
2024-03-28 04:06:28 +00:00
applyBoxNum ( items [ itAsteroid ] ) ;
applyBoxM ( moAsteroid ) ;
2024-04-06 16:36:14 +00:00
applyBoxNum ( items [ itTreat ] ) ;
applyBoxM ( moVampire ) ;
2024-04-13 22:42:11 +00:00
applyBoxNum ( asteroids_generated ) ;
applyBoxNum ( asteroid_orbs_generated ) ;
2024-02-27 01:17:38 +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-08-02 00:04:18 +00:00
const int MODECODE_BOX = 387 ;
2016-08-26 09:58:03 +00:00
2020-08-02 00:04:18 +00:00
modecode_t fill_modecode ( ) {
2020-03-27 19:21:08 +00:00
dynamicval < int > sp1 ( multi : : players , save . box [ 197 ] ) ;
dynamicval < eGeometry > sp2 ( geometry , ( eGeometry ) save . box [ 116 ] ) ;
2020-08-02 00:04:18 +00:00
if ( among ( geometry , gArchimedean , gProduct , gRotSpace , gArbitrary ) )
return 6 ; /* these would not be saved nor loaded correctly */
2020-03-27 19:21:08 +00:00
dynamicval < bool > sp3 ( shmup : : on , save . box [ 119 ] ) ;
2021-04-11 20:15:40 +00:00
dynamicval < eLandStructure > sp4 ( land_structure , ( eLandStructure ) save . box [ 196 ] ) ;
2020-03-27 19:21:08 +00:00
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 ] ) ;
2022-09-29 13:12:56 +00:00
# if CAP_INV
2020-03-27 19:21:08 +00:00
dynamicval < bool > spinv ( inv : : on , save . box [ 306 ] ) ;
2022-09-29 13:12:56 +00:00
# endif
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
2024-05-16 16:13:48 +00:00
if ( multi : : players < 0 | | multi : : players > MAXPLAYER )
2020-08-02 00:04:18 +00:00
return 6 ;
if ( multi : : players = = 0 )
2017-03-23 10:53:57 +00:00
multi : : players = 1 ;
2020-08-02 00:04:18 +00:00
if ( shmup : : on & & multi : : players = = 1 & & boxid < = 258 )
return 6 ; /* not sure why */
2024-05-16 16:13:48 +00:00
2020-08-02 00:04:18 +00:00
return modecode ( 2 ) ;
}
/** \brief load the current game values from save into the highscore tables */
void loadBoxHigh ( ) {
saved_modecode = save . box [ MODECODE_BOX ] ;
boxid = 0 ; loadingHi = true ; applyBoxes ( ) ; loadingHi = false ;
2016-08-26 09:58:03 +00:00
}
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 ( ) {
2022-09-15 10:38:39 +00:00
if ( scorefile = = " " ) return ;
2017-07-22 23:33:27 +00:00
# if !ISWINDOWS
2024-05-16 16:13:48 +00:00
if ( saveposition > = 0 ) {
2022-09-15 10:38:39 +00:00
if ( truncate ( scorefile . c_str ( ) , saveposition ) ) { }
2017-03-23 10:53:57 +00:00
saveposition = - 1 ;
}
# endif
}
2021-05-27 10:57:12 +00:00
scores : : score scorebox ;
2024-03-27 19:59:41 +00:00
EX bool save_cheats ;
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
2024-03-27 19:59:41 +00:00
if ( autocheat & & ! save_cheats ) return ;
2022-09-15 10:38:39 +00:00
if ( scorefile = = " " ) return ;
2017-07-22 23:33:27 +00:00
# if CAP_TOUR
2024-03-27 19:59:41 +00:00
if ( tour : : on & & ! save_cheats ) return ;
2017-04-08 15:18:29 +00:00
# endif
2024-03-27 19:59:41 +00:00
if ( randomPatternsMode & & ! save_cheats ) return ;
2018-05-15 21:26:04 +00:00
if ( daily : : on ) return ;
2024-03-27 19:59:41 +00:00
if ( peace : : on & & ! save_cheats ) return ;
2019-09-08 10:25:00 +00:00
if ( experimental ) return ;
2022-06-16 21:11:22 +00:00
if ( ! gold ( ) & & ! racing : : on ) return ;
2024-05-16 16:13:48 +00:00
2017-03-23 10:53:57 +00:00
remove_emergency_save ( ) ;
2024-05-16 16:13:48 +00:00
2020-08-02 00:04:18 +00:00
auto & xcode = scores : : saved_modecode ;
2016-08-26 09:58:03 +00:00
2020-08-02 00:04:18 +00:00
xcode = modecode ( ) ;
2024-05-16 16:13:48 +00:00
2022-09-15 10:38:39 +00:00
FILE * f = fopen ( scorefile . c_str ( ) , " at " ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
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
}
2024-05-16 16:13:48 +00:00
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 ) ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
if ( ( tactic : : on | | yendor : : on ) & & ! items [ itOrbSafety ] & & ! cheater ) {
int t = ( int ) ( timer - timerstart ) ;
if ( tactic : : on ) {
2018-04-11 15:44:46 +00:00
int score = items [ treasureType ( specialland ) ] ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
if ( score ) {
2024-05-16 16:13:48 +00:00
int c =
2018-04-11 15:44:46 +00:00
anticheat : : certify ( dnameof ( specialland ) , turncount , t , ( int ) timerstart ,
2020-08-02 00:04:18 +00:00
unsigned ( xcode ) * 999 + tactic : : id + 256 * score ) ;
2018-01-06 21:05:22 +00:00
fprintf ( f , " TACTICS %s %d %d %d %d %d %d %d %d date: %s \n " , VER ,
2024-05-16 16:13:48 +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 ,
2024-05-16 16:13:48 +00:00
yendor : : lastchallenge , items [ itOrbYendor ] , yendor : : won , turncount , t , int ( timerstart ) ,
2016-08-26 09:58:03 +00:00
anticheat : : certify ( yendor : : won ? " WON " : " LOST " , turncount , t , ( int ) timerstart ,
2020-08-02 00:04:18 +00:00
unsigned ( xcode ) * 999 + yendor : : lastchallenge + 256 * items [ itOrbYendor ] ) ,
2019-08-20 11:07:16 +00:00
int ( xcode ) ,
2016-08-26 09:58:03 +00:00
buf ) ;
fclose ( f ) ;
return ;
}
2022-07-12 11:19:18 +00:00
# if CAP_RACING
2022-06-16 21:11:22 +00:00
if ( racing : : on ) {
2022-08-07 00:16:40 +00:00
auto & bs = racing : : best_scores_to_save ;
if ( racing : : official_race & & ! cheater & & bs . count ( specialland ) ) {
2022-06-16 21:11:22 +00:00
fprintf ( f , " RACING %s %d %d date: %s \n " , VER ,
2022-08-07 00:16:40 +00:00
int ( specialland ) , bs [ specialland ] ,
2022-06-16 21:11:22 +00:00
buf ) ;
2022-08-07 00:16:40 +00:00
bs . erase ( specialland ) ;
2022-06-16 21:11:22 +00:00
}
2022-09-14 14:18:22 +00:00
fclose ( f ) ;
2022-06-16 21:11:22 +00:00
return ;
}
2022-07-12 11:19:18 +00:00
# endif
2022-06-16 21:11:22 +00:00
2016-08-26 09:58:03 +00:00
fprintf ( f , " HyperRogue: game statistics (version " VER " ) \n " ) ;
if ( cheater )
fprintf ( f , " CHEATER! (cheated %d times) \n " , cheater ) ;
if ( true ) {
fprintf ( f , VER ) ;
2024-05-16 16:13:48 +00:00
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 ( ) ;
2024-05-16 16:13:48 +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 ] ) ;
2021-05-27 10:57:12 +00:00
scorebox = scores : : save ;
2016-08-26 09:58:03 +00:00
anticheat : : save ( f ) ;
fprintf ( f , " \n " ) ;
2024-02-22 23:39:12 +00:00
if ( yasc_message ! = " " ) fprintf ( f , " YASC %s \n " , yasc_message . c_str ( ) ) ;
2016-08-26 09:58:03 +00:00
}
2024-05-16 16:13:48 +00:00
fprintf ( f , " Played on: %s - %s (%d %s) \n " , sbuf , buf , turncount ,
2016-08-26 09:58:03 +00:00
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 ( ) ) ;
2021-04-11 20:15:40 +00:00
if ( ! ls : : nice_walls ( ) )
fprintf ( f , " land structure: %s \n " , land_structure_name ( true ) . c_str ( ) ) ;
2024-05-16 16:13:48 +00:00
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 ) ;
2024-05-16 16:13:48 +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 ) ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
fprintf ( f , " \n " ) ;
2024-05-16 16:13:48 +00:00
for ( int i = 0 ; i < ittypes ; i + + ) if ( items [ i ] )
2016-08-26 09:58:03 +00:00
fprintf ( f , " %4dx %s \n " , items [ i ] , iinf [ i ] . name ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
fprintf ( f , " \n " ) ;
2024-05-16 16:13:48 +00:00
for ( int i = 1 ; i < motypes ; i + + ) if ( kills [ i ] )
2016-08-26 09:58:03 +00:00
fprintf ( f , " %4dx %s \n " , kills [ i ] , minf [ i ] . name ) ;
2024-05-16 16:13:48 +00:00
2016-08-26 09:58:03 +00:00
fprintf ( f , " \n \n \n " ) ;
2024-05-16 16:13:48 +00:00
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 ) ;
}
2021-05-27 10:57:12 +00:00
bool tamper = false ;
2016-08-26 09:58:03 +00:00
// 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 ;
2022-09-15 10:38:39 +00:00
if ( scorefile = = " " ) 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
2022-09-15 10:38:39 +00:00
FILE * f = fopen ( scorefile . c_str ( ) , " rt " ) ;
2016-08-26 09:58:03 +00:00
havesave = f ;
if ( ! f ) return ;
bool ok = false ;
2017-07-16 21:00:55 +00:00
int coh = counthints ( ) ;
2021-05-27 10:57:12 +00:00
auto & sc = scorebox ;
2016-08-26 09:58:03 +00:00
while ( ! feof ( f ) ) {
2020-08-02 00:04:18 +00:00
char buf [ 12000 ] ;
if ( fgets ( buf , 12000 , f ) = = NULL ) break ;
if ( buf [ 0 ] = = ' M ' & & buf [ 1 ] = = ' O ' ) {
string s = buf ;
while ( s ! = " " & & s . back ( ) < 32 ) s . pop_back ( ) ;
load_modecode_line ( s ) ;
}
2016-08-26 09:58:03 +00:00
if ( buf [ 0 ] = = ' H ' & & buf [ 1 ] = = ' y ' ) {
2024-03-27 19:59:41 +00:00
if ( fscanf ( f , " %9999s " , buf ) < = 0 ) break ;
2017-03-23 10:53:57 +00:00
sc . ver = buf ;
2024-03-27 19:59:41 +00:00
if ( sc . ver = = " CHEATER! " & & save_cheats ) {
2024-04-27 23:41:25 +00:00
if ( fgets ( buf , 12000 , f ) = = NULL ) break ;
2024-03-27 19:59:41 +00:00
if ( fscanf ( f , " %9999s " , 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 ;
2024-05-16 16:13:48 +00:00
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 ;
2024-05-16 16:13:48 +00:00
2020-08-02 00:04:18 +00:00
if ( boxid < = MODECODE_BOX ) save . box [ MODECODE_BOX ] = sc . box [ MODECODE_BOX ] = fill_modecode ( ) ;
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 ) ;
2024-05-16 16:13:48 +00:00
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 + + )
2024-05-16 16:13:48 +00:00
if ( anticheat : : check ( cert , ver , dnameof ( l2 ) , tc , t , ts , xc * 999 + unsigned ( tid ) + 256 * score ) )
2018-01-06 21:05:22 +00:00
break ;
2024-05-16 16:13:48 +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 ) ) ) {
2024-05-16 16:13:48 +00:00
if ( score ! = 0
2018-01-06 21:05:22 +00:00
& & ! ( 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 + + )
2024-05-16 16:13:48 +00:00
if ( anticheat : : check ( cert , ver , won ? " WON " : " LOST " , tc , t , ts , xc * 999 + cid + 256 * oy ) )
2018-01-06 21:05:22 +00:00
break ;
2024-05-16 16:13:48 +00:00
2018-01-06 21:05:22 +00:00
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 ;
2024-05-16 16:13:48 +00:00
if ( cid > 0 & & cid < YENDORLEVELS )
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 ) ;
}
}
}
2022-07-12 11:19:18 +00:00
# if CAP_RACING
2022-06-16 21:11:22 +00:00
if ( buf [ 0 ] = = ' R ' & & buf [ 1 ] = = ' A ' & & buf [ 2 ] = = ' C ' ) {
char buf1 [ 80 ] , ver [ 10 ] ;
int land , score ;
sscanf ( buf , " %70s%9s%d%d " , buf1 , ver , & land , & score ) ;
2022-08-07 00:16:40 +00:00
/* score may equal 0 because of earlier bugs */
2022-08-07 13:01:28 +00:00
if ( score ) {
auto & res = racing : : best_scores [ eLand ( land ) ] ;
if ( score < res | | res = = 0 ) res = score ;
}
2022-06-16 21:11:22 +00:00
println ( hlog , " loaded the score for " , dnameof ( eLand ( land ) ) , " of " , score ) ;
}
2022-07-12 11:19:18 +00:00
# endif
2022-06-16 21:11:22 +00:00
2016-08-26 09:58:03 +00:00
}
fclose ( f ) ;
2024-03-27 19:59:41 +00:00
// this is the index of Orb of Safety
2024-05-16 16:13:48 +00:00
if ( ok & & sc . box [ 65 + 4 + itOrbSafety - itOrbLightning ] )
2021-05-27 10:57:12 +00:00
load_last_save ( ) ;
}
EX void load_last_save ( ) {
auto & sc = scorebox ;
anticheat : : tampered = tamper ;
2016-08-26 09:58:03 +00:00
// printf("box = %d (%d)\n", sc.box[65 + 4 + itOrbSafety - itOrbLightning], boxid);
// printf("boxid = %d\n", boxid);
2021-05-27 10:57:12 +00:00
using namespace scores ;
for ( int i = 0 ; i < boxid ; i + + ) save . box [ i ] = sc . box [ i ] ;
for ( int i = boxid ; i < MAXBOX ; i + + ) save . box [ i ] = 0 ;
2020-03-27 19:21:08 +00:00
// for(int i=160; i<200; i++) printf("%d: %d ", i, save.box[i]);
2020-08-02 10:53:19 +00:00
2021-05-27 10:57:12 +00:00
if ( meaning . count ( sc . box [ MODECODE_BOX ] ) ) {
shstream ss ;
ss . s = meaning [ sc . box [ MODECODE_BOX ] ] ;
ss . read ( ss . vernum ) ;
2024-03-27 20:31:39 +00:00
if ( ss . vernum < 0xAA05 )
mapstream : : load_geometry ( ss ) ;
else {
ss . write_char ( 0 ) ;
load_mode_data_with_zero ( ss ) ;
}
2021-05-27 10:57:12 +00:00
}
2020-08-02 10:53:19 +00:00
2021-05-27 10:57:12 +00:00
loadBox ( ) ;
2016-08-26 09:58:03 +00:00
// printf("boxid = %d\n", boxid);
2022-09-29 13:12:56 +00:00
# if CAP_COMPLEX2
2021-05-27 10:57:12 +00:00
if ( items [ itHolyGrail ] ) {
items [ itHolyGrail ] - - ;
camelot : : knighted = newRoundTableRadius ( ) ;
items [ itHolyGrail ] + + ;
2016-08-26 09:58:03 +00:00
}
2021-05-27 10:57:12 +00:00
else camelot : : knighted = 0 ;
2022-09-29 13:12:56 +00:00
# endif
2021-05-27 10:57:12 +00:00
safety = true ;
if ( items [ itSavedPrincess ] < 0 ) items [ itSavedPrincess ] = 0 ;
addMessage ( XLAT ( " Game loaded. " ) ) ;
showstartmenu = false ;
// reset unsavable special modes just in case
peace : : on = false ;
randomPatternsMode = false ;
yendor : : on = false ;
tour : : on = false ;
save_turns = turncount ;
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
2021-05-27 10:57:12 +00:00
if ( ! casual )
saveStats ( ) ;
2018-06-10 22:58:38 +00:00
# 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 ;
2024-05-16 16:13:48 +00:00
for ( int i : player_indices ( ) )
2021-03-06 10:46:13 +00:00
multi : : deaths [ i ] + + ;
2018-06-10 22:58:38 +00:00
# if CAP_SAVE
2024-05-16 16:13:48 +00:00
anticheat : : tampered = false ;
2018-06-10 22:58:38 +00:00
# endif
achievementsReceived . clear ( ) ;
princess : : saved = false ;
princess : : nodungeon = false ;
princess : : reviveAt = 0 ;
princess : : forceVizier = false ;
princess : : forceMouse = false ;
2020-10-15 14:33:52 +00:00
# if CAP_COMPLEX2
2019-12-08 18:17:28 +00:00
camelot : : knighted = 0 ;
2020-10-15 14:33:52 +00:00
# endif
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 ( ) {
2022-12-16 22:08:54 +00:00
if ( sl2 ) return mdGeodesic ;
if ( nonisotropic ) return nisot : : geodesic_movement ? mdGeodesic : mdLiePerspective ;
2019-10-06 09:52:06 +00:00
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
2021-09-17 10:20:15 +00:00
EX void geometry_settings ( bool was_default ) {
if ( was_default ) pmodel = default_model ( ) ;
if ( WDIM = = 2 & & ( cgflags & qIDEAL ) & & vid . always3 & & vid . texture_step < 32 ) vid . texture_step = 32 ;
if ( sl2 ) nisot : : geodesic_movement = true ;
}
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 ) {
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 ;
2024-05-16 16:13:48 +00:00
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
2022-12-08 18:38:06 +00:00
if ( DUAL & & geometry ! = gArchimedean & & ! mhybrid )
2018-08-30 00:11:43 +00:00
variation = ginf [ geometry ] . default_variation ;
2019-02-17 17:28:20 +00:00
# if CAP_BT
2023-03-23 00:32:24 +00:00
if ( bt : : in ( ) | | WDIM = = 3 | | aperiodic | | arb : : in ( ) ) if ( ! mhybrid ) 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 ;
2022-12-08 18:38:06 +00:00
if ( INVERSE & & ! mhybrid ) variation = gp : : variation_for ( gp : : param ) ;
if ( ginf [ target ] . default_variation = = eVariation : : pure & & geometry ! = gArchimedean & & ! mhybrid )
2020-04-10 22:42:41 +00:00
variation = eVariation : : pure ;
2021-09-17 10:20:15 +00:00
geometry_settings ( was_default ) ;
2024-05-16 16:13:48 +00:00
2021-07-30 10:25:17 +00:00
if ( geometry = = gArbitrary ) {
arb : : convert : : base_geometry = geometry ;
arb : : convert : : base_variation = variation ;
}
2020-07-24 00:30:50 +00:00
if ( rotspace ) {
2024-05-16 16:13:48 +00:00
check_cgi ( ) ; cgi . require_basics ( ) ;
2020-07-24 00:30:50 +00:00
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 ( ) ;
2021-07-07 16:26:03 +00:00
if ( is_subcube_based ( target ) ) {
2021-07-06 23:48:20 +00:00
if ( ! reg3 : : in ( ) ) geometry = hyperbolic ? gSpace435 : gCell8 ;
variation = target ;
return ;
}
2021-07-08 22:07:35 +00:00
if ( is_reg3_variation ( target ) ) {
if ( ! reg3 : : in ( ) ) geometry = hyperbolic ? gSpace435 : gCell8 ;
variation = target ;
return ;
}
2021-07-09 12:01:55 +00:00
if ( target ! = eVariation : : pure ) {
2023-03-23 00:32:24 +00:00
if ( bt : : in ( ) | | sol | | aperiodic | | WDIM = = 3 ) if ( ! mproduct ) geometry = gNormal ;
2021-07-09 12:01:55 +00:00
}
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 ;
}
}
2024-05-16 16:13:48 +00:00
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 ;
2024-05-16 16:13:48 +00:00
tactic : : on = yendor : : on = princess : : challenge =
2018-06-10 22:58:38 +00:00
randomPatternsMode = inv : : on = false ;
2018-11-24 22:52:13 +00:00
racing : : on = false ;
2023-10-28 06:22:31 +00:00
bow : : weapon = bow : : wBlade ;
2018-06-10 22:58:38 +00:00
break ;
2024-05-16 16:13:48 +00:00
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 ;
2023-10-28 06:22:31 +00:00
bow : : weapon = bow : : wBlade ;
2019-05-29 00:44:30 +00:00
if ( ! dual : : state ) dual : : enable ( ) ;
else dual : : disable ( ) ;
break ;
2024-05-16 16:13:48 +00:00
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 ;
2024-05-16 16:13:48 +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 ;
2021-04-11 20:15:40 +00:00
land_structure = ls : : any_chaos ( ) ? lsNiceWalls : lsChaos ;
2022-05-21 11:08:42 +00:00
if ( closed_or_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 ( ) ;
2021-04-11 20:15:40 +00:00
land_structure = lsNiceWalls ;
randomPatternsMode = false ;
2018-08-28 15:17:34 +00:00
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 ;
2021-04-11 20:15:40 +00:00
land_structure = lsNiceWalls ;
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
2024-05-16 16:13:48 +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 ;
2022-10-06 10:57:58 +00:00
yendor : : on = false ;
2022-02-02 02:01:31 +00:00
land_structure = racing : : on ? lsSingle : lsNiceWalls ;
2018-11-24 22:52:13 +00:00
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 ;
2021-04-11 20:15:40 +00:00
land_structure = tactic : : on ? lsSingle : lsNiceWalls ;
2018-06-10 22:58:38 +00:00
if ( ! tactic : : on ) firstland = laIce ;
2019-05-29 00:44:30 +00:00
dual : : disable ( ) ;
2018-06-10 22:58:38 +00:00
break ;
2024-05-16 16:13:48 +00:00
2018-06-10 22:58:38 +00:00
case rg : : shmup :
shmup : : on = ! shmup : : on ;
princess : : challenge = false ;
2018-11-24 22:52:13 +00:00
if ( ! shmup : : on ) racing : : on = false ;
2023-10-28 06:22:31 +00:00
if ( ! shmup : : on & & multi : : players > 1 ) bow : : weapon = bow : : wBlade ;
2018-06-10 22:58:38 +00:00
break ;
2024-05-16 16:13:48 +00:00
2018-06-10 22:58:38 +00:00
case rg : : randpattern :
randomPatternsMode = ! randomPatternsMode ;
tactic : : on = false ;
yendor : : on = false ;
peace : : on = false ;
princess : : challenge = false ;
break ;
2024-05-16 16:13:48 +00:00
2018-06-10 22:58:38 +00:00
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 ;
2021-04-11 20:15:40 +00:00
land_structure = princess : : challenge ? lsSingle : lsNiceWalls ;
2018-06-10 22:58:38 +00:00
inv : : on = false ;
2018-11-24 22:52:13 +00:00
racing : : on = false ;
2024-05-08 19:32:26 +00:00
use_custom_land_list = false ;
2019-05-29 00:44:30 +00:00
dual : : disable ( ) ;
2018-06-10 22:58:38 +00:00
break ;
2024-05-16 16:13:48 +00:00
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 ;
2024-05-16 16:13:48 +00:00
2018-06-10 22:58:38 +00:00
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 " ) ) ;
2024-05-16 16:13:48 +00:00
if ( dual : : state = = 1 ) dual : : assign_landsides ( ) ;
2019-05-28 23:09:38 +00:00
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 ( ) ;
2020-10-15 14:33:52 +00:00
# if CAP_ARCM
2021-08-05 09:56:12 +00:00
arcm : : current_or_fake ( ) . compute_geometry ( ) ;
2020-10-15 14:33:52 +00:00
# endif
2016-08-26 09:58:03 +00:00
initcells ( ) ;
2021-12-11 22:28:05 +00:00
get_expansion ( ) . reset ( ) ;
2022-05-21 11:08:42 +00:00
init_disk_cells ( ) ;
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 ) ) {
2024-05-16 16:13:48 +00:00
popScreenAll ( ) ;
2022-07-12 11:19:18 +00:00
# if CAP_RACING
2022-06-16 21:11:34 +00:00
if ( switchWhat = = rg : : nothing & & racing : : on ) {
racing : : restore_goals ( ) ;
racing : : reset_race ( ) ;
2024-02-22 15:47:20 +00:00
shmup : : count_pauses = 0 ;
2022-06-16 21:11:34 +00:00
return ;
}
2022-07-12 11:19:18 +00:00
# endif
2018-06-10 22:58:38 +00:00
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 ;
2022-09-15 10:38:39 +00:00
EX bool savefile_selection = false ;
EX void select_savefile ( ) {
if ( ! savefile_selection ) return ;
start_game ( ) ;
bool canceled = true ;
if ( scorefile = = " " ) scorefile = " hyperrogue.log " ;
pushScreen ( [ ] { quitmainloop = true ; } ) ;
dialog : : openFileDialog ( scorefile , XLAT ( " choose your score/save file " ) , " .log " , [ & ] {
println ( hlog , " hook called " ) ;
canceled = false ;
quitmainloop = true ;
return true ;
} ) ;
clearMessages ( ) ;
mainloop ( ) ;
quitmainloop = false ;
stop_game ( ) ;
popScreenAll ( ) ;
if ( canceled ) scorefile = " " ;
}
EX void progress_warning ( ) {
if ( scorefile = = " " & & savefile_selection )
addMessage ( XLAT ( " Your progress will not be saved. " ) ) ;
}
2023-12-13 20:34:54 +00:00
EX void restore_all_golems ( ) {
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 ;
}
}
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 ;
2024-05-16 16:13:48 +00:00
2019-09-05 07:01:47 +00:00
// initlanguage();
2021-02-06 00:39:18 +00:00
initialize_all ( ) ;
2024-05-16 16:13:48 +00:00
2019-09-05 07:01:47 +00:00
# if CAP_SAVE
2022-09-15 10:38:39 +00:00
select_savefile ( ) ;
2019-09-05 07:01:47 +00:00
loadsave ( ) ;
2024-03-23 23:30:50 +00:00
if ( IRREGULAR & & ! irr : : base ) irr : : auto_creator ( ) ;
2019-09-05 07:01:47 +00:00
# endif
start_game ( ) ;
2023-12-13 20:34:54 +00:00
restore_all_golems ( ) ;
2024-05-16 16:13:48 +00:00
2019-09-05 07:01:47 +00:00
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 ] ) ;
2024-05-16 16:13:48 +00:00
2019-09-05 07:01:47 +00:00
# if CAP_SAVE
2021-05-27 10:57:12 +00:00
if ( ! casual )
saveStats ( ) ;
2019-09-05 07:01:47 +00:00
# endif
clearMemory ( ) ;
2020-05-03 18:56:41 +00:00
# if !ISMOBILE
2021-02-06 00:39:18 +00:00
quit_all ( ) ;
2019-09-05 07:01:47 +00:00
# endif
2024-05-16 16:13:48 +00:00
achievement_close ( ) ;
2020-04-11 18:40:12 +00:00
callhooks ( hooks_final_cleanup ) ;
2019-09-05 07:01:47 +00:00
}
2024-03-14 18:28:33 +00:00
string modheader = " # HyperRogue saved game mode file " ;
2024-03-24 10:15:13 +00:00
set < string > allowed_params = {
" creature_scale " , " global_boundary_ratio " , " specialland "
} ;
2024-03-14 18:28:33 +00:00
EX void save_mode_to_file ( const string & fname ) {
shstream ss ;
save_mode_data ( ss ) ;
string s = as_hexstring ( ss . s ) ;
fhstream f ( fname , " w " ) ;
2024-03-22 00:22:34 +00:00
if ( ! f . f ) throw hstream_exception ( ) ;
2024-03-14 18:28:33 +00:00
println ( f , modheader ) ;
println ( f , s ) ;
2024-03-22 00:22:34 +00:00
if ( custom_welcome ! = " " ) println ( f , " CMSG " , custom_welcome ) ;
2024-03-24 10:15:13 +00:00
for ( auto & ap : allowed_params ) {
2024-05-26 18:22:29 +00:00
auto & s = params [ ap ] ;
2024-03-24 10:15:13 +00:00
if ( s - > dosave ( ) )
println ( f , ap , " = " , s - > save ( ) ) ;
}
2024-03-14 18:28:33 +00:00
}
EX void load_mode_from_file ( const string & fname ) {
fhstream f ( fname , " r " ) ;
2024-03-22 00:22:34 +00:00
if ( ! f . f ) throw hstream_exception ( ) ;
2024-03-14 18:28:33 +00:00
string header = scanline ( f ) ;
if ( header [ 0 ] ! = ' # ' ) throw hstream_exception ( ) ;
string hex = scanline ( f ) ;
shstream ss ;
ss . s = from_hexstring ( hex + " 00 " ) ;
2024-03-22 00:22:34 +00:00
custom_welcome = " " ;
while ( true ) {
2024-03-24 10:15:13 +00:00
string s = scanline_noblank ( f ) ;
2024-03-22 00:22:34 +00:00
if ( s = = " " ) break ;
2024-03-24 10:15:13 +00:00
else if ( s . substr ( 0 , 5 ) = = " CMSG " ) custom_welcome = s . substr ( 5 ) ;
else {
auto pos = s . find ( " = " ) ;
if ( pos ! = string : : npos ) {
string name = s . substr ( 0 , pos ) ;
string value = s . substr ( pos + 1 ) ;
if ( ! params . count ( name ) | | ! allowed_params . count ( name ) ) {
println ( hlog , " # parameter unknown: " , name ) ;
continue ;
}
params [ name ] - > load_as_animation ( value ) ;
}
}
2024-03-22 00:22:34 +00:00
}
2024-03-14 18:28:33 +00:00
stop_game ( ) ;
load_mode_data_with_zero ( ss ) ;
start_game ( ) ;
}
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 ( ) ;
2024-05-16 16:13:48 +00:00
crush_next . clear ( ) ;
2018-01-02 10:15:42 +00:00
crush_now . clear ( ) ;
2019-01-28 20:47:47 +00:00
rosemap . clear ( ) ;
2023-09-14 15:37:37 +00:00
hv_land . clear ( ) ;
2023-10-29 09:14:18 +00:00
bow : : bowpath . clear ( ) ;
bow : : clear_bowpath ( ) ;
bow : : fire_mode = false ;
2022-05-21 15:00:16 +00:00
for ( auto & am : adj_memo ) am . clear ( ) ;
2024-05-16 16:13:48 +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 ) ;
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 + + )
2021-05-22 21:01:32 +00:00
set_if_removed ( shpos [ i ] [ p ] , NULL ) ;
2021-08-22 09:00:04 +00:00
vector < cell * > to_remove ;
for ( auto p : rosemap ) if ( is_cell_removed ( p . first ) ) to_remove . push_back ( p . first ) ;
for ( auto r : to_remove ) rosemap . erase ( r ) ;
2020-02-23 01:51:27 +00:00
} ) ;
2018-06-10 23:58:31 +00:00
}