From 90f89770c2374ae7ec1117bb8dcd76647213884c Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 13 Aug 2018 08:35:23 +0100 Subject: [PATCH 1/3] Set CMD_NO_TEST flag for plan commands (cherry picked from commit 8709fdade35bf93decedf71996838b122268fd5c) --- src/command.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index 406a38ae90..3cff93b7f8 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -361,11 +361,11 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdOpenCloseAirport, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_OPEN_CLOSE_AIRPORT - DEF_CMD(CmdAddPlan, 0, CMDT_OTHER_MANAGEMENT ), // CMD_ADD_PLAN - DEF_CMD(CmdAddPlanLine, 0, CMDT_OTHER_MANAGEMENT ), // CMD_ADD_PLAN_LINE - DEF_CMD(CmdRemovePlan, 0, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_PLAN - DEF_CMD(CmdRemovePlanLine, 0, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_PLAN_LINE - DEF_CMD(CmdChangePlanVisibility, 0, CMDT_OTHER_MANAGEMENT ), // CMD_CHANGE_PLAN_VISIBILITY + DEF_CMD(CmdAddPlan, CMD_NO_TEST, CMDT_OTHER_MANAGEMENT ), // CMD_ADD_PLAN + DEF_CMD(CmdAddPlanLine, CMD_NO_TEST, CMDT_OTHER_MANAGEMENT ), // CMD_ADD_PLAN_LINE + DEF_CMD(CmdRemovePlan, CMD_NO_TEST, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_PLAN + DEF_CMD(CmdRemovePlanLine, CMD_NO_TEST, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_PLAN_LINE + DEF_CMD(CmdChangePlanVisibility, CMD_NO_TEST, CMDT_OTHER_MANAGEMENT ), // CMD_CHANGE_PLAN_VISIBILITY }; /*! From 2f2937356b859b33e1fc10d40da83e23372c6250 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 13 Aug 2018 08:36:29 +0100 Subject: [PATCH 2/3] Add command parameter validation to plan commands (cherry picked from commit 957cff34dc30bdf51943e6810f8c0d449d70c75a) --- src/plans_base.h | 7 +++++-- src/plans_cmd.cpp | 33 ++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/plans_base.h b/src/plans_base.h index 7dac2d45b4..ef39d65d16 100644 --- a/src/plans_base.h +++ b/src/plans_base.h @@ -125,11 +125,14 @@ struct PlanLine { return buffer; } - void Import(const TileIndex* data, const uint data_length) + bool Import(const TileIndex* data, const uint data_length) { for (uint i = data_length; i != 0; i--, data++) { - this->tiles.push_back(FROM_LE32(*data)); + TileIndex t = FROM_LE32(*data); + if (t >= MapSize()) return false; + this->tiles.push_back(t); } + return true; } void AddLineToCalculateCentreTile(uint64 &x, uint64 &y, uint32 &count) const diff --git a/src/plans_cmd.cpp b/src/plans_cmd.cpp index 1cc585e410..9bcd2dc694 100644 --- a/src/plans_cmd.cpp +++ b/src/plans_cmd.cpp @@ -30,8 +30,10 @@ CommandCost CmdAddPlan(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (!Plan::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_PLANS); + Owner o = (Owner) p1; + CommandCost ret = CheckOwnership(o); + if (ret.Failed()) return ret; if (flags & DC_EXEC) { - Owner o = (Owner) p1; _new_plan = new Plan(o); if (o == _local_company) { _new_plan->SetVisibility(true); @@ -53,12 +55,19 @@ CommandCost CmdAddPlan(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2 */ CommandCost CmdAddPlanLine(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { + Plan *p = Plan::GetIfValid(p1); + if (p == NULL) return CMD_ERROR; + CommandCost ret = CheckOwnership(p->owner); + if (ret.Failed()) return ret; + if (p2 > (MAX_CMD_TEXT_LENGTH / sizeof(TileIndex))) return_cmd_error(STR_ERROR_TOO_MANY_NODES); if (flags & DC_EXEC) { - Plan *p = Plan::Get(p1); PlanLine *pl = p->NewLine(); if (!pl) return_cmd_error(STR_ERROR_NO_MORE_SPACE_FOR_LINES); - if (p2 > (MAX_CMD_TEXT_LENGTH / sizeof(TileIndex))) return_cmd_error(STR_ERROR_TOO_MANY_NODES); - pl->Import((const TileIndex *) text, p2); + if (!pl->Import((const TileIndex *) text, p2)) { + delete pl; + p->lines.pop_back(); + return CMD_ERROR; + } if (p->IsListable()) { pl->SetVisibility(p->visible); if (p->visible) pl->MarkDirty(); @@ -80,8 +89,11 @@ CommandCost CmdAddPlanLine(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 */ CommandCost CmdChangePlanVisibility(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { + Plan *p = Plan::GetIfValid(p1); + if (p == NULL) return CMD_ERROR; + CommandCost ret = CheckOwnership(p->owner); + if (ret.Failed()) return ret; if (flags & DC_EXEC) { - Plan *p = Plan::Get(p1); p->visible_by_all = p2 != 0; Window *w = FindWindowById(WC_PLANS, 0); if (w) w->InvalidateData(INVALID_PLAN, false); @@ -100,8 +112,11 @@ CommandCost CmdChangePlanVisibility(TileIndex tile, DoCommandFlag flags, uint32 */ CommandCost CmdRemovePlan(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { + Plan *p = Plan::GetIfValid(p1); + if (p == NULL) return CMD_ERROR; + CommandCost ret = CheckOwnership(p->owner); + if (ret.Failed()) return ret; if (flags & DC_EXEC) { - Plan *p = Plan::Get(p1); if (p->IsListable()) { p->SetVisibility(false); Window *w = FindWindowById(WC_PLANS, 0); @@ -124,8 +139,12 @@ CommandCost CmdRemovePlan(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 */ CommandCost CmdRemovePlanLine(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { + Plan *p = Plan::GetIfValid(p1); + if (p == NULL) return CMD_ERROR; + CommandCost ret = CheckOwnership(p->owner); + if (ret.Failed()) return ret; + if (p2 >= p->lines.size()) return CMD_ERROR; if (flags & DC_EXEC) { - Plan *p = Plan::Get(p1); PlanLineVector::iterator it = p->lines.begin(); std::advance(it, p2); (*it)->SetVisibility(false); From 98da7019d665b117dcd86e58bb5337a250f4f162 Mon Sep 17 00:00:00 2001 From: keldorkatarn Date: Tue, 10 Apr 2018 15:29:22 +0200 Subject: [PATCH 3/3] Plans: Make it possible to give plans a name. (cherry picked from commit d9b553f130a6eda36e2eea217785f829d3d8624e) --- src/command.cpp | 2 ++ src/command_type.h | 1 + src/lang/english.txt | 3 +++ src/plans_base.h | 12 ++++++++++++ src/plans_cmd.cpp | 29 +++++++++++++++++++++++++++++ src/plans_gui.cpp | 31 +++++++++++++++++++++++++++---- src/plans_type.h | 2 ++ src/saveload/extended_ver_sl.cpp | 2 +- src/saveload/plans_sl.cpp | 1 + src/widgets/plans_widget.h | 1 + 10 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index 3cff93b7f8..0f2d635a2b 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -203,6 +203,7 @@ CommandProc CmdAddPlanLine; CommandProc CmdRemovePlan; CommandProc CmdRemovePlanLine; CommandProc CmdChangePlanVisibility; +CommandProc CmdRenamePlan; #define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type} @@ -366,6 +367,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdRemovePlan, CMD_NO_TEST, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_PLAN DEF_CMD(CmdRemovePlanLine, CMD_NO_TEST, CMDT_OTHER_MANAGEMENT ), // CMD_REMOVE_PLAN_LINE DEF_CMD(CmdChangePlanVisibility, CMD_NO_TEST, CMDT_OTHER_MANAGEMENT ), // CMD_CHANGE_PLAN_VISIBILITY + DEF_CMD(CmdRenamePlan, CMD_NO_TEST, CMDT_OTHER_MANAGEMENT ), // CMD_RENAME_PLAN }; /*! diff --git a/src/command_type.h b/src/command_type.h index 211efa61fd..8522dc08b3 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -334,6 +334,7 @@ enum Commands { CMD_REMOVE_PLAN, CMD_REMOVE_PLAN_LINE, CMD_CHANGE_PLAN_VISIBILITY, + CMD_RENAME_PLAN, CMD_END, ///< Must ALWAYS be on the end of this list!! (period) }; diff --git a/src/lang/english.txt b/src/lang/english.txt index 339aeb5705..edab6d5e0b 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -4105,8 +4105,10 @@ STR_PLANS_VISIBILITY_TOOLTIP :{BLACK}Toggle t STR_PLANS_DELETE :{BLACK}Delete STR_PLANS_DELETE_TOOLTIP :{BLACK}Delete the selected item in the list STR_PLANS_LIST_ITEM_PLAN :Plan #{NUM}: {NUM} line{P "" s} ({DATE_SHORT}) +STR_PLANS_LIST_ITEM_NAMED_PLAN :{RAW_STRING}: {NUM} line{P "" s} ({DATE_SHORT}) STR_PLANS_LIST_ITEM_LINE : -- Line #{NUM}: {NUM} segment{P "" s} STR_PLANS_LIST_TOOLTIP :{BLACK}Double click any item in the list to (un)fold the related plan.{}Ctrl+Click to scroll to. +STR_PLANS_QUERY_RENAME_PLAN :{WHITE}Rename plan # Vehicle loading indicators STR_PERCENT_UP_SMALL :{TINY_FONT}{WHITE}{NUM}%{UP_ARROW} @@ -4534,6 +4536,7 @@ STR_ERROR_CAN_T_DELETE_SIGN :{WHITE}Can't de STR_ERROR_TOO_MANY_PLANS :{WHITE}... too many plans STR_ERROR_TOO_MANY_NODES :{WHITE}... too many nodes in plan line STR_ERROR_NO_MORE_SPACE_FOR_LINES :{WHITE}No more space for lines +STR_ERROR_CAN_T_RENAME_PLAN :{WHITE}Can't rename plan... # Translatable comment for OpenTTD's desktop shortcut STR_DESKTOP_SHORTCUT_COMMENT :A simulation game based on Transport Tycoon Deluxe diff --git a/src/plans_base.h b/src/plans_base.h index ef39d65d16..06cf40e15c 100644 --- a/src/plans_base.h +++ b/src/plans_base.h @@ -21,6 +21,7 @@ #include "date_func.h" #include "viewport_func.h" #include "core/endian_func.hpp" +#include #include typedef Pool PlanPool; @@ -164,6 +165,7 @@ struct Plan : PlanPool::PoolItem<&_plan_pool> { bool visible_by_all; bool show_lines; Date creation_date; + std::string name; Plan(Owner owner = INVALID_OWNER) { @@ -247,12 +249,22 @@ struct Plan : PlanPool::PoolItem<&_plan_pool> { return this->visible; } + bool HasName() const + { + return !this->name.empty(); + } + bool ToggleVisibilityByAll() { if (_current_plan->owner == _local_company) DoCommandP(0, _current_plan->index, !this->visible_by_all, CMD_CHANGE_PLAN_VISIBILITY); return this->visible_by_all; } + const std::string &GetName() const + { + return this->name; + } + TileIndex CalculateCentreTile() const { uint64 x = 0; diff --git a/src/plans_cmd.cpp b/src/plans_cmd.cpp index 9bcd2dc694..3b8158414f 100644 --- a/src/plans_cmd.cpp +++ b/src/plans_cmd.cpp @@ -15,6 +15,7 @@ #include "plans_func.h" #include "window_func.h" #include "company_func.h" +#include "string_func.h" #include "window_gui.h" #include "table/strings.h" @@ -157,3 +158,31 @@ CommandCost CmdRemovePlanLine(TileIndex tile, DoCommandFlag flags, uint32 p1, ui } return CommandCost(); } + +/** +* Give a custom name to your plan +* @param tile unused +* @param flags type of operation +* @param p1 ID of plan to name +* @param p2 unused +* @param text the new name +* @return the cost of this operation or an error +*/ +CommandCost CmdRenamePlan(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + if (text == NULL) return CMD_ERROR; + + Plan *p = Plan::GetIfValid(p1); + if (p == NULL) return CMD_ERROR; + CommandCost ret = CheckOwnership(p->owner); + if (ret.Failed()) return ret; + + if (Utf8StringLength(text) >= MAX_LENGTH_PLAN_NAME_CHARS) return CMD_ERROR; + + if (flags & DC_EXEC) { + p->name = text; + InvalidateWindowClassesData(WC_PLANS); + } + + return CommandCost(); +} diff --git a/src/plans_gui.cpp b/src/plans_gui.cpp index 544227484b..a298c37c3c 100644 --- a/src/plans_gui.cpp +++ b/src/plans_gui.cpp @@ -20,6 +20,7 @@ #include "window_func.h" #include "viewport_func.h" #include "gfx_func.h" +#include "textbuf_gui.h" #include "tilehighlight_func.h" #include "strings_func.h" #include "core/pool_func.hpp" @@ -56,6 +57,7 @@ static const NWidgetPart _nested_plans_widgets[] = { NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_PLN_SHOW_ALL), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_SHOW_ALL, STR_PLANS_SHOW_ALL_TOOLTIP), EndContainer(), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_PLN_DELETE), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_PLANS_DELETE, STR_PLANS_DELETE_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_PLN_RENAME), SetResize(1, 0), SetFill(1, 0), SetDataTip(STR_BUTTON_RENAME, STR_NULL), NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), EndContainer(), @@ -126,6 +128,16 @@ struct PlansWindow : Window { this->SetWidgetDirty(WID_PLN_LIST); break; } + + case WID_PLN_RENAME: { + if (_current_plan) { + SetDParamStr(0, _current_plan->GetName().c_str()); + ShowQueryString(STR_JUST_RAW_STRING, STR_PLANS_QUERY_RENAME_PLAN, + MAX_LENGTH_PLAN_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_LEN_IN_CHARS); + } + break; + } + case WID_PLN_SHOW_ALL: { Plan *p; FOR_ALL_PLANS(p) { @@ -187,6 +199,13 @@ struct PlansWindow : Window { } } + virtual void OnQueryTextFinished(char *str) + { + if (_current_plan == nullptr || str == nullptr) return; + + DoCommandP(0, _current_plan->index, 0, CMD_RENAME_PLAN | CMD_MSG(STR_ERROR_CAN_T_RENAME_PLAN), NULL, str); + } + bool AllPlansHidden() const { Plan *p; @@ -202,10 +221,10 @@ struct PlansWindow : Window { this->SetWidgetDisabledState(WID_PLN_SHOW_ALL, this->vscroll->GetCount() == 0); this->hide_all_sel->SetDisplayedPlane(this->vscroll->GetCount() != 0 && this->AllPlansHidden() ? 1 : 0); if (_current_plan) { - this->SetWidgetsDisabledState(_current_plan->owner != _local_company, WID_PLN_ADD_LINES, WID_PLN_VISIBILITY, WID_PLN_DELETE, WIDGET_LIST_END); + this->SetWidgetsDisabledState(_current_plan->owner != _local_company, WID_PLN_ADD_LINES, WID_PLN_VISIBILITY, WID_PLN_DELETE, WID_PLN_RENAME, WIDGET_LIST_END); this->GetWidget(WID_PLN_VISIBILITY)->widget_data = _current_plan->visible_by_all ? STR_PLANS_VISIBILITY_PRIVATE : STR_PLANS_VISIBILITY_PUBLIC; } else { - this->SetWidgetsDisabledState(true, WID_PLN_ADD_LINES, WID_PLN_VISIBILITY, WID_PLN_DELETE, WIDGET_LIST_END); + this->SetWidgetsDisabledState(true, WID_PLN_ADD_LINES, WID_PLN_VISIBILITY, WID_PLN_DELETE, WID_PLN_RENAME, WIDGET_LIST_END); } this->DrawWidgets(); } @@ -235,10 +254,14 @@ struct PlansWindow : Window { if (list[i].is_plan) { DrawCompanyIcon(p->owner, icon_left, y + (this->resize.step_height - this->company_icon_spr_dim.height) / 2); DrawBoolButton(btn_left, y + (this->resize.step_height - SETTING_BUTTON_HEIGHT) / 2, p->visible, true); - SetDParam(0, list[i].plan_id + 1); + if (p->HasName()) { + SetDParamStr(0, p->GetName().c_str()); + } else { + SetDParam(0, list[i].plan_id + 1); + } SetDParam(1, p->lines.size()); SetDParam(2, p->creation_date); - DrawString(text_left, text_right, y + (this->resize.step_height - FONT_HEIGHT_NORMAL) / 2, STR_PLANS_LIST_ITEM_PLAN, p->visible_by_all ? TC_LIGHT_BLUE : TC_YELLOW); + DrawString(text_left, text_right, y + (this->resize.step_height - FONT_HEIGHT_NORMAL) / 2, p->HasName() ? STR_PLANS_LIST_ITEM_NAMED_PLAN : STR_PLANS_LIST_ITEM_PLAN, p->visible_by_all ? TC_LIGHT_BLUE : TC_YELLOW); } else { PlanLine *pl = p->lines[list[i].line_id]; DrawBoolButton(btn_left, y + (this->resize.step_height - SETTING_BUTTON_HEIGHT) / 2, pl->visible, true); diff --git a/src/plans_type.h b/src/plans_type.h index 5947fc39e4..39c2a68b5f 100644 --- a/src/plans_type.h +++ b/src/plans_type.h @@ -20,6 +20,8 @@ typedef uint16 PlanID; struct PlanLine; struct Plan; +static const uint MAX_LENGTH_PLAN_NAME_CHARS = 128; ///< The maximum length of a plan name in characters including '\0' + static const PlanID INVALID_PLAN = 0xFFFF; ///< Sentinel for an invalid plan. #endif /* PLANS_TYPE_H */ diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 12cd6e837a..138d20334f 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -45,7 +45,7 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { - { XSLFI_ENH_VIEWPORT_PLANS, XSCF_IGNORABLE_ALL, 2, 2, "enh_viewport_plans", NULL, NULL, "PLAN" }, + { XSLFI_ENH_VIEWPORT_PLANS, XSCF_IGNORABLE_ALL, 3, 3, "enh_viewport_plans", NULL, NULL, "PLAN" }, { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker }; diff --git a/src/saveload/plans_sl.cpp b/src/saveload/plans_sl.cpp index 97b32b825e..95b1716d43 100644 --- a/src/saveload/plans_sl.cpp +++ b/src/saveload/plans_sl.cpp @@ -21,6 +21,7 @@ static const SaveLoad _plan_desc[] = { SLE_VAR(Plan, visible, SLE_BOOL), SLE_VAR(Plan, visible_by_all, SLE_BOOL), SLE_VAR(Plan, creation_date, SLE_INT32), + SLE_CONDSTDSTR_X(Plan, name, 0, 0, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ENH_VIEWPORT_PLANS, 3)), SLE_END() }; diff --git a/src/widgets/plans_widget.h b/src/widgets/plans_widget.h index 430abbcbac..08be7306b2 100644 --- a/src/widgets/plans_widget.h +++ b/src/widgets/plans_widget.h @@ -24,6 +24,7 @@ enum PlansWidgets { WID_PLN_SHOW_ALL, WID_PLN_DELETE, WID_PLN_HIDE_ALL_SEL, + WID_PLN_RENAME, }; #endif /* WIDGETS_PLANS_WIDGET_H */