lucaFiorini 3 weeks ago committed by GitHub
commit 3b90d00378
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -43,6 +43,7 @@ static const char * const _subdirs[] = {
"save" PATHSEP "autosave" PATHSEP,
"scenario" PATHSEP,
"scenario" PATHSEP "heightmap" PATHSEP,
"orderlist" PATHSEP,
"gm" PATHSEP,
"data" PATHSEP,
"baseset" PATHSEP,
@ -1100,7 +1101,7 @@ void DeterminePaths(const char *exe, bool only_local_path)
DEBUG(misc, 1, "%s found as personal directory", _personal_dir.c_str());
static const Subdirectory default_subdirs[] = {
SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR, SOCIAL_INTEGRATION_DIR
SAVE_DIR, AUTOSAVE_DIR, SCENARIO_DIR, HEIGHTMAP_DIR, ORDERLIST_DIR, BASESET_DIR, NEWGRF_DIR, AI_DIR, AI_LIBRARY_DIR, GAME_DIR, GAME_LIBRARY_DIR, SCREENSHOT_DIR, SOCIAL_INTEGRATION_DIR
};
for (uint i = 0; i < lengthof(default_subdirs); i++) {

@ -18,6 +18,7 @@ enum AbstractFileType {
FT_SAVEGAME, ///< old or new savegame
FT_SCENARIO, ///< old or new scenario
FT_HEIGHTMAP, ///< heightmap file
FT_ORDERLIST, ///< orderlist file
FT_INVALID = 7, ///< Invalid or unknown file type.
FT_NUMBITS = 3, ///< Number of bits required for storing a #AbstractFileType value.
@ -34,6 +35,9 @@ enum DetailedFileType {
DFT_HEIGHTMAP_BMP, ///< BMP file.
DFT_HEIGHTMAP_PNG, ///< PNG file.
/* Orderlist files */
DFT_ORDERLIST, ///< JSON file.
/* fios 'files' */
DFT_FIOS_DRIVE, ///< A drive (letter) entry.
DFT_FIOS_PARENT, ///< A parent directory entry.
@ -76,6 +80,7 @@ enum FiosType {
FIOS_TYPE_OLD_SCENARIO = MAKE_FIOS_TYPE(FT_SCENARIO, DFT_OLD_GAME_FILE),
FIOS_TYPE_PNG = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_PNG),
FIOS_TYPE_BMP = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_BMP),
FIOS_TYPE_ORDERLIST = MAKE_FIOS_TYPE(FT_ORDERLIST, DFT_ORDERLIST),
FIOS_TYPE_INVALID = MAKE_FIOS_TYPE(FT_INVALID, DFT_INVALID),
};
@ -111,6 +116,7 @@ enum Subdirectory {
AUTOSAVE_DIR, ///< Subdirectory of save for autosaves
SCENARIO_DIR, ///< Base directory for all scenarios
HEIGHTMAP_DIR, ///< Subdirectory of scenario for heightmaps
ORDERLIST_DIR, ///< Subdirectort for all orderlists
OLD_GM_DIR, ///< Old subdirectory for the music
OLD_DATA_DIR, ///< Old subdirectory for the data.
BASESET_DIR, ///< Subdirectory for all base data (base sets, intro game)

@ -91,6 +91,10 @@ void FileList::BuildFileList(AbstractFileType abstract_filetype, SaveLoadOperati
FiosGetHeightmapList(fop, show_dirs, *this);
break;
case FT_ORDERLIST:
FiosGetOrderlistList(fop, show_dirs, *this);
break;
default:
NOT_REACHED();
}
@ -185,6 +189,7 @@ bool FiosBrowseTo(const FiosItem *item)
case FIOS_TYPE_OLDFILE:
case FIOS_TYPE_SCENARIO:
case FIOS_TYPE_OLD_SCENARIO:
case FIOS_TYPE_ORDERLIST:
case FIOS_TYPE_PNG:
case FIOS_TYPE_BMP:
return false;
@ -224,6 +229,11 @@ static std::string FiosMakeFilename(const std::string *path, const char *name, c
* @param last Last element of buffer \a buf.
* @return The completed filename.
*/
std::string FiosMakeOrderListName(const char *name)
{
return FiosMakeFilename(_fios_path, name, ".json");
}
std::string FiosMakeSavegameName(const char *name)
{
const char *extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
@ -249,9 +259,23 @@ std::string FiosMakeHeightmapName(const char *name)
* @param name Filename to delete.
* @return Whether the file deletion was successful.
*/
bool FiosDelete(const char *name)
bool FiosDelete(const char *name,AbstractFileType ft)
{
std::string filename = FiosMakeSavegameName(name);
std::string filename;
switch (ft) {
case FT_SAVEGAME:
case FT_SCENARIO:
filename = FiosMakeSavegameName(name);
break;
case FT_ORDERLIST:
filename = FiosMakeOrderListName(name);
break;
default:
NOT_REACHED();
break;
}
return unlink(filename.c_str()) == 0;
}
@ -499,6 +523,52 @@ void FiosGetSavegameList(SaveLoadOperation fop, bool show_dirs, FileList &file_l
FiosGetFileList(fop, show_dirs, &FiosGetSavegameListCallback, NO_DIRECTORY, file_list);
}
/**
* Callback for FiosGetOrderlistList. It tells if a file is a orederlist or not.
* @param fop Purpose of collecting the list.
* @param file Name of the file to check.
* @param ext A pointer to the extension identifier inside file
* @param title Buffer if a callback wants to lookup the title of the file; nullptr to skip the lookup
* @param last Last available byte in buffer (to prevent buffer overflows); not used when title == nullptr
* @return a FIOS_TYPE_* type of the found file, FIOS_TYPE_INVALID if not a savegame
* @see FiosGetFileList
* @see FiosGetOrderlistList
*/
FiosType FiosGetOrderlistListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last)
{
/* Show orderlist files
* .json orderlist files
*/
/* Don't crash if we supply no extension */
if (ext == nullptr) ext = "";
if (StrEqualsIgnoreCase(ext, ".json")) {
GetFileTitle(file, title, last, ORDERLIST_DIR);
return FIOS_TYPE_ORDERLIST;
}
return FIOS_TYPE_INVALID;
}
/**
* Get a list of orderlists.
* @param fop Purpose of collecting the list.
* @param show_dirs Whether to show directories.
* @param file_list Destination of the found files.
* @see FiosGetFileList
*/
void FiosGetOrderlistList(SaveLoadOperation fop, bool show_dirs, FileList &file_list)
{
static std::optional<std::string> fios_save_path;
if (!fios_save_path) fios_save_path = FioFindDirectory(ORDERLIST_DIR);
_fios_path = &(*fios_save_path);
FiosGetFileList(fop, show_dirs, &FiosGetOrderlistListCallback, NO_DIRECTORY, file_list);
}
/**
* Callback for FiosGetFileList. It tells if a file is a scenario or not.
* @param fop Purpose of collecting the list.

@ -15,6 +15,7 @@
#include "newgrf_config.h"
#include "network/core/tcp_content_type.h"
#include <vector>
#include <order_type.h>
/** Special values for save-load window for the data parameter of #InvalidateWindowData. */
@ -51,23 +52,27 @@ DECLARE_ENUM_AS_BIT_SET(SortingBits)
/* Variables to display file lists */
extern SortingBits _savegame_sort_order;
void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop);
void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop,const Vehicle * veh = nullptr);
void FiosGetSavegameList(SaveLoadOperation fop, bool show_dirs, FileList &file_list);
void FiosGetScenarioList(SaveLoadOperation fop, bool show_dirs, FileList &file_list);
void FiosGetHeightmapList(SaveLoadOperation fop, bool show_dirs, FileList &file_list);
void FiosGetOrderlistList(SaveLoadOperation fop, bool show_dirs, FileList &file_list);
bool FiosBrowseTo(const FiosItem *item);
std::string FiosGetCurrentPath();
std::optional<uint64_t> FiosGetDiskFreeSpace(const std::string &path);
bool FiosDelete(const char *name);
bool FiosDelete(const char *name, AbstractFileType file_type);
std::string FiosMakeHeightmapName(const char *name);
std::string FiosMakeSavegameName(const char *name);
std::string FiosMakeOrderListName(const char *name);
FiosType FiosGetSavegameListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last);
FiosType FiosGetScenarioListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last);
FiosType FiosGetHeightmapListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last);
FiosType FiosGetOrderlistListCallback(SaveLoadOperation fop, const std::string &file, const char *ext, char *title, const char *last);
void ScanScenarios();
const char *FindScenario(const ContentInfo *ci, bool md5sum);

@ -29,6 +29,11 @@
#include "gamelog.h"
#include "stringfilter_type.h"
#include "gamelog.h"
#include "vehicle_base.h"
#include <string>
#include <sstream>
#include <fstream>
#include <streambuf>
#include "widgets/fios_widget.h"
@ -234,6 +239,101 @@ static constexpr NWidgetPart _nested_save_dialog_widgets[] = {
EndContainer(),
};
/** Save Orderlist */
static constexpr NWidgetPart _nested_save_orderlist_dialog_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_SL_CAPTION),
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
EndContainer(),
/* Current directory and free space */
NWidget(WWT_PANEL, COLOUR_GREY, WID_SL_BACKGROUND), SetFill(1, 0), SetResize(1, 0), EndContainer(),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
/* Left side : filter box and available files */
NWidget(NWID_VERTICAL),
/* Filter box with label */
NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 1),
NWidget(NWID_HORIZONTAL), SetPadding(WidgetDimensions::unscaled.framerect.top, 0, WidgetDimensions::unscaled.framerect.bottom, 0),
SetPIP(WidgetDimensions::unscaled.frametext.left, WidgetDimensions::unscaled.frametext.right, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetFill(0, 1), SetDataTip(STR_SAVELOAD_FILTER_TITLE , STR_NULL),
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_SL_FILTER), SetFill(1, 0), SetMinimalSize(50, 12), SetResize(1, 0),
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
EndContainer(),
EndContainer(),
/* Sort buttons */
NWidget(NWID_HORIZONTAL),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYNAME), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYDATE), SetDataTip(STR_SORT_BY_CAPTION_DATE, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
EndContainer(),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetMinimalSize(12, 12), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
EndContainer(),
/* Files */
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PANEL, COLOUR_GREY, WID_SL_FILE_BACKGROUND),
NWidget(WWT_INSET, COLOUR_GREY, WID_SL_DRIVES_DIRECTORIES_LIST), SetPadding(2, 2, 2, 2),
SetDataTip(0x0, STR_SAVELOAD_LIST_TOOLTIP), SetResize(1, 10), SetScrollbar(WID_SL_SCROLLBAR), EndContainer(),
EndContainer(),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SL_SCROLLBAR),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY),
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_SL_SAVE_OSK_TITLE), SetPadding(2, 2, 2, 2), SetFill(1, 0), SetResize(1, 0),
SetDataTip(STR_SAVELOAD_OSKTITLE, STR_SAVELOAD_EDITBOX_TOOLTIP),
EndContainer(),
/* Save button*/
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_DELETE_SELECTION), SetDataTip(STR_SAVELOAD_DELETE_BUTTON, STR_SAVELOAD_DELETE_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SAVE_GAME), SetDataTip(STR_SAVELOAD_SAVE_BUTTON, STR_SAVELOAD_SAVE_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
EndContainer(),
EndContainer(),
EndContainer(),
};
/** Load Orderlist */
static constexpr NWidgetPart _nested_load_orderlist_dialog_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_SL_CAPTION),
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
EndContainer(),
/* Current directory and free space */
NWidget(WWT_PANEL, COLOUR_GREY, WID_SL_BACKGROUND), SetFill(1, 0), SetResize(1, 0), EndContainer(),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
/* Left side : filter box and available files */
NWidget(NWID_VERTICAL),
/* Filter box with label */
NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1), SetResize(1, 1),
NWidget(NWID_HORIZONTAL), SetPadding(WidgetDimensions::unscaled.framerect.top, 0, WidgetDimensions::unscaled.framerect.bottom, 0),
SetPIP(WidgetDimensions::unscaled.frametext.left, WidgetDimensions::unscaled.frametext.right, 0),
NWidget(WWT_TEXT, COLOUR_GREY), SetFill(0, 1), SetDataTip(STR_SAVELOAD_FILTER_TITLE , STR_NULL),
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_SL_FILTER), SetFill(1, 0), SetMinimalSize(50, 12), SetResize(1, 0),
SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
EndContainer(),
EndContainer(),
/* Sort buttons */
NWidget(NWID_HORIZONTAL),
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYNAME), SetDataTip(STR_SORT_BY_CAPTION_NAME, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_SORT_BYDATE), SetDataTip(STR_SORT_BY_CAPTION_DATE, STR_TOOLTIP_SORT_ORDER), SetFill(1, 0), SetResize(1, 0),
EndContainer(),
NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SL_HOME_BUTTON), SetMinimalSize(12, 12), SetDataTip(SPR_HOUSE_ICON, STR_SAVELOAD_HOME_BUTTON),
EndContainer(),
/* Files */
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PANEL, COLOUR_GREY, WID_SL_FILE_BACKGROUND),
NWidget(WWT_INSET, COLOUR_GREY, WID_SL_DRIVES_DIRECTORIES_LIST), SetFill(1, 1), SetPadding(2, 2, 2, 2),
SetDataTip(0x0, STR_SAVELOAD_LIST_TOOLTIP), SetResize(1, 10), SetScrollbar(WID_SL_SCROLLBAR), EndContainer(),
EndContainer(),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SL_SCROLLBAR),
EndContainer(),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SL_LOAD_BUTTON), SetDataTip(STR_SAVELOAD_LOAD_BUTTON, STR_SAVELOAD_LOAD_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
EndContainer(),
EndContainer(),
};
/** Text colours of #DetailedFileType fios entries in the window. */
static const TextColour _fios_colours[] = {
TC_LIGHT_BROWN, // DFT_OLD_GAME_FILE
@ -278,6 +378,7 @@ struct SaveLoadWindow : public Window {
private:
static const uint EDITBOX_MAX_SIZE = 50;
const Vehicle* veh;
QueryString filename_editbox; ///< Filename editbox.
AbstractFileType abstract_filetype; /// Type of file to select.
SaveLoadOperation fop; ///< File operation to perform.
@ -311,11 +412,13 @@ public:
this->filename_editbox.text.Assign(GenerateDefaultSaveName());
}
SaveLoadWindow(WindowDesc *desc, AbstractFileType abstract_filetype, SaveLoadOperation fop)
SaveLoadWindow(WindowDesc *desc, AbstractFileType abstract_filetype, SaveLoadOperation fop,const Vehicle * veh = nullptr)
: Window(desc), filename_editbox(64), abstract_filetype(abstract_filetype), fop(fop), filter_editbox(EDITBOX_MAX_SIZE)
{
assert(this->fop == SLO_SAVE || this->fop == SLO_LOAD);
this->veh = veh;
/* For saving, construct an initial file name. */
if (this->fop == SLO_SAVE) {
switch (this->abstract_filetype) {
@ -325,6 +428,7 @@ public:
case FT_SCENARIO:
case FT_HEIGHTMAP:
case FT_ORDERLIST:
this->filename_editbox.text.Assign("UNNAMED");
break;
@ -355,6 +459,10 @@ public:
caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_HEIGHTMAP : STR_SAVELOAD_LOAD_HEIGHTMAP;
break;
case FT_ORDERLIST:
caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_ORDERLIST : STR_SAVELOAD_LOAD_ORDERLIST;
break;
default:
NOT_REACHED();
}
@ -367,9 +475,9 @@ public:
this->querystrings[WID_SL_FILTER] = &this->filter_editbox;
this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR;
/* pause is only used in single-player, non-editor mode, non-menu mode. It
/* pause is only used in single-player, non-editor mode, non-menu mode, when not operationg on orderlists. It
* will be unpaused in the WE_DESTROY event handler. */
if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR) {
if (_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR && this->abstract_filetype != FT_ORDERLIST) {
DoCommandP(0, PM_PAUSED_SAVELOAD, 1, CMD_PAUSE);
}
SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0);
@ -393,6 +501,10 @@ public:
o_dir.name = FioFindDirectory(HEIGHTMAP_DIR);
break;
case FT_ORDERLIST:
o_dir.name = FioFindDirectory(ORDERLIST_DIR);
break;
default:
o_dir.name = _personal_dir;
}
@ -643,12 +755,26 @@ public:
case WID_SL_LOAD_BUTTON: {
if (this->selected == nullptr || _load_check_data.HasErrors()) break;
_file_to_saveload.Set(*this->selected);
if (this->abstract_filetype == FT_HEIGHTMAP) {
this->Close();
ShowHeightmapLoad();
}else if (this->abstract_filetype == FT_ORDERLIST) {
FILE * file = FioFOpenFile(this->selected->name, "r", NO_DIRECTORY);
if (file != nullptr) {
std::ifstream t(file);
std::stringstream buffer;
buffer << t.rdbuf();
veh->orders->FromJSONString(veh, buffer.str());
this->Close();
}
} else if (!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility != GLC_NOT_FOUND || _settings_client.gui.UserIsAllowedToChangeNewGRFs()) {
_switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD_GAME;
ClearErrorMessages();
@ -672,6 +798,8 @@ public:
break;
case WID_SL_DRIVES_DIRECTORIES_LIST: { // Click the listbox
printf("test");
auto it = this->vscroll->GetScrolledItemFromWidget(this->display_list, pt.y, this, WID_SL_DRIVES_DIRECTORIES_LIST, WidgetDimensions::scaled.inset.top);
if (it == this->display_list.end()) return;
@ -685,13 +813,18 @@ public:
}
if (click_count == 1) {
if (this->selected != file) {
this->selected = file;
_load_check_data.Clear();
if (GetDetailedFileType(file->type) == DFT_GAME_FILE) {
/* Other detailed file types cannot be checked before. */
auto type = GetDetailedFileType(file->type);
if(GetDetailedFileType(file->type) == DFT_GAME_FILE) {
SaveOrLoad(file->name, SLO_CHECK, DFT_GAME_FILE, NO_DIRECTORY, false);
}
this->InvalidateData(SLIWD_SELECTION_CHANGES);
@ -704,7 +837,7 @@ public:
} else if (!_load_check_data.HasErrors()) {
this->selected = file;
if (this->fop == SLO_LOAD) {
if (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO) {
if (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO || this->abstract_filetype == FT_ORDERLIST) {
this->OnClick(pt, WID_SL_LOAD_BUTTON, 1);
} else {
assert(this->abstract_filetype == FT_HEIGHTMAP);
@ -737,6 +870,18 @@ public:
case WID_SL_SAVE_GAME: // Save game
/* Note, this is also called via the OSK; and we need to lower the button. */
this->HandleButtonClick(WID_SL_SAVE_GAME);
if (this->abstract_filetype == FT_ORDERLIST) {
std::string fileName = FiosMakeOrderListName(this->filename_editbox.text.buf);
std::ofstream output;
output.open(fileName);
output << this->veh->orders->ToJSONString();
output.close();
this->Close();
}
break;
}
}
@ -776,7 +921,7 @@ public:
if (this->fop != SLO_SAVE) return;
if (this->IsWidgetLowered(WID_SL_DELETE_SELECTION)) { // Delete button clicked
if (!FiosDelete(this->filename_editbox.text.buf)) {
if (!FiosDelete(this->filename_editbox.text.buf, this->abstract_filetype)) {
ShowErrorMessage(STR_ERROR_UNABLE_TO_DELETE_FILE, INVALID_STRING_ID, WL_ERROR);
} else {
this->InvalidateData(SLIWD_RESCAN_FILES);
@ -911,9 +1056,8 @@ public:
!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility == GLC_ALL_GOOD);
break;
}
default:
NOT_REACHED();
default: break;
}
break;
@ -948,6 +1092,14 @@ static WindowDesc _load_heightmap_dialog_desc(__FILE__, __LINE__,
std::begin(_nested_load_heightmap_dialog_widgets), std::end(_nested_load_heightmap_dialog_widgets)
);
/** Load orderlist*/
static WindowDesc _load_orderlist_dialog_desc(__FILE__, __LINE__,
WDP_CENTER, "load_orderlist", 257, 320,
WC_SAVELOAD, WC_NONE,
0,
std::begin(_nested_load_orderlist_dialog_widgets), std::end(_nested_load_orderlist_dialog_widgets)
);
/** Save game/scenario */
static WindowDesc _save_dialog_desc(__FILE__, __LINE__,
WDP_CENTER, "save_game", 500, 294,
@ -956,22 +1108,38 @@ static WindowDesc _save_dialog_desc(__FILE__, __LINE__,
std::begin(_nested_save_dialog_widgets), std::end(_nested_save_dialog_widgets)
);
/** Load orderlist*/
static WindowDesc _save_orderlist_dialog_desc(__FILE__, __LINE__,
WDP_CENTER, "save_orderlist", 257, 320,
WC_SAVELOAD, WC_NONE,
0,
std::begin(_nested_save_orderlist_dialog_widgets), std::end(_nested_save_orderlist_dialog_widgets)
);
/**
* Launch save/load dialog in the given mode.
* @param abstract_filetype Kind of file to handle.
* @param fop File operation to perform (load or save).
*/
void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop)
void ShowSaveLoadDialog(AbstractFileType abstract_filetype, SaveLoadOperation fop,const Vehicle * veh)
{
CloseWindowById(WC_SAVELOAD, 0);
WindowDesc *sld;
if (fop == SLO_SAVE) {
if (abstract_filetype == FT_ORDERLIST){
if (fop == SLO_SAVE) {
sld = &_save_orderlist_dialog_desc;
} else if (fop == SLO_LOAD) {
sld = &_load_orderlist_dialog_desc;
}
} else if (fop == SLO_SAVE) {
sld = &_save_dialog_desc;
} else {
/* Dialogue for loading a file. */
sld = (abstract_filetype == FT_HEIGHTMAP) ? &_load_heightmap_dialog_desc : &_load_dialog_desc;
}
new SaveLoadWindow(sld, abstract_filetype, fop);
new SaveLoadWindow(sld, abstract_filetype, fop, veh);
}

@ -3257,8 +3257,10 @@ STR_SAVELOAD_SAVE_CAPTION :{WHITE}Save Gam
STR_SAVELOAD_LOAD_CAPTION :{WHITE}Load Game
STR_SAVELOAD_SAVE_SCENARIO :{WHITE}Save Scenario
STR_SAVELOAD_LOAD_SCENARIO :{WHITE}Load Scenario
STR_SAVELOAD_LOAD_HEIGHTMAP :{WHITE}Load Heightmap
STR_SAVELOAD_SAVE_HEIGHTMAP :{WHITE}Save Heightmap
STR_SAVELOAD_LOAD_HEIGHTMAP :{WHITE}Load Heightmap
STR_SAVELOAD_SAVE_ORDERLIST :{WHITE}Save Orderlist
STR_SAVELOAD_LOAD_ORDERLIST :{WHITE}Load Orderlist
STR_SAVELOAD_HOME_BUTTON :{BLACK}Click here to jump to the current default save/load directory
STR_SAVELOAD_BYTES_FREE :{BLACK}{BYTES} free
STR_SAVELOAD_LIST_TOOLTIP :{BLACK}List of drives, directories and saved-game files
@ -5854,3 +5856,8 @@ STR_PLANE :{BLACK}{PLANE}
STR_SHIP :{BLACK}{SHIP}
STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY})
#json messages
STR_ERROR_ORDERLIST_MALFORMED_JSON :{WHITE}Input JSON was malformed
STR_ERROR_JON :{WHITE}JSON error

@ -1687,6 +1687,8 @@ STR_ORDER_REVERSE_ORDER_LIST :Reverse order l
STR_ORDER_APPEND_REVERSED_ORDER_LIST :Append reversed order list
STR_ORDER_DUPLICATE_ORDER :Duplicate order
STR_ORDER_CHANGE_JUMP_TARGET :Change jump target
STR_ORDER_EXPORT_ORDER_LIST :Export orderlist
STR_ORDER_IMPORT_ORDER_LIST :Import orderlist
STR_ORDER_TRY_ACQUIRE_SLOT_BUTTON :Try acquire slot
STR_ORDER_RELEASE_SLOT_BUTTON :Release slot

@ -233,6 +233,9 @@ public:
void MakeChangeCounter();
void MakeLabel(OrderLabelSubType subtype);
std::string ToJSONString() const;
static Order FromJSONString(std::string jsonSTR);
/**
* Is this a 'goto' order with a real destination?
* @return True if the type is either #OT_GOTO_WAYPOINT, #OT_GOTO_DEPOT or #OT_GOTO_STATION.
@ -780,6 +783,7 @@ public:
inline std::vector<DispatchSlot> &GetScheduledDispatchMutable() { return this->scheduled_dispatch; }
void SetScheduledDispatch(std::vector<DispatchSlot> dispatch_list);
void AddScheduledDispatch(uint32_t offset);
void RemoveScheduledDispatch(uint32_t offset);
void AdjustScheduledDispatch(int32_t adjust);
@ -856,6 +860,19 @@ public:
*/
inline int32_t GetScheduledDispatchDelay() const { return this->scheduled_dispatch_max_delay; }
/**
* Get the scheduled dispatch flags
* @return flags
*/
inline int8_t GetScheduledDispatchFlags() const {return this->scheduled_dispatch_flags; }
/**
* Set the scheduled disaptch flags
* @param flags
*/
inline void SetScheduledDispatchFlags(int8_t flags) { this->scheduled_dispatch_flags = flags; }
inline void BorrowSchedule(DispatchSchedule &other)
{
this->CopyBasicFields(other);
@ -869,12 +886,16 @@ public:
inline std::string &ScheduleName() { return this->name; }
inline const std::string &ScheduleName() const { return this->name; }
static DispatchSchedule FromJSONString(std::string jsonString);
std::string ToJSONString();
};
/**
* Shared order list linking together the linked list of orders and the list
* of vehicles sharing this order list.
*/
struct OrderList : OrderListPool::PoolItem<&_orderlist_pool> {
private:
friend void AfterLoadVehicles(bool part_of_load); ///< For instantiating the shared vehicle chain
@ -959,6 +980,9 @@ public:
void DeleteOrderAt(int index);
void MoveOrder(int from, int to);
std::string ToJSONString();
static void FromJSONString(const Vehicle* v,std::string str);
/**
* Is this a shared order list?
* @return whether this order list is shared among multiple vehicles
@ -1025,7 +1049,7 @@ public:
void DebugCheckSanity() const;
#endif
bool CheckOrderListIndexing() const;
inline std::vector<DispatchSchedule> &GetScheduledDispatchScheduleSet() { return this->dispatch_schedules; }
inline const std::vector<DispatchSchedule> &GetScheduledDispatchScheduleSet() const { return this->dispatch_schedules; }

@ -26,6 +26,7 @@
#include "core/container_func.hpp"
#include "core/pool_func.hpp"
#include "core/random_func.hpp"
#include "core/serialisation.hpp"
#include "aircraft.h"
#include "roadveh.h"
#include "station_base.h"
@ -40,12 +41,16 @@
#include "tracerestrict.h"
#include "train.h"
#include "date_func.h"
#include "3rdparty/nlohmann/json.hpp"
#include "command_aux.h"
#include "rev.h"
#include "table/strings.h"
#include "3rdparty/robin_hood/robin_hood.h"
#include <3rdparty/nlohmann/json.hpp>
#include "safeguards.h"
#include <error.h>
/* DestinationID must be at least as large as every these below, because it can
* be any of them
@ -269,6 +274,100 @@ void Order::MakeLabel(OrderLabelSubType subtype)
this->flags = subtype;
}
std::string Order::ToJSONString() const
{
std::string out;
nlohmann::json json;
json["packed-data"] = this->Pack();
json["destination-id"] = this->GetDestination();
Station * station = Station::GetIfValid(this->GetDestination());
if(station != nullptr)
json["destination-name"] = station->cached_name;
if(this->extra.get() != nullptr){
auto& extraJson = json["extra"];
extraJson["cargo-type-flags"] = this->extra.get()->cargo_type_flags;
extraJson["colour"] = this->extra.get()->colour;
extraJson["dispatch-index"] = this->extra.get()->dispatch_index;
extraJson["xdata"] = this->extra.get()->xdata;
extraJson["xdata2"] = this->extra.get()->xdata2;
extraJson["xflags"] = this->extra.get()->xflags;
}
json["refit-cargo"] = this->GetRefitCargo();
json["wait-time"] = this->GetWaitTime();
json["travel-time"] = this->GetTravelTime();
json["max-speed"] = this->GetMaxSpeed();
out = json.dump();
return out;
}
Order Order::FromJSONString(std::string jsonSTR)
{
nlohmann::json json = nlohmann::json::parse(jsonSTR);
if (!json.contains("packed-data") && json["packed_data"].is_number_integer()) {
Order errOrder;
errOrder.MakeLabel(OLST_TEXT);
errOrder.SetColour(COLOUR_RED);
errOrder.SetLabelText("JSON_ERR: JSON does not contain mandatory 'packed-data' field for this order");
return errOrder;
}
Order new_order = Order(json.at("packed-data").get<uint64_t>());
if (json.contains("destination-id") && json["destination-id"].is_number_integer())
json["destination-id"].get_to(new_order.dest);
if (json.contains("extra") && json["extra"].is_object()) {
auto &extraJson = json["extra"];
new_order.AllocExtraInfo();
if (extraJson.contains("cargo-type-flags") && extraJson["cargo-type-flags"].is_array())
for (int i = 0; i < 64; i++)
extraJson["cargo-type-flags"][i].get_to(new_order.extra->cargo_type_flags[i]);
if (extraJson.contains("colour"))
extraJson["colour"].get_to(new_order.extra->colour);
if (extraJson.contains("dispatch-index"))
extraJson["dispatch-index"].get_to(new_order.extra->dispatch_index);
if (extraJson.contains("xdata"))
extraJson["xdata"].get_to(new_order.extra->xdata);
if (extraJson.contains("xdata2"))
extraJson["xdata2"].get_to(new_order.extra->xdata2);
if (extraJson.contains("xflags"))
extraJson["xflags"].get_to(new_order.extra->xflags);
}
if (json.contains("refit-cargo"))
json["refit-cargo"].get_to(new_order.refit_cargo);
if (json.contains("wait-time"))
json["wait-time"].get_to(new_order.wait_time);
if (json.contains("travel-time"))
json["travel-time"].get_to(new_order.travel_time);
if (json.contains("max-speed"))
json["max-speed"].get_to(new_order.max_speed);
return new_order;
}
/**
* Make this depot/station order also a refit order.
* @param cargo the cargo type to change to.
@ -824,6 +923,85 @@ void OrderList::MoveOrder(int from, int to)
this->ReindexOrderList();
}
std::string OrderList::ToJSONString()
{
nlohmann::json json;
json["version"] = ORDERLIST_JSON_OUTPUT_VERSION;
json["source"] = std::string(_openttd_revision);
if (this == nullptr) { //order list not intiailised, return an empty result
json["error"] = "Orderlist was not initialised";
return json.dump();
};
auto& SD_data = this->GetScheduledDispatchScheduleSet();
auto& headJson = json["head"];
for (unsigned int i = 0; auto &SD : SD_data) {
headJson["scheduled-dispatch"][i++] = nlohmann::json::parse(SD.ToJSONString());
}
const Order* o = this->GetFirstOrder();
if (o != nullptr) {
int i = 0;
do {
json["orders"][i++] = nlohmann::json::parse(o->ToJSONString());
} while ((o = this->GetNext(o)) != this->GetFirstOrder());
}
return json.dump();
}
void OrderList::FromJSONString(const Vehicle * v,std::string json_str)
{
Order errOrder;
errOrder.MakeLabel(OLST_TEXT);
errOrder.SetColour(COLOUR_RED);
nlohmann::json json;
try {
json = nlohmann::json::parse(json_str);
} catch(nlohmann::json::parse_error e){
ShowErrorMessage(STR_ERROR_JON, STR_ERROR_ORDERLIST_MALFORMED_JSON,WL_ERROR);
return;
}
//delete all orders before setting the new orders
DoCommandP(v->tile, v->index, v->GetNumOrders(), CMD_DELETE_ORDER | CMD_MSG(STR_ERROR_CAN_T_DELETE_THIS_ORDER));
if (json.contains("orders")) {
auto &ordersJson = json["orders"];
if (ordersJson.is_array()) {
for (nlohmann::json::iterator it = ordersJson.begin(); it != ordersJson.end(); ++it) {
auto &orderJson = it.value();
OrderID new_orderID = v->GetNumOrders();
DoCommandPEx(v->tile, v->index, new_orderID, 0, CMD_INSERT_ORDER | CMD_MSG(STR_ERROR_CAN_T_INSERT_NEW_ORDER), nullptr, orderJson.dump().c_str(), nullptr, 0);
}
}
}
if (json.contains("head")){
auto &headJson = json["head"];
if (headJson.contains("scheduled-dispatch")) {
auto &SDJson = headJson["scheduled-dispatch"];
if (SDJson.is_array()) {
for (nlohmann::json::iterator it = SDJson.begin(); it != SDJson.end(); ++it) {
DoCommandPEx(0, v->index, 0, 0, CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, it.value().dump().c_str() , 0);
}
}
}
}
}
/**
* Removes the vehicle from the shared order list.
* @note This is supposed to be called when the vehicle is still in the chain
@ -1017,16 +1195,19 @@ uint GetOrderDistance(const Order *prev, const Order *cur, const Vehicle *v, int
* - p2 = (bit 0 - 15) - the selected order (if any). If the last order is given,
* the order will be inserted before that one
* @param p3 packed order to insert
* @param text unused
* @param orderJson is an optional field for an Order object encoded as JSON
* @return the cost of this operation or an error
*/
CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint32_t p2, uint64_t p3, const char *text, const CommandAuxiliaryBase *aux_data)
CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint32_t p2, uint64_t p3, const char *orderJson, const CommandAuxiliaryBase *aux_data)
{
VehicleID veh = GB(p1, 0, 20);
VehicleOrderID sel_ord = GB(p2, 0, 16);
Order new_order(p3);
Order new_order = (orderJson != nullptr) ? Order::FromJSONString(orderJson) : Order(p3);
VehicleOrderID sel_ord = GB(p2, 0, 16);
VehicleID veh = GB(p1, 0, 20);
return CmdInsertOrderIntl(flags, Vehicle::GetIfValid(veh), sel_ord, new_order, false);
CommandCost ret = CmdInsertOrderIntl(flags, Vehicle::GetIfValid(veh), sel_ord, new_order, false);
return ret;
}
/**

@ -35,6 +35,7 @@
#include "tracerestrict.h"
#include "scope.h"
#include "core/backup_type.hpp"
#include "fios.h"
#include "widgets/order_widget.h"
@ -633,6 +634,8 @@ enum OrderDropDownID {
static const StringID _order_manage_list_dropdown[] = {
STR_ORDER_REVERSE_ORDER_LIST,
STR_ORDER_APPEND_REVERSED_ORDER_LIST,
STR_ORDER_EXPORT_ORDER_LIST,
STR_ORDER_IMPORT_ORDER_LIST,
INVALID_STRING_ID
};
@ -1545,6 +1548,7 @@ private:
};
int selected_order;
VehicleID vehicle_id;
VehicleOrderID order_over; ///< Order over which another order is dragged, \c INVALID_VEH_ORDER_ID if none.
OrderPlaceObjectState goto_type;
Scrollbar *vscroll;
@ -3488,6 +3492,8 @@ public:
switch (index) {
case 0: this->OrderClick_ReverseOrderList(0); break;
case 1: this->OrderClick_ReverseOrderList(1); break;
case 2: ShowSaveLoadDialog(FT_ORDERLIST, SLO_SAVE, this->GetVehicle()); break;
case 3: ShowSaveLoadDialog(FT_ORDERLIST, SLO_LOAD, this->GetVehicle()); break;
default: NOT_REACHED();
}
break;

@ -313,6 +313,8 @@ enum CloneOptions {
CO_UNSHARE = 2
};
const uint8_t ORDERLIST_JSON_OUTPUT_VERSION = 1;
struct Order;
struct OrderList;

@ -20,6 +20,7 @@
#include "settings_type.h"
#include "schdispatch.h"
#include "vehicle_gui.h"
#include <3rdparty/nlohmann/json.hpp>
#include <algorithm>
@ -357,15 +358,15 @@ CommandCost CmdScheduledDispatchClear(TileIndex tile, DoCommandFlag flags, uint3
* @param p1 Vehicle index
* @param p2 Duration, in scaled tick
* @param p3 Start tick
* @param text unused
* @param text optional JSON string
* @return the cost of this operation or an error
*/
CommandCost CmdScheduledDispatchAddNewSchedule(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint32_t p2, uint64_t p3, const char *text, const CommandAuxiliaryBase *aux_data)
CommandCost CmdScheduledDispatchAddNewSchedule(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint32_t p2, uint64_t p3, const char *scheduleJson, const CommandAuxiliaryBase *aux_data)
{
VehicleID veh = GB(p1, 0, 20);
Vehicle *v = Vehicle::GetIfValid(veh);
if (v == nullptr || !v->IsPrimaryVehicle() || p2 == 0) return CMD_ERROR;
if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
CommandCost ret = CheckOwnership(v->owner);
if (ret.Failed()) return ret;
@ -376,10 +377,19 @@ CommandCost CmdScheduledDispatchAddNewSchedule(TileIndex tile, DoCommandFlag fla
if (flags & DC_EXEC) {
v->orders->GetScheduledDispatchScheduleSet().emplace_back();
DispatchSchedule &ds = v->orders->GetScheduledDispatchScheduleSet().back();
ds.SetScheduledDispatchDuration(p2);
ds.SetScheduledDispatchStartTick((StateTicks)p3);
ds.UpdateScheduledDispatch(nullptr);
SetTimetableWindowsDirty(v, STWDF_SCHEDULED_DISPATCH);
if (scheduleJson == nullptr) {
if (p2 == 0) return CMD_ERROR;
ds.SetScheduledDispatchDuration(p2);
ds.SetScheduledDispatchStartTick((StateTicks)p3);
ds.UpdateScheduledDispatch(nullptr);
SetTimetableWindowsDirty(v, STWDF_SCHEDULED_DISPATCH);
} else {
DispatchSchedule new_ds = DispatchSchedule::FromJSONString(scheduleJson);
ds.BorrowSchedule(new_ds);
}
}
return CommandCost();
@ -801,3 +811,56 @@ void DispatchSchedule::UpdateScheduledDispatch(const Vehicle *v)
SetTimetableWindowsDirty(v, STWDF_SCHEDULED_DISPATCH);
}
}
DispatchSchedule DispatchSchedule::FromJSONString(std::string jsonString)
{
nlohmann::json json = nlohmann::json::parse(jsonString);
DispatchSchedule new_schedule;
if (json.contains("slots")) {
auto &slotsJson = json.at("slots");
if (slotsJson.is_array()) {
for (nlohmann::json::iterator it = slotsJson.begin(); it != slotsJson.end(); ++it) {
DispatchSlot newDispatchSlot;
newDispatchSlot.offset = it.value().at("offset").template get<uint32_t>();
newDispatchSlot.flags = it.value().at("flags").template get<uint16_t>();
new_schedule.GetScheduledDispatchMutable().push_back(newDispatchSlot);
}
}
}
new_schedule.ScheduleName() = json.at("name").get<std::string>();
new_schedule.SetScheduledDispatchStartTick(json.at("start-tick").get<uint32_t>());
new_schedule.SetScheduledDispatchDuration(json.at("duration").get<uint32_t>());
new_schedule.SetScheduledDispatchDelay(json.at("max-delay").get<uint32_t>());
new_schedule.SetScheduledDispatchFlags(json.at("flags").get<uint8_t>());
return new_schedule;
}
std::string DispatchSchedule::ToJSONString()
{
nlohmann::json json;
for (unsigned int i = 0; auto & SD_slot : this->GetScheduledDispatch()) {
auto &slotsJson = json["slots"][i++];
slotsJson["offset"] = SD_slot.offset;
slotsJson["flags"] = SD_slot.flags;
}
json["name"] = this->ScheduleName();
json["start-tick"] = this->GetScheduledDispatchStartTick().value;
json["duration"] = this->GetScheduledDispatchDuration();
json["max-delay"] = this->GetScheduledDispatchDelay();
json["flags"] = this->GetScheduledDispatchFlags();
return json.dump();
}

@ -86,7 +86,7 @@
/* Define all types here, so we don't have to include the whole _type.h maze */
typedef uint BridgeType; ///< Internal name, not of any use for you.
typedef byte CargoID; ///< The ID of a cargo.
typedef uint8_t CargoID; ///< The ID of a cargo.
class CommandCost; ///< The cost of a command.
typedef uint16_t EngineID; ///< The ID of an engine.
typedef uint16_t GoalID; ///< The ID of a goal.

Loading…
Cancel
Save