Merge pull request #3477 from IntelOrca/feature/multiple-trackdesign-locations

Scan track designs from multiple locations and refactor
This commit is contained in:
Ted John 2016-05-08 14:15:38 +01:00
commit fc6a589c35
30 changed files with 4640 additions and 4793 deletions

View File

@ -7,6 +7,10 @@
objects = {
/* Begin PBXBuildFile section */
008BF72A1CDAA5C30019A2AD /* track_design_index.c in Sources */ = {isa = PBXBuildFile; fileRef = 008BF7261CDAA5C30019A2AD /* track_design_index.c */; };
008BF72B1CDAA5C30019A2AD /* track_design_save.c in Sources */ = {isa = PBXBuildFile; fileRef = 008BF7271CDAA5C30019A2AD /* track_design_save.c */; };
008BF72C1CDAA5C30019A2AD /* track_design.c in Sources */ = {isa = PBXBuildFile; fileRef = 008BF7281CDAA5C30019A2AD /* track_design.c */; };
C61FAAE21CD1643A0010C9D8 /* paint_surface.c in Sources */ = {isa = PBXBuildFile; fileRef = C61FAAE01CD1643A0010C9D8 /* paint_surface.c */; };
C650B2191CCABBDD00B4D91C /* S4Importer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C650B2151CCABBDD00B4D91C /* S4Importer.cpp */; };
C650B21A1CCABBDD00B4D91C /* tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C650B2171CCABBDD00B4D91C /* tables.cpp */; };
C650B21C1CCABC4400B4D91C /* ConvertCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C650B21B1CCABC4400B4D91C /* ConvertCommand.cpp */; };
@ -318,6 +322,12 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
008BF7261CDAA5C30019A2AD /* track_design_index.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = track_design_index.c; sourceTree = "<group>"; };
008BF7271CDAA5C30019A2AD /* track_design_save.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = track_design_save.c; sourceTree = "<group>"; };
008BF7281CDAA5C30019A2AD /* track_design.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = track_design.c; sourceTree = "<group>"; };
008BF7291CDAA5C30019A2AD /* track_design.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = track_design.h; sourceTree = "<group>"; };
C61FAAE01CD1643A0010C9D8 /* paint_surface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = paint_surface.c; sourceTree = "<group>"; };
C61FAAE11CD1643A0010C9D8 /* paint_surface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = paint_surface.h; sourceTree = "<group>"; };
C650B2151CCABBDD00B4D91C /* S4Importer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = S4Importer.cpp; sourceTree = "<group>"; };
C650B2161CCABBDD00B4D91C /* S4Importer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = S4Importer.h; sourceTree = "<group>"; };
C650B2171CCABBDD00B4D91C /* tables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tables.cpp; sourceTree = "<group>"; };
@ -1250,6 +1260,10 @@
C686F8F01CDBC3B7009F9BFC /* thrill */,
C686F8FC1CDBC3B7009F9BFC /* transport */,
C686F9021CDBC3B7009F9BFC /* water */,
008BF7261CDAA5C30019A2AD /* track_design_index.c */,
008BF7271CDAA5C30019A2AD /* track_design_save.c */,
008BF7281CDAA5C30019A2AD /* track_design.c */,
008BF7291CDAA5C30019A2AD /* track_design.h */,
D442716F1CC81B3200D84D28 /* cable_lift.c */,
D44271701CC81B3200D84D28 /* cable_lift.h */,
D44271731CC81B3200D84D28 /* ride_data.c */,
@ -1721,6 +1735,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
008BF72B1CDAA5C30019A2AD /* track_design_save.c in Sources */,
D44272491CC81B3200D84D28 /* track.c in Sources */,
C686F9201CDBC3B7009F9BFC /* side_friction_roller_coaster.c in Sources */,
D44272561CC81B3200D84D28 /* cheats.c in Sources */,
@ -1799,6 +1814,7 @@
C686F9321CDBC3B7009F9BFC /* haunted_house.c in Sources */,
D442720A1CC81B3200D84D28 /* drawing_fast.cpp in Sources */,
C686F9521CDBC3B7009F9BFC /* river_rapids.c in Sources */,
008BF72C1CDAA5C30019A2AD /* track_design.c in Sources */,
D44272191CC81B3200D84D28 /* colour.c in Sources */,
D442724C1CC81B3200D84D28 /* vehicle.c in Sources */,
D44272261CC81B3200D84D28 /* currency.c in Sources */,
@ -1919,6 +1935,7 @@
C686F9501CDBC3B7009F9BFC /* log_flume.c in Sources */,
D44272241CC81B3200D84D28 /* intro.c in Sources */,
D44272631CC81B3200D84D28 /* game_bottom_toolbar.c in Sources */,
008BF72A1CDAA5C30019A2AD /* track_design_index.c in Sources */,
D44272091CC81B3200D84D28 /* drawing.c in Sources */,
C686F94C1CDBC3B7009F9BFC /* monorail.c in Sources */,
D442728E1CC81B3200D84D28 /* title_logo.c in Sources */,

View File

@ -3364,7 +3364,7 @@ STR_3353 :New name contains invalid characters
STR_3354 :Another file exists with this name, or file is write-protected
STR_3355 :File is write-protected or locked
STR_3356 :Delete File
STR_3357 :{WINDOW_COLOUR_2}Are you sure you want to permanently delete {STRINGID} ?
STR_3357 :{WINDOW_COLOUR_2}Are you sure you want to permanently delete {STRING} ?
STR_3358 :Can't delete track design...
STR_3359 :{BLACK}No track designs of this type
STR_3360 :Warning!
@ -4118,6 +4118,8 @@ STR_5809 :{WINDOW_COLOUR_2}Number of information kiosks and other facilities:
STR_5810 :Disable train length limit
STR_5811 :{SMALLFONT}{BLACK}If checked, you can have up to{NEWLINE}255 cars per train
STR_5812 :Show multiplayer window
STR_5813 :{OPENQUOTES}{STRING}{ENDQUOTES}
STR_5814 :{WINDOW_COLOUR_1}{OPENQUOTES}{STRING}{ENDQUOTES}
#############
# Scenarios #

View File

@ -181,6 +181,9 @@
<ClCompile Include="src\ride\thrill\twist.c" />
<ClCompile Include="src\ride\track.c" />
<ClCompile Include="src\ride\track_data.c" />
<ClCompile Include="src\ride\track_design.c" />
<ClCompile Include="src\ride\track_design_save.c" />
<ClCompile Include="src\ride\track_design_index.c" />
<ClCompile Include="src\ride\track_paint.c" />
<ClCompile Include="src\ride\transport\chairlift.c" />
<ClCompile Include="src\ride\transport\lift.c" />
@ -374,6 +377,7 @@
<ClInclude Include="src\ride\station.h" />
<ClInclude Include="src\ride\track.h" />
<ClInclude Include="src\ride\track_data.h" />
<ClInclude Include="src\ride\track_design.h" />
<ClInclude Include="src\ride\track_paint.h" />
<ClInclude Include="src\ride\vehicle.h" />
<ClInclude Include="src\ride\vehicle_paint.h" />

View File

@ -242,20 +242,6 @@
// 8 x 8 in size.
#define RCT2_ADDRESS_CHARACTER_BITMAP 0x00F4393C
#define RCT2_ADDRESS_TRACK_PREVIEW_X_MIN 0x00F440F9
#define RCT2_ADDRESS_TRACK_PREVIEW_X_MAX 0x00F440FB
#define RCT2_ADDRESS_TRACK_PREVIEW_Y_MIN 0x00F440FD
#define RCT2_ADDRESS_TRACK_PREVIEW_Y_MAX 0x00F440FF
#define RCT2_ADDRESS_TRACK_PREVIEW_Z_MIN 0x00F44101
#define RCT2_ADDRESS_TRACK_PREVIEW_Z_MAX 0x00F44103
#define RCT2_ADDRESS_TRACK_DESIGN_CACHE 0x00F44105
#define RCT2_ADDRESS_TRACK_DESIGN_INDEX_CACHE 0x00F44109
#define RCT2_ADDRESS_TRACK_DESIGN_NEXT_INDEX_CACHE 0x00F44119
#define RCT2_ADDRESS_TRACK_DESIGN_COST 0x00F4411D
#define RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE 0x00F44152
#define RCT2_ADDRESS_ABOVE_GROUND_FLAGS 0x00F441D4
#define RCT2_ADDRESS_TRACK_LIST 0x00F441EC
@ -602,7 +588,19 @@
#define RCT2_ADDRESS_CONSTRUCT_PATH_SLOPE 0x00F3EF91
#define RCT2_ADDRESS_CONSTRUCT_PATH_VALID_DIRECTIONS 0x00F3EF9E
#define RCT2_ADDRESS_TRACK_PREVIEW_ROTATION 0x00F440AE
#define RCT2_ADDRESS_TRACK_PREVIEW_X_MIN 0x00F440F9
#define RCT2_ADDRESS_TRACK_PREVIEW_X_MAX 0x00F440FB
#define RCT2_ADDRESS_TRACK_PREVIEW_Y_MIN 0x00F440FD
#define RCT2_ADDRESS_TRACK_PREVIEW_Y_MAX 0x00F440FF
#define RCT2_ADDRESS_TRACK_PREVIEW_Z_MIN 0x00F44101
#define RCT2_ADDRESS_TRACK_PREVIEW_Z_MAX 0x00F44103
#define RCT2_ADDRESS_TRACK_DESIGN_CACHE 0x00F44105
#define RCT2_ADDRESS_TRACK_DESIGN_INDEX_CACHE 0x00F44109
#define RCT2_ADDRESS_TRACK_DESIGN_NEXT_INDEX_CACHE 0x00F44119
#define RCT2_ADDRESS_TRACK_DESIGN_COST 0x00F4411D
#define RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE 0x00F44152
#define RCT2_ADDRESS_NEXT_FREE_MAP_ELEMENT 0x0140E9A4

View File

@ -227,7 +227,7 @@ static void set_all_land_owned()
{
int mapSize = gMapSize;
game_do_command(64, 1, 64, 2, GAME_COMMAND_SET_LAND_OWNERSHIP, (mapSize - 2) * 32, (mapSize - 2) * 32);
game_do_command(64, 1, 64, 2, GAME_COMMAND_SET_LAND_OWNERSHIP, (mapSize - 3) * 32, (mapSize - 3) * 32);
}
/**

View File

@ -42,6 +42,7 @@
#include "ride/ride_ratings.h"
#include "ride/vehicle.h"
#include "ride/track.h"
#include "ride/track_design.h"
#include "scenario.h"
#include "title.h"
#include "util/sawyercoding.h"

View File

@ -19,12 +19,13 @@
#include "../common.h"
#include "../drawing/drawing.h"
#include "../management/research.h"
#include "../peep/peep.h"
#include "../ride/ride.h"
#include "../ride/track_design.h"
#include "../ride/vehicle.h"
#include "../world/park.h"
#include "../management/research.h"
#include "../scenario.h"
#include "../world/park.h"
#include "colour.h"
struct rct_window;
@ -209,8 +210,8 @@ typedef struct {
typedef struct {
uint16 var_480;
uint16 var_482;
uint16 var_484;
bool reload_track_designs;
} track_list_variables;
typedef struct {
@ -627,7 +628,7 @@ void ride_construction_toolupdate_construct(int screenX, int screenY);
void ride_construction_tooldown_construct(int screenX, int screenY);
void window_maze_construction_update_pressed_widgets();
void window_track_place_open();
void window_track_place_open(const track_design_file_ref *tdFileRef);
rct_window *window_new_ride_open();
rct_window *window_new_ride_open_research();
void window_install_track_open(const char* path);
@ -651,7 +652,7 @@ void window_research_funding_page_paint(rct_window *w, rct_drawpixelinfo *dpi, i
void window_scenery_open();
void window_music_credits_open();
void window_publisher_credits_open();
void window_track_manage_open();
void window_track_manage_open(track_design_file_ref *tdFileRef);
void window_viewport_open();
void window_themes_open();
void window_title_editor_open(int tab);

View File

@ -1879,10 +1879,13 @@ enum {
STR_TRACK_MANAGE_RENAME = 3348,
STR_TRACK_MANAGE_DELETE = 3349,
STR_TRACK_DESIGN_RENAME_TITLE = 3350,
STR_TRACK_DESIGN_RENAME_DESC = 3351,
STR_CANT_RENAME_TRACK_DESIGN = 3352,
STR_NEW_NAME_CONTAINS_INVALID_CHARACTERS = 3353,
STR_ANOTHER_FILE_EXISTS_WITH_NAME_OR_FILE_IS_WRITE_PROTECTED = 3354,
STR_FILE_IS_WRITE_PROTECTED_OR_LOCKED = 3355,
STR_DELETE_FILE = 3356,
STR_ARE_YOU_SURE_YOU_WANT_TO_PERMANENTLY_DELETE_TRACK = 3357,
STR_CANT_DELETE_TRACK_DESIGN = 3358,
@ -1900,6 +1903,10 @@ enum {
STR_INSTALL_NEW_TRACK_DESIGN = 3376,
STR_INSTALL_NEW_TRACK_DESIGN_TIP = 3377,
STR_UNABLE_TO_INSTALL_THIS_TRACK_DESIGN = 3380,
STR_SELECT_NEW_NAME_FOR_TRACK_DESIGN = 3383,
STR_AN_EXISTING_TRACK_DESIGN_ALREADY_HAS_THIS_NAME = 3384,
STR_SAVE_TRACK_SCENERY_UNABLE_TO_SELECT_ADDITIONAL_ITEM_OF_SCENERY = 3389,
STR_SAVE_TRACK_SCENERY_TOO_MANY_ITEMS_SELECTED = 3390,
@ -2454,6 +2461,9 @@ enum {
STR_SHORTCUT_SHOW_MULTIPLAYER = 5812,
STR_TRACK_LIST_NAME_FORMAT = 5813,
STR_TRACK_PREVIEW_NAME_FORMAT = 5814,
// Have to include resource strings (from scenarios and objects) for the time being now that language is partially working
STR_COUNT = 32768
};

View File

@ -16,13 +16,14 @@
#include "addresses.h"
#include "config.h"
#include "game.h"
#include "localisation/localisation.h"
#include "object.h"
#include "platform/platform.h"
#include "ride/track.h"
#include "util/sawyercoding.h"
#include "game.h"
#include "rct1.h"
#include "ride/track.h"
#include "ride/track_design.h"
#include "util/sawyercoding.h"
#include "world/entrance.h"
#include "world/footpath.h"
#include "world/scenery.h"
@ -336,7 +337,7 @@ void object_list_load()
ride_list_item ride_list;
ride_list.entry_index = 0xFC;
ride_list.type = 0xFC;
track_load_list(ride_list);
// track_load_list(ride_list);
object_list_examine();
}

View File

@ -527,8 +527,8 @@ enum{
};
enum{
RCT1_RIDE_MODE_REVERSE_INCLINE_LAUNCHED_SHUTTLE = 2,
RCT1_RIDE_MODE_POWERED_LAUNCH = 3,
};
enum {

View File

@ -41,6 +41,7 @@
#include "rct1.h"
#include "ride/ride.h"
#include "ride/track.h"
#include "ride/track_design.h"
#include "scenario.h"
#include "title.h"
#include "util/util.h"
@ -158,9 +159,7 @@ int rct2_init()
object_list_load();
scenario_load_list();
ride_list_item item = { 253, 0 };
track_load_list(item);
track_design_index_create();
font_sprite_initialise_characters();
if (!gOpenRCT2Headless) {

View File

@ -199,7 +199,7 @@ rct_ride *get_ride(int index)
rct_ride_entry *get_ride_entry(int index)
{
if (index < 0 || index >= object_entry_group_counts[0])
if (index < 0 || index >= object_entry_group_counts[OBJECT_TYPE_RIDE])
{
log_error("invalid index %d for ride type", index);
return NULL;
@ -207,6 +207,19 @@ rct_ride_entry *get_ride_entry(int index)
return gRideTypeList[index];
}
void get_ride_entry_name(char *name, int index)
{
if (index < 0 || index >= object_entry_group_counts[OBJECT_TYPE_RIDE])
{
log_error("invalid index %d for ride type", index);
return;
}
const char *entryName = object_entry_groups[OBJECT_TYPE_RIDE].entries[index].name;
memcpy(name, entryName, 8);
name[8] = '\0';
}
rct_ride_measurement *get_ride_measurement(int index)
{
return &(RCT2_ADDRESS(RCT2_ADDRESS_RIDE_MEASUREMENTS, rct_ride_measurement)[index]);
@ -5485,10 +5498,17 @@ void game_command_set_ride_status(int *eax, int *ebx, int *ecx, int *edx, int *e
void ride_set_name(int rideIndex, const char *name)
{
typedef union name_union {
char as_char[4];
int as_int;
} name_union;
name_union name_buffer[9];
safe_strcpy((char *)name_buffer, name, sizeof(name_buffer));
gGameCommandErrorTitle = STR_CANT_RENAME_RIDE_ATTRACTION;
game_do_command(1, (rideIndex << 8) | 1, 0, *((int*)(name + 0)), GAME_COMMAND_SET_RIDE_NAME, *((int*)(name + 8)), *((int*)(name + 4)));
game_do_command(2, (rideIndex << 8) | 1, 0, *((int*)(name + 12)), GAME_COMMAND_SET_RIDE_NAME, *((int*)(name + 20)), *((int*)(name + 16)));
game_do_command(0, (rideIndex << 8) | 1, 0, *((int*)(name + 24)), GAME_COMMAND_SET_RIDE_NAME, *((int*)(name + 32)), *((int*)(name + 28)));
game_do_command(1, (rideIndex << 8) | 1, 0, name_buffer[0].as_int, GAME_COMMAND_SET_RIDE_NAME, name_buffer[2].as_int, name_buffer[1].as_int);
game_do_command(2, (rideIndex << 8) | 1, 0, name_buffer[3].as_int, GAME_COMMAND_SET_RIDE_NAME, name_buffer[5].as_int, name_buffer[4].as_int);
game_do_command(0, (rideIndex << 8) | 1, 0, name_buffer[6].as_int, GAME_COMMAND_SET_RIDE_NAME, name_buffer[8].as_int, name_buffer[7].as_int);
}
/**
@ -5836,6 +5856,21 @@ bool shop_item_has_common_price(int shopItem)
}
}
money32 ride_create_command(int type, int subType, int flags, uint8 *outRideIndex, uint8 *outRideColour)
{
int eax = 0;
int ebx = flags;
int ecx = 0;
int edx = type | (subType << 8);
int esi = 0;
int edi = 0;
int ebp = 0;
money32 cost = game_do_command_p(GAME_COMMAND_CREATE_RIDE, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
*outRideIndex = edi & 0xFF;
*outRideColour = eax;
return cost;
}
/**
*
* rct2: 0x006B3F0F
@ -7200,14 +7235,14 @@ bool ride_select_forwards_from_back()
}
}
money32 ride_remove_track_piece(int x, int y, int z, int direction, int type)
money32 ride_remove_track_piece(int x, int y, int z, int direction, int type, uint8 flags)
{
gGameCommandErrorTitle = STR_RIDE_CONSTRUCTION_CANT_REMOVE_THIS;
if (network_get_mode() == NETWORK_MODE_CLIENT)
{
game_command_callback = game_command_callback_ride_remove_track_piece;
}
return game_do_command(x, (GAME_COMMAND_FLAG_APPLY) | ((direction & 3) << 8), y, type, GAME_COMMAND_REMOVE_TRACK, z, 0);
return game_do_command(x, flags | ((direction & 3) << 8), y, type, GAME_COMMAND_REMOVE_TRACK, z, 0);
}
/**
@ -7811,25 +7846,15 @@ void sub_6CB945(int rideIndex)
location.x -= TileDirectionDelta[direction].x;
location.y -= TileDirectionDelta[direction].y;
}
mapElement = map_get_first_element_at(location.x / 32, location.y / 32);
mapElement = map_get_first_element_at(location.x >> 5, location.y >> 5);
bool trackFound = false;
do {
if (mapElement->base_height != location.z)
continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK)
continue;
if (mapElement->properties.track.ride_index != rideIndex)
continue;
// Only allow sequence 0
if (mapElement->properties.track.sequence & 0xF)
continue;
if (!(RCT2_ADDRESS(0x0099BA64, uint8)[mapElement->properties.track.type * 16] & (1 << 4)))
continue;
if (mapElement->base_height != location.z) continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK) continue;
if (mapElement->properties.track.ride_index != rideIndex) continue;
if ((mapElement->properties.track.sequence & 0x0F) != 0) continue;
if (!(RCT2_ADDRESS(0x0099BA64, uint8)[mapElement->properties.track.type * 16] & (1 << 4))) continue;
trackFound = true;
break;
@ -7850,53 +7875,29 @@ void sub_6CB945(int rideIndex)
}
}
if (specialTrack == false)
if (specialTrack == false) {
continue;
}
const rct_preview_track *trackBlock = get_track_def_from_ride(ride, mapElement->properties.track.type);
while ((++trackBlock)->index != 0xFF) {
rct_xyz16 blockLocation = location;
switch (direction) {
case 0:
blockLocation.x += trackBlock->x;
blockLocation.y += trackBlock->y;
break;
case 1:
blockLocation.x += trackBlock->y;
blockLocation.y -= trackBlock->x;
break;
case 2:
blockLocation.x -= trackBlock->x;
blockLocation.y -= trackBlock->y;
break;
case 3:
blockLocation.x -= trackBlock->y;
blockLocation.y += trackBlock->x;
break;
}
blockLocation.z += trackBlock->z / 8;
map_offset_with_rotation(&blockLocation.x, &blockLocation.y, trackBlock->x, trackBlock->y, direction);
bool trackFound = false;
mapElement = map_get_first_element_at(blockLocation.x / 32, blockLocation.y / 32);
mapElement = map_get_first_element_at(blockLocation.x >> 5, blockLocation.y >> 5);
do {
if (blockLocation.z != mapElement->base_height)
continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK)
continue;
if (!(RCT2_ADDRESS(0x0099BA64, uint8)[mapElement->properties.track.type * 16] & 0x10))
continue;
if (blockLocation.z != mapElement->base_height) continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_TRACK) continue;
if (!(RCT2_ADDRESS(0x0099BA64, uint8)[mapElement->properties.track.type * 16] & (1 << 4))) continue;
trackFound = true;
break;
} while (!map_element_is_last_for_tile(mapElement++));
if (trackFound == false)
if (!trackFound) {
break;
}
mapElement->properties.track.sequence &= 0x8F;
mapElement->properties.track.sequence |= (stationId << 4);
@ -7904,8 +7905,7 @@ void sub_6CB945(int rideIndex)
}
}
uint16* locationList = RCT2_ADDRESS(0x00F441B0, uint16);
uint16 *locationList = RCT2_ADDRESS(0x00F441B0, uint16);
for (uint8 stationId = 0; stationId < 4; ++stationId) {
if (ride->entrances[stationId] != 0xFFFF) {
*locationList++ = ride->entrances[stationId];
@ -7917,11 +7917,9 @@ void sub_6CB945(int rideIndex)
ride->exits[stationId] = 0xFFFF;
}
}
*locationList++ = 0xFFFF;
locationList = RCT2_ADDRESS(0x00F441B0, uint16);
for (; *locationList != 0xFFFF; locationList++) {
uint16* locationList2 = locationList;
locationList2++;
@ -7934,50 +7932,40 @@ void sub_6CB945(int rideIndex)
}
} while (*locationList2++ != 0xFFFF);
if (duplicateLocation == true)
if (duplicateLocation == true) {
continue;
}
rct_xy16 location = {
.x = (*locationList & 0xFF) * 32,
.y = ((*locationList >> 8) & 0xFF) * 32
};
rct_map_element* mapElement = map_get_first_element_at(location.x / 32, location.y / 32);
rct_map_element *mapElement = map_get_first_element_at(location.x >> 5, location.y >> 5);
do {
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_ENTRANCE)
continue;
if (mapElement->properties.entrance.ride_index != rideIndex)
continue;
// If its a park entrance
if (mapElement->properties.entrance.type > ENTRANCE_TYPE_RIDE_EXIT)
continue;
if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_ENTRANCE) continue;
if (mapElement->properties.entrance.ride_index != rideIndex) continue;
if (mapElement->properties.entrance.type > ENTRANCE_TYPE_RIDE_EXIT) continue;
rct_xy16 nextLocation = location;
nextLocation.x += TileDirectionDelta[map_element_get_direction(mapElement)].x;
nextLocation.y += TileDirectionDelta[map_element_get_direction(mapElement)].y;
bool shouldRemove = true;
rct_map_element* trackElement = map_get_first_element_at(nextLocation.x / 32, nextLocation.y / 32);
rct_map_element *trackElement = map_get_first_element_at(nextLocation.x >> 5, nextLocation.y >> 5);
do {
if (map_element_get_type(trackElement) != MAP_ELEMENT_TYPE_TRACK)
continue;
if (trackElement->properties.track.ride_index != rideIndex)
continue;
if (trackElement->base_height != mapElement->base_height)
continue;
if (map_element_get_type(trackElement) != MAP_ELEMENT_TYPE_TRACK) continue;
if (trackElement->properties.track.ride_index != rideIndex) continue;
if (trackElement->base_height != mapElement->base_height) continue;
uint32 edi = trackElement->properties.track.type * 16;
edi |= trackElement->properties.track.sequence & 0xF;
uint8 direction = (map_element_get_direction(mapElement) - map_element_get_direction(trackElement) + 2) & 3;
if (!(RCT2_GLOBAL(0x0099BA64 + edi, uint32) & (1 << direction)))
if (!(RCT2_GLOBAL(0x0099BA64 + edi, uint32) & (1 << direction))) {
continue;
}
uint8 stationId = 0;
if (trackElement->properties.track.type != TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP) {
@ -7985,15 +7973,14 @@ void sub_6CB945(int rideIndex)
}
if (mapElement->properties.entrance.type == ENTRANCE_TYPE_RIDE_EXIT) {
if (ride->exits[stationId] != 0xFFFF)
if (ride->exits[stationId] != 0xFFFF) {
break;
}
ride->exits[stationId] = (location.x / 32) | ((location.y / 32) << 8);
}
else {
if (ride->entrances[stationId] != 0xFFFF)
} else {
if (ride->entrances[stationId] != 0xFFFF) {
break;
}
ride->entrances[stationId] = (location.x / 32) | ((location.y / 32) << 8);
}
@ -8615,3 +8602,14 @@ rct_vehicle *ride_get_broken_vehicle(rct_ride *ride) {
return vehicle;
}
/**
*
* rct2: 0x006D235B
*/
void ride_delete(uint8 rideIndex)
{
rct_ride *ride = get_ride(rideIndex);
user_string_free(ride->name);
ride->type = RIDE_TYPE_NULL;
}

View File

@ -874,6 +874,7 @@ enum {
/** Helper macros until rides are stored in this module. */
rct_ride *get_ride(int index);
rct_ride_entry *get_ride_entry(int index);
void get_ride_entry_name(char *name, int index);
rct_ride_measurement *get_ride_measurement(int index);
/**
@ -983,6 +984,7 @@ void game_command_callback_ride_remove_track_piece(int eax, int ebx, int ecx, in
void game_command_demolish_ride(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
void game_command_set_ride_appearance(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
void game_command_set_ride_price(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
money32 ride_create_command(int type, int subType, int flags, uint8 *outRideIndex, uint8 *outRideColour);
void ride_clear_for_construction(int rideIndex);
void set_vehicle_type_image_max_sizes(rct_ride_entry_vehicle* vehicle_type, int num_images);
@ -1030,7 +1032,7 @@ void ride_get_entrance_or_exit_position_from_screen_position(int x, int y, int *
bool ride_select_backwards_from_front();
bool ride_select_forwards_from_back();
money32 ride_remove_track_piece(int x, int y, int z, int direction, int type);
money32 ride_remove_track_piece(int x, int y, int z, int direction, int type, uint8 flags);
bool ride_are_all_possible_entrances_and_exits_built(rct_ride *ride);
void ride_fix_breakdown(int rideIndex, int reliabilityIncreaseFactor);
@ -1073,4 +1075,6 @@ void window_ride_construction_do_station_check();
void window_ride_construction_do_entrance_exit_check();
void game_command_callback_place_ride_entrance_or_exit(int eax, int ebx, int ecx, int edx, int esi, int edi, int ebp);
void ride_delete(uint8 rideIndex);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -45,64 +45,19 @@ typedef struct {
} rct_preview_track;
/* size 0x0A */
typedef struct{
typedef struct {
sint8 rotation_begin; // 0x00
sint8 rotation_end; // 0x01
sint16 z_begin; // 0x02
sint16 z_end; // 0x04
sint16 x; // 0x06
sint16 y; // 0x08
}rct_track_coordinates;
/**
* Size: 0x04
*/
typedef struct {
union {
uint32 all;
struct {
sint8 x;
sint8 y;
union{
uint16 maze_entry;
struct{
uint8 unk_2;
uint8 type;
};
};
};
};
} rct_maze_element;
/* Size: 0x02 */
typedef struct{
uint8 type;
uint8 flags;
}rct_track_element;
/* Track Scenery entry size: 0x16 */
typedef struct{
rct_object_entry scenery_object; // 0x00
sint8 x; // 0x10
sint8 y; // 0x11
sint8 z; // 0x12
uint8 flags; // 0x13 direction quadrant tertiary colour
uint8 primary_colour; // 0x14
uint8 secondary_colour; // 0x15
}rct_track_scenery;
/* Track Entrance entry size: 0x6 */
typedef struct{
sint8 z;
uint8 direction; // 0x01
sint16 x; // 0x02
sint16 y; // 0x04
}rct_track_entrance;
} rct_track_coordinates;
enum{
TRACK_ELEMENT_FLAG_CHAIN_LIFT = (1<<7),
TRACK_ELEMENT_FLAG_INVERTED = (1<<6),
TRACK_ELEMENT_FLAG_TERMINAL_STATION = (1<<3),
TRACK_ELEMENT_FLAG_TERMINAL_STATION = 1 << 3,
TRACK_ELEMENT_FLAG_INVERTED = 1 << 6,
TRACK_ELEMENT_FLAG_CHAIN_LIFT = 1 << 7,
};
enum {
@ -115,86 +70,8 @@ enum {
#define TRACK_ELEMENT_FLAG_COLOUR_MASK 0x30
#define TRACK_ELEMENT_FLAG_STATION_NO_MASK 0x02
#define TRACK_PREVIEW_IMAGE_SIZE (370 * 217)
#define MAX_STATION_PLATFORM_LENGTH 32
/**
* Track design structure.
* size: 0x4E72B
*/
typedef struct {
uint8 type; // 0x00
uint8 vehicle_type;
union{
// After loading the track this is converted to
// a cost but before its a flags register
money32 cost; // 0x02
uint32 flags; // 0x02
};
union{
// After loading the track this is converted to
// a flags register
uint8 ride_mode; // 0x06
uint8 track_flags; // 0x06
};
uint8 version_and_colour_scheme; // 0x07 0b0000_VVCC
rct_vehicle_colour vehicle_colours[32]; // 0x08
union{
uint8 pad_48;
uint8 track_spine_colour_rct1; // 0x48
};
union{
uint8 entrance_style; // 0x49
uint8 track_rail_colour_rct1; // 0x49
};
union{
uint8 total_air_time; // 0x4A
uint8 track_support_colour_rct1; // 0x4A
};
uint8 depart_flags; // 0x4B
uint8 number_of_trains; // 0x4C
uint8 number_of_cars_per_train; // 0x4D
uint8 min_waiting_time; // 0x4E
uint8 max_waiting_time; // 0x4F
uint8 var_50;
sint8 max_speed; // 0x51
sint8 average_speed; // 0x52
uint16 ride_length; // 0x53
uint8 max_positive_vertical_g; // 0x55
sint8 max_negative_vertical_g; // 0x56
uint8 max_lateral_g; // 0x57
union {
uint8 inversions; // 0x58
uint8 holes; // 0x58
};
uint8 drops; // 0x59
uint8 highest_drop_height; // 0x5A
uint8 excitement; // 0x5B
uint8 intensity; // 0x5C
uint8 nausea; // 0x5D
money16 upkeep_cost; // 0x5E
uint8 track_spine_colour[4]; // 0x60
uint8 track_rail_colour[4]; // 0x64
uint8 track_support_colour[4]; // 0x68
uint32 flags2; // 0x6C
rct_object_entry vehicle_object; // 0x70
uint8 space_required_x; // 0x80
uint8 space_required_y; // 0x81
uint8 vehicle_additional_colour[32]; // 0x82
uint8 lift_hill_speed_num_circuits; // 0xA2 0bCCCL_LLLL
} rct_track_td6;
typedef struct{
rct_track_td6 track_td6;
uint8 preview[4][TRACK_PREVIEW_IMAGE_SIZE]; // 0xA3
} rct_track_design;
enum {
TRACK_FLAGS2_CONTAINS_LOG_FLUME_REVERSER = (1 << 1),
TRACK_FLAGS2_SIX_FLAGS_RIDE_DEPRECATED = (1 << 31) // Not used anymore.
};
enum {
TRACK_NONE = 0,
@ -584,38 +461,12 @@ typedef struct {
extern const rct_trackdefinition *gFlatRideTrackDefinitions;
extern const rct_trackdefinition *gTrackDefinitions;
extern rct_map_element **gTrackSavedMapElements;
void track_load_list(ride_list_item item);
int sub_67726A(const char *path);
rct_track_design *track_get_info(int index, uint8** preview);
rct_track_design *temp_track_get_info(char* path, uint8** preview);
rct_track_td6* load_track_design(const char *path);
int track_rename(const char *text);
int track_delete();
void track_mirror();
void reset_track_list_cache();
int track_is_connected_by_shape(rct_map_element *a, rct_map_element *b);
int sub_6D01B3(uint8 bl, uint8 rideIndex, int x, int y, int z);
int save_track_design(uint8 rideIndex);
int install_track(char* source_path, char* dest_name);
void window_track_list_format_name(utf8 *dst, const utf8 *src, int colour, bool quotes);
void game_command_place_track_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp);
void game_command_place_maze_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp);
void track_save_reset_scenery();
void track_save_select_nearby_scenery(int rideIndex);
void track_save_toggle_map_element(int interactionType, int x, int y, rct_map_element *mapElement);
const rct_preview_track *get_track_def_from_ride(rct_ride *ride, int trackType);
const rct_preview_track *get_track_def_from_ride_index(int rideIndex, int trackType);
const rct_track_coordinates *get_track_coord_from_ride(rct_ride *ride, int trackType);
void game_command_place_track(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
void game_command_remove_track(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
void game_command_set_maze_track(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
void game_command_set_brakes_speed(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
void track_circuit_iterator_begin(track_circuit_iterator *it, rct_xy_element first);
bool track_circuit_iterator_previous(track_circuit_iterator *it);
bool track_circuit_iterator_next(track_circuit_iterator *it);
@ -623,9 +474,10 @@ bool track_circuit_iterator_next(track_circuit_iterator *it);
void track_get_back(rct_xy_element *input, rct_xy_element *output);
void track_get_front(rct_xy_element *input, rct_xy_element *output);
bool track_element_is_lift_hill(rct_map_element *trackElement);
bool track_element_is_block_start(rct_map_element *trackElement);
bool track_element_is_covered(int trackElementType);
bool track_element_is_station(rct_map_element *trackElement);
bool track_element_is_lift_hill(rct_map_element *trackElement);
bool track_element_is_cable_lift(rct_map_element *trackElement);
void track_element_set_cable_lift(rct_map_element *trackElement);
void track_element_clear_cable_lift(rct_map_element *trackElement);
@ -634,7 +486,9 @@ int track_get_actual_bank(rct_map_element *mapElement, int bank);
int track_get_actual_bank_2(int rideType, int trackColour, int bank);
int track_get_actual_bank_3(rct_vehicle *vehicle, rct_map_element *mapElement);
bool track_element_is_station(rct_map_element *trackElement);
bool track_element_is_covered(int trackElementType);
void game_command_place_track(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
void game_command_remove_track(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
void game_command_set_maze_track(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
void game_command_set_brakes_speed(int *eax, int *ebx, int *ecx, int *edx, int *esi, int *edi, int *ebp);
#endif

1881
src/ride/track_design.c Normal file

File diff suppressed because it is too large Load Diff

216
src/ride/track_design.h Normal file
View File

@ -0,0 +1,216 @@
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#ifndef _TRACK_DESIGN_H_
#define _TRACK_DESIGN_H_
#include "../common.h"
#include "../object.h"
#include "../world/map.h"
#include "vehicle.h"
#define TRACK_PREVIEW_IMAGE_SIZE (370 * 217)
/* Maze Element entry size: 0x04 */
typedef struct {
union {
uint32 all;
struct {
sint8 x;
sint8 y;
union {
uint16 maze_entry;
struct{
uint8 unk_2;
uint8 type;
};
};
};
};
} rct_td6_maze_element;
/* Track Element entry size: 0x02 */
typedef struct {
uint8 type; // 0x00
uint8 flags; // 0x01
} rct_td6_track_element;
/* Track Entrance entry size: 0x06 */
typedef struct {
sint8 z; // 0x00
uint8 direction; // 0x01
sint16 x; // 0x02
sint16 y; // 0x04
} rct_td6_entrance_element;
/* Track Scenery entry size: 0x16 */
typedef struct {
rct_object_entry scenery_object; // 0x00
sint8 x; // 0x10
sint8 y; // 0x11
sint8 z; // 0x12
uint8 flags; // 0x13 direction quadrant tertiary colour
uint8 primary_colour; // 0x14
uint8 secondary_colour; // 0x15
} rct_td6_scenery_element;
/**
* Track design structure.
* size: 0x4E72B
*/
typedef struct {
uint8 type; // 0x00
uint8 vehicle_type;
union{
// After loading the track this is converted to
// a cost but before its a flags register
money32 cost; // 0x02
uint32 flags; // 0x02
};
union{
// After loading the track this is converted to
// a flags register
uint8 ride_mode; // 0x06
uint8 track_flags; // 0x06
};
uint8 version_and_colour_scheme; // 0x07 0b0000_VVCC
rct_vehicle_colour vehicle_colours[32]; // 0x08
union{
uint8 pad_48;
uint8 track_spine_colour_rct1; // 0x48
};
union{
uint8 entrance_style; // 0x49
uint8 track_rail_colour_rct1; // 0x49
};
union{
uint8 total_air_time; // 0x4A
uint8 track_support_colour_rct1; // 0x4A
};
uint8 depart_flags; // 0x4B
uint8 number_of_trains; // 0x4C
uint8 number_of_cars_per_train; // 0x4D
uint8 min_waiting_time; // 0x4E
uint8 max_waiting_time; // 0x4F
uint8 var_50;
sint8 max_speed; // 0x51
sint8 average_speed; // 0x52
uint16 ride_length; // 0x53
uint8 max_positive_vertical_g; // 0x55
sint8 max_negative_vertical_g; // 0x56
uint8 max_lateral_g; // 0x57
union {
uint8 inversions; // 0x58
uint8 holes; // 0x58
};
uint8 drops; // 0x59
uint8 highest_drop_height; // 0x5A
uint8 excitement; // 0x5B
uint8 intensity; // 0x5C
uint8 nausea; // 0x5D
money16 upkeep_cost; // 0x5E
uint8 track_spine_colour[4]; // 0x60
uint8 track_rail_colour[4]; // 0x64
uint8 track_support_colour[4]; // 0x68
uint32 flags2; // 0x6C
rct_object_entry vehicle_object; // 0x70
uint8 space_required_x; // 0x80
uint8 space_required_y; // 0x81
uint8 vehicle_additional_colour[32]; // 0x82
uint8 lift_hill_speed_num_circuits; // 0xA2 0bCCCL_LLLL
void *elements; // 0xA3 (data starts here in file)
size_t elementsSize;
rct_td6_maze_element *maze_elements;
rct_td6_track_element *track_elements;
rct_td6_entrance_element *entrance_elements;
rct_td6_scenery_element *scenery_elements;
utf8 *name;
} rct_track_td6;
typedef struct{
rct_track_td6 track_td6;
uint8 preview[4][TRACK_PREVIEW_IMAGE_SIZE]; // 0xA3
} rct_track_design;
typedef struct {
utf8 *name;
utf8 *path;
} track_design_file_ref;
enum {
TRACK_FLAGS2_CONTAINS_LOG_FLUME_REVERSER = (1 << 1),
TRACK_FLAGS2_SIX_FLAGS_RIDE_DEPRECATED = (1 << 31) // Not used anymore.
};
enum {
TDPF_PLACE_SCENERY = 1 << 0,
};
enum {
PTD_OPERATION_DRAW_OUTLINES,
PTD_OPERATION_1,
PTD_OPERATION_2,
PTD_OPERATION_GET_PLACE_Z,
PTD_OPERATION_4,
PTD_OPERATION_GET_COST,
PTD_OPERATION_CLEAR_OUTLINES
};
extern rct_track_td6 *gActiveTrackDesign;
extern uint8 gTrackDesignPlaceFlags;
extern bool gTrackDesignSceneryToggle;
extern rct_xyz16 gTrackPreviewMin;
extern rct_xyz16 gTrackPreviewMax;
extern rct_xyz16 gTrackPreviewOrigin;
extern uint8 byte_F4414E;
extern uint8 byte_9D8150;
rct_track_td6 *track_design_open(const utf8 *path);
void track_design_dispose(rct_track_td6 *td6);
void track_design_mirror(rct_track_td6 *td6);
int sub_6D01B3(rct_track_td6 *td6, uint8 bl, uint8 rideIndex, int x, int y, int z);
void track_design_index_create();
size_t track_design_index_get_count_for_ride(uint8 rideType, const char *entry);
size_t track_design_index_get_for_ride(track_design_file_ref **tdRefs, uint8 rideType, const char *entry);
utf8 *track_design_get_name_from_path(const utf8 *path);
bool track_design_index_rename(const utf8 *path, const utf8 *newName);
bool track_design_index_delete(const utf8 *path);
bool track_design_index_install(const utf8 *srcPath, const utf8 *destPath);
void game_command_place_track_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp);
void game_command_place_maze_design(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp);
///////////////////////////////////////////////////////////////////////////////
// Track design preview
///////////////////////////////////////////////////////////////////////////////
void track_design_draw_preview(rct_track_td6 *td6, uint8 *pixels);
///////////////////////////////////////////////////////////////////////////////
// Track design saving
///////////////////////////////////////////////////////////////////////////////
void track_design_save_init();
void track_design_save_reset_scenery();
void track_design_save_select_nearby_scenery(int rideIndex);
void track_design_save_toggle_map_element(int interactionType, int x, int y, rct_map_element *mapElement);
bool track_design_save(uint8 rideIndex);
#endif

View File

@ -0,0 +1,360 @@
#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include "../common.h"
#include "../config.h"
#include "../game.h"
#include "../interface/window.h"
#include "../localisation/string_ids.h"
#include "../util/util.h"
#include "track.h"
#include "track_design.h"
typedef struct {
uint8 ride_type;
char ride_entry[9];
utf8 path[MAX_PATH];
} td_index_item;
static bool track_design_index_read_header(SDL_RWops *file, uint32 *tdidxCount);
static void track_design_index_scan();
static int track_design_index_item_compare(const void *a, const void *b);
static void track_design_index_include(const utf8 *directory);
static void track_design_add_file(const utf8 *path);
static void track_design_add(const td_index_item *item);
static void track_design_index_dispose();
static void track_design_index_get_path(utf8 * buffer, size_t bufferLength);
static const uint32 TrackIndexMagicNumber = 0x58444954;
static const uint16 TrackIndexVersion = 0;
static td_index_item *_tdIndex = NULL;
static size_t _tdIndexSize = 0;
static size_t _tdIndexCapacity = 0;
void track_design_index_create()
{
track_design_index_dispose();
log_verbose("saving track design index (tracks.idx)");
utf8 path[MAX_PATH];
track_design_index_get_path(path, sizeof(path));
SDL_RWops *file = SDL_RWFromFile(path, "wb");
if (file != NULL) {
track_design_index_scan();
SDL_RWwrite(file, &TrackIndexMagicNumber, sizeof(TrackIndexMagicNumber), 1);
SDL_RWwrite(file, &TrackIndexVersion, sizeof(TrackIndexVersion), 1);
SDL_RWwrite(file, &_tdIndexSize, sizeof(uint32), 1);
SDL_RWwrite(file, _tdIndex, sizeof(td_index_item), _tdIndexSize);
SDL_RWclose(file);
track_design_index_dispose();
}
}
size_t track_design_index_get_count_for_ride(uint8 rideType, const char *entry)
{
log_verbose("reading track design index (tracks.idx)");
utf8 path[MAX_PATH];
track_design_index_get_path(path, sizeof(path));
// Return list
size_t refsCount = 0;
SDL_RWops *file = SDL_RWFromFile(path, "rb");
if (file != NULL) {
uint32 tdidxCount;
if (!track_design_index_read_header(file, &tdidxCount)) {
SDL_RWclose(file);
return 0;
}
for (uint32 i = 0; i < tdidxCount; i++) {
td_index_item tdItem;
SDL_RWread(file, &tdItem, sizeof(td_index_item), 1);
if (tdItem.ride_type != rideType) continue;
if (entry != NULL && _strcmpi(entry, tdItem.ride_entry) != 0) continue;
refsCount++;
}
SDL_RWclose(file);
}
return refsCount;
}
size_t track_design_index_get_for_ride(track_design_file_ref **tdRefs, uint8 rideType, const char *entry)
{
log_verbose("reading track design index (tracks.idx)");
utf8 path[MAX_PATH];
track_design_index_get_path(path, sizeof(path));
// Return list
size_t refsCount = 0;
size_t refsCapacity = 0;
track_design_file_ref *refs = NULL;
SDL_RWops *file = SDL_RWFromFile(path, "rb");
if (file != NULL) {
uint32 tdidxCount;
if (!track_design_index_read_header(file, &tdidxCount)) {
SDL_RWclose(file);
return 0;
}
for (uint32 i = 0; i < tdidxCount; i++) {
td_index_item tdItem;
SDL_RWread(file, &tdItem, sizeof(td_index_item), 1);
if (tdItem.ride_type != rideType) continue;
if (entry != NULL && _strcmpi(entry, tdItem.ride_entry) != 0) continue;
size_t nextIndex = refsCount;
if (nextIndex >= refsCapacity) {
refsCapacity = max(8, refsCapacity * 2);
refs = realloc(refs, refsCapacity * sizeof(track_design_file_ref));
if (refs == NULL) {
log_fatal("Unable to allocate more memory.");
exit(-1);
}
}
refs[nextIndex].name = track_design_get_name_from_path(tdItem.path);
refs[nextIndex].path = _strdup(tdItem.path);
refsCount++;
}
SDL_RWclose(file);
}
*tdRefs = realloc(refs, refsCount * sizeof(track_design_file_ref));
return refsCount;
}
utf8 *track_design_get_name_from_path(const utf8 *path)
{
const char *filename = path_get_filename(path);
const char *lastDot = strrchr(filename, '.');
size_t nameLength;
if (lastDot == NULL) {
nameLength = strlen(filename);
} else {
nameLength = (size_t)(lastDot - filename);
}
return strndup(filename, nameLength);
}
/**
*
* rct2: 0x006D3664
*/
bool track_design_index_rename(const utf8 *path, const utf8 *newName)
{
if (str_is_null_or_empty(newName)) {
gGameCommandErrorText = STR_CANT_RENAME_TRACK_DESIGN;
return false;
}
if (!filename_valid_characters(newName)) {
gGameCommandErrorText = STR_NEW_NAME_CONTAINS_INVALID_CHARACTERS;
return false;
}
utf8 newPath[MAX_PATH];
const char *lastPathSep = strrchr(path, platform_get_path_separator());
if (lastPathSep == NULL) {
gGameCommandErrorText = STR_CANT_RENAME_TRACK_DESIGN;
return false;
}
size_t directoryLength = (size_t)(lastPathSep - path + 1);
memcpy(newPath, path, directoryLength);
strcpy(newPath + directoryLength, newName);
strcat(newPath, ".td6");
if (!platform_file_move(path, newPath)) {
gGameCommandErrorText = STR_ANOTHER_FILE_EXISTS_WITH_NAME_OR_FILE_IS_WRITE_PROTECTED;
return false;
}
track_design_index_create();
rct_window *trackListWindow = window_find_by_class(WC_TRACK_DESIGN_LIST);
if (trackListWindow != NULL) {
trackListWindow->track_list.reload_track_designs = true;
}
return true;
}
/**
*
* rct2: 0x006D3761
*/
bool track_design_index_delete(const utf8 *path)
{
if (!platform_file_delete(path)) {
gGameCommandErrorText = STR_FILE_IS_WRITE_PROTECTED_OR_LOCKED;
return false;
}
track_design_index_create();
rct_window *trackListWindow = window_find_by_class(WC_TRACK_DESIGN_LIST);
if (trackListWindow != NULL) {
trackListWindow->track_list.reload_track_designs = true;
}
return true;
}
/**
*
* rct2: 0x006D40B2
*/
bool track_design_index_install(const utf8 *srcPath, const utf8 *destPath)
{
if (!platform_file_copy(srcPath, destPath, false)) {
return false;
}
track_design_index_create();
rct_window *trackListWindow = window_find_by_class(WC_TRACK_DESIGN_LIST);
if (trackListWindow != NULL) {
trackListWindow->track_list.reload_track_designs = true;
}
return true;
}
static bool track_design_index_read_header(SDL_RWops *file, uint32 *tdidxCount)
{
uint32 tdidxMagicNumber;
uint16 tdidxVersion;
SDL_RWread(file, &tdidxMagicNumber, sizeof(tdidxMagicNumber), 1);
SDL_RWread(file, &tdidxVersion, sizeof(tdidxVersion), 1);
SDL_RWread(file, tdidxCount, sizeof(uint32), 1);
if (tdidxMagicNumber != TrackIndexMagicNumber) {
log_error("invalid track index file");
return false;
}
if (tdidxVersion != TrackIndexVersion) {
log_error("unsupported track index file version");
return false;
}
return true;
}
static void track_design_index_scan()
{
utf8 directory[MAX_PATH];
// Get track directory from RCT2
safe_strcpy(directory, gConfigGeneral.game_path, sizeof(directory));
safe_strcat_path(directory, "Tracks", sizeof(directory));
track_design_index_include(directory);
// Get track directory from user directory
platform_get_user_directory(directory, "track");
track_design_index_include(directory);
// Sort items by ride type then by filename
qsort(_tdIndex, _tdIndexSize, sizeof(td_index_item), track_design_index_item_compare);
}
static int track_design_index_item_compare(const void *a, const void *b)
{
const td_index_item *tdA = (const td_index_item*)a;
const td_index_item *tdB = (const td_index_item*)b;
if (tdA->ride_type != tdB->ride_type) {
return tdA->ride_type - tdB->ride_type;
}
const utf8 *tdAName = path_get_filename(tdA->path);
const utf8 *tdBName = path_get_filename(tdB->path);
return _stricmp(tdAName, tdBName);
}
static void track_design_index_include(const utf8 *directory)
{
int handle;
file_info fileInfo;
// Scenarios in this directory
utf8 pattern[MAX_PATH];
safe_strcpy(pattern, directory, sizeof(pattern));
safe_strcat_path(pattern, "*.td6", sizeof(pattern));
handle = platform_enumerate_files_begin(pattern);
while (platform_enumerate_files_next(handle, &fileInfo)) {
utf8 path[MAX_PATH];
safe_strcpy(path, directory, sizeof(pattern));
safe_strcat_path(path, fileInfo.path, sizeof(pattern));
track_design_add_file(path);
}
platform_enumerate_files_end(handle);
// Include sub-directories
utf8 subDirectory[MAX_PATH];
handle = platform_enumerate_directories_begin(directory);
while (platform_enumerate_directories_next(handle, subDirectory)) {
utf8 path[MAX_PATH];
safe_strcpy(path, directory, sizeof(pattern));
safe_strcat_path(path, subDirectory, sizeof(pattern));
track_design_index_include(path);
}
platform_enumerate_directories_end(handle);
}
static void track_design_add_file(const utf8 *path)
{
rct_track_td6 *td6 = track_design_open(path);
if (td6 != NULL) {
td_index_item tdIndexItem = { 0 };
safe_strcpy(tdIndexItem.path, path, sizeof(tdIndexItem.path));
memcpy(tdIndexItem.ride_entry, td6->vehicle_object.name, 8);
tdIndexItem.ride_type = td6->type;
track_design_add(&tdIndexItem);
track_design_dispose(td6);
}
}
static void track_design_add(const td_index_item *item)
{
size_t nextIndex = _tdIndexSize;
if (nextIndex >= _tdIndexCapacity) {
_tdIndexCapacity = max(128, _tdIndexCapacity * 2);
_tdIndex = realloc(_tdIndex, _tdIndexCapacity * sizeof(td_index_item));
if (_tdIndex == NULL) {
log_fatal("Unable to allocate more memory.");
exit(-1);
}
}
_tdIndex[nextIndex] = *item;
_tdIndexSize++;
}
static void track_design_index_dispose()
{
SafeFree(_tdIndex);
_tdIndexSize = 0;
_tdIndexCapacity = 0;
}
static void track_design_index_get_path(utf8 * buffer, size_t bufferLength)
{
platform_get_user_directory(buffer, NULL);
safe_strcat(buffer, "tracks.idx", bufferLength);
}

1315
src/ride/track_design_save.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,7 @@
#include "../ride/ride.h"
#include "../ride/ride_data.h"
#include "../ride/track.h"
#include "../ride/track_design.h"
#include "../scenario.h"
#include "../util/util.h"
#include "../world/footpath.h"
@ -1961,7 +1962,7 @@ static void window_editor_object_selection_manage_tracks()
RCT2_GLOBAL(0xF44158, uint8) = ride_type;
ride_list_item item = { ride_type, entry_index };
track_load_list(item);
// track_load_list(item);
window_track_list_open(item);
}

View File

@ -17,14 +17,16 @@
#include "../addresses.h"
#include "../audio/audio.h"
#include "../editor.h"
#include "../localisation/localisation.h"
#include "../interface/themes.h"
#include "../interface/widget.h"
#include "../interface/window.h"
#include "../localisation/localisation.h"
#include "../ride/ride.h"
#include "../ride/track.h"
#include "../ride/track_design.h"
#include "../sprites.h"
#include "../util/util.h"
#include "error.h"
#include "../interface/themes.h"
enum {
WIDX_BACKGROUND,
@ -86,127 +88,71 @@ static rct_window_event_list window_install_track_events = {
NULL
};
ride_list_item _window_install_track_item;
static rct_track_td6 *_trackDesign;
static utf8 *_trackPath;
static utf8 *_trackName;
static uint8 *_trackDesignPreviewPixels;
char track_dest_name[MAX_PATH];
char track_path[MAX_PATH];
static void window_install_track_update_preview();
static void window_install_track_design(rct_window *w);
/**
*
* rct2: 0x006D386D
*/
void window_install_track_open(const char* path)
void window_install_track_open(const utf8 *path)
{
rct_window *w;
int x, y;
void *mem;
_trackDesign = track_design_open(path);
if (_trackDesign == NULL) {
window_error_open(3010, STR_NONE);
return;
}
object_unload_all();
if (_trackDesign->type == RIDE_TYPE_NULL){
log_error("Failed to load track (ride type null): %s", path);
return;
}
if (!object_load_chunk(0, &_trackDesign->vehicle_object, NULL)){
log_error("Failed to load track (vehicle load fail): %s", path);
return;
}
window_close_by_class(WC_EDITOR_OBJECT_SELECTION);
window_close_construction_windows();
ride_list_item item = {
.type = 0xFF,
.entry_index = 0
};
_window_install_track_item = item;
mem = malloc(1285292);
if (mem == NULL)
return;
RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_CACHE, void*) = mem;
RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) = 0;
gTrackDesignSceneryToggle = false;
_currentTrackPieceDirection = 2;
reset_track_list_cache();
x = gScreenWidth / 2 - 201;
y = max(28, gScreenHeight / 2 - 200);
int x = gScreenWidth / 2 - 201;
int y = max(28, gScreenHeight / 2 - 200);
w = window_create(x, y, 402, 400, &window_install_track_events, WC_INSTALL_TRACK, 0);
rct_window *w = window_create(x, y, 402, 400, &window_install_track_events, WC_INSTALL_TRACK, 0);
w->widgets = window_install_track_widgets;
w->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_ROTATE) | (1 << WIDX_TOGGLE_SCENERY) | (1 << WIDX_INSTALL) | (1 << WIDX_CANCEL);
window_init_scroll_widgets(w);
w->track_list.var_482 = 0;
w->track_list.var_484 = 0;
window_push_others_right(w);
strncpy(track_path, path, MAX_PATH);
track_path[MAX_PATH - 1] = '\0';
char* track_name_pointer = strrchr(track_path, platform_get_path_separator());
track_name_pointer++;
strncpy(track_dest_name, track_name_pointer, MAX_PATH);
track_dest_name[MAX_PATH - 1] = '\0';
_trackPath = _strdup(path);
_trackName = track_design_get_name_from_path(path);
_trackDesignPreviewPixels = calloc(4, TRACK_PREVIEW_IMAGE_SIZE);
window_install_track_update_preview();
window_invalidate(w);
}
/**
*
* rct2: 0x006CFB82
*/
static void window_install_track_select(rct_window *w, int index)
{
utf8 *trackDesignItem, *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, utf8);
rct_track_design *trackDesign;
w->track_list.var_480 = index;
audio_play_sound_panned(SOUND_CLICK_1, w->x + (w->width / 2), 0, 0, 0);
if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) && index == 0) {
window_close(w);
ride_construct_new(_window_install_track_item);
return;
}
if (RCT2_GLOBAL(0x00F44153, uint8) != 0)
RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) = 1;
if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER))
index--;
trackDesignItem = trackDesignList + (index * 128);
RCT2_GLOBAL(0x00F4403C, utf8*) = trackDesignItem;
window_track_list_format_name(
(char*)0x009BC313,
trackDesignItem,
gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER ?
0 :
FORMAT_WHITE,
1);
char track_path[MAX_PATH] = { 0 };
substitute_path(track_path, (char*)RCT2_ADDRESS_TRACKS_PATH, trackDesignItem);
if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) {
window_track_manage_open();
return;
}
if (!load_track_design(track_path)) {
w->track_list.var_480 = 0xFFFF;
window_invalidate(w);
return;
}
trackDesign = track_get_info(index, NULL);
if (trackDesign == NULL) return;
if (trackDesign->track_td6.track_flags & 4)
window_error_open(STR_THIS_DESIGN_WILL_BE_BUILT_WITH_AN_ALTERNATIVE_VEHICLE_TYPE, -1);
window_close(w);
window_track_place_open();
}
/**
*
* rct2: 0x006D41DC
*/
static void window_install_track_close(rct_window *w)
{
free(RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_CACHE, void*));
SafeFree(_trackPath);
SafeFree(_trackName);
SafeFree(_trackDesignPreviewPixels);
track_design_dispose(_trackDesign);
_trackDesign = NULL;
}
/**
@ -215,8 +161,6 @@ static void window_install_track_close(rct_window *w)
*/
static void window_install_track_mouseup(rct_window *w, int widgetIndex)
{
int result;
switch (widgetIndex) {
case WIDX_CLOSE:
case WIDX_CANCEL:
@ -228,24 +172,12 @@ static void window_install_track_mouseup(rct_window *w, int widgetIndex)
window_invalidate(w);
break;
case WIDX_TOGGLE_SCENERY:
RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) ^= 1;
reset_track_list_cache();
gTrackDesignSceneryToggle = !gTrackDesignSceneryToggle;
window_install_track_update_preview();
window_invalidate(w);
break;
case WIDX_INSTALL:
result = install_track(track_path, track_dest_name);
if (result == 1)
window_close(w);
else if(result == 0){
window_error_open(3380, 3382);
window_close(w);
}
else{
// Copy the track name into the string buffer.
window_track_list_format_name(RCT2_ADDRESS(0x009BC677, char), track_dest_name, 0, 0);
window_text_input_open(w, WIDX_INSTALL, 3383, 3384, 3165, 0, 255);
}
window_install_track_design(w);
break;
}
}
@ -260,17 +192,17 @@ static void window_install_track_invalidate(rct_window *w)
colour_scheme_update(w);
w->pressed_widgets |= 1 << WIDX_TRACK_PREVIEW;
if (RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) == 0)
if (!gTrackDesignSceneryToggle) {
w->pressed_widgets |= (1 << WIDX_TOGGLE_SCENERY);
else
} else {
w->pressed_widgets &= ~(1 << WIDX_TOGGLE_SCENERY);
}
if (w->track_list.var_482 != 0xFFFF) {
w->disabled_widgets &= ~(1 << WIDX_TRACK_PREVIEW);
}
else {
w->disabled_widgets |= (1 << WIDX_TRACK_PREVIEW);
}
// if (w->track_list.var_482 != 0xFFFF) {
// w->disabled_widgets &= ~(1 << WIDX_TRACK_PREVIEW);
// } else {
// w->disabled_widgets |= (1 << WIDX_TRACK_PREVIEW);
// }
}
/**
@ -279,36 +211,18 @@ static void window_install_track_invalidate(rct_window *w)
*/
static void window_install_track_paint(rct_window *w, rct_drawpixelinfo *dpi)
{
rct_widget *widget;
rct_track_design *trackDesign = NULL;
uint8 *image, *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, uint8);
uint16 holes, speed, drops, dropHeight, inversions;
fixed32_2dp rating;
int x, y, colour, gForces, airTime;
rct_g1_element tmpElement, *substituteElement;
window_draw_widgets(w, dpi);
if (w->track_list.var_482 == 0xFFFF)
return;
// Track preview
widget = &window_install_track_widgets[WIDX_TRACK_PREVIEW];
x = w->x + widget->left + 1;
y = w->y + widget->top + 1;
colour = ColourMapA[w->colours[0]].darkest;
rct_widget *widget = &window_install_track_widgets[WIDX_TRACK_PREVIEW];
int x = w->x + widget->left + 1;
int y = w->y + widget->top + 1;
int colour = ColourMapA[w->colours[0]].darkest;
gfx_fill_rect(dpi, x, y, x + 369, y + 216, colour);
//call 6d3993 (load track)
trackDesign = temp_track_get_info(track_path, &image);
if (trackDesign == NULL)
return;
rct_track_td6* track_td6 = &trackDesign->track_td6;
substituteElement = &g1Elements[0];
tmpElement = *substituteElement;
substituteElement->offset = image;
rct_g1_element *substituteElement = &g1Elements[0];
rct_g1_element tmpElement = *substituteElement;
substituteElement->offset = _trackDesignPreviewPixels + (_currentTrackPieceDirection * TRACK_PREVIEW_IMAGE_SIZE);
substituteElement->width = 370;
substituteElement->height = 217;
substituteElement->x_offset = 0;
@ -323,9 +237,10 @@ static void window_install_track_paint(rct_window *w, rct_drawpixelinfo *dpi)
RCT2_GLOBAL(0x00F44153, uint8) = 0;
// Warnings
if (track_td6->track_flags & 1) {
rct_track_td6 *td6 = _trackDesign;
if (td6->track_flags & 1) {
RCT2_GLOBAL(0x00F44153, uint8) = 1;
if (RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) == 0) {
if (!gTrackDesignSceneryToggle) {
// Scenery not available
gfx_draw_string_centred_clipped(dpi, STR_DESIGN_INCLUDES_SCENERY_WHICH_IS_UNAVAILABLE, NULL, 0, x, y, 368);
y -= 10;
@ -333,8 +248,7 @@ static void window_install_track_paint(rct_window *w, rct_drawpixelinfo *dpi)
}
// Track design name
window_track_list_format_name((char*)0x009BC677, (char*)0x009E3504, FORMAT_WINDOW_COLOUR_1, 1);
gfx_draw_string_centred_clipped(dpi, 3165, NULL, 0, x, y, 368);
gfx_draw_string_centred_clipped(dpi, STR_TRACK_PREVIEW_NAME_FORMAT, &_trackName, 0, x, y, 368);
// Information
x = w->x + widget->left + 1;
@ -342,85 +256,85 @@ static void window_install_track_paint(rct_window *w, rct_drawpixelinfo *dpi)
// 0x006D3CF1 -- 0x006d3d71 missing
// Stats
rating = track_td6->excitement * 10;
fixed32_2dp rating = td6->excitement * 10;
gfx_draw_string_left(dpi, STR_TRACK_LIST_EXCITEMENT_RATING, &rating, 0, x, y);
y += 10;
rating = track_td6->intensity * 10;
rating = td6->intensity * 10;
gfx_draw_string_left(dpi, STR_TRACK_LIST_INTENSITY_RATING, &rating, 0, x, y);
y += 10;
rating = track_td6->nausea * 10;
rating = td6->nausea * 10;
gfx_draw_string_left(dpi, STR_TRACK_LIST_NAUSEA_RATING, &rating, 0, x, y);
y += 14;
if (track_td6->type != RIDE_TYPE_MAZE) {
if (track_td6->type == RIDE_TYPE_MINI_GOLF) {
if (td6->type != RIDE_TYPE_MAZE) {
if (td6->type == RIDE_TYPE_MINI_GOLF) {
// Holes
holes = track_td6->holes & 0x1F;
uint16 holes = td6->holes & 0x1F;
gfx_draw_string_left(dpi, STR_HOLES, &holes, 0, x, y);
y += 10;
}
else {
// Maximum speed
speed = ((track_td6->max_speed << 16) * 9) >> 18;
uint16 speed = ((td6->max_speed << 16) * 9) >> 18;
gfx_draw_string_left(dpi, STR_MAX_SPEED, &speed, 0, x, y);
y += 10;
// Average speed
speed = ((track_td6->average_speed << 16) * 9) >> 18;
speed = ((td6->average_speed << 16) * 9) >> 18;
gfx_draw_string_left(dpi, STR_AVERAGE_SPEED, &speed, 0, x, y);
y += 10;
}
// Ride length
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint16) = 1345;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint16) = track_td6->ride_length;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint16) = td6->ride_length;
gfx_draw_string_left_clipped(dpi, STR_TRACK_LIST_RIDE_LENGTH, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, 0, x, y, 214);
y += 10;
}
if (ride_type_has_flag(track_td6->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) {
if (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) {
// Maximum positive vertical Gs
gForces = track_td6->max_positive_vertical_g * 32;
int gForces = td6->max_positive_vertical_g * 32;
gfx_draw_string_left(dpi, STR_MAX_POSITIVE_VERTICAL_G, &gForces, 0, x, y);
y += 10;
// Maximum negative verical Gs
gForces = track_td6->max_negative_vertical_g * 32;
gForces = td6->max_negative_vertical_g * 32;
gfx_draw_string_left(dpi, STR_MAX_NEGATIVE_VERTICAL_G, &gForces, 0, x, y);
y += 10;
// Maximum lateral Gs
gForces = track_td6->max_lateral_g * 32;
gForces = td6->max_lateral_g * 32;
gfx_draw_string_left(dpi, STR_MAX_LATERAL_G, &gForces, 0, x, y);
y += 10;
// If .TD6
if (track_td6->version_and_colour_scheme / 4 >= 2) {
if (track_td6->total_air_time != 0) {
if (td6->version_and_colour_scheme / 4 >= 2) {
if (td6->total_air_time != 0) {
// Total air time
airTime = track_td6->total_air_time * 25;
int airTime = td6->total_air_time * 25;
gfx_draw_string_left(dpi, STR_TOTAL_AIR_TIME, &airTime, 0, x, y);
y += 10;
}
}
}
if (ride_type_has_flag(track_td6->type, RIDE_TYPE_FLAG_HAS_DROPS)) {
if (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_DROPS)) {
// Drops
drops = track_td6->drops & 0x3F;
uint16 drops = td6->drops & 0x3F;
gfx_draw_string_left(dpi, STR_DROPS, &drops, 0, x, y);
y += 10;
// Drop height is multiplied by 0.75
dropHeight = (track_td6->highest_drop_height + (track_td6->highest_drop_height / 2)) / 2;
uint16 dropHeight = (td6->highest_drop_height + (td6->highest_drop_height / 2)) / 2;
gfx_draw_string_left(dpi, STR_HIGHEST_DROP_HEIGHT, &drops, 0, x, y);
y += 10;
}
if (track_td6->type != RIDE_TYPE_MINI_GOLF) {
inversions = track_td6->inversions & 0x1F;
if (td6->type != RIDE_TYPE_MINI_GOLF) {
uint16 inversions = td6->inversions & 0x1F;
if (inversions != 0) {
// Inversions
gfx_draw_string_left(dpi, STR_INVERSIONS, &inversions, 0, x, y);
@ -429,16 +343,16 @@ static void window_install_track_paint(rct_window *w, rct_drawpixelinfo *dpi)
}
y += 4;
if (track_td6->space_required_x != 0xFF) {
if (td6->space_required_x != 0xFF) {
// Space required
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint16) = track_td6->space_required_x;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint16) = track_td6->space_required_y;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint16) = td6->space_required_x;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint16) = td6->space_required_y;
gfx_draw_string_left(dpi, STR_TRACK_LIST_SPACE_REQUIRED, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, 0, x, y);
y += 10;
}
if (track_td6->cost != 0) {
gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, &track_td6->cost, 0, x, y);
if (td6->cost != 0) {
gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, &td6->cost, 0, x, y);
y += 14;
}
}
@ -449,17 +363,51 @@ static void window_install_track_paint(rct_window *w, rct_drawpixelinfo *dpi)
*/
static void window_install_track_text_input(rct_window *w, int widgetIndex, char *text)
{
if (text == NULL) {
window_close(w);
if (widgetIndex != WIDX_INSTALL || str_is_null_or_empty(text)) {
return;
}
if (widgetIndex == WIDX_INSTALL) {
char* extension_pointer = track_dest_name;
while (*extension_pointer++ != '.');
--extension_pointer;
strcat(text, extension_pointer);
strcpy(track_dest_name, text);
window_event_mouse_up_call(w, WIDX_INSTALL);
free(_trackName);
_trackName = _strdup(text);
window_event_mouse_up_call(w, WIDX_INSTALL);
}
static void window_install_track_update_preview()
{
track_design_draw_preview(_trackDesign, _trackDesignPreviewPixels);
}
static void window_install_track_design(rct_window *w)
{
utf8 destPath[MAX_PATH];
platform_get_user_directory(destPath, "track");
if (!platform_ensure_directory_exists(destPath)) {
log_error("Unable to create directory '%s'", destPath);
window_error_open(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE);
return;
}
strcat(destPath, _trackName);
strcat(destPath, ".td6");
if (platform_file_exists(destPath)) {
log_info("%s already exists, prompting user for a different track design name", destPath);
window_error_open(STR_UNABLE_TO_INSTALL_THIS_TRACK_DESIGN, STR_NONE);
window_text_input_raw_open(
w,
WIDX_INSTALL,
STR_SELECT_NEW_NAME_FOR_TRACK_DESIGN,
STR_AN_EXISTING_TRACK_DESIGN_ALREADY_HAS_THIS_NAME,
_trackName,
255
);
} else {
if (track_design_index_install(_trackPath, destPath)) {
window_close(w);
} else {
window_error_open(STR_CANT_SAVE_TRACK_DESIGN, STR_NONE);
}
}
}

View File

@ -29,6 +29,7 @@
#include "../rct1.h"
#include "../ride/ride.h"
#include "../ride/track.h"
#include "../ride/track_design.h"
#include "../world/scenery.h"
#include "../ride/ride_data.h"
@ -841,15 +842,17 @@ static ride_list_item window_new_ride_scroll_get_ride_list_item_at(rct_window *w
static int get_num_track_designs(ride_list_item item)
{
track_load_list(item);
char *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, char);
int count = 0;
while (*trackDesignList != 0 && trackDesignList < (char*)0x00F635EC) {
trackDesignList += 128;
count++;
char entry[9];
const char *entryPtr = NULL;
if (item.type < 0x80) {
rct_ride_entry *rideEntry = get_ride_entry(item.entry_index);
if ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) && !rideTypeShouldLoseSeparateFlag(rideEntry)) {
get_ride_entry_name(entry, item.entry_index);
entryPtr = entry;
}
}
return count;
return track_design_index_get_count_for_ride(item.type, entryPtr);
}
/**
@ -942,10 +945,7 @@ static void window_new_ride_select(rct_window *w)
#endif
if (allowTrackDesigns && ride_type_has_flag(item.type, RIDE_TYPE_FLAG_HAS_TRACK)) {
track_load_list(item);
char *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, char);
if (*trackDesignList != 0) {
if (_lastTrackDesignCount > 0) {
window_track_list_open(item);
return;
}

View File

@ -30,6 +30,7 @@
#include "../ride/ride.h"
#include "../ride/ride_data.h"
#include "../ride/track.h"
#include "../ride/track_design.h"
#include "../sprites.h"
#include "../windows/error.h"
#include "../world/map.h"
@ -4629,7 +4630,8 @@ static void cancel_scenery_selection(){
*
* rct2: 0x006D27A3
*/
static void setup_scenery_selection(rct_window* w){
static void setup_scenery_selection(rct_window* w)
{
rct_ride* ride = get_ride(w->number);
if (RCT2_GLOBAL(0x009DEA6F, uint8) & 1){
@ -4639,9 +4641,8 @@ static void setup_scenery_selection(rct_window* w){
while (tool_set(w, 0, 12));
RCT2_GLOBAL(0x00F64DE8, uint8) = (uint8)w->number;
RCT2_GLOBAL(0x009DA193, uint8) = 0xFF;
gTrackSavedMapElements[0] = (rct_map_element*)-1;
track_design_save_init();
gGamePaused |= GAME_PAUSED_SAVING_TRACK;
RCT2_GLOBAL(0x009DEA6F, uint8) |= 1;
@ -4662,7 +4663,7 @@ static void setup_scenery_selection(rct_window* w){
*/
static void window_ride_measurements_design_reset()
{
track_save_reset_scenery();
track_design_save_reset_scenery();
}
/**
@ -4671,7 +4672,7 @@ static void window_ride_measurements_design_reset()
*/
static void window_ride_measurements_design_select_nearby_scenery()
{
track_save_select_nearby_scenery(RCT2_GLOBAL(0x00F64DE8, uint8));
track_design_save_select_nearby_scenery(RCT2_GLOBAL(0x00F64DE8, uint8));
}
/**
@ -4690,7 +4691,7 @@ static void window_ride_measurements_design_cancel()
*/
static void window_ride_measurements_design_save(rct_window *w)
{
if (save_track_design((uint8)w->number) == 0) return;
if (track_design_save((uint8)w->number) == 0) return;
window_ride_measurements_design_cancel();
}
@ -4790,7 +4791,7 @@ static void window_ride_measurements_dropdown(rct_window *w, int widgetIndex, in
dropdownIndex = gDropdownHighlightedIndex;
if (dropdownIndex == 0)
save_track_design((uint8)w->number);
track_design_save((uint8)w->number);
else
setup_scenery_selection(w);
}
@ -4822,7 +4823,7 @@ static void window_ride_measurements_tooldown(rct_window *w, int widgetIndex, in
case VIEWPORT_INTERACTION_ITEM_LARGE_SCENERY:
case VIEWPORT_INTERACTION_ITEM_WALL:
case VIEWPORT_INTERACTION_ITEM_FOOTPATH:
track_save_toggle_map_element(interactionType, mapX, mapY, mapElement);
track_design_save_toggle_map_element(interactionType, mapX, mapY, mapElement);
break;
}
}

View File

@ -1789,7 +1789,8 @@ static void window_ride_construction_mouseup_demolish(rct_window* w)
_currentTrackBeginY,
_currentTrackBeginZ,
_currentTrackPieceDirection,
_currentTrackPieceType
_currentTrackPieceType,
GAME_COMMAND_FLAG_APPLY
);
if (cost == MONEY32_UNDEFINED) {
sub_6C84CE();

View File

@ -17,15 +17,16 @@
#include "../addresses.h"
#include "../audio/audio.h"
#include "../editor.h"
#include "../localisation/localisation.h"
#include "../interface/themes.h"
#include "../interface/widget.h"
#include "../interface/window.h"
#include "../localisation/localisation.h"
#include "../rct1.h"
#include "../ride/ride.h"
#include "../ride/track.h"
#include "../ride/track_design.h"
#include "../sprites.h"
#include "error.h"
#include "../interface/themes.h"
#include "../rct1.h"
enum {
WIDX_BACKGROUND,
@ -52,6 +53,7 @@ static rct_widget window_track_list_widgets[] = {
static void window_track_list_close(rct_window *w);
static void window_track_list_mouseup(rct_window *w, int widgetIndex);
static void window_track_list_update(rct_window *w);
static void window_track_list_scrollgetsize(rct_window *w, int scrollIndex, int *width, int *height);
static void window_track_list_scrollmousedown(rct_window *w, int scrollIndex, int x, int y);
static void window_track_list_scrollmouseover(rct_window *w, int scrollIndex, int x, int y);
@ -67,7 +69,7 @@ static rct_window_event_list window_track_list_events = {
NULL,
NULL,
NULL,
NULL,
window_track_list_update,
NULL,
NULL,
NULL,
@ -91,31 +93,34 @@ static rct_window_event_list window_track_list_events = {
window_track_list_scrollpaint
};
#define TRACK_DESIGN_INDEX_UNLOADED UINT16_MAX
ride_list_item _window_track_list_item;
static track_design_file_ref *_trackDesigns = NULL;
static size_t _trackDesignsCount = 0;
static uint16 _loadedTrackDesignIndex;
static rct_track_td6 *_loadedTrackDesign;
static uint8 *_trackDesignPreviewPixels;
static void track_list_load_designs(ride_list_item item);
static bool track_list_load_design_for_preview(utf8 *path);
/**
*
* rct2: 0x006CF1A2
*/
void window_track_list_open(ride_list_item item)
{
rct_window *w;
int x, y;
void *mem;
window_close_construction_windows();
_window_track_list_item = item;
track_list_load_designs(item);
if (RCT2_GLOBAL(0x00F635ED, uint8) & 1)
if (RCT2_GLOBAL(0x00F635ED, uint8) & 1) {
window_error_open(STR_WARNING, STR_TOO_MANY_TRACK_DESIGNS_OF_THIS_TYPE);
}
mem = malloc(1285292);
if (mem == NULL)
return;
RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_CACHE, void*) = mem;
reset_track_list_cache();
int x, y;
if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) {
x = gScreenWidth / 2 - 300;
y = max(28, gScreenHeight / 2 - 200);
@ -123,7 +128,7 @@ void window_track_list_open(ride_list_item item)
x = 0;
y = 29;
}
w = window_create(
rct_window *w = window_create(
x,
y,
600,
@ -136,92 +141,14 @@ void window_track_list_open(ride_list_item item)
w->enabled_widgets = (1 << WIDX_CLOSE) | (1 << WIDX_ROTATE) | (1 << WIDX_TOGGLE_SCENERY) | (1 << WIDX_BACK);
window_init_scroll_widgets(w);
w->track_list.var_480 = 0xFFFF;
w->track_list.var_482 = gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER ? 0 : 1;
w->track_list.var_484 = 0;
RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) = 0;
w->track_list.reload_track_designs = false;
w->selected_list_item = gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER ? 0 : 1;
gTrackDesignSceneryToggle = false;
window_push_others_right(w);
_currentTrackPieceDirection = 2;
}
/**
*
* rct2: 0x006CFB82
*/
static void window_track_list_select(rct_window *w, int index)
{
utf8 *trackDesignItem, *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, utf8);
rct_track_design *trackDesign;
w->track_list.var_480 = index;
audio_play_sound_panned(SOUND_CLICK_1, w->x + (w->width / 2), 0, 0, 0);
if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) && index == 0) {
window_close(w);
ride_construct_new(_window_track_list_item);
return;
}
if (RCT2_GLOBAL(0x00F44153, uint8) != 0)
RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) = 1;
if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER))
index--;
trackDesignItem = trackDesignList + (index * 128);
RCT2_GLOBAL(0x00F4403C, utf8*) = trackDesignItem;
window_track_list_format_name(
(char*)0x009BC313,
trackDesignItem,
gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER ?
0 :
FORMAT_WHITE,
1);
char track_path[MAX_PATH] = { 0 };
substitute_path(track_path, (char*)RCT2_ADDRESS_TRACKS_PATH, trackDesignItem);
if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) {
window_track_manage_open();
return;
}
if (!load_track_design(track_path)) {
w->track_list.var_480 = 0xFFFF;
window_invalidate(w);
return;
}
trackDesign = track_get_info(index, NULL);
if (trackDesign == NULL) return;
if (trackDesign->track_td6.track_flags & 4)
window_error_open(STR_THIS_DESIGN_WILL_BE_BUILT_WITH_AN_ALTERNATIVE_VEHICLE_TYPE, -1);
window_close(w);
window_track_place_open();
}
static int window_track_list_get_list_item_index_from_position(int x, int y)
{
int index;
uint8 *trackDesignItem, *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, uint8);
index = 0;
if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) {
y -= 10;
if (y < 0)
return index;
index++;
}
for (trackDesignItem = trackDesignList; *trackDesignItem != 0; trackDesignItem += 128) {
y -= 10;
if (y < 0)
return index;
index++;
}
return -1;
_trackDesignPreviewPixels = calloc(4, TRACK_PREVIEW_IMAGE_SIZE);
}
/**
@ -230,7 +157,67 @@ static int window_track_list_get_list_item_index_from_position(int x, int y)
*/
static void window_track_list_close(rct_window *w)
{
free(RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_CACHE, void*));
// Dispose track design and preview
track_design_dispose(_loadedTrackDesign);
_loadedTrackDesign = NULL;
SafeFree(_trackDesignPreviewPixels);
// Dispose track list
for (size_t i = 0; i < _trackDesignsCount; i++) {
free(_trackDesigns[i].name);
free(_trackDesigns[i].path);
}
SafeFree(_trackDesigns);
_trackDesignsCount = 0;
}
/**
*
* rct2: 0x006CFB82
*/
static void window_track_list_select(rct_window *w, int index)
{
w->track_list.var_480 = index;
audio_play_sound_panned(SOUND_CLICK_1, w->x + (w->width / 2), 0, 0, 0);
if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) {
if (index == 0) {
window_close(w);
ride_construct_new(_window_track_list_item);
return;
}
index--;
}
if (RCT2_GLOBAL(0x00F44153, uint8) != 0) {
gTrackDesignSceneryToggle = true;
}
track_design_file_ref *tdRef = &_trackDesigns[index];
if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) {
window_track_manage_open(tdRef);
} else {
if (_loadedTrackDesignIndex != TRACK_DESIGN_INDEX_UNLOADED && (_loadedTrackDesign->track_flags & 4)) {
window_error_open(STR_THIS_DESIGN_WILL_BE_BUILT_WITH_AN_ALTERNATIVE_VEHICLE_TYPE, STR_NONE);
}
window_track_place_open(tdRef);
}
}
static int window_track_list_get_list_item_index_from_position(int x, int y)
{
int maxItems = _trackDesignsCount;
if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) {
// Extra item: custom design
maxItems++;
}
int index = y / 10;
if (index < 0 || index >= maxItems) {
index = -1;
}
return index;
}
/**
@ -254,8 +241,8 @@ static void window_track_list_mouseup(rct_window *w, int widgetIndex)
window_invalidate(w);
break;
case WIDX_TOGGLE_SCENERY:
RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) ^= 1;
reset_track_list_cache();
gTrackDesignSceneryToggle = !gTrackDesignSceneryToggle;
_loadedTrackDesignIndex = TRACK_DESIGN_INDEX_UNLOADED;
window_invalidate(w);
break;
case WIDX_BACK:
@ -277,11 +264,13 @@ static void window_track_list_mouseup(rct_window *w, int widgetIndex)
*/
static void window_track_list_scrollgetsize(rct_window *w, int scrollIndex, int *width, int *height)
{
uint8 *trackDesignItem, *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, uint8);
size_t numItems = _trackDesignsCount;
if (!(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) {
// Extra item: custom design
numItems++;
}
*height = gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER ? 0 : 10;
for (trackDesignItem = trackDesignList; *trackDesignItem != 0; trackDesignItem += 128)
*height += 10;
*height = (int)(numItems * 10);
}
/**
@ -290,14 +279,12 @@ static void window_track_list_scrollgetsize(rct_window *w, int scrollIndex, int
*/
static void window_track_list_scrollmousedown(rct_window *w, int scrollIndex, int x, int y)
{
int i;
if (w->track_list.var_484 & 1)
return;
i = window_track_list_get_list_item_index_from_position(x, y);
if (i != -1)
window_track_list_select(w, i);
if (!(w->track_list.var_484 & 1)) {
int i = window_track_list_get_list_item_index_from_position(x, y);
if (i != -1) {
window_track_list_select(w, i);
}
}
}
/**
@ -306,15 +293,12 @@ static void window_track_list_scrollmousedown(rct_window *w, int scrollIndex, in
*/
static void window_track_list_scrollmouseover(rct_window *w, int scrollIndex, int x, int y)
{
int i;
if (w->track_list.var_484 & 1)
return;
i = window_track_list_get_list_item_index_from_position(x, y);
if (i != -1 && w->track_list.var_482 != i) {
w->track_list.var_482 = i;
window_invalidate(w);
if (!(w->track_list.var_484 & 1)) {
int i = window_track_list_get_list_item_index_from_position(x, y);
if (i != -1 && w->selected_list_item != i) {
w->selected_list_item = i;
window_invalidate(w);
}
}
}
@ -327,6 +311,15 @@ static void window_track_list_tooltip(rct_window* w, int widgetIndex, rct_string
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS, uint16) = STR_LIST;
}
static void window_track_list_update(rct_window *w)
{
if (w->track_list.reload_track_designs) {
track_list_load_designs(_window_track_list_item);
w->selected_list_item = 0;
window_invalidate(w);
}
}
/**
*
* rct2: 0x006CF2D6
@ -353,15 +346,16 @@ static void window_track_list_invalidate(rct_window *w)
window_track_list_widgets[WIDX_TRACK_LIST].tooltip = STR_CLICK_ON_DESIGN_TO_BUILD_IT_TIP;
}
if ((gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) || w->track_list.var_482 != 0) {
if ((gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) || w->selected_list_item != 0) {
w->pressed_widgets |= 1 << WIDX_TRACK_PREVIEW;
w->disabled_widgets &= ~(1 << WIDX_TRACK_PREVIEW);
window_track_list_widgets[WIDX_ROTATE].type = WWT_FLATBTN;
window_track_list_widgets[WIDX_TOGGLE_SCENERY].type = WWT_FLATBTN;
if (RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) == 0)
w->pressed_widgets |= (1 << WIDX_TOGGLE_SCENERY);
else
if (gTrackDesignSceneryToggle) {
w->pressed_widgets &= ~(1 << WIDX_TOGGLE_SCENERY);
} else {
w->pressed_widgets |= (1 << WIDX_TOGGLE_SCENERY);
}
} else {
w->pressed_widgets &= ~(1 << WIDX_TRACK_PREVIEW);
w->disabled_widgets |= (1 << WIDX_TRACK_PREVIEW);
@ -376,41 +370,43 @@ static void window_track_list_invalidate(rct_window *w)
*/
static void window_track_list_paint(rct_window *w, rct_drawpixelinfo *dpi)
{
rct_widget *widget;
rct_track_design *trackDesign = NULL;
uint8 *image;
utf8 *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, utf8);
uint16 holes, speed, drops, dropHeight, inversions;
fixed32_2dp rating;
int trackIndex, x, y, colour, gForces, airTime;
rct_g1_element tmpElement, *substituteElement;
window_draw_widgets(w, dpi);
trackIndex = w->track_list.var_482;
int trackIndex = w->selected_list_item;
if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) {
if (*trackDesignList == 0 || trackIndex == -1)
if (_trackDesignsCount == 0 || trackIndex == -1) {
return;
}
} else if (trackIndex-- == 0) {
return;
}
// Track preview
widget = &window_track_list_widgets[WIDX_TRACK_PREVIEW];
int x, y, colour;
rct_widget *widget = &window_track_list_widgets[WIDX_TRACK_PREVIEW];
x = w->x + widget->left + 1;
y = w->y + widget->top + 1;
colour = ColourMapA[w->colours[0]].darkest;
gfx_fill_rect(dpi, x, y, x + 369, y + 216, colour);
trackDesign = track_get_info(trackIndex, &image);
if (trackDesign == NULL)
if (_loadedTrackDesignIndex != trackIndex) {
utf8 *path = _trackDesigns[trackIndex].path;
if (track_list_load_design_for_preview(path)) {
_loadedTrackDesignIndex = trackIndex;
} else {
_loadedTrackDesignIndex = TRACK_DESIGN_INDEX_UNLOADED;
return;
}
}
rct_track_td6 *td6 = _loadedTrackDesign;
if (td6 == NULL) {
return;
}
rct_track_td6* track_td6 = &trackDesign->track_td6;
substituteElement = &g1Elements[0];
tmpElement = *substituteElement;
substituteElement->offset = image;
rct_g1_element *substituteElement = &g1Elements[0];
rct_g1_element tmpElement = *substituteElement;
substituteElement->offset = _trackDesignPreviewPixels + (_currentTrackPieceDirection * TRACK_PREVIEW_IMAGE_SIZE);
substituteElement->width = 370;
substituteElement->height = 217;
substituteElement->x_offset = 0;
@ -424,15 +420,15 @@ static void window_track_list_paint(rct_window *w, rct_drawpixelinfo *dpi)
RCT2_GLOBAL(0x00F44153, uint8) = 0;
// Warnings
if ((track_td6->track_flags & 4) && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) {
if ((td6->track_flags & 4) && !(gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER)) {
// Vehicle design not available
gfx_draw_string_centred_clipped(dpi, STR_VEHICLE_DESIGN_UNAVAILABLE, NULL, 0, x, y, 368);
y -= 10;
}
if (track_td6->track_flags & 1) {
if (td6->track_flags & 1) {
RCT2_GLOBAL(0x00F44153, uint8) = 1;
if (RCT2_GLOBAL(RCT2_ADDRESS_TRACK_DESIGN_SCENERY_TOGGLE, uint8) == 0) {
if (!gTrackDesignSceneryToggle) {
// Scenery not available
gfx_draw_string_centred_clipped(dpi, STR_DESIGN_INCLUDES_SCENERY_WHICH_IS_UNAVAILABLE, NULL, 0, x, y, 368);
y -= 10;
@ -440,92 +436,92 @@ static void window_track_list_paint(rct_window *w, rct_drawpixelinfo *dpi)
}
// Track design name
window_track_list_format_name((char*)0x009BC677, trackDesignList + (trackIndex * 128), FORMAT_WINDOW_COLOUR_1, 1);
gfx_draw_string_centred_clipped(dpi, 3165, NULL, 0, x, y, 368);
utf8 *trackName = _trackDesigns[trackIndex].name;
gfx_draw_string_centred_clipped(dpi, STR_TRACK_PREVIEW_NAME_FORMAT, &trackName, 0, x, y, 368);
// Information
x = w->x + widget->left + 1;
y = w->y + widget->bottom + 2;
// Stats
rating = track_td6->excitement * 10;
fixed32_2dp rating = td6->excitement * 10;
gfx_draw_string_left(dpi, STR_TRACK_LIST_EXCITEMENT_RATING, &rating, 0, x, y);
y += 10;
rating = track_td6->intensity * 10;
rating = td6->intensity * 10;
gfx_draw_string_left(dpi, STR_TRACK_LIST_INTENSITY_RATING, &rating, 0, x, y);
y += 10;
rating = track_td6->nausea * 10;
rating = td6->nausea * 10;
gfx_draw_string_left(dpi, STR_TRACK_LIST_NAUSEA_RATING, &rating, 0, x, y);
y += 14;
if (track_td6->type != RIDE_TYPE_MAZE) {
if (track_td6->type == RIDE_TYPE_MINI_GOLF) {
if (td6->type != RIDE_TYPE_MAZE) {
if (td6->type == RIDE_TYPE_MINI_GOLF) {
// Holes
holes = track_td6->holes & 0x1F;
uint16 holes = td6->holes & 0x1F;
gfx_draw_string_left(dpi, STR_HOLES, &holes, 0, x, y);
y += 10;
} else {
// Maximum speed
speed = ((track_td6->max_speed << 16) * 9) >> 18;
uint16 speed = ((td6->max_speed << 16) * 9) >> 18;
gfx_draw_string_left(dpi, STR_MAX_SPEED, &speed, 0, x, y);
y += 10;
// Average speed
speed = ((track_td6->average_speed << 16) * 9) >> 18;
speed = ((td6->average_speed << 16) * 9) >> 18;
gfx_draw_string_left(dpi, STR_AVERAGE_SPEED, &speed, 0, x, y);
y += 10;
}
// Ride length
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint16) = 1345;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint16) = track_td6->ride_length;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint16) = td6->ride_length;
gfx_draw_string_left_clipped(dpi, STR_TRACK_LIST_RIDE_LENGTH, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, 0, x, y, 214);
y += 10;
}
if (ride_type_has_flag(track_td6->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) {
if (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_G_FORCES)) {
// Maximum positive vertical Gs
gForces = track_td6->max_positive_vertical_g * 32;
int gForces = td6->max_positive_vertical_g * 32;
gfx_draw_string_left(dpi, STR_MAX_POSITIVE_VERTICAL_G, &gForces, 0, x, y);
y += 10;
// Maximum negative verical Gs
gForces = track_td6->max_negative_vertical_g * 32;
gForces = td6->max_negative_vertical_g * 32;
gfx_draw_string_left(dpi, STR_MAX_NEGATIVE_VERTICAL_G, &gForces, 0, x, y);
y += 10;
// Maximum lateral Gs
gForces = track_td6->max_lateral_g * 32;
gForces = td6->max_lateral_g * 32;
gfx_draw_string_left(dpi, STR_MAX_LATERAL_G, &gForces, 0, x, y);
y += 10;
// If .TD6
if (track_td6->version_and_colour_scheme / 4 >= 2) {
if (track_td6->total_air_time != 0) {
if (td6->version_and_colour_scheme / 4 >= 2) {
if (td6->total_air_time != 0) {
// Total air time
airTime = track_td6->total_air_time * 25;
int airTime = td6->total_air_time * 25;
gfx_draw_string_left(dpi, STR_TOTAL_AIR_TIME, &airTime, 0, x, y);
y += 10;
}
}
}
if (ride_type_has_flag(track_td6->type, RIDE_TYPE_FLAG_HAS_DROPS)) {
if (ride_type_has_flag(td6->type, RIDE_TYPE_FLAG_HAS_DROPS)) {
// Drops
drops = track_td6->drops & 0x3F;
uint16 drops = td6->drops & 0x3F;
gfx_draw_string_left(dpi, STR_DROPS, &drops, 0, x, y);
y += 10;
// Drop height is multiplied by 0.75
dropHeight = (track_td6->highest_drop_height + (track_td6->highest_drop_height / 2)) / 2;
uint16 dropHeight = (td6->highest_drop_height + (td6->highest_drop_height / 2)) / 2;
gfx_draw_string_left(dpi, STR_HIGHEST_DROP_HEIGHT, &drops, 0, x, y);
y += 10;
}
if (track_td6->type != RIDE_TYPE_MINI_GOLF) {
inversions = track_td6->inversions & 0x1F;
if (td6->type != RIDE_TYPE_MINI_GOLF) {
uint16 inversions = td6->inversions & 0x1F;
if (inversions != 0) {
// Inversions
gfx_draw_string_left(dpi, STR_INVERSIONS, &inversions, 0, x, y);
@ -534,16 +530,16 @@ static void window_track_list_paint(rct_window *w, rct_drawpixelinfo *dpi)
}
y += 4;
if (track_td6->space_required_x != 0xFF) {
if (td6->space_required_x != 0xFF) {
// Space required
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint16) = track_td6->space_required_x;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint16) = track_td6->space_required_y;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, uint16) = td6->space_required_x;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, uint16) = td6->space_required_y;
gfx_draw_string_left(dpi, STR_TRACK_LIST_SPACE_REQUIRED, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, 0, x, y);
y += 10;
}
if (track_td6->cost != 0) {
gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, &track_td6->cost, 0, x, y);
if (td6->cost != 0) {
gfx_draw_string_left(dpi, STR_TRACK_LIST_COST_AROUND, &td6->cost, 0, x, y);
y += 14;
}
}
@ -554,28 +550,23 @@ static void window_track_list_paint(rct_window *w, rct_drawpixelinfo *dpi)
*/
static void window_track_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi, int scrollIndex)
{
rct_string_id stringId, stringId2;
int i, x, y, colour;
utf8 *trackDesignItem, *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, utf8);
colour = ColourMapA[w->colours[0]].mid_light;
int colour = ColourMapA[w->colours[0]].mid_light;
colour = (colour << 24) | (colour << 16) | (colour << 8) | colour;
gfx_clear(dpi, colour);
i = 0;
x = 0;
y = 0;
trackDesignItem = trackDesignList;
int x = 0;
int y = 0;
size_t listIndex = 0;
if (gScreenFlags & SCREEN_FLAGS_TRACK_MANAGER) {
if (*trackDesignItem == 0) {
if (_trackDesignsCount == 0) {
// No track designs
gfx_draw_string_left(dpi, STR_NO_TRACK_DESIGNS_OF_THIS_TYPE, NULL, 0, x, y - 1);
return;
}
} else {
// Build custom track item
if (i == w->track_list.var_482) {
rct_string_id stringId;
if (listIndex == w->selected_list_item) {
// Highlight
gfx_fill_rect(dpi, x, y, w->width, y + 9, 0x2000000 | 49);
stringId = 1193;
@ -583,15 +574,16 @@ static void window_track_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi,
stringId = 1191;
}
stringId2 = STR_BUILD_CUSTOM_DESIGN;
rct_string_id stringId2 = STR_BUILD_CUSTOM_DESIGN;
gfx_draw_string_left(dpi, stringId, &stringId2, 0, x, y - 1);
y += 10;
i++;
listIndex++;
}
while (*trackDesignItem != 0) {
for (size_t i = 0; i < _trackDesignsCount; i++, listIndex++) {
if (y + 10 >= dpi->y && y < dpi->y + dpi->height) {
if (i == w->track_list.var_482) {
rct_string_id stringId;
if (listIndex == w->selected_list_item) {
// Highlight
gfx_fill_rect(dpi, x, y, w->width, y + 9, 0x2000000 | 49);
stringId = 1193;
@ -600,12 +592,38 @@ static void window_track_list_scrollpaint(rct_window *w, rct_drawpixelinfo *dpi,
}
// Draw track name
window_track_list_format_name((char *)language_get_string(3165), trackDesignItem, 0, 1);
stringId2 = 3165;
gfx_draw_string_left(dpi, stringId, &stringId2, 0, x, y - 1);
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 0, rct_string_id) = STR_TRACK_LIST_NAME_FORMAT;
RCT2_GLOBAL(RCT2_ADDRESS_COMMON_FORMAT_ARGS + 2, utf8*) = _trackDesigns[i].name;
gfx_draw_string_left(dpi, stringId, (void*)RCT2_ADDRESS_COMMON_FORMAT_ARGS, 0, x, y - 1);
}
y += 10;
i++;
trackDesignItem += 128;
}
}
static void track_list_load_designs(ride_list_item item)
{
char entry[9];
const char *entryPtr = NULL;
if (item.type < 0x80) {
rct_ride_entry *rideEntry = get_ride_entry(item.entry_index);
if ((rideEntry->flags & RIDE_ENTRY_FLAG_SEPARATE_RIDE) && !rideTypeShouldLoseSeparateFlag(rideEntry)) {
get_ride_entry_name(entry, item.entry_index);
entryPtr = entry;
}
}
_trackDesignsCount = track_design_index_get_for_ride(&_trackDesigns, item.type, entryPtr);
}
static bool track_list_load_design_for_preview(utf8 *path)
{
// Dispose currently loaded track
track_design_dispose(_loadedTrackDesign);
_loadedTrackDesign = NULL;
_loadedTrackDesign = track_design_open(path);
if (_loadedTrackDesign != NULL) {
track_design_draw_preview(_loadedTrackDesign, _trackDesignPreviewPixels);
return true;
}
return false;
}

View File

@ -21,6 +21,8 @@
#include "../interface/window.h"
#include "../localisation/localisation.h"
#include "../ride/track.h"
#include "../ride/track_design.h"
#include "../util/util.h"
#include "error.h"
#pragma region Widgets
@ -47,10 +49,10 @@ static rct_widget window_track_manage_widgets[] = {
static rct_widget window_track_delete_prompt_widgets[] = {
{ WWT_FRAME, 0, 0, 249, 0, 73, STR_NONE, STR_NONE },
{ WWT_CAPTION, 0, 1, 248, 1, 14, 3356, STR_WINDOW_TITLE_TIP },
{ WWT_CAPTION, 0, 1, 248, 1, 14, STR_DELETE_FILE, STR_WINDOW_TITLE_TIP },
{ WWT_CLOSEBOX, 0, 237, 247, 2, 13, STR_CLOSE_X, STR_CLOSE_WINDOW_TIP },
{ WWT_DROPDOWN_BUTTON, 0, 10, 119, 54, 65, 3349, STR_NONE },
{ WWT_DROPDOWN_BUTTON, 0, 130, 239, 54, 65, 972, STR_NONE },
{ WWT_DROPDOWN_BUTTON, 0, 10, 119, 54, 65, STR_TRACK_MANAGE_DELETE, STR_NONE },
{ WWT_DROPDOWN_BUTTON, 0, 130, 239, 54, 65, STR_CANCEL, STR_NONE },
{ WIDGETS_END }
};
@ -134,19 +136,19 @@ static rct_window_event_list window_track_delete_prompt_events = {
#pragma endregion
static track_design_file_ref *_trackDesignFileReference;
static void window_track_delete_prompt_open();
/**
*
* rct2: 0x006D348F
*/
void window_track_manage_open()
void window_track_manage_open(track_design_file_ref *tdFileRef)
{
rct_window *w, *trackDesignListWindow;
window_close_by_class(WC_MANAGE_TRACK_DESIGN);
w = window_create_centred(
rct_window *w = window_create_centred(
250,
44,
&window_track_manage_events,
@ -160,9 +162,15 @@ void window_track_manage_open()
(1 << WIDX_DELETE);
window_init_scroll_widgets(w);
trackDesignListWindow = window_find_by_class(WC_TRACK_DESIGN_LIST);
if (trackDesignListWindow != NULL)
rct_window *trackDesignListWindow = window_find_by_class(WC_TRACK_DESIGN_LIST);
if (trackDesignListWindow != NULL) {
trackDesignListWindow->track_list.var_484 |= 1;
}
utf8 *title = (utf8*)language_get_string(3155);
format_string(title, STR_TRACK_LIST_NAME_FORMAT, &tdFileRef->name);
_trackDesignFileReference = tdFileRef;
}
/**
@ -172,8 +180,9 @@ void window_track_manage_open()
static void window_track_manage_close(rct_window *w)
{
rct_window *trackDesignListWindow = window_find_by_class(WC_TRACK_DESIGN_LIST);
if (trackDesignListWindow != NULL)
if (trackDesignListWindow != NULL) {
trackDesignListWindow->track_list.var_484 &= ~1;
}
}
/**
@ -182,24 +191,19 @@ static void window_track_manage_close(rct_window *w)
*/
static void window_track_manage_mouseup(rct_window *w, int widgetIndex)
{
char *trackDesignList = RCT2_ADDRESS(RCT2_ADDRESS_TRACK_LIST, char);
rct_window *trackDesignListWindow;
char *dst, *src;
switch (widgetIndex) {
case WIDX_CLOSE:
window_close(w);
break;
case WIDX_RENAME:
trackDesignListWindow = window_find_by_class(WC_TRACK_DESIGN_LIST);
if (trackDesignListWindow != NULL) {
src = &trackDesignList[trackDesignListWindow->track_list.var_482 * 128];
dst = (char*)0x009BC677;
while (*src != 0 && *src != '.')
*dst++ = *src++;
*dst = 0;
window_text_input_open(w, widgetIndex, 3350, 3351, 3165, 0, 127);
}
window_text_input_raw_open(
w,
widgetIndex,
STR_TRACK_DESIGN_RENAME_TITLE,
STR_TRACK_DESIGN_RENAME_DESC,
_trackDesignFileReference->name,
127
);
break;
case WIDX_DELETE:
window_track_delete_prompt_open();
@ -213,10 +217,11 @@ static void window_track_manage_mouseup(rct_window *w, int widgetIndex)
*/
static void window_track_manage_textinput(rct_window *w, int widgetIndex, char *text)
{
if (widgetIndex != WIDX_RENAME || text == NULL)
if (widgetIndex != WIDX_RENAME || str_is_null_or_empty(text)) {
return;
}
if (track_rename(text)) {
if (track_design_index_rename(_trackDesignFileReference->path, text)) {
window_close_by_class(WC_TRACK_DELETE_PROMPT);
window_close(w);
} else {
@ -279,10 +284,11 @@ static void window_track_delete_prompt_mouseup(rct_window *w, int widgetIndex)
break;
case WIDX_PROMPT_DELETE:
window_close(w);
if (track_delete())
if (track_design_index_delete(_trackDesignFileReference->path)) {
window_close_by_class(WC_MANAGE_TRACK_DESIGN);
else
} else {
window_error_open(STR_CANT_DELETE_TRACK_DESIGN, gGameCommandErrorText);
}
break;
}
}
@ -298,12 +304,15 @@ static void window_track_delete_prompt_invalidate(rct_window *w)
*/
static void window_track_delete_prompt_paint(rct_window *w, rct_drawpixelinfo *dpi)
{
rct_string_id stringId;
window_draw_widgets(w, dpi);
stringId = 3155;
gfx_draw_string_centred_wrapped(
dpi, &stringId, w->x + 125, w->y + 28, 246, STR_ARE_YOU_SURE_YOU_WANT_TO_PERMANENTLY_DELETE_TRACK, 0
dpi,
&_trackDesignFileReference->name,
w->x + 125,
w->y + 28,
246,
STR_ARE_YOU_SURE_YOU_WANT_TO_PERMANENTLY_DELETE_TRACK,
0
);
}

View File

@ -16,17 +16,19 @@
#include "../addresses.h"
#include "../audio/audio.h"
#include "../cheats.h"
#include "../game.h"
#include "../input.h"
#include "../interface/themes.h"
#include "../interface/viewport.h"
#include "../interface/widget.h"
#include "../interface/window.h"
#include "../localisation/localisation.h"
#include "../sprites.h"
#include "../ride/track.h"
#include "../ride/track_data.h"
#include "../interface/themes.h"
#include "../cheats.h"
#include "../ride/track_design.h"
#include "../sprites.h"
#include "../util/util.h"
#define TRACK_MINI_PREVIEW_WIDTH 168
#define TRACK_MINI_PREVIEW_HEIGHT 78
@ -98,12 +100,27 @@ static uint8 *_window_track_place_mini_preview;
static sint16 _window_track_place_last_x;
static sint16 _window_track_place_last_y;
static uint8 _window_track_place_last_was_valid;
static uint8 _window_track_place_ride_index;
static bool _window_track_place_last_was_valid;
static sint16 _window_track_place_last_valid_x;
static sint16 _window_track_place_last_valid_y;
static sint16 _window_track_place_last_valid_z;
static money32 _window_track_place_last_cost;
static rct_track_td6 *_trackDesign;
static void window_track_place_clear_provisional();
static int window_track_place_get_base_z(int x, int y);
static void window_track_place_attempt_placement(rct_track_td6 *td6, int x, int y, int z, int bl, money32 *cost, uint8 *rideIndex);
static void window_track_place_clear_mini_preview();
static void window_track_place_draw_mini_preview(rct_track_td6 *td6);
static void window_track_place_draw_mini_preview_track(rct_track_td6 *td6, int pass, rct_xy16 origin, rct_xy16 *min, rct_xy16 *max);
static void window_track_place_draw_mini_preview_maze(rct_track_td6 *td6, int pass, rct_xy16 origin, rct_xy16 *min, rct_xy16 *max);
static rct_xy16 draw_mini_preview_get_pixel_position(sint16 x, sint16 y);
static bool draw_mini_preview_is_pixel_in_bounds(rct_xy16 pixel);
static uint8 *draw_mini_preview_get_pixel_ptr(rct_xy16 pixel);
/**
*
* rct2: 0x006D182E
@ -115,265 +132,23 @@ static void window_track_place_clear_mini_preview()
#define swap(x, y) x = x ^ y; y = x ^ y; x = x ^ y;
/**
*
* rct2: 0x006D1845
*/
static void window_track_place_draw_mini_preview()
{
rct_track_td6 *track = RCT2_ADDRESS(0x009D8178, rct_track_td6);
uint8 *pixel, colour, bits;
int i, rotation, pass, x, y, pixelX, pixelY, originX, originY, minX, minY, maxX, maxY;
rct_maze_element *mazeElement;
rct_track_element *trackElement;
const rct_preview_track *trackBlock;
window_track_place_clear_mini_preview();
minX = 0;
minY = 0;
maxX = 0;
maxY = 0;
// First pass is used to determine the width and height of the image so it can centre it
for (pass = 0; pass < 2; pass++) {
originX = 0;
originY = 0;
if (pass == 1) {
originX -= ((maxX + minX) >> 6) << 5;
originY -= ((maxY + minY) >> 6) << 5;
}
if (track->type != RIDE_TYPE_MAZE) {
#pragma region Track
rotation = _currentTrackPieceDirection + get_current_rotation();
trackElement = RCT2_ADDRESS(0x009D821B, rct_track_element);
while (trackElement->type != 255) {
int trackType = trackElement->type;
if (trackType == 101)
trackType = 255;
// Station track is a lighter colour
colour = RCT2_ADDRESS(0x0099BA64, uint8)[trackType * 16] & 0x10 ? 222 : 218;
// Follow a single track piece shape
trackBlock = TrackBlocks[trackType];
while (trackBlock->index != 255) {
x = originX;
y = originY;
switch (rotation & 3) {
case 0:
x += trackBlock->x;
y += trackBlock->y;
break;
case 1:
x += trackBlock->y;
y -= trackBlock->x;
break;
case 2:
x -= trackBlock->x;
y -= trackBlock->y;
break;
case 3:
x -= trackBlock->y;
y += trackBlock->x;
break;
}
if (pass == 0) {
minX = min(minX, x);
maxX = max(maxX, x);
minY = min(minY, y);
maxY = max(maxY, y);
} else {
pixelX = 80 + ((y / 32) - (x / 32)) * 4;
pixelY = 38 + ((y / 32) + (x / 32)) * 2;
if (pixelX >= 0 && pixelY >= 0 && pixelX <= 160 && pixelY <= 75) {
pixel = &_window_track_place_mini_preview[pixelY * TRACK_MINI_PREVIEW_WIDTH + pixelX];
bits = trackBlock->var_08 << (rotation & 3);
bits = (bits & 0x0F) | ((bits & 0xF0) >> 4);
for (i = 0; i < 4; i++) {
if (bits & 1) pixel[338 + i] = colour;
if (bits & 2) pixel[168 + i] = colour;
if (bits & 4) pixel[ 2 + i] = colour;
if (bits & 8) pixel[172 + i] = colour;
}
}
}
trackBlock++;
}
// Change rotation and next position based on track curvature
rotation &= 3;
const rct_track_coordinates* track_coordinate = &TrackCoordinates[trackType];
trackType *= 10;
switch (rotation) {
case 0:
originX += track_coordinate->x;
originY += track_coordinate->y;
break;
case 1:
originX += track_coordinate->y;
originY -= track_coordinate->x;
break;
case 2:
originX -= track_coordinate->x;
originY -= track_coordinate->y;
break;
case 3:
originX -= track_coordinate->y;
originY += track_coordinate->x;
break;
}
rotation += track_coordinate->rotation_end - track_coordinate->rotation_begin;
rotation &= 3;
if (track_coordinate->rotation_end & 4)
rotation |= 4;
if (!(rotation & 4)) {
originX += TileDirectionDelta[rotation].x;
originY += TileDirectionDelta[rotation].y;
}
trackElement++;
}
#pragma endregion
} else {
#pragma region Maze
rotation = (_currentTrackPieceDirection + get_current_rotation()) & 3;
mazeElement = RCT2_ADDRESS(0x009D821B, rct_maze_element);
while (mazeElement->all != 0) {
x = mazeElement->x * 32;
y = mazeElement->y * 32;
switch (rotation) {
case 1:
x = -x;
swap(x, y);
break;
case 2:
x = -x;
y = -y;
break;
case 3:
x = -x;
swap(x, y);
break;
}
x += originX;
y += originY;
// Entrance or exit is a lighter colour
colour = mazeElement->type == 8 || mazeElement->type == 128 ? 222 : 218;
if (pass == 0) {
minX = min(minX, x);
maxX = max(maxX, x);
minY = min(minY, y);
maxY = max(maxY, y);
} else {
pixelX = 80 + ((y / 32) - (x / 32)) * 4;
pixelY = 38 + ((y / 32) + (x / 32)) * 2;
if (pixelX <= 160 && pixelY <= 75) {
pixel = &_window_track_place_mini_preview[pixelY * TRACK_MINI_PREVIEW_WIDTH + pixelX];
for (i = 0; i < 4; i++) {
pixel[338 + i] = colour;
pixel[168 + i] = colour;
pixel[ 2 + i] = colour;
pixel[172 + i] = colour;
}
}
}
mazeElement++;
}
#pragma endregion
}
}
}
/**
*
* rct2: 0x006D017F
*/
static void window_track_place_clear_provisional()
{
if (_window_track_place_last_was_valid) {
sub_6D01B3(
6,
RCT2_GLOBAL(0x00F440EB, uint8),
_window_track_place_last_valid_x,
_window_track_place_last_valid_y,
_window_track_place_last_valid_z
);
_window_track_place_last_was_valid = 0;
}
}
/**
*
* rct2: 0x006D17C6
*/
static int window_track_place_get_base_z(int x, int y)
{
rct_map_element *mapElement;
int z;
mapElement = map_get_surface_element_at(x >> 5, y >> 5);
z = mapElement->base_height * 8;
// Increase Z above slope
if (mapElement->properties.surface.slope & 0x0F) {
z += 16;
// Increase Z above double slope
if (mapElement->properties.surface.slope & 0x10)
z += 16;
}
// Increase Z above water
if (mapElement->properties.surface.terrain & 0x1F)
z = max(z, (mapElement->properties.surface.terrain & 0x1F) << 4);
return z + sub_6D01B3(3, 0, x, y, z);
}
static void window_track_place_attempt_placement(int x, int y, int z, int bl, money32 *cost, uint8 *rideIndex)
{
int eax, ebx, ecx, edx, esi, edi, ebp;
money32 result;
edx = esi = ebp = 0;
eax = x;
ebx = bl;
ecx = y;
edi = z;
result = game_do_command_p(GAME_COMMAND_PLACE_TRACK_DESIGN, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
if (cost != NULL) *cost = result;
if (rideIndex != NULL) *rideIndex = edi & 0xFF;
}
/**
*
* rct2: 0x006CFCA0
*/
void window_track_place_open()
void window_track_place_open(const track_design_file_ref *tdFileRef)
{
rct_window *w;
rct_track_td6 *td6 = track_design_open(tdFileRef->path);
if (td6 == NULL) {
return;
}
window_close_construction_windows();
_window_track_place_mini_preview = malloc(TRACK_MINI_PREVIEW_SIZE);
window_track_place_clear_mini_preview();
w = window_create(
rct_window *w = window_create(
0,
29,
200,
@ -392,7 +167,12 @@ void window_track_place_open()
_window_track_place_last_cost = MONEY32_UNDEFINED;
_window_track_place_last_x = 0xFFFF;
_currentTrackPieceDirection = (2 - get_current_rotation()) & 3;
window_track_place_draw_mini_preview();
window_track_place_draw_mini_preview(td6);
char *title = (char*)language_get_string(3155);
format_string(title, STR_TRACK_LIST_NAME_FORMAT, &td6->name);
_trackDesign = td6;
}
/**
@ -406,7 +186,9 @@ static void window_track_place_close(rct_window *w)
map_invalidate_map_selection_tiles();
RCT2_GLOBAL(RCT2_ADDRESS_MAP_SELECTION_FLAGS, uint16) &= ~6;
hide_gridlines();
free(_window_track_place_mini_preview);
SafeFree(_window_track_place_mini_preview);
track_design_dispose(_trackDesign);
_trackDesign = NULL;
}
/**
@ -424,14 +206,14 @@ static void window_track_place_mouseup(rct_window *w, int widgetIndex)
_currentTrackPieceDirection = (_currentTrackPieceDirection + 1) & 3;
window_invalidate(w);
_window_track_place_last_x = 0xFFFF;
window_track_place_draw_mini_preview();
window_track_place_draw_mini_preview(_trackDesign);
break;
case WIDX_MIRROR:
track_mirror();
track_design_mirror(_trackDesign);
_currentTrackPieceDirection = (0 - _currentTrackPieceDirection) & 3;
window_invalidate(w);
_window_track_place_last_x = 0xFFFF;
window_track_place_draw_mini_preview();
window_track_place_draw_mini_preview(_trackDesign);
break;
case WIDX_SELECT_DIFFERENT_DESIGN:
window_close(w);
@ -474,7 +256,7 @@ static void window_track_place_toolupdate(rct_window* w, int widgetIndex, int x,
// Check if tool map position has changed since last update
if (mapX == _window_track_place_last_x && mapY == _window_track_place_last_y) {
sub_6D01B3(0, 0, mapX, mapY, 0);
sub_6D01B3(_trackDesign, PTD_OPERATION_DRAW_OUTLINES, 0, mapX, mapY, 0);
return;
}
@ -487,13 +269,13 @@ static void window_track_place_toolupdate(rct_window* w, int widgetIndex, int x,
// Try increasing Z until a feasible placement is found
for (i = 0; i < 7; i++) {
window_track_place_attempt_placement(mapX, mapY, mapZ, 105, &cost, &rideIndex);
window_track_place_attempt_placement(_trackDesign, mapX, mapY, mapZ, 105, &cost, &rideIndex);
if (cost != MONEY32_UNDEFINED) {
RCT2_GLOBAL(0x00F440EB, uint16) = rideIndex;
_window_track_place_ride_index = rideIndex;
_window_track_place_last_valid_x = mapX;
_window_track_place_last_valid_y = mapY;
_window_track_place_last_valid_z = mapZ;
_window_track_place_last_was_valid = 1;
_window_track_place_last_was_valid = true;
break;
}
mapZ += 8;
@ -507,7 +289,7 @@ static void window_track_place_toolupdate(rct_window* w, int widgetIndex, int x,
widget_invalidate(w, WIDX_PRICE);
}
sub_6D01B3(0, 0, mapX, mapY, mapZ);
sub_6D01B3(_trackDesign, PTD_OPERATION_DRAW_OUTLINES, 0, mapX, mapY, mapZ);
}
/**
@ -533,7 +315,7 @@ static void window_track_place_tooldown(rct_window* w, int widgetIndex, int x, i
mapZ = window_track_place_get_base_z(mapX, mapY);
for (i = 0; i < 7; i++) {
RCT2_GLOBAL(0x009A8C29, uint8) |= 1;
window_track_place_attempt_placement(mapX, mapY, mapZ, 1, &cost, &rideIndex);
window_track_place_attempt_placement(_trackDesign, mapX, mapY, mapZ, 1, &cost, &rideIndex);
RCT2_GLOBAL(0x009A8C29, uint8) &= ~1;
if (cost != MONEY32_UNDEFINED) {
@ -541,7 +323,7 @@ static void window_track_place_tooldown(rct_window* w, int widgetIndex, int x, i
audio_play_sound_at_location(SOUND_PLACE_ITEM, mapX, mapY, mapZ);
_currentRideIndex = rideIndex;
if (RCT2_GLOBAL(0x00F4414E, uint8) & 1) {
if (byte_F4414E & 1) {
window_ride_main_open(rideIndex);
window_close(w);
} else {
@ -578,7 +360,7 @@ static void window_track_place_toolabort(rct_window *w, int widgetIndex)
*/
static void window_track_place_unknown14(rct_window *w)
{
window_track_place_draw_mini_preview();
window_track_place_draw_mini_preview(_trackDesign);
}
static void window_track_place_invalidate(rct_window *w)
@ -586,21 +368,85 @@ static void window_track_place_invalidate(rct_window *w)
colour_scheme_update(w);
}
/**
*
* rct2: 0x006D017F
*/
static void window_track_place_clear_provisional()
{
if (_window_track_place_last_was_valid) {
sub_6D01B3(
_trackDesign,
PTD_OPERATION_CLEAR_OUTLINES,
_window_track_place_ride_index,
_window_track_place_last_valid_x,
_window_track_place_last_valid_y,
_window_track_place_last_valid_z
);
_window_track_place_last_was_valid = false;
}
}
/**
*
* rct2: 0x006D17C6
*/
static int window_track_place_get_base_z(int x, int y)
{
rct_map_element *mapElement;
int z;
mapElement = map_get_surface_element_at(x >> 5, y >> 5);
z = mapElement->base_height * 8;
// Increase Z above slope
if (mapElement->properties.surface.slope & 0x0F) {
z += 16;
// Increase Z above double slope
if (mapElement->properties.surface.slope & 0x10)
z += 16;
}
// Increase Z above water
if (mapElement->properties.surface.terrain & 0x1F)
z = max(z, (mapElement->properties.surface.terrain & 0x1F) << 4);
return z + sub_6D01B3(_trackDesign, PTD_OPERATION_GET_PLACE_Z, 0, x, y, z);
}
static void window_track_place_attempt_placement(rct_track_td6 *td6, int x, int y, int z, int bl, money32 *cost, uint8 *rideIndex)
{
int eax, ebx, ecx, edx, esi, edi, ebp;
money32 result;
edx = esi = ebp = 0;
eax = x;
ebx = bl;
ecx = y;
edi = z;
gActiveTrackDesign = _trackDesign;
result = game_do_command_p(GAME_COMMAND_PLACE_TRACK_DESIGN, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
gActiveTrackDesign = NULL;
if (cost != NULL) *cost = result;
if (rideIndex != NULL) *rideIndex = edi & 0xFF;
}
/**
*
* rct2: 0x006CFD9D
*/
static void window_track_place_paint(rct_window *w, rct_drawpixelinfo *dpi)
{
rct_drawpixelinfo clippedDpi;
rct_g1_element tmpElement, *substituteElement;
window_draw_widgets(w, dpi);
// Draw mini tile preview
rct_drawpixelinfo clippedDpi;
if (clip_drawpixelinfo(&clippedDpi, dpi, w->x + 4, w->y + 18, 168, 78)) {
substituteElement = &g1Elements[0];
tmpElement = *substituteElement;
rct_g1_element *substituteElement = &g1Elements[0];
rct_g1_element tmpElement = *substituteElement;
substituteElement->offset = _window_track_place_mini_preview;
substituteElement->width = TRACK_MINI_PREVIEW_WIDTH;
substituteElement->height = TRACK_MINI_PREVIEW_HEIGHT;
@ -612,7 +458,151 @@ static void window_track_place_paint(rct_window *w, rct_drawpixelinfo *dpi)
}
// Price
if (_window_track_place_last_cost != MONEY32_UNDEFINED)
if (!(gParkFlags & PARK_FLAGS_NO_MONEY))
gfx_draw_string_centred(dpi, STR_COST_LABEL, w->x + 88, w->y + 94, 0, &_window_track_place_last_cost);
if (_window_track_place_last_cost != MONEY32_UNDEFINED && !(gParkFlags & PARK_FLAGS_NO_MONEY)) {
gfx_draw_string_centred(dpi, STR_COST_LABEL, w->x + 88, w->y + 94, 0, &_window_track_place_last_cost);
}
}
/**
*
* rct2: 0x006D1845
*/
static void window_track_place_draw_mini_preview(rct_track_td6 *td6)
{
window_track_place_clear_mini_preview();
// First pass is used to determine the width and height of the image so it can centre it
rct_xy16 min = { 0, 0 };
rct_xy16 max = { 0, 0 };
for (int pass = 0; pass < 2; pass++) {
rct_xy16 origin = { 0, 0 };
if (pass == 1) {
origin.x -= ((max.x + min.x) >> 6) << 5;
origin.y -= ((max.y + min.y) >> 6) << 5;
}
if (td6->type == RIDE_TYPE_MAZE) {
window_track_place_draw_mini_preview_maze(td6, pass, origin, &min, &max);
} else {
window_track_place_draw_mini_preview_track(td6, pass, origin, &min, &max);
}
}
}
static void window_track_place_draw_mini_preview_track(rct_track_td6 *td6, int pass, rct_xy16 origin, rct_xy16 *min, rct_xy16 *max)
{
uint8 rotation = (_currentTrackPieceDirection + get_current_rotation()) & 3;
rct_td6_track_element *trackElement = td6->track_elements;
while (trackElement->type != 255) {
int trackType = trackElement->type;
if (trackType == TRACK_ELEM_INVERTED_90_DEG_UP_TO_FLAT_QUARTER_LOOP) {
trackType = 255;
}
// Follow a single track piece shape
const rct_preview_track *trackBlock = TrackBlocks[trackType];
while (trackBlock->index != 255) {
sint16 x = origin.x;
sint16 y = origin.y;
map_offset_with_rotation(&x, &y, trackBlock->x, trackBlock->y, rotation);
if (pass == 0) {
min->x = min(min->x, x);
max->x = max(max->x, x);
min->y = min(min->y, y);
max->y = max(max->y, y);
} else {
rct_xy16 pixelPosition = draw_mini_preview_get_pixel_position(x, y);
if (draw_mini_preview_is_pixel_in_bounds(pixelPosition)) {
uint8 *pixel = draw_mini_preview_get_pixel_ptr(pixelPosition);
uint8 bits = trackBlock->var_08 << (rotation & 3);
bits = (bits & 0x0F) | ((bits & 0xF0) >> 4);
// Station track is a lighter colour
uint8 colour = RCT2_ADDRESS(0x0099BA64, uint8)[trackType * 16] & 0x10 ? 222 : 218;
for (int i = 0; i < 4; i++) {
if (bits & 1) pixel[338 + i] = colour;
if (bits & 2) pixel[168 + i] = colour;
if (bits & 4) pixel[ 2 + i] = colour;
if (bits & 8) pixel[172 + i] = colour;
}
}
}
trackBlock++;
}
// Change rotation and next position based on track curvature
rotation &= 3;
const rct_track_coordinates* track_coordinate = &TrackCoordinates[trackType];
trackType *= 10;
map_offset_with_rotation(&origin.x, &origin.y, track_coordinate->x, track_coordinate->y, rotation);
rotation += track_coordinate->rotation_end - track_coordinate->rotation_begin;
rotation &= 3;
if (track_coordinate->rotation_end & 4) {
rotation |= 4;
}
if (!(rotation & 4)) {
origin.x += TileDirectionDelta[rotation].x;
origin.y += TileDirectionDelta[rotation].y;
}
trackElement++;
}
}
static void window_track_place_draw_mini_preview_maze(rct_track_td6 *td6, int pass, rct_xy16 origin, rct_xy16 *min, rct_xy16 *max)
{
uint8 rotation = (_currentTrackPieceDirection + get_current_rotation()) & 3;
rct_td6_maze_element *mazeElement = td6->maze_elements;
while (mazeElement->all != 0) {
sint16 x = mazeElement->x * 32;
sint16 y = mazeElement->y * 32;
rotate_map_coordinates(&x, &y, rotation);
x += origin.x;
y += origin.y;
if (pass == 0) {
min->x = min(min->x, x);
max->x = max(max->x, x);
min->y = min(min->y, y);
max->y = max(max->y, y);
} else {
rct_xy16 pixelPosition = draw_mini_preview_get_pixel_position(x, y);
if (draw_mini_preview_is_pixel_in_bounds(pixelPosition)) {
uint8 *pixel = draw_mini_preview_get_pixel_ptr(pixelPosition);
// Entrance or exit is a lighter colour
uint8 colour = mazeElement->type == 8 || mazeElement->type == 128 ? 222 : 218;
for (int i = 0; i < 4; i++) {
pixel[338 + i] = colour;
pixel[168 + i] = colour;
pixel[ 2 + i] = colour;
pixel[172 + i] = colour;
}
}
}
mazeElement++;
}
}
static rct_xy16 draw_mini_preview_get_pixel_position(sint16 x, sint16 y)
{
return (rct_xy16) {
80 + ((y / 32) - (x / 32)) * 4,
38 + ((y / 32) + (x / 32)) * 2
};
}
static bool draw_mini_preview_is_pixel_in_bounds(rct_xy16 pixel)
{
return pixel.x >= 0 && pixel.y >= 0 && pixel.x <= 160 && pixel.y <= 75;
}
static uint8 *draw_mini_preview_get_pixel_ptr(rct_xy16 pixel)
{
return &_window_track_place_mini_preview[pixel.y * TRACK_MINI_PREVIEW_WIDTH + pixel.x];
}

View File

@ -1820,16 +1820,7 @@ money32 map_set_land_ownership(uint8 flags, sint16 x1, sint16 y1, sint16 x2, sin
y1 = clamp(0, y1, gMapSizeUnits);
x2 = min(x2, gMapSizeUnits);
y2 = min(y2, gMapSizeUnits);
for (sint16 y = y1; y <= y2; y += 32) {
for (sint16 x = x1; x <= x2; x += 32) {
if (x > gMapSizeUnits)
continue;
if (y > gMapSizeUnits)
continue;
map_buy_land_rights(x, y, x2, y2, 6, flags | (newOwnership << 8));
}
}
map_buy_land_rights(x1, y1, x2, y2, 6, flags | (newOwnership << 8));
if (!(RCT2_GLOBAL(0x9E2E28, uint8) & 1)) {
return 0;
@ -2925,8 +2916,8 @@ void game_command_place_scenery(int* eax, int* ebx, int* ecx, int* edx, int* esi
gCommandPosition.x += 16;
gCommandPosition.y += 16;
if(game_is_not_paused() || gCheatsBuildInPauseMode){
if(sub_68B044()){
if(RCT2_GLOBAL(0x009D8150, uint8) & 1 || (x <= gMapSizeMaxXY && y <= gMapSizeMaxXY)){
if (sub_68B044()) {
if ((byte_9D8150 & 1) || (x <= gMapSizeMaxXY && y <= gMapSizeMaxXY)) {
rct_scenery_entry* scenery_entry = (rct_scenery_entry*)object_entry_groups[OBJECT_TYPE_SMALL_SCENERY].chunks[scenery_type];
if(scenery_entry->small_scenery.flags & SMALL_SCENERY_FLAG_FULL_TILE || !(scenery_entry->small_scenery.flags & SMALL_SCENERY_FLAG9)){
if(scenery_entry->small_scenery.flags & (SMALL_SCENERY_FLAG9 | SMALL_SCENERY_FLAG24 | SMALL_SCENERY_FLAG25)){
@ -5494,3 +5485,25 @@ rct_map_element *map_get_track_element_at_with_direction_from_ride(int x, int y,
return NULL;
};
void map_offset_with_rotation(sint16 *x, sint16 *y, sint16 offsetX, sint16 offsetY, uint8 rotation)
{
switch (rotation & 3) {
case MAP_ELEMENT_DIRECTION_WEST:
*x += offsetX;
*y += offsetY;
break;
case MAP_ELEMENT_DIRECTION_NORTH:
*x += offsetY;
*y -= offsetX;
break;
case MAP_ELEMENT_DIRECTION_EAST:
*x -= offsetX;
*y -= offsetY;
break;
case MAP_ELEMENT_DIRECTION_SOUTH:
*x -= offsetY;
*y += offsetX;
break;
}
}

View File

@ -425,6 +425,8 @@ bool map_large_scenery_get_origin(
int *outX, int *outY, int *outZ, rct_map_element** outElement
);
void map_offset_with_rotation(sint16 *x, sint16 *y, sint16 offsetX, sint16 offsetY, uint8 rotation);
rct_map_element *map_get_track_element_at(int x, int y, int z);
rct_map_element *map_get_track_element_at_of_type(int x, int y, int z, int trackType);
rct_map_element *map_get_track_element_at_of_type_seq(int x, int y, int z, int trackType, int sequence);

View File

@ -19,6 +19,7 @@
#include "../common.h"
#include "../object.h"
#include "../world/map.h"
typedef struct {
uint32 flags; // 0x06