mirror of https://github.com/OpenTTD/OpenTTD
Compare commits
10 Commits
a938044d43
...
3888cbe275
Author | SHA1 | Date |
---|---|---|
|
3888cbe275 | |
|
2cdd50f40e | |
|
56942a15c7 | |
|
5eeda026a4 | |
|
edc5b8ea1f | |
|
7b68c184fb | |
|
2f4c5e6737 | |
|
016970154d | |
|
a92619be89 | |
|
679f95d1de |
|
@ -1067,6 +1067,7 @@ static uint DeliverGoodsToIndustry(const Station *st, CargoType cargo_type, uint
|
|||
|
||||
uint amount = std::min(num_pieces, 0xFFFFu - it->waiting);
|
||||
it->waiting += amount;
|
||||
it->GetOrCreateHistory()[THIS_MONTH].accepted += amount;
|
||||
it->last_accepted = TimerGameEconomy::date;
|
||||
num_pieces -= amount;
|
||||
accepted += amount;
|
||||
|
|
18
src/gfx.cpp
18
src/gfx.cpp
|
@ -580,10 +580,11 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
|||
|
||||
const uint shadow_offset = ScaleGUITrad(1);
|
||||
|
||||
auto draw_line = [&](const ParagraphLayouter::Line &line, bool do_shadow, int left, int min_x, int max_x, bool truncation, TextColour &last_colour) {
|
||||
auto draw_line = [&](const ParagraphLayouter::Line &line, bool do_shadow, int left, int min_x, int max_x, bool truncation, TextColour initial_colour) {
|
||||
const DrawPixelInfo *dpi = _cur_dpi;
|
||||
int dpi_left = dpi->left;
|
||||
int dpi_right = dpi->left + dpi->width - 1;
|
||||
TextColour last_colour = initial_colour;
|
||||
|
||||
for (int run_index = 0; run_index < line.CountRuns(); run_index++) {
|
||||
const ParagraphLayouter::VisualRun &run = line.GetVisualRun(run_index);
|
||||
|
@ -593,13 +594,10 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
|||
|
||||
FontCache *fc = f->fc;
|
||||
TextColour colour = f->colour;
|
||||
if (colour == TC_INVALID || HasFlag(last_colour, TC_FORCED)) {
|
||||
colour = last_colour;
|
||||
} else {
|
||||
/* Update the last colour for the truncation ellipsis. */
|
||||
last_colour = colour;
|
||||
}
|
||||
if (colour == TC_INVALID || HasFlag(initial_colour, TC_FORCED)) colour = initial_colour;
|
||||
bool colour_has_shadow = (colour & TC_NO_SHADE) == 0 && colour != TC_BLACK;
|
||||
/* Update the last colour for the truncation ellipsis. */
|
||||
last_colour = colour;
|
||||
if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue;
|
||||
SetColourRemap(do_shadow ? TC_BLACK : colour);
|
||||
|
||||
|
@ -625,16 +623,16 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
|||
GfxMainBlitter(sprite, begin_x + (do_shadow ? shadow_offset : 0), top + (do_shadow ? shadow_offset : 0), BlitterMode::ColourRemap);
|
||||
}
|
||||
}
|
||||
return last_colour;
|
||||
};
|
||||
|
||||
/* Draw shadow, then foreground */
|
||||
for (bool do_shadow : {true, false}) {
|
||||
TextColour last_colour = default_colour;
|
||||
draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation, last_colour);
|
||||
TextColour colour = draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation, default_colour);
|
||||
|
||||
if (truncation) {
|
||||
int x = (_current_text_dir == TD_RTL) ? left : (right - truncation_width);
|
||||
draw_line(*truncation_layout->front(), do_shadow, x, INT32_MIN, INT32_MAX, false, last_colour);
|
||||
draw_line(*truncation_layout->front(), do_shadow, x, INT32_MIN, INT32_MAX, false, colour);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -182,8 +182,9 @@ protected:
|
|||
static const int MIN_GRAPH_NUM_LINES_Y = 9; ///< Minimal number of horizontal lines to draw.
|
||||
static const int MIN_GRID_PIXEL_SIZE = 20; ///< Minimum distance between graph lines.
|
||||
|
||||
uint64_t excluded_data = 0; ///< bitmask of the datasets that shouldn't be displayed.
|
||||
uint64_t excluded_range = 0; ///< bitmask of ranges that should not be displayed.
|
||||
uint64_t excluded_data = 0; ///< bitmask of datasets hidden by the player.
|
||||
uint64_t excluded_range = 0; ///< bitmask of ranges hidden by the player.
|
||||
uint64_t masked_range = 0; ///< bitmask of ranges that are not available for the current data.
|
||||
uint8_t num_on_x_axis = 0;
|
||||
uint8_t num_vert_lines = GRAPH_NUM_MONTHS;
|
||||
|
||||
|
@ -216,13 +217,20 @@ protected:
|
|||
uint8_t highlight_range = UINT8_MAX; ///< Data range that should be highlighted, or UINT8_MAX for none.
|
||||
bool highlight_state = false; ///< Current state of highlight, toggled every TIMER_BLINK_INTERVAL period.
|
||||
|
||||
template <typename Tprojection>
|
||||
struct Filler {
|
||||
struct BaseFiller {
|
||||
DataSet &dataset; ///< Dataset to fill.
|
||||
|
||||
inline void MakeZero(uint i) const { this->dataset.values[i] = 0; }
|
||||
inline void MakeInvalid(uint i) const { this->dataset.values[i] = INVALID_DATAPOINT; }
|
||||
};
|
||||
|
||||
template <typename Tprojection>
|
||||
struct Filler : BaseFiller {
|
||||
const Tprojection &proj; ///< Projection to apply.
|
||||
|
||||
constexpr Filler(DataSet &dataset, const Tprojection &proj) : BaseFiller(dataset), proj(proj) {}
|
||||
|
||||
inline void Fill(uint i, const auto &data) const { this->dataset.values[i] = std::invoke(this->proj, data); }
|
||||
inline void MakeInvalid(uint i) const { this->dataset.values[i] = INVALID_DATAPOINT; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -675,13 +683,17 @@ public:
|
|||
uint index = 0;
|
||||
Rect line = r.WithHeight(line_height);
|
||||
for (const auto &str : this->ranges) {
|
||||
bool lowered = !HasBit(this->excluded_range, index);
|
||||
bool lowered = !HasBit(this->excluded_range, index) && !HasBit(this->masked_range, index);
|
||||
|
||||
/* Redraw frame if lowered */
|
||||
if (lowered) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
|
||||
|
||||
const Rect text = line.Shrink(WidgetDimensions::scaled.framerect);
|
||||
DrawString(text, str, TC_BLACK, SA_CENTER, false, FS_SMALL);
|
||||
DrawString(text, str, (this->highlight_state && this->highlight_range == index) ? TC_WHITE : TC_BLACK, SA_CENTER, false, FS_SMALL);
|
||||
|
||||
if (HasBit(this->masked_range, index)) {
|
||||
GfxFillRect(line.Shrink(WidgetDimensions::scaled.bevel), GetColourGradient(COLOUR_BROWN, SHADE_DARKER), FILLRECT_CHECKER);
|
||||
}
|
||||
|
||||
line = line.Translate(0, line_height);
|
||||
++index;
|
||||
|
@ -704,6 +716,7 @@ public:
|
|||
case WID_GRAPH_RANGE_MATRIX: {
|
||||
int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
|
||||
|
||||
if (HasBit(this->masked_range, row)) break;
|
||||
ToggleBit(this->excluded_range, row);
|
||||
this->SetDirty();
|
||||
break;
|
||||
|
@ -1115,6 +1128,7 @@ struct BaseCargoGraphWindow : BaseGraphWindow {
|
|||
{
|
||||
this->CreateNestedTree();
|
||||
|
||||
this->excluded_range = this->masked_range;
|
||||
this->cargo_types = this->GetCargoTypes(number);
|
||||
|
||||
this->vscroll = this->GetScrollbar(WID_GRAPH_MATRIX_SCROLLBAR);
|
||||
|
@ -1608,7 +1622,9 @@ CompanyID PerformanceRatingDetailWindow::company = CompanyID::Invalid();
|
|||
struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
|
||||
static inline constexpr StringID RANGE_LABELS[] = {
|
||||
STR_GRAPH_INDUSTRY_RANGE_PRODUCED,
|
||||
STR_GRAPH_INDUSTRY_RANGE_TRANSPORTED
|
||||
STR_GRAPH_INDUSTRY_RANGE_TRANSPORTED,
|
||||
STR_GRAPH_INDUSTRY_RANGE_DELIVERED,
|
||||
STR_GRAPH_INDUSTRY_RANGE_WAITING,
|
||||
};
|
||||
|
||||
static inline CargoTypes excluded_cargo_types{};
|
||||
|
@ -1623,6 +1639,10 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
|
|||
this->draw_dates = !TimerGameEconomy::UsingWallclockUnits();
|
||||
this->ranges = RANGE_LABELS;
|
||||
|
||||
const Industry *i = Industry::Get(window_number);
|
||||
if (!i->IsCargoProduced()) this->masked_range = (1U << 0) | (1U << 1);
|
||||
if (!i->IsCargoAccepted()) this->masked_range = (1U << 2) | (1U << 3);
|
||||
|
||||
this->InitializeWindow(window_number, STR_GRAPH_LAST_24_MINUTES_TIME_LABEL);
|
||||
}
|
||||
|
||||
|
@ -1630,6 +1650,9 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
|
|||
{
|
||||
CargoTypes cargo_types{};
|
||||
const Industry *i = Industry::Get(window_number);
|
||||
for (const auto &a : i->accepted) {
|
||||
if (IsValidCargoType(a.cargo)) SetBit(cargo_types, a.cargo);
|
||||
}
|
||||
for (const auto &p : i->produced) {
|
||||
if (IsValidCargoType(p.cargo)) SetBit(cargo_types, p.cargo);
|
||||
}
|
||||
|
@ -1643,7 +1666,7 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
|
|||
|
||||
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
|
||||
{
|
||||
if (widget == WID_GRAPH_CAPTION) return GetString(STR_GRAPH_INDUSTRY_PRODUCTION_CAPTION, this->window_number);
|
||||
if (widget == WID_GRAPH_CAPTION) return GetString(STR_GRAPH_INDUSTRY_CAPTION, this->window_number);
|
||||
|
||||
return this->Window::GetWidgetString(widget, stringid);
|
||||
}
|
||||
|
@ -1691,6 +1714,33 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
|
|||
FillFromHistory<GRAPH_NUM_MONTHS>(p.history, i->valid_history, produced_filler, transported_filler);
|
||||
}
|
||||
|
||||
for (const auto &a : i->accepted) {
|
||||
if (!IsValidCargoType(a.cargo)) continue;
|
||||
const CargoSpec *cs = CargoSpec::Get(a.cargo);
|
||||
|
||||
this->data.reserve(this->data.size() + 2);
|
||||
|
||||
DataSet &accepted = this->data.emplace_back();
|
||||
accepted.colour = cs->legend_colour;
|
||||
accepted.exclude_bit = cs->Index();
|
||||
accepted.range_bit = 2;
|
||||
accepted.dash = 1;
|
||||
auto accepted_filler = Filler{accepted, &Industry::AcceptedHistory::accepted};
|
||||
|
||||
DataSet &waiting = this->data.emplace_back();
|
||||
waiting.colour = cs->legend_colour;
|
||||
waiting.exclude_bit = cs->Index();
|
||||
waiting.range_bit = 3;
|
||||
waiting.dash = 4;
|
||||
auto waiting_filler = Filler{waiting, &Industry::AcceptedHistory::waiting};
|
||||
|
||||
if (a.history == nullptr) {
|
||||
FillFromEmpty<GRAPH_NUM_MONTHS>(i->valid_history, accepted_filler, waiting_filler);
|
||||
} else {
|
||||
FillFromHistory<GRAPH_NUM_MONTHS>(*a.history, i->valid_history, accepted_filler, waiting_filler);
|
||||
}
|
||||
}
|
||||
|
||||
this->SetDirty();
|
||||
}
|
||||
};
|
||||
|
|
108
src/hotkeys.cpp
108
src/hotkeys.cpp
|
@ -27,8 +27,16 @@ static std::vector<HotkeyList*> *_hotkey_lists = nullptr;
|
|||
|
||||
/** String representation of a keycode */
|
||||
struct KeycodeNames {
|
||||
const std::string_view name; ///< Name of the keycode
|
||||
std::string_view name; ///< Name of the keycode
|
||||
WindowKeyCodes keycode; ///< The keycode
|
||||
std::string_view short_name;
|
||||
|
||||
KeycodeNames(const std::string_view name, const WindowKeyCodes keycode, const std::string_view short_name = "")
|
||||
{
|
||||
this->name = name;
|
||||
this->keycode = keycode;
|
||||
this->short_name = short_name.empty() ? name : short_name;
|
||||
}
|
||||
};
|
||||
|
||||
/** Array of non-standard keycodes that can be used in the hotkeys config file. */
|
||||
|
@ -38,16 +46,17 @@ static const std::initializer_list<KeycodeNames> _keycode_to_name = {
|
|||
{"ALT", WKC_ALT},
|
||||
{"META", WKC_META},
|
||||
{"GLOBAL", WKC_GLOBAL_HOTKEY},
|
||||
{"ESC", WKC_ESC},
|
||||
{"BACKSPACE", WKC_BACKSPACE},
|
||||
{"INS", WKC_INSERT},
|
||||
{"DEL", WKC_DELETE},
|
||||
{"PAGEUP", WKC_PAGEUP},
|
||||
{"PAGEDOWN", WKC_PAGEDOWN},
|
||||
{"END", WKC_END},
|
||||
{"HOME", WKC_HOME},
|
||||
{"RETURN", WKC_RETURN},
|
||||
{"SPACE", WKC_SPACE},
|
||||
{"ESC", WKC_ESC, "Esc"},
|
||||
{"TAB", WKC_TAB, "Tab"},
|
||||
{"BACKSPACE", WKC_BACKSPACE, "BS"},
|
||||
{"INS", WKC_INSERT, "Ins"},
|
||||
{"DEL", WKC_DELETE, "Del"},
|
||||
{"PAGEUP", WKC_PAGEUP, "Page up"},
|
||||
{"PAGEDOWN", WKC_PAGEDOWN, "Page down"},
|
||||
{"END", WKC_END, "End"},
|
||||
{"HOME", WKC_HOME, "Home"},
|
||||
{"RETURN", WKC_RETURN, "Enter"},
|
||||
{"SPACE", WKC_SPACE, "Space"},
|
||||
{"F1", WKC_F1},
|
||||
{"F2", WKC_F2},
|
||||
{"F3", WKC_F3},
|
||||
|
@ -61,31 +70,31 @@ static const std::initializer_list<KeycodeNames> _keycode_to_name = {
|
|||
{"F11", WKC_F11},
|
||||
{"F12", WKC_F12},
|
||||
{"BACKQUOTE", WKC_BACKQUOTE},
|
||||
{"PAUSE", WKC_PAUSE},
|
||||
{"NUM_DIV", WKC_NUM_DIV},
|
||||
{"NUM_MUL", WKC_NUM_MUL},
|
||||
{"NUM_MINUS", WKC_NUM_MINUS},
|
||||
{"NUM_PLUS", WKC_NUM_PLUS},
|
||||
{"NUM_ENTER", WKC_NUM_ENTER},
|
||||
{"NUM_DOT", WKC_NUM_DECIMAL},
|
||||
{"SLASH", WKC_SLASH},
|
||||
{"PAUSE", WKC_PAUSE, "Pause"},
|
||||
{"NUM_DIV", WKC_NUM_DIV, "Num /"},
|
||||
{"NUM_MUL", WKC_NUM_MUL, "Num *"},
|
||||
{"NUM_MINUS", WKC_NUM_MINUS, "Num -"},
|
||||
{"NUM_PLUS", WKC_NUM_PLUS, "Num +"},
|
||||
{"NUM_ENTER", WKC_NUM_ENTER, "Num Enter"},
|
||||
{"NUM_DOT", WKC_NUM_DECIMAL, "Num ."},
|
||||
{"SLASH", WKC_SLASH, "/"},
|
||||
{"/", WKC_SLASH}, /* deprecated, use SLASH */
|
||||
{"SEMICOLON", WKC_SEMICOLON},
|
||||
{"SEMICOLON", WKC_SEMICOLON, ";"},
|
||||
{";", WKC_SEMICOLON}, /* deprecated, use SEMICOLON */
|
||||
{"EQUALS", WKC_EQUALS},
|
||||
{"EQUALS", WKC_EQUALS, "="},
|
||||
{"=", WKC_EQUALS}, /* deprecated, use EQUALS */
|
||||
{"L_BRACKET", WKC_L_BRACKET},
|
||||
{"L_BRACKET", WKC_L_BRACKET, "["},
|
||||
{"[", WKC_L_BRACKET}, /* deprecated, use L_BRACKET */
|
||||
{"BACKSLASH", WKC_BACKSLASH},
|
||||
{"BACKSLASH", WKC_BACKSLASH, "\\"},
|
||||
{"\\", WKC_BACKSLASH}, /* deprecated, use BACKSLASH */
|
||||
{"R_BRACKET", WKC_R_BRACKET},
|
||||
{"R_BRACKET", WKC_R_BRACKET, "]"},
|
||||
{"]", WKC_R_BRACKET}, /* deprecated, use R_BRACKET */
|
||||
{"SINGLEQUOTE", WKC_SINGLEQUOTE},
|
||||
{"SINGLEQUOTE", WKC_SINGLEQUOTE, "'"},
|
||||
{"'", WKC_SINGLEQUOTE}, /* deprecated, use SINGLEQUOTE */
|
||||
{"COMMA", WKC_COMMA},
|
||||
{"PERIOD", WKC_PERIOD},
|
||||
{"COMMA", WKC_COMMA, ","},
|
||||
{"PERIOD", WKC_PERIOD, "."},
|
||||
{".", WKC_PERIOD}, /* deprecated, use PERIOD */
|
||||
{"MINUS", WKC_MINUS},
|
||||
{"MINUS", WKC_MINUS, "-"},
|
||||
{"-", WKC_MINUS}, /* deprecated, use MINUS */
|
||||
};
|
||||
|
||||
|
@ -196,6 +205,40 @@ static std::string KeycodeToString(uint16_t keycode)
|
|||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* A short representation of the keycode, for printing as a hotkey hint.
|
||||
* @param keycode The keycode to convert to a string.
|
||||
* @return A string representation of this keycode.
|
||||
*/
|
||||
std::string KeycodeToShortString(uint16_t keycode)
|
||||
{
|
||||
std::string str;
|
||||
if (keycode & WKC_SHIFT) {
|
||||
// TODO
|
||||
str += "»";
|
||||
}
|
||||
if (keycode & WKC_CTRL) {
|
||||
str += "^";
|
||||
}
|
||||
if (keycode & WKC_ALT) {
|
||||
str += "A+";
|
||||
}
|
||||
if (keycode & WKC_META) {
|
||||
str += "M+";
|
||||
}
|
||||
keycode = keycode & ~WKC_SPECIAL_KEYS;
|
||||
|
||||
for (const auto &kn : _keycode_to_name) {
|
||||
if (kn.keycode == keycode) {
|
||||
str += kn.short_name;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
assert(keycode < 128);
|
||||
str.push_back(keycode);
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert all keycodes attached to a hotkey to a single string. If multiple
|
||||
* keycodes are attached to the hotkey they are split by a comma.
|
||||
|
@ -350,3 +393,12 @@ void HandleGlobalHotkeys([[maybe_unused]] char32_t key, uint16_t keycode)
|
|||
}
|
||||
}
|
||||
|
||||
const Hotkey* HotkeyList::GetHotkeyByNum(int num) const
|
||||
{
|
||||
for (const Hotkey &hotkey : this->items) {
|
||||
if (hotkey.num == num) {
|
||||
return &hotkey;
|
||||
}
|
||||
}
|
||||
return (const Hotkey*) nullptr;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ struct HotkeyList {
|
|||
void Save(IniFile &ini) const;
|
||||
|
||||
int CheckMatch(uint16_t keycode, bool global_only = false) const;
|
||||
const Hotkey* GetHotkeyByNum(int num) const;
|
||||
|
||||
GlobalHotkeyHandlerFunc global_hotkey_handler;
|
||||
private:
|
||||
|
@ -64,5 +65,6 @@ void SaveHotkeysToConfig();
|
|||
|
||||
|
||||
void HandleGlobalHotkeys(char32_t key, uint16_t keycode);
|
||||
std::string KeycodeToShortString(uint16_t keycode);
|
||||
|
||||
#endif /* HOTKEYS_H */
|
||||
|
|
|
@ -77,10 +77,27 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
|
|||
HistoryData<ProducedHistory> history{}; ///< History of cargo produced and transported for this month and 24 previous months
|
||||
};
|
||||
|
||||
struct AcceptedHistory {
|
||||
uint16_t accepted = 0; /// Total accepted.
|
||||
uint16_t waiting = 0; /// Average waiting.
|
||||
};
|
||||
|
||||
struct AcceptedCargo {
|
||||
CargoType cargo = 0; ///< Cargo type
|
||||
uint16_t waiting = 0; ///< Amount of cargo waiting to processed
|
||||
uint32_t accumulated_waiting = 0; ///< Accumulated waiting total over the last month, used to calculate average.
|
||||
TimerGameEconomy::Date last_accepted{}; ///< Last day cargo was accepted by this industry
|
||||
std::unique_ptr<HistoryData<AcceptedHistory>> history{}; ///< History of accepted and waiting cargo.
|
||||
|
||||
/**
|
||||
* Get history data, creating it if necessary.
|
||||
* @return Accepted history data.
|
||||
*/
|
||||
inline HistoryData<AcceptedHistory> &GetOrCreateHistory()
|
||||
{
|
||||
if (this->history == nullptr) this->history = std::make_unique<HistoryData<AcceptedHistory>>();
|
||||
return *this->history;
|
||||
}
|
||||
};
|
||||
|
||||
using ProducedCargoes = std::vector<ProducedCargo>;
|
||||
|
@ -151,7 +168,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
|
|||
*/
|
||||
inline const AcceptedCargo &GetAccepted(size_t slot) const
|
||||
{
|
||||
static const AcceptedCargo empty{INVALID_CARGO, 0, {}};
|
||||
static const AcceptedCargo empty{INVALID_CARGO, 0, 0, {}, {}};
|
||||
return slot < this->accepted.size() ? this->accepted[slot] : empty;
|
||||
}
|
||||
|
||||
|
|
|
@ -1245,6 +1245,10 @@ void OnTick_Industry()
|
|||
|
||||
for (Industry *i : Industry::Iterate()) {
|
||||
ProduceIndustryGoods(i);
|
||||
|
||||
if ((TimerGameTick::counter + i->index) % Ticks::DAY_TICKS == 0) {
|
||||
for (auto &a : i->accepted) a.accumulated_waiting += a.waiting;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2505,6 +2509,14 @@ static void UpdateIndustryStatistics(Industry *i)
|
|||
RotateHistory(p.history);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &a : i->accepted) {
|
||||
if (!IsValidCargoType(a.cargo)) continue;
|
||||
if (a.history == nullptr) continue;
|
||||
|
||||
(*a.history)[THIS_MONTH].waiting = GetAndResetAccumulatedAverage<uint16_t>(a.accumulated_waiting);
|
||||
RotateHistory(*a.history);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -27,6 +27,4 @@ DEF_CMD_TRAIT(CMD_INDUSTRY_SET_EXCLUSIVITY, CmdIndustrySetExclusivity, CommandFl
|
|||
DEF_CMD_TRAIT(CMD_INDUSTRY_SET_TEXT, CmdIndustrySetText, CommandFlags({CommandFlag::Deity, CommandFlag::StrCtrl}), CMDT_OTHER_MANAGEMENT)
|
||||
DEF_CMD_TRAIT(CMD_INDUSTRY_SET_PRODUCTION, CmdIndustrySetProduction, CommandFlag::Deity, CMDT_OTHER_MANAGEMENT)
|
||||
|
||||
void CcBuildIndustry(Commands cmd, const CommandCost &result, TileIndex tile, IndustryType indtype, uint32_t, bool, uint32_t);
|
||||
|
||||
#endif /* INDUSTRY_CMD_H */
|
||||
|
|
|
@ -257,25 +257,6 @@ void SortIndustryTypes()
|
|||
std::sort(_sorted_industry_types.begin(), _sorted_industry_types.end(), IndustryTypeNameSorter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Command callback. In case of failure to build an industry, show an error message.
|
||||
* @param result Result of the command.
|
||||
* @param tile Tile where the industry is placed.
|
||||
* @param indtype Industry type.
|
||||
*/
|
||||
void CcBuildIndustry(Commands, const CommandCost &result, TileIndex tile, IndustryType indtype, uint32_t, bool, uint32_t)
|
||||
{
|
||||
if (result.Succeeded()) return;
|
||||
|
||||
if (indtype < NUM_INDUSTRYTYPES) {
|
||||
const IndustrySpec *indsp = GetIndustrySpec(indtype);
|
||||
if (indsp->enabled) {
|
||||
ShowErrorMessage(GetEncodedString(STR_ERROR_CAN_T_BUILD_HERE, indsp->name),
|
||||
GetEncodedString(result.GetErrorMessage()), WL_INFO, TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr NWidgetPart _nested_build_industry_widgets[] = {
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN),
|
||||
|
@ -745,7 +726,7 @@ public:
|
|||
AutoRestoreBackup backup_generating_world(_generating_world, true);
|
||||
AutoRestoreBackup backup_ignore_industry_restritions(_ignore_industry_restrictions, true);
|
||||
|
||||
Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, &CcBuildIndustry, tile, this->selected_type, layout_index, false, seed);
|
||||
Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, tile, this->selected_type, layout_index, false, seed);
|
||||
} else {
|
||||
success = Command<CMD_BUILD_INDUSTRY>::Post(STR_ERROR_CAN_T_CONSTRUCT_THIS_INDUSTRY, tile, this->selected_type, layout_index, false, seed);
|
||||
}
|
||||
|
@ -845,7 +826,7 @@ public:
|
|||
nvp->InitializeViewport(this, Industry::Get(window_number)->location.GetCenterTile(), ScaleZoomGUI(ZoomLevel::Industry));
|
||||
|
||||
const Industry *i = Industry::Get(window_number);
|
||||
if (!i->IsCargoProduced()) this->DisableWidget(WID_IV_GRAPH);
|
||||
if (!i->IsCargoProduced() && !i->IsCargoAccepted()) this->DisableWidget(WID_IV_GRAPH);
|
||||
|
||||
this->InvalidateData();
|
||||
}
|
||||
|
@ -1242,7 +1223,7 @@ static constexpr NWidgetPart _nested_industry_view_widgets[] = {
|
|||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_DISPLAY), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_INDUSTRY_DISPLAY_CHAIN, STR_INDUSTRY_DISPLAY_CHAIN_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_GRAPH), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_INDUSTRY_VIEW_PRODUCTION_GRAPH, STR_INDUSTRY_VIEW_PRODUCTION_GRAPH_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_IV_GRAPH), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_INDUSTRY_VIEW_CARGO_GRAPH, STR_INDUSTRY_VIEW_CARGO_GRAPH_TOOLTIP),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_CREAM),
|
||||
EndContainer(),
|
||||
};
|
||||
|
|
|
@ -634,9 +634,11 @@ STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL :{BLACK}Display
|
|||
STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO :{BLACK}Toggle graph of this cargo type
|
||||
STR_GRAPH_CARGO_PAYMENT_CARGO :{TINY_FONT}{BLACK}{STRING}
|
||||
|
||||
STR_GRAPH_INDUSTRY_PRODUCTION_CAPTION :{WHITE}{INDUSTRY} - Production History
|
||||
STR_GRAPH_INDUSTRY_CAPTION :{WHITE}{INDUSTRY} - Cargo History
|
||||
STR_GRAPH_INDUSTRY_RANGE_PRODUCED :Produced
|
||||
STR_GRAPH_INDUSTRY_RANGE_TRANSPORTED :Transported
|
||||
STR_GRAPH_INDUSTRY_RANGE_DELIVERED :Delivered
|
||||
STR_GRAPH_INDUSTRY_RANGE_WAITING :Waiting
|
||||
|
||||
STR_GRAPH_PERFORMANCE_DETAIL_TOOLTIP :{BLACK}Show detailed performance ratings
|
||||
|
||||
|
@ -4024,8 +4026,8 @@ STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE :{BLACK}Producti
|
|||
STR_INDUSTRY_VIEW_PRODUCTION_LAST_MINUTE_TITLE :{BLACK}Production last minute:
|
||||
STR_INDUSTRY_VIEW_TRANSPORTED :{YELLOW}{CARGO_LONG}{RAW_STRING}{BLACK} ({COMMA}% transported)
|
||||
STR_INDUSTRY_VIEW_LOCATION_TOOLTIP :{BLACK}Centre the main view on industry location. Ctrl+Click to open a new viewport on industry location
|
||||
STR_INDUSTRY_VIEW_PRODUCTION_GRAPH :{BLACK}Production Graph
|
||||
STR_INDUSTRY_VIEW_PRODUCTION_GRAPH_TOOLTIP :{BLACK}Shows the graph of industry production history
|
||||
STR_INDUSTRY_VIEW_CARGO_GRAPH :{BLACK}Cargo Graph
|
||||
STR_INDUSTRY_VIEW_CARGO_GRAPH_TOOLTIP :{BLACK}Shows the graph of industry cargo history
|
||||
STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}Production level: {YELLOW}{COMMA}%
|
||||
STR_INDUSTRY_VIEW_INDUSTRY_ANNOUNCED_CLOSURE :{YELLOW}The industry has announced imminent closure!
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#define HISTORY_FUNC_HPP
|
||||
|
||||
#include "../core/bitmath_func.hpp"
|
||||
#include "../core/math_func.hpp"
|
||||
#include "../timer/timer_game_economy.h"
|
||||
#include "history_type.hpp"
|
||||
|
||||
/**
|
||||
|
@ -34,6 +36,19 @@ void RotateHistory(HistoryData<T> &history)
|
|||
history[THIS_MONTH] = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an average value for the previous month, as reset for the next month.
|
||||
* @param total Accrued total to average. Will be reset to zero.
|
||||
* @return Average value for the month.
|
||||
*/
|
||||
template <typename T, typename Taccrued>
|
||||
T GetAndResetAccumulatedAverage(Taccrued &total)
|
||||
{
|
||||
T result = ClampTo<T>(total / std::max(1U, TimerGameEconomy::days_since_last_month));
|
||||
total = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill some data with historical data.
|
||||
* @param history Historical data to fill from.
|
||||
|
@ -53,4 +68,21 @@ void FillFromHistory(const HistoryData<T> &history, ValidHistoryMask valid_histo
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill some data with empty records.
|
||||
* @param valid_history Mask of valid history records.
|
||||
* @param fillers Fillers to fill with history data.
|
||||
*/
|
||||
template <uint N, typename... Tfillers>
|
||||
void FillFromEmpty(ValidHistoryMask valid_history, Tfillers... fillers)
|
||||
{
|
||||
for (uint i = 0; i != N; ++i) {
|
||||
if (HasBit(valid_history, N - i)) {
|
||||
(fillers.MakeZero(i), ...);
|
||||
} else {
|
||||
(fillers.MakeInvalid(i), ...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HISTORY_FUNC_HPP */
|
||||
|
|
|
@ -79,7 +79,6 @@ static constexpr auto _callback_tuple = std::make_tuple(
|
|||
&CcCreateGroup,
|
||||
&CcFoundRandomTown,
|
||||
&CcRoadStop,
|
||||
&CcBuildIndustry,
|
||||
&CcStartStopVehicle,
|
||||
&CcGame,
|
||||
&CcAddVehicleNewGroup
|
||||
|
|
|
@ -469,14 +469,14 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
|
|||
|
||||
/** Hotkeys for order window. */
|
||||
enum OrderHotKeys : int32_t {
|
||||
OHK_SKIP,
|
||||
OHK_DELETE,
|
||||
OHK_GOTO,
|
||||
OHK_NONSTOP,
|
||||
OHK_FULLLOAD,
|
||||
OHK_UNLOAD,
|
||||
OHK_NEAREST_DEPOT,
|
||||
OHK_ALWAYS_SERVICE,
|
||||
OHK_SKIP = WID_O_SKIP,
|
||||
OHK_DELETE = WID_O_DELETE,
|
||||
OHK_GOTO = WID_O_GOTO,
|
||||
OHK_NONSTOP = WID_O_NON_STOP,
|
||||
OHK_FULLLOAD = WID_O_FULL_LOAD,
|
||||
OHK_UNLOAD = WID_O_UNLOAD,
|
||||
OHK_ALWAYS_SERVICE = WID_O_DEPOT_ACTION,
|
||||
OHK_NEAREST_DEPOT = WID_O_END,
|
||||
OHK_TRANSFER,
|
||||
OHK_NO_UNLOAD,
|
||||
OHK_NO_LOAD,
|
||||
|
|
|
@ -19,12 +19,50 @@
|
|||
|
||||
static OldPersistentStorage _old_ind_persistent_storage;
|
||||
|
||||
class SlIndustryAcceptedHistory : public DefaultSaveLoadHandler<SlIndustryAcceptedHistory, Industry::AcceptedCargo> {
|
||||
public:
|
||||
static inline const SaveLoad description[] = {
|
||||
SLE_VAR(Industry::AcceptedHistory, accepted, SLE_UINT16),
|
||||
SLE_VAR(Industry::AcceptedHistory, waiting, SLE_UINT16),
|
||||
};
|
||||
static inline const SaveLoadCompatTable compat_description = _industry_produced_history_sl_compat;
|
||||
|
||||
void Save(Industry::AcceptedCargo *a) const override
|
||||
{
|
||||
if (!IsValidCargoType(a->cargo) || a->history == nullptr) {
|
||||
/* Don't save any history if cargo slot isn't used. */
|
||||
SlSetStructListLength(0);
|
||||
return;
|
||||
}
|
||||
|
||||
SlSetStructListLength(a->history->size());
|
||||
|
||||
for (auto &h : *a->history) {
|
||||
SlObject(&h, this->GetDescription());
|
||||
}
|
||||
}
|
||||
|
||||
void Load(Industry::AcceptedCargo *a) const override
|
||||
{
|
||||
size_t len = SlGetStructListLength(UINT32_MAX);
|
||||
if (len == 0) return;
|
||||
|
||||
auto &history = a->GetOrCreateHistory();
|
||||
for (auto &h : history) {
|
||||
if (--len > history.size()) break; // unsigned so wraps after hitting zero.
|
||||
SlObject(&h, this->GetDescription());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SlIndustryAccepted : public VectorSaveLoadHandler<SlIndustryAccepted, Industry, Industry::AcceptedCargo, INDUSTRY_NUM_INPUTS> {
|
||||
public:
|
||||
static inline const SaveLoad description[] = {
|
||||
SLE_VAR(Industry::AcceptedCargo, cargo, SLE_UINT8),
|
||||
SLE_VAR(Industry::AcceptedCargo, waiting, SLE_UINT16),
|
||||
SLE_VAR(Industry::AcceptedCargo, last_accepted, SLE_INT32),
|
||||
SLE_CONDVAR(Industry::AcceptedCargo, accumulated_waiting, SLE_UINT32, SLV_INDUSTRY_ACCEPTED_HISTORY, SL_MAX_VERSION),
|
||||
SLEG_CONDSTRUCTLIST("history", SlIndustryAcceptedHistory, SLV_INDUSTRY_ACCEPTED_HISTORY, SL_MAX_VERSION),
|
||||
};
|
||||
static inline const SaveLoadCompatTable compat_description = _industry_accepts_sl_compat;
|
||||
|
||||
|
|
|
@ -858,7 +858,7 @@ static bool LoadOldIndustry(LoadgameState &ls, int num)
|
|||
|
||||
if (i->location.tile != 0) {
|
||||
/* Copy data from old fixed arrays to industry. */
|
||||
std::copy(std::begin(_old_accepted), std::end(_old_accepted), std::back_inserter(i->accepted));
|
||||
std::move(std::begin(_old_accepted), std::end(_old_accepted), std::back_inserter(i->accepted));
|
||||
std::copy(std::begin(_old_produced), std::end(_old_produced), std::back_inserter(i->produced));
|
||||
|
||||
i->town = RemapTown(i->location.tile);
|
||||
|
|
|
@ -405,6 +405,7 @@ enum SaveLoadVersion : uint16_t {
|
|||
|
||||
SLV_FACE_STYLES, ///< 355 PR#14319 Addition of face styles, replacing gender and ethnicity.
|
||||
SLV_INDUSTRY_NUM_VALID_HISTORY, ///< 356 PR#14416 Store number of valid history records for industries.
|
||||
SLV_INDUSTRY_ACCEPTED_HISTORY, ///< 357 PR#14321 Add per-industry history of cargo delivered and waiting.
|
||||
|
||||
SL_MAX_VERSION, ///< Highest possible saveload version
|
||||
};
|
||||
|
|
|
@ -37,6 +37,7 @@ TimerGameEconomy::Year TimerGameEconomy::year = {};
|
|||
TimerGameEconomy::Month TimerGameEconomy::month = {};
|
||||
TimerGameEconomy::Date TimerGameEconomy::date = {};
|
||||
TimerGameEconomy::DateFract TimerGameEconomy::date_fract = {};
|
||||
uint TimerGameEconomy::days_since_last_month = {};
|
||||
|
||||
/**
|
||||
* Converts a Date to a Year, Month & Day.
|
||||
|
@ -133,6 +134,7 @@ bool TimerManager<TimerGameEconomy>::Elapsed([[maybe_unused]] TimerGameEconomy::
|
|||
|
||||
/* increase day counter */
|
||||
TimerGameEconomy::date++;
|
||||
++TimerGameEconomy::days_since_last_month;
|
||||
|
||||
TimerGameEconomy::YearMonthDay ymd = TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::date);
|
||||
|
||||
|
@ -177,6 +179,8 @@ bool TimerManager<TimerGameEconomy>::Elapsed([[maybe_unused]] TimerGameEconomy::
|
|||
}
|
||||
}
|
||||
|
||||
if (new_month) TimerGameEconomy::days_since_last_month = 0;
|
||||
|
||||
/* check if we reached the maximum year, decrement dates by a year */
|
||||
if (TimerGameEconomy::year == EconomyTime::MAX_YEAR + 1) {
|
||||
TimerGameEconomy::year--;
|
||||
|
|
|
@ -37,6 +37,8 @@ public:
|
|||
static Date date; ///< Current date in days (day counter).
|
||||
static DateFract date_fract; ///< Fractional part of the day.
|
||||
|
||||
static uint days_since_last_month; ///< Number of days that have elapsed since the last month.
|
||||
|
||||
static YearMonthDay ConvertDateToYMD(Date date);
|
||||
static Date ConvertYMDToDate(Year year, Month month, Day day);
|
||||
static void SetDate(Date date, DateFract fract);
|
||||
|
|
|
@ -2305,29 +2305,29 @@ static ToolbarButtonProc * const _scen_toolbar_button_procs[] = {
|
|||
};
|
||||
|
||||
enum MainToolbarEditorHotkeys : int32_t {
|
||||
MTEHK_PAUSE,
|
||||
MTEHK_FASTFORWARD,
|
||||
MTEHK_SETTINGS,
|
||||
MTEHK_SAVEGAME,
|
||||
MTEHK_GENLAND,
|
||||
MTEHK_GENTOWN,
|
||||
MTEHK_GENINDUSTRY,
|
||||
MTEHK_BUILD_ROAD,
|
||||
MTEHK_BUILD_TRAM,
|
||||
MTEHK_BUILD_DOCKS,
|
||||
MTEHK_BUILD_TREES,
|
||||
MTEHK_SIGN,
|
||||
MTEHK_MUSIC,
|
||||
MTEHK_LANDINFO,
|
||||
MTEHK_PAUSE = WID_TE_PAUSE,
|
||||
MTEHK_FASTFORWARD = WID_TE_FAST_FORWARD,
|
||||
MTEHK_SETTINGS = WID_TE_SETTINGS,
|
||||
MTEHK_SAVEGAME = WID_TE_SAVE,
|
||||
MTEHK_GENLAND = WID_TE_LAND_GENERATE,
|
||||
MTEHK_GENTOWN = WID_TE_TOWN_GENERATE,
|
||||
MTEHK_GENINDUSTRY = WID_TE_INDUSTRY,
|
||||
MTEHK_BUILD_ROAD = WID_TE_ROADS,
|
||||
MTEHK_BUILD_TRAM = WID_TE_TRAMS,
|
||||
MTEHK_BUILD_DOCKS = WID_TE_WATER,
|
||||
MTEHK_BUILD_TREES = WID_TE_TREES,
|
||||
MTEHK_SIGN = WID_TE_SIGNS,
|
||||
MTEHK_MUSIC = WID_TE_MUSIC_SOUND,
|
||||
MTEHK_LANDINFO = WID_TE_HELP,
|
||||
MTEHK_ZOOM_IN = WID_TE_ZOOM_IN,
|
||||
MTEHK_ZOOM_OUT = WID_TE_ZOOM_OUT,
|
||||
MTEHK_SMALLMAP = WID_TE_SMALL_MAP,
|
||||
MTEHK_TERRAFORM = WID_TE_END,
|
||||
MTEHK_EXTRA_VIEWPORT,
|
||||
MTEHK_SMALL_SCREENSHOT,
|
||||
MTEHK_ZOOMEDIN_SCREENSHOT,
|
||||
MTEHK_DEFAULTZOOM_SCREENSHOT,
|
||||
MTEHK_GIANT_SCREENSHOT,
|
||||
MTEHK_ZOOM_IN,
|
||||
MTEHK_ZOOM_OUT,
|
||||
MTEHK_TERRAFORM,
|
||||
MTEHK_SMALLMAP,
|
||||
MTEHK_EXTRA_VIEWPORT,
|
||||
};
|
||||
|
||||
struct ScenarioEditorToolbarWindow : Window {
|
||||
|
|
|
@ -10,47 +10,52 @@
|
|||
#ifndef TOOLBAR_GUI_H
|
||||
#define TOOLBAR_GUI_H
|
||||
|
||||
#include "widgets/toolbar_widget.h"
|
||||
|
||||
// TODO: Replace all instances of "MTHK_blah" with "WID_blah" where we can,
|
||||
// then redefine this as AdditionalMainToolbarHotkeys, or something like that.
|
||||
enum MainToolbarHotkeys : int32_t {
|
||||
MTHK_PAUSE,
|
||||
MTHK_FASTFORWARD,
|
||||
MTHK_SETTINGS,
|
||||
MTHK_SAVEGAME,
|
||||
MTHK_LOADGAME,
|
||||
MTHK_SMALLMAP,
|
||||
MTHK_TOWNDIRECTORY,
|
||||
MTHK_SUBSIDIES,
|
||||
MTHK_STATIONS,
|
||||
MTHK_FINANCES,
|
||||
MTHK_COMPANIES,
|
||||
MTHK_STORY,
|
||||
MTHK_GOAL,
|
||||
MTHK_GRAPHS,
|
||||
MTHK_LEAGUE,
|
||||
MTHK_INDUSTRIES,
|
||||
MTHK_TRAIN_LIST,
|
||||
MTHK_ROADVEH_LIST,
|
||||
MTHK_SHIP_LIST,
|
||||
MTHK_AIRCRAFT_LIST,
|
||||
MTHK_ZOOM_IN,
|
||||
MTHK_ZOOM_OUT,
|
||||
MTHK_BUILD_RAIL,
|
||||
MTHK_BUILD_ROAD,
|
||||
MTHK_BUILD_TRAM,
|
||||
MTHK_BUILD_DOCKS,
|
||||
MTHK_BUILD_AIRPORT,
|
||||
MTHK_PAUSE = WID_TN_PAUSE,
|
||||
MTHK_FASTFORWARD = WID_TN_FAST_FORWARD,
|
||||
MTHK_SETTINGS = WID_TN_SETTINGS,
|
||||
MTHK_SAVEGAME = WID_TN_SAVE,
|
||||
MTHK_SMALLMAP = WID_TN_SMALL_MAP,
|
||||
MTHK_TOWNDIRECTORY = WID_TN_TOWNS,
|
||||
MTHK_SUBSIDIES = WID_TN_SUBSIDIES,
|
||||
MTHK_STATIONS = WID_TN_STATIONS,
|
||||
MTHK_FINANCES = WID_TN_FINANCES,
|
||||
MTHK_COMPANIES = WID_TN_COMPANIES,
|
||||
MTHK_STORY = WID_TN_STORY,
|
||||
MTHK_GOAL = WID_TN_GOAL,
|
||||
MTHK_GRAPHS = WID_TN_GRAPHS,
|
||||
MTHK_LEAGUE = WID_TN_LEAGUE,
|
||||
MTHK_INDUSTRIES = WID_TN_INDUSTRIES,
|
||||
MTHK_TRAIN_LIST = WID_TN_TRAINS,
|
||||
MTHK_ROADVEH_LIST = WID_TN_ROADVEHS,
|
||||
MTHK_SHIP_LIST = WID_TN_SHIPS,
|
||||
MTHK_AIRCRAFT_LIST = WID_TN_AIRCRAFT,
|
||||
MTHK_ZOOM_IN = WID_TN_ZOOM_IN,
|
||||
MTHK_ZOOM_OUT = WID_TN_ZOOM_OUT,
|
||||
MTHK_BUILD_RAIL = WID_TN_RAILS,
|
||||
MTHK_BUILD_ROAD = WID_TN_ROADS,
|
||||
MTHK_BUILD_TRAM = WID_TN_TRAMS,
|
||||
MTHK_BUILD_DOCKS = WID_TN_WATER,
|
||||
MTHK_BUILD_AIRPORT = WID_TN_AIR,
|
||||
MTHK_TERRAFORM = WID_TN_LANDSCAPE,
|
||||
MTHK_MUSIC = WID_TN_MUSIC_SOUND,
|
||||
MTHK_LANDINFO = WID_TN_HELP,
|
||||
// Hotkeys without associated widgets.
|
||||
MTHK_LOADGAME = WID_TN_END,
|
||||
MTHK_BUILD_TREES,
|
||||
MTHK_MUSIC,
|
||||
MTHK_LANDINFO,
|
||||
MTHK_SCRIPT_DEBUG,
|
||||
MTHK_SMALL_SCREENSHOT,
|
||||
MTHK_ZOOMEDIN_SCREENSHOT,
|
||||
MTHK_DEFAULTZOOM_SCREENSHOT,
|
||||
MTHK_GIANT_SCREENSHOT,
|
||||
MTHK_CHEATS,
|
||||
MTHK_TERRAFORM,
|
||||
MTHK_EXTRA_VIEWPORT,
|
||||
MTHK_CLIENT_LIST,
|
||||
MTHK_SIGN_LIST
|
||||
MTHK_SIGN_LIST,
|
||||
};
|
||||
|
||||
void AllocateToolbar();
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
|
||||
#include "safeguards.h"
|
||||
|
||||
#include "widgets/toolbar_widget.h"
|
||||
#include "hotkeys.h"
|
||||
|
||||
WidgetDimensions WidgetDimensions::scaled = {};
|
||||
|
||||
static std::string GetStringForWidget(const Window *w, const NWidgetCore *nwid, bool secondary = false)
|
||||
|
@ -3108,6 +3111,87 @@ void NWidgetLeaf::Draw(const Window *w)
|
|||
}
|
||||
|
||||
DrawOutline(w, this);
|
||||
|
||||
// TODO: Draw hints only if Alt is being held.
|
||||
// Don't draw hotkey hints on disabled widgets or editboxes with focus.
|
||||
if (!(this->IsDisabled() || (this->type == WWT_EDITBOX && w->nested_focus == this))) {
|
||||
this->DrawHotkeyHint(w);
|
||||
}
|
||||
}
|
||||
|
||||
void NWidgetLeaf::DrawHotkeyHint(const Window* w) {
|
||||
// TODO: Use global hotkey for autoroads for rail, road, tram, etc. if
|
||||
// they have been set.
|
||||
|
||||
const uint o = ScaleGUITrad(1);
|
||||
Rect r = this->GetCurrentRect().Shrink(o);
|
||||
std::string hint;
|
||||
uint16_t keycode = 0;
|
||||
|
||||
if (w->window_desc.cls == WC_MAIN_TOOLBAR && this->index == WID_TN_FAST_FORWARD) {
|
||||
// Special-case hint text for Fast-forwards because it's not really a hotkey
|
||||
hint = "Tab";
|
||||
} else if (w->window_desc.hotkeys != nullptr) {
|
||||
// Widget IDs can coincidentally overlap with hotkey IDs if
|
||||
// they aren't assigned properly. Avoid this.
|
||||
auto hk = w->window_desc.hotkeys->GetHotkeyByNum(this->index);
|
||||
if (hk != nullptr && hk->keycodes.size()) {
|
||||
// Find the "best" of the available keycodes
|
||||
// TODO: maybe don't repeat this work so much
|
||||
keycode = *(hk->keycodes.begin());
|
||||
hint = KeycodeToShortString(keycode);
|
||||
for (auto k : hk->keycodes) {
|
||||
if (!(keycode & WKC_GLOBAL_HOTKEY) && (k & WKC_GLOBAL_HOTKEY)) {
|
||||
keycode = k;
|
||||
} else {
|
||||
auto h = KeycodeToShortString(k);
|
||||
if (hint.length() > h.length()) {
|
||||
keycode = k;
|
||||
hint = h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to a string
|
||||
// TODO: Glyphs for Shift, Alt, etc. Colour for global.
|
||||
// - On screen keyboard has a sprite for shift.
|
||||
hint = KeycodeToShortString(keycode);
|
||||
}
|
||||
}
|
||||
|
||||
if (hint.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose the font-size
|
||||
auto fontsize = FS_SMALL;
|
||||
// It's better to have a single consistent size.
|
||||
/*auto availableSize = Dimension(r.right - r.left, r.bottom - r.top);*/
|
||||
/*auto desiredSize = GetStringBoundingBox(hint, FS_NORMAL);*/
|
||||
/*if (availableSize < desiredSize) {*/
|
||||
/* fontsize = FS_SMALL;*/
|
||||
/*}*/
|
||||
|
||||
// Choose hint colour
|
||||
auto colour = TC_WHITE;
|
||||
if (keycode & WKC_GLOBAL_HOTKEY) {
|
||||
colour = TC_LIGHT_BLUE;
|
||||
}
|
||||
|
||||
auto alignment = SA_LEFT | SA_BOTTOM;
|
||||
|
||||
// Draw a slightly shoddy outline
|
||||
DrawStringMultiLine(r.Translate( o, o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize);
|
||||
DrawStringMultiLine(r.Translate( o, -o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize);
|
||||
DrawStringMultiLine(r.Translate(-o, o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize);
|
||||
DrawStringMultiLine(r.Translate(-o, -o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize);
|
||||
DrawStringMultiLine(r.Translate( o, 0), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize);
|
||||
DrawStringMultiLine(r.Translate(-o, 0), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize);
|
||||
DrawStringMultiLine(r.Translate( 0, o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize);
|
||||
DrawStringMultiLine(r.Translate( 0, -o), hint, TC_BLACK | TC_NO_SHADE, alignment, false, fontsize);
|
||||
|
||||
// Draw the hint text
|
||||
DrawStringMultiLine(r, hint, colour | TC_NO_SHADE, alignment, false, fontsize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -922,6 +922,7 @@ public:
|
|||
|
||||
void SetupSmallestSize(Window *w) override;
|
||||
void Draw(const Window *w) override;
|
||||
void DrawHotkeyHint(const Window* window);
|
||||
|
||||
bool ButtonHit(const Point &pt);
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ enum OrderWidgets : WidgetID {
|
|||
WID_O_SEL_TOP_ROW, ///< #NWID_SELECTION widget for the top row of the 'your non-trains' order window.
|
||||
WID_O_SEL_BOTTOM_MIDDLE, ///< #NWID_SELECTION widget for the middle part of the bottom row of the 'your train' order window.
|
||||
WID_O_SHARED_ORDER_LIST, ///< Open list of shared vehicles.
|
||||
WID_O_END, ///< End of the list of widgets.
|
||||
};
|
||||
|
||||
#endif /* WIDGETS_ORDER_WIDGET_H */
|
||||
|
|
|
@ -73,6 +73,7 @@ enum ToolbarEditorWidgets : WidgetID {
|
|||
WID_TE_MUSIC_SOUND, ///< Music/sound configuration menu.
|
||||
WID_TE_HELP, ///< Help menu.
|
||||
WID_TE_SWITCH_BAR, ///< Only available when toolbar has been split to switch between different subsets.
|
||||
WID_TE_END,
|
||||
};
|
||||
|
||||
#endif /* WIDGETS_TOOLBAR_WIDGET_H */
|
||||
|
|
Loading…
Reference in New Issue