mirror of https://github.com/OpenTTD/OpenTTD
Compare commits
12 Commits
b24ceb27f5
...
894e6bb087
Author | SHA1 | Date |
---|---|---|
|
894e6bb087 | |
|
c677c40d53 | |
|
56942a15c7 | |
|
5eeda026a4 | |
|
edc5b8ea1f | |
|
a8650c6b06 | |
|
7bb4940ebd | |
|
b8e56cd05d | |
|
df5237e721 | |
|
03f5f7145f | |
|
0dc40877fd | |
|
03672ed8eb |
|
@ -454,6 +454,7 @@ add_files(
|
|||
spritecache.cpp
|
||||
spritecache.h
|
||||
spritecache_internal.h
|
||||
spritecache_type.h
|
||||
station.cpp
|
||||
station_base.h
|
||||
station_cmd.cpp
|
||||
|
|
|
@ -83,7 +83,7 @@ void AIInstance::Died()
|
|||
|
||||
void AIInstance::LoadDummyScript()
|
||||
{
|
||||
ScriptAllocatorScope alloc_scope(this->engine);
|
||||
ScriptAllocatorScope alloc_scope(this->engine.get());
|
||||
Script_CreateDummy(this->engine->GetVM(), STR_ERROR_AI_NO_AI_FOUND, "AI");
|
||||
}
|
||||
|
||||
|
|
|
@ -141,6 +141,8 @@ void SetLocalCompany(CompanyID new_company)
|
|||
MarkWholeScreenDirty();
|
||||
InvalidateWindowClassesData(WC_SIGN_LIST, -1);
|
||||
InvalidateWindowClassesData(WC_GOALS_LIST);
|
||||
InvalidateWindowClassesData(WC_COMPANY_COLOUR, -1);
|
||||
ResetVehicleColourMap();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2369,7 +2369,7 @@ static bool ConFont(std::span<std::string_view> argv)
|
|||
FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
|
||||
/* Make sure all non sprite fonts are loaded. */
|
||||
if (!setting->font.empty() && !fc->HasParent()) {
|
||||
InitFontCache(fs == FS_MONO);
|
||||
InitFontCache(fs);
|
||||
fc = FontCache::Get(fs);
|
||||
}
|
||||
IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -84,7 +84,7 @@ struct EnginePreviewWindow : Window {
|
|||
|
||||
/* Get size of engine sprite, on loan from depot_gui.cpp */
|
||||
EngineID engine = static_cast<EngineID>(this->window_number);
|
||||
EngineImageType image_type = EIT_PURCHASE;
|
||||
EngineImageType image_type = EIT_PREVIEW;
|
||||
uint x, y;
|
||||
int x_offs, y_offs;
|
||||
|
||||
|
|
|
@ -126,10 +126,10 @@ void SetFont(FontSize fontsize, const std::string &font, uint size)
|
|||
CheckForMissingGlyphs();
|
||||
_fcsettings = std::move(backup);
|
||||
} else {
|
||||
InitFontCache(true);
|
||||
InitFontCache(fontsize);
|
||||
}
|
||||
|
||||
LoadStringWidthTable(fontsize == FS_MONO);
|
||||
LoadStringWidthTable(fontsize);
|
||||
UpdateAllVirtCoords();
|
||||
ReInitAllWindows(true);
|
||||
|
||||
|
@ -213,15 +213,13 @@ std::string GetFontCacheFontName(FontSize fs)
|
|||
|
||||
/**
|
||||
* (Re)initialize the font cache related things, i.e. load the non-sprite fonts.
|
||||
* @param monospace Whether to initialise the monospace or regular fonts.
|
||||
* @param fontsizes Font sizes to be initialised.
|
||||
*/
|
||||
void InitFontCache(bool monospace)
|
||||
void InitFontCache(FontSizes fontsizes)
|
||||
{
|
||||
FontCache::InitializeFontCaches();
|
||||
|
||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||
if (monospace != (fs == FS_MONO)) continue;
|
||||
|
||||
for (FontSize fs : fontsizes) {
|
||||
FontCache *fc = FontCache::Get(fs);
|
||||
if (fc->HasParent()) delete fc;
|
||||
|
||||
|
|
|
@ -167,9 +167,9 @@ inline void InitializeUnicodeGlyphMap()
|
|||
}
|
||||
}
|
||||
|
||||
inline void ClearFontCache()
|
||||
inline void ClearFontCache(FontSizes fontsizes)
|
||||
{
|
||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||
for (FontSize fs : fontsizes) {
|
||||
FontCache::Get(fs)->ClearFontCache();
|
||||
}
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ inline FontCacheSubSetting *GetFontCacheSubSetting(FontSize fs)
|
|||
|
||||
uint GetFontCacheFontSize(FontSize fs);
|
||||
std::string GetFontCacheFontName(FontSize fs);
|
||||
void InitFontCache(bool monospace);
|
||||
void InitFontCache(FontSizes fontsizes);
|
||||
void UninitFontCache();
|
||||
|
||||
bool GetFontAAState();
|
||||
|
|
29
src/gfx.cpp
29
src/gfx.cpp
|
@ -8,6 +8,7 @@
|
|||
/** @file gfx.cpp Handling of drawing text and other gfx related stuff. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "gfx_func.h"
|
||||
#include "gfx_layout.h"
|
||||
#include "progress.h"
|
||||
#include "zoom_func.h"
|
||||
|
@ -579,7 +580,7 @@ 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) {
|
||||
auto draw_line = [&](const ParagraphLayouter::Line &line, bool do_shadow, int left, int min_x, int max_x, bool truncation, TextColour &last_colour) {
|
||||
const DrawPixelInfo *dpi = _cur_dpi;
|
||||
int dpi_left = dpi->left;
|
||||
int dpi_right = dpi->left + dpi->width - 1;
|
||||
|
@ -592,10 +593,15 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
|||
|
||||
FontCache *fc = f->fc;
|
||||
TextColour colour = f->colour;
|
||||
if (colour == TC_INVALID || HasFlag(default_colour, TC_FORCED)) colour = default_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;
|
||||
}
|
||||
bool colour_has_shadow = (colour & TC_NO_SHADE) == 0 && colour != TC_BLACK;
|
||||
SetColourRemap(do_shadow ? TC_BLACK : colour); // the last run also sets the colour for the truncation dots
|
||||
if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue;
|
||||
SetColourRemap(do_shadow ? TC_BLACK : colour);
|
||||
|
||||
for (int i = 0; i < run.GetGlyphCount(); i++) {
|
||||
GlyphID glyph = glyphs[i];
|
||||
|
@ -623,11 +629,12 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
|||
|
||||
/* Draw shadow, then foreground */
|
||||
for (bool do_shadow : {true, false}) {
|
||||
draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation);
|
||||
TextColour last_colour = default_colour;
|
||||
draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation, last_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);
|
||||
draw_line(*truncation_layout->front(), do_shadow, x, INT32_MIN, INT32_MAX, false, last_colour);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1240,14 +1247,14 @@ static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode,
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize _stringwidth_table cache
|
||||
* @param monospace Whether to load the monospace cache or the normal fonts.
|
||||
* Initialize _stringwidth_table cache for the specified font sizes.
|
||||
* @param fontsizes Font sizes to initialise.
|
||||
*/
|
||||
void LoadStringWidthTable(bool monospace)
|
||||
void LoadStringWidthTable(FontSizes fontsizes)
|
||||
{
|
||||
ClearFontCache();
|
||||
ClearFontCache(fontsizes);
|
||||
|
||||
for (FontSize fs = monospace ? FS_MONO : FS_BEGIN; fs < (monospace ? FS_END : FS_MONO); fs++) {
|
||||
for (FontSize fs : fontsizes) {
|
||||
for (uint i = 0; i != 224; i++) {
|
||||
_stringwidth_table[fs][i] = GetGlyphWidth(fs, i + 32);
|
||||
}
|
||||
|
@ -1812,7 +1819,7 @@ bool AdjustGUIZoom(bool automatic)
|
|||
if (old_font_zoom != _font_zoom) {
|
||||
GfxClearFontSpriteCache();
|
||||
}
|
||||
ClearFontCache();
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
LoadStringWidthTable();
|
||||
|
||||
SetupWidgetDimensions();
|
||||
|
|
|
@ -149,7 +149,7 @@ int GetStringHeight(StringID str, int maxw);
|
|||
int GetStringLineCount(std::string_view str, int maxw);
|
||||
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion);
|
||||
Dimension GetStringMultiLineBoundingBox(std::string_view str, const Dimension &suggestion, FontSize fontsize = FS_NORMAL);
|
||||
void LoadStringWidthTable(bool monospace = false);
|
||||
void LoadStringWidthTable(FontSizes fontsizes = FONTSIZES_REQUIRED);
|
||||
|
||||
void DrawDirtyBlocks();
|
||||
void AddDirtyBlock(int left, int top, int right, int bottom);
|
||||
|
|
|
@ -258,6 +258,13 @@ enum FontSize : uint8_t {
|
|||
};
|
||||
DECLARE_INCREMENT_DECREMENT_OPERATORS(FontSize)
|
||||
|
||||
using FontSizes = EnumBitSet<FontSize, uint8_t>;
|
||||
|
||||
/** Mask of all possible font sizes. */
|
||||
constexpr FontSizes FONTSIZES_ALL{FS_NORMAL, FS_SMALL, FS_LARGE, FS_MONO};
|
||||
/** Mask of font sizes required to be present. */
|
||||
constexpr FontSizes FONTSIZES_REQUIRED{FS_NORMAL, FS_SMALL, FS_LARGE};
|
||||
|
||||
inline std::string_view FontSizeToName(FontSize fs)
|
||||
{
|
||||
static const std::string_view SIZE_TO_NAME[] = { "medium", "small", "large", "mono" };
|
||||
|
|
|
@ -245,7 +245,7 @@ static void RealChangeBlitter(std::string_view repl_blitter)
|
|||
|
||||
/* Clear caches that might have sprites for another blitter. */
|
||||
VideoDriver::GetInstance()->ClearSystemSprites();
|
||||
ClearFontCache();
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
GfxClearSpriteCache();
|
||||
ReInitAllWindows(false);
|
||||
}
|
||||
|
@ -326,7 +326,7 @@ void CheckBlitter()
|
|||
{
|
||||
if (!SwitchNewGRFBlitter()) return;
|
||||
|
||||
ClearFontCache();
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
GfxClearSpriteCache();
|
||||
ReInitAllWindows(false);
|
||||
}
|
||||
|
@ -338,7 +338,7 @@ void GfxLoadSprites()
|
|||
|
||||
SwitchNewGRFBlitter();
|
||||
VideoDriver::GetInstance()->ClearSystemSprites();
|
||||
ClearFontCache();
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
GfxInitSpriteMem();
|
||||
LoadSpriteTables();
|
||||
GfxInitPalettes();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
/** @file graph_gui.cpp GUI that shows performance graphs. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include <ranges>
|
||||
#include "misc/history_func.hpp"
|
||||
#include "graph_gui.h"
|
||||
#include "window_gui.h"
|
||||
|
@ -174,6 +175,7 @@ protected:
|
|||
static const int GRAPH_PAYMENT_RATE_STEPS = 20; ///< Number of steps on Payment rate graph.
|
||||
static const int PAYMENT_GRAPH_X_STEP_DAYS = 10; ///< X-axis step label for cargo payment rates "Days in transit".
|
||||
static const int PAYMENT_GRAPH_X_STEP_SECONDS = 20; ///< X-axis step label for cargo payment rates "Seconds in transit".
|
||||
static const int ECONOMY_YEAR_MINUTES = 12; ///< Minutes per economic year.
|
||||
static const int ECONOMY_QUARTER_MINUTES = 3; ///< Minutes per economic quarter.
|
||||
static const int ECONOMY_MONTH_MINUTES = 1; ///< Minutes per economic month.
|
||||
|
||||
|
@ -182,8 +184,28 @@ 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.
|
||||
struct GraphScale {
|
||||
StringID label = STR_NULL;
|
||||
uint8_t month_increment = 0;
|
||||
int16_t x_values_increment = 0;
|
||||
const HistoryRange *history_range = nullptr;
|
||||
};
|
||||
|
||||
static inline constexpr GraphScale MONTHLY_SCALE_WALLCLOCK[] = {
|
||||
{STR_GRAPH_LAST_24_MINUTES_TIME_LABEL, HISTORY_MONTH.total_division, ECONOMY_MONTH_MINUTES, &HISTORY_MONTH},
|
||||
{STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, HISTORY_QUARTER.total_division, ECONOMY_QUARTER_MINUTES, &HISTORY_QUARTER},
|
||||
{STR_GRAPH_LAST_288_MINUTES_TIME_LABEL, HISTORY_YEAR.total_division, ECONOMY_YEAR_MINUTES, &HISTORY_YEAR},
|
||||
};
|
||||
|
||||
static inline constexpr GraphScale MONTHLY_SCALE_CALENDAR[] = {
|
||||
{STR_GRAPH_LAST_24_MONTHS, HISTORY_MONTH.total_division, ECONOMY_MONTH_MINUTES, &HISTORY_MONTH},
|
||||
{STR_GRAPH_LAST_24_QUARTERS, HISTORY_QUARTER.total_division, ECONOMY_QUARTER_MINUTES, &HISTORY_QUARTER},
|
||||
{STR_GRAPH_LAST_24_YEARS, HISTORY_YEAR.total_division, ECONOMY_YEAR_MINUTES, &HISTORY_YEAR},
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
|
@ -210,19 +232,28 @@ protected:
|
|||
};
|
||||
std::vector<DataSet> data{};
|
||||
|
||||
std::span<const StringID> ranges = {};
|
||||
std::span<const StringID> ranges{};
|
||||
std::span<const GraphScale> scales{};
|
||||
uint8_t selected_scale = 0;
|
||||
|
||||
uint8_t highlight_data = UINT8_MAX; ///< Data set that should be highlighted, or UINT8_MAX for none.
|
||||
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; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -609,24 +640,34 @@ protected:
|
|||
this->SetDirty();
|
||||
}};
|
||||
|
||||
void UpdateMatrixSize(WidgetID widget, Dimension &size, Dimension &resize, auto labels)
|
||||
{
|
||||
size = {};
|
||||
for (const StringID &str : labels) {
|
||||
size = maxdim(size, GetStringBoundingBox(str, FS_SMALL));
|
||||
}
|
||||
|
||||
size.width += WidgetDimensions::scaled.framerect.Horizontal();
|
||||
size.height += WidgetDimensions::scaled.framerect.Vertical();
|
||||
|
||||
/* Set fixed height for number of ranges. */
|
||||
size.height *= static_cast<uint>(std::size(labels));
|
||||
|
||||
resize.width = 0;
|
||||
resize.height = 0;
|
||||
this->GetWidget<NWidgetCore>(widget)->SetMatrixDimension(1, ClampTo<uint32_t>(std::size(labels)));
|
||||
}
|
||||
|
||||
public:
|
||||
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_GRAPH_RANGE_MATRIX:
|
||||
for (const StringID &str : this->ranges) {
|
||||
size = maxdim(size, GetStringBoundingBox(str, FS_SMALL));
|
||||
}
|
||||
this->UpdateMatrixSize(widget, size, resize, this->ranges);
|
||||
break;
|
||||
|
||||
size.width += WidgetDimensions::scaled.framerect.Horizontal();
|
||||
size.height += WidgetDimensions::scaled.framerect.Vertical();
|
||||
|
||||
/* Set fixed height for number of ranges. */
|
||||
size.height *= static_cast<uint>(std::size(this->ranges));
|
||||
|
||||
resize.width = 0;
|
||||
resize.height = 0;
|
||||
this->GetWidget<NWidgetCore>(WID_GRAPH_RANGE_MATRIX)->SetMatrixDimension(1, ClampTo<uint32_t>(std::size(this->ranges)));
|
||||
case WID_GRAPH_SCALE_MATRIX:
|
||||
this->UpdateMatrixSize(widget, size, resize, this->scales | std::views::transform(&GraphScale::label));
|
||||
break;
|
||||
|
||||
case WID_GRAPH_GRAPH: {
|
||||
|
@ -675,13 +716,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;
|
||||
|
@ -689,6 +734,21 @@ public:
|
|||
break;
|
||||
}
|
||||
|
||||
case WID_GRAPH_SCALE_MATRIX: {
|
||||
uint line_height = GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical();
|
||||
uint8_t selected_month_increment = this->scales[this->selected_scale].month_increment;
|
||||
Rect line = r.WithHeight(line_height);
|
||||
for (const auto &scale : this->scales) {
|
||||
/* Redraw frame if selected */
|
||||
if (selected_month_increment == scale.month_increment) DrawFrameRect(line, COLOUR_BROWN, FrameFlag::Lowered);
|
||||
|
||||
DrawString(line.Shrink(WidgetDimensions::scaled.framerect), scale.label, TC_BLACK, SA_CENTER, false, FS_SMALL);
|
||||
|
||||
line = line.Translate(0, line_height);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
@ -704,11 +764,24 @@ 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;
|
||||
}
|
||||
|
||||
case WID_GRAPH_SCALE_MATRIX: {
|
||||
int row = GetRowFromWidget(pt.y, widget, 0, GetCharacterHeight(FS_SMALL) + WidgetDimensions::scaled.framerect.Vertical());
|
||||
const auto &scale = this->scales[row];
|
||||
if (this->selected_scale != row) {
|
||||
this->selected_scale = row;
|
||||
this->month_increment = scale.month_increment;
|
||||
this->x_values_increment = scale.x_values_increment;
|
||||
this->InvalidateData();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
@ -1115,6 +1188,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 +1682,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,13 +1699,27 @@ 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);
|
||||
}
|
||||
|
||||
void OnInit() override
|
||||
{
|
||||
this->BaseCargoGraphWindow::OnInit();
|
||||
|
||||
this->scales = TimerGameEconomy::UsingWallclockUnits() ? MONTHLY_SCALE_WALLCLOCK : MONTHLY_SCALE_CALENDAR;
|
||||
}
|
||||
|
||||
CargoTypes GetCargoTypes(WindowNumber window_number) const override
|
||||
{
|
||||
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,14 +1733,14 @@ 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);
|
||||
}
|
||||
|
||||
void UpdateStatistics(bool initialize) override
|
||||
{
|
||||
int mo = TimerGameEconomy::month - this->num_vert_lines;
|
||||
int mo = (TimerGameEconomy::month / this->month_increment - this->num_vert_lines) * this->month_increment;
|
||||
auto yr = TimerGameEconomy::year;
|
||||
while (mo < 0) {
|
||||
yr--;
|
||||
|
@ -1688,7 +1778,34 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
|
|||
transported.dash = 2;
|
||||
auto transported_filler = Filler{transported, &Industry::ProducedHistory::transported};
|
||||
|
||||
FillFromHistory<GRAPH_NUM_MONTHS>(p.history, i->valid_history, produced_filler, transported_filler);
|
||||
FillFromHistory<GRAPH_NUM_MONTHS>(p.history, i->valid_history, *this->scales[this->selected_scale].history_range, 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, *this->scales[this->selected_scale].history_range, accepted_filler, waiting_filler);
|
||||
} else {
|
||||
FillFromHistory<GRAPH_NUM_MONTHS>(*a.history, i->valid_history, *this->scales[this->selected_scale].history_range, accepted_filler, waiting_filler);
|
||||
}
|
||||
}
|
||||
|
||||
this->SetDirty();
|
||||
|
@ -1708,7 +1825,7 @@ static constexpr NWidgetPart _nested_industry_production_widgets[] = {
|
|||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_GRAPH_GRAPH), SetMinimalSize(495, 0), SetFill(1, 1), SetResize(1, 1),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
|
||||
NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_RANGE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO),
|
||||
NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_RANGE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_TOGGLE_RANGE),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 4),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_ENABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_ENABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_ENABLE_ALL), SetFill(1, 0),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_GRAPH_DISABLE_CARGOES), SetStringTip(STR_GRAPH_CARGO_DISABLE_ALL, STR_GRAPH_CARGO_TOOLTIP_DISABLE_ALL), SetFill(1, 0),
|
||||
|
@ -1717,6 +1834,8 @@ static constexpr NWidgetPart _nested_industry_production_widgets[] = {
|
|||
NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_MATRIX), SetFill(1, 0), SetResize(0, 2), SetMatrixDataTip(1, 0, STR_GRAPH_CARGO_PAYMENT_TOGGLE_CARGO), SetScrollbar(WID_GRAPH_MATRIX_SCROLLBAR),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_BROWN, WID_GRAPH_MATRIX_SCROLLBAR),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 4),
|
||||
NWidget(WWT_MATRIX, COLOUR_BROWN, WID_GRAPH_SCALE_MATRIX), SetFill(1, 0), SetResize(0, 0), SetMatrixDataTip(1, 0, STR_GRAPH_SELECT_SCALE),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(0, 24), SetFill(0, 1),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SPACER), SetMinimalSize(5, 0), SetFill(0, 1), SetResize(0, 1),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1838,7 +1842,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
|
|||
p.history[LAST_MONTH].production += ScaleByCargoScale(p.rate * 8, false);
|
||||
}
|
||||
|
||||
UpdateValidHistory(i->valid_history);
|
||||
UpdateValidHistory(i->valid_history, TimerGameEconomy::month);
|
||||
}
|
||||
|
||||
if (indspec->callback_mask.Test(IndustryCallbackMask::DecideColour)) {
|
||||
|
@ -2490,21 +2494,48 @@ void GenerateIndustries()
|
|||
_industry_builder.Reset();
|
||||
}
|
||||
|
||||
template <>
|
||||
Industry::ProducedHistory SumHistory(std::span<const Industry::ProducedHistory> history)
|
||||
{
|
||||
uint32_t production = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &p) { return r + p.production; });
|
||||
uint32_t transported = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &p) { return r + p.transported; });
|
||||
auto count = std::size(history);
|
||||
return {.production = ClampTo<uint16_t>(production / count), .transported = ClampTo<uint16_t>(transported / count)};
|
||||
}
|
||||
|
||||
template <>
|
||||
Industry::AcceptedHistory SumHistory(std::span<const Industry::AcceptedHistory> history)
|
||||
{
|
||||
uint32_t accepted = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &a) { return r + a.accepted; });
|
||||
uint32_t waiting = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &a) { return r + a.waiting; });;
|
||||
auto count = std::size(history);
|
||||
return {.accepted = ClampTo<uint16_t>(accepted / count), .waiting = ClampTo<uint16_t>(waiting / count)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Monthly update of industry statistics.
|
||||
* @param i Industry to update.
|
||||
*/
|
||||
static void UpdateIndustryStatistics(Industry *i)
|
||||
{
|
||||
UpdateValidHistory(i->valid_history);
|
||||
auto month = TimerGameEconomy::month;
|
||||
UpdateValidHistory(i->valid_history, month);
|
||||
|
||||
for (auto &p : i->produced) {
|
||||
if (IsValidCargoType(p.cargo)) {
|
||||
if (p.history[THIS_MONTH].production != 0) i->last_prod_year = TimerGameEconomy::year;
|
||||
|
||||
RotateHistory(p.history);
|
||||
RotateHistory(p.history, i->valid_history, month);
|
||||
}
|
||||
}
|
||||
|
||||
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, i->valid_history, month);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
|
|
|
@ -622,6 +622,14 @@ STR_GRAPH_COMPANY_VALUES_CAPTION :{WHITE}Company
|
|||
|
||||
STR_GRAPH_LAST_24_MINUTES_TIME_LABEL :{TINY_FONT}{BLACK}Last 24 minutes
|
||||
STR_GRAPH_LAST_72_MINUTES_TIME_LABEL :{TINY_FONT}{BLACK}Last 72 minutes
|
||||
STR_GRAPH_LAST_288_MINUTES_TIME_LABEL :{TINY_FONT}{BLACK}Last 288 minutes
|
||||
|
||||
STR_GRAPH_LAST_24_MONTHS :{TINY_FONT}{BLACK}2 years (monthly)
|
||||
STR_GRAPH_LAST_24_QUARTERS :{TINY_FONT}{BLACK}6 years (quarterly)
|
||||
STR_GRAPH_LAST_24_YEARS :{TINY_FONT}{BLACK}24 years (yearly)
|
||||
|
||||
STR_GRAPH_TOGGLE_RANGE :Toggle graph for this data range
|
||||
STR_GRAPH_SELECT_SCALE :Change horizontal scale of graph
|
||||
|
||||
STR_GRAPH_CARGO_PAYMENT_RATES_CAPTION :{WHITE}Cargo Payment Rates
|
||||
STR_GRAPH_CARGO_PAYMENT_RATES_DAYS :{TINY_FONT}{BLACK}Days in transit
|
||||
|
@ -634,9 +642,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 +4034,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!
|
||||
|
||||
|
|
|
@ -5001,6 +5001,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}需要
|
|||
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}土地倾斜的方向不对
|
||||
STR_ERROR_CAN_T_DO_THIS :{WHITE}不能这样做……
|
||||
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}必须先摧毁建筑
|
||||
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}……建筑物被保护
|
||||
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}无法清除这个区域……
|
||||
STR_ERROR_SITE_UNSUITABLE :{WHITE}……地点不合适
|
||||
STR_ERROR_ALREADY_BUILT :{WHITE}……已经建成
|
||||
|
|
|
@ -1356,8 +1356,8 @@ STR_CONFIG_SETTING_AUTOSLOPE_HELPTEXT :可以在建築
|
|||
STR_CONFIG_SETTING_CATCHMENT :容許更真實的服務範圍設定:{STRING}
|
||||
STR_CONFIG_SETTING_CATCHMENT_HELPTEXT :使車站和機場的服務範圍根據其種類和大小而改變。
|
||||
|
||||
STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES :公司車站可以為自帶車站的工業設施提供服務:{STRING}
|
||||
STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES_HELPTEXT :啟用後,公司車站可以為附近自帶車站的工業設施(如油井)提供服務。禁用後,這些工業設施只能由其自帶的車站提供服務,並且這些車站不會提供除了該工業設施以外的產品
|
||||
STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES :公司車站可以服務附設車站的工業設施:{STRING}
|
||||
STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES_HELPTEXT :啟用後,公司車站可以為附近附設車站的工業(如鑽油平台)提供服務。停用後,這些工業只能由其附設的車站提供服務,並且附設車站不會提供除了該工業設施以外的任何服務
|
||||
|
||||
STR_CONFIG_SETTING_EXTRADYNAMITE :允許移除更多市鎮擁有的道路、橋樑及隧道:{STRING}
|
||||
STR_CONFIG_SETTING_EXTRADYNAMITE_HELPTEXT :使玩家更容易地移除市鎮擁有的基礎建設和建築物。
|
||||
|
@ -5001,7 +5001,7 @@ STR_ERROR_FLAT_LAND_REQUIRED :{WHITE}需要
|
|||
STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION :{WHITE}地面斜坡方向不對
|
||||
STR_ERROR_CAN_T_DO_THIS :{WHITE}不能執行以下動作...
|
||||
STR_ERROR_BUILDING_MUST_BE_DEMOLISHED :{WHITE}必須先摧毀建築物
|
||||
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}……建築物受到保護
|
||||
STR_ERROR_BUILDING_IS_PROTECTED :{WHITE}……建築物被保護
|
||||
STR_ERROR_CAN_T_CLEAR_THIS_AREA :{WHITE}不能清除這個地段...
|
||||
STR_ERROR_SITE_UNSUITABLE :{WHITE}... 地點不適合
|
||||
STR_ERROR_ALREADY_BUILT :{WHITE}……經已建成
|
||||
|
|
|
@ -8,6 +8,7 @@ add_files(
|
|||
getoptdata.cpp
|
||||
getoptdata.h
|
||||
hashtable.hpp
|
||||
history.cpp
|
||||
history_func.hpp
|
||||
history_type.hpp
|
||||
lrucache.hpp
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file history.cpp Implementation of functions for storing historical data. */
|
||||
|
||||
#ifndef HISTORY_CPP
|
||||
#define HISTORY_CPP
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../core/bitmath_func.hpp"
|
||||
#include "history_type.hpp"
|
||||
#include "history_func.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
static void UpdateValidHistory(ValidHistoryMask &valid_history, const HistoryRange &hr, uint cur_month)
|
||||
{
|
||||
if (cur_month % hr.total_division != 0) return;
|
||||
if (hr.division != 1 && !HasBit(valid_history, hr.first - hr.division)) return;
|
||||
|
||||
SB(valid_history, hr.first, hr.records, GB(valid_history, hr.first, hr.records) << 1ULL | 1ULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update mask of valid records.
|
||||
* @param[in,out] valid_history Valid history records.
|
||||
* @param age Current economy month.
|
||||
*/
|
||||
void UpdateValidHistory(ValidHistoryMask &valid_history, uint cur_month)
|
||||
{
|
||||
UpdateValidHistory(valid_history, HISTORY_MONTH, cur_month);
|
||||
UpdateValidHistory(valid_history, HISTORY_QUARTER, cur_month);
|
||||
UpdateValidHistory(valid_history, HISTORY_YEAR, cur_month);
|
||||
}
|
||||
|
||||
bool IsValidHistory(ValidHistoryMask valid_history, const HistoryRange &hr, uint age)
|
||||
{
|
||||
if (hr.hr == nullptr) {
|
||||
if (age < hr.periods) {
|
||||
uint slot = hr.first + age;
|
||||
return HasBit(valid_history, slot);
|
||||
}
|
||||
} else {
|
||||
if (age * hr.division < static_cast<uint>(hr.hr->periods - hr.division)) {
|
||||
uint start = age * hr.division + ((TimerGameEconomy::month / hr.hr->division) % hr.division);
|
||||
return IsValidHistory(valid_history, *hr.hr, start/* + hr.division - 1*/);
|
||||
}
|
||||
if (age < hr.periods) {
|
||||
uint slot = hr.first + age - ((hr.hr->periods / hr.division) - 1);
|
||||
return HasBit(valid_history, slot);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* HISTORY_CPP */
|
|
@ -11,16 +11,22 @@
|
|||
#define HISTORY_FUNC_HPP
|
||||
|
||||
#include "../core/bitmath_func.hpp"
|
||||
#include "../core/math_func.hpp"
|
||||
#include "../timer/timer_game_economy.h"
|
||||
#include "history_type.hpp"
|
||||
|
||||
void UpdateValidHistory(ValidHistoryMask &valid_history, uint cur_month);
|
||||
bool IsValidHistory(ValidHistoryMask valid_history, const HistoryRange &hr, uint age);
|
||||
|
||||
/**
|
||||
* Update mask of valid history records.
|
||||
* @param[in,out] valid_history Valid history records.
|
||||
* Sum history data elements.
|
||||
* @note The summation should prevent overflowing, and perform transformations relevant to the type of data.
|
||||
* @tparam T type of history data element.
|
||||
* @param history History elements to sum.
|
||||
* @return Sum of history elements.
|
||||
*/
|
||||
inline void UpdateValidHistory(ValidHistoryMask &valid_history)
|
||||
{
|
||||
SB(valid_history, LAST_MONTH, HISTORY_RECORDS - LAST_MONTH, GB(valid_history, LAST_MONTH, HISTORY_RECORDS - LAST_MONTH) << 1ULL | 1ULL);
|
||||
}
|
||||
template <typename T>
|
||||
T SumHistory(typename std::span<const T> history);
|
||||
|
||||
/**
|
||||
* Rotate history.
|
||||
|
@ -28,25 +34,104 @@ inline void UpdateValidHistory(ValidHistoryMask &valid_history)
|
|||
* @param history Historical data to rotate.
|
||||
*/
|
||||
template <typename T>
|
||||
void RotateHistory(HistoryData<T> &history)
|
||||
void RotateHistory(HistoryData<T> &history, ValidHistoryMask valid_history, const HistoryRange &hr, uint cur_month)
|
||||
{
|
||||
std::rotate(std::rbegin(history), std::rbegin(history) + 1, std::rend(history));
|
||||
history[THIS_MONTH] = {};
|
||||
if (cur_month % hr.total_division != 0) return;
|
||||
|
||||
std::move_backward(std::next(std::begin(history), hr.first), std::next(std::begin(history), hr.last - 1), std::next(std::begin(history), hr.last));
|
||||
|
||||
if (hr.division == 1) {
|
||||
history[hr.first] = history[hr.first - 1];
|
||||
} else if (HasBit(valid_history, hr.first - hr.division)) {
|
||||
auto first = std::next(std::begin(history), hr.first - hr.division);
|
||||
auto last = std::next(first, hr.division);
|
||||
history[hr.first] = SumHistory<T>(std::span{first, last});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void RotateHistory(HistoryData<T> &history, ValidHistoryMask valid_history, uint cur_month)
|
||||
{
|
||||
RotateHistory(history, valid_history, HISTORY_MONTH, cur_month);
|
||||
RotateHistory(history, valid_history, HISTORY_QUARTER, cur_month);
|
||||
RotateHistory(history, valid_history, HISTORY_YEAR, cur_month);
|
||||
history.front() = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool GetHistory(const HistoryData<T> &history, ValidHistoryMask valid_history, const HistoryRange &hr, uint age, T &result)
|
||||
{
|
||||
if (hr.hr == nullptr) {
|
||||
if (age < hr.periods) {
|
||||
uint slot = hr.first + age;
|
||||
result = history[slot];
|
||||
return HasBit(valid_history, slot);
|
||||
}
|
||||
} else {
|
||||
if (age * hr.division < static_cast<uint>(hr.hr->periods - hr.division)) {
|
||||
bool is_valid = false;
|
||||
std::array<T, HISTORY_MAX_DIVISION> tmp_result; // No need to clear as we fill every element we use.
|
||||
uint start = age * hr.division + ((TimerGameEconomy::month / hr.hr->division) % hr.division);
|
||||
for (auto i = start; i != start + hr.division; ++i) {
|
||||
is_valid |= GetHistory(history, valid_history, *hr.hr, i, tmp_result[i - start]);
|
||||
}
|
||||
result = SumHistory<T>(std::span{std::begin(tmp_result), hr.division});
|
||||
return is_valid;
|
||||
}
|
||||
if (age < hr.periods) {
|
||||
uint slot = hr.first + age - ((hr.hr->periods / hr.division) - 1);
|
||||
result = history[slot];
|
||||
return HasBit(valid_history, slot);
|
||||
}
|
||||
}
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill some data with historical data.
|
||||
* @param history Historical data to fill from.
|
||||
* @param valid_history Mask of valid history records.
|
||||
* @param hr History range to fill with.
|
||||
* @param fillers Fillers to fill with history data.
|
||||
*/
|
||||
template <uint N, typename T, typename... Tfillers>
|
||||
void FillFromHistory(const HistoryData<T> &history, ValidHistoryMask valid_history, Tfillers... fillers)
|
||||
void FillFromHistory(const HistoryData<T> &history, ValidHistoryMask valid_history, const HistoryRange &hr, Tfillers... fillers)
|
||||
{
|
||||
T result{};
|
||||
for (uint i = 0; i != N; ++i) {
|
||||
if (GetHistory(history, valid_history, hr, N - i - 1, result)) {
|
||||
(fillers.Fill(i, result), ...);
|
||||
} else {
|
||||
(fillers.MakeInvalid(i), ...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill some data with empty records.
|
||||
* @param valid_history Mask of valid history records.
|
||||
* @param hr History range to fill with.
|
||||
* @param fillers Fillers to fill with history data.
|
||||
*/
|
||||
template <uint N, typename... Tfillers>
|
||||
void FillFromEmpty(ValidHistoryMask valid_history, const HistoryRange &hr, Tfillers... fillers)
|
||||
{
|
||||
for (uint i = 0; i != N; ++i) {
|
||||
if (HasBit(valid_history, N - i)) {
|
||||
auto &data = history[N - i];
|
||||
(fillers.Fill(i, data), ...);
|
||||
if (IsValidHistory(valid_history, hr, N - i - 1)) {
|
||||
(fillers.MakeZero(i), ...);
|
||||
} else {
|
||||
(fillers.MakeInvalid(i), ...);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,40 @@
|
|||
#ifndef HISTORY_TYPE_HPP
|
||||
#define HISTORY_TYPE_HPP
|
||||
|
||||
static constexpr uint8_t HISTORY_RECORDS = 25;
|
||||
#include "../stdafx.h"
|
||||
|
||||
static constexpr uint8_t HISTORY_PERIODS = 24;
|
||||
|
||||
struct HistoryRange {
|
||||
const HistoryRange *hr;
|
||||
const uint8_t periods; ///< Number of periods for this range.
|
||||
const uint8_t records; ///< Number of records needed for this range.
|
||||
const uint8_t first; ///< Index of first element in history data.
|
||||
const uint8_t last; ///< Index of last element in history data.
|
||||
const uint8_t division; ///< Number of divisions of the previous history range.
|
||||
const uint8_t total_division; ///< Number of divisions of the initial history range.
|
||||
|
||||
explicit constexpr HistoryRange(uint8_t periods) :
|
||||
hr(nullptr), periods(periods), records(this->periods), first(1), last(this->first + this->records), division(1), total_division(1)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr HistoryRange(const HistoryRange &hr, uint8_t division, uint8_t periods) :
|
||||
hr(&hr), periods(periods), records(this->periods - ((hr.periods / division) - 1)), first(hr.last), last(this->first + this->records),
|
||||
division(division), total_division(division * hr.total_division)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr HistoryRange HISTORY_MONTH{HISTORY_PERIODS};
|
||||
static constexpr HistoryRange HISTORY_QUARTER{HISTORY_MONTH, 3, HISTORY_PERIODS};
|
||||
static constexpr HistoryRange HISTORY_YEAR{HISTORY_QUARTER, 4, HISTORY_PERIODS};
|
||||
|
||||
/** Maximum number of divisions from previous history range. */
|
||||
static constexpr uint8_t HISTORY_MAX_DIVISION = std::max({HISTORY_MONTH.division, HISTORY_QUARTER.division, HISTORY_YEAR.division});
|
||||
|
||||
/** Total number of records require for all history data. */
|
||||
static constexpr uint8_t HISTORY_RECORDS = HISTORY_YEAR.last;
|
||||
|
||||
static constexpr uint8_t THIS_MONTH = 0;
|
||||
static constexpr uint8_t LAST_MONTH = 1;
|
||||
|
|
|
@ -79,7 +79,6 @@ static constexpr auto _callback_tuple = std::make_tuple(
|
|||
&CcCreateGroup,
|
||||
&CcFoundRandomTown,
|
||||
&CcRoadStop,
|
||||
&CcBuildIndustry,
|
||||
&CcStartStopVehicle,
|
||||
&CcGame,
|
||||
&CcAddVehicleNewGroup
|
||||
|
|
|
@ -700,7 +700,7 @@ int openttd_main(std::span<std::string_view> arguments)
|
|||
InitializeLanguagePacks();
|
||||
|
||||
/* Initialize the font cache */
|
||||
InitFontCache(false);
|
||||
InitFontCache(FONTSIZES_REQUIRED);
|
||||
|
||||
/* This must be done early, since functions use the SetWindowDirty* calls */
|
||||
InitWindowSystem();
|
||||
|
|
|
@ -182,6 +182,6 @@ bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_is
|
|||
if (best_font == nullptr) return false;
|
||||
|
||||
callback->SetFontNames(settings, best_font, &best_index);
|
||||
InitFontCache(callback->Monospace());
|
||||
InitFontCache(callback->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -53,7 +53,7 @@ static ScriptStorage &GetStorage()
|
|||
|
||||
/* static */ ScriptInstance *ScriptObject::ActiveInstance::active = nullptr;
|
||||
|
||||
ScriptObject::ActiveInstance::ActiveInstance(ScriptInstance &instance) : alc_scope(instance.engine)
|
||||
ScriptObject::ActiveInstance::ActiveInstance(ScriptInstance &instance) : alc_scope(instance.engine.get())
|
||||
{
|
||||
this->last_active = ScriptObject::ActiveInstance::active;
|
||||
ScriptObject::ActiveInstance::active = &instance;
|
||||
|
@ -230,8 +230,8 @@ ScriptObject::DisableDoCommandScope::DisableDoCommandScope()
|
|||
|
||||
/* static */ bool ScriptObject::CanSuspend()
|
||||
{
|
||||
Squirrel *squirrel = ScriptObject::GetActiveInstance().engine;
|
||||
return GetStorage().allow_do_command && squirrel->CanSuspend();
|
||||
Squirrel &squirrel = *ScriptObject::GetActiveInstance().engine;
|
||||
return GetStorage().allow_do_command && squirrel.CanSuspend();
|
||||
}
|
||||
|
||||
/* static */ ScriptEventQueue &ScriptObject::GetEventQueue()
|
||||
|
|
|
@ -50,8 +50,8 @@ static void PrintFunc(bool error_msg, std::string_view message)
|
|||
|
||||
ScriptInstance::ScriptInstance(std::string_view api_name)
|
||||
{
|
||||
this->storage = new ScriptStorage();
|
||||
this->engine = new Squirrel(api_name);
|
||||
this->storage = std::make_unique<ScriptStorage>();
|
||||
this->engine = std::make_unique<Squirrel>(api_name);
|
||||
this->engine->SetPrintFunction(&PrintFunc);
|
||||
}
|
||||
|
||||
|
@ -59,10 +59,10 @@ void ScriptInstance::Initialize(const std::string &main_script, const std::strin
|
|||
{
|
||||
ScriptObject::ActiveInstance active(*this);
|
||||
|
||||
this->controller = new ScriptController(company);
|
||||
this->controller = std::make_unique<ScriptController>(company);
|
||||
|
||||
/* Register the API functions and classes */
|
||||
this->engine->SetGlobalPointer(this->engine);
|
||||
this->engine->SetGlobalPointer(this->engine.get());
|
||||
this->RegisterAPI();
|
||||
if (this->IsDead()) {
|
||||
/* Failed to register API; a message has already been logged. */
|
||||
|
@ -81,12 +81,11 @@ void ScriptInstance::Initialize(const std::string &main_script, const std::strin
|
|||
}
|
||||
|
||||
/* Create the main-class */
|
||||
this->instance = new SQObject();
|
||||
if (!this->engine->CreateClassInstance(instance_name, this->controller, this->instance)) {
|
||||
this->instance = std::make_unique<SQObject>();
|
||||
if (!this->engine->CreateClassInstance(instance_name, this->controller.get(), this->instance.get())) {
|
||||
/* If CreateClassInstance has returned false instance has not been
|
||||
* registered with squirrel, so avoid trying to Release it by clearing it now */
|
||||
delete this->instance;
|
||||
this->instance = nullptr;
|
||||
this->instance.reset();
|
||||
this->Died();
|
||||
return;
|
||||
}
|
||||
|
@ -158,11 +157,10 @@ ScriptInstance::~ScriptInstance()
|
|||
ScriptObject::ActiveInstance active(*this);
|
||||
this->in_shutdown = true;
|
||||
|
||||
if (instance != nullptr) this->engine->ReleaseObject(this->instance);
|
||||
if (engine != nullptr) delete this->engine;
|
||||
delete this->storage;
|
||||
delete this->controller;
|
||||
delete this->instance;
|
||||
if (instance != nullptr) this->engine->ReleaseObject(this->instance.get());
|
||||
|
||||
/* Engine must be reset explicitly in scope of the active instance. */
|
||||
this->engine.reset();
|
||||
}
|
||||
|
||||
void ScriptInstance::Continue()
|
||||
|
@ -179,11 +177,9 @@ void ScriptInstance::Died()
|
|||
|
||||
this->last_allocated_memory = this->GetAllocatedMemory(); // Update cache
|
||||
|
||||
if (this->instance != nullptr) this->engine->ReleaseObject(this->instance);
|
||||
delete this->instance;
|
||||
delete this->engine;
|
||||
this->instance = nullptr;
|
||||
this->engine = nullptr;
|
||||
if (this->instance != nullptr) this->engine->ReleaseObject(this->instance.get());
|
||||
this->engine.reset();
|
||||
this->instance.reset();
|
||||
}
|
||||
|
||||
void ScriptInstance::GameLoop()
|
||||
|
|
|
@ -256,7 +256,7 @@ public:
|
|||
void ReleaseSQObject(HSQOBJECT *obj);
|
||||
|
||||
protected:
|
||||
class Squirrel *engine = nullptr; ///< A wrapper around the squirrel vm.
|
||||
std::unique_ptr<class Squirrel> engine; ///< A wrapper around the squirrel vm.
|
||||
std::string api_version{}; ///< Current API used by this script.
|
||||
|
||||
/**
|
||||
|
@ -288,9 +288,9 @@ protected:
|
|||
virtual void LoadDummyScript() = 0;
|
||||
|
||||
private:
|
||||
class ScriptController *controller = nullptr; ///< The script main class.
|
||||
class ScriptStorage *storage = nullptr; ///< Some global information for each running script.
|
||||
SQObject *instance = nullptr; ///< Squirrel-pointer to the script main class.
|
||||
std::unique_ptr<class ScriptStorage> storage; ///< Some global information for each running script.
|
||||
std::unique_ptr<class ScriptController> controller; ///< The script main class.
|
||||
std::unique_ptr<SQObject> instance; ///< Squirrel-pointer to the script main class.
|
||||
|
||||
bool is_started = false; ///< Is the scripts constructor executed?
|
||||
bool is_dead = false; ///< True if the script has been stopped.
|
||||
|
|
|
@ -1036,9 +1036,8 @@ struct GameOptionsWindow : Window {
|
|||
this->SetWidgetDisabledState(WID_GO_GUI_FONT_AA, _fcsettings.prefer_sprite);
|
||||
this->SetDirty();
|
||||
|
||||
InitFontCache(false);
|
||||
InitFontCache(true);
|
||||
ClearFontCache();
|
||||
InitFontCache(FONTSIZES_ALL);
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
CheckForMissingGlyphs();
|
||||
SetupWidgetDimensions();
|
||||
UpdateAllVirtCoords();
|
||||
|
@ -1051,7 +1050,7 @@ struct GameOptionsWindow : Window {
|
|||
this->SetWidgetLoweredState(WID_GO_GUI_FONT_AA, _fcsettings.global_aa);
|
||||
MarkWholeScreenDirty();
|
||||
|
||||
ClearFontCache();
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
break;
|
||||
#endif /* HAS_TRUETYPE_FONT */
|
||||
|
||||
|
|
|
@ -535,7 +535,7 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
|
|||
|
||||
struct GrfSpriteOffset {
|
||||
size_t file_pos;
|
||||
uint8_t control_flags;
|
||||
SpriteCacheCtrlFlags control_flags{};
|
||||
};
|
||||
|
||||
/** Map from sprite numbers to position in the GRF file. */
|
||||
|
@ -565,7 +565,7 @@ void ReadGRFSpriteOffsets(SpriteFile &file)
|
|||
size_t old_pos = file.GetPos();
|
||||
file.SeekTo(data_offset, SEEK_CUR);
|
||||
|
||||
GrfSpriteOffset offset = { 0, 0 };
|
||||
GrfSpriteOffset offset{0};
|
||||
|
||||
/* Loop over all sprite section entries and store the file
|
||||
* offset for each newly encountered ID. */
|
||||
|
@ -574,7 +574,6 @@ void ReadGRFSpriteOffsets(SpriteFile &file)
|
|||
if (id != prev_id) {
|
||||
_grf_sprite_offsets[prev_id] = offset;
|
||||
offset.file_pos = file.GetPos() - 4;
|
||||
offset.control_flags = 0;
|
||||
}
|
||||
prev_id = id;
|
||||
uint length = file.ReadDword();
|
||||
|
@ -585,11 +584,11 @@ void ReadGRFSpriteOffsets(SpriteFile &file)
|
|||
uint8_t zoom = file.ReadByte();
|
||||
length--;
|
||||
if (colour.Any() && zoom == 0) { // ZoomLevel::Normal (normal zoom)
|
||||
SetBit(offset.control_flags, (colour != SpriteComponent::Palette) ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP : SCCF_ALLOW_ZOOM_MIN_1X_PAL);
|
||||
SetBit(offset.control_flags, (colour != SpriteComponent::Palette) ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL);
|
||||
offset.control_flags.Set((colour != SpriteComponent::Palette) ? SpriteCacheCtrlFlag::AllowZoomMin1x32bpp : SpriteCacheCtrlFlag::AllowZoomMin1xPal);
|
||||
offset.control_flags.Set((colour != SpriteComponent::Palette) ? SpriteCacheCtrlFlag::AllowZoomMin2x32bpp : SpriteCacheCtrlFlag::AllowZoomMin2xPal);
|
||||
}
|
||||
if (colour.Any() && zoom == 2) { // ZoomLevel::In2x (2x zoomed in)
|
||||
SetBit(offset.control_flags, (colour != SpriteComponent::Palette) ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL);
|
||||
offset.control_flags.Set((colour != SpriteComponent::Palette) ? SpriteCacheCtrlFlag::AllowZoomMin2x32bpp : SpriteCacheCtrlFlag::AllowZoomMin2xPal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -621,7 +620,7 @@ bool LoadNextSprite(SpriteID load_index, SpriteFile &file, uint file_sprite_id)
|
|||
uint8_t grf_type = file.ReadByte();
|
||||
|
||||
SpriteType type;
|
||||
uint8_t control_flags = 0;
|
||||
SpriteCacheCtrlFlags control_flags;
|
||||
if (grf_type == 0xFF) {
|
||||
/* Some NewGRF files have "empty" pseudo-sprites which are 1
|
||||
* byte long. Catch these so the sprites won't be displayed. */
|
||||
|
|
|
@ -11,24 +11,9 @@
|
|||
#define SPRITECACHE_H
|
||||
|
||||
#include "gfx_type.h"
|
||||
#include "spritecache_type.h"
|
||||
#include "spriteloader/spriteloader.hpp"
|
||||
|
||||
/** Data structure describing a sprite. */
|
||||
struct Sprite {
|
||||
uint16_t height; ///< Height of the sprite.
|
||||
uint16_t width; ///< Width of the sprite.
|
||||
int16_t x_offs; ///< Number of pixels to shift the sprite to the right.
|
||||
int16_t y_offs; ///< Number of pixels to shift the sprite downwards.
|
||||
std::byte data[]; ///< Sprite data.
|
||||
};
|
||||
|
||||
enum SpriteCacheCtrlFlags : uint8_t {
|
||||
SCCF_ALLOW_ZOOM_MIN_1X_PAL = 0, ///< Allow use of sprite min zoom setting at 1x in palette mode.
|
||||
SCCF_ALLOW_ZOOM_MIN_1X_32BPP = 1, ///< Allow use of sprite min zoom setting at 1x in 32bpp mode.
|
||||
SCCF_ALLOW_ZOOM_MIN_2X_PAL = 2, ///< Allow use of sprite min zoom setting at 2x in palette mode.
|
||||
SCCF_ALLOW_ZOOM_MIN_2X_32BPP = 3, ///< Allow use of sprite min zoom setting at 2x in 32bpp mode.
|
||||
};
|
||||
|
||||
extern uint _sprite_cache_size;
|
||||
|
||||
/** SpriteAllocator that allocates memory via a unique_ptr array. */
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "core/math_func.hpp"
|
||||
#include "gfx_type.h"
|
||||
#include "spritecache_type.h"
|
||||
#include "spriteloader/spriteloader.hpp"
|
||||
|
||||
#include "table/sprites.h"
|
||||
|
@ -27,7 +28,7 @@ struct SpriteCache {
|
|||
uint32_t lru = 0;
|
||||
SpriteType type = SpriteType::Invalid; ///< In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as recolour sprite. If the recolour sprite gets into the cache it might be drawn as real sprite which causes enormous trouble.
|
||||
bool warned = false; ///< True iff the user has been warned about incorrect use of this sprite
|
||||
uint8_t control_flags = 0; ///< Control flags, see SpriteCacheCtrlFlags
|
||||
SpriteCacheCtrlFlags control_flags{}; ///< Control flags, see SpriteCacheCtrlFlags
|
||||
|
||||
void ClearSpriteData();
|
||||
};
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file spritecache_type.h Types related to the sprite cache. */
|
||||
|
||||
#ifndef SPRITECACHE_TYPE_H
|
||||
#define SPRITECACHE_TYPE_H
|
||||
|
||||
#include "core/enum_type.hpp"
|
||||
|
||||
/** Data structure describing a sprite. */
|
||||
struct Sprite {
|
||||
uint16_t height; ///< Height of the sprite.
|
||||
uint16_t width; ///< Width of the sprite.
|
||||
int16_t x_offs; ///< Number of pixels to shift the sprite to the right.
|
||||
int16_t y_offs; ///< Number of pixels to shift the sprite downwards.
|
||||
std::byte data[]; ///< Sprite data.
|
||||
};
|
||||
|
||||
enum class SpriteCacheCtrlFlag : uint8_t {
|
||||
AllowZoomMin1xPal, ///< Allow use of sprite min zoom setting at 1x in palette mode.
|
||||
AllowZoomMin1x32bpp, ///< Allow use of sprite min zoom setting at 1x in 32bpp mode.
|
||||
AllowZoomMin2xPal, ///< Allow use of sprite min zoom setting at 2x in palette mode.
|
||||
AllowZoomMin2x32bpp, ///< Allow use of sprite min zoom setting at 2x in 32bpp mode.
|
||||
};
|
||||
|
||||
using SpriteCacheCtrlFlags = EnumBitSet<SpriteCacheCtrlFlag, uint8_t>;
|
||||
|
||||
#endif /* SPRITECACHE_TYPE_H */
|
|
@ -256,7 +256,7 @@ static ZoomLevels LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFil
|
|||
return {};
|
||||
}
|
||||
|
||||
static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp)
|
||||
static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp)
|
||||
{
|
||||
static const ZoomLevel zoom_lvl_map[6] = {ZoomLevel::Normal, ZoomLevel::In4x, ZoomLevel::In2x, ZoomLevel::Out2x, ZoomLevel::Out4x, ZoomLevel::Out8x};
|
||||
|
||||
|
@ -295,11 +295,11 @@ static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFil
|
|||
is_wanted_zoom_lvl = true;
|
||||
ZoomLevel zoom_min = sprite_type == SpriteType::Font ? ZoomLevel::Min : _settings_client.gui.sprite_zoom_min;
|
||||
if (zoom_min >= ZoomLevel::In2x &&
|
||||
HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL) && zoom_lvl < ZoomLevel::In2x) {
|
||||
control_flags.Test(load_32bpp ? SpriteCacheCtrlFlag::AllowZoomMin2x32bpp : SpriteCacheCtrlFlag::AllowZoomMin2xPal) && zoom_lvl < ZoomLevel::In2x) {
|
||||
is_wanted_zoom_lvl = false;
|
||||
}
|
||||
if (zoom_min >= ZoomLevel::Normal &&
|
||||
HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP : SCCF_ALLOW_ZOOM_MIN_1X_PAL) && zoom_lvl < ZoomLevel::Normal) {
|
||||
control_flags.Test(load_32bpp ? SpriteCacheCtrlFlag::AllowZoomMin1x32bpp : SpriteCacheCtrlFlag::AllowZoomMin1xPal) && zoom_lvl < ZoomLevel::Normal) {
|
||||
is_wanted_zoom_lvl = false;
|
||||
}
|
||||
} else {
|
||||
|
@ -359,7 +359,7 @@ static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFil
|
|||
return loaded_sprites;
|
||||
}
|
||||
|
||||
ZoomLevels SpriteLoaderGrf::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp)
|
||||
ZoomLevels SpriteLoaderGrf::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp)
|
||||
{
|
||||
if (this->container_ver >= 2) {
|
||||
return LoadSpriteV2(sprite, file, file_pos, sprite_type, load_32bpp, control_flags, avail_8bpp, avail_32bpp);
|
||||
|
|
|
@ -17,7 +17,7 @@ class SpriteLoaderGrf : public SpriteLoader {
|
|||
uint8_t container_ver;
|
||||
public:
|
||||
SpriteLoaderGrf(uint8_t container_ver) : container_ver(container_ver) {}
|
||||
ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override;
|
||||
ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override;
|
||||
};
|
||||
|
||||
#endif /* SPRITELOADER_GRF_HPP */
|
||||
|
|
|
@ -48,7 +48,7 @@ static void Convert32bppTo8bpp(SpriteLoader::Sprite &sprite)
|
|||
}
|
||||
}
|
||||
|
||||
ZoomLevels SpriteLoaderMakeIndexed::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp)
|
||||
ZoomLevels SpriteLoaderMakeIndexed::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp)
|
||||
{
|
||||
ZoomLevels avail = this->baseloader.LoadSprite(sprite, file, file_pos, sprite_type, true, control_flags, avail_8bpp, avail_32bpp);
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class SpriteLoaderMakeIndexed : public SpriteLoader {
|
|||
SpriteLoader &baseloader;
|
||||
public:
|
||||
SpriteLoaderMakeIndexed(SpriteLoader &baseloader) : baseloader(baseloader) {}
|
||||
ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override;
|
||||
ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override;
|
||||
};
|
||||
|
||||
#endif /* SPRITELOADER_MAKEINDEXED_H */
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "../core/alloc_type.hpp"
|
||||
#include "../core/enum_type.hpp"
|
||||
#include "../gfx_type.h"
|
||||
#include "../spritecache_type.h"
|
||||
#include "sprite_file_type.hpp"
|
||||
|
||||
struct Sprite;
|
||||
|
@ -94,7 +95,7 @@ public:
|
|||
* @param[out] avail_32bpp Available 32bpp sprites.
|
||||
* @return Available sprites matching \a load_32bpp.
|
||||
*/
|
||||
virtual ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) = 0;
|
||||
virtual ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) = 0;
|
||||
|
||||
virtual ~SpriteLoader() = default;
|
||||
};
|
||||
|
|
|
@ -2278,7 +2278,7 @@ std::string_view GetCurrentLanguageIsoCode()
|
|||
*/
|
||||
bool MissingGlyphSearcher::FindMissingGlyphs()
|
||||
{
|
||||
InitFontCache(this->Monospace());
|
||||
InitFontCache(this->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
||||
|
||||
this->Reset();
|
||||
for (auto text = this->NextString(); text.has_value(); text = this->NextString()) {
|
||||
|
@ -2395,7 +2395,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
|
|||
/* Our fallback font does miss characters too, so keep the
|
||||
* user chosen font as that is more likely to be any good than
|
||||
* the wild guess we made */
|
||||
InitFontCache(searcher->Monospace());
|
||||
InitFontCache(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -2412,12 +2412,12 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
|
|||
ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING);
|
||||
|
||||
/* Reset the font width */
|
||||
LoadStringWidthTable(searcher->Monospace());
|
||||
LoadStringWidthTable(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the font with cache */
|
||||
LoadStringWidthTable(searcher->Monospace());
|
||||
LoadStringWidthTable(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
||||
|
||||
#if !(defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
|
||||
/*
|
||||
|
|
|
@ -3,6 +3,7 @@ add_test_files(
|
|||
bitmath_func.cpp
|
||||
enum_over_optimisation.cpp
|
||||
flatset_type.cpp
|
||||
history_func.cpp
|
||||
landscape_partial_pixel_z.cpp
|
||||
math_func.cpp
|
||||
mock_environment.h
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file history_func.cpp Test functionality for misc/history_func. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../3rdparty/catch2/catch.hpp"
|
||||
|
||||
#include "../misc/history_type.hpp"
|
||||
#include "../misc/history_func.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
template <>
|
||||
uint16_t SumHistory(std::span<const uint16_t> history)
|
||||
{
|
||||
uint32_t total = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const uint16_t &value) { return r + value; });
|
||||
return ClampTo<uint16_t>(total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get history records and return the value, instead returning its validity.
|
||||
* @param history History data to extract from.
|
||||
* @param hr History range to get.
|
||||
* @param age Age of data to get.
|
||||
* @return Historical value for the period and age.
|
||||
*/
|
||||
template <typename T>
|
||||
T GetHistory(const HistoryData<T> &history, const HistoryRange &hr, uint age)
|
||||
{
|
||||
T result;
|
||||
GetHistory(history, 0, hr, age, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_CASE("History Rotation and Reporting tests")
|
||||
{
|
||||
HistoryData<uint16_t> history{};
|
||||
ValidHistoryMask valid_history = 0;
|
||||
|
||||
/* Fill the history with decreasing data points for 24 years of history. This ensures that no data period should
|
||||
* contain the same value as another period. */
|
||||
uint16_t i = 12 * HISTORY_PERIODS;
|
||||
for (uint date = 1; date <= 12 * HISTORY_PERIODS; ++date, --i) {
|
||||
history[THIS_MONTH] = i;
|
||||
UpdateValidHistory(valid_history, date % 12);
|
||||
RotateHistory(history, valid_history, date % 12);
|
||||
}
|
||||
|
||||
/* With the decreasing sequence, the expected value is triangle number (x*x+n)/2 and the square of the total divisions.
|
||||
* for quarters: 1 + 2 + 3 = 6, 4 + 5 + 6 = 15, 7 + 8 + 9 = 24, 10 + 11 + 12 = 33
|
||||
* 13 + 14 + 15 = 42, 16 + 17 + 18 = 51, 19 + 20 + 21 = 60, 22 + 23 + 24 = 69...
|
||||
* for years: 6 + 15 + 24 + 33 = 78, 42 + 51 + 60 + 69 = 222...
|
||||
*/
|
||||
for (uint j = 0; j < HISTORY_PERIODS; ++j) {
|
||||
CHECK(GetHistory(history, HISTORY_MONTH, j) == (( 1 * 1 + 1) / 2) + 1 * 1 * j);
|
||||
CHECK(GetHistory(history, HISTORY_QUARTER, j) == (( 3 * 3 + 3) / 2) + 3 * 3 * j);
|
||||
CHECK(GetHistory(history, HISTORY_YEAR, j) == ((12 * 12 + 12) / 2) + 12 * 12 * j);
|
||||
}
|
||||
|
||||
/* Double-check quarter history matches summed month history. */
|
||||
CHECK(GetHistory(history, HISTORY_MONTH, 0) + GetHistory(history, HISTORY_MONTH, 1) + GetHistory(history, HISTORY_MONTH, 2) == GetHistory(history, HISTORY_QUARTER, 0));
|
||||
CHECK(GetHistory(history, HISTORY_MONTH, 3) + GetHistory(history, HISTORY_MONTH, 4) + GetHistory(history, HISTORY_MONTH, 5) == GetHistory(history, HISTORY_QUARTER, 1));
|
||||
CHECK(GetHistory(history, HISTORY_MONTH, 6) + GetHistory(history, HISTORY_MONTH, 7) + GetHistory(history, HISTORY_MONTH, 8) == GetHistory(history, HISTORY_QUARTER, 2));
|
||||
CHECK(GetHistory(history, HISTORY_MONTH, 9) + GetHistory(history, HISTORY_MONTH, 10) + GetHistory(history, HISTORY_MONTH, 11) == GetHistory(history, HISTORY_QUARTER, 3));
|
||||
CHECK(GetHistory(history, HISTORY_MONTH, 12) + GetHistory(history, HISTORY_MONTH, 13) + GetHistory(history, HISTORY_MONTH, 14) == GetHistory(history, HISTORY_QUARTER, 4));
|
||||
CHECK(GetHistory(history, HISTORY_MONTH, 15) + GetHistory(history, HISTORY_MONTH, 16) + GetHistory(history, HISTORY_MONTH, 17) == GetHistory(history, HISTORY_QUARTER, 5));
|
||||
CHECK(GetHistory(history, HISTORY_MONTH, 18) + GetHistory(history, HISTORY_MONTH, 19) + GetHistory(history, HISTORY_MONTH, 20) == GetHistory(history, HISTORY_QUARTER, 6));
|
||||
CHECK(GetHistory(history, HISTORY_MONTH, 21) + GetHistory(history, HISTORY_MONTH, 22) + GetHistory(history, HISTORY_MONTH, 23) == GetHistory(history, HISTORY_QUARTER, 7));
|
||||
|
||||
/* Double-check year history matches summed quarter history. */
|
||||
CHECK(GetHistory(history, HISTORY_QUARTER, 0) + GetHistory(history, HISTORY_QUARTER, 1) + GetHistory(history, HISTORY_QUARTER, 2) + GetHistory(history, HISTORY_QUARTER, 3) == GetHistory(history, HISTORY_YEAR, 0));
|
||||
CHECK(GetHistory(history, HISTORY_QUARTER, 4) + GetHistory(history, HISTORY_QUARTER, 5) + GetHistory(history, HISTORY_QUARTER, 6) + GetHistory(history, HISTORY_QUARTER, 7) == GetHistory(history, HISTORY_YEAR, 1));
|
||||
CHECK(GetHistory(history, HISTORY_QUARTER, 8) + GetHistory(history, HISTORY_QUARTER, 9) + GetHistory(history, HISTORY_QUARTER, 10) + GetHistory(history, HISTORY_QUARTER, 11) == GetHistory(history, HISTORY_YEAR, 2));
|
||||
CHECK(GetHistory(history, HISTORY_QUARTER, 12) + GetHistory(history, HISTORY_QUARTER, 13) + GetHistory(history, HISTORY_QUARTER, 14) + GetHistory(history, HISTORY_QUARTER, 15) == GetHistory(history, HISTORY_YEAR, 3));
|
||||
CHECK(GetHistory(history, HISTORY_QUARTER, 16) + GetHistory(history, HISTORY_QUARTER, 17) + GetHistory(history, HISTORY_QUARTER, 18) + GetHistory(history, HISTORY_QUARTER, 19) == GetHistory(history, HISTORY_YEAR, 4));
|
||||
CHECK(GetHistory(history, HISTORY_QUARTER, 20) + GetHistory(history, HISTORY_QUARTER, 21) + GetHistory(history, HISTORY_QUARTER, 22) + GetHistory(history, HISTORY_QUARTER, 23) == GetHistory(history, HISTORY_YEAR, 5));
|
||||
}
|
|
@ -33,7 +33,7 @@ static bool MockLoadNextSprite(SpriteID load_index)
|
|||
sc->id = 0;
|
||||
sc->type = is_mapgen ? SpriteType::MapGen : SpriteType::Normal;
|
||||
sc->warned = false;
|
||||
sc->control_flags = 0;
|
||||
sc->control_flags = {};
|
||||
|
||||
/* Fill with empty sprites up until the default sprite count. */
|
||||
return load_index < SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -37,6 +37,7 @@ enum GraphWidgets : WidgetID {
|
|||
WID_GRAPH_MATRIX_SCROLLBAR,///< Cargo list scrollbar.
|
||||
|
||||
WID_GRAPH_RANGE_MATRIX, ///< Range list.
|
||||
WID_GRAPH_SCALE_MATRIX, ///< Horizontal axis scale list.
|
||||
|
||||
WID_PHG_DETAILED_PERFORMANCE, ///< Detailed performance.
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue