2022-06-11 01:59:57 +00:00
// Config Needs To Load First
# include <libreborn/libreborn.h>
2024-02-04 02:07:53 +00:00
# include "game-mode-internal.h"
2022-06-11 01:59:57 +00:00
// Game Mode UI Code Is Useless In Headless Mode
2024-02-04 02:07:53 +00:00
# ifndef MCPI_HEADLESS_MODE
2022-04-28 03:38:30 +00:00
# include <string>
2024-02-02 09:20:34 +00:00
# include <set>
2022-04-28 03:38:30 +00:00
# include <symbols/minecraft.h>
2024-02-02 09:20:34 +00:00
# include <mods/text-input-box/TextInputScreen.h>
# include <mods/touch/touch.h>
2022-04-28 03:38:30 +00:00
2024-02-02 09:20:34 +00:00
// Strings
# define GAME_MODE_STR(mode) ("Game Mode: " mode)
# define SURVIVAL_STR GAME_MODE_STR("Survival")
# define CREATIVE_STR GAME_MODE_STR("Creative")
2022-04-28 03:38:30 +00:00
2024-02-02 09:20:34 +00:00
// Structure
struct CreateWorldScreen {
TextInputScreen super ;
TextInputBox * name ;
TextInputBox * seed ;
Button * game_mode ;
Button * create ;
Button * back ;
2024-01-06 11:30:23 +00:00
} ;
2024-02-02 09:20:34 +00:00
static void create_world ( Minecraft * minecraft , std : : string name , bool is_creative , std : : string seed ) ;
CUSTOM_VTABLE ( create_world_screen , Screen ) {
TextInputScreen : : setup ( vtable ) ;
// Constants
static int line_height = 8 ;
static int bottom_padding = 4 ;
static int inner_padding = 4 ;
static int description_padding = 4 ;
static int title_padding = 8 ;
// Init
static Screen_init_t original_init = vtable - > init ;
vtable - > init = [ ] ( Screen * super ) {
original_init ( super ) ;
CreateWorldScreen * self = ( CreateWorldScreen * ) super ;
// Name
self - > name = TextInputBox : : create ( " World Name " , " Unnamed world " ) ;
self - > super . m_textInputs - > push_back ( self - > name ) ;
self - > name - > init ( super - > font ) ;
self - > name - > setFocused ( true ) ;
// Seed
self - > seed = TextInputBox : : create ( " Seed " ) ;
self - > super . m_textInputs - > push_back ( self - > seed ) ;
self - > seed - > init ( super - > font ) ;
self - > seed - > setFocused ( false ) ;
2022-04-28 03:38:30 +00:00
// Game Mode
2024-02-02 09:20:34 +00:00
self - > game_mode = touch_create_button ( 1 , CREATIVE_STR ) ;
super - > rendered_buttons . push_back ( self - > game_mode ) ;
super - > selectable_buttons . push_back ( self - > game_mode ) ;
// Create
self - > create = touch_create_button ( 2 , " Create " ) ;
super - > rendered_buttons . push_back ( self - > create ) ;
super - > selectable_buttons . push_back ( self - > create ) ;
// Back
self - > back = touch_create_button ( 3 , " Back " ) ;
super - > rendered_buttons . push_back ( self - > back ) ;
super - > selectable_buttons . push_back ( self - > back ) ;
} ;
// Removal
static Screen_removed_t original_removed = vtable - > removed ;
vtable - > removed = [ ] ( Screen * super ) {
original_removed ( super ) ;
CreateWorldScreen * self = ( CreateWorldScreen * ) super ;
delete self - > name ;
delete self - > seed ;
self - > game_mode - > vtable - > destructor_deleting ( self - > game_mode ) ;
self - > back - > vtable - > destructor_deleting ( self - > back ) ;
self - > create - > vtable - > destructor_deleting ( self - > create ) ;
} ;
// Rendering
static Screen_render_t original_render = vtable - > render ;
vtable - > render = [ ] ( Screen * super , int x , int y , float param_1 ) {
// Background
super - > vtable - > renderBackground ( super ) ;
// Call Original Method
original_render ( super , x , y , param_1 ) ;
// Title
std : : string title = " Create world " ;
Screen_drawCenteredString ( super , super - > font , & title , super - > width / 2 , title_padding , 0xffffffff ) ;
// Game Mode Description
CreateWorldScreen * self = ( CreateWorldScreen * ) super ;
bool is_creative = self - > game_mode - > text = = CREATIVE_STR ;
std : : string description = is_creative ? Strings_creative_mode_description : Strings_survival_mode_description ;
Screen_drawString ( super , super - > font , & description , self - > game_mode - > x , self - > game_mode - > y + self - > game_mode - > height + description_padding , 0xa0a0a0 ) ;
} ;
// Positioning
vtable - > setupPositions = [ ] ( Screen * super ) {
Screen_setupPositions_non_virtual ( super ) ;
CreateWorldScreen * self = ( CreateWorldScreen * ) super ;
// Height/Width
int width = 120 ;
int height = 24 ;
self - > create - > width = self - > back - > width = self - > game_mode - > width = width ;
int seed_width = self - > game_mode - > width ;
int name_width = width * 1.5f ;
self - > create - > height = self - > back - > height = self - > game_mode - > height = height ;
int text_box_height = self - > game_mode - > height ;
// Find Center Y
int top = ( title_padding * 2 ) + line_height ;
int bottom = super - > height - self - > create - > height - ( bottom_padding * 2 ) ;
int center_y = ( ( bottom - top ) / 2 ) + top ;
center_y - = ( description_padding + line_height ) / 2 ;
// X/Y
self - > create - > y = self - > back - > y = super - > height - bottom_padding - height ;
self - > create - > x = self - > game_mode - > x = ( super - > width / 2 ) - inner_padding - width ;
self - > back - > x = ( super - > width / 2 ) + inner_padding ;
int seed_x = self - > back - > x ;
int name_x = ( super - > width / 2 ) - ( name_width / 2 ) ;
int name_y = center_y - inner_padding - height ;
self - > game_mode - > y = center_y + inner_padding ;
int seed_y = self - > game_mode - > y ;
// Update Text Boxes
self - > name - > setSize ( name_x , name_y , name_width , text_box_height ) ;
self - > seed - > setSize ( seed_x , seed_y , seed_width , text_box_height ) ;
} ;
// ESC
vtable - > handleBackEvent = [ ] ( Screen * super , bool do_nothing ) {
if ( ! do_nothing ) {
ScreenChooser_setScreen ( & super - > minecraft - > screen_chooser , 5 ) ;
2022-04-28 03:38:30 +00:00
}
2024-02-02 09:20:34 +00:00
return true ;
} ;
// Button Click
vtable - > buttonClicked = [ ] ( Screen * super , Button * button ) {
CreateWorldScreen * self = ( CreateWorldScreen * ) super ;
bool is_creative = self - > game_mode - > text = = CREATIVE_STR ;
if ( button = = self - > game_mode ) {
// Toggle Game Mode
self - > game_mode - > text = is_creative ? SURVIVAL_STR : CREATIVE_STR ;
} else if ( button = = self - > back ) {
// Back
super - > vtable - > handleBackEvent ( super , false ) ;
} else if ( button = = self - > create ) {
// Create
create_world ( super - > minecraft , self - > name - > getText ( ) , is_creative , self - > seed - > getText ( ) ) ;
2022-04-28 03:38:30 +00:00
}
2024-02-02 09:20:34 +00:00
} ;
}
static Screen * create_create_world_screen ( ) {
// Construct
CreateWorldScreen * screen = new CreateWorldScreen ;
ALLOC_CHECK ( screen ) ;
Screen_constructor ( & screen - > super . super ) ;
2022-04-28 03:38:30 +00:00
2024-02-02 09:20:34 +00:00
// Set VTable
screen - > super . super . vtable = get_create_world_screen_vtable ( ) ;
2022-04-28 03:38:30 +00:00
// Return
2024-02-02 09:20:34 +00:00
return ( Screen * ) screen ;
2022-04-28 03:38:30 +00:00
}
2024-02-02 09:20:34 +00:00
// Unique Level Name (https://github.com/ReMinecraftPE/mcpe/blob/d7a8b6baecf8b3b050538abdbc976f690312aa2d/source/client/gui/screens/CreateWorldScreen.cpp#L65-L83)
static std : : string getUniqueLevelName ( LevelStorageSource * source , const std : : string & in ) {
std : : set < std : : string > maps ;
std : : vector < LevelSummary > vls ;
source - > vtable - > getLevelList ( source , & vls ) ;
for ( int i = 0 ; i < int ( vls . size ( ) ) ; i + + ) {
const LevelSummary & ls = vls [ i ] ;
maps . insert ( ls . folder ) ;
}
std : : string out = in ;
while ( maps . find ( out ) ! = maps . end ( ) ) {
out + = " - " ;
}
return out ;
2022-04-28 03:38:30 +00:00
}
// Create World
2024-02-02 09:20:34 +00:00
static void create_world ( Minecraft * minecraft , std : : string name , bool is_creative , std : : string seed_str ) {
// Get Seed
int seed ;
seed_str = Util_stringTrim ( & seed_str ) ;
if ( ! seed_str . empty ( ) ) {
int num ;
if ( sscanf ( seed_str . c_str ( ) , " %d " , & num ) > 0 ) {
seed = num ;
} else {
seed = Util_hashCode ( & seed_str ) ;
}
} else {
seed = Common_getEpochTimeS ( ) ;
}
// Get Folder Name
name = Util_stringTrim ( & name ) ;
std : : string folder = " " ;
for ( char c : name ) {
if (
c > = ' ' & & c < = ' ~ ' & &
c ! = ' / ' & &
c ! = ' \\ ' & &
c ! = ' ` ' & &
c ! = ' ? ' & &
c ! = ' * ' & &
c ! = ' < ' & &
c ! = ' > ' & &
c ! = ' | ' & &
c ! = ' " ' & &
c ! = ' : '
) {
folder + = c ;
}
}
if ( folder . empty ( ) ) {
folder = " World " ;
}
folder = getUniqueLevelName ( Minecraft_getLevelSource ( minecraft ) , folder ) ;
2022-04-28 03:38:30 +00:00
// Settings
LevelSettings settings ;
2024-02-02 09:20:34 +00:00
settings . game_type = is_creative ;
settings . seed = seed ;
2022-04-28 03:38:30 +00:00
// Create World
2024-02-02 09:20:34 +00:00
minecraft - > vtable - > selectLevel ( minecraft , & folder , & name , & settings ) ;
2022-04-28 03:38:30 +00:00
// Multiplayer
2024-01-07 08:23:43 +00:00
Minecraft_hostMultiplayer ( minecraft , 19132 ) ;
2022-04-28 03:38:30 +00:00
// Open ProgressScreen
2024-01-06 11:30:23 +00:00
ProgressScreen * screen = alloc_ProgressScreen ( ) ;
2022-04-28 03:38:30 +00:00
ALLOC_CHECK ( screen ) ;
2024-01-07 08:23:43 +00:00
screen = ProgressScreen_constructor ( screen ) ;
Minecraft_setScreen ( minecraft , ( Screen * ) screen ) ;
2022-04-28 03:38:30 +00:00
}
// Redirect Create World Button
# define create_SelectWorldScreen_tick_injection(prefix) \
2024-01-06 11:30:23 +00:00
static void prefix # # SelectWorldScreen_tick_injection ( prefix # # SelectWorldScreen * screen ) { \
2024-02-02 09:20:34 +00:00
if ( screen - > should_create_world ) { \
/* Open Screen */ \
Minecraft_setScreen ( screen - > minecraft , create_create_world_screen ( ) ) ; \
2022-04-28 03:38:30 +00:00
/* Finish */ \
2024-02-02 09:20:34 +00:00
screen - > should_create_world = false ; \
2022-04-28 03:38:30 +00:00
} else { \
/* Call Original Method */ \
2024-01-07 08:23:43 +00:00
prefix # # SelectWorldScreen_tick_non_virtual ( screen ) ; \
2022-04-28 03:38:30 +00:00
} \
}
create_SelectWorldScreen_tick_injection ( )
create_SelectWorldScreen_tick_injection ( Touch_ )
// Init
void _init_game_mode_ui ( ) {
// Hijack Create World Button
patch_address ( SelectWorldScreen_tick_vtable_addr , ( void * ) SelectWorldScreen_tick_injection ) ;
patch_address ( Touch_SelectWorldScreen_tick_vtable_addr , ( void * ) Touch_SelectWorldScreen_tick_injection ) ;
}
# else
void _init_game_mode_ui ( ) {
}
# endif