lucaFiorini 2 months 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, "save" PATHSEP "autosave" PATHSEP,
"scenario" PATHSEP, "scenario" PATHSEP,
"scenario" PATHSEP "heightmap" PATHSEP, "scenario" PATHSEP "heightmap" PATHSEP,
"orderlist" PATHSEP,
"gm" PATHSEP, "gm" PATHSEP,
"data" PATHSEP, "data" PATHSEP,
"baseset" 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()); DEBUG(misc, 1, "%s found as personal directory", _personal_dir.c_str());
static const Subdirectory default_subdirs[] = { 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++) { for (uint i = 0; i < lengthof(default_subdirs); i++) {

@ -18,6 +18,7 @@ enum AbstractFileType {
FT_SAVEGAME, ///< old or new savegame FT_SAVEGAME, ///< old or new savegame
FT_SCENARIO, ///< old or new scenario FT_SCENARIO, ///< old or new scenario
FT_HEIGHTMAP, ///< heightmap file FT_HEIGHTMAP, ///< heightmap file
FT_ORDERLIST, ///< orderlist file
FT_INVALID = 7, ///< Invalid or unknown file type. FT_INVALID = 7, ///< Invalid or unknown file type.
FT_NUMBITS = 3, ///< Number of bits required for storing a #AbstractFileType value. 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_BMP, ///< BMP file.
DFT_HEIGHTMAP_PNG, ///< PNG file. DFT_HEIGHTMAP_PNG, ///< PNG file.
/* Orderlist files */
DFT_ORDERLIST, ///< JSON file.
/* fios 'files' */ /* fios 'files' */
DFT_FIOS_DRIVE, ///< A drive (letter) entry. DFT_FIOS_DRIVE, ///< A drive (letter) entry.
DFT_FIOS_PARENT, ///< A parent directory 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_OLD_SCENARIO = MAKE_FIOS_TYPE(FT_SCENARIO, DFT_OLD_GAME_FILE),
FIOS_TYPE_PNG = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_PNG), FIOS_TYPE_PNG = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_PNG),
FIOS_TYPE_BMP = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_BMP), 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), FIOS_TYPE_INVALID = MAKE_FIOS_TYPE(FT_INVALID, DFT_INVALID),
}; };
@ -111,6 +116,7 @@ enum Subdirectory {
AUTOSAVE_DIR, ///< Subdirectory of save for autosaves AUTOSAVE_DIR, ///< Subdirectory of save for autosaves
SCENARIO_DIR, ///< Base directory for all scenarios SCENARIO_DIR, ///< Base directory for all scenarios
HEIGHTMAP_DIR, ///< Subdirectory of scenario for heightmaps HEIGHTMAP_DIR, ///< Subdirectory of scenario for heightmaps
ORDERLIST_DIR, ///< Subdirectort for all orderlists
OLD_GM_DIR, ///< Old subdirectory for the music OLD_GM_DIR, ///< Old subdirectory for the music
OLD_DATA_DIR, ///< Old subdirectory for the data. OLD_DATA_DIR, ///< Old subdirectory for the data.
BASESET_DIR, ///< Subdirectory for all base data (base sets, intro game) 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); FiosGetHeightmapList(fop, show_dirs, *this);
break; break;
case FT_ORDERLIST:
FiosGetOrderlistList(fop, show_dirs, *this);
break;
default: default:
NOT_REACHED(); NOT_REACHED();
} }
@ -185,6 +189,7 @@ bool FiosBrowseTo(const FiosItem *item)
case FIOS_TYPE_OLDFILE: case FIOS_TYPE_OLDFILE:
case FIOS_TYPE_SCENARIO: case FIOS_TYPE_SCENARIO:
case FIOS_TYPE_OLD_SCENARIO: case FIOS_TYPE_OLD_SCENARIO:
case FIOS_TYPE_ORDERLIST:
case FIOS_TYPE_PNG: case FIOS_TYPE_PNG:
case FIOS_TYPE_BMP: case FIOS_TYPE_BMP:
return false; 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. * @param last Last element of buffer \a buf.
* @return The completed filename. * @return The completed filename.
*/ */
std::string FiosMakeOrderListName(const char *name)
{
return FiosMakeFilename(_fios_path, name, ".json");
}
std::string FiosMakeSavegameName(const char *name) std::string FiosMakeSavegameName(const char *name)
{ {
const char *extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav"; const char *extension = (_game_mode == GM_EDITOR) ? ".scn" : ".sav";
@ -249,9 +259,23 @@ std::string FiosMakeHeightmapName(const char *name)
* @param name Filename to delete. * @param name Filename to delete.
* @return Whether the file deletion was successful. * @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; 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); 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. * Callback for FiosGetFileList. It tells if a file is a scenario or not.
* @param fop Purpose of collecting the list. * @param fop Purpose of collecting the list.

@ -15,6 +15,7 @@
#include "newgrf_config.h" #include "newgrf_config.h"
#include "network/core/tcp_content_type.h" #include "network/core/tcp_content_type.h"
#include <vector> #include <vector>
#include <order_type.h>
/** Special values for save-load window for the data parameter of #InvalidateWindowData. */ /** 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 */ /* Variables to display file lists */
extern SortingBits _savegame_sort_order; 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 FiosGetSavegameList(SaveLoadOperation fop, bool show_dirs, FileList &file_list);
void FiosGetScenarioList(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 FiosGetHeightmapList(SaveLoadOperation fop, bool show_dirs, FileList &file_list);
void FiosGetOrderlistList(SaveLoadOperation fop, bool show_dirs, FileList &file_list);
bool FiosBrowseTo(const FiosItem *item); bool FiosBrowseTo(const FiosItem *item);
std::string FiosGetCurrentPath(); std::string FiosGetCurrentPath();
std::optional<uint64_t> FiosGetDiskFreeSpace(const std::string &path); 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 FiosMakeHeightmapName(const char *name);
std::string FiosMakeSavegameName(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 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 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 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(); void ScanScenarios();
const char *FindScenario(const ContentInfo *ci, bool md5sum); const char *FindScenario(const ContentInfo *ci, bool md5sum);

@ -29,6 +29,11 @@
#include "gamelog.h" #include "gamelog.h"
#include "stringfilter_type.h" #include "stringfilter_type.h"
#include "gamelog.h" #include "gamelog.h"
#include "vehicle_base.h"
#include <string>
#include <sstream>
#include <fstream>
#include <streambuf>
#include "widgets/fios_widget.h" #include "widgets/fios_widget.h"
@ -234,6 +239,101 @@ static constexpr NWidgetPart _nested_save_dialog_widgets[] = {
EndContainer(), 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. */ /** Text colours of #DetailedFileType fios entries in the window. */
static const TextColour _fios_colours[] = { static const TextColour _fios_colours[] = {
TC_LIGHT_BROWN, // DFT_OLD_GAME_FILE TC_LIGHT_BROWN, // DFT_OLD_GAME_FILE
@ -278,6 +378,7 @@ struct SaveLoadWindow : public Window {
private: private:
static const uint EDITBOX_MAX_SIZE = 50; static const uint EDITBOX_MAX_SIZE = 50;
const Vehicle* veh;
QueryString filename_editbox; ///< Filename editbox. QueryString filename_editbox; ///< Filename editbox.
AbstractFileType abstract_filetype; /// Type of file to select. AbstractFileType abstract_filetype; /// Type of file to select.
SaveLoadOperation fop; ///< File operation to perform. SaveLoadOperation fop; ///< File operation to perform.
@ -311,11 +412,13 @@ public:
this->filename_editbox.text.Assign(GenerateDefaultSaveName()); 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) : 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); assert(this->fop == SLO_SAVE || this->fop == SLO_LOAD);
this->veh = veh;
/* For saving, construct an initial file name. */ /* For saving, construct an initial file name. */
if (this->fop == SLO_SAVE) { if (this->fop == SLO_SAVE) {
switch (this->abstract_filetype) { switch (this->abstract_filetype) {
@ -325,6 +428,7 @@ public:
case FT_SCENARIO: case FT_SCENARIO:
case FT_HEIGHTMAP: case FT_HEIGHTMAP:
case FT_ORDERLIST:
this->filename_editbox.text.Assign("UNNAMED"); this->filename_editbox.text.Assign("UNNAMED");
break; break;
@ -355,6 +459,10 @@ public:
caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_HEIGHTMAP : STR_SAVELOAD_LOAD_HEIGHTMAP; caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_HEIGHTMAP : STR_SAVELOAD_LOAD_HEIGHTMAP;
break; break;
case FT_ORDERLIST:
caption_string = (this->fop == SLO_SAVE) ? STR_SAVELOAD_SAVE_ORDERLIST : STR_SAVELOAD_LOAD_ORDERLIST;
break;
default: default:
NOT_REACHED(); NOT_REACHED();
} }
@ -367,9 +475,9 @@ public:
this->querystrings[WID_SL_FILTER] = &this->filter_editbox; this->querystrings[WID_SL_FILTER] = &this->filter_editbox;
this->filter_editbox.cancel_button = QueryString::ACTION_CLEAR; 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. */ * 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); DoCommandP(0, PM_PAUSED_SAVELOAD, 1, CMD_PAUSE);
} }
SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0); SetObjectToPlace(SPR_CURSOR_ZZZ, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0);
@ -393,6 +501,10 @@ public:
o_dir.name = FioFindDirectory(HEIGHTMAP_DIR); o_dir.name = FioFindDirectory(HEIGHTMAP_DIR);
break; break;
case FT_ORDERLIST:
o_dir.name = FioFindDirectory(ORDERLIST_DIR);
break;
default: default:
o_dir.name = _personal_dir; o_dir.name = _personal_dir;
} }
@ -643,12 +755,26 @@ public:
case WID_SL_LOAD_BUTTON: { case WID_SL_LOAD_BUTTON: {
if (this->selected == nullptr || _load_check_data.HasErrors()) break; if (this->selected == nullptr || _load_check_data.HasErrors()) break;
_file_to_saveload.Set(*this->selected); _file_to_saveload.Set(*this->selected);
if (this->abstract_filetype == FT_HEIGHTMAP) { if (this->abstract_filetype == FT_HEIGHTMAP) {
this->Close(); this->Close();
ShowHeightmapLoad(); 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()) { } 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; _switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD_GAME;
ClearErrorMessages(); ClearErrorMessages();
@ -672,6 +798,8 @@ public:
break; break;
case WID_SL_DRIVES_DIRECTORIES_LIST: { // Click the listbox 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); 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; if (it == this->display_list.end()) return;
@ -685,13 +813,18 @@ public:
} }
if (click_count == 1) { if (click_count == 1) {
if (this->selected != file) { if (this->selected != file) {
this->selected = file; this->selected = file;
_load_check_data.Clear(); _load_check_data.Clear();
if (GetDetailedFileType(file->type) == DFT_GAME_FILE) { auto type = GetDetailedFileType(file->type);
/* Other detailed file types cannot be checked before. */
if(GetDetailedFileType(file->type) == DFT_GAME_FILE) {
SaveOrLoad(file->name, SLO_CHECK, DFT_GAME_FILE, NO_DIRECTORY, false); SaveOrLoad(file->name, SLO_CHECK, DFT_GAME_FILE, NO_DIRECTORY, false);
} }
this->InvalidateData(SLIWD_SELECTION_CHANGES); this->InvalidateData(SLIWD_SELECTION_CHANGES);
@ -704,7 +837,7 @@ public:
} else if (!_load_check_data.HasErrors()) { } else if (!_load_check_data.HasErrors()) {
this->selected = file; this->selected = file;
if (this->fop == SLO_LOAD) { 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); this->OnClick(pt, WID_SL_LOAD_BUTTON, 1);
} else { } else {
assert(this->abstract_filetype == FT_HEIGHTMAP); assert(this->abstract_filetype == FT_HEIGHTMAP);
@ -737,6 +870,18 @@ public:
case WID_SL_SAVE_GAME: // Save game case WID_SL_SAVE_GAME: // Save game
/* Note, this is also called via the OSK; and we need to lower the button. */ /* Note, this is also called via the OSK; and we need to lower the button. */
this->HandleButtonClick(WID_SL_SAVE_GAME); 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; break;
} }
} }
@ -776,7 +921,7 @@ public:
if (this->fop != SLO_SAVE) return; if (this->fop != SLO_SAVE) return;
if (this->IsWidgetLowered(WID_SL_DELETE_SELECTION)) { // Delete button clicked 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); ShowErrorMessage(STR_ERROR_UNABLE_TO_DELETE_FILE, INVALID_STRING_ID, WL_ERROR);
} else { } else {
this->InvalidateData(SLIWD_RESCAN_FILES); this->InvalidateData(SLIWD_RESCAN_FILES);
@ -911,9 +1056,8 @@ public:
!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility == GLC_ALL_GOOD); !_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility == GLC_ALL_GOOD);
break; break;
} }
default: default: break;
NOT_REACHED();
} }
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) 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 */ /** Save game/scenario */
static WindowDesc _save_dialog_desc(__FILE__, __LINE__, static WindowDesc _save_dialog_desc(__FILE__, __LINE__,
WDP_CENTER, "save_game", 500, 294, 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) 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. * Launch save/load dialog in the given mode.
* @param abstract_filetype Kind of file to handle. * @param abstract_filetype Kind of file to handle.
* @param fop File operation to perform (load or save). * @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); CloseWindowById(WC_SAVELOAD, 0);
WindowDesc *sld; 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; sld = &_save_dialog_desc;
} else { } else {
/* Dialogue for loading a file. */ /* Dialogue for loading a file. */
sld = (abstract_filetype == FT_HEIGHTMAP) ? &_load_heightmap_dialog_desc : &_load_dialog_desc; 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_LOAD_CAPTION :{WHITE}Load Game
STR_SAVELOAD_SAVE_SCENARIO :{WHITE}Save Scenario STR_SAVELOAD_SAVE_SCENARIO :{WHITE}Save Scenario
STR_SAVELOAD_LOAD_SCENARIO :{WHITE}Load Scenario STR_SAVELOAD_LOAD_SCENARIO :{WHITE}Load Scenario
STR_SAVELOAD_LOAD_HEIGHTMAP :{WHITE}Load Heightmap
STR_SAVELOAD_SAVE_HEIGHTMAP :{WHITE}Save 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_HOME_BUTTON :{BLACK}Click here to jump to the current default save/load directory
STR_SAVELOAD_BYTES_FREE :{BLACK}{BYTES} free STR_SAVELOAD_BYTES_FREE :{BLACK}{BYTES} free
STR_SAVELOAD_LIST_TOOLTIP :{BLACK}List of drives, directories and saved-game files 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_SHIP :{BLACK}{SHIP}
STR_TOOLBAR_RAILTYPE_VELOCITY :{STRING} ({VELOCITY}) 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_APPEND_REVERSED_ORDER_LIST :Append reversed order list
STR_ORDER_DUPLICATE_ORDER :Duplicate order STR_ORDER_DUPLICATE_ORDER :Duplicate order
STR_ORDER_CHANGE_JUMP_TARGET :Change jump target 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_TRY_ACQUIRE_SLOT_BUTTON :Try acquire slot
STR_ORDER_RELEASE_SLOT_BUTTON :Release slot STR_ORDER_RELEASE_SLOT_BUTTON :Release slot

@ -233,6 +233,9 @@ public:
void MakeChangeCounter(); void MakeChangeCounter();
void MakeLabel(OrderLabelSubType subtype); void MakeLabel(OrderLabelSubType subtype);
std::string ToJSONString() const;
static Order FromJSONString(std::string jsonSTR);
/** /**
* Is this a 'goto' order with a real destination? * 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. * @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; } inline std::vector<DispatchSlot> &GetScheduledDispatchMutable() { return this->scheduled_dispatch; }
void SetScheduledDispatch(std::vector<DispatchSlot> dispatch_list); void SetScheduledDispatch(std::vector<DispatchSlot> dispatch_list);
void AddScheduledDispatch(uint32_t offset); void AddScheduledDispatch(uint32_t offset);
void RemoveScheduledDispatch(uint32_t offset); void RemoveScheduledDispatch(uint32_t offset);
void AdjustScheduledDispatch(int32_t adjust); void AdjustScheduledDispatch(int32_t adjust);
@ -856,6 +860,19 @@ public:
*/ */
inline int32_t GetScheduledDispatchDelay() const { return this->scheduled_dispatch_max_delay; } 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) inline void BorrowSchedule(DispatchSchedule &other)
{ {
this->CopyBasicFields(other); this->CopyBasicFields(other);
@ -869,12 +886,16 @@ public:
inline std::string &ScheduleName() { return this->name; } inline std::string &ScheduleName() { return this->name; }
inline const std::string &ScheduleName() const { 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 * Shared order list linking together the linked list of orders and the list
* of vehicles sharing this order list. * of vehicles sharing this order list.
*/ */
struct OrderList : OrderListPool::PoolItem<&_orderlist_pool> { struct OrderList : OrderListPool::PoolItem<&_orderlist_pool> {
private: private:
friend void AfterLoadVehicles(bool part_of_load); ///< For instantiating the shared vehicle chain friend void AfterLoadVehicles(bool part_of_load); ///< For instantiating the shared vehicle chain
@ -959,6 +980,9 @@ public:
void DeleteOrderAt(int index); void DeleteOrderAt(int index);
void MoveOrder(int from, int to); void MoveOrder(int from, int to);
std::string ToJSONString();
static void FromJSONString(const Vehicle* v,std::string str);
/** /**
* Is this a shared order list? * Is this a shared order list?
* @return whether this order list is shared among multiple vehicles * @return whether this order list is shared among multiple vehicles
@ -1025,7 +1049,7 @@ public:
void DebugCheckSanity() const; void DebugCheckSanity() const;
#endif #endif
bool CheckOrderListIndexing() const; bool CheckOrderListIndexing() const;
inline std::vector<DispatchSchedule> &GetScheduledDispatchScheduleSet() { return this->dispatch_schedules; } inline std::vector<DispatchSchedule> &GetScheduledDispatchScheduleSet() { return this->dispatch_schedules; }
inline const std::vector<DispatchSchedule> &GetScheduledDispatchScheduleSet() const { 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/container_func.hpp"
#include "core/pool_func.hpp" #include "core/pool_func.hpp"
#include "core/random_func.hpp" #include "core/random_func.hpp"
#include "core/serialisation.hpp"
#include "aircraft.h" #include "aircraft.h"
#include "roadveh.h" #include "roadveh.h"
#include "station_base.h" #include "station_base.h"
@ -40,12 +41,16 @@
#include "tracerestrict.h" #include "tracerestrict.h"
#include "train.h" #include "train.h"
#include "date_func.h" #include "date_func.h"
#include "3rdparty/nlohmann/json.hpp"
#include "command_aux.h"
#include "rev.h"
#include "table/strings.h" #include "table/strings.h"
#include "3rdparty/robin_hood/robin_hood.h" #include "3rdparty/robin_hood/robin_hood.h"
#include <3rdparty/nlohmann/json.hpp>
#include "safeguards.h" #include "safeguards.h"
#include <error.h>
/* DestinationID must be at least as large as every these below, because it can /* DestinationID must be at least as large as every these below, because it can
* be any of them * be any of them
@ -269,6 +274,100 @@ void Order::MakeLabel(OrderLabelSubType subtype)
this->flags = 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. * Make this depot/station order also a refit order.
* @param cargo the cargo type to change to. * @param cargo the cargo type to change to.
@ -824,6 +923,85 @@ void OrderList::MoveOrder(int from, int to)
this->ReindexOrderList(); 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. * Removes the vehicle from the shared order list.
* @note This is supposed to be called when the vehicle is still in the chain * @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, * - p2 = (bit 0 - 15) - the selected order (if any). If the last order is given,
* the order will be inserted before that one * the order will be inserted before that one
* @param p3 packed order to insert * @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 * @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); Order new_order = (orderJson != nullptr) ? Order::FromJSONString(orderJson) : Order(p3);
VehicleOrderID sel_ord = GB(p2, 0, 16);
Order new_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 "tracerestrict.h"
#include "scope.h" #include "scope.h"
#include "core/backup_type.hpp" #include "core/backup_type.hpp"
#include "fios.h"
#include "widgets/order_widget.h" #include "widgets/order_widget.h"
@ -633,6 +634,8 @@ enum OrderDropDownID {
static const StringID _order_manage_list_dropdown[] = { static const StringID _order_manage_list_dropdown[] = {
STR_ORDER_REVERSE_ORDER_LIST, STR_ORDER_REVERSE_ORDER_LIST,
STR_ORDER_APPEND_REVERSED_ORDER_LIST, STR_ORDER_APPEND_REVERSED_ORDER_LIST,
STR_ORDER_EXPORT_ORDER_LIST,
STR_ORDER_IMPORT_ORDER_LIST,
INVALID_STRING_ID INVALID_STRING_ID
}; };
@ -1545,6 +1548,7 @@ private:
}; };
int selected_order; int selected_order;
VehicleID vehicle_id;
VehicleOrderID order_over; ///< Order over which another order is dragged, \c INVALID_VEH_ORDER_ID if none. VehicleOrderID order_over; ///< Order over which another order is dragged, \c INVALID_VEH_ORDER_ID if none.
OrderPlaceObjectState goto_type; OrderPlaceObjectState goto_type;
Scrollbar *vscroll; Scrollbar *vscroll;
@ -3488,6 +3492,8 @@ public:
switch (index) { switch (index) {
case 0: this->OrderClick_ReverseOrderList(0); break; case 0: this->OrderClick_ReverseOrderList(0); break;
case 1: this->OrderClick_ReverseOrderList(1); 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(); default: NOT_REACHED();
} }
break; break;

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

@ -20,6 +20,7 @@
#include "settings_type.h" #include "settings_type.h"
#include "schdispatch.h" #include "schdispatch.h"
#include "vehicle_gui.h" #include "vehicle_gui.h"
#include <3rdparty/nlohmann/json.hpp>
#include <algorithm> #include <algorithm>
@ -357,15 +358,15 @@ CommandCost CmdScheduledDispatchClear(TileIndex tile, DoCommandFlag flags, uint3
* @param p1 Vehicle index * @param p1 Vehicle index
* @param p2 Duration, in scaled tick * @param p2 Duration, in scaled tick
* @param p3 Start tick * @param p3 Start tick
* @param text unused * @param text optional JSON string
* @return the cost of this operation or an error * @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); VehicleID veh = GB(p1, 0, 20);
Vehicle *v = Vehicle::GetIfValid(veh); 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); CommandCost ret = CheckOwnership(v->owner);
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
@ -376,10 +377,19 @@ CommandCost CmdScheduledDispatchAddNewSchedule(TileIndex tile, DoCommandFlag fla
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
v->orders->GetScheduledDispatchScheduleSet().emplace_back(); v->orders->GetScheduledDispatchScheduleSet().emplace_back();
DispatchSchedule &ds = v->orders->GetScheduledDispatchScheduleSet().back(); DispatchSchedule &ds = v->orders->GetScheduledDispatchScheduleSet().back();
ds.SetScheduledDispatchDuration(p2); if (scheduleJson == nullptr) {
ds.SetScheduledDispatchStartTick((StateTicks)p3); if (p2 == 0) return CMD_ERROR;
ds.UpdateScheduledDispatch(nullptr); ds.SetScheduledDispatchDuration(p2);
SetTimetableWindowsDirty(v, STWDF_SCHEDULED_DISPATCH); 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(); return CommandCost();
@ -801,3 +811,56 @@ void DispatchSchedule::UpdateScheduledDispatch(const Vehicle *v)
SetTimetableWindowsDirty(v, STWDF_SCHEDULED_DISPATCH); 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 */ /* 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 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. class CommandCost; ///< The cost of a command.
typedef uint16_t EngineID; ///< The ID of an engine. typedef uint16_t EngineID; ///< The ID of an engine.
typedef uint16_t GoalID; ///< The ID of a goal. typedef uint16_t GoalID; ///< The ID of a goal.

Loading…
Cancel
Save