2022-05-06 11:21:01 +00:00
# if NILRIDER
2024-08-24 14:11:06 +00:00
# define CUSTOM_CAPTION "Nil Rider 2.0"
2022-05-06 11:21:01 +00:00
# define MAXMDIM 4
# define CAP_INV 0
# define CAP_COMPLEX2 0
# define CAP_EDIT 0
2022-05-09 12:54:08 +00:00
# define CAP_TEXTURE 1
2022-05-06 11:21:01 +00:00
# define CAP_BT 0
# define CAP_SOLV 0
# define CAP_THREAD 0
# define CAP_RUG 0
# define CAP_SVG 0
# define CAP_TOUR 0
# define CAP_IRR 0
# define CAP_CRYSTAL 0
# define CAP_ARCM 0
# define CAP_HISTORY 0
# define CAP_STARTANIM 0
# define CAP_SAVE 0
# define CAP_TRANS 0
# ifdef BWEB
# include "../../hyperweb.cpp"
# else
# include "../../hyper.cpp"
# endif
# include "../simple-impossible.cpp"
# include "../rogueviz.cpp"
# endif
2022-04-24 17:48:45 +00:00
# include "nilrider.h"
# include "statues.cpp"
# include "timestamp.cpp"
# include "levels.cpp"
# include "level.cpp"
# include "planning.cpp"
2022-04-30 10:04:50 +00:00
# include "solver.cpp"
2022-05-01 14:45:18 +00:00
# include "save.cpp"
2022-04-24 17:48:45 +00:00
2024-08-24 13:43:22 +00:00
# ifdef RVCOL
namespace hr {
void rv_achievement ( const string & name ) ;
void rv_leaderboard ( const string & name , int score ) ;
}
# endif
2022-04-24 17:48:45 +00:00
namespace nilrider {
2023-08-06 09:51:43 +00:00
multi : : config scfg_nilrider ;
2022-04-24 17:48:45 +00:00
/** is the game paused? */
bool paused = false ;
bool planning_mode = false ;
2022-05-01 14:42:38 +00:00
bool view_replay = false ;
2022-04-24 17:48:45 +00:00
int simulation_start_tick ;
2022-05-01 10:12:04 +00:00
ld aimspeed_key_x = 1 , aimspeed_key_y = 1 , aimspeed_mouse_x = 1 , aimspeed_mouse_y = 1 ;
2022-05-01 10:51:17 +00:00
vector < string > move_names = { " camera down " , " move left " , " camera up " , " move right " , " fine control " , " pause " , " reverse time " , " view simulation " , " menu " } ;
int reversals = 0 ;
2022-05-06 10:56:08 +00:00
bool loaded_or_planned = false ;
2022-05-01 10:51:17 +00:00
2022-04-24 17:48:45 +00:00
void frame ( ) {
2022-05-01 14:42:38 +00:00
if ( planning_mode & & ! view_replay ) return ;
2022-04-24 17:48:45 +00:00
shiftmatrix V = ggmatrix ( cwt . at ) ;
2024-08-18 11:56:46 +00:00
curlev - > draw_level_rec ( V ) ;
2022-04-24 17:48:45 +00:00
2024-08-20 09:47:26 +00:00
curlev - > current . draw_unilcycle ( V , my_scheme ) ;
2024-08-20 09:35:31 +00:00
for ( auto g : curlev - > ghosts ) {
ld t = curlev - > current . timer ;
2024-08-20 11:58:11 +00:00
ld maxt = g . history . back ( ) . timer ;
auto gr = ghost_repeat ;
if ( gr < = 0 ) gr = 1e6 ;
t - = floor ( t / gr ) * gr ;
for ( ; t < maxt ; t + = gr ) {
int a = 0 , b = isize ( g . history ) ;
while ( a ! = b ) {
int s = ( a + b ) / 2 ;
if ( g . history [ s ] . timer < t ) a = s + 1 ;
else b = s ;
}
2024-08-20 12:44:13 +00:00
if ( b < isize ( g . history ) & & g . history [ b ] . where ! = curlev - > current . where ) g . history [ b ] . draw_unilcycle ( V , g . cs ) ;
2024-08-20 09:35:31 +00:00
}
}
2022-04-24 17:48:45 +00:00
}
2022-05-06 12:16:39 +00:00
bool crash_sound = true ;
2022-05-28 16:49:29 +00:00
bool running ;
bool backing ;
2022-08-14 17:24:13 +00:00
/* land for music */
eLand music_nilrider = eLand ( 400 ) ;
eLand music_nilrider_back = eLand ( 401 ) ;
eLand music_nilrider_paused = eLand ( 402 ) ;
eLand music_nilrider_planning = eLand ( 403 ) ;
eLand music_nilrider_nonrunning = eLand ( 404 ) ;
2022-05-28 16:49:29 +00:00
void sync_music ( eLand l ) {
2022-08-14 17:24:13 +00:00
musicpos [ music_nilrider ] = zgmod ( curlev - > current . timer * 1000 , musiclength [ music_nilrider ] ) ;
musicpos [ music_nilrider_back ] = zgmod ( - curlev - > current . timer * 1000 , musiclength [ music_nilrider_back ] ) ;
2022-05-28 16:49:29 +00:00
}
2022-05-06 12:16:39 +00:00
2022-04-24 17:48:45 +00:00
bool turn ( int delta ) {
2022-05-01 14:42:38 +00:00
if ( planning_mode & & ! view_replay ) return false ;
2022-05-01 10:12:04 +00:00
2023-08-06 09:51:43 +00:00
multi : : get_actions ( scfg_nilrider ) ;
2022-05-01 10:51:17 +00:00
auto & a = multi : : actionspressed ;
auto & la = multi : : lactionpressed ;
2022-04-24 17:48:45 +00:00
2022-05-01 10:51:17 +00:00
ld mul = 1 ;
if ( a [ 16 + 4 ] ) mul / = 5 ;
if ( a [ 16 + 3 ] & & ! paused ) curlev - > current . heading_angle - = aimspeed_key_x * mul * delta / 1000. ;
if ( a [ 16 + 1 ] & & ! paused ) curlev - > current . heading_angle + = aimspeed_key_x * mul * delta / 1000. ;
if ( a [ 16 + 2 ] & & ! paused ) min_gfx_slope - = aimspeed_key_y * mul * delta / 1000. ;
if ( a [ 16 + 0 ] & & ! paused ) min_gfx_slope + = aimspeed_key_y * mul * delta / 1000. ;
2022-05-01 10:12:04 +00:00
curlev - > current . heading_angle - = aimspeed_mouse_x * mouseaim_x * mul ;
min_gfx_slope + = aimspeed_mouse_y * mouseaim_y * mul ;
2022-04-24 17:48:45 +00:00
# if CAP_VR
if ( vrhr : : active ( ) ) {
2022-05-01 10:12:04 +00:00
curlev - > current . heading_angle - = aimspeed_mouse_x * vrhr : : vraim_x * mul * delta / 400 ;
min_gfx_slope - = aimspeed_mouse_y * vrhr : : vraim_y * mul * delta / 400 ;
2022-04-24 17:48:45 +00:00
}
# endif
2022-11-12 21:38:45 +00:00
if ( min_gfx_slope < - 90. _deg ) min_gfx_slope = - 90. _deg ;
if ( min_gfx_slope > + 90. _deg ) min_gfx_slope = + 90. _deg ;
2022-05-28 16:49:29 +00:00
backing = false ;
2022-05-01 10:51:17 +00:00
if ( a [ 16 + 6 ] ) {
if ( ! la [ 16 + 6 ] ) reversals + + ;
if ( planning_mode )
simulation_start_tick + = 2 * delta ;
else for ( int i = 0 ; i < delta ; i + + ) {
if ( isize ( curlev - > history ) > 1 ) {
backing = true ;
curlev - > history . pop_back ( ) ;
curlev - > current = curlev - > history . back ( ) ;
2022-05-06 12:16:39 +00:00
crash_sound = true ;
2022-05-01 10:51:17 +00:00
}
else {
reversals = 0 ;
2022-05-06 10:56:08 +00:00
loaded_or_planned = false ;
2022-05-06 12:16:39 +00:00
crash_sound = true ;
2022-05-01 10:51:17 +00:00
}
}
}
2022-05-06 12:16:39 +00:00
if ( ! paused & & ! view_replay & & ! backing ) {
auto t = curlev - > current . collected_triangles ;
bool fail = false ;
2024-08-18 11:04:33 +00:00
for ( int i = 0 ; i < delta * simulation_speed ; i + + ) {
2022-05-06 12:16:39 +00:00
curlev - > history . push_back ( curlev - > current ) ;
curlev - > current . be_consistent ( ) ;
2024-08-24 13:43:22 +00:00
auto goals = curlev - > current . goals ;
2022-05-06 12:16:39 +00:00
bool b = curlev - > current . tick ( curlev ) ;
2022-05-28 16:49:29 +00:00
running = b ;
2022-05-06 12:31:50 +00:00
if ( ! b ) {
curlev - > history . pop_back ( ) ;
fail = true ;
2022-05-06 19:07:57 +00:00
break ;
2022-05-06 12:31:50 +00:00
}
2024-08-24 13:43:22 +00:00
# if RVCOL
if ( b ) {
goals = curlev - > current . goals & ~ goals ;
int gid = 0 ;
for ( auto & g : curlev - > goals ) {
if ( goals & Flag ( gid ) ) {
if ( g . achievement_name ! = " " ) rv_achievement ( g . achievement_name ) ;
if ( g . leaderboard_name ! = " " ) {
auto res = curlev - > current_score [ gid ] ;
rv_leaderboard ( g . leaderboard_name , abs ( res ) * 1000 ) ;
}
}
gid + + ;
}
}
# endif
2022-05-06 12:16:39 +00:00
}
if ( t ! = curlev - > current . collected_triangles )
playSound ( cwt . at , " pickup-gold " ) ;
if ( fail & & crash_sound ) {
char ch = curlev - > mapchar ( curlev - > current . where ) ;
if ( ch = = ' r ' ) {
playSound ( cwt . at , " closegate " ) ;
crash_sound = false ;
}
if ( ch = = ' ! ' ) {
playSound ( cwt . at , " seen-air " ) ;
crash_sound = false ;
}
}
2022-04-24 17:48:45 +00:00
}
if ( ! paused ) curlev - > current . centerview ( curlev ) ;
return false ;
}
void main_menu ( ) ;
2024-08-19 10:25:02 +00:00
void layer_selection_screen ( ) ;
2022-04-24 17:48:45 +00:00
2022-05-01 10:51:17 +00:00
# define PSEUDOKEY_PAUSE 2511
# define PSEUDOKEY_SIM 2512
2022-05-01 14:42:38 +00:00
void toggle_replay ( ) {
view_replay = ! view_replay ;
paused = false ;
simulation_start_tick = ticks ;
if ( ! view_replay & & ! planning_mode ) {
paused = true ;
curlev - > current = curlev - > history . back ( ) ;
}
}
2022-04-24 17:48:45 +00:00
void run ( ) {
2022-05-06 18:28:08 +00:00
cmode = sm : : PANNING | sm : : NORMAL ;
2022-04-24 17:48:45 +00:00
clearMessages ( ) ;
dialog : : init ( ) ;
2022-05-01 14:42:38 +00:00
if ( view_replay & & ! paused ) {
2022-04-24 17:48:45 +00:00
int ttick = gmod ( ticks - simulation_start_tick , isize ( curlev - > history ) ) ;
curlev - > current = curlev - > history [ ttick ] ;
curlev - > current . centerview ( curlev ) ;
}
2022-05-01 14:42:38 +00:00
if ( planning_mode & & ! view_replay )
2022-04-30 10:02:30 +00:00
cmode | = sm : : SHOWCURSOR ;
2022-05-01 10:12:04 +00:00
if ( aimspeed_mouse_x = = 0 & & aimspeed_mouse_y = = 0 )
cmode | = sm : : SHOWCURSOR ;
2022-07-05 14:03:12 +00:00
gamescreen ( ) ;
2022-05-01 14:42:38 +00:00
if ( planning_mode & & ! view_replay ) {
2022-04-24 17:48:45 +00:00
curlev - > draw_planning_screen ( ) ;
if ( ! holdmouse ) {
auto t0 = SDL_GetTicks ( ) ;
while ( SDL_GetTicks ( ) < t0 + 100 ) {
if ( ! curlev - > simulate ( ) ) break ;
}
}
}
2022-05-06 12:31:50 +00:00
curlev - > current . draw_instruments ( curlev ) ;
2022-04-24 17:48:45 +00:00
if ( paused & & ! planning_mode ) {
displayButton ( current_display - > xcenter , current_display - > ycenter , mousing ? XLAT ( " paused -- click to unpause " ) : XLAT ( " paused -- press p to continue " ) , ' p ' , 8 ) ;
}
int x = vid . fsize ;
2022-05-01 10:51:17 +00:00
auto show_button = [ & ] ( int c , string s , color_t col = dialog : : dialogcolor ) {
2022-04-24 17:48:45 +00:00
if ( displayButtonS ( x , vid . yres - vid . fsize , s , col , 0 , vid . fsize ) )
getcstat = c ;
x + = textwidth ( vid . fsize , s ) + vid . fsize ;
} ;
2022-05-01 14:42:38 +00:00
if ( planning_mode & & ! view_replay ) {
2022-04-24 17:48:45 +00:00
for ( auto & b : buttons ) show_button ( b . first , b . second , planmode = = b . first ? 0xFFD500 : dialog : : dialogcolor ) ;
2022-05-01 10:51:17 +00:00
show_button ( PSEUDOKEY_SIM , " simulation " ) ;
2024-08-19 10:25:02 +00:00
if ( curlev - > sublevels . size ( ) & & layer_edited ) {
show_button ( ' L ' , " layer: " + layer_edited - > name ) ;
}
2022-04-24 17:48:45 +00:00
}
2022-05-01 14:42:38 +00:00
bool pause_av = view_replay | | ! planning_mode ;
if ( pause_av ) {
show_button ( PSEUDOKEY_SIM , planning_mode ? " return " : " replay " , ( view_replay & & ! planning_mode ) ? 0xFF0000 : dialog : : dialogcolor ) ;
2022-05-01 10:51:17 +00:00
show_button ( PSEUDOKEY_PAUSE , " pause " , paused ? 0xFF0000 : dialog : : dialogcolor ) ;
2022-04-24 17:48:45 +00:00
}
2022-05-01 10:51:17 +00:00
show_button ( PSEUDOKEY_MENU , " menu " ) ;
2022-04-24 17:48:45 +00:00
2022-05-01 10:51:17 +00:00
dialog : : add_key_action ( PSEUDOKEY_MENU , [ ] {
2022-05-06 16:51:04 +00:00
if ( curlev - > current . timer ) paused = true ;
2022-09-24 07:13:00 +00:00
game_keys_scroll = true ;
2022-04-24 17:48:45 +00:00
pushScreen ( main_menu ) ;
} ) ;
2022-05-01 10:51:17 +00:00
if ( pause_av ) dialog : : add_key_action ( PSEUDOKEY_PAUSE , [ ] {
2022-04-24 17:48:45 +00:00
paused = ! paused ;
2022-09-24 07:13:00 +00:00
game_keys_scroll = true ;
2022-05-01 14:42:38 +00:00
if ( view_replay & & ! paused )
2022-05-06 12:31:50 +00:00
simulation_start_tick = ticks - curlev - > current . timer * tps ;
2022-04-24 17:48:45 +00:00
} ) ;
dialog : : add_key_action ( ' - ' , [ ] {
paused = false ;
} ) ;
2022-05-01 14:42:38 +00:00
dialog : : add_key_action ( PSEUDOKEY_SIM , toggle_replay ) ;
2022-04-24 17:48:45 +00:00
dialog : : display ( ) ;
2022-05-01 10:51:17 +00:00
2024-08-19 10:25:02 +00:00
if ( planning_mode & & ! view_replay & & curlev - > sublevels . size ( ) ) {
dialog : : add_key_action ( ' L ' , [ ] { pushScreen ( layer_selection_screen ) ; } ) ;
}
2024-05-26 18:22:29 +00:00
int * t = scfg_nilrider . keyaction ;
2022-05-01 10:51:17 +00:00
for ( int i = 1 ; i < 512 ; i + + ) {
auto & ka = dialog : : key_actions ;
if ( t [ i ] = = 16 + 5 ) ka [ i ] = ka [ PSEUDOKEY_PAUSE ] ;
if ( t [ i ] = = 16 + 7 ) ka [ i ] = ka [ PSEUDOKEY_SIM ] ;
if ( t [ i ] = = 16 + 8 ) ka [ i ] = ka [ PSEUDOKEY_MENU ] ;
}
2022-04-24 17:48:45 +00:00
keyhandler = [ ] ( int sym , int uni ) {
if ( paused ) handlePanning ( sym , uni ) ;
2022-05-01 14:42:38 +00:00
if ( planning_mode & & ! view_replay & & curlev - > handle_planning ( sym , uni ) ) return ;
2022-04-24 17:48:45 +00:00
dialog : : handleNavigation ( sym , uni ) ;
} ;
}
2022-05-06 10:56:08 +00:00
void clear_path ( level * l ) {
l - > history . clear ( ) ;
l - > current = l - > start ;
l - > history . push_back ( l - > start ) ;
paused = false ;
reversals = 0 ;
loaded_or_planned = false ;
2022-05-06 12:16:39 +00:00
crash_sound = true ;
2022-05-06 10:56:08 +00:00
}
2024-08-20 08:33:49 +00:00
string fname = " horizontal.nrl " ;
2022-04-24 17:48:45 +00:00
void pick_level ( ) {
dialog : : init ( XLAT ( " select the track " ) , 0xC0C0FFFF , 150 , 100 ) ;
for ( auto l : all_levels ) {
dialog : : addItem ( l - > name , l - > hotkey ) ;
dialog : : add_action ( [ l ] {
curlev = l ;
recompute_plan_transform = true ;
l - > init ( ) ;
2022-05-06 10:56:08 +00:00
clear_path ( l ) ;
2022-04-24 17:48:45 +00:00
popScreen ( ) ;
} ) ;
}
dialog : : addBreak ( 100 ) ;
2024-08-20 08:33:49 +00:00
dialog : : addItem ( " load a level from a file " , ' 0 ' ) ;
dialog : : add_action ( [ ] {
dialog : : openFileDialog ( fname , XLAT ( " level to load: " ) , " .nrl " , [ ] ( ) {
try {
2024-08-20 17:13:28 +00:00
load_level ( fname , true ) ;
2024-08-20 08:33:49 +00:00
return true ;
}
catch ( hr_exception & e ) {
addMessage ( e . what ( ) ) ;
return false ;
}
} ) ;
} ) ;
2022-04-24 17:48:45 +00:00
dialog : : addBack ( ) ;
dialog : : display ( ) ;
}
2024-08-19 10:25:02 +00:00
void layer_selection_screen ( ) {
poly_outline = 0xFF ;
dialog : : init ( XLAT ( " layer selection " ) , 0xC0C0FFFF , 150 , 100 ) ;
dialog : : addBreak ( 50 ) ;
auto layers = curlev - > gen_layer_list ( ) ;
char key = ' a ' ;
for ( auto l : layers ) {
dialog : : addBoolItem ( l - > name , l = = layer_edited , key + + ) ;
dialog : : add_action ( [ l ] { layer_edited = l ; popScreen ( ) ; } ) ;
}
dialog : : addBack ( ) ;
dialog : : display ( ) ;
}
2022-04-24 17:48:45 +00:00
void pick_game ( ) {
clearMessages ( ) ;
2022-05-06 16:50:47 +00:00
dialog : : init ( ) ;
poly_outline = 0xFF ;
dialog : : addBigItem ( curlev - > name , ' t ' ) ;
2022-05-06 14:49:37 +00:00
dialog : : addBreak ( 50 ) ;
dialog : : addHelp ( curlev - > longdesc ) ;
int gid = 0 ;
for ( auto & g : curlev - > goals ) {
dialog : : addBreak ( 50 ) ;
auto man = curlev - > records [ 0 ] [ gid ] ;
auto plan = curlev - > records [ 1 ] [ gid ] ;
if ( man & & plan )
dialog : : addInfo ( " manual: " + format_timer ( man ) + " planning: " + format_timer ( plan ) , g . color ) ;
else if ( man )
dialog : : addInfo ( " manual: " + format_timer ( man ) , g . color ) ;
else if ( plan )
dialog : : addInfo ( " planning: " + format_timer ( plan ) , g . color ) ;
else
dialog : : addInfo ( " goal not obtained: " , g . color ) ;
dialog : : addBreak ( 50 ) ;
dialog : : addHelp ( g . desc ) ;
gid + + ;
}
2022-04-24 17:48:45 +00:00
dialog : : addBreak ( 100 ) ;
2022-05-06 16:50:47 +00:00
dialog : : addItem ( " change the track " , ' t ' ) ;
dialog : : add_action_push ( pick_level ) ;
dialog : : addBreak ( 100 ) ;
add_edit ( planning_mode ) ;
2023-08-22 18:11:46 +00:00
dialog : : addItem ( XLAT ( " play this track " ) , SDLK_ESCAPE ) ;
dialog : : addItem ( XLAT ( " quit Nil Rider " ) , ' q ' ) ;
dialog : : add_action ( [ ] { quitmainloop = true ; } ) ;
2022-04-24 17:48:45 +00:00
dialog : : display ( ) ;
}
2022-05-06 00:53:36 +00:00
void nil_set_geodesic ( ) {
pmodel = mdGeodesic ;
nisot : : geodesic_movement = true ;
popScreen ( ) ;
}
void nil_set_perspective ( ) {
pmodel = mdPerspective ;
nisot : : geodesic_movement = false ;
pconf . rotational_nil = 0 ;
}
void nil_projection ( ) {
dialog : : init ( XLAT ( " projection of Nil " ) , 0xC0C0FFFF , 150 , 100 ) ;
dialog : : addBoolItem ( " geodesics " , pmodel = = mdGeodesic , ' g ' ) ;
dialog : : add_action ( [ ] { popScreen ( ) ; nil_set_geodesic ( ) ; } ) ;
dialog : : addInfo ( " In this mode, the light is assumed to travel along the geodesics (the shortest paths in Nil). " ) ;
dialog : : addBreak ( 100 ) ;
dialog : : addBoolItem ( " constant direction " , pmodel = = mdPerspective , ' c ' ) ;
dialog : : add_action ( [ ] { popScreen ( ) ; nil_set_perspective ( ) ; } ) ;
dialog : : addInfo ( " In this mode, the light is assumed to travel along the lines of constant direction. " ) ;
dialog : : addBreak ( 100 ) ;
dialog : : addBack ( ) ;
dialog : : display ( ) ;
}
2022-04-24 17:48:45 +00:00
void settings ( ) {
dialog : : init ( XLAT ( " settings " ) , 0xC0C0FFFF , 150 , 100 ) ;
2022-05-01 10:12:04 +00:00
add_edit ( aimspeed_key_x ) ;
add_edit ( aimspeed_key_y ) ;
add_edit ( aimspeed_mouse_x ) ;
add_edit ( aimspeed_mouse_y ) ;
2022-05-01 14:53:50 +00:00
add_edit ( whrad ) ;
add_edit ( whdist ) ;
add_edit ( min_gfx_slope ) ;
2022-05-05 20:37:05 +00:00
add_edit ( stepped_display ) ;
2024-08-20 11:42:03 +00:00
add_edit ( simulation_speed ) ;
2024-08-20 11:58:11 +00:00
add_edit ( ghost_repeat ) ;
2024-08-20 11:47:22 +00:00
dialog : : addBreak ( 100 ) ;
2024-08-20 11:42:25 +00:00
add_edit ( my_scheme . wheel1 ) ;
add_edit ( my_scheme . wheel2 ) ;
add_edit ( my_scheme . seat ) ;
add_edit ( my_scheme . seatpost ) ;
2024-08-20 11:47:22 +00:00
dialog : : addBreak ( 100 ) ;
2022-05-06 00:53:36 +00:00
dialog : : addItem ( " projection " , ' P ' ) ;
dialog : : add_action_push ( nil_projection ) ;
2022-05-01 10:51:17 +00:00
dialog : : addItem ( " configure keys " , ' k ' ) ;
2023-08-06 09:51:43 +00:00
dialog : : add_action_push ( multi : : get_key_configurer ( 1 , move_names , " Nilrider keys " , scfg_nilrider ) ) ;
2022-08-14 17:55:25 +00:00
# if CAP_AUDIO
add_edit ( effvolume ) ;
add_edit ( musicvolume ) ;
# endif
2022-05-09 07:40:35 +00:00
# if CAP_VR
2022-05-06 18:28:08 +00:00
vrhr : : enable_button ( ) ;
vrhr : : reference_button ( ) ;
2022-05-09 07:40:35 +00:00
# endif
2022-05-06 18:28:08 +00:00
2022-04-24 17:48:45 +00:00
dialog : : addItem ( " RogueViz settings " , ' r ' ) ;
dialog : : add_key_action ( ' r ' , [ ] {
pushScreen ( showSettings ) ;
} ) ;
2022-08-20 14:44:41 +00:00
# if CAP_FILES && !ISWEB
dialog : : addItem ( " save the current config " , ' s ' ) ;
dialog : : add_action ( [ ] {
dynamicval < eGeometry > g ( geometry , gNormal ) ;
saveConfig ( ) ;
} ) ;
# endif
2022-04-24 17:48:45 +00:00
dialog : : addBreak ( 100 ) ;
dialog : : addBack ( ) ;
2022-05-06 00:53:36 +00:00
dialog : : display ( ) ;
2022-04-24 17:48:45 +00:00
}
2024-08-20 17:14:13 +00:00
template < class T , class U , class V > void replays_of_type ( vector < T > & v , const U & loader , const V & ghost_loader ) {
2022-05-02 01:38:20 +00:00
int i = 0 ;
for ( auto & r : v ) {
dialog : : addItem ( r . name , ' a ' ) ;
2024-08-20 17:14:13 +00:00
dialog : : add_action ( [ & v , i , loader , ghost_loader ] {
pushScreen ( [ & v , i , loader , ghost_loader ] {
2024-08-19 10:56:32 +00:00
dialog : : init ( XLAT ( planning_mode ? " saved plan " : " replay " ) , 0xC0C0FFFF , 150 , 100 ) ;
dialog : : addInfo ( v [ i ] . name ) ;
dialog : : addItem ( planning_mode ? " load plan " : " load replay " , ' l ' ) ;
dialog : : add_action ( [ & v , i , loader ] { popScreen ( ) ; loader ( v [ i ] ) ; } ) ;
dialog : : addItem ( planning_mode ? " load plan as ghost " : " load replay as ghost " , ' g ' ) ;
2024-08-20 17:14:13 +00:00
dialog : : add_action ( [ & v , i , ghost_loader ] { popScreen ( ) ; ghost_loader ( v [ i ] ) ; } ) ;
2024-08-19 10:56:32 +00:00
dialog : : addItem ( " rename " , ' r ' ) ;
dialog : : add_action ( [ & v , i ] {
popScreen ( ) ;
dialog : : edit_string ( v [ i ] . name , planning_mode ? " name plan " : " name replay " , " " ) ;
dialog : : get_di ( ) . reaction_final = [ ] { save ( ) ; } ;
} ) ;
dialog : : addItem ( " delete " , ' d ' ) ;
dialog : : add_action ( [ & v , i ] {
popScreen ( ) ;
dialog : : push_confirm_dialog (
[ & v , i ] { v . erase ( v . begin ( ) + i ) ; save ( ) ; } ,
" Are you sure you want to delete ' " + v [ i ] . name + " '? "
) ;
} ) ;
dialog : : display ( ) ;
} ) ;
2022-05-02 01:38:20 +00:00
} ) ;
i + + ;
}
}
2022-05-09 13:09:02 +00:00
# if CAP_SAVE
2024-08-19 10:56:32 +00:00
2022-05-02 01:38:20 +00:00
void replays ( ) {
dialog : : init ( XLAT ( planning_mode ? " saved plans " : " replays " ) , 0xC0C0FFFF , 150 , 100 ) ;
if ( ! planning_mode ) replays_of_type ( curlev - > manual_replays , [ ] ( manual_replay & r ) {
view_replay = false ;
2022-05-06 10:56:08 +00:00
loaded_or_planned = true ;
2024-08-20 12:45:09 +00:00
curlev - > history = curlev - > headings_to_history ( r ) ;
2022-05-02 01:38:20 +00:00
toggle_replay ( ) ;
popScreen ( ) ;
2024-08-20 17:14:13 +00:00
} , [ ] ( manual_replay & r ) { curlev - > load_manual_as_ghost ( r ) ; } ) ;
2022-05-02 01:38:20 +00:00
if ( planning_mode ) replays_of_type ( curlev - > plan_replays , [ ] ( plan_replay & r ) {
view_replay = false ;
curlev - > history . clear ( ) ;
curlev - > plan = r . plan ;
popScreen ( ) ;
2024-08-20 17:14:13 +00:00
} , [ ] ( plan_replay & r ) { curlev - > load_plan_as_ghost ( r ) ; } ) ;
dialog : : addBreak ( 100 ) ;
if ( isize ( curlev - > ghosts ) ) {
dialog : : addSelItem ( " forget all ghosts " , its ( isize ( curlev - > ghosts ) ) , ' G ' ) ;
dialog : : add_action ( [ ] { curlev - > ghosts . clear ( ) ; } ) ;
}
else if ( isize ( curlev - > manual_replays ) | | isize ( curlev - > plan_replays ) ) {
dialog : : addSelItem ( " load all plans and replays as ghosts " , its ( isize ( curlev - > manual_replays ) + isize ( curlev - > plan_replays ) ) , ' G ' ) ;
dialog : : add_action ( [ ] { curlev - > load_all_ghosts ( ) ; } ) ;
}
else dialog : : addBreak ( 100 ) ;
2024-08-20 17:18:39 +00:00
if ( planning_mode ) {
dialog : : addItem ( " save the current plan " , ' s ' ) ;
dialog : : add_action ( [ ] {
curlev - > plan_replays . emplace_back ( plan_replay { new_replay_name ( ) , my_scheme , curlev - > plan } ) ;
save ( ) ;
} ) ;
}
else {
dialog : : addItem ( " save the current replay " , ' s ' ) ;
dialog : : add_action ( save_manual_replay ) ;
}
2022-05-02 01:38:20 +00:00
dialog : : addBack ( ) ;
dialog : : display ( ) ;
}
void pop_and_push_replays ( ) {
popScreen ( ) ;
pushScreen ( replays ) ;
}
2022-05-09 13:09:02 +00:00
# endif
2022-05-02 01:38:20 +00:00
2023-08-07 15:16:44 +00:00
reaction_t on_quit = [ ] { quitmainloop = true ; } ;
2022-04-24 17:48:45 +00:00
2022-08-14 17:24:39 +00:00
void restart ( ) {
clear_path ( curlev ) ;
}
2022-04-24 17:48:45 +00:00
void main_menu ( ) {
clearMessages ( ) ;
2022-05-06 16:51:18 +00:00
poly_outline = 0xFF ;
2022-04-24 17:48:45 +00:00
dialog : : init ( XLAT ( " Nil Rider " ) , 0xC0C0FFFF , 150 , 100 ) ;
dialog : : addItem ( " continue " , ' c ' ) ;
dialog : : add_action ( popScreen ) ;
if ( ! planning_mode ) {
dialog : : addItem ( " restart " , ' r ' ) ;
dialog : : add_action ( [ ] {
2022-05-06 10:56:08 +00:00
clear_path ( curlev ) ;
2022-04-24 17:48:45 +00:00
popScreen ( ) ;
} ) ;
dialog : : addItem ( " view the replay " , ' v ' ) ;
2022-05-01 14:42:38 +00:00
dialog : : add_action ( toggle_replay ) ;
2022-04-24 17:48:45 +00:00
2022-05-09 13:09:02 +00:00
# if CAP_SAVE
2024-08-20 17:18:39 +00:00
dialog : : addItem ( " saved replays " , ' s ' ) ;
2022-05-02 01:38:20 +00:00
dialog : : add_action ( pop_and_push_replays ) ;
2022-05-09 13:09:02 +00:00
# endif
2022-04-24 17:48:45 +00:00
}
else {
2022-05-09 13:09:02 +00:00
# if CAP_SAVE
2024-08-20 17:18:39 +00:00
dialog : : addItem ( " saved plans " , ' s ' ) ;
2022-05-02 01:38:20 +00:00
dialog : : add_action ( pop_and_push_replays ) ;
2022-05-09 13:09:02 +00:00
# endif
2022-04-24 17:48:45 +00:00
}
2022-05-06 14:49:37 +00:00
dialog : : addItem ( " track / mode / goals " , ' t ' ) ;
2022-04-24 17:48:45 +00:00
dialog : : add_action_push ( pick_game ) ;
2022-05-06 14:49:37 +00:00
dialog : : addItem ( " change settings " , ' o ' ) ;
2022-04-24 17:48:45 +00:00
dialog : : add_action_push ( settings ) ;
2022-05-09 12:45:57 +00:00
# if CAP_VIDEO
dialog : : addItem ( " record video " , ' v ' ) ;
dialog : : add_action ( [ ] {
dialog : : openFileDialog ( anims : : videofile , XLAT ( " record to video file " ) ,
" .mp4 " , [ ] {
anims : : period = isize ( curlev - > history ) ;
anims : : noframes = anims : : period * 60 / 1000 ;
int a = addHook ( anims : : hooks_anim , 100 , [ ] {
int ttick = ticks % isize ( curlev - > history ) ;
curlev - > current = curlev - > history [ ttick ] ;
curlev - > current . centerview ( curlev ) ;
anims : : moved ( ) ;
} ) ;
int af = addHook ( hooks_frame , 100 , [ ] {
int ttick = ticks % isize ( curlev - > history ) ;
curlev - > current = curlev - > history [ ttick ] ;
if ( planning_mode & & ! view_replay ) curlev - > draw_planning_screen ( ) ;
} ) ;
bool b = anims : : record_video_std ( ) ;
delHook ( anims : : hooks_anim , a ) ;
delHook ( hooks_frame , af ) ;
return b ;
} ) ;
} ) ;
# endif
2022-04-24 17:48:45 +00:00
dialog : : addItem ( " quit " , ' q ' ) ;
dialog : : add_action ( [ ] {
on_quit ( ) ;
} ) ;
dialog : : display ( ) ;
}
bool on ;
2022-05-01 10:51:17 +00:00
void change_default_key ( int key , int val ) {
2024-05-26 18:22:29 +00:00
int * t = scfg_nilrider . keyaction ;
2022-05-01 10:51:17 +00:00
t [ key ] = val ;
}
void nilrider_keys ( ) {
2023-08-06 09:51:43 +00:00
clear_config ( scfg_nilrider ) ;
2022-05-01 10:51:17 +00:00
change_default_key ( ' s ' , 16 + 0 ) ;
change_default_key ( ' a ' , 16 + 1 ) ;
change_default_key ( ' w ' , 16 + 2 ) ;
change_default_key ( ' d ' , 16 + 3 ) ;
2022-08-14 17:24:24 +00:00
# if CAP_SDL2
change_default_key ( SDL_SCANCODE_LCTRL , 16 + 4 ) ;
# else
2022-05-01 10:51:17 +00:00
change_default_key ( SDLK_LCTRL , 16 + 4 ) ;
2022-08-14 17:24:24 +00:00
# endif
2022-05-01 10:51:17 +00:00
change_default_key ( ' p ' , 16 + 5 ) ;
change_default_key ( ' b ' , 16 + 6 ) ;
change_default_key ( ' r ' , 16 + 7 ) ;
change_default_key ( ' v ' , 16 + 8 ) ;
2023-08-06 09:51:43 +00:00
sconfig_savers ( scfg_nilrider , " nilrider " ) ;
2022-05-01 10:51:17 +00:00
}
2022-05-28 16:49:29 +00:00
bool nilrider_music ( eLand & l ) {
if ( planning_mode & & ! view_replay )
2022-08-14 17:24:13 +00:00
l = music_nilrider_planning ;
2022-05-28 16:49:29 +00:00
else if ( paused )
2022-08-14 17:24:13 +00:00
l = music_nilrider_paused ;
2022-05-28 16:49:29 +00:00
else if ( ! running )
2022-08-14 17:24:13 +00:00
l = music_nilrider_nonrunning ;
2022-05-28 16:49:29 +00:00
else if ( backing )
2022-08-14 17:24:13 +00:00
l = music_nilrider_back ;
else l = music_nilrider ;
2022-05-28 16:49:29 +00:00
return false ;
}
2023-08-06 20:17:20 +00:00
local_parameter_set lps_nilrider ( " nilrider: " ) ;
void default_settings ( ) {
lps_add ( lps_nilrider , vid . cells_drawn_limit , 1 ) ;
2024-05-26 19:15:26 +00:00
lps_add ( lps_nilrider , ccolor : : plain . ctab , colortable { 0 } ) ;
2023-08-06 20:17:20 +00:00
lps_add ( lps_nilrider , smooth_scrolling , true ) ;
2023-08-07 15:59:50 +00:00
lps_add ( lps_nilrider , mapeditor : : drawplayer , false ) ;
2023-08-06 20:17:20 +00:00
lps_add ( lps_nilrider , backcolor , 0xC0C0FFFF ) ;
lps_add ( lps_nilrider , logfog , 1 ) ;
2024-05-24 18:57:53 +00:00
lps_add ( lps_nilrider , ccolor : : which , & ccolor : : plain ) ;
2024-05-26 18:42:47 +00:00
lps_add ( lps_nilrider , ccolor : : rwalls , 0 ) ;
2023-08-06 20:17:20 +00:00
# if CAP_VR
2023-08-08 09:50:55 +00:00
lps_add ( lps_nilrider , vrhr : : hsm , vrhr : : eHeadset : : reference ) ;
lps_add ( lps_nilrider , vrhr : : eyes , vrhr : : eEyes : : equidistant ) ;
2023-08-06 20:17:20 +00:00
lps_add ( lps_nilrider , vrhr : : absolute_unit_in_meters , 6 ) ;
# endif
}
2022-04-24 17:48:45 +00:00
void initialize ( ) {
2022-05-02 01:38:20 +00:00
load ( ) ;
2022-05-01 10:51:17 +00:00
nilrider_keys ( ) ;
2022-04-24 17:48:45 +00:00
check_cgi ( ) ;
cgi . prepare_shapes ( ) ;
init_statues ( ) ;
curlev - > init ( ) ;
2024-05-26 18:22:29 +00:00
param_enum ( planning_mode , " nil_planning " , false )
2022-04-24 17:48:45 +00:00
- > editable ( { { " manual " , " control the unicycle manually " } , { " planning " , " try to plan the optimal route! " } } , " game mode " , ' p ' ) ;
2024-05-26 18:22:29 +00:00
param_enum ( stepped_display , " stepped_display " , false )
2022-05-05 20:37:05 +00:00
- > editable ( { { " smooth " , " ride on a smooth surface " } , { " blocky " , " makes slopes more visible -- actual physics are not affected " } } , " game mode " , ' s ' ) ;
2022-08-14 17:24:13 +00:00
param_i ( nilrider_tempo , " nilrider_tempo " ) ;
param_i ( nilrider_shift , " nilrider_shift " ) ;
2024-08-18 14:18:40 +00:00
param_f ( simulation_speed , " nilrider_simulation_speed " )
- > editable ( 0.1 , 5 , 0 , " Nil Rider simulation speed " ,
" If you want to go faster, make this higher. " , ' z ' )
- > set_sets ( [ ] { dialog : : bound_low ( 0 ) ; dialog : : scaleLog ( ) ; } ) ;
2024-08-20 11:58:11 +00:00
param_f ( ghost_repeat , " ghost_repeat " )
- > editable ( 0.01 , 999 , 0 , " ghost repeat period " ,
" will repeat ghosts every time interval (in seconds). " , ' z ' )
- > set_sets ( [ ] { dialog : : bound_low ( 0.01 ) ; dialog : : scaleLog ( ) ; } ) ;
2024-08-20 11:42:25 +00:00
param_color ( my_scheme . wheel1 , " color:wheel1 " , true , my_scheme . wheel1 ) - > editable ( " wheel color 1 " , " " , ' w ' ) ;
param_color ( my_scheme . wheel2 , " color:wheel2 " , true , my_scheme . wheel2 ) - > editable ( " wheel color 2 " , " " , ' x ' ) ;
param_color ( my_scheme . seat , " color:seat " , true , my_scheme . seat ) - > editable ( " seat color " , " " , ' p ' ) ;
param_color ( my_scheme . seatpost , " color:seatpost " , true , my_scheme . seatpost ) - > editable ( " seatpost color " , " " , ' o ' ) ;
2022-04-24 17:48:45 +00:00
rv_hook ( hooks_frame , 100 , frame ) ;
rv_hook ( shmup : : hooks_turn , 100 , turn ) ;
2022-05-06 11:16:20 +00:00
rv_hook ( hooks_resetGL , 100 , cleanup_textures ) ;
2022-05-28 16:49:29 +00:00
rv_hook ( hooks_music , 100 , nilrider_music ) ;
rv_hook ( hooks_sync_music , 100 , sync_music ) ;
2022-04-24 17:48:45 +00:00
on = true ;
on_cleanup_or_next ( [ ] { on = false ; } ) ;
pushScreen ( run ) ;
}
2022-05-06 00:53:55 +00:00
void initialize_all ( ) {
showstartmenu = false ;
stop_game ( ) ;
geometry = gNil ;
2022-05-06 10:14:18 +00:00
variation = eVariation : : pure ;
2022-05-06 00:53:55 +00:00
nil_set_geodesic ( ) ;
enable_canvas ( ) ;
2023-08-07 15:59:50 +00:00
lps_enable ( & lps_nilrider ) ;
2022-05-06 00:53:55 +00:00
initialize ( ) ;
2022-05-06 16:50:47 +00:00
poly_outline = 0xFF ;
pushScreen ( pick_game ) ;
2023-08-06 20:17:20 +00:00
start_game ( ) ;
2022-05-06 00:53:55 +00:00
}
2022-05-01 14:42:38 +00:00
auto celldemo = arg : : add3 ( " -unilcycle " , initialize ) + arg : : add3 ( " -unilplan " , [ ] { planning_mode = true ; } ) + arg : : add3 ( " -viewsim " , [ ] { view_replay = true ; } )
2022-04-24 17:48:45 +00:00
+ arg : : add3 ( " -oqc " , [ ] { on_quit = popScreenAll ; } )
2022-05-03 20:08:17 +00:00
+ arg : : add3 ( " -nilsolve-set " , [ ] {
arg : : shift ( ) ; solver_unit = arg : : argf ( ) ;
arg : : shift ( ) ; nospeed = arg : : argi ( ) ;
arg : : shift ( ) ; goal_id = arg : : argi ( ) ;
curlev - > solve ( ) ; } )
2022-04-30 10:04:50 +00:00
+ arg : : add3 ( " -nilsolve " , [ ] { curlev - > solve ( ) ; } )
2022-05-06 00:53:36 +00:00
+ arg : : add3 ( " -nilgeo " , nil_set_geodesic )
+ arg : : add3 ( " -nilper " , nil_set_perspective )
2022-05-06 00:53:55 +00:00
+ arg : : add3 ( " -nilrider " , initialize_all )
2022-05-06 19:08:15 +00:00
+ arg : : add3 ( " -nilrider-q " , [ ] { arg : : shift ( ) ; reduce_quality = arg : : argi ( ) ; } )
2022-05-01 10:12:04 +00:00
+ addHook ( hooks_configfile , 100 , [ ] {
param_f ( aimspeed_key_x , " nilrider_key_x " )
- > editable ( - 5 , 5 , 0.1 , " navigation sensitivity (keyboard) " , " press Left/Right to navigate (lCtrl to fine-tune) " , ' n ' ) ;
param_f ( aimspeed_key_y , " nilrider_key_y " )
- > editable ( - 5 , 5 , 0.1 , " camera sensitivity (keyboard) " , " press Up/Down to set the camera angle (lCtrl to fine-tune) " , ' c ' ) ;
param_f ( aimspeed_mouse_x , " nilrider_mouse_x " )
- > editable ( - 5 , 5 , 0.1 , " navigation sensitivity (mouse/vr) " , " move mouse Left/Right to navigate (lCtrl to fine-tune) " , ' N ' ) ;
param_f ( aimspeed_mouse_y , " nilrider_mouse_y " )
- > editable ( - 5 , 5 , 0.1 , " camera sensitivity (mouse/vr) " , " move mouse Up/Down to set the camera angle (lCtrl to fine-tune) " , ' C ' ) ;
2022-05-01 14:53:50 +00:00
param_f ( whrad , " nilrider_radius " )
- > editable ( 0 , 0.5 , 0.01 , " wheel radius " , " note: this parameter is just visual, it does not affect the physics in any way " , ' w ' ) ;
param_f ( whdist , " nilrider_dist " )
2022-05-05 23:36:55 +00:00
- > editable ( 0 , 5 , 0.05 , " camera distance " , " how far is the unicycle from the camera " , ' d ' )
- > set_reaction ( [ ] { curlev - > current . centerview ( curlev ) ; } ) ;
2022-05-01 14:53:50 +00:00
param_f ( min_gfx_slope , " min_gfx_slope " )
2022-11-12 21:38:45 +00:00
- > editable ( - 90. _deg , 90. _deg , degree , " min camera slope " , " affected by up/down " , ' m ' ) ;
2022-05-01 10:12:04 +00:00
} )
2022-04-24 17:48:45 +00:00
+ arg : : add3 ( " -fullsim " , [ ] {
/* for animations */
popScreenAll ( ) ;
rv_hook ( anims : : hooks_anim , 100 , [ ] {
int ttick = ticks % isize ( curlev - > history ) ;
curlev - > current = curlev - > history [ ttick ] ;
curlev - > current . centerview ( curlev ) ;
anims : : moved ( ) ;
} ) ;
} ) + arg : : add3 ( " -unillevel " , [ ] {
arg : : shift ( ) ;
for ( auto l : all_levels ) if ( appears ( l - > name , arg : : args ( ) ) ) curlev = l ;
if ( on ) curlev - > init ( ) ;
} )
2024-08-20 08:18:54 +00:00
+ arg : : add3 ( " -load-level " , [ ] {
2024-08-20 17:13:28 +00:00
arg : : shift ( ) ; load_level ( arg : : args ( ) , true ) ;
2024-08-20 08:18:54 +00:00
} )
2022-04-24 17:48:45 +00:00
+ arg : : add3 ( " -simplemodel " , [ ] {
nisot : : geodesic_movement = false ;
pmodel = mdPerspective ;
pconf . rotational_nil = 0 ;
2024-08-20 09:35:31 +00:00
} )
2024-08-20 12:45:20 +00:00
+ arg : : add3 ( " -ghost-all " , [ ] {
2024-08-20 17:14:13 +00:00
curlev - > load_all_ghosts ( ) ;
2022-04-24 17:48:45 +00:00
} ) ;
2023-08-06 20:17:20 +00:00
auto hook0 = addHook ( hooks_configfile , 300 , default_settings ) ;
2022-05-06 11:21:01 +00:00
# ifdef NILRIDER
auto hook1 =
addHook ( hooks_config , 100 , [ ] {
if ( arg : : curphase = = 1 )
conffile = " nilrider.ini " ;
if ( arg : : curphase = = 2 ) initialize_all ( ) ;
} ) ;
# endif
2022-04-24 17:48:45 +00:00
}