2019-08-10 11:43:24 +00:00
// Hyperbolic Rogue -- map editor and vector graphics editor
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
/** \file mapedit.cpp
* \ brief HyperRogue editors ( map and vector graphics )
*/
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-09 23:15:41 +00:00
EX namespace mapeditor {
2022-10-26 22:02:10 +00:00
/* changes when the map is changed */
EX int map_version ;
2020-04-17 18:04:33 +00:00
EX bool drawing_tool ;
2021-02-01 10:45:52 +00:00
EX bool intexture ;
2022-03-19 23:38:19 +00:00
EX bool snapping ;
2022-10-27 07:45:03 +00:00
EX bool building_mode = true ;
2020-04-17 18:04:33 +00:00
2019-08-09 23:15:41 +00:00
# if HDR
enum eShapegroup { sgPlayer , sgMonster , sgItem , sgFloor , sgWall } ;
2023-08-23 17:44:37 +00:00
static constexpr int USERSHAPEGROUPS = 5 ;
2019-08-09 23:15:41 +00:00
# endif
2020-04-17 18:04:33 +00:00
2020-04-18 14:10:56 +00:00
EX color_t dtfill = 0 ;
EX color_t dtcolor = 0x000000FF ;
EX ld dtwidth = .02 ;
2017-04-15 02:48:59 +00:00
2020-04-17 18:04:33 +00:00
/* drawing_tool shapes */
struct dtshape {
cell * where ;
color_t col , fill ;
ld lw ;
virtual void rotate ( const transmatrix & T ) = 0 ;
virtual void save ( hstream & hs ) = 0 ;
virtual dtshape * load ( hstream & hs ) = 0 ;
2020-07-27 16:49:04 +00:00
virtual void draw ( const shiftmatrix & V ) = 0 ;
2020-04-17 18:04:33 +00:00
virtual ld distance ( hyperpoint h ) = 0 ;
2020-04-18 12:32:54 +00:00
virtual ~ dtshape ( ) { }
2020-04-17 18:04:33 +00:00
} ;
struct dtline : dtshape {
hyperpoint s , e ;
void rotate ( const transmatrix & T ) override {
s = T * s ;
e = T * e ;
}
void save ( hstream & hs ) override {
hs . write < char > ( 1 ) ;
hs . write ( s ) ;
hs . write ( e ) ;
}
dtshape * load ( hstream & hs ) override {
hs . read ( s ) ;
hs . read ( e ) ;
return this ;
}
2020-07-27 16:49:04 +00:00
void draw ( const shiftmatrix & V ) override {
2020-04-17 18:04:33 +00:00
queueline ( V * s , V * e , col , 2 + vid . linequality ) ;
}
ld distance ( hyperpoint h ) override {
return hdist ( s , h ) + hdist ( e , h ) - hdist ( s , e ) ;
}
} ;
struct dtcircle : dtshape {
hyperpoint s ; ld radius ;
void rotate ( const transmatrix & T ) override {
s = T * s ;
}
void save ( hstream & hs ) override {
hs . write < char > ( 2 ) ;
hs . write ( s ) ;
hs . write ( radius ) ;
}
dtshape * load ( hstream & hs ) override {
hs . read ( s ) ;
hs . read ( radius ) ;
return this ;
}
2020-07-27 16:49:04 +00:00
void draw ( const shiftmatrix & V ) override {
2020-04-17 18:04:33 +00:00
ld len = sin_auto ( radius ) ;
int ll = ceil ( 360 * len ) ;
2020-07-27 16:49:04 +00:00
shiftmatrix W = V * rgpushxto0 ( s ) ;
2020-04-17 18:04:33 +00:00
for ( int i = 0 ; i < = ll ; i + + )
2022-11-12 21:38:45 +00:00
curvepoint ( xspinpush0 ( TAU * i / ll , radius ) ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( W , col , fill , PPR : : LINE ) ;
2020-04-17 18:04:33 +00:00
}
ld distance ( hyperpoint h ) override {
return abs ( hdist ( s , h ) - radius ) ;
}
} ;
2020-04-26 09:01:45 +00:00
struct dttext : dtshape {
hyperpoint where ;
ld size ;
string caption ;
void rotate ( const transmatrix & T ) override {
where = T * where ;
}
void save ( hstream & hs ) override {
hs . write < char > ( 4 ) ;
hs . write ( where ) ;
hs . write ( size ) ;
hs . write ( caption ) ;
}
dtshape * load ( hstream & hs ) override {
hs . read ( where ) ;
hs . read ( size ) ;
hs . read ( caption ) ;
return this ;
}
2020-07-27 16:49:04 +00:00
void draw ( const shiftmatrix & V ) override {
2020-04-26 09:01:45 +00:00
queuestr ( V * rgpushxto0 ( where ) , size , caption , col ) ;
}
ld distance ( hyperpoint h ) override {
return hdist ( h , where ) ;
}
} ;
2020-04-17 18:04:33 +00:00
struct dtfree : dtshape {
vector < hyperpoint > lh ;
void rotate ( const transmatrix & T ) override {
for ( auto & h : lh ) h = T * h ;
}
void save ( hstream & hs ) override {
hs . write < char > ( 3 ) ;
hs . write ( lh ) ;
}
dtshape * load ( hstream & hs ) override {
hs . read ( lh ) ;
return this ;
}
2020-07-27 16:49:04 +00:00
void draw ( const shiftmatrix & V ) override {
for ( auto & p : lh ) curvepoint ( p ) ;
queuecurve ( V , col , fill , PPR : : LINE ) ;
2020-04-17 18:04:33 +00:00
}
ld distance ( hyperpoint h ) override {
ld mind = 99 ;
for ( auto h1 : lh ) mind = min ( mind , hdist ( h , h1 ) ) ;
return mind ;
}
} ;
vector < unique_ptr < dtshape > > dtshapes ;
2020-05-01 10:27:49 +00:00
EX void clear_dtshapes ( ) { dtshapes . clear ( ) ; }
2022-03-19 23:38:19 +00:00
EX shiftpoint full_mouseh ( ) {
2022-05-06 10:40:48 +00:00
# if CAP_EDIT
2022-03-19 23:38:19 +00:00
if ( GDIM = = 3 ) return find_mouseh3 ( ) ;
if ( snapping ) return mouse_snap ( ) ;
2022-05-06 10:40:48 +00:00
# endif
2022-03-19 23:38:19 +00:00
return mouseh ;
}
2020-04-17 18:04:33 +00:00
EX void draw_dtshapes ( ) {
2020-07-30 00:29:59 +00:00
# if CAP_EDIT
2020-04-17 18:04:33 +00:00
for ( auto & shp : dtshapes ) {
if ( shp = = nullptr ) continue ;
auto & sh = * shp ;
cell * & c = sh . where ;
2020-07-27 16:49:04 +00:00
for ( const shiftmatrix & V : current_display - > all_drawn_copies [ c ] ) {
2020-04-17 18:04:33 +00:00
dynamicval < ld > lw ( vid . linewidth , vid . linewidth * sh . lw ) ;
sh . draw ( V ) ;
}
}
2020-04-17 18:34:49 +00:00
if ( drawing_tool & & ( cmode & sm : : DRAW ) ) {
2022-03-19 23:38:19 +00:00
shiftpoint moh = full_mouseh ( ) ;
2020-04-18 14:10:56 +00:00
dynamicval < ld > lw ( vid . linewidth , vid . linewidth * dtwidth * 100 ) ;
2023-12-02 18:34:06 +00:00
if ( holdmouse & & mousekey = = ' c ' ) {
torus_rug_jump ( moh , lstart ) ;
2020-07-28 15:50:24 +00:00
queue_hcircle ( rgpushxto0 ( lstart ) , hdist ( lstart , moh ) ) ;
2023-12-02 18:34:06 +00:00
}
else if ( holdmouse & & mousekey = = ' l ' ) {
torus_rug_jump ( moh , lstart ) ;
2020-07-28 15:50:24 +00:00
queueline ( lstart , moh , dtcolor , 4 + vid . linequality , PPR : : LINE ) ;
2023-12-02 18:34:06 +00:00
}
2020-04-17 18:34:49 +00:00
else if ( ! holdmouse ) {
2020-07-28 15:50:24 +00:00
shiftmatrix T = rgpushxto0 ( moh ) ;
2020-04-18 14:10:56 +00:00
queueline ( T * xpush0 ( - .1 ) , T * xpush0 ( .1 ) , dtcolor ) ;
queueline ( T * ypush0 ( - .1 ) , T * ypush0 ( .1 ) , dtcolor ) ;
2020-04-17 18:34:49 +00:00
}
}
2020-07-30 00:29:59 +00:00
# endif
2020-04-17 18:04:33 +00:00
}
/** dtshapes takes ownership of sh */
void dt_add ( cell * where , dtshape * sh ) {
sh - > where = where ;
2020-04-18 14:10:56 +00:00
sh - > col = dtcolor ;
2020-04-17 18:04:33 +00:00
sh - > fill = dtfill ;
2020-04-18 14:10:56 +00:00
sh - > lw = dtwidth * 100 ;
2020-04-17 18:04:33 +00:00
dtshapes . push_back ( unique_ptr < dtshape > ( sh ) ) ;
}
2020-07-27 16:49:04 +00:00
EX void dt_add_line ( shiftpoint h1 , shiftpoint h2 , int maxl ) {
2023-12-02 18:34:06 +00:00
torus_rug_jump ( h2 , h1 ) ;
2020-04-17 18:04:33 +00:00
if ( hdist ( h1 , h2 ) > 1 & & maxl > 0 ) {
2020-07-27 16:49:04 +00:00
shiftpoint h3 = mid ( h1 , h2 ) ;
2020-04-17 18:04:33 +00:00
dt_add_line ( h1 , h3 , maxl - 1 ) ;
dt_add_line ( h3 , h2 , maxl - 1 ) ;
return ;
}
cell * b = centerover ;
2023-12-02 11:07:23 +00:00
shiftmatrix T = rgpushxto0 ( h1 ) ;
auto T1 = inverse_shift ( ggmatrix ( b ) , T ) ;
virtualRebase ( b , T1 ) ;
hyperpoint xh1 = tC0 ( T1 ) ;
2020-04-17 18:04:33 +00:00
auto l = new dtline ;
2020-07-27 16:49:04 +00:00
l - > s = xh1 ;
2023-12-02 11:07:23 +00:00
l - > e = inverse_shift ( T * inverse ( T1 ) , h2 ) ;
2020-04-17 18:04:33 +00:00
dt_add ( b , l ) ;
}
2020-07-27 16:49:04 +00:00
EX void dt_add_circle ( shiftpoint h1 , shiftpoint h2 ) {
2020-04-17 18:04:33 +00:00
cell * b = centerover ;
2023-12-02 18:34:06 +00:00
torus_rug_jump ( h2 , h1 ) ;
2020-04-17 18:04:33 +00:00
auto d = hdist ( h1 , h2 ) ;
2020-07-27 16:49:04 +00:00
auto xh1 = inverse_shift ( ggmatrix ( b ) , h1 ) ;
virtualRebase ( b , xh1 ) ;
2020-04-17 18:04:33 +00:00
auto l = new dtcircle ;
2020-07-27 16:49:04 +00:00
l - > s = xh1 ;
2020-04-17 18:04:33 +00:00
l - > radius = d ;
dt_add ( b , l ) ;
}
2020-07-27 16:49:04 +00:00
EX void dt_add_text ( shiftpoint h , ld size , string cap ) {
2020-04-26 09:01:45 +00:00
cell * b = centerover ;
2020-07-27 16:49:04 +00:00
auto xh = inverse_shift ( ggmatrix ( b ) , h ) ;
virtualRebase ( b , xh ) ;
2020-04-26 09:01:45 +00:00
auto l = new dttext ;
2020-07-27 16:49:04 +00:00
l - > where = xh ;
2020-04-26 09:01:45 +00:00
l - > size = size ;
l - > caption = cap ;
dt_add ( b , l ) ;
2020-07-12 19:26:15 +00:00
l - > col > > = 8 ;
2020-04-26 09:01:45 +00:00
}
2020-04-17 18:04:33 +00:00
dtshape * load_shape ( hstream & hs ) {
char type = hs . get < char > ( ) ;
switch ( type ) {
case 1 :
return ( new dtline ) - > load ( hs ) ;
case 2 :
return ( new dtcircle ) - > load ( hs ) ;
case 3 :
return ( new dtfree ) - > load ( hs ) ;
2020-07-12 19:39:02 +00:00
case 4 :
return ( new dttext ) - > load ( hs ) ;
2020-04-17 18:04:33 +00:00
default :
return nullptr ;
}
}
dtfree * cfree ;
cell * cfree_at ;
2023-12-02 11:07:23 +00:00
shiftmatrix cfree_old ;
2020-04-17 18:04:33 +00:00
2020-09-21 10:01:22 +00:00
EX void dt_finish ( ) {
cfree = nullptr ;
cfree_at = nullptr ;
}
2023-12-02 18:34:06 +00:00
EX void torus_rug_jump ( shiftpoint & h , shiftpoint last ) {
if ( ! rug : : rugged ) return ;
again :
auto C = ggmatrix ( centerover ) ;
auto T1 = inverse_shift ( C , rgpushxto0 ( h ) ) ;
for ( int a = 0 ; a < 2 ; a + + ) for ( int s : { - 1 , 1 } ) {
transmatrix T = eumove ( s * euc : : eu . optimal_axes [ a ] ) ;
shiftpoint h1 = C * T * tC0 ( T1 ) ;
if ( hdist ( h1 , last ) < hdist ( h , last ) - 1e-6 ) { h = h1 ; goto again ; }
}
}
2020-07-27 16:49:04 +00:00
EX void dt_add_free ( shiftpoint h ) {
2017-04-15 02:48:59 +00:00
2023-12-02 18:34:06 +00:00
if ( cfree ) torus_rug_jump ( h , cfree_old * cfree - > lh . back ( ) ) ;
2020-04-17 18:04:33 +00:00
cell * b = centerover ;
2020-07-27 16:49:04 +00:00
shiftmatrix T = rgpushxto0 ( h ) ;
auto T1 = inverse_shift ( ggmatrix ( b ) , T ) ;
2020-04-17 18:04:33 +00:00
virtualRebase ( b , T1 ) ;
2023-12-02 11:07:23 +00:00
if ( cfree ) {
cfree - > lh . push_back ( inverse_shift ( cfree_old , tC0 ( T ) ) ) ;
}
2020-04-17 18:04:33 +00:00
if ( b ! = cfree_at & & ! ( dtfill & & cfree_at ) ) {
cfree = new dtfree ;
dt_add ( b , cfree ) ;
2020-07-28 15:50:24 +00:00
cfree - > lh . push_back ( tC0 ( T1 ) ) ;
2020-04-17 18:04:33 +00:00
cfree_at = b ;
2023-12-02 11:07:23 +00:00
cfree_old = T * inverse ( T1 ) ;
2020-04-17 18:04:33 +00:00
}
}
2020-07-27 16:49:04 +00:00
EX void dt_erase ( shiftpoint h ) {
2020-04-17 18:04:33 +00:00
ld nearest = 1 ;
int nearest_id = - 1 ;
int id = - 1 ;
for ( auto & shp : dtshapes ) {
id + + ;
if ( shp = = nullptr ) continue ;
auto & sh = * shp ;
cell * & c = sh . where ;
2020-07-27 16:49:04 +00:00
for ( const shiftmatrix & V : current_display - > all_drawn_copies [ c ] ) {
ld dist = sh . distance ( inverse_shift ( V , h ) ) ;
2020-04-17 18:04:33 +00:00
if ( dist < nearest ) nearest = dist , nearest_id = id ;
}
}
if ( nearest_id > = 0 )
dtshapes . erase ( dtshapes . begin ( ) + nearest_id ) ;
}
2020-07-27 16:49:04 +00:00
EX shiftpoint lstart ;
2020-07-28 15:50:24 +00:00
EX hyperpoint lstart_rel ;
2019-02-08 16:08:00 +00:00
cell * lstartcell ;
2019-02-28 16:49:57 +00:00
ld front_edit = 0.5 ;
2019-08-10 20:33:20 +00:00
enum class eFront { sphere_camera , sphere_center , equidistants , const_x , const_y } ;
2019-05-15 15:38:49 +00:00
eFront front_config ;
ld front_step = 0.1 ;
2019-02-08 16:08:00 +00:00
2019-09-06 06:17:02 +00:00
# if HDR
2017-06-18 16:52:15 +00:00
struct editwhat {
double dist ;
int rotid , symid , pointid ;
bool side ;
cell * c ;
2019-09-06 06:17:02 +00:00
} ;
# endif
EX editwhat ew , ewsearch ;
EX bool autochoose = ISMOBILE ;
2017-06-18 16:52:15 +00:00
2020-04-19 20:43:16 +00:00
EX void scaleall ( ld z , bool keep_mouse ) {
2018-02-03 13:31:17 +00:00
2020-04-19 20:43:16 +00:00
if ( keep_mouse ) {
ld mrx = ( .0 + mousex - current_display - > xcenter ) / vpconf . scale ;
ld mry = ( .0 + mousey - current_display - > ycenter ) / vpconf . scale ;
if ( vid . xres > vid . yres ) {
vpconf . xposition + = ( vpconf . scale - vpconf . scale * z ) * mrx / current_display - > scrsize ;
vpconf . yposition + = ( vpconf . scale - vpconf . scale * z ) * mry / current_display - > scrsize ;
}
2018-02-03 13:31:17 +00:00
}
2020-04-16 22:53:58 +00:00
vpconf . scale * = z ;
// printf("scale = " LDF "\n", vpconf.scale);
2018-02-03 13:31:17 +00:00
# if CAP_TEXTURE
2018-02-13 22:25:44 +00:00
// display(texture::itt);
2018-03-17 20:12:46 +00:00
texture : : config . itt = xyscale ( texture : : config . itt , 1 / z ) ;
if ( false & & texture : : config . tstate ) {
2018-02-03 13:31:17 +00:00
calcparam ( ) ;
2018-03-17 20:12:46 +00:00
texture : : config . perform_mapping ( ) ;
if ( texture : : config . tstate = = texture : : tsAdjusting )
texture : : config . finish_mapping ( ) ;
2018-02-03 13:31:17 +00:00
}
# endif
}
2017-07-22 23:33:27 +00:00
# if CAP_EDIT
2019-08-09 23:15:41 +00:00
EX map < int , cell * > modelcell ;
2017-07-10 18:47:38 +00:00
2019-08-09 23:15:41 +00:00
EX void applyModelcell ( cell * c ) {
2017-12-09 02:48:30 +00:00
if ( patterns : : whichPattern = = ' H ' ) return ;
2017-12-09 07:06:41 +00:00
auto si = patterns : : getpatterninfo0 ( c ) ;
cell * c2 = modelcell [ si . id ] ;
2016-08-26 09:58:03 +00:00
if ( c2 ) {
c - > wall = c2 - > wall ;
c - > land = c2 - > land ;
c - > monst = c2 - > monst ;
c - > item = c2 - > item ;
c - > landparam = c2 - > landparam ;
c - > wparam = c2 - > wparam ;
c - > mondir = c2 - > mondir ;
2017-11-12 23:37:18 +00:00
c - > stuntime = c2 - > stuntime ;
c - > hitpoints = c2 - > hitpoints ;
2017-12-09 07:06:41 +00:00
if ( c2 - > mondir ! = NODIR ) {
auto si2 = patterns : : getpatterninfo0 ( c2 ) ;
2020-01-18 15:03:32 +00:00
c - > mondir = gmod ( c2 - > mondir - si2 . dir + si . dir , c - > type ) ;
2017-12-09 07:06:41 +00:00
// todo reflect
}
2016-08-26 09:58:03 +00:00
}
}
# endif
2020-08-02 00:04:18 +00:00
EX }
2016-08-26 09:58:03 +00:00
2020-08-02 00:04:18 +00:00
# if HDR
struct hstream ;
2020-09-21 10:02:07 +00:00
struct fhstream ;
2020-08-02 00:04:18 +00:00
# endif
EX namespace mapstream {
2018-09-07 13:15:00 +00:00
# if CAP_EDIT
2018-11-24 16:01:49 +00:00
2020-09-21 10:02:07 +00:00
EX std : : map < cell * , int > cellids ;
EX vector < cell * > cellbyid ;
EX vector < char > relspin ;
2016-08-26 09:58:03 +00:00
2022-06-16 21:06:49 +00:00
void load_drawing_tool ( hstream & hs ) {
2020-04-17 18:04:33 +00:00
using namespace mapeditor ;
if ( hs . vernum < 0xA82A ) return ;
int i = hs . get < int > ( ) ;
while ( i - - ) {
auto sh = load_shape ( hs ) ;
if ( ! sh ) continue ;
hs . read ( sh - > col ) ;
hs . read ( sh - > fill ) ;
hs . read ( sh - > lw ) ;
int id = hs . get < int > ( ) ;
sh - > where = cellbyid [ id ] ;
sh - > rotate ( spin ( currentmap - > spin_angle ( sh - > where , relspin [ id ] ) - currentmap - > spin_angle ( sh - > where , 0 ) ) ) ;
dtshapes . push_back ( unique_ptr < dtshape > ( sh ) ) ;
}
}
void save_drawing_tool ( hstream & hs ) {
using namespace mapeditor ;
hs . write < int > ( isize ( dtshapes ) ) ;
for ( auto & shp : dtshapes ) {
if ( shp = = nullptr ) { hs . write < char > ( 0 ) ; }
else {
auto & sh = * shp ;
sh . save ( hs ) ;
hs . write ( sh . col ) ;
hs . write ( sh . fill ) ;
hs . write ( sh . lw ) ;
hs . write ( cellids [ sh . where ] ) ;
}
}
}
2016-08-26 09:58:03 +00:00
void addToQueue ( cell * c ) {
if ( cellids . count ( c ) ) return ;
2018-06-22 12:47:24 +00:00
int numcells = isize ( cellbyid ) ;
2016-08-26 09:58:03 +00:00
cellbyid . push_back ( c ) ;
cellids [ c ] = numcells ;
}
2021-03-16 18:54:23 +00:00
EX int fixspin ( int rspin , int dir , int t , int vernum ) {
2019-05-29 13:53:13 +00:00
if ( vernum < 11018 & & dir = = 14 )
return NODIR ;
else if ( vernum < 11018 & & dir = = 15 )
return NOBARRIERS ;
else if ( dir > = 0 & & dir < t )
return ( dir + rspin ) % t ;
else
return dir ;
}
2020-08-03 21:25:08 +00:00
# endif
2019-05-29 13:53:13 +00:00
2020-08-02 00:04:18 +00:00
EX void save_geometry ( hstream & f ) {
2018-11-24 16:01:49 +00:00
f . write ( geometry ) ;
2018-08-28 15:17:34 +00:00
char nbtype = char ( variation ) ;
2018-11-24 16:01:49 +00:00
f . write ( nbtype ) ;
2019-02-26 13:56:26 +00:00
# if CAP_GP
2020-07-28 11:18:06 +00:00
if ( GOLDBERG | | INVERSE ) {
2018-11-24 16:01:49 +00:00
f . write ( gp : : param . first ) ;
f . write ( gp : : param . second ) ;
2018-04-10 22:30:50 +00:00
}
2019-02-26 13:56:26 +00:00
# endif
2024-03-23 20:21:16 +00:00
# if CAP_IRR
if ( IRREGULAR ) irr : : save_map_bin ( f ) ;
# endif
2022-02-17 20:00:10 +00:00
# if MAXMDIM >= 4
2021-10-08 09:30:14 +00:00
if ( variation = = eVariation : : coxeter ) {
f . write ( reg3 : : coxeter_param ) ;
}
if ( is_subcube_based ( variation ) ) {
f . write ( reg3 : : subcube_count ) ;
}
2022-02-17 20:00:10 +00:00
# endif
2019-02-26 13:56:26 +00:00
# if CAP_FIELD
2018-06-21 23:48:46 +00:00
if ( geometry = = gFieldQuotient ) {
2017-11-07 13:08:50 +00:00
using namespace fieldpattern ;
2018-11-24 16:01:49 +00:00
f . write ( quotient_field_changed ) ;
2017-11-07 13:08:50 +00:00
if ( quotient_field_changed ) {
2021-09-17 09:56:18 +00:00
decltype ( current_extra ) cex = WDIM = = 3 ? - 1 : current_extra ;
f . write ( cex ) ;
if ( cex = = - 1 ) {
f . write ( fieldpattern : : underlying_geometry ) ;
hwrite_fpattern ( f , currfp ) ;
}
else {
f . write ( fgeomextras [ current_extra ] . current_prime_id ) ;
}
2017-11-07 13:08:50 +00:00
}
}
2019-02-26 13:56:26 +00:00
# endif
# if CAP_CRYSTAL
2019-08-22 10:14:39 +00:00
if ( cryst ) {
2018-12-14 18:26:03 +00:00
f . write ( ginf [ gCrystal ] . sides ) ;
if ( ginf [ gCrystal ] . sides = = 8 )
2018-12-15 13:16:57 +00:00
f . write ( ginf [ gCrystal ] . vertex ) ;
2018-12-14 18:26:03 +00:00
}
2019-02-26 13:56:26 +00:00
# endif
# if CAP_ARCM
2018-11-24 16:01:49 +00:00
if ( geometry = = gArchimedean ) f . write ( arcm : : current . symbol ) ;
2019-02-26 13:56:26 +00:00
# endif
2021-07-30 10:25:17 +00:00
if ( geometry = = gArbitrary ) {
f . write < bool > ( rulegen : : known ( ) ) ;
f . write < bool > ( arb : : convert : : in ( ) ) ;
if ( arb : : convert : : in ( ) ) {
dynamicval < eGeometry > dg ( geometry , arb : : convert : : base_geometry ) ;
dynamicval < eVariation > dv ( variation , arb : : convert : : base_variation ) ;
save_geometry ( f ) ;
}
else
f . write ( arb : : current . filename ) ;
}
2020-03-22 08:48:24 +00:00
if ( geometry = = gNil ) {
2019-11-23 20:08:47 +00:00
f . write ( S7 ) ;
f . write ( nilv : : nilperiod ) ;
}
2021-03-30 09:27:48 +00:00
# if CAP_SOLV
2020-03-22 08:48:24 +00:00
if ( geometry = = gArnoldCat ) {
2019-11-23 20:08:47 +00:00
f . write ( asonov : : period_xy ) ;
f . write ( asonov : : period_z ) ;
}
2021-03-30 09:27:48 +00:00
# endif
2022-12-08 18:38:06 +00:00
if ( mproduct ) {
2020-07-24 00:30:50 +00:00
f . write ( hybrid : : csteps ) ;
2019-11-30 10:22:33 +00:00
f . write ( product : : cspin ) ;
2020-07-24 00:30:50 +00:00
f . write ( product : : cmirror ) ;
2021-10-07 11:24:08 +00:00
f . write ( vid . plevel_factor ) ;
2020-07-24 00:30:50 +00:00
}
2024-06-16 15:46:56 +00:00
if ( mtwisted ) {
2020-07-24 00:30:50 +00:00
f . write ( hybrid : : csteps ) ;
2019-11-23 20:08:47 +00:00
}
2022-12-08 18:38:06 +00:00
if ( mhybrid ) {
2019-11-23 20:08:47 +00:00
hybrid : : in_underlying_geometry ( [ & ] { save_geometry ( f ) ; } ) ;
}
2020-08-02 00:04:18 +00:00
if ( fake : : in ( ) ) {
f . write ( fake : : around ) ;
fake : : in_underlying_geometry ( [ & ] { save_geometry ( f ) ; } ) ;
}
2020-03-22 08:48:24 +00:00
if ( bt : : in ( ) )
2019-11-23 20:08:47 +00:00
f . write ( vid . binary_width ) ;
2019-12-14 10:26:03 +00:00
if ( euc : : in ( ) ) {
2019-12-08 09:59:09 +00:00
f . write ( euc : : eu_input . user_axes ) ;
f . write ( euc : : eu_input . twisted ) ;
2019-11-27 21:04:41 +00:00
}
2019-12-27 10:57:23 +00:00
f . write ( mine_adjacency_rule ) ;
2023-08-14 19:10:44 +00:00
f . write ( req_disksize ) ;
f . write ( diskshape ) ;
2019-11-23 20:08:47 +00:00
}
2020-08-02 00:04:18 +00:00
EX void load_geometry ( hstream & f ) {
2021-09-17 10:20:15 +00:00
bool was_default = pmodel = = default_model ( ) ;
2020-08-02 00:04:18 +00:00
auto vernum = f . get_vernum ( ) ;
2019-11-23 20:08:47 +00:00
f . read ( geometry ) ;
char nbtype ;
f . read ( nbtype ) ;
variation = eVariation ( nbtype ) ;
# if CAP_GP
2020-07-28 11:18:06 +00:00
if ( GOLDBERG | | INVERSE ) {
2019-11-23 20:08:47 +00:00
f . read ( gp : : param . first ) ;
f . read ( gp : : param . second ) ;
}
# endif
2024-03-23 20:21:16 +00:00
# if CAP_IRR
if ( IRREGULAR ) { irr : : load_map_full ( f ) ; stop_game ( ) ; }
# endif
2022-02-17 20:00:10 +00:00
# if MAXMDIM >= 4
2021-10-08 09:30:14 +00:00
if ( variation = = eVariation : : coxeter & & vernum > = 0xA908 ) {
f . read ( reg3 : : coxeter_param ) ;
}
if ( is_subcube_based ( variation ) & & vernum > = 0xA908 ) {
f . read ( reg3 : : subcube_count ) ;
}
2022-02-17 20:00:10 +00:00
# endif
2019-11-23 20:08:47 +00:00
# if CAP_CRYSTAL
2020-08-02 00:04:18 +00:00
if ( cryst & & vernum > = 10504 ) {
2019-11-23 20:08:47 +00:00
int sides ;
f . read ( sides ) ;
# if CAP_CRYSTAL
crystal : : set_crystal ( sides ) ;
# endif
if ( sides = = 8 ) {
int vertices ;
eVariation v = variation ;
f . read ( vertices ) ;
if ( vertices = = 3 ) {
set_variation ( eVariation : : bitruncated ) ;
set_variation ( v ) ;
}
}
}
# endif
# if CAP_FIELD
if ( geometry = = gFieldQuotient ) {
using namespace fieldpattern ;
f . read ( quotient_field_changed ) ;
if ( quotient_field_changed ) {
2021-09-17 09:56:18 +00:00
auto cex = current_extra ;
2019-11-23 20:08:47 +00:00
f . read ( current_extra ) ;
2021-09-17 09:56:18 +00:00
if ( current_extra = = - 1 ) {
current_extra = cex ;
f . read ( geometry ) ;
check_cgi ( ) ;
cgi . require_basics ( ) ;
fieldpattern : : field_from_current ( ) ;
set_geometry ( gFieldQuotient ) ;
hread_fpattern ( f , currfp ) ;
}
else {
auto & ge = fgeomextras [ current_extra ] ;
auto & id = ge . current_prime_id ;
f . read ( id ) ;
if ( vernum < 0xA80C ) switch ( ge . base ) {
case gNormal : id + + ; break ;
case g45 : id + + ; break ;
case g46 : id + = 2 ; break ;
case g47 : id + + ; break ;
default : ;
}
enableFieldChange ( ) ;
2019-11-23 20:08:47 +00:00
}
}
}
# endif
2021-07-30 10:25:17 +00:00
if ( geometry = = gArbitrary ) {
bool rk = vernum > = 0xA905 & & f . get < bool > ( ) ;
bool ac = vernum > = 0xA905 & & f . get < bool > ( ) ;
if ( ac ) {
load_geometry ( f ) ;
arb : : convert : : convert ( ) ;
arb : : convert : : activate ( ) ;
}
else {
string s ;
f . read ( s ) ;
2024-06-02 15:14:41 +00:00
arb : : run_raw ( s ) ;
2021-07-30 10:25:17 +00:00
stop_game ( ) ;
}
if ( rk ) rulegen : : prepare_rules ( ) ;
2020-08-02 10:53:19 +00:00
}
2019-11-23 20:08:47 +00:00
# if CAP_ARCM
if ( geometry = = gArchimedean ) {
string & symbol = arcm : : current . symbol ;
symbol = f . get < string > ( ) ;
arcm : : current . parse ( ) ;
if ( arcm : : current . errors > 0 ) {
printf ( " Errors! %s \n " , arcm : : current . errormsg . c_str ( ) ) ;
}
}
# endif
2020-08-02 00:04:18 +00:00
if ( geometry = = gNil & & vernum > = 0xA80C ) {
2019-11-23 20:08:47 +00:00
f . read ( S7 ) ;
f . read ( nilv : : nilperiod ) ;
nilv : : set_flags ( ) ;
}
2021-03-30 09:27:48 +00:00
# if CAP_SOLV
2020-08-02 00:04:18 +00:00
if ( geometry = = gArnoldCat & & vernum > = 0xA80C ) {
2019-11-23 20:08:47 +00:00
f . read ( asonov : : period_xy ) ;
f . read ( asonov : : period_z ) ;
asonov : : set_flags ( ) ;
}
2021-03-30 09:27:48 +00:00
# endif
2020-08-02 00:04:18 +00:00
if ( geometry = = gProduct & & vernum > = 0xA80C ) {
2020-07-24 00:30:50 +00:00
f . read ( hybrid : : csteps ) ;
2020-08-02 00:04:18 +00:00
if ( vernum > = 0xA80D ) f . read ( product : : cspin ) ;
if ( vernum > = 0xA833 ) f . read ( product : : cmirror ) ;
2021-10-07 11:24:08 +00:00
if ( vernum > = 0xA908 ) f . read ( vid . plevel_factor ) ;
2020-07-24 00:30:50 +00:00
}
2024-06-16 15:46:56 +00:00
if ( geometry = = gTwistedProduct & & vernum > = 0xA833 ) {
2020-07-24 00:30:50 +00:00
f . read ( hybrid : : csteps ) ;
2019-11-23 20:08:47 +00:00
}
2022-12-08 18:38:06 +00:00
if ( mhybrid & & vernum > = 0xA80C ) {
2019-11-23 20:08:47 +00:00
auto g = geometry ;
load_geometry ( f ) ;
2019-11-23 22:09:06 +00:00
set_geometry ( g ) ;
2019-11-23 20:08:47 +00:00
}
2020-08-02 00:04:18 +00:00
if ( fake : : in ( ) ) {
ld ar ;
f . read ( ar ) ;
fake : : around = ar ;
load_geometry ( f ) ;
fake : : change_around ( ) ;
}
if ( bt : : in ( ) & & vernum > = 0xA80C )
2019-11-23 20:08:47 +00:00
f . read ( vid . binary_width ) ;
2020-08-02 00:04:18 +00:00
if ( euc : : in ( ) & & vernum > = 0xA80D ) {
2019-12-08 09:59:09 +00:00
f . read ( euc : : eu_input . user_axes ) ;
f . read ( euc : : eu_input . twisted ) ;
2019-11-27 21:04:41 +00:00
}
2020-08-02 00:04:18 +00:00
if ( vernum > = 0xA810 )
2019-12-27 10:57:23 +00:00
f . read ( mine_adjacency_rule ) ;
2023-08-14 19:10:44 +00:00
if ( vernum > = 0xA933 ) {
f . read ( req_disksize ) ;
f . read ( diskshape ) ;
}
2021-09-17 10:20:15 +00:00
geometry_settings ( was_default ) ;
2019-11-23 20:08:47 +00:00
}
2020-09-21 10:02:07 +00:00
2022-06-16 21:06:49 +00:00
EX hookset < void ( hstream & ) > hooks_savemap , hooks_loadmap_old ;
EX hookset < void ( hstream & , int ) > hooks_loadmap ;
2021-03-16 18:54:23 +00:00
EX cell * save_start ( ) {
2022-12-08 18:38:06 +00:00
return ( closed_manifold | | euclid | | mproduct | | arcm : : in ( ) | | sol | | INVERSE ) ? currentmap - > gamestart ( ) : cwt . at - > master - > c7 ;
2021-03-16 18:54:23 +00:00
}
2020-08-03 21:25:08 +00:00
# if CAP_EDIT
2022-06-16 21:06:49 +00:00
void save_only_map ( hstream & f ) {
2019-11-23 20:08:47 +00:00
f . write ( patterns : : whichPattern ) ;
save_geometry ( f ) ;
2022-07-12 11:19:18 +00:00
# if CAP_RACING
2022-06-16 21:13:06 +00:00
if ( racing : : on ) racing : : restore_goals ( ) ;
2022-07-12 11:19:18 +00:00
# endif
2018-12-24 14:10:52 +00:00
// game settings
f . write ( safety ) ;
f . write ( autocheat ) ;
f . write ( gen_wandering ) ;
f . write ( reptilecheat ) ;
f . write ( timerghost ) ;
2024-05-05 18:15:53 +00:00
f . write ( ccolor : : plain . ctab [ 0 ] ) ;
2018-12-24 14:10:52 +00:00
f . write ( patterns : : whichShape ) ;
f . write ( patterns : : subpattern_flags ) ;
2024-05-05 18:15:53 +00:00
char wc = ' * ' ;
f . write ( wc ) ;
f . write ( ccolor : : which - > name ) ;
2018-12-24 14:10:52 +00:00
f . write ( patterns : : displaycodes ) ;
2020-01-28 15:42:07 +00:00
f . write ( canvas_default_wall ) ;
2018-12-24 14:10:52 +00:00
f . write ( mapeditor : : drawplayer ) ;
2024-05-05 18:15:53 +00:00
if ( ccolor : : which = = & ccolor : : formula ) f . write ( ccolor : : color_formula ) ;
2022-03-07 03:01:59 +00:00
f . write ( canvasfloor ) ;
f . write ( canvasdark ) ;
2018-12-24 14:10:52 +00:00
{
int i = ittypes ; f . write ( i ) ;
for ( int k = 0 ; k < i ; k + + ) f . write ( items [ k ] ) ;
i = motypes ; f . write ( i ) ;
for ( int k = 0 ; k < i ; k + + ) f . write ( kills [ k ] ) ;
}
2021-03-16 18:54:23 +00:00
addToQueue ( save_start ( ) ) ;
2023-04-14 23:27:35 +00:00
# if CAP_PORTALS
2021-10-09 22:38:38 +00:00
if ( intra : : in ) intra : : prepare_need_to_save ( ) ;
2022-02-17 20:00:10 +00:00
# endif
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( cellbyid ) ; i + + ) {
2016-08-26 09:58:03 +00:00
cell * c = cellbyid [ i ] ;
if ( i ) {
2021-04-04 12:26:39 +00:00
bool ok = false ;
2018-08-17 22:46:45 +00:00
for ( int j = 0 ; j < c - > type ; j + + ) if ( c - > move ( j ) & & cellids . count ( c - > move ( j ) ) & &
cellids [ c - > move ( j ) ] < i ) {
int32_t i = cellids [ c - > move ( j ) ] ;
2018-11-24 16:01:49 +00:00
f . write ( i ) ;
f . write_char ( c - > c . spin ( j ) ) ;
f . write_char ( j ) ;
2021-04-04 12:26:39 +00:00
ok = true ;
2016-08-26 09:58:03 +00:00
break ;
}
2021-04-04 12:26:39 +00:00
if ( ! ok ) {
println ( hlog , " parent not found for " , c , " ! " ) ;
for ( int j = 0 ; j < c - > type ; j + + ) println ( hlog , j , " : " , c - > move ( j ) , " ; " , int ( cellids . count ( c - > move ( j ) ) ? cellids [ c - > move ( j ) ] : - 1 ) ) ;
throw hr_exception ( " parent not found " ) ;
}
2016-08-26 09:58:03 +00:00
}
2018-11-24 16:01:49 +00:00
f . write_char ( c - > land ) ;
f . write_char ( c - > mondir ) ;
f . write_char ( c - > monst ) ;
2019-01-16 23:48:55 +00:00
if ( c - > monst = = moTortoise )
f . write ( tortoise : : emap [ c ] = tortoise : : getb ( c ) ) ;
2018-11-24 16:01:49 +00:00
f . write_char ( c - > wall ) ;
2021-05-31 18:43:50 +00:00
if ( dice : : on ( c ) ) {
auto & dat = dice : : data [ c ] ;
f . write_char ( dice : : get_die_id ( dat . which ) ) ;
f . write_char ( dat . val ) ;
f . write_char ( dat . dir ) ;
2021-06-16 09:12:42 +00:00
f . write_char ( dat . mirrored ) ;
2021-05-31 18:43:50 +00:00
}
2018-11-24 16:01:49 +00:00
f . write_char ( c - > item ) ;
2019-01-16 23:48:55 +00:00
if ( c - > item = = itBabyTortoise )
f . write ( tortoise : : babymap [ c ] ) ;
2018-11-24 16:01:49 +00:00
f . write_char ( c - > mpdist ) ;
2022-06-16 21:07:20 +00:00
if ( inmirrororwall ( c ) ) {
f . write_char ( c - > barleft ) ;
f . write_char ( c - > barright ) ;
f . write_char ( c - > bardir ) ;
}
2018-11-24 16:01:49 +00:00
f . write ( c - > wparam ) ; f . write ( c - > landparam ) ;
f . write_char ( c - > stuntime ) ; f . write_char ( c - > hitpoints ) ;
2021-10-09 22:38:38 +00:00
bool blocked = false ;
2023-04-14 23:27:35 +00:00
# if CAP_PORTALS
2021-10-09 22:38:38 +00:00
if ( intra : : in & & isWall3 ( c ) & & ! intra : : need_to_save . count ( c ) ) blocked = true ;
2022-02-17 20:00:10 +00:00
# endif
2021-10-09 22:38:38 +00:00
if ( ! blocked )
2016-08-26 09:58:03 +00:00
for ( int j = 0 ; j < c - > type ; j + + ) {
2018-08-17 22:46:45 +00:00
cell * c2 = c - > move ( j ) ;
2022-06-16 21:07:36 +00:00
if ( c2 & & c2 - > land ! = laNone & & c2 - > land ! = laMemory ) addToQueue ( c2 ) ;
2016-08-26 09:58:03 +00:00
}
}
2018-06-22 12:47:24 +00:00
printf ( " cells saved = %d \n " , isize ( cellbyid ) ) ;
2018-11-24 16:01:49 +00:00
int32_t n = - 1 ; f . write ( n ) ;
2018-08-17 22:46:45 +00:00
int32_t id = cellids . count ( cwt . at ) ? cellids [ cwt . at ] : - 1 ;
2018-11-24 16:01:49 +00:00
f . write ( id ) ;
2022-02-26 22:58:59 +00:00
if ( f . vernum > = 0xA90C ) {
vector < color_t > v ;
for ( auto c : walking : : colors_of_floors ) v . push_back ( c ) ;
f . write ( v ) ;
}
2020-04-17 18:04:33 +00:00
save_drawing_tool ( f ) ;
2016-08-26 09:58:03 +00:00
2019-05-29 14:27:24 +00:00
f . write ( vid . always3 ) ;
2019-01-28 21:28:17 +00:00
f . write ( mutantphase ) ;
f . write ( rosewave ) ;
f . write ( rosephase ) ;
f . write ( turncount ) ;
int rms = isize ( rosemap ) ; f . write ( rms ) ;
for ( auto p : rosemap ) f . write ( cellids [ p . first ] ) , f . write ( p . second ) ;
f . write ( multi : : players ) ;
if ( multi : : players > 1 )
for ( int i = 0 ; i < multi : : players ; i + + )
f . write ( cellids [ multi : : player [ i ] . at ] ) ;
2021-09-17 10:00:53 +00:00
2023-04-14 23:27:35 +00:00
# if CAP_PORTALS
2021-09-17 10:00:53 +00:00
if ( intra : : in ) {
for ( int i = 0 ; i < isize ( intra : : portals_to_save ) ; i + + ) {
auto & p = intra : : portals_to_save [ i ] ;
if ( cellids . count ( p . cw1 . at ) ) {
f . write < char > ( 1 ) ;
f . write ( i ) ;
f . write ( cellids [ p . cw1 . at ] ) ;
f . write < char > ( p . cw1 . spin ) ;
}
if ( cellids . count ( p . cw2 . at ) ) {
f . write < char > ( 2 ) ;
f . write ( i ) ;
f . write ( cellids [ p . cw2 . at ] ) ;
f . write < char > ( p . cw2 . spin ) ;
}
}
f . write < char > ( 0 ) ;
}
2022-02-17 20:00:10 +00:00
# endif
2020-09-21 10:02:07 +00:00
2022-06-16 21:10:44 +00:00
if ( f . vernum > = 0xA912 ) {
2022-07-12 11:19:18 +00:00
# if CAP_RACING
2022-06-16 21:10:44 +00:00
f . write ( racing : : on ) ;
if ( racing : : on ) {
f . write < int > ( isize ( racing : : track ) ) ;
for ( auto & t : racing : : track ) f . write < int > ( cellids [ t ] ) ;
racing : : save_ghosts ( f ) ;
}
2022-07-12 11:19:18 +00:00
# else
bool on = false ;
f . write ( on ) ;
# endif
2022-06-16 21:10:44 +00:00
}
2020-09-21 10:02:07 +00:00
callhooks ( hooks_savemap , f ) ;
2021-03-31 09:37:29 +00:00
f . write < int > ( 0 ) ;
2016-08-26 09:58:03 +00:00
cellids . clear ( ) ;
cellbyid . clear ( ) ;
}
2022-06-16 21:06:49 +00:00
void load_usershapes ( hstream & f ) {
2019-05-29 13:53:13 +00:00
if ( f . vernum > = 7400 ) while ( true ) {
int i = f . get < int > ( ) ;
if ( i = = - 1 ) break ;
# if CAP_POLY
int j = f . get < int > ( ) , l = f . get < int > ( ) ;
if ( i > = 4 ) i = 3 ;
if ( i < 0 | | i > = mapeditor : : USERSHAPEGROUPS ) break ;
if ( l < 0 | | l > = USERLAYERS ) break ;
initShape ( i , j ) ;
usershapelayer & ds ( usershapes [ i ] [ j ] - > d [ l ] ) ;
if ( f . vernum > = 11008 ) f . read ( ds . zlevel ) ;
f . read ( ds . sym ) ; f . read ( ds . rots ) ; f . read ( ds . color ) ;
ds . list . clear ( ) ;
int siz = f . get < int > ( ) ;
ds . shift = f . get < hyperpoint > ( ) ;
ds . spin = f . get < hyperpoint > ( ) ;
for ( int i = 0 ; i < siz ; i + + )
ds . list . push_back ( f . get < hyperpoint > ( ) ) ;
# else
printf ( " cannot read shapes \n " ) ; exit ( 1 ) ;
# endif
}
2016-08-26 09:58:03 +00:00
}
2022-06-16 21:06:49 +00:00
void load_only_map ( hstream & f ) {
2019-05-29 13:53:13 +00:00
stop_game ( ) ;
2019-05-21 22:19:03 +00:00
if ( f . vernum > = 10420 & & f . vernum < 10503 ) {
2018-12-14 18:26:03 +00:00
int i ;
f . read ( i ) ;
patterns : : whichPattern = patterns : : ePattern ( i ) ;
}
2019-05-21 22:19:03 +00:00
else if ( f . vernum > = 7400 ) f . read ( patterns : : whichPattern ) ;
2017-11-07 13:08:50 +00:00
2019-11-23 20:08:47 +00:00
if ( f . vernum > = 10203 )
load_geometry ( f ) ;
2018-12-24 14:10:52 +00:00
2019-05-29 14:50:17 +00:00
check_cgi ( ) ;
cgi . require_basics ( ) ;
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2016-08-26 09:58:03 +00:00
initcells ( ) ;
if ( shmup : : on ) shmup : : init ( ) ;
2018-12-24 14:10:52 +00:00
2019-05-21 22:19:03 +00:00
if ( f . vernum > = 10505 ) {
2018-12-24 14:10:52 +00:00
// game settings
f . read ( safety ) ;
bool b ;
f . read ( b ) ; if ( b ) autocheat = true ;
f . read ( gen_wandering ) ;
f . read ( reptilecheat ) ;
f . read ( timerghost ) ;
2024-05-05 18:15:53 +00:00
f . read ( ccolor : : plain . ctab [ 0 ] ) ;
2018-12-24 14:10:52 +00:00
f . read ( patterns : : whichShape ) ;
f . read ( patterns : : subpattern_flags ) ;
2024-05-05 18:15:53 +00:00
char wc ;
f . read ( wc ) ;
if ( wc = = ' * ' ) {
string name ;
f . read ( name ) ;
for ( auto & p : ccolor : : all ) if ( p - > name = = name ) ccolor : : which = p ;
}
2018-12-24 14:10:52 +00:00
f . read ( patterns : : displaycodes ) ;
2020-03-22 08:48:24 +00:00
if ( f . vernum > = 0xA816 )
2020-01-28 15:42:07 +00:00
f . read ( canvas_default_wall ) ;
2018-12-24 14:10:52 +00:00
f . read ( mapeditor : : drawplayer ) ;
2024-05-05 18:15:53 +00:00
if ( wc = = ' f ' ) f . read ( ccolor : : color_formula ) ;
2022-03-27 20:42:56 +00:00
if ( f . vernum > = 0xA90D ) {
2022-03-08 01:59:49 +00:00
f . read ( canvasfloor ) ;
f . read ( canvasdark ) ;
}
2018-12-24 14:10:52 +00:00
int i ;
f . read ( i ) ; if ( i > ittypes | | i < 0 ) throw hstream_exception ( ) ;
for ( int k = 0 ; k < i ; k + + ) f . read ( items [ k ] ) ;
f . read ( i ) ; if ( i > motypes | | i < 0 ) throw hstream_exception ( ) ;
for ( int k = 0 ; k < i ; k + + ) f . read ( kills [ k ] ) ;
}
2016-08-26 09:58:03 +00:00
2022-12-08 18:38:06 +00:00
int sub = mhybrid ? 2 : 0 ;
2016-08-26 09:58:03 +00:00
while ( true ) {
cell * c ;
int rspin ;
2018-06-22 12:47:24 +00:00
if ( isize ( cellbyid ) = = 0 ) {
2017-04-04 09:13:15 +00:00
c = currentmap - > gamestart ( ) ;
2016-08-26 09:58:03 +00:00
rspin = 0 ;
}
else {
2018-11-24 16:01:49 +00:00
int32_t parent = f . get < int > ( ) ;
2016-08-26 09:58:03 +00:00
2018-06-22 12:47:24 +00:00
if ( parent < 0 | | parent > = isize ( cellbyid ) ) break ;
2018-11-24 16:01:49 +00:00
int dir = f . read_char ( ) ;
2016-08-26 09:58:03 +00:00
cell * c2 = cellbyid [ parent ] ;
2019-11-23 22:09:06 +00:00
dir = fixspin ( relspin [ parent ] , dir , c2 - > type - sub , f . vernum ) ;
2016-08-26 09:58:03 +00:00
c = createMov ( c2 , dir ) ;
2017-11-07 13:08:50 +00:00
// printf("%p:%d,%d -> %p\n", c2, relspin[parent], dir, c);
2016-08-26 09:58:03 +00:00
// spinval becomes xspinval
2020-01-18 15:03:32 +00:00
rspin = gmod ( c2 - > c . spin ( dir ) - f . read_char ( ) , c - > type - sub ) ;
2022-12-08 18:38:06 +00:00
if ( GDIM = = 3 & & rspin & & ! mhybrid ) {
2019-04-13 11:08:08 +00:00
println ( hlog , " rspin in 3D " ) ;
throw hstream_exception ( ) ;
}
2016-08-26 09:58:03 +00:00
}
cellbyid . push_back ( c ) ;
relspin . push_back ( rspin ) ;
2018-11-24 16:01:49 +00:00
c - > land = ( eLand ) f . read_char ( ) ;
2019-11-23 22:09:06 +00:00
c - > mondir = fixspin ( rspin , f . read_char ( ) , c - > type - sub , f . vernum ) ;
2018-11-24 16:01:49 +00:00
c - > monst = ( eMonster ) f . read_char ( ) ;
2019-05-21 22:19:03 +00:00
if ( c - > monst = = moTortoise & & f . vernum > = 11001 )
2019-01-16 23:48:55 +00:00
f . read ( tortoise : : emap [ c ] ) ;
2018-11-24 16:01:49 +00:00
c - > wall = ( eWall ) f . read_char ( ) ;
2021-05-31 18:43:50 +00:00
if ( dice : : on ( c ) ) {
auto & dat = dice : : data [ c ] ;
dat . which = dice : : get_by_id ( f . read_char ( ) ) ;
dat . val = f . read_char ( ) ;
dat . dir = fixspin ( rspin , f . read_char ( ) , c - > type , f . vernum ) ;
2021-06-16 09:12:42 +00:00
if ( f . vernum > = 0xA902 )
dat . mirrored = f . read_char ( ) ;
2021-05-31 18:43:50 +00:00
}
2018-11-24 16:01:49 +00:00
// c->barleft = (eLand) f.read_char();
// c->barright = (eLand) f.read_char();
c - > item = ( eItem ) f . read_char ( ) ;
2019-05-21 22:19:03 +00:00
if ( c - > item = = itBabyTortoise & & f . vernum > = 11001 )
2019-01-16 23:48:55 +00:00
f . read ( tortoise : : babymap [ c ] ) ;
2018-11-24 16:01:49 +00:00
c - > mpdist = f . read_char ( ) ;
2016-08-26 09:58:03 +00:00
c - > bardir = NOBARRIERS ;
2022-06-16 21:07:20 +00:00
if ( inmirrororwall ( c ) & & f . vernum > = 0xA912 ) {
c - > barleft = ( eLand ) f . read_char ( ) ;
c - > barright = ( eLand ) f . read_char ( ) ;
c - > bardir = fixspin ( rspin , f . read_char ( ) , c - > type , f . vernum ) ;
}
2018-11-24 16:01:49 +00:00
// fixspin(rspin, f.read_char(), c->type);
2019-05-21 22:19:03 +00:00
if ( f . vernum < 7400 ) {
2018-06-28 10:59:35 +00:00
short z ;
2018-11-24 16:01:49 +00:00
f . read ( z ) ;
2018-06-28 10:59:35 +00:00
c - > wparam = z ;
2016-08-26 09:58:03 +00:00
}
2018-11-24 16:01:49 +00:00
else f . read ( c - > wparam ) ;
f . read ( c - > landparam ) ;
2016-08-26 09:58:03 +00:00
// backward compatibility
2019-05-21 22:19:03 +00:00
if ( f . vernum < 7400 & & ! isIcyLand ( c - > land ) ) c - > landparam = HEAT ( c ) ;
2018-11-24 16:01:49 +00:00
c - > stuntime = f . read_char ( ) ;
c - > hitpoints = f . read_char ( ) ;
2016-08-26 09:58:03 +00:00
2017-12-09 02:48:30 +00:00
if ( patterns : : whichPattern )
2017-12-09 07:06:41 +00:00
mapeditor : : modelcell [ patterns : : getpatterninfo0 ( c ) . id ] = c ;
2016-08-26 09:58:03 +00:00
}
2018-11-24 16:01:49 +00:00
int32_t whereami = f . get < int > ( ) ;
2018-06-22 12:47:24 +00:00
if ( whereami > = 0 & & whereami < isize ( cellbyid ) )
2018-08-17 22:46:45 +00:00
cwt . at = cellbyid [ whereami ] ;
else cwt . at = currentmap - > gamestart ( ) ;
2016-08-26 09:58:03 +00:00
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( cellbyid ) ; i + + ) {
2016-08-26 09:58:03 +00:00
cell * c = cellbyid [ i ] ;
if ( c - > bardir ! = NODIR & & c - > bardir ! = NOBARRIERS )
extendBarrier ( c ) ;
}
for ( int d = BARLEV - 1 ; d > = 0 ; d - - )
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( cellbyid ) ; i + + ) {
2016-08-26 09:58:03 +00:00
cell * c = cellbyid [ i ] ;
if ( c - > mpdist < = d )
for ( int j = 0 ; j < c - > type ; j + + ) {
cell * c2 = createMov ( c , j ) ;
setdist ( c2 , d + 1 , c ) ;
}
}
relspin . clear ( ) ;
if ( shmup : : on ) shmup : : init ( ) ;
timerstart = time ( NULL ) ; turncount = 0 ;
sagephase = 0 ; hardcoreAt = 0 ;
timerstopped = false ;
savecount = 0 ; savetime = 0 ;
cheater = 1 ;
2022-02-26 22:58:59 +00:00
if ( f . vernum > = 0xA90C ) {
vector < color_t > v ;
f . read ( v ) ;
walking : : colors_of_floors . clear ( ) ;
for ( auto c : v ) walking : : colors_of_floors . insert ( c ) ;
}
2022-08-13 20:43:45 +00:00
else walking : : colors_of_floors . clear ( ) ;
2022-02-26 22:58:59 +00:00
2020-04-17 18:04:33 +00:00
load_drawing_tool ( f ) ;
2019-05-29 14:27:24 +00:00
dynamicval < bool > a3 ( vid . always3 , vid . always3 ) ;
2019-08-22 09:24:25 +00:00
if ( f . vernum > = 0xA616 ) { f . read ( vid . always3 ) ; geom3 : : apply_always3 ( ) ; }
2019-01-28 21:28:17 +00:00
2019-05-29 13:53:13 +00:00
if ( f . vernum < 0xA61A ) load_usershapes ( f ) ;
2019-05-21 22:19:03 +00:00
if ( f . vernum > = 11005 ) {
2019-01-28 21:28:17 +00:00
f . read ( mutantphase ) ;
f . read ( rosewave ) ;
f . read ( rosephase ) ;
f . read ( turncount ) ;
int i ; f . read ( i ) ;
if ( i ) havewhat | = HF_ROSE ;
while ( i - - ) {
int cid ; int val ; f . read ( cid ) ; f . read ( val ) ;
if ( cid > = 0 & & cid < isize ( cellbyid ) ) rosemap [ cellbyid [ cid ] ] = val ;
}
f . read ( multi : : players ) ;
if ( multi : : players > 1 )
for ( int i = 0 ; i < multi : : players ; i + + ) {
auto & mp = multi : : player [ i ] ;
int whereami = f . get < int > ( ) ;
if ( whereami > = 0 & & whereami < isize ( cellbyid ) )
mp . at = cellbyid [ whereami ] ;
else
mp . at = currentmap - > gamestart ( ) ;
mp . spin = 0 ,
mp . mirrored = false ;
}
}
2021-09-17 10:00:53 +00:00
2023-04-14 23:27:35 +00:00
# if CAP_PORTALS
2021-09-17 10:00:53 +00:00
if ( intra : : in ) {
while ( true ) {
char k = f . get < char > ( ) ;
if ( ! k ) break ;
int i = f . get < int > ( ) ;
int id = f . get < int > ( ) ;
int spin = f . get < char > ( ) ;
auto & p = intra : : portals_to_save [ i ] ;
auto & cw = ( k = = 1 ? p . cw1 : p . cw2 ) ;
cw . at = cellbyid [ id ] ;
cw . spin = fixspin ( relspin [ id ] , spin , cw . at - > type , f . vernum ) ;
}
}
2022-02-17 20:00:10 +00:00
# endif
2022-06-16 21:10:44 +00:00
if ( f . vernum > = 0xA912 ) {
2022-07-12 11:19:18 +00:00
# if CAP_RACING
2022-06-16 21:10:44 +00:00
f . read ( racing : : on ) ;
if ( racing : : on ) {
if ( ! shmup : : on ) {
shmup : : on = true ;
shmup : : init ( ) ;
}
racing : : track . resize ( f . get < int > ( ) ) ;
for ( auto & t : racing : : track ) t = cellbyid [ f . get < int > ( ) ] ;
racing : : load_ghosts ( f ) ;
racing : : configure_track ( false ) ;
}
2022-07-12 11:19:18 +00:00
# else
bool on ;
f . read ( on ) ;
# endif
2022-06-16 21:10:44 +00:00
}
2021-09-17 10:00:53 +00:00
2021-03-31 09:37:29 +00:00
if ( f . vernum > = 0xA848 ) {
int i ;
f . read ( i ) ;
while ( i ) {
callhooks ( hooks_loadmap , f , i ) ;
f . read ( i ) ;
}
}
else
callhooks ( hooks_loadmap_old , f ) ;
2020-09-21 10:02:07 +00:00
2019-01-28 21:28:17 +00:00
cellbyid . clear ( ) ;
2019-05-29 13:53:13 +00:00
restartGraph ( ) ;
2016-08-26 09:58:03 +00:00
bfs ( ) ;
2019-05-29 13:53:13 +00:00
game_active = true ;
}
2022-06-16 21:06:49 +00:00
void save_usershapes ( hstream & f ) {
2019-05-29 13:53:13 +00:00
int32_t n ;
# if CAP_POLY
for ( int i = 0 ; i < mapeditor : : USERSHAPEGROUPS ; i + + ) for ( auto usp : usershapes [ i ] ) {
usershape * us = usp . second ;
if ( ! us ) continue ;
for ( int l = 0 ; l < USERLAYERS ; l + + ) if ( isize ( us - > d [ l ] . list ) ) {
usershapelayer & ds ( us - > d [ l ] ) ;
f . write ( i ) ; f . write ( usp . first ) ; f . write ( l ) ;
f . write ( ds . zlevel ) ;
f . write ( ds . sym ) ; f . write ( ds . rots ) ; f . write ( ds . color ) ;
n = isize ( ds . list ) ; f . write ( n ) ;
f . write ( ds . shift ) ;
f . write ( ds . spin ) ;
for ( int i = 0 ; i < isize ( ds . list ) ; i + + ) f . write ( ds . list [ i ] ) ;
}
}
# endif
n = - 1 ; f . write ( n ) ;
}
2020-09-21 10:02:07 +00:00
EX bool saveMap ( const char * fname ) {
2019-05-29 13:53:13 +00:00
fhstream f ( fname , " wb " ) ;
if ( ! f . f ) return false ;
2022-06-16 21:06:49 +00:00
saveMap ( f ) ;
return true ;
}
EX void saveMap ( hstream & f ) {
f . write ( f . get_vernum ( ) ) ;
2019-05-29 13:53:13 +00:00
f . write ( dual : : state ) ;
2023-04-14 23:27:35 +00:00
# if CAP_PORTALS
2021-09-17 10:00:53 +00:00
int q = intra : : in ? isize ( intra : : data ) : 0 ;
f . write ( q ) ;
2022-02-17 20:00:10 +00:00
# else
int q = 0 ;
# endif
2021-09-17 10:00:53 +00:00
if ( q ) {
2023-04-14 23:27:35 +00:00
# if CAP_PORTALS
2021-09-17 10:00:53 +00:00
intra : : prepare_to_save ( ) ;
int qp = isize ( intra : : portals_to_save ) ;
f . write ( qp ) ;
for ( auto & ps : intra : : portals_to_save ) {
f . write ( ps . spin ) ;
f . write ( ps . mirrored ) ;
}
intra : : resetter ir ;
for ( int i = 0 ; i < q ; i + + ) { intra : : switch_to ( i ) ; save_only_map ( f ) ; }
2022-02-17 20:00:10 +00:00
# endif
2021-09-17 10:00:53 +00:00
}
else {
// make sure we save in correct order
if ( dual : : state ) dual : : switch_to ( 1 ) ;
dual : : split_or_do ( [ & ] { save_only_map ( f ) ; } ) ;
}
2019-05-29 13:53:13 +00:00
save_usershapes ( f ) ;
}
2020-09-21 10:02:07 +00:00
EX bool loadMap ( const string & fname ) {
2019-05-29 13:53:13 +00:00
fhstream f ( fname , " rb " ) ;
if ( ! f . f ) return false ;
2022-06-16 21:06:49 +00:00
return loadMap ( f ) ;
}
EX bool loadMap ( hstream & f ) {
2019-05-29 13:53:13 +00:00
f . read ( f . vernum ) ;
if ( f . vernum > 10505 & & f . vernum < 11000 )
f . vernum = 11005 ;
auto ds = dual : : state ;
if ( f . vernum > = 0xA61A )
f . read ( ds ) ;
else
ds = 0 ;
if ( ds = = 1 & & dual : : state = = 0 ) dual : : enable ( ) ;
if ( ds = = 0 & & dual : : state = = 1 ) dual : : disable ( ) ;
2021-09-17 10:00:53 +00:00
int q = 0 ;
if ( f . vernum > = 0xA907 ) {
f . read ( q ) ;
}
if ( q ) {
int qp ;
f . read ( qp ) ;
2023-04-14 23:27:35 +00:00
# if CAP_PORTALS
2021-09-17 10:00:53 +00:00
intra : : portals_to_save . resize ( qp ) ;
for ( auto & ps : intra : : portals_to_save ) {
f . read ( ps . spin ) ;
f . read ( ps . mirrored ) ;
println ( hlog , tie ( ps . spin , ps . mirrored ) ) ;
}
for ( int i = 0 ; i < q ; i + + ) {
intra : : in = true ; /* so that it knows to load portals */
2022-02-28 16:32:19 +00:00
game_active = false ;
2021-09-17 10:00:53 +00:00
load_only_map ( f ) ;
intra : : in = false ;
intra : : become ( ) ;
}
intra : : start ( ) ;
intra : : load_saved_portals ( ) ;
2022-02-17 20:00:10 +00:00
# endif
2021-09-17 10:00:53 +00:00
}
else {
dual : : split_or_do ( [ & ] { load_only_map ( f ) ; } ) ;
}
2019-08-02 15:58:00 +00:00
if ( dual : : state ) dual : : assign_landsides ( ) ;
2019-05-29 13:53:13 +00:00
if ( f . vernum > = 0xA61A )
load_usershapes ( f ) ;
2016-08-26 09:58:03 +00:00
return true ;
}
# endif
2020-08-02 00:04:18 +00:00
EX }
2016-08-26 09:58:03 +00:00
2020-08-02 00:04:18 +00:00
EX namespace mapeditor {
2016-08-26 09:58:03 +00:00
2019-08-09 23:15:41 +00:00
EX bool drawplayer = true ;
2016-08-26 09:58:03 +00:00
2019-08-09 23:15:41 +00:00
EX cell * drawcell ;
2017-07-04 13:38:33 +00:00
2017-07-22 23:33:27 +00:00
# if CAP_EDIT
2016-08-26 09:58:03 +00:00
int paintwhat = 0 ;
2022-10-25 22:19:22 +00:00
int paintwhat_alt_wall = 0 ;
2016-08-26 09:58:03 +00:00
int painttype = 0 ;
2019-02-28 16:06:53 +00:00
int paintstatueid = 0 ;
2016-08-26 09:58:03 +00:00
int radius = 0 ;
string paintwhat_str = " clear monster " ;
2017-11-12 23:37:18 +00:00
cellwalker copysource ;
2016-08-26 09:58:03 +00:00
int whichpart ;
2017-07-04 13:38:33 +00:00
2016-08-26 09:58:03 +00:00
const char * mapeditorhelp =
" This mode allows you to edit the map. \n \n "
" NOTE: Use at your own risk. Combinations which never "
" appear in the real game may work in an undefined way "
" (do not work, look strangely, give strange messages, or crash the game). \n \n "
" To get the most of this editor, "
" some knowledge of inner workings of HyperRogue is required. "
" Each cell has four main fields: land type, wall type, monster type, item type. "
" The same wall type (especially \" none \" , \" sea \" , or \" bonfire \" ) may look or "
" work a bit differently, based on the land it is in. Sometimes an object may "
" appear twice on the list due to subtle differences (for example, Demons could "
" move next turn or not). \n \n "
" Press w, i, l, or m to choose which aspect of cells to change, "
" then just click on the cells and they will change. Press 'c' while "
" hovering over a cell to copy that cell, this copies all information about it. "
" When copying large areas or placing multi-tile monsters, it might be important where "
" on the cell you are clicking. \n \n "
" You can also press 0-9 to apply your changes to a greater radius. "
" This also affects the copy/paste feature, allowing to copy a larger area. \n \n "
" Press F2 to save the current map (and F3 to load it). If you try this after "
" a long game of HyperRogue (without using Orbs of Safety), the filesize will "
" be very large! "
" Note however that large structures, such as "
" Great Walls, large circles and horocycles, are destroyed by this. \n \n "
" Press 'b' to mark cells as boundaries. Such cells, and cells beyond "
" them, are not copied by the copy/paste feature, nor saved by the "
" save feature. \n \n " ;
const char * patthelp =
" Press 'r' to choose a regular pattern. When a pattern is on, "
" editing a cell automatically edits all cells which are "
" equivalent according to this pattern. You can choose from "
" several patterns, and choose which symmetries matter "
" for equivalence. Also, you can press Space to switch between "
" the map and graphics editor quickly -- note that editing floors "
" with the graphics editor also adheres to the pattern. " ;
string mehelptext ( ) {
return XLAT ( mapeditorhelp ) + XLAT ( patthelp ) ;
}
struct undo_info {
cell * c ;
eWall w ;
eItem i ;
eMonster m ;
eLand l ;
char wparam ;
int32_t lparam ;
char dir ;
} ;
vector < undo_info > undo ;
bool checkEq ( undo_info & u ) {
return u . w = = u . c - > wall & & u . i = = u . c - > item & & u . m = = u . c - > monst & & u . l = = u . c - > land & &
u . dir = = u . c - > mondir & & u . lparam = = u . c - > landparam & & u . wparam = = u . c - > wparam ;
}
void saveUndo ( cell * c ) {
undo_info u ;
u . c = c ; u . w = c - > wall ; u . i = c - > item ; u . m = c - > monst ; u . l = c - > land ; u . dir = c - > mondir ;
u . wparam = c - > wparam ; u . lparam = c - > landparam ;
undo . push_back ( u ) ;
}
2018-06-22 12:47:24 +00:00
undo_info & lastUndo ( ) { return undo [ isize ( undo ) - 1 ] ; }
2016-08-26 09:58:03 +00:00
void undoLock ( ) {
2018-06-22 12:47:24 +00:00
if ( ! isize ( undo ) | | lastUndo ( ) . c ) {
2016-08-26 09:58:03 +00:00
undo_info i ; i . c = NULL ; undo . push_back ( i ) ;
}
}
void applyUndo ( ) {
2018-06-22 12:47:24 +00:00
while ( isize ( undo ) & & ! lastUndo ( ) . c ) undo . pop_back ( ) ;
while ( isize ( undo ) ) {
2016-08-26 09:58:03 +00:00
undo_info & i ( lastUndo ( ) ) ;
if ( ! i . c ) break ;
i . c - > wall = i . w ;
i . c - > item = i . i ;
i . c - > monst = i . m ;
i . c - > land = i . l ;
i . c - > mondir = i . dir ;
i . c - > wparam = i . wparam ;
i . c - > landparam = i . lparam ;
undo . pop_back ( ) ;
}
}
void checkUndo ( ) {
2018-06-22 12:47:24 +00:00
if ( checkEq ( undo [ isize ( undo ) - 1 ] ) ) undo . pop_back ( ) ;
2016-08-26 09:58:03 +00:00
}
int itc ( int k ) {
if ( k = = 0 ) return 0 ;
if ( k = = 1 ) return 0x40 ;
if ( k = = 2 ) return 0x80 ;
if ( k = = 3 ) return 0xFF ;
return 0x20 ;
}
bool choosefile = false ;
2019-08-10 20:30:02 +00:00
int editor_fsize ( ) {
return min ( vid . fsize + 5 , ( vid . yres - 16 ) / 32 ) ;
}
2016-08-26 09:58:03 +00:00
void displayFunctionKeys ( ) {
2019-08-10 20:30:02 +00:00
int fs = editor_fsize ( ) ;
2024-06-28 21:33:28 +00:00
displayButton ( 8 , vid . yres - 8 - fs * 2 , XLAT ( " ESC = menu " ) , SDLK_ESCAPE , 0 ) ;
}
EX void map_editor_menu ( ) {
dialog : : add_key_action ( ' - ' , [ ] {
if ( ! holdmouse ) undoLock ( ) ;
if ( mouseover ) {
allInPattern ( mouseover_cw ( false ) ) ;
if ( ! ( GDIM = = 3 & & building_mode ) )
holdmouse = true ;
}
} ) ;
if ( anyshiftclick ) {
dialog : : addInfo (
( painttype = = 6 & & ( GDIM = = 3 ) ) ? " wall " :
painttype = = 3 ? XLATN ( winf [ paintwhat_alt_wall ] . name ) : " clear " ) ;
}
else
dialog : : addInfo ( paintwhat_str ) ;
dialog : : addInfo ( XLAT ( " use at your own risk! " ) , 0x800000 ) ;
dialog : : addSelItem ( XLAT ( " radius " ) , its ( radius ) , ' 0 ' ) ;
dialog : : add_action ( [ ] {
dialog : : editNumber ( radius , 0 , 9 , 1 , 1 , XLAT ( " radius " ) , " " ) ;
} ) ;
dialog : : addBoolItem ( XLAT ( " boundary " ) , painttype = = 5 , ' b ' ) ;
dialog : : add_action ( [ ] { painttype = 5 , paintwhat_str = XLAT ( " boundary " ) ; } ) ;
2024-06-29 08:03:42 +00:00
dialog : : addBoolItem ( XLAT ( " monsters " ) , painttype = = 0 , ' m ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] { pushScreen ( showList ) , painttype = 0 , dialog : : infix = " " ; } ) ;
2024-06-29 08:03:42 +00:00
dialog : : addBoolItem ( XLAT ( " items " ) , painttype = = 1 , ' i ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] { pushScreen ( showList ) , painttype = 1 , dialog : : infix = " " ; } ) ;
2024-06-29 08:03:42 +00:00
dialog : : addBoolItem ( XLAT ( " lands " ) , painttype = = 2 , ' l ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] { pushScreen ( showList ) , painttype = 2 , dialog : : infix = " " ; } ) ;
2024-06-29 08:03:42 +00:00
dialog : : addBoolItem ( XLAT ( " walls " ) , painttype = = 3 , ' w ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] { pushScreen ( showList ) , painttype = 3 , dialog : : infix = " " ; } ) ;
dialog : : addBoolItem ( XLAT ( " paint " ) , painttype = = 6 , ' w ' ) ;
dialog : : add_action ( [ ] {
painttype = 6 ;
paintwhat_str = " paint " ;
dialog : : openColorDialog ( ( unsigned & ) ( paintwhat = ( painttype = = 6 ? paintwhat : 0x808080 ) ) ) ;
} ) ;
dialog : : addBoolItem ( XLAT ( " copy " ) , painttype = = 4 , ' c ' ) ;
dialog : : add_action ( [ ] {
if ( mouseover ) { copysource = mouseover_cw ( true ) ; painttype = 4 ; paintwhat_str = XLAT ( " copying " ) ; }
else { painttype = 7 ; paintwhat_str = XLAT ( " select area to copy " ) ; }
} ) ;
dialog : : addBoolItem ( XLAT ( " teleport player " ) , painttype = = 8 , ' t ' ) ;
dialog : : add_action ( [ ] {
if ( mouseover ) {
playermoved = true ;
cwt = mouseover_cw ( true ) ;
}
else { painttype = 8 ; paintwhat_str = XLAT ( " teleport where " ) ; }
} ) ;
if ( painttype = = 4 ) {
dialog : : addBoolItem_action ( XLAT ( " flip " ) , copysource . mirrored , ' f ' ) ;
}
else if ( painttype = = 3 ) {
dialog : : addItem ( XLAT ( " set Shift+click " ) , ' z ' ) ;
dialog : : add_action ( [ ] { paintwhat_alt_wall = paintwhat ; } ) ;
}
else dialog : : addInfo ( XLAT ( " press Shift to clear " ) ) ;
dialog : : addItem ( XLAT ( " undo " ) , ' u ' ) ;
dialog : : add_action ( applyUndo ) ;
if ( WDIM = = 3 )
dialog : : addBoolItem_action ( XLAT ( " build on walls " ) , building_mode , ' B ' ) ;
else dialog : : addBreak ( 100 ) ;
dialog : : addBreak ( 800 ) ;
}
void editor_menu ( int i ) {
cmode | = sm : : DIALOG_STRICT_X ;
if ( i = = 1 ) dialog : : init ( XLAT ( " map editor " ) ) ;
if ( i = = 2 ) dialog : : init ( XLAT ( " shape editor " ) ) ;
if ( i = = 3 ) dialog : : init ( intexture ? XLAT ( " texture editor " ) : XLAT ( " drawing tool " ) ) ;
if ( i = = 1 ) map_editor_menu ( ) ;
else draw_editor_menu ( ) ;
dialog : : addBreak ( 50 ) ;
dialog : : addItem ( " switch the editor mode " , ' ' ) ;
dialog : : add_action ( [ i ] {
if ( i = = 2 & & ! ( cheater | | autocheat ) ) {
dialog : : cheat_if_confirmed ( [ ] { cheater + + ; } ) ;
return ;
}
if ( i = = 1 ) {
popScreen ( ) ;
pushScreen ( showDrawEditor ) ;
initdraw ( mouseover ? mouseover : cwt . at ) ;
drawing_tool = false ;
}
if ( i = = 2 ) drawing_tool = true ;
if ( i = = 3 ) {
popScreen ( ) ;
pushScreen ( showMapEditor ) ;
}
} ) ;
dialog : : addItem ( " help " , SDLK_F1 ) ;
dialog : : add_action ( [ i ] {
gotoHelp ( i = = 1 ? mehelptext ( ) : drawhelptext ( ) ) ;
} ) ;
if ( i = = 2 ) {
dialog : : addItem ( XLAT ( " save only the shapes " ) , SDLK_F2 ) ;
dialog : : add_action ( [ ] {
dialog : : openFileDialog ( picfile , XLAT ( " pics to save: " ) , " .pic " ,
[ ] ( ) {
return savePicFile ( picfile ) ;
} ) ;
} ) ;
dialog : : addItem ( XLAT ( " load only the shapes " ) , SDLK_F3 ) ;
dialog : : add_action ( [ ] {
dialog : : openFileDialog ( picfile , XLAT ( " pics to load: " ) , " .pic " ,
[ ] ( ) {
return loadPicFile ( picfile ) ;
} ) ;
} ) ;
}
else {
dialog : : addItem ( XLAT ( " save the map " ) , SDLK_F2 ) ;
dialog : : add_action ( save_level ) ;
dialog : : addItem ( XLAT ( " load the map " ) , SDLK_F3 ) ;
dialog : : add_action ( load_level ) ;
}
if ( ! show_menu ) displayFunctionKeys ( ) ;
dialog : : addItem ( XLAT ( " reset " ) , SDLK_F5 ) ;
dialog : : add_action ( [ i ] {
if ( i = = 1 ) dialog : : push_confirm_dialog ( [ ] { restart_game ( ) ; } , XLAT ( " Are you sure you want to clear the map? " ) ) ;
if ( i = = 3 ) dialog : : push_confirm_dialog ( [ ] {
stop_game ( ) ;
enable_canvas ( ) ;
canvas_default_wall = waInvisibleFloor ;
ccolor : : set_plain ( 0xFFFFFF ) ;
dtcolor = ( forecolor < < 8 ) | 255 ;
drawplayer = false ;
vid . use_smart_range = 2 ;
start_game ( ) ;
} ,
XLAT ( " Are you sure you want to restart? This will let you draw on a blank screen. " )
) ;
if ( i = = 2 ) dialog : : push_confirm_dialog ( [ ] {
for ( int i = 0 ; i < USERSHAPEGROUPS ; i + + ) {
for ( auto us : usershapes [ i ] )
if ( us . second ) delete us . second ;
usershapes [ i ] . clear ( ) ;
}
} ,
XLAT ( " Are you sure you want to restart? This will erase all shapes. " )
) ;
} ) ;
dialog : : addBoolItem_action ( " draw the player " , drawplayer , SDLK_F7 ) ;
dialog : : addItem ( XLAT ( " map settings " ) , SDLK_F8 ) ;
dialog : : add_action_push ( map_settings ) ;
dialog : : addItem ( ( i = = 2 & & drawcellShapeGroup ( ) = = sgFloor ) ? XLAT ( " complex tessellations " ) : XLAT ( " patterns " ) , ' r ' ) ;
dialog : : add_action_push ( patterns : : showPattern ) ;
dialog : : addItem ( " hide menu " , ' h ' ) ;
dialog : : add_action ( [ ] { show_menu = ! show_menu ; } ) ;
dialog : : addBack ( ) ;
if ( show_menu ) { dialog : : display ( ) ; }
else dialog : : add_key_action ( SDLK_ESCAPE , [ ] { show_menu = true ; } ) ;
2016-08-26 09:58:03 +00:00
}
2019-12-06 13:03:54 +00:00
Remove USE_UNORDERED_MAP because it has bit-rotted.
Trying to compile with `-DUSE_UNORDERED_MAP` produces lots of compiler errors
like these, because of missing `std::hash` specializations.
Also, `#define unordered_map map` is just evil!
```
./nonisotropic.cpp:875:36: note: in instantiation of template class 'std::__1::unordered_map<hr::nilv::mvec, hr::heptagon *,
std::__1::hash<hr::nilv::mvec>, std::__1::equal_to<hr::nilv::mvec>, std::__1::allocator<std::__1::pair<const hr::nilv::mvec, hr::heptagon
*> > >' requested here
unordered_map<mvec, heptagon*> at;
^
./nonisotropic.cpp:239:58: note: in instantiation of template class 'std::__1::unordered_map<std::__1::pair<hr::heptagon *, hr::heptagon *>,
hr::heptagon *, std::__1::hash<std::__1::pair<hr::heptagon *, hr::heptagon *> >, std::__1::equal_to<std::__1::pair<hr::heptagon *,
hr::heptagon *> >, std::__1::allocator<std::__1::pair<const std::__1::pair<hr::heptagon *, hr::heptagon *>, hr::heptagon *> > >'
requested here
unordered_map<pair<heptagon*, heptagon*>, heptagon*> at;
^
./nonisotropic.cpp:457:49: error: no matching member function for call to 'iadj'
while(h1->distance < h2->distance) back = iadj(h2, down) * back, h2 = h2->cmove(down);
^~~~
cell.cpp:42:15: note: candidate function not viable: no known conversion from 'hr::sn::hrmap_solnih' to 'hr::hrmap' for object argument
transmatrix iadj(heptagon *h, int d) {
^
cell.cpp:41:22: note: candidate function not viable: no known conversion from 'hr::sn::hrmap_solnih' to 'hr::hrmap' for object argument
struct transmatrix iadj(cell *c, int i) { cell *c1 = c->cmove(i); return adj(c1, c->c.spin(i)); }
^
```
2020-09-25 03:15:19 +00:00
EX set < cell * > affected ;
EX set < int > affected_id ;
2016-08-26 09:58:03 +00:00
2019-08-09 23:15:41 +00:00
EX void showMapEditor ( ) {
2022-05-06 17:12:02 +00:00
cmode = sm : : MAP | sm : : PANNING ;
2024-06-28 21:33:28 +00:00
if ( show_menu ) cmode | = sm : : SIDE ;
2022-10-27 07:45:03 +00:00
if ( building_mode ) {
if ( anyshiftclick ) cmode | = sm : : EDIT_INSIDE_WALLS ;
else cmode | = sm : : EDIT_BEFORE_WALLS ;
}
2022-07-05 09:51:06 +00:00
gamescreen ( ) ;
2017-07-10 18:47:38 +00:00
2019-08-10 20:30:02 +00:00
int fs = editor_fsize ( ) ;
2017-07-10 18:47:38 +00:00
2019-12-06 13:03:54 +00:00
affected . clear ( ) ;
affected_id . clear ( ) ;
if ( lmouseover ) {
celllister cl ( lmouseover , radius , 10000 , nullptr ) ;
for ( cell * c : cl . lst ) affected . insert ( c ) , affected_id . insert ( patterns : : getpatterninfo0 ( c ) . id ) ;
}
2017-07-10 18:47:38 +00:00
getcstat = ' - ' ;
2024-06-28 21:33:28 +00:00
if ( ! show_menu ) {
if ( anyshiftclick ) {
displayfr ( 8 , 8 + fs , 2 , vid . fsize ,
( painttype = = 6 & & ( GDIM = = 3 ) ) ? " wall " :
painttype = = 3 ? XLATN ( winf [ paintwhat_alt_wall ] . name ) : " clear " ,
forecolor , 0 ) ;
}
else
displayfr ( 8 , 8 + fs , 2 , vid . fsize , paintwhat_str , forecolor , 0 ) ;
2022-10-25 22:19:22 +00:00
}
2017-12-03 17:24:34 +00:00
2024-06-28 21:33:28 +00:00
editor_menu ( 1 ) ;
if ( mouseover ) forCellCM ( c , mouseover ) { }
2016-08-26 09:58:03 +00:00
}
int spillinc ( ) {
if ( radius > = 2 ) return 0 ;
if ( anyshiftclick ) return - 1 ;
return 1 ;
}
2019-08-09 23:15:41 +00:00
EX eShapegroup drawcellShapeGroup ( ) {
2018-08-27 17:27:35 +00:00
if ( drawcell = = cwt . at & & drawplayer ) return sgPlayer ;
2019-02-28 16:06:53 +00:00
if ( drawcell - > wall = = waEditStatue ) return sgWall ;
2018-08-27 17:27:35 +00:00
if ( drawcell - > monst ) return sgMonster ;
if ( drawcell - > item ) return sgItem ;
return sgFloor ;
2016-08-26 09:58:03 +00:00
}
2019-08-09 23:15:41 +00:00
EX int drawcellShapeID ( ) {
2018-08-17 22:46:45 +00:00
if ( drawcell = = cwt . at & & drawplayer ) return vid . cs . charid ;
2019-02-28 16:06:53 +00:00
if ( drawcell - > wall = = waEditStatue ) return drawcell - > wparam ;
2016-08-26 09:58:03 +00:00
if ( drawcell - > monst ) return drawcell - > monst ;
if ( drawcell - > item ) return drawcell - > item ;
2018-08-27 17:27:35 +00:00
auto si = patterns : : getpatterninfo0 ( drawcell ) ;
2018-08-30 17:14:04 +00:00
return si . id ;
2016-08-26 09:58:03 +00:00
}
2018-08-28 11:45:11 +00:00
bool editingShape ( eShapegroup group , int id ) {
2016-08-26 09:58:03 +00:00
if ( group ! = mapeditor : : drawcellShapeGroup ( ) ) return false ;
2017-12-09 07:06:41 +00:00
return id = = drawcellShapeID ( ) ;
2016-08-26 09:58:03 +00:00
}
2017-11-12 23:37:18 +00:00
void editCell ( const pair < cellwalker , cellwalker > & where ) {
2018-08-17 22:46:45 +00:00
cell * c = where . first . at ;
2017-11-12 23:37:18 +00:00
int cdir = where . first . spin ;
2016-08-26 09:58:03 +00:00
saveUndo ( c ) ;
switch ( painttype ) {
2018-09-12 22:35:21 +00:00
case 0 : {
2022-10-25 22:19:22 +00:00
if ( anyshiftclick ) { c - > monst = moNone ; mirror : : destroyKilled ( ) ; break ; }
2018-09-12 22:35:21 +00:00
eMonster last = c - > monst ;
2016-08-26 09:58:03 +00:00
c - > monst = eMonster ( paintwhat ) ;
c - > hitpoints = 3 ;
c - > stuntime = 0 ;
c - > mondir = cdir ;
2018-08-17 22:46:45 +00:00
if ( ( isWorm ( c ) | | isIvy ( c ) | | isMutantIvy ( c ) ) & & c - > move ( cdir ) & &
! isWorm ( c - > move ( cdir ) ) & & ! isIvy ( c - > move ( cdir ) ) )
2016-08-26 09:58:03 +00:00
c - > mondir = NODIR ;
2018-06-17 10:03:39 +00:00
if ( c - > monst = = moMimic ) {
c - > monst = moNone ;
mirror : : createMirror ( cellwalker ( c , cdir , true ) , 0 ) ;
c - > monst = moMimic ;
}
2018-09-12 22:35:21 +00:00
if ( c - > monst = = moTortoise & & last = = moTortoise ) {
cell * c1 = c ;
for ( int i = 0 ; i < 100 ; i + + ) c1 = c1 - > cmove ( hrand ( c1 - > type ) ) ;
2019-01-16 23:48:55 +00:00
tortoise : : emap [ c ] = tortoise : : getRandomBits ( ) ;
2018-09-12 22:35:21 +00:00
}
2021-05-31 18:43:50 +00:00
if ( isDie ( c - > monst ) ) {
if ( ! dice : : generate_random ( c ) ) c - > monst = moNone ;
}
2021-09-30 11:16:22 +00:00
mirror : : destroyKilled ( ) ;
2016-08-26 09:58:03 +00:00
break ;
2018-09-12 22:35:21 +00:00
}
case 1 : {
2022-10-25 22:19:22 +00:00
if ( anyshiftclick ) { c - > item = itNone ; break ; }
2018-09-12 22:35:21 +00:00
eItem last = c - > item ;
2016-08-26 09:58:03 +00:00
c - > item = eItem ( paintwhat ) ;
2017-03-23 10:53:57 +00:00
if ( c - > item = = itBabyTortoise )
2018-09-12 22:35:21 +00:00
tortoise : : babymap [ c ] = getBits ( c ) ^ ( last = = itBabyTortoise ? tortoise : : getRandomBits ( ) : 0 ) ;
2016-08-26 09:58:03 +00:00
break ;
2018-09-12 22:35:21 +00:00
}
2016-08-26 09:58:03 +00:00
case 2 : {
2022-10-26 22:02:10 +00:00
if ( anyshiftclick ) { c - > land = laNone ; c - > wall = waNone ; map_version + + ; break ; }
2016-08-26 09:58:03 +00:00
eLand last = c - > land ;
c - > land = eLand ( paintwhat ) ;
if ( isIcyLand ( c ) & & isIcyLand ( last ) )
HEAT ( c ) + = spillinc ( ) / 100. ;
else if ( last = = laDryForest & & c - > land = = laDryForest )
c - > landparam + = spillinc ( ) ;
else if ( last = = laOcean & & c - > land = = laOcean )
c - > landparam + = spillinc ( ) ;
else if ( last = = laHive & & c - > land = = laHive )
c - > landparam + = spillinc ( ) ;
else
c - > landparam = 0 ;
break ;
}
case 3 : {
eWall last = c - > wall ;
2022-10-25 22:19:22 +00:00
c - > wall = eWall ( anyshiftclick ? paintwhat_alt_wall : paintwhat ) ;
2022-10-26 22:02:10 +00:00
map_version + + ;
2016-08-26 09:58:03 +00:00
if ( last ! = c - > wall ) {
if ( hasTimeout ( c ) )
c - > wparam = 10 ;
else if ( c - > wall = = waWaxWall )
c - > landparam = hrand ( 0xFFFFFF + 1 ) ;
}
else if ( hasTimeout ( c ) )
c - > wparam + = spillinc ( ) ;
2019-02-28 16:06:53 +00:00
if ( c - > wall = = waEditStatue ) {
c - > wparam = paintstatueid ;
c - > mondir = cdir ;
}
2021-05-31 18:43:50 +00:00
if ( isDie ( c - > wall ) ) {
if ( ! dice : : generate_random ( c ) ) c - > wall = waNone ;
}
2016-08-26 09:58:03 +00:00
break ;
}
case 5 :
2022-10-26 22:02:10 +00:00
map_version + + ;
2016-08-26 09:58:03 +00:00
c - > land = laNone ;
c - > wall = waNone ;
c - > item = itNone ;
c - > monst = moNone ;
c - > landparam = 0 ;
// c->tmp = -1;
break ;
case 6 :
2022-10-26 22:02:10 +00:00
map_version + + ;
2016-08-26 09:58:03 +00:00
c - > land = laCanvas ;
2022-10-25 22:19:22 +00:00
c - > wall = ( ( GDIM = = 3 ) ^ anyshiftclick ) ? waWaxWall : waNone ;
2017-03-23 10:53:57 +00:00
c - > landparam = paintwhat > > 8 ;
2016-08-26 09:58:03 +00:00
break ;
2020-09-15 18:12:13 +00:00
case 4 : {
2022-10-26 22:02:10 +00:00
map_version + + ;
2018-08-17 22:46:45 +00:00
cell * copywhat = where . second . at ;
2016-08-26 09:58:03 +00:00
c - > wall = copywhat - > wall ;
c - > item = copywhat - > item ;
c - > land = copywhat - > land ;
c - > monst = copywhat - > monst ;
c - > landparam = copywhat - > landparam ;
c - > wparam = copywhat - > wparam ;
2017-11-12 23:37:18 +00:00
c - > hitpoints = copywhat - > hitpoints ;
c - > stuntime = copywhat - > stuntime ;
2021-05-31 18:43:50 +00:00
if ( dice : : on ( c ) ) dice : : data [ c ] = dice : : data [ copywhat ] ;
2016-08-26 09:58:03 +00:00
if ( copywhat - > mondir = = NODIR ) c - > mondir = NODIR ;
2020-01-18 15:03:32 +00:00
else c - > mondir = gmod ( ( where . first . mirrored = = where . second . mirrored ? 1 : - 1 ) * ( copywhat - > mondir - where . second . spin ) + cdir , c - > type ) ;
2016-08-26 09:58:03 +00:00
break ;
2020-09-15 18:12:13 +00:00
}
case 7 :
if ( c ) {
copysource = c ;
painttype = 4 ;
paintwhat_str = XLAT ( " copying " ) ;
}
break ;
2024-06-28 21:33:28 +00:00
case 8 :
playermoved = true ;
cwt = c ;
break ;
2016-08-26 09:58:03 +00:00
}
checkUndo ( ) ;
}
2017-11-12 23:37:18 +00:00
vector < pair < cellwalker , cellwalker > > spill_list ;
2018-06-28 11:35:03 +00:00
void list_spill ( cellwalker tgt , cellwalker src , manual_celllister & cl ) {
2018-06-29 10:16:35 +00:00
spill_list . clear ( ) ;
2017-11-12 23:37:18 +00:00
spill_list . emplace_back ( tgt , src ) ;
2020-11-05 15:05:06 +00:00
if ( painttype = = 7 ) return ;
2017-11-12 23:37:18 +00:00
int crad = 0 , nextstepat = 0 ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( spill_list ) ; i + + ) {
2017-11-12 23:37:18 +00:00
if ( i = = nextstepat ) {
2018-06-22 12:47:24 +00:00
crad + + ; nextstepat = isize ( spill_list ) ;
2017-11-12 23:37:18 +00:00
if ( crad > radius ) break ;
}
auto sd = spill_list [ i ] ;
2018-08-17 22:46:45 +00:00
for ( int i = 0 ; i < sd . first . at - > type ; i + + ) {
2017-11-12 23:37:18 +00:00
auto sd2 = sd ;
2018-03-24 11:59:01 +00:00
sd2 . first = sd2 . first + i + wstep ;
2018-08-17 22:46:45 +00:00
if ( ! cl . add ( sd2 . first . at ) ) continue ;
if ( sd2 . second . at ) {
2018-03-24 11:59:01 +00:00
sd2 . second = sd2 . second + i + wstep ;
2018-08-17 22:46:45 +00:00
if ( sd2 . second . at - > land = = laNone ) continue ;
2017-11-12 23:37:18 +00:00
}
spill_list . push_back ( sd2 ) ;
}
}
}
2018-01-05 13:19:22 +00:00
2018-06-28 11:35:03 +00:00
void editAt ( cellwalker where , manual_celllister & cl ) {
2017-11-12 23:37:18 +00:00
if ( painttype = = 4 & & radius ) {
2018-08-17 22:46:45 +00:00
if ( where . at - > type ! = copysource . at - > type ) return ;
2017-11-12 23:37:18 +00:00
if ( where . spin < 0 ) where . spin = 0 ;
2018-08-28 15:17:34 +00:00
if ( BITRUNCATED & & ! ctof ( mouseover ) & & ( ( where . spin & 1 ) ! = ( copysource . spin & 1 ) ) )
2018-03-24 11:59:01 +00:00
where + = 1 ;
2017-11-12 23:37:18 +00:00
}
2018-08-17 22:46:45 +00:00
if ( painttype ! = 4 ) copysource . at = NULL ;
2018-06-28 10:59:35 +00:00
list_spill ( where , copysource , cl ) ;
2017-11-12 23:37:18 +00:00
for ( auto & st : spill_list )
editCell ( st ) ;
}
2024-06-28 21:33:28 +00:00
EX void allInPattern ( cellwalker where ) {
2016-08-26 09:58:03 +00:00
2018-06-28 11:35:03 +00:00
manual_celllister cl ;
2023-03-11 16:22:59 +00:00
bool call_editAt = ! patterns : : whichPattern ;
# if CAP_TEXTURE
call_editAt | = ( texture : : config . tstate = = texture : : tsActive ) ;
# endif
if ( call_editAt ) {
2018-06-28 10:59:35 +00:00
editAt ( where , cl ) ;
2016-08-26 09:58:03 +00:00
return ;
}
2018-08-17 22:46:45 +00:00
cl . add ( where . at ) ;
2016-08-26 09:58:03 +00:00
int at = 0 ;
2018-06-28 10:59:35 +00:00
while ( at < isize ( cl . lst ) ) {
cell * c2 = cl . lst [ at ] ;
2016-08-26 09:58:03 +00:00
at + + ;
2018-06-28 10:59:35 +00:00
forCellEx ( c3 , c2 ) cl . add ( c3 ) ;
2017-11-12 23:37:18 +00:00
}
2018-08-17 22:46:45 +00:00
auto si = patterns : : getpatterninfo0 ( where . at ) ;
2017-11-12 23:37:18 +00:00
int cdir = where . spin ;
2017-12-09 07:06:41 +00:00
if ( cdir > = 0 ) cdir = cdir - si . dir ;
2017-11-12 23:37:18 +00:00
2018-06-28 10:59:35 +00:00
for ( cell * c2 : cl . lst ) {
2017-12-09 07:06:41 +00:00
auto si2 = patterns : : getpatterninfo0 ( c2 ) ;
if ( si2 . id = = si . id ) {
2019-08-09 12:39:21 +00:00
editAt ( cellwalker ( c2 , cdir > = 0 ? cdir + si2 . dir : - 1 ) , cl ) ;
2017-12-09 07:06:41 +00:00
modelcell [ si2 . id ] = c2 ;
2016-08-26 09:58:03 +00:00
}
2017-12-09 07:06:41 +00:00
}
2016-08-26 09:58:03 +00:00
}
2024-06-28 21:33:28 +00:00
EX cellwalker mouseover_cw ( bool fix ) {
2017-11-12 23:37:18 +00:00
int d = neighborId ( mouseover , mouseover2 ) ;
if ( d = = - 1 & & fix ) d = hrand ( mouseover - > type ) ;
return cellwalker ( mouseover , d ) ;
}
2020-04-17 18:04:33 +00:00
2024-06-28 21:33:28 +00:00
EX void save_level ( ) {
2020-09-21 10:02:23 +00:00
# if ISWEB
mapstream : : saveMap ( " web.lev " ) ;
offer_download ( " web.lev " , " mime/type " ) ;
# else
2020-04-17 18:04:33 +00:00
dialog : : openFileDialog ( levelfile , XLAT ( " level to save: " ) , " .lev " , [ ] ( ) {
if ( mapstream : : saveMap ( levelfile . c_str ( ) ) ) {
addMessage ( XLAT ( " Map saved to %1 " , levelfile ) ) ;
return true ;
}
else {
addMessage ( XLAT ( " Failed to save map to %1 " , levelfile ) ) ;
return false ;
}
} ) ;
2020-09-21 10:02:23 +00:00
# endif
2020-04-17 18:04:33 +00:00
}
2024-06-28 21:33:28 +00:00
EX void load_level ( ) {
2020-09-21 10:02:23 +00:00
# if ISWEB
offer_choose_file ( [ ] {
mapstream : : loadMap ( " data.txt " ) ;
} ) ;
# else
dialog : : openFileDialog ( levelfile , XLAT ( " level to load: " ) , " .lev " , [ ] ( ) {
2020-04-17 18:04:33 +00:00
if ( mapstream : : loadMap ( levelfile . c_str ( ) ) ) {
addMessage ( XLAT ( " Map loaded from %1 " , levelfile ) ) ;
return true ;
}
else {
addMessage ( XLAT ( " Failed to load map from %1 " , levelfile ) ) ;
return false ;
}
} ) ;
2020-09-21 10:02:23 +00:00
# endif
2020-04-17 18:04:33 +00:00
}
2017-11-12 23:37:18 +00:00
2024-06-28 21:33:28 +00:00
EX void showList ( ) {
2022-10-21 09:28:37 +00:00
string caption ;
2017-12-09 03:01:56 +00:00
dialog : : v . clear ( ) ;
2017-12-09 02:48:30 +00:00
if ( painttype = = 4 ) painttype = 0 ;
switch ( painttype ) {
case 0 :
2022-10-21 09:28:37 +00:00
caption = " monsters " ;
2017-12-09 02:48:30 +00:00
for ( int i = 0 ; i < motypes ; i + + ) {
eMonster m = eMonster ( i ) ;
if (
m = = moTongue | | m = = moPlayer | | m = = moFireball | | m = = moBullet | |
m = = moFlailBullet | | m = = moShadow | | m = = moAirball | |
m = = moWolfMoved | | m = = moGolemMoved | |
m = = moTameBomberbirdMoved | | m = = moKnightMoved | |
m = = moDeadBug | | m = = moLightningBolt | | m = = moDeadBird | |
m = = moMouseMoved | | m = = moPrincessMoved | | m = = moPrincessArmedMoved ) ;
2017-12-09 03:01:56 +00:00
else if ( m = = moDragonHead ) dialog : : vpush ( i , " Dragon Head " ) ;
else dialog : : vpush ( i , minf [ i ] . name ) ;
2017-12-09 02:48:30 +00:00
}
break ;
case 1 :
2022-10-21 09:28:37 +00:00
caption = " items " ;
2017-12-09 03:01:56 +00:00
for ( int i = 0 ; i < ittypes ; i + + ) dialog : : vpush ( i , iinf [ i ] . name ) ;
2017-12-09 02:48:30 +00:00
break ;
case 2 :
2022-10-21 09:28:37 +00:00
caption = " lands " ;
2017-12-09 03:01:56 +00:00
for ( int i = 0 ; i < landtypes ; i + + ) dialog : : vpush ( i , linf [ i ] . name ) ;
2017-12-09 02:48:30 +00:00
break ;
case 3 :
2022-10-21 09:28:37 +00:00
caption = " walls " ;
2017-12-09 03:01:56 +00:00
for ( int i = 0 ; i < walltypes ; i + + ) if ( i ! = waChasmD ) dialog : : vpush ( i , winf [ i ] . name ) ;
2017-12-09 02:48:30 +00:00
break ;
}
// sort(v.begin(), v.end());
2017-12-09 03:01:56 +00:00
if ( dialog : : infix ! = " " ) mouseovers = dialog : : infix ;
2017-12-09 02:48:30 +00:00
2022-10-21 09:28:37 +00:00
cmode = 0 ;
gamescreen ( ) ;
dialog : : init ( caption ) ;
if ( dialog : : infix ! = " " ) mouseovers = dialog : : infix ;
dialog : : addBreak ( 50 ) ;
dialog : : start_list ( 900 , 900 , ' 1 ' ) ;
2017-12-09 02:48:30 +00:00
2022-10-21 09:28:37 +00:00
for ( auto & vi : dialog : : v ) {
dialog : : addItem ( vi . first , dialog : : list_fake_key + + ) ;
dialog : : add_action ( [ & vi ] {
paintwhat = vi . second ;
paintwhat_str = vi . first ;
2019-02-28 16:06:53 +00:00
2017-12-09 02:48:30 +00:00
mousepressed = false ;
popScreen ( ) ;
2019-02-28 16:06:53 +00:00
if ( painttype = = 3 & & paintwhat = = waEditStatue )
dialog : : editNumber ( paintstatueid , 0 , 127 , 1 , 1 , XLAT1 ( " editable statue " ) ,
XLAT ( " These statues are designed to have their graphics edited in the Vector Graphics Editor. Each number has its own, separate graphics. " )
) ;
2022-10-21 09:28:37 +00:00
} ) ;
}
dialog : : end_list ( ) ;
dialog : : addBreak ( 50 ) ;
2022-10-21 10:47:20 +00:00
dialog : : addInfo ( XLAT ( " press letters to search " ) ) ;
2022-10-21 09:28:37 +00:00
dialog : : addBack ( ) ;
dialog : : display ( ) ;
keyhandler = [ ] ( int sym , int uni ) {
dialog : : handleNavigation ( sym , uni ) ;
2022-10-21 10:47:20 +00:00
if ( dialog : : editInfix ( uni ) ) dialog : : list_skip = 0 ;
2017-12-09 02:48:30 +00:00
else if ( doexiton ( sym , uni ) ) popScreen ( ) ;
} ;
}
2018-07-22 10:50:03 +00:00
2016-08-26 09:58:03 +00:00
// VECTOR GRAPHICS EDITOR
const char * drawhelp =
" In this mode you can draw your own player characters, "
" floors, monsters, and items. Press 'e' while hovering over "
" an object to edit it. Start drawing shapes with 'n', and "
" add extra vertices with 'a'. Press 0-9 to draw symmetric "
" pictures easily. More complex pictures can "
" be created by using several layers ('l'). See the edges of "
" the screen for more keys. " ;
2024-06-28 21:33:28 +00:00
EX string drawhelptext ( ) {
2017-07-10 18:47:38 +00:00
return XLAT ( drawhelp ) ;
2016-08-26 09:58:03 +00:00
}
2024-06-28 21:33:28 +00:00
EX int dslayer ;
2016-08-26 09:58:03 +00:00
bool coloring ;
2024-06-28 21:33:28 +00:00
EX color_t colortouse = 0xC0C0C0FFu ;
2017-10-04 19:25:40 +00:00
// fake key sent to change the color
2023-08-23 17:44:37 +00:00
static constexpr int COLORKEY = ( - 10000 ) ;
2016-08-26 09:58:03 +00:00
2020-07-27 16:49:04 +00:00
EX shiftmatrix drawtrans , drawtransnew ;
2016-08-26 09:58:03 +00:00
2019-02-17 17:41:40 +00:00
# if CAP_POLY
2016-08-26 09:58:03 +00:00
void loadShape ( int sg , int id , hpcshape & sh , int d , int layer ) {
usershapelayer * dsCur = & usershapes [ sg ] [ id ] - > d [ layer ] ;
dsCur - > list . clear ( ) ;
dsCur - > sym = d = = 2 ;
for ( int i = sh . s ; i < sh . s + ( sh . e - sh . s ) / d ; i + + )
2019-05-26 16:04:02 +00:00
dsCur - > list . push_back ( cgi . hpc [ i ] ) ;
2016-08-26 09:58:03 +00:00
}
2019-02-17 17:41:40 +00:00
# endif
2016-08-26 09:58:03 +00:00
2020-07-27 16:49:04 +00:00
EX void drawGhosts ( cell * c , const shiftmatrix & V , int ct ) {
2019-12-06 13:03:54 +00:00
if ( ! ( cmode & sm : : MAP ) ) return ;
if ( darken ! = 0 ) return ;
if ( GDIM = = 2 & & mouseout ( ) ) return ;
if ( patterns : : whichPattern ) {
if ( ! affected_id . count ( patterns : : getpatterninfo0 ( c ) . id ) ) return ;
}
else {
if ( ! affected . count ( c ) ) return ;
2019-10-25 10:44:41 +00:00
}
2019-12-06 13:03:54 +00:00
queuecircleat1 ( c , V , .78 , 0x00FFFFFF ) ;
2016-08-26 09:58:03 +00:00
}
2024-06-28 21:33:28 +00:00
EX hyperpoint ccenter = C02 ;
EX hyperpoint coldcenter = C02 ;
2016-08-26 09:58:03 +00:00
2024-06-28 21:33:28 +00:00
EX unsigned gridcolor = 0xC0C0C040 ;
2017-12-22 20:23:17 +00:00
2020-07-27 16:49:04 +00:00
shiftpoint in_front_dist ( ld d ) {
2020-07-28 16:05:25 +00:00
2020-07-28 16:09:26 +00:00
ld ys = current_display - > xsize / 2 ;
double mx = current_display - > tanfov * ( mousex - current_display - > xcenter ) / ys ;
double my = current_display - > tanfov * ( mousey - current_display - > ycenter ) / ys / pconf . stretch ;
2020-07-28 16:05:25 +00:00
hyperpoint tgt = point3 ( mx , my , 1 ) ;
tgt * = d / hypot_d ( 3 , tgt ) ;
return shiftless ( direct_exp ( lp_iapply ( tgt ) ) ) ; /* todo direct_shift */
2019-08-10 20:33:20 +00:00
}
2020-07-28 15:50:24 +00:00
EX shiftpoint find_mouseh3 ( ) {
2020-12-30 19:21:07 +00:00
if ( vrhr : : active ( ) )
2020-12-30 17:49:12 +00:00
return mouseh ;
2019-05-15 15:38:49 +00:00
if ( front_config = = eFront : : sphere_camera )
2019-08-10 20:33:20 +00:00
return in_front_dist ( front_edit ) ;
2019-05-15 15:38:49 +00:00
ld step = 0.01 ;
ld cdist = 0 ;
2020-09-16 03:57:05 +00:00
auto idt = z_inverse ( unshift ( drawtrans ) ) ;
2019-05-15 15:38:49 +00:00
2020-07-27 16:49:04 +00:00
auto qu = [ & ] ( ld d ) {
2019-08-10 20:33:20 +00:00
ld d1 = front_edit ;
2020-07-27 16:49:04 +00:00
shiftpoint h1 = in_front_dist ( d ) ;
2019-05-15 15:38:49 +00:00
if ( front_config = = eFront : : sphere_center )
2020-04-11 13:08:24 +00:00
d1 = geo_dist ( drawtrans * C0 , h1 ) ;
2019-05-15 15:38:49 +00:00
if ( front_config = = eFront : : equidistants ) {
2020-07-27 16:49:04 +00:00
hyperpoint h = idt * unshift ( in_front_dist ( d ) ) ;
2019-08-10 20:33:20 +00:00
d1 = asin_auto ( h [ 2 ] ) ;
}
if ( front_config = = eFront : : const_x ) {
2020-07-27 16:49:04 +00:00
hyperpoint h = idt * unshift ( in_front_dist ( d ) ) ;
2019-08-10 20:33:20 +00:00
d1 = asin_auto ( h [ 0 ] ) ;
}
if ( front_config = = eFront : : const_y ) {
2020-07-27 16:49:04 +00:00
hyperpoint h = idt * unshift ( in_front_dist ( d ) ) ;
2019-08-10 20:33:20 +00:00
d1 = asin_auto ( h [ 1 ] ) ;
2019-05-15 15:38:49 +00:00
}
2019-08-10 20:33:20 +00:00
return pow ( d1 - front_edit , 2 ) ;
2019-05-15 15:38:49 +00:00
} ;
ld bq = qu ( cdist ) ;
2019-08-10 20:33:20 +00:00
while ( abs ( step ) > 1e-10 ) {
2019-05-15 15:38:49 +00:00
ld cq = qu ( cdist + step ) ;
if ( cq < bq ) cdist + = step , bq = cq ;
2019-08-10 20:33:20 +00:00
else step * = - .5 ;
2019-05-15 15:38:49 +00:00
}
2019-08-10 20:33:20 +00:00
return in_front_dist ( cdist ) ;
2019-05-15 15:38:49 +00:00
}
2019-08-10 20:33:20 +00:00
int parallels = 12 , meridians = 6 ;
ld equi_range = 1 ;
2019-05-15 15:38:49 +00:00
2019-09-06 06:17:02 +00:00
EX void drawGrid ( ) {
2020-04-17 18:34:49 +00:00
if ( ! drawcell ) drawcell = cwt . at ;
2019-05-15 15:38:49 +00:00
color_t lightgrid = gridcolor ;
lightgrid - = ( lightgrid & 0xFF ) / 2 ;
2020-07-27 16:49:04 +00:00
shiftmatrix d2 = drawtrans * rgpushxto0 ( ccenter ) * rspintox ( gpushxto0 ( ccenter ) * coldcenter ) ;
2019-05-15 15:38:49 +00:00
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) {
2019-02-28 16:49:57 +00:00
queuecircleat ( mapeditor : : drawcell , 1 , 0x80D080FF ) ;
2019-02-28 18:01:40 +00:00
color_t cols [ 4 ] = { 0x80D080FF , 0x80D080FF , 0xFFFFFF40 , 0x00000040 } ;
2019-05-15 15:38:49 +00:00
if ( true ) {
2020-07-27 16:49:04 +00:00
shiftmatrix t = rgpushxto0 ( find_mouseh3 ( ) ) ;
2019-05-15 15:38:49 +00:00
for ( int i = 0 ; i < 4 ; i + + )
2019-08-10 20:33:20 +00:00
queueline ( t * cpush0 ( i & 1 , 0.1 ) , t * cpush0 ( i & 1 , - 0.1 ) , cols [ i ] , - 1 , i < 2 ? PPR : : LINE : PPR : : SUPERLINE ) ;
2019-05-15 15:38:49 +00:00
}
if ( front_config = = eFront : : sphere_center ) for ( int i = 0 ; i < 4 ; i + = 2 ) {
2019-08-10 20:33:20 +00:00
auto pt = [ & ] ( ld a , ld b ) {
2020-07-27 16:49:04 +00:00
return direct_exp ( spin ( a * degree ) * cspin ( 0 , 2 , b * degree ) * xtangent ( front_edit ) ) ;
2019-08-10 20:33:20 +00:00
} ;
for ( int ai = 0 ; ai < parallels ; ai + + ) {
ld a = ai * 360 / parallels ;
for ( int b = - 90 ; b < 90 ; b + = 5 ) curvepoint ( pt ( a , b ) ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( d2 , cols [ i + ( ( ai * 4 ) % parallels ! = 0 ) ] , 0 , i < 2 ? PPR : : LINE : PPR : : SUPERLINE ) ;
2019-05-15 15:38:49 +00:00
}
2019-08-10 20:33:20 +00:00
for ( int bi = 1 - meridians ; bi < meridians ; bi + + ) {
ld b = 90 * bi / meridians ;
for ( int a = 0 ; a < = 360 ; a + = 5 ) curvepoint ( pt ( a , b ) ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( d2 , cols [ i + ( bi ! = 0 ) ] , 0 , i < 2 ? PPR : : LINE : PPR : : SUPERLINE ) ;
2019-05-15 15:38:49 +00:00
}
}
2019-08-10 20:33:20 +00:00
transmatrix T ;
if ( front_config = = eFront : : equidistants ) T = Id ;
2022-11-12 21:38:45 +00:00
else if ( front_config = = eFront : : const_x ) T = cspin90 ( 2 , 0 ) ;
else if ( front_config = = eFront : : const_y ) T = cspin90 ( 2 , 1 ) ;
2019-08-10 20:33:20 +00:00
else return ;
for ( int i = 0 ; i < 4 ; i + = 2 ) {
2019-05-15 15:38:49 +00:00
for ( int u = 2 ; u < = 20 ; u + + ) {
PRING ( d ) {
2022-11-12 21:38:45 +00:00
curvepoint ( T * xspinpush ( d * cgi . S_step , u / 20. ) * zpush0 ( front_edit ) ) ;
2019-05-15 15:38:49 +00:00
}
2020-07-27 16:49:04 +00:00
queuecurve ( d2 , cols [ i + ( u % 5 ! = 0 ) ] , 0 , i < 2 ? PPR : : LINE : PPR : : SUPERLINE ) ;
2019-05-15 15:38:49 +00:00
}
2019-05-26 16:04:02 +00:00
for ( int d = 0 ; d < cgi . S84 ; d + + ) {
2022-11-12 21:38:45 +00:00
for ( int u = 0 ; u < = 20 ; u + + ) curvepoint ( T * xspinpush ( d * cgi . S_step , u / 20. ) * zpush ( front_edit ) * C0 ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( d2 , cols [ i + ( d % ( cgi . S84 / drawcell - > type ) ! = 0 ) ] , 0 , i < 2 ? PPR : : LINE : PPR : : SUPERLINE ) ;
2019-05-15 15:38:49 +00:00
}
}
2019-02-28 16:49:57 +00:00
return ;
}
2018-08-28 03:40:15 +00:00
2019-05-26 16:04:02 +00:00
for ( int d = 0 ; d < cgi . S84 ; d + + ) {
unsigned col = ( d % ( cgi . S84 / drawcell - > type ) = = 0 ) ? gridcolor : lightgrid ;
2022-11-12 21:38:45 +00:00
queueline ( d2 * C0 , d2 * xspinpush0 ( d * cgi . S_step , 1 ) , col , 4 + vid . linequality ) ;
2018-08-01 09:07:22 +00:00
}
for ( int u = 2 ; u < = 20 ; u + + ) {
PRING ( d ) {
2022-11-12 21:38:45 +00:00
curvepoint ( xspinpush0 ( d * cgi . S_step , u / 20. ) ) ;
2016-08-26 09:58:03 +00:00
}
2020-07-27 16:49:04 +00:00
queuecurve ( d2 , ( u % 5 = = 0 ) ? gridcolor : lightgrid , 0 , PPR : : LINE ) ;
2017-07-10 18:47:38 +00:00
}
2018-08-01 09:07:22 +00:00
queueline ( drawtrans * ccenter , drawtrans * coldcenter , gridcolor , 4 + vid . linequality ) ;
2022-03-19 23:38:19 +00:00
if ( snapping & & ! mouseout ( ) )
queuestr ( mouse_snap ( ) , 10 , " x " , 0xC040C0 ) ;
2016-08-26 09:58:03 +00:00
}
2017-12-22 21:34:01 +00:00
static ld brush_sizes [ 10 ] = {
0.001 , 0.002 , 0.005 , 0.0075 , 0.01 , 0.015 , 0.02 , 0.05 , 0.075 , 0.1 } ;
static unsigned texture_colors [ ] = {
11 ,
0x000000FF ,
0xFFFFFFFF ,
0xFF0000FF ,
0xFFFF00FF ,
0x00FF00FF ,
0x00FFFFFF ,
0x0000FFFF ,
0xFF00FFFF ,
0xC0C0C0FF ,
0x808080FF ,
0x404040FF ,
0x804000FF
} ;
2018-08-28 03:39:41 +00:00
2024-06-28 21:33:28 +00:00
EX bool area_in_pi = false ;
2018-08-28 03:39:41 +00:00
ld compute_area ( hpcshape & sh ) {
ld area = 0 ;
for ( int i = sh . s ; i < sh . e - 1 ; i + + ) {
2019-05-26 16:04:02 +00:00
hyperpoint h1 = cgi . hpc [ i ] ;
hyperpoint h2 = cgi . hpc [ i + 1 ] ;
2018-08-28 03:39:41 +00:00
if ( euclid )
area + = ( h2 [ 1 ] + h1 [ 1 ] ) * ( h2 [ 0 ] - h1 [ 0 ] ) / 2 ;
else {
hyperpoint rh2 = gpushxto0 ( h1 ) * h2 ;
hyperpoint rh1 = gpushxto0 ( h2 ) * h1 ;
// ld a1 = atan2(h1[1], h1[0]);
// ld a2 = atan2(h2[1], h2[0]);
ld b1 = atan2 ( rh1 [ 1 ] , rh1 [ 0 ] ) ;
ld b2 = atan2 ( rh2 [ 1 ] , rh2 [ 0 ] ) ;
// C0 -> H1 -> H2 -> C0
// at C0: (a1-a2)
// at H1: (rh2 - a1 - M_PI)
// at H2: (a2+M_PI - rh1)
// total: rh2 - rh1
2018-11-08 17:18:25 +00:00
// ld z = degree;
2018-08-28 03:39:41 +00:00
ld x = b2 - b1 + M_PI ;
2022-11-12 21:38:45 +00:00
cyclefix ( x , 0 ) ;
2018-08-28 03:39:41 +00:00
area + = x ;
}
}
return area ;
}
2019-02-28 18:02:01 +00:00
2019-08-15 13:05:43 +00:00
# define EDITING_TRIANGLES (GDIM == 3)
2019-02-28 18:02:01 +00:00
2022-03-19 23:38:19 +00:00
EX shiftpoint mouse_snap ( ) {
ld xdist = HUGE_VAL ;
shiftpoint resh ;
auto snap_to = [ & ] ( shiftpoint h ) {
ld dist = hdist ( h , mouseh ) ;
if ( dist < xdist ) xdist = dist , resh = h ;
} ;
for ( auto & p : gmatrix ) {
cell * c = p . first ;
auto & T = p . second ;
snap_to ( T * C0 ) ;
for ( int i = 0 ; i < c - > type ; i + + ) {
hyperpoint h1 = get_corner_position ( c , i ) ;
hyperpoint h2 = get_corner_position ( c , ( i + 1 ) % c - > type ) ;
snap_to ( T * h1 ) ;
snap_to ( T * mid ( h1 , h2 ) ) ;
}
}
return resh ;
}
2024-06-28 21:33:28 +00:00
EX bool show_menu = true ;
2019-08-09 23:15:41 +00:00
EX void showDrawEditor ( ) {
2019-02-17 17:41:40 +00:00
# if CAP_POLY
2022-05-06 17:12:02 +00:00
cmode = sm : : DRAW | sm : : PANNING ;
2024-06-28 21:33:28 +00:00
if ( show_menu ) cmode | = sm : : SIDE ;
2022-07-05 09:51:06 +00:00
gamescreen ( ) ;
2017-07-10 18:47:38 +00:00
drawGrid ( ) ;
2018-07-09 16:39:34 +00:00
if ( callhandlers ( false , hooks_prestats ) ) return ;
2016-08-26 09:58:03 +00:00
2017-06-18 16:52:15 +00:00
if ( ! mouseout ( ) ) getcstat = ' - ' ;
2024-06-28 21:33:28 +00:00
bool freedraw = drawing_tool | | intexture ;
2016-08-26 09:58:03 +00:00
2024-06-28 21:33:28 +00:00
# if CAP_TEXTURE
if ( freedraw & & ! show_menu ) for ( int i = 0 ; i < 10 ; i + + ) {
int fs = editor_fsize ( ) ;
if ( 8 + fs * ( 6 + i ) < vid . yres - 8 - fs * 7 )
displayColorButton ( vid . xres - 8 , 8 + fs * ( 6 + i ) , " ### " , 1000 + i , 16 , 1 , dialog : : displaycolor ( texture_colors [ i + 1 ] ) ) ;
2021-02-01 10:45:52 +00:00
2024-06-28 21:33:28 +00:00
if ( displayfr ( vid . xres - 8 - fs * 3 , 8 + fs * ( 6 + i ) , 0 , vid . fsize , its ( i + 1 ) , dtwidth = = brush_sizes [ i ] ? 0xFF8000 : 0xC0C0C0 , 16 ) )
getcstat = 2000 + i ;
}
# endif
editor_menu ( drawing_tool ? 3 : 2 ) ;
keyhandler = handle_key_draw ;
# else
popScreen ( ) ;
# endif
}
EX void draw_editor_menu ( ) {
2021-02-01 10:45:52 +00:00
# if CAP_TEXTURE
if ( texture : : config . tstate ! = texture : : tsActive & & intexture ) {
intexture = false ; drawing_tool = true ;
}
# endif
2017-12-22 20:57:55 +00:00
2024-06-28 21:33:28 +00:00
bool freedraw = drawing_tool | | intexture ;
2017-12-21 10:36:07 +00:00
# if CAP_TEXTURE
2021-02-01 10:45:52 +00:00
if ( intexture ) {
2018-03-17 20:12:46 +00:00
texture : : config . data . update ( ) ;
2020-04-17 18:04:33 +00:00
freedraw = true ;
drawing_tool = false ;
2017-12-17 23:24:56 +00:00
}
2017-12-21 10:36:07 +00:00
# endif
2020-04-17 18:04:33 +00:00
2024-06-28 21:33:28 +00:00
int prec = snapping ? 15 : 4 ;
2020-04-17 18:04:33 +00:00
if ( ! freedraw ) {
2017-12-17 23:24:56 +00:00
2024-06-28 21:33:28 +00:00
int sg = drawcellShapeGroup ( ) ;
string line1 , line2 ;
2016-08-26 09:58:03 +00:00
2017-12-17 23:24:56 +00:00
switch ( sg ) {
2018-08-27 17:27:35 +00:00
case sgPlayer :
2017-12-17 23:24:56 +00:00
line1 = XLAT ( " character " ) ;
line2 = csname ( vid . cs ) ;
break ;
2016-08-26 09:58:03 +00:00
2018-08-27 17:27:35 +00:00
case sgMonster :
2017-12-17 23:24:56 +00:00
line1 = XLAT ( " monster " ) ;
line2 = XLAT1 ( minf [ drawcell - > monst ] . name ) ;
break ;
2018-08-27 17:27:35 +00:00
case sgItem :
2017-12-17 23:24:56 +00:00
line1 = XLAT ( " item " ) ;
line2 = XLAT1 ( iinf [ drawcell - > item ] . name ) ;
break ;
2018-08-27 17:27:35 +00:00
case sgFloor :
2019-08-15 13:05:43 +00:00
line1 = GDIM = = 3 ? XLAT ( " pick something " ) : XLAT ( " floor " ) ;
2019-05-15 15:41:57 +00:00
line2 = " # " + its ( drawcellShapeID ( ) ) ;
break ;
case sgWall :
line1 = XLAT ( " statue " ) ;
2018-08-27 17:27:35 +00:00
line2 = " # " + its ( drawcellShapeID ( ) ) ;
break ;
2017-12-17 23:24:56 +00:00
}
2016-08-26 09:58:03 +00:00
2024-06-28 21:33:28 +00:00
usershape * us = usershapes [ drawcellShapeGroup ( ) ] [ drawcellShapeID ( ) ] ;
dialog : : addInfo ( line1 + " : " + line2 ) ;
string s = GDIM = = 3 ? XLAT ( " color group " ) : XLAT ( " layers " ) ;
dialog : : addSelItem ( s , its ( dslayer ) , ' l ' ) ;
dialog : : add_action ( [ s ] {
dialog : : editNumber ( dslayer , 0 , USERLAYERS - 1 , 1 , 0 , s , " " ) ;
dialog : : bound_low ( 0 ) ;
dialog : : bound_up ( USERLAYERS - 1 ) ;
} ) ;
dialog : : addBoolItem_action ( " one layer only " , onelayeronly , ' l ' - 96 ) ;
dialog : : add_key_action ( ' - ' , [ ] { auto act = at_or_null ( dialog : : key_actions , mousekey ) ; if ( act ) ( * act ) ( ) ; } ) ;
auto auto_to_shape = [ ] ( string text , char ch ) {
dialog : : addItem_mouse ( text , ch ) ;
dialog : : add_action ( [ ch ] {
if ( GDIM = = 2 & & ! mouseover ) { mousekey = ch ; return ; }
shiftpoint mh = full_mouseh ( ) ;
hyperpoint mh1 = inverse_shift ( drawtrans , mh ) ;
dslayer % = USERLAYERS ;
applyToShape ( drawcellShapeGroup ( ) , drawcellShapeID ( ) , ch , mh1 ) ;
} ) ;
} ;
2016-08-26 09:58:03 +00:00
2024-06-28 21:33:28 +00:00
auto_to_shape ( XLAT ( " start new shape " ) , ' n ' ) ;
auto_to_shape ( XLAT ( " load current " ) , ' u ' ) ;
2017-12-17 23:24:56 +00:00
2024-06-28 21:33:28 +00:00
if ( us & & isize ( us - > d [ dslayer ] . list ) ) {
2016-08-26 09:58:03 +00:00
2024-06-28 21:33:28 +00:00
usershapelayer & ds ( us - > d [ dslayer ] ) ;
if ( ! EDITING_TRIANGLES ) {
dialog : : addSelItem ( XLAT ( " rotations " ) , its ( ds . rots ) , ' 1 ' ) ;
dialog : : add_action ( [ & ds ] {
auto & ne = dialog : : editNumber ( ds . rots , 1 , 100 , 1 , 1 , XLAT ( " rotations " ) , " " ) ;
dialog : : bound_low ( 1 ) ;
dialog : : bound_up ( 999 ) ;
ne . reaction = [ ] { usershape_changes + + ; } ;
} ) ;
dialog : : addBoolItem ( " symmetry " , ds . sym , ' 0 ' ) ;
dialog : : add_action ( [ & ds ] { ds . sym = ! ds . sym ; usershape_changes + + ; } ) ;
}
else dialog : : addBreak ( 200 ) ;
dialog : : addItem ( XLAT ( " %1 vertices " , its ( isize ( ds . list ) ) ) , SDLK_F4 ) ;
dialog : : add_action ( export_for_polygon ) ;
auto_to_shape ( XLAT ( " add vertex " ) , ' a ' ) ;
if ( ! EDITING_TRIANGLES ) {
auto_to_shape ( XLAT ( " move vertex " ) , ' m ' ) ; // autochoose?
auto_to_shape ( XLAT ( " delete vertex " ) , ' d ' ) ; // autochoose?
dialog : : addItem_mouse ( XLAT ( " choose " ) , ' c ' ) ;
dialog : : add_action ( [ ] { if ( mouseover ) { ew = ewsearch ; autochoose = false ; } else mousekey = ' c ' ; } ) ;
dialog : : addBoolItem_action ( XLAT ( " switch auto " ) , autochoose , ' b ' ) ;
2019-02-28 18:02:01 +00:00
}
else {
2024-06-28 21:33:28 +00:00
auto_to_shape ( XLAT ( " reuse " ) , ' c ' ) ; // autochoose?
auto_to_shape ( XLAT ( " delete " ) , ' d ' ) ; // autochoose?
dialog : : addBreak ( 200 ) ;
2019-02-28 18:02:01 +00:00
}
2024-06-28 21:33:28 +00:00
if ( GDIM = = 2 ) {
auto_to_shape ( XLAT ( " shift " ) , ' t ' ) ;
auto_to_shape ( XLAT ( " spin " ) , ' y ' ) ;
auto_to_shape ( XLAT ( " z-level " ) , ' z ' ) ;
}
else dialog : : addBreak ( 300 ) ;
cgi . require_usershapes ( ) ;
auto & sh = cgi . ushr [ & us - > d [ dslayer ] ] ;
if ( sh . e > = sh . s + 3 )
dialog : : addSelItem ( XLAT ( " area " ) , area_in_pi ? fts ( compute_area ( sh ) / M_PI , 4 ) + " π " : fts ( compute_area ( sh ) , prec ) , ' w ' ) ;
dialog : : add_action ( [ ] { area_in_pi = ! area_in_pi ; } ) ;
2017-06-18 16:52:15 +00:00
}
else {
2024-06-28 21:33:28 +00:00
if ( among ( mousekey , ' a ' , ' c ' , ' d ' , ' t ' , ' y ' ) ) mousekey = ' n ' ;
dialog : : addBreak ( 1100 ) ;
2019-02-28 16:49:57 +00:00
}
2017-12-17 23:24:56 +00:00
}
2024-06-28 21:33:28 +00:00
2020-04-17 18:04:33 +00:00
else if ( freedraw ) {
2024-06-28 21:33:28 +00:00
dialog : : addBoolItem_action ( " symmetry " , texture : : texturesym , ' 0 ' ) ;
if ( drawing_tool ) {
if ( dtfill )
dialog : : addBoolItem ( XLAT ( " fill " ) , dtfill , ' f ' ) ;
else
dialog : : addColorItem ( XLAT ( " fill " ) , dtcolor , ' f ' ) ;
}
dialog : : addSelItem ( XLAT ( " brush size " ) , fts ( dtwidth ) , ' b ' ) ;
dialog : : add_action ( [ ] { dialog : : editNumber ( dtwidth , 0 , 0.1 , 0.005 , 0.02 , XLAT ( " brush size " ) , XLAT ( " brush size " ) ) ; } ) ;
# if CAP_TEXTURE
if ( intexture ) {
dialog : : addItem ( XLAT ( " undo " ) , ' u ' ) ;
dialog : : add_action ( [ ] { texture : : config . data . undo ( ) ; } ) ;
}
else dialog : : addBreak ( 100 ) ;
# endif
2024-06-29 08:03:42 +00:00
dialog : : addItem_mouse ( XLAT ( " draw " ) , ' d ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] { mousekey = ' d ' ; } ) ;
2024-06-29 08:03:42 +00:00
dialog : : addItem_mouse ( XLAT ( " line " ) , ' l ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] { mousekey = ' l ' ; } ) ;
2024-06-29 08:03:42 +00:00
dialog : : addItem_mouse ( XLAT ( " circle " ) , ' c ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] { mousekey = ' c ' ; } ) ;
2024-06-29 08:03:42 +00:00
if ( drawing_tool ) dialog : : addItem_mouse ( XLAT ( " text " ) , ' T ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] { mousekey = ' T ' ; } ) ;
2024-06-29 08:03:42 +00:00
if ( drawing_tool ) dialog : : addItem_mouse ( XLAT ( " erase " ) , ' e ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] { mousekey = ' e ' ; } ) ;
2018-06-22 12:47:24 +00:00
int s = isize ( texture : : config . data . pixels_to_draw ) ;
2024-06-28 21:33:28 +00:00
if ( s ) dialog : : addInfo ( its ( s ) ) ; else dialog : : addBreak ( 100 ) ;
dialog : : addBreak ( 700 ) ;
2016-08-26 09:58:03 +00:00
}
2024-06-28 21:33:28 +00:00
if ( GDIM = = 2 )
2024-06-29 08:03:42 +00:00
dialog : : addBoolItem_action ( XLAT ( " snap " ) , snapping , ' S ' ) ;
2018-01-05 16:30:03 +00:00
2024-06-28 21:33:28 +00:00
if ( GDIM = = 3 ) {
2024-06-29 08:03:42 +00:00
if ( front_config = = eFront : : sphere_camera ) dialog : : addItem ( XLAT ( " camera " ) , ' z ' ) ;
if ( front_config = = eFront : : sphere_center ) dialog : : addItem ( XLAT ( " spheres " ) , ' z ' ) ;
2024-06-28 21:33:28 +00:00
if ( nonisotropic & & front_config = = eFront : : equidistants ) dialog : : addSelItem ( " Z = " , fts ( front_edit ) , ' z ' ) ;
if ( nonisotropic & & front_config = = eFront : : const_x ) dialog : : addSelItem ( " X = " , fts ( front_edit ) , ' z ' ) ;
if ( nonisotropic & & front_config = = eFront : : const_y ) dialog : : addSelItem ( " Y = " , fts ( front_edit ) , ' z ' ) ;
dialog : : add_action ( launch_z_editor ) ;
}
2018-01-05 16:30:03 +00:00
2024-06-28 21:33:28 +00:00
if ( mousekey = = ' g ' ) {
2024-06-29 08:03:42 +00:00
dialog : : addColorItem ( XLAT ( " grid color " ) , gridcolor , ' p ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( edit_grid_color ) ;
}
else {
2024-06-29 08:03:42 +00:00
dialog : : addColorItem ( XLAT ( " color " ) , freedraw ? dtcolor : colortouse , ' p ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ freedraw ] {
if ( freedraw ) {
dialog : : openColorDialog ( dtcolor , texture_colors ) ;
return ;
}
dialog : : openColorDialog ( colortouse ) ;
dialog : : get_di ( ) . reaction = [ ] ( ) {
applyToShape ( drawcellShapeGroup ( ) , drawcellShapeID ( ) , COLORKEY , C0 ) ;
} ;
} ) ;
2017-12-22 21:34:01 +00:00
}
2017-12-21 10:36:07 +00:00
2024-06-28 21:33:28 +00:00
if ( GDIM = = 2 ) {
2024-06-29 08:03:42 +00:00
dialog : : addItem_mouse ( XLAT ( " grid " ) , ' g ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] {
shiftpoint mh = full_mouseh ( ) ;
hyperpoint mh1 = inverse_shift ( drawtrans , mh ) ;
if ( mouseover & & coldcenter = = ccenter & & ccenter = = mh1 )
edit_grid_color ( ) ;
else if ( mouseover ) coldcenter = ccenter , ccenter = mh1 ;
else mousekey = ' g ' ;
} ) ;
}
2016-08-26 09:58:03 +00:00
2024-06-28 21:33:28 +00:00
if ( ! freedraw ) {
2024-06-29 08:03:42 +00:00
dialog : : addItem_mouse ( XLAT ( " select shape to edit " ) , ' e ' ) ;
2024-06-28 21:33:28 +00:00
dialog : : add_action ( [ ] {
if ( mouseover ) initdraw ( mouseover ) ;
else mousekey = ' e ' ;
} ) ;
}
2022-03-19 23:39:33 +00:00
2017-06-09 01:41:33 +00:00
if ( ! mouseout ( ) ) {
2022-03-19 23:39:33 +00:00
shiftpoint h1 = drawtrans * ccenter ;
shiftpoint h2 = drawtrans * coldcenter ;
transmatrix T = gpushxto0 ( unshift ( h1 ) ) ;
T = spintox ( T * unshift ( h2 ) ) * T ;
shiftpoint mh1 = full_mouseh ( ) ;
hyperpoint mh = T * unshift ( mh1 ) ;
2024-06-28 21:33:28 +00:00
string coord ;
coord = XLAT ( " x: %1 " , fts ( mh [ 0 ] , prec ) ) ;
coord + = " " + XLAT ( " y: %1 " , fts ( mh [ 1 ] , prec ) ) ;
coord + = " " + XLAT ( " z: %1 " , fts ( mh [ 2 ] , prec ) ) ;
if ( MDIM = = 4 ) coord + = " " + XLAT ( " w: %1 " , fts ( mh [ 3 ] , prec ) ) ;
dialog : : addInfo ( coord ) ;
2020-07-27 16:49:04 +00:00
mh = inverse_exp ( shiftless ( mh ) ) ;
2024-06-28 21:33:28 +00:00
coord = XLAT ( " r: %1 " , fts ( hypot_d ( 3 , mh ) , prec ) ) ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) {
2024-06-28 21:33:28 +00:00
coord + = " " + XLAT ( " ϕ: %1° " , fts ( - atan2 ( mh [ 2 ] , hypot_d ( 2 , mh ) ) / degree , prec ) ) ;
coord + = " " + XLAT ( " λ: %1° " , fts ( - atan2 ( mh [ 1 ] , mh [ 0 ] ) / degree , prec ) ) ;
2019-04-13 11:04:17 +00:00
}
else {
2024-06-28 21:33:28 +00:00
coord + = " " + XLAT ( " ϕ: %1° " , fts ( - atan2 ( mh [ 1 ] , mh [ 0 ] ) / degree , prec ) ) ;
}
dialog : : addInfo ( coord ) ;
}
else dialog : : addBreak ( 200 ) ;
}
EX void launch_z_editor ( ) {
dialog : : editNumber ( front_edit , 0 , 5 , 0.1 , 0.5 , XLAT ( " z-level " ) , " " ) ;
dialog : : get_di ( ) . extra_options = [ ] ( ) {
dialog : : addBoolItem ( XLAT ( " The distance from the camera to added points. " ) , front_config = = eFront : : sphere_camera , ' A ' ) ;
dialog : : add_action ( [ ] { front_config = eFront : : sphere_camera ; } ) ;
dialog : : addBoolItem ( XLAT ( " place points at fixed radius " ) , front_config = = eFront : : sphere_center , ' B ' ) ;
dialog : : add_action ( [ ] { front_config = eFront : : sphere_center ; } ) ;
dialog : : addBoolItem ( nonisotropic ? XLAT ( " place points on surfaces of const Z " ) : XLAT ( " place points on equidistant surfaces " ) , front_config = = eFront : : equidistants , ' C ' ) ;
dialog : : add_action ( [ ] { front_config = eFront : : equidistants ; } ) ;
if ( nonisotropic ) {
dialog : : addBoolItem ( XLAT ( " place points on surfaces of const X " ) , front_config = = eFront : : const_x , ' D ' ) ;
dialog : : add_action ( [ ] { front_config = eFront : : const_x ; } ) ;
dialog : : addBoolItem ( XLAT ( " place points on surfaces of const Y " ) , front_config = = eFront : : const_y , ' E ' ) ;
dialog : : add_action ( [ ] { front_config = eFront : : const_y ; } ) ;
}
dialog : : addSelItem ( XLAT ( " mousewheel step " ) , fts ( front_step ) , ' S ' ) ;
dialog : : add_action ( [ ] {
dialog : : editNumber ( front_step , - 10 , 10 , 0.1 , 0.1 , XLAT ( " mousewheel step " ) , " hint: shift for finer steps " ) ;
} ) ;
if ( front_config = = eFront : : sphere_center ) {
dialog : : addSelItem ( XLAT ( " parallels to draw " ) , its ( parallels ) , ' P ' ) ;
dialog : : add_action ( [ ] {
dialog : : editNumber ( parallels , 0 , 72 , 1 , 12 , XLAT ( " parallels to draw " ) , " " ) ;
} ) ;
dialog : : addSelItem ( XLAT ( " meridians to draw " ) , its ( meridians ) , ' M ' ) ;
dialog : : add_action ( [ ] {
dialog : : editNumber ( meridians , 0 , 72 , 1 , 12 , XLAT ( " meridians to draw " ) , " " ) ;
} ) ;
}
else if ( front_config ! = eFront : : sphere_camera ) {
dialog : : addSelItem ( XLAT ( " range of grid to draw " ) , fts ( equi_range ) , ' R ' ) ;
dialog : : add_action ( [ ] {
dialog : : editNumber ( equi_range , 0 , 5 , 0.1 , 1 , XLAT ( " range of grid to draw " ) , " " ) ;
} ) ;
}
} ;
}
EX void edit_grid_color ( ) {
static unsigned grid_colors [ ] = {
8 ,
0x00000040 ,
0xFFFFFF40 ,
0xFF000040 ,
0x0000F040 ,
0x00000080 ,
0xFFFFFF80 ,
0xFF000080 ,
0x0000F080 ,
} ;
dialog : : openColorDialog ( gridcolor , grid_colors ) ;
}
EX void export_for_polygon_to ( hstream & hs ) {
for ( int i = 0 ; i < USERSHAPEGROUPS ; i + + ) for ( auto usp : usershapes [ i ] ) {
auto us = usp . second ;
if ( ! us ) continue ;
for ( int l = 0 ; l < USERLAYERS ; l + + ) if ( isize ( us - > d [ l ] . list ) ) {
usershapelayer & ds ( us - > d [ l ] ) ;
println ( hs , spaced ( " // " , i , usp . first , l , " [ " , ds . color , double ( ds . zlevel ) , " ] " ) ) ;
print ( hs , " ID, " , us - > d [ l ] . rots , " , " , us - > d [ l ] . sym ? 2 : 1 , " , " ) ;
for ( int i = 0 ; i < isize ( us - > d [ l ] . list ) ; i + + ) {
for ( int d = 0 ; d < GDIM ; d + + ) print ( hs , fts ( us - > d [ l ] . list [ i ] [ d ] ) , " , " ) ;
print ( hs , " " ) ;
}
println ( hs ) ;
2019-04-13 11:04:17 +00:00
}
2016-08-26 09:58:03 +00:00
}
2018-08-28 03:39:41 +00:00
2024-06-28 21:33:28 +00:00
for ( int i = 0 ; i < USERSHAPEGROUPS ; i + + ) for ( auto usp : usershapes [ i ] ) {
auto us = usp . second ;
if ( ! us ) continue ;
for ( int l = 0 ; l < USERLAYERS ; l + + ) if ( isize ( us - > d [ l ] . list ) ) {
usershapelayer & ds ( us - > d [ l ] ) ;
println ( hs , spaced ( " // " , i , usp . first , l , " [ " , ds . color , double ( ds . zlevel ) , " ] " ) ) ;
print ( hs , " { " ) ;
for ( int r = 0 ; r < us - > d [ l ] . rots ; r + + ) {
for ( int i = 0 ; i < isize ( us - > d [ l ] . list ) ; i + + ) {
hyperpoint h = us - > d [ l ] . list [ i ] ;
h = spin ( TAU * r / us - > d [ l ] . rots ) * h ;
for ( int d = 0 ; d < GDIM ; d + + ) print ( hs , fts ( h [ d ] ) , " , " ) ;
}
if ( us - > d [ l ] . sym ) for ( int i = isize ( us - > d [ l ] . list ) - 1 ; i > = 0 ; i - - ) {
hyperpoint h = us - > d [ l ] . list [ i ] ;
h [ 1 ] = - h [ 1 ] ;
h = spin ( TAU * r / us - > d [ l ] . rots ) * h ;
for ( int d = 0 ; d < GDIM ; d + + ) print ( hs , fts ( h [ d ] ) , " , " ) ;
}
}
println ( hs , " }, " ) ;
}
2018-08-28 03:39:41 +00:00
}
2024-06-28 21:33:28 +00:00
}
2018-08-28 03:39:41 +00:00
2024-06-28 21:33:28 +00:00
EX void export_for_polygon ( ) {
export_for_polygon_to ( hlog ) ;
fhstream f ( " shape.txt " , " w " ) ;
export_for_polygon_to ( f ) ;
2016-08-26 09:58:03 +00:00
}
2024-06-28 21:33:28 +00:00
2019-02-17 17:41:40 +00:00
# if CAP_POLY
2017-10-04 20:27:51 +00:00
void loadShapes ( int sg , int id ) {
delete usershapes [ sg ] [ id ] ;
usershapes [ sg ] [ id ] = NULL ;
initquickqueue ( ) ;
dynamicval < bool > ws ( mmspatial , false ) ;
2020-07-27 16:49:04 +00:00
auto sId = shiftless ( Id ) ;
2017-10-04 20:27:51 +00:00
if ( sg = = 0 ) {
2020-07-27 16:49:04 +00:00
multi : : cpid = id , drawMonsterType ( moPlayer , drawcell , sId , 0xC0C0C0 , 0 , 0xC0C0C0 ) ;
2017-10-04 20:27:51 +00:00
}
else if ( sg = = 1 ) {
2020-07-27 16:49:04 +00:00
drawMonsterType ( eMonster ( id ) , drawcell , sId , minf [ id ] . color , 0 , minf [ id ] . color ) ;
2017-10-04 20:27:51 +00:00
}
else if ( sg = = 2 ) {
2020-07-27 16:49:04 +00:00
drawItemType ( eItem ( id ) , drawcell , sId , iinf [ id ] . color , 0 , false ) ;
2017-10-04 20:27:51 +00:00
}
else {
2020-07-27 16:49:04 +00:00
draw_qfi ( drawcell , sId , 0 , PPR : : FLOOR ) ;
2017-10-04 20:27:51 +00:00
}
sortquickqueue ( ) ;
int layer = 0 ;
initShape ( sg , id ) ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( ptds ) ; i + + ) {
2018-09-04 17:53:42 +00:00
auto pp = dynamic_cast < dqi_poly * > ( & * ptds [ i ] ) ;
if ( ! pp ) continue ;
auto & ptd = * pp ;
2017-10-04 20:27:51 +00:00
2018-09-04 17:53:42 +00:00
int cnt = ptd . cnt ;
2017-10-04 20:27:51 +00:00
usershapelayer * dsCur = & usershapes [ sg ] [ id ] - > d [ layer ] ;
dsCur - > list . clear ( ) ;
2018-09-04 17:53:42 +00:00
dsCur - > color = ptd . color ;
2017-10-04 20:27:51 +00:00
dsCur - > sym = false ;
dsCur - > rots = 1 ;
2019-02-28 16:23:02 +00:00
dsCur - > zlevel = 0 ;
2017-10-04 20:27:51 +00:00
2019-05-26 16:04:02 +00:00
for ( auto & v : cgi . symmetriesAt )
2018-09-04 17:53:42 +00:00
if ( v [ 0 ] = = ptd . offset ) {
2017-10-04 20:27:51 +00:00
dsCur - > rots = v [ 1 ] ;
dsCur - > sym = v [ 2 ] = = 2 ;
}
int d = dsCur - > rots * ( dsCur - > sym ? 2 : 1 ) ;
for ( int i = 0 ; i < cnt / d ; i + + )
2020-07-27 16:49:04 +00:00
dsCur - > list . push_back ( unshift ( ptd . V ) * glhr : : gltopoint ( ( * ptd . tab ) [ i + ptd . offset ] ) ) ;
2017-10-04 20:27:51 +00:00
layer + + ;
if ( layer = = USERLAYERS ) break ;
}
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2017-10-04 20:27:51 +00:00
}
2019-02-17 17:41:40 +00:00
2024-06-28 21:33:28 +00:00
EX void applyToShape ( int sg , int id , int uni , hyperpoint mh ) {
2016-08-26 09:58:03 +00:00
bool haveshape = usershapes [ sg ] [ id ] ;
bool xnew = false ;
2017-06-18 16:52:15 +00:00
if ( uni = = ' - ' ) uni = mousekey ;
2016-08-26 09:58:03 +00:00
if ( ! haveshape ) {
2017-10-04 20:27:51 +00:00
if ( uni = = ' n ' )
2016-08-26 09:58:03 +00:00
initShape ( sg , id ) ;
2017-10-04 20:27:51 +00:00
else if ( uni = = ' u ' ) ;
2022-03-19 23:38:19 +00:00
else if ( uni = = ' S ' ) { snapping = ! snapping ; return ; }
2016-08-26 09:58:03 +00:00
else if ( uni > = ' 0 ' & & uni < = ' 9 ' ) {
initShape ( sg , id ) ;
xnew = true ;
}
else
return ;
}
usershapelayer * dsCur = & usershapes [ sg ] [ id ] - > d [ dslayer ] ;
2017-06-18 16:52:15 +00:00
2016-08-26 09:58:03 +00:00
if ( uni = = ' n ' | | xnew ) {
dsCur - > list . clear ( ) ;
dsCur - > list . push_back ( mh ) ;
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2016-08-26 09:58:03 +00:00
}
2017-10-04 20:27:51 +00:00
if ( uni = = ' u ' )
loadShapes ( sg , id ) ;
2019-02-28 16:23:02 +00:00
2022-03-19 23:38:19 +00:00
if ( uni = = ' S ' ) snapping = ! snapping ;
2019-08-15 13:05:43 +00:00
if ( uni = = ' z ' & & haveshape & & GDIM = = 2 )
2019-02-28 16:23:02 +00:00
dialog : : editNumber ( dsCur - > zlevel , - 10 , + 10 , 0.1 , 0 , XLAT ( " z-level " ) ,
XLAT ( " Changing the z-level will make this layer affected by the parallax effect. " ) ) ;
2017-10-04 20:27:51 +00:00
2019-02-28 18:02:01 +00:00
if ( EDITING_TRIANGLES ) {
if ( uni = = ' a ' ) {
dsCur - > list . push_back ( mh ) ;
uni = 0 ;
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2019-02-28 18:02:01 +00:00
}
else if ( uni = = ' c ' | | uni = = ' d ' | | uni = = ' m ' ) {
hyperpoint best = mh ;
hyperpoint onscr ;
applymodel ( drawtrans * mh , onscr ) ;
println ( hlog , " onscr = " , onscr ) ;
ld dist = HUGE_VAL ;
for ( auto & layer : usershapes [ sg ] [ id ] - > d )
for ( const hyperpoint & h : layer . list ) {
hyperpoint h1 ;
applymodel ( drawtrans * h , h1 ) ;
ld d = sqhypot_d ( 2 , h1 - onscr ) ;
if ( d < dist ) dist = d , best = h ;
}
if ( uni = = ' c ' ) dsCur - > list . push_back ( best ) ;
else if ( uni = = ' d ' ) {
2022-07-05 17:23:05 +00:00
vector < hyperpoint > oldlist = std : : move ( dsCur - > list ) ;
2019-02-28 18:02:01 +00:00
dsCur - > list . clear ( ) ;
int i ;
for ( i = 0 ; i < isize ( oldlist ) ; i + = 3 )
if ( oldlist [ i ] ! = best & & oldlist [ i + 1 ] ! = best & & oldlist [ i + 2 ] ! = best )
dsCur - > list . push_back ( oldlist [ i ] ) ,
dsCur - > list . push_back ( oldlist [ i + 1 ] ) ,
dsCur - > list . push_back ( oldlist [ i + 2 ] ) ;
for ( ; i < isize ( oldlist ) ; i + + )
if ( oldlist [ i ] ! = best )
dsCur - > list . push_back ( oldlist [ i ] ) ;
}
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2019-02-28 18:02:01 +00:00
uni = 0 ;
}
2019-05-15 15:42:36 +00:00
else if ( uni = = COLORKEY ) dsCur - > color = colortouse ;
2019-02-28 18:02:01 +00:00
else if ( uni ! = ' D ' ) uni = 0 ;
}
2016-08-26 09:58:03 +00:00
if ( uni = = ' a ' & & haveshape ) {
2022-11-12 21:38:45 +00:00
mh = spin ( TAU * - ew . rotid / dsCur - > rots ) * mh ;
2017-06-18 16:52:15 +00:00
if ( ew . symid ) mh = Mirror * mh ;
2018-06-22 12:47:24 +00:00
if ( ew . pointid < 0 | | ew . pointid > = isize ( dsCur - > list ) )
ew . pointid = isize ( dsCur - > list ) - 1 , ew . side = 1 ;
2017-06-18 16:52:15 +00:00
dsCur - > list . insert ( dsCur - > list . begin ( ) + ew . pointid + ( ew . side ? 1 : 0 ) , mh ) ;
if ( ew . side ) ew . pointid + + ;
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2016-08-26 09:58:03 +00:00
}
if ( uni = = ' D ' ) {
delete usershapes [ sg ] [ id ] ;
usershapes [ sg ] [ id ] = NULL ;
}
2017-06-18 16:52:15 +00:00
if ( uni = = ' m ' | | uni = = ' d ' ) {
int i = ew . pointid ;
2018-06-22 12:47:24 +00:00
if ( i < 0 | | i > = isize ( dsCur - > list ) ) return ;
2017-06-18 16:52:15 +00:00
2022-11-12 21:38:45 +00:00
mh = spin ( TAU * - ew . rotid / dsCur - > rots ) * mh ;
2017-06-18 16:52:15 +00:00
if ( ew . symid ) mh = Mirror * mh ;
if ( uni = = ' m ' | | uni = = ' M ' )
2016-08-26 09:58:03 +00:00
dsCur - > list [ i ] = mh ;
2017-06-18 16:52:15 +00:00
if ( uni = = ' d ' | | uni = = ' b ' ) {
2016-08-26 09:58:03 +00:00
dsCur - > list . erase ( dsCur - > list . begin ( ) + i ) ;
2017-06-18 16:52:15 +00:00
if ( ew . side = = 1 & & ew . pointid > = i ) ew . pointid - - ;
if ( ew . side = = 0 & & ew . pointid > i ) ew . pointid - - ;
2016-08-26 09:58:03 +00:00
}
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2016-08-26 09:58:03 +00:00
}
2017-06-18 16:52:15 +00:00
2016-08-26 09:58:03 +00:00
if ( uni = = ' K ' ) {
if ( vid . cs . charid > = 4 ) {
2019-05-26 16:04:02 +00:00
loadShape ( sg , id , cgi . shCatBody , 2 , 0 ) ;
loadShape ( sg , id , cgi . shCatHead , 2 , 1 ) ;
2016-08-26 09:58:03 +00:00
}
else {
2019-05-26 16:04:02 +00:00
if ( ! ( vid . cs . charid & 1 ) ) loadShape ( sg , id , cgi . shPBody , 2 , 0 ) ;
else loadShape ( sg , id , cgi . shFemaleBody , 2 , 0 ) ;
2016-08-26 09:58:03 +00:00
2019-05-26 16:04:02 +00:00
loadShape ( sg , id , cgi . shPSword , 1 , 1 ) ;
2016-08-26 09:58:03 +00:00
if ( vid . cs . charid & 1 )
2019-05-26 16:04:02 +00:00
loadShape ( sg , id , cgi . shFemaleDress , 2 , 2 ) ;
2016-08-26 09:58:03 +00:00
2017-03-23 10:53:57 +00:00
/* if(vid.cs.charid&1)
2019-05-26 16:04:02 +00:00
loadShape ( sg , id , cgi . shPrincessDress , 1 , 3 ) ;
2016-08-26 09:58:03 +00:00
else
2019-05-26 16:04:02 +00:00
loadShape ( sg , id , cgi . shPrinceDress , 2 , 3 ) ; */
2017-03-23 10:53:57 +00:00
2019-05-26 16:04:02 +00:00
loadShape ( sg , id , cgi . shRatCape2 , 1 , 3 ) ;
2016-08-26 09:58:03 +00:00
if ( vid . cs . charid & 1 )
2019-05-26 16:04:02 +00:00
loadShape ( sg , id , cgi . shFemaleHair , 2 , 4 ) ;
2016-08-26 09:58:03 +00:00
else
2019-05-26 16:04:02 +00:00
loadShape ( sg , id , cgi . shPHead , 2 , 4 ) ;
2016-08-26 09:58:03 +00:00
2019-05-26 16:04:02 +00:00
loadShape ( sg , id , cgi . shPFace , 2 , 5 ) ;
2016-08-26 09:58:03 +00:00
}
2019-05-26 16:04:02 +00:00
// loadShape(sg, id, cgi.shWolf, 2, dslayer);
2016-08-26 09:58:03 +00:00
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2016-08-26 09:58:03 +00:00
}
if ( uni = = ' + ' ) dsCur - > rots + + ;
if ( uni > = ' 1 ' & & uni < = ' 9 ' ) {
dsCur - > rots = uni - ' 0 ' ;
if ( dsCur - > rots = = 9 ) dsCur - > rots = 21 ;
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2016-08-26 09:58:03 +00:00
}
if ( uni = = ' 0 ' ) {
dsCur - > sym = ! dsCur - > sym ;
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2016-08-26 09:58:03 +00:00
}
if ( uni = = ' t ' ) {
dsCur - > shift = mh ;
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2016-08-26 09:58:03 +00:00
}
if ( uni = = ' y ' ) {
dsCur - > spin = mh ;
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2016-08-26 09:58:03 +00:00
}
if ( uni = = COLORKEY ) dsCur - > color = colortouse ;
}
2019-05-21 21:59:07 +00:00
void writeHyperpoint ( hstream & f , hyperpoint h ) {
println ( f , spaced_of ( & h [ 0 ] , MDIM ) ) ;
2016-08-26 09:58:03 +00:00
}
2019-05-21 21:59:07 +00:00
hyperpoint readHyperpoint ( fhstream & f ) {
2016-08-26 09:58:03 +00:00
hyperpoint h ;
2019-05-21 21:59:07 +00:00
for ( int i = 0 ; i < MDIM ; i + + ) scan ( f , h [ i ] ) ;
2016-08-26 09:58:03 +00:00
return h ;
}
string drawHelpLine ( ) {
return XLAT ( " vector graphics editor " ) ;
}
2024-06-28 21:33:28 +00:00
EX bool onelayeronly ;
2017-09-03 19:11:33 +00:00
2024-06-28 21:33:28 +00:00
EX bool loadPicFile ( const string & s ) {
2024-05-07 20:19:09 +00:00
fhstream f ( s , " rt " ) ;
2019-05-21 21:59:07 +00:00
if ( ! f . f ) {
2024-05-07 20:19:09 +00:00
addMessage ( XLAT ( " Failed to load pictures from %1 " , s ) ) ;
2017-12-14 11:10:40 +00:00
return false ;
2017-12-03 08:52:17 +00:00
}
2019-05-21 21:59:07 +00:00
scanline ( f ) ;
2019-05-21 22:19:03 +00:00
scan ( f , f . vernum ) ;
printf ( " vernum = %x \n " , f . vernum ) ;
if ( f . vernum = = 0 ) {
2024-05-07 20:19:09 +00:00
addMessage ( XLAT ( " Failed to load pictures from %1 " , s ) ) ;
2019-05-21 21:59:07 +00:00
return false ;
2017-12-03 08:52:17 +00:00
}
2017-12-22 20:37:30 +00:00
2019-05-21 22:19:03 +00:00
if ( f . vernum > = 0xA0A0 ) {
2018-07-07 09:05:48 +00:00
int tg , wp ;
int nt ;
2019-05-21 21:59:07 +00:00
scan ( f , tg , nt , wp , patterns : : subpattern_flags ) ;
2018-08-28 02:05:32 +00:00
patterns : : whichPattern = patterns : : ePattern ( wp ) ;
2018-08-28 15:17:34 +00:00
set_geometry ( eGeometry ( tg ) ) ;
set_variation ( eVariation ( nt ) ) ;
2018-06-10 22:58:38 +00:00
start_game ( ) ;
2017-12-22 20:37:30 +00:00
}
2017-12-03 08:52:17 +00:00
while ( true ) {
2019-05-21 21:59:07 +00:00
int i , j , l , sym , rots , siz ;
color_t color ;
if ( ! scan ( f , i , j , l , sym , rots , color , siz ) ) break ;
if ( i = = - 1 ) break ;
2017-12-03 08:52:17 +00:00
if ( siz < 0 | | siz > 1000 ) break ;
2017-12-22 20:37:30 +00:00
if ( i > = 4 ) {
2018-08-28 02:05:32 +00:00
if ( i < 8 ) patterns : : whichPattern = patterns : : ePattern ( " xxxxfpzH " [ i ] ) ;
2017-12-22 20:37:30 +00:00
patterns : : subpattern_flags = 0 ;
i = 3 ;
}
2017-12-03 08:52:17 +00:00
initShape ( i , j ) ;
2024-05-07 20:19:09 +00:00
println ( hlog , " shape " , tie ( i , j ) , " layer " , l ) ;
2017-12-03 08:52:17 +00:00
usershapelayer & ds ( usershapes [ i ] [ j ] - > d [ l ] ) ;
2019-05-21 22:19:03 +00:00
if ( f . vernum > = 0xA608 ) scan ( f , ds . zlevel ) ;
2017-12-03 08:52:17 +00:00
ds . shift = readHyperpoint ( f ) ;
ds . spin = readHyperpoint ( f ) ;
ds . list . clear ( ) ;
for ( int i = 0 ; i < siz ; i + + ) {
ds . list . push_back ( readHyperpoint ( f ) ) ;
2019-05-21 21:59:07 +00:00
writeHyperpoint ( hlog , ds . list [ i ] ) ;
2017-12-03 08:52:17 +00:00
}
ds . sym = sym ;
ds . rots = rots ;
ds . color = color ;
}
addMessage ( XLAT ( " Pictures loaded from %1 " , picfile ) ) ;
2019-05-26 16:04:02 +00:00
usershape_changes + + ;
2017-12-14 11:10:40 +00:00
return true ;
2017-12-03 08:52:17 +00:00
}
2024-06-28 21:33:28 +00:00
EX bool savePicFile ( const string & s ) {
2019-05-21 21:59:07 +00:00
fhstream f ( picfile , " wt " ) ;
if ( ! f . f ) {
2017-12-03 08:52:17 +00:00
addMessage ( XLAT ( " Failed to save pictures to %1 " , picfile ) ) ;
2017-12-14 11:10:40 +00:00
return false ;
2017-12-03 08:52:17 +00:00
}
2019-05-21 21:59:07 +00:00
println ( f , " HyperRogue saved picture " ) ;
2019-05-21 22:19:03 +00:00
println ( f , f . vernum ) ;
if ( f . vernum > = 0xA0A0 )
2019-05-21 21:59:07 +00:00
println ( f , spaced ( geometry , int ( variation ) , patterns : : whichPattern , patterns : : subpattern_flags ) ) ;
2018-08-27 17:27:35 +00:00
for ( int i = 0 ; i < USERSHAPEGROUPS ; i + + ) for ( auto usp : usershapes [ i ] ) {
usershape * us = usp . second ;
2017-12-03 08:52:17 +00:00
if ( ! us ) continue ;
2018-06-22 12:47:24 +00:00
for ( int l = 0 ; l < USERLAYERS ; l + + ) if ( isize ( us - > d [ l ] . list ) ) {
2017-12-03 08:52:17 +00:00
usershapelayer & ds ( us - > d [ l ] ) ;
2019-05-21 21:59:07 +00:00
println ( f , spaced ( i , usp . first , l , ds . sym , ds . rots , ds . color , int ( isize ( ds . list ) ) ) ) ;
print ( f , spaced ( ds . zlevel ) , " " ) ;
2017-12-03 08:52:17 +00:00
writeHyperpoint ( f , ds . shift ) ;
writeHyperpoint ( f , ds . spin ) ;
2019-05-21 21:59:07 +00:00
println ( f ) ;
2018-06-22 12:47:24 +00:00
for ( int i = 0 ; i < isize ( ds . list ) ; i + + )
2017-12-03 08:52:17 +00:00
writeHyperpoint ( f , ds . list [ i ] ) ;
}
}
2019-05-21 21:59:07 +00:00
println ( f , " -1 " ) ;
2017-12-03 08:52:17 +00:00
addMessage ( XLAT ( " Pictures saved to %1 " , picfile ) ) ;
2017-12-14 11:10:40 +00:00
return true ;
2017-12-03 08:52:17 +00:00
}
2017-12-22 21:34:01 +00:00
2024-06-28 21:33:28 +00:00
# endif
EX void handle_key_draw ( int sym , int uni ) {
2017-06-18 16:52:15 +00:00
2019-08-15 13:05:43 +00:00
if ( uni = = PSEUDOKEY_WHEELUP & & GDIM = = 3 & & front_step ) {
2019-05-15 15:38:49 +00:00
front_edit + = front_step * shiftmul ; return ;
}
2019-08-15 13:05:43 +00:00
if ( uni = = PSEUDOKEY_WHEELDOWN & & GDIM = = 3 & & front_step ) {
2019-05-15 15:38:49 +00:00
front_edit - = front_step * shiftmul ; return ;
}
2017-07-10 18:47:38 +00:00
handlePanning ( sym , uni ) ;
2024-06-28 21:33:28 +00:00
dialog : : handleNavigation ( sym , uni ) ;
2016-08-26 09:58:03 +00:00
2017-12-22 20:23:17 +00:00
if ( uni = = SETMOUSEKEY ) {
2017-12-25 09:26:50 +00:00
if ( mousekey = = newmousekey )
2017-12-22 20:23:17 +00:00
mousekey = ' - ' ;
else
mousekey = newmousekey ;
}
2018-08-28 03:39:41 +00:00
2022-03-19 23:38:19 +00:00
shiftpoint mh = full_mouseh ( ) ;
2017-12-22 20:23:17 +00:00
2021-02-01 10:45:52 +00:00
bool freedraw = drawing_tool | | intexture ;
2024-06-28 21:33:28 +00:00
if ( ! freedraw ) return ;
2020-04-17 18:04:33 +00:00
if ( freedraw ) {
2020-07-28 15:50:24 +00:00
if ( lstartcell ) lstart = ggmatrix ( lstartcell ) * lstart_rel ;
2020-07-04 11:33:14 +00:00
2020-04-18 14:10:56 +00:00
int tcolor = ( dtcolor > > 8 ) | ( ( dtcolor & 0xFF ) < < 24 ) ;
2018-01-04 11:25:02 +00:00
2024-06-28 21:33:28 +00:00
if ( uni = = ' - ' ) {
2020-04-17 18:04:33 +00:00
if ( mousekey = = ' e ' ) {
2020-07-28 15:50:24 +00:00
dt_erase ( mh ) ;
2020-04-17 18:04:33 +00:00
}
2020-07-12 19:26:15 +00:00
else if ( mousekey = = ' l ' | | mousekey = = ' c ' | | mousekey = = ' T ' ) {
2020-07-28 15:50:24 +00:00
if ( ! holdmouse ) lstart = mh , lstartcell = mouseover , lstart_rel = inverse_shift ( ggmatrix ( mouseover ) , lstart ) , holdmouse = true ;
2018-01-04 11:25:02 +00:00
}
2020-07-03 12:42:33 +00:00
# if CAP_TEXTURE
2020-04-17 18:04:33 +00:00
else if ( intexture ) {
2018-03-17 20:12:46 +00:00
if ( ! holdmouse ) texture : : config . data . undoLock ( ) ;
2020-07-28 15:50:24 +00:00
texture : : drawPixel ( mouseover , mh , tcolor ) ;
2018-01-04 11:25:02 +00:00
holdmouse = true ; lstartcell = NULL ;
}
2020-07-03 12:42:33 +00:00
# endif
2020-04-17 18:04:33 +00:00
else {
2020-07-28 15:50:24 +00:00
dt_add_free ( mh ) ;
2020-04-17 18:04:33 +00:00
holdmouse = true ;
}
2018-01-04 11:25:02 +00:00
}
if ( sym = = PSEUDOKEY_RELEASE ) {
printf ( " release \n " ) ;
2020-07-03 12:42:33 +00:00
# if CAP_TEXTURE
2020-04-17 18:04:33 +00:00
if ( mousekey = = ' l ' & & intexture ) {
2018-03-17 20:12:46 +00:00
texture : : config . data . undoLock ( ) ;
2018-01-04 11:25:02 +00:00
texture : : where = mouseover ;
2020-07-28 15:50:24 +00:00
texture : : drawPixel ( mouseover , mh , tcolor ) ;
texture : : drawLine ( mh , lstart , tcolor ) ;
2018-01-04 11:25:02 +00:00
lstartcell = NULL ;
}
2020-07-03 12:42:33 +00:00
else
# endif
if ( mousekey = = ' l ' ) {
2020-07-28 15:50:24 +00:00
dt_add_line ( mh , lstart , 10 ) ;
2020-04-17 18:04:33 +00:00
lstartcell = NULL ;
}
2020-07-03 12:42:33 +00:00
# if CAP_TEXTURE
2020-04-17 18:04:33 +00:00
else if ( mousekey = = ' c ' & & intexture ) {
2018-03-17 20:12:46 +00:00
texture : : config . data . undoLock ( ) ;
2020-07-28 15:50:24 +00:00
ld rad = hdist ( lstart , mh ) ;
2020-04-18 14:10:56 +00:00
int circp = int ( 1 + 3 * ( circlelength ( rad ) / dtwidth ) ) ;
2018-01-04 11:25:02 +00:00
if ( circp > 1000 ) circp = 1000 ;
2020-07-27 16:49:04 +00:00
shiftmatrix T = rgpushxto0 ( lstart ) ;
2018-01-04 11:25:02 +00:00
texture : : where = lstartcell ;
for ( int i = 0 ; i < circp ; i + + )
2022-11-12 21:38:45 +00:00
texture : : drawPixel ( T * xspinpush0 ( TAU * i / circp , rad ) , tcolor ) ;
2018-01-04 11:25:02 +00:00
lstartcell = NULL ;
}
2020-07-03 12:42:33 +00:00
# endif
2020-04-17 18:04:33 +00:00
else if ( mousekey = = ' c ' ) {
2020-07-28 15:50:24 +00:00
dt_add_circle ( lstart , mh ) ;
2020-04-17 18:04:33 +00:00
lstartcell = NULL ;
}
2020-07-12 19:26:15 +00:00
else if ( mousekey = = ' T ' ) {
static string text = " " ;
dialog : : edit_string ( text , " " , " " ) ;
2020-07-28 15:50:24 +00:00
shiftpoint h = mh ;
2023-08-09 12:01:24 +00:00
dialog : : get_di ( ) . reaction_final = [ h ] {
2020-07-12 19:26:15 +00:00
if ( text ! = " " )
dt_add_text ( h , dtwidth * 50 , text ) ;
} ;
lstartcell = nullptr ;
}
2020-09-21 10:01:22 +00:00
else
dt_finish ( ) ;
2017-12-22 21:34:01 +00:00
}
if ( uni > = 1000 & & uni < 1010 )
2020-04-18 14:10:56 +00:00
dtcolor = texture_colors [ uni - 1000 + 1 ] ;
2017-12-22 21:34:01 +00:00
if ( uni > = 2000 & & uni < 2010 )
2020-04-18 14:10:56 +00:00
dtwidth = brush_sizes [ uni - 2000 ] ;
2017-12-18 22:42:08 +00:00
2020-04-17 18:04:33 +00:00
if ( uni = = ' f ' ) {
2020-04-18 14:10:56 +00:00
if ( dtfill = = dtcolor )
2020-04-17 18:04:33 +00:00
dtfill = 0 ;
else
2020-04-18 14:10:56 +00:00
dtfill = dtcolor ;
2017-12-18 22:42:08 +00:00
}
2017-12-17 23:24:56 +00:00
}
2016-08-26 09:58:03 +00:00
}
2017-07-10 18:47:38 +00:00
2020-04-11 18:40:12 +00:00
auto hooks = addHook ( hooks_clearmemory , 0 , [ ] ( ) {
2017-07-10 18:47:38 +00:00
if ( mapeditor : : painttype = = 4 )
mapeditor : : painttype = 0 , mapeditor : : paintwhat = 0 ,
mapeditor : : paintwhat_str = " clear monster " ;
2018-08-17 22:46:45 +00:00
mapeditor : : copysource . at = NULL ;
2017-07-10 18:47:38 +00:00
mapeditor : : undo . clear ( ) ;
2018-06-28 00:49:26 +00:00
if ( ! cheater ) patterns : : displaycodes = false ;
2017-12-09 02:48:30 +00:00
if ( ! cheater ) patterns : : whichShape = 0 ;
2017-07-10 18:47:38 +00:00
modelcell . clear ( ) ;
2020-04-17 18:04:33 +00:00
mapeditor : : dtshapes . clear ( ) ;
2020-09-21 10:01:22 +00:00
dt_finish ( ) ;
2020-04-17 18:34:49 +00:00
drawcell = nullptr ;
2018-01-26 00:45:49 +00:00
} ) +
addHook ( hooks_removecells , 0 , [ ] ( ) {
modelcell . clear ( ) ;
2018-08-17 22:46:45 +00:00
set_if_removed ( mapeditor : : copysource . at , NULL ) ;
2020-02-23 01:51:27 +00:00
} ) ;
2017-03-23 10:53:57 +00:00
# endif
2019-08-09 23:15:41 +00:00
EX void initdraw ( cell * c ) {
2019-06-13 16:11:01 +00:00
# if CAP_EDIT
2017-06-18 16:52:15 +00:00
mapeditor : : drawcell = c ;
ew . c = c ;
ew . rotid = 0 ;
ew . symid = 0 ;
ew . pointid = - 1 ;
ew . side = 0 ;
ewsearch = ew ;
2019-05-15 15:38:49 +00:00
ccenter = coldcenter = C0 ;
2019-06-01 14:59:04 +00:00
# endif
2017-06-18 16:52:15 +00:00
}
2017-12-17 23:24:56 +00:00
transmatrix textrans ;
2018-01-04 11:25:02 +00:00
2020-07-27 16:49:04 +00:00
EX void queue_hcircle ( shiftmatrix Ctr , ld radius ) {
2018-01-04 11:25:02 +00:00
vector < hyperpoint > pts ;
2018-08-01 09:07:22 +00:00
int circp = int ( 6 * pow ( 2 , vid . linequality ) ) ;
2018-01-04 11:25:02 +00:00
if ( radius > 0.04 ) circp * = 2 ;
if ( radius > .1 ) circp * = 2 ;
for ( int j = 0 ; j < circp ; j + + )
2022-11-12 21:38:45 +00:00
pts . push_back ( xspinpush0 ( TAU * j / circp , radius ) ) ;
2018-08-01 09:07:22 +00:00
for ( int j = 0 ; j < circp ; j + + ) curvepoint ( pts [ j ] ) ;
curvepoint ( pts [ 0 ] ) ;
2020-07-27 16:49:04 +00:00
queuecurve ( Ctr , dtcolor , 0 , PPR : : LINE ) ;
2018-01-04 11:25:02 +00:00
}
2018-01-05 16:30:03 +00:00
2019-02-17 17:41:40 +00:00
# if CAP_POLY
2019-09-06 06:17:02 +00:00
EX bool haveUserShape ( eShapegroup group , int id ) {
2018-08-27 17:27:35 +00:00
# if !CAP_EDIT
return false ;
# else
return usershapes [ group ] . count ( id ) & & usershapes [ group ] [ id ] ;
# endif
}
2019-02-17 17:41:40 +00:00
# endif
2019-01-03 14:12:45 +00:00
2019-02-17 17:41:40 +00:00
# if CAP_TEXTURE
2020-07-27 16:49:04 +00:00
EX void draw_texture_ghosts ( cell * c , const shiftmatrix & V ) {
2019-01-03 14:12:45 +00:00
if ( ! c ) return ;
if ( holdmouse & & ! lstartcell ) return ;
cell * ls = lstartcell ? lstartcell : lmouseover ;
if ( ! ls ) return ;
auto sio = patterns : : getpatterninfo0 ( ls ) ;
auto sih = patterns : : getpatterninfo0 ( c ) ;
if ( sio . id = = sih . id ) {
if ( c = = ls )
2020-09-16 03:57:05 +00:00
textrans = z_inverse ( V . T * applyPatterndir ( ls , sio ) ) ;
2019-01-03 14:12:45 +00:00
2020-07-27 16:49:04 +00:00
transmatrix mh = textrans * rgpushxto0 ( unshift ( mouseh , V . shift ) ) ;
transmatrix ml = textrans * rgpushxto0 ( unshift ( lstart , V . shift ) ) ;
2019-01-14 21:56:52 +00:00
2019-01-03 14:12:45 +00:00
for ( int j = 0 ; j < = texture : : texturesym ; j + + )
for ( int i = 0 ; i < c - > type ; i + = sih . symmetries ) {
2022-11-12 21:38:45 +00:00
shiftmatrix M2 = V * applyPatterndir ( c , sih ) * spin ( TAU * i / c - > type ) ;
2019-01-03 14:12:45 +00:00
if ( j ) M2 = M2 * Mirror ;
switch ( holdmouse ? mousekey : ' d ' ) {
case ' c ' :
queue_hcircle ( M2 * ml , hdist ( lstart , mouseh ) ) ;
break ;
case ' l ' :
2020-04-18 14:10:56 +00:00
queueline ( M2 * mh * C0 , M2 * ml * C0 , dtcolor , 4 + vid . linequality , PPR : : LINE ) ;
2019-01-03 14:12:45 +00:00
break ;
default :
2020-04-18 14:10:56 +00:00
queue_hcircle ( M2 * mh , dtwidth ) ;
2019-01-03 14:12:45 +00:00
}
}
}
}
2019-02-17 17:41:40 +00:00
# endif
2018-08-27 17:27:35 +00:00
2019-02-17 17:41:40 +00:00
# if CAP_POLY
2020-07-27 16:49:04 +00:00
EX bool drawUserShape ( const shiftmatrix & V , eShapegroup group , int id , color_t color , cell * c , PPR prio IS ( PPR : : DEFAULT ) ) {
2017-08-06 12:50:16 +00:00
# if !CAP_EDIT
2017-06-18 16:52:15 +00:00
return false ;
# else
2017-12-03 17:24:34 +00:00
2018-08-28 12:09:36 +00:00
// floors handled separately
if ( c & & c = = drawcell & & editingShape ( group , id ) & & group ! = sgFloor )
2018-08-28 11:45:11 +00:00
drawtrans = V ;
2017-06-18 16:52:15 +00:00
usershape * us = usershapes [ group ] [ id ] ;
2024-06-28 21:33:28 +00:00
if ( us ) {
2019-05-26 16:04:02 +00:00
cgi . require_usershapes ( ) ;
2017-06-18 16:52:15 +00:00
for ( int i = 0 ; i < USERLAYERS ; i + + ) {
2017-09-03 19:11:33 +00:00
if ( i ! = dslayer & & onelayeronly ) continue ;
2017-06-18 16:52:15 +00:00
usershapelayer & ds ( us - > d [ i ] ) ;
2019-05-26 16:04:02 +00:00
hpcshape & sh ( cgi . ushr [ & ds ] ) ;
2017-06-18 16:52:15 +00:00
2019-04-07 01:08:43 +00:00
if ( sh . s ! = sh . e ) {
2022-12-06 00:04:26 +00:00
shiftmatrix V1 = GDIM = = 3 ? V : orthogonal_move ( V , ds . zlevel ) ;
auto & last = queuepolyat ( V1 , sh , ds . color ? ds . color : color , prio ) ;
2019-08-15 13:05:43 +00:00
if ( GDIM = = 3 ) {
2019-04-07 01:08:43 +00:00
last . tinf = & user_triangles_texture ;
last . offset_texture = ds . texture_offset ;
}
}
2017-06-18 16:52:15 +00:00
}
}
2018-01-04 11:25:02 +00:00
if ( cmode & sm : : DRAW ) {
2017-12-21 10:36:07 +00:00
2019-02-28 18:02:01 +00:00
if ( c = = drawcell & & EDITING_TRIANGLES & & mapeditor : : editingShape ( group , id ) ) {
if ( ! us ) return false ;
usershapelayer & ds ( us - > d [ mapeditor : : dslayer ] ) ;
for ( int i = 0 ; i < isize ( ds . list ) ; i + + ) {
int j = ( i % 3 = = 2 ? i - 2 : i + 1 ) ;
if ( j < isize ( ds . list ) )
queueline ( V * ds . list [ i ] , V * ds . list [ j ] , 0xFF00FFFF , - 1 , PPR : : SUPERLINE ) ;
2019-12-26 22:38:28 +00:00
queuestr ( V * ds . list [ i ] , 10 , " x " , 0xFF00FF ) ;
2019-02-28 18:02:01 +00:00
}
}
if ( mapeditor : : editingShape ( group , id ) & & ! EDITING_TRIANGLES ) {
2017-06-18 16:52:15 +00:00
2018-06-22 12:47:24 +00:00
/* for(int a=0; a<isize(ds.list); a++) {
2017-12-17 23:24:56 +00:00
hyperpoint P2 = V * ds . list [ a ] ;
int xc , yc , sc ;
getcoord ( P2 , xc , yc , sc ) ;
2019-12-26 22:38:28 +00:00
queuestr ( xc , yc , sc , 10 , " x " ,
2017-12-17 23:24:56 +00:00
a = = 0 ? 0x00FF00 :
2018-06-22 12:47:24 +00:00
a = = isize ( ds . list ) - 1 ? 0xFF0000 :
2017-12-17 23:24:56 +00:00
0xFFFF00 ) ;
} */
if ( ! us ) return false ;
usershapelayer & ds ( us - > d [ mapeditor : : dslayer ] ) ;
2022-03-19 23:38:19 +00:00
shiftpoint moh = full_mouseh ( ) ;
2020-07-28 15:50:24 +00:00
hyperpoint mh = inverse_shift ( mapeditor : : drawtrans , moh ) ;
2017-12-17 23:24:56 +00:00
for ( int a = 0 ; a < ds . rots ; a + + )
for ( int b = 0 ; b < ( ds . sym ? 2 : 1 ) ; b + + ) {
if ( mouseout ( ) ) break ;
2022-11-12 21:38:45 +00:00
shiftpoint P2 = V * spin ( TAU * a / ds . rots ) * ( b ? Mirror * mh : mh ) ;
2017-12-17 23:24:56 +00:00
2019-12-26 22:38:28 +00:00
queuestr ( P2 , 10 , " x " , 0xFF00FF ) ;
2017-12-17 23:24:56 +00:00
}
2018-06-22 12:47:24 +00:00
if ( isize ( ds . list ) = = 0 ) return us ;
2017-12-17 23:24:56 +00:00
2022-11-12 21:38:45 +00:00
shiftpoint Plast = V * spin ( - TAU / ds . rots ) * ( ds . sym ? Mirror * ds . list [ 0 ] : ds . list [ isize ( ds . list ) - 1 ] ) ;
2017-12-17 23:24:56 +00:00
int state = 0 ;
int gstate = 0 ;
double dist2 = 0 ;
2020-07-27 16:49:04 +00:00
shiftpoint lpsm ;
2017-12-17 23:24:56 +00:00
for ( int a = 0 ; a < ds . rots ; a + + )
for ( int b = 0 ; b < ( ds . sym ? 2 : 1 ) ; b + + ) {
2022-11-12 21:38:45 +00:00
hyperpoint mh2 = spin ( TAU * - ew . rotid / ds . rots ) * mh ;
2017-12-17 23:24:56 +00:00
if ( ew . symid ) mh2 = Mirror * mh2 ;
2022-11-12 21:38:45 +00:00
shiftpoint pseudomouse = V * spin ( TAU * a / ds . rots ) * mirrorif ( mh2 , b ) ;
2017-12-17 23:24:56 +00:00
2018-06-22 12:47:24 +00:00
for ( int t = 0 ; t < isize ( ds . list ) ; t + + ) {
int ti = b ? isize ( ds . list ) - 1 - t : t ;
2017-06-18 16:52:15 +00:00
2022-11-12 21:38:45 +00:00
shiftpoint P2 = V * spin ( TAU * a / ds . rots ) * mirrorif ( ds . list [ ti ] , b ) ;
2017-12-17 23:24:56 +00:00
if ( ! mouseout ( ) ) {
2020-07-28 15:50:24 +00:00
double d = hdist ( moh , P2 ) ;
2017-12-17 23:24:56 +00:00
if ( d < ewsearch . dist )
ewsearch . dist = d ,
ewsearch . rotid = a ,
ewsearch . symid = b ,
ewsearch . pointid = ti ,
ewsearch . c = c ,
ewsearch . side = b ,
state = 1 ,
2020-07-28 15:50:24 +00:00
dist2 = d + hdist ( moh , Plast ) - hdist ( P2 , Plast ) ;
2017-12-17 23:24:56 +00:00
else if ( state = = 1 ) {
2020-07-28 15:50:24 +00:00
double dist3 = d + hdist ( moh , Plast ) - hdist ( P2 , Plast ) ;
2017-12-17 23:24:56 +00:00
if ( dist3 < dist2 )
ewsearch . side = ! ewsearch . side ;
state = 2 ;
}
2017-06-18 16:52:15 +00:00
}
2017-12-17 23:24:56 +00:00
2019-12-26 22:38:28 +00:00
queuestr ( P2 , 10 , " o " ,
2017-12-17 23:24:56 +00:00
0xC000C0 ) ;
if ( ! mouseout ( ) ) {
if ( gstate = = 1 ) queueline ( lpsm , P2 , 0x90000080 ) , gstate = 0 ;
if ( ti = = ew . pointid ) {
queueline ( pseudomouse , P2 , 0xF0000080 ) ;
2018-07-07 09:05:48 +00:00
if ( ew . side = = ( b = = 1 ) ) queueline ( pseudomouse , Plast , 0x90000080 ) ;
2017-12-17 23:24:56 +00:00
else gstate = 1 , lpsm = pseudomouse ;
}
2017-06-18 16:52:15 +00:00
}
2017-12-17 23:24:56 +00:00
Plast = P2 ;
2017-06-18 16:52:15 +00:00
}
2017-12-17 23:24:56 +00:00
}
if ( gstate = = 1 ) queueline ( lpsm , V * ds . list [ 0 ] , 0x90000080 ) , gstate = 0 ;
if ( state = = 1 ) {
2020-07-27 16:49:04 +00:00
shiftpoint P2 = V * ds . list [ 0 ] ;
2020-07-28 15:50:24 +00:00
if ( hdist ( moh , P2 ) + hdist ( moh , Plast ) - hdist ( P2 , Plast ) < dist2 )
2017-12-17 23:24:56 +00:00
ewsearch . side = 1 ;
2017-06-18 16:52:15 +00:00
}
}
}
return us ;
# endif
}
2019-02-17 17:41:40 +00:00
# endif
2018-08-28 11:45:11 +00:00
2022-03-07 03:01:59 +00:00
2022-04-22 23:00:59 +00:00
EX string canvasFloorName ( int id ) {
2022-03-07 03:01:59 +00:00
if ( id > = 0 & & id < caflEND )
return XLAT ( canvasFloorNames [ id ] ) ;
return its ( id ) ;
}
string allCanvasFloorNames ( ) {
string ret ;
for ( int i = 0 ; i < caflEND ; i + + ) {
ret + = its ( i ) + " : " + canvasFloorName ( i ) + " " ;
}
return ret ;
}
2019-08-09 23:15:41 +00:00
EX void map_settings ( ) {
2018-12-24 14:10:52 +00:00
cmode = sm : : SIDE | sm : : MAYDARK ;
2022-07-05 09:51:06 +00:00
gamescreen ( ) ;
2018-12-24 14:10:52 +00:00
dialog : : init ( XLAT ( " Map settings " ) ) ;
2019-05-03 10:11:40 +00:00
dialog : : addBoolItem_action_neg ( XLAT ( " disable wandering monsters " ) , gen_wandering , ' w ' ) ;
2018-12-24 14:10:52 +00:00
if ( gen_wandering ) {
2019-05-03 10:11:40 +00:00
dialog : : addBoolItem_action_neg ( XLAT ( " disable ghost timer " ) , timerghost , ' g ' ) ;
2018-12-24 14:10:52 +00:00
}
else dialog : : addBreak ( 100 ) ;
2019-05-03 10:11:40 +00:00
dialog : : addBoolItem_action ( XLAT ( " simple pattern generation " ) , reptilecheat , ' p ' ) ;
2019-01-18 20:03:55 +00:00
dialog : : addInfo ( XLAT ( " (e.g. pure Reptile pattern) " ) ) ;
2018-12-24 14:10:52 +00:00
2019-05-03 10:11:40 +00:00
dialog : : addBoolItem_action ( XLAT ( " safety generation " ) , safety , ' s ' ) ;
2019-01-18 20:03:55 +00:00
dialog : : addInfo ( XLAT ( " (no treasure, no dangers) " ) ) ;
2018-12-24 14:10:52 +00:00
2019-01-18 20:03:55 +00:00
dialog : : addBoolItem ( XLAT ( " god mode " ) , autocheat , ' G ' ) ;
2018-12-24 14:10:52 +00:00
dialog : : add_action ( [ ] ( ) { autocheat = true ; } ) ;
2019-01-18 20:03:55 +00:00
dialog : : addInfo ( XLAT ( " (unlock all, allow cheats, normal character display, cannot be turned off!) " ) ) ;
2022-10-24 07:39:01 +00:00
add_edit ( game_keys_scroll ) ;
dialog : : addInfo ( XLAT ( " hint: shift+A to enter the map editor " ) ) ;
2018-12-24 14:10:52 +00:00
2023-04-14 23:27:35 +00:00
# if CAP_PORTALS
2022-10-26 22:01:32 +00:00
if ( WDIM = = 3 & & ! intra : : in ) {
dialog : : addBoolItem ( XLAT ( " become a portal map " ) , intra : : in , ' m ' ) ;
dialog : : add_action_push ( intra : : become_menu ) ;
}
if ( WDIM = = 3 & & intra : : in ) {
dialog : : addItem ( XLAT ( " manage portals " ) , ' m ' ) ;
dialog : : add_action_push ( intra : : show_portals ) ;
}
2023-04-11 14:44:22 +00:00
# endif
2022-10-26 22:01:32 +00:00
2019-01-18 20:03:55 +00:00
dialog : : addItem ( XLAT ( " change the pattern/color of new Canvas cells " ) , ' c ' ) ;
2019-03-30 22:59:51 +00:00
dialog : : add_action_push ( patterns : : showPrePatternNoninstant ) ;
2020-02-26 00:39:41 +00:00
dialog : : addItem ( XLAT ( " configure WFC " ) , ' W ' ) ;
dialog : : add_action_push ( wfc : : wfc_menu ) ;
2021-06-16 11:14:30 +00:00
dialog : : addItem ( XLAT ( " edit cell values " ) , ' G ' ) ;
dialog : : add_action ( push_debug_screen ) ;
2022-03-07 03:01:59 +00:00
dialog : : addSelItem ( XLAT ( " canvas floor shape " ) , canvasFloorName ( canvasfloor ) , ' S ' ) ;
dialog : : add_action ( [ ] {
dialog : : editNumber ( canvasfloor , 0 , caflEND - 1 , 1 , 0 , XLAT ( " canvas floor shape " ) , allCanvasFloorNames ( ) ) ;
} ) ;
dialog : : addSelItem ( XLAT ( " canvas darkness " ) , its ( canvasdark ) , ' d ' ) ;
dialog : : add_action ( [ ] {
dialog : : editNumber ( canvasdark , 0 , 2 , 1 , 0 , XLAT ( " canvas darkness " ) ,
" 0: no darkening (bright mode, canvas, reptiles, etc) \n "
" 1: light darkening (r'lyeh, palace, dragon chasms, etc) \n "
" 2: normal darkening (default, most lands) " ) ;
} ) ;
2019-01-16 23:59:45 +00:00
2018-12-24 14:10:52 +00:00
dialog : : addBack ( ) ;
dialog : : display ( ) ;
}
2019-08-09 23:15:41 +00:00
EX }
2017-05-27 19:40:40 +00:00
2018-07-19 21:46:58 +00:00
# if CAP_EDIT
2019-08-09 21:39:36 +00:00
EX string levelfile = " hyperrogue.lev " ;
EX const char * loadlevel = NULL ;
EX string picfile = " hyperrogue.pic " ;
2018-07-19 21:46:58 +00:00
2018-07-22 10:50:03 +00:00
# if CAP_COMMANDLINE
2018-07-19 21:46:58 +00:00
int read_editor_args ( ) {
using namespace arg ;
if ( argis ( " -lev " ) ) { shift ( ) ; levelfile = args ( ) ; }
else if ( argis ( " -pic " ) ) { shift ( ) ; picfile = args ( ) ; }
else if ( argis ( " -load " ) ) { PHASE ( 3 ) ; shift ( ) ; mapstream : : loadMap ( args ( ) ) ; }
2021-03-31 09:37:29 +00:00
else if ( argis ( " -save " ) ) { PHASE ( 3 ) ; shift ( ) ; mapstream : : saveMap ( args ( ) . c_str ( ) ) ; }
2020-05-01 10:27:49 +00:00
else if ( argis ( " -d:draw " ) ) { PHASE ( 3 ) ;
# if CAP_EDIT
start_game ( ) ;
mapeditor : : drawing_tool = true ;
mapeditor : : initdraw ( cwt . at ) ;
launch_dialog ( mapeditor : : showDrawEditor ) ;
# endif
}
2019-02-17 17:41:40 +00:00
# if CAP_POLY
2020-12-31 15:35:07 +00:00
else if ( argis ( " -dred " ) ) {
PHASEFROM ( 2 ) ;
mapeditor : : dtcolor = 0xFF0000FF ;
mapeditor : : dtwidth = 0.1 ;
}
2018-07-19 21:46:58 +00:00
else if ( argis ( " -picload " ) ) { PHASE ( 3 ) ; shift ( ) ; mapeditor : : loadPicFile ( args ( ) ) ; }
2019-02-17 17:41:40 +00:00
# endif
2018-07-19 21:46:58 +00:00
else return 1 ;
return 0 ;
}
auto ah_editor = addHook ( hooks_args , 0 , read_editor_args ) ;
# endif
# endif
2018-06-10 23:58:31 +00:00
}