1
0
Fork 0

Feature: Custom company colours, picked from HSV colour picker.

pull/11634/head
Peter Nelson 2025-07-25 08:56:51 +01:00
parent 1a967aad95
commit f8531bc64b
No known key found for this signature in database
GPG Key ID: 8EF8F0A467DF75ED
24 changed files with 661 additions and 73 deletions

View File

@ -35,6 +35,7 @@
#include "smallmap_gui.h"
#include "game/game.hpp"
#include "goal_base.h"
#include "spritecache.h"
#include "story_base.h"
#include "company_cmd.h"
#include "timer/timer.h"
@ -55,6 +56,7 @@ void UpdateObjectColours(const Company *c);
CompanyID _local_company; ///< Company controlled by the human player at this client. Can also be #COMPANY_SPECTATOR.
CompanyID _current_company; ///< Company currently doing an action.
TypedIndexContainer<std::array<Colours, MAX_COMPANIES>, CompanyID> _company_colours; ///< NOSAVE: can be determined from company structs.
TypedIndexContainer<std::array<PaletteID, MAX_COMPANIES>, CompanyID> _company_palettes; ///< NOSAVE: can be determined from company structs.
std::string _company_manager_face; ///< for company manager face storage in openttd.cfg
uint _cur_company_tick_index; ///< used to generate a name for one company that doesn't have a name yet per tick
@ -163,7 +165,7 @@ TextColour GetDrawStringCompanyColour(CompanyID company)
*/
PaletteID GetCompanyPalette(CompanyID company)
{
return GetColourPalette(_company_colours[company]);
return _company_palettes[company];
}
/**
@ -513,7 +515,7 @@ static Colours GenerateCompanyColour()
/* Move the colours that look similar to each company's colour to the side */
for (const Company *c : Company::Iterate()) {
Colours pcolour = c->colour;
Colours pcolour = Colours(c->colour & 0xF);
for (uint i = 0; i < COLOUR_END; i++) {
if (colours[i] == pcolour) {
@ -566,6 +568,40 @@ restart:;
}
}
void ClearLivery(Livery &livery)
{
DeallocateDynamicSprite(livery.cached_pal_1cc);
DeallocateDynamicSprite(livery.cached_pal_2cc);
DeallocateDynamicSprite(livery.cached_pal_2cr);
livery.cached_pal_1cc = PALETTE_RECOLOUR_START + GB(livery.colour1, 0, 4);
livery.cached_pal_2cc = SPR_2CCMAP_BASE + GB(livery.colour1, 0, 4) + GB(livery.colour2, 0, 4) * 16;
livery.cached_pal_2cr = SPR_2CCMAP_BASE + GB(livery.colour2, 0, 4) + GB(livery.colour1, 0, 4) * 16;
}
/**
* Update cached palettes for a livery.
* @param livery Livery to update.
* @param always_update Always update instead of clearing livery (for LS_DEFAULT which is always needed).
*/
void UpdateLivery(Livery &livery, bool always_update)
{
if ((always_update || livery.in_use != 0) && (ColoursPacker(livery.colour1).IsCustom() || ColoursPacker(livery.colour2).IsCustom())) {
PaletteID pal_1cc = PALETTE_RECOLOUR_START + GB(livery.colour1, 0, 4);
livery.cached_pal_1cc = CreateCompanyColourRemap(livery.colour1, livery.colour1, false, pal_1cc, livery.cached_pal_1cc);
if (_loaded_newgrf_features.has_2CC) {
PaletteID pal_2cc = SPR_2CCMAP_BASE + GB(livery.colour1, 0, 4) + GB(livery.colour2, 0, 4) * 16;
livery.cached_pal_2cc = CreateCompanyColourRemap(livery.colour1, livery.colour2, true, pal_2cc, livery.cached_pal_2cc);
PaletteID pal_2cr = SPR_2CCMAP_BASE + GB(livery.colour2, 0, 4) + GB(livery.colour1, 0, 4) * 16;
livery.cached_pal_2cr = CreateCompanyColourRemap(livery.colour2, livery.colour1, true, pal_2cr, livery.cached_pal_2cr);
}
} else {
ClearLivery(livery);
}
}
/**
* Reset the livery schemes to the company's primary colour.
* This is used on loading games without livery information and on new company start up.
@ -577,6 +613,7 @@ void ResetCompanyLivery(Company *c)
c->livery[scheme].in_use = 0;
c->livery[scheme].colour1 = c->colour;
c->livery[scheme].colour2 = c->colour;
UpdateLivery(c->livery[scheme], scheme == LS_DEFAULT);
}
for (Group *g : Group::Iterate()) {
@ -584,8 +621,12 @@ void ResetCompanyLivery(Company *c)
g->livery.in_use = 0;
g->livery.colour1 = c->colour;
g->livery.colour2 = c->colour;
UpdateLivery(g->livery, false);
}
}
_company_colours[c->index] = c->livery[LS_DEFAULT].colour1;
_company_palettes[c->index] = c->livery[LS_DEFAULT].cached_pal_1cc;
}
/**
@ -613,7 +654,6 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = CompanyID::Invalid(
c->colour = colour;
ResetCompanyLivery(c);
_company_colours[c->index] = c->colour;
/* Scale the initial loan based on the inflation rounded down to the loan interval. The maximum loan has already been inflation adjusted. */
c->money = c->current_loan = std::min<int64_t>((INITIAL_LOAN * _economy.inflation_prices >> 16) / LOAN_INTERVAL * LOAN_INTERVAL, _economy.max_loan);
@ -1076,11 +1116,16 @@ CommandCost CmdSetCompanyManagerFace(DoCommandFlags flags, uint32_t bits, uint s
*/
void UpdateCompanyLiveries(Company *c)
{
UpdateLivery(c->livery[LS_DEFAULT], true);
for (int i = 1; i < LS_END; i++) {
if (!HasBit(c->livery[i].in_use, 0)) c->livery[i].colour1 = c->livery[LS_DEFAULT].colour1;
if (!HasBit(c->livery[i].in_use, 1)) c->livery[i].colour2 = c->livery[LS_DEFAULT].colour2;
UpdateLivery(c->livery[i], false);
}
UpdateCompanyGroupLiveries(c);
_company_colours[c->index] = c->livery[LS_DEFAULT].colour1;
_company_palettes[c->index] = c->livery[LS_DEFAULT].cached_pal_1cc;
}
/**
@ -1093,7 +1138,7 @@ void UpdateCompanyLiveries(Company *c)
*/
CommandCost CmdSetCompanyColour(DoCommandFlags flags, LiveryScheme scheme, bool primary, Colours colour)
{
if (scheme >= LS_END || (colour >= COLOUR_END && colour != INVALID_COLOUR)) return CMD_ERROR;
if (scheme >= LS_END) return CMD_ERROR;
/* Default scheme can't be reset to invalid. */
if (scheme == LS_DEFAULT && colour == INVALID_COLOUR) return CMD_ERROR;
@ -1117,9 +1162,11 @@ CommandCost CmdSetCompanyColour(DoCommandFlags flags, LiveryScheme scheme, bool
* original and cached company colours too. */
if (scheme == LS_DEFAULT) {
UpdateCompanyLiveries(c);
_company_colours[_current_company] = colour;
/* Update cached colour/palette for company */
c->colour = colour;
CompanyAdminUpdate(c);
} else {
UpdateLivery(c->livery[scheme], false);
}
} else {
if (scheme != LS_DEFAULT) AssignBit(c->livery[scheme].in_use, 1, colour != INVALID_COLOUR);
@ -1128,6 +1175,8 @@ CommandCost CmdSetCompanyColour(DoCommandFlags flags, LiveryScheme scheme, bool
if (scheme == LS_DEFAULT) {
UpdateCompanyLiveries(c);
} else {
UpdateLivery(c->livery[scheme], false);
}
}

View File

@ -14,6 +14,7 @@
#include "company_type.h"
#include "gfx_type.h"
#include "vehicle_type.h"
#include "livery.h"
bool CheckTakeoverVehicleLimit(CompanyID cbig, CompanyID small);
void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner);
@ -24,6 +25,8 @@ void CompanyAdminUpdate(const Company *company);
void CompanyAdminBankrupt(CompanyID company_id);
void UpdateLandscapingLimits();
void UpdateCompanyLiveries(Company *c);
void ClearLivery(Livery &livery);
void UpdateLivery(Livery &livery, bool always_update);
Money GetAvailableMoney(CompanyID company);
Money GetAvailableMoneyForCommand();
@ -37,7 +40,9 @@ extern CompanyID _local_company;
extern CompanyID _current_company;
extern TypedIndexContainer<std::array<Colours, MAX_COMPANIES>, CompanyID> _company_colours;
extern TypedIndexContainer<std::array<PaletteID, MAX_COMPANIES>, CompanyID> _company_palettes;
extern std::string _company_manager_face;
PaletteID GetCompanyPalette(CompanyID company);
/**

View File

@ -7,6 +7,7 @@
/** @file company_gui.cpp %Company related GUIs. */
#include "palette_func.h"
#include "stdafx.h"
#include "currency.h"
#include "error.h"
@ -47,6 +48,7 @@
#include "timer/timer.h"
#include "timer/timer_window.h"
#include "core/string_consumer.hpp"
#include "blitter/factory.hpp"
#include "widgets/company_widget.h"
@ -584,6 +586,13 @@ static const LiveryClass _livery_class[LS_END] = {
LC_ROAD, LC_ROAD,
};
StringID GetColourString(Colours colour)
{
if (ColoursPacker(colour).IsCustom()) return STR_COLOUR_CUSTOM;
if (colour == INVALID_COLOUR) return STR_COLOUR_DEFAULT;
return STR_COLOUR_DARK_BLUE + colour;
}
/**
* Colour selection list item, with icon and string components.
* @tparam TSprite Recolourable sprite to draw as icon.
@ -591,12 +600,372 @@ static const LiveryClass _livery_class[LS_END] = {
template <SpriteID TSprite = SPR_SQUARE>
class DropDownListColourItem : public DropDownIcon<DropDownString<DropDownListItem>> {
public:
DropDownListColourItem(int colour, bool masked) :
DropDownIcon<DropDownString<DropDownListItem>>(TSprite, GetColourPalette(static_cast<Colours>(colour % COLOUR_END)), GetString((Colours)colour < COLOUR_END ? (STR_COLOUR_DARK_BLUE + colour) : STR_COLOUR_DEFAULT), colour, masked)
DropDownListColourItem(PaletteID pal, StringID str, int value, bool masked) :
DropDownIcon<DropDownString<DropDownListItem>>(TSprite, pal, GetString(str), value, masked)
{
}
};
class SelectCustomColourWindow : public Window {
/* Preserved values from parent window when this window was opened. */
uint32_t sel;
LiveryClass lc;
bool ctrl_pressed;
bool primary;
bool group;
Scrollbar *hue, *sat, *val, *con;
Colours current;
HsvColour current_hsv;
uint8_t contrast;
static inline const int BUTTON_SIZE = 10;
static inline const int CON_MAX = UINT8_MAX;
void SetPresetColours()
{
for (uint i = 0; i < std::size(_settings_client.gui.preset_colours); ++i) {
this->GetWidget<NWidgetCore>(WID_SCC_PRESETS + i)->colour = _settings_client.gui.preset_colours[i];
}
}
Colour AdjustBrightness(const Colour &rgb, ColourShade shade, int contrast) const
{
int level = (shade - SHADE_NORMAL) * contrast / (SHADE_END / 2);
return {
ClampTo<uint8_t>(std::max(1, rgb.r + level)),
ClampTo<uint8_t>(std::max(1, rgb.g + level)),
ClampTo<uint8_t>(std::max(1, rgb.b + level))
};
}
bool SetScrollbarPositions()
{
bool changed = false;
changed |= this->hue->SetPosition(HsvColour::HUE_MAX - this->current_hsv.h);
changed |= this->sat->SetPosition(this->current_hsv.s);
changed |= this->val->SetPosition(HsvColour::VAL_MAX - this->current_hsv.v);
changed |= this->con->SetPosition(CON_MAX - this->contrast);
return changed;
}
public:
SelectCustomColourWindow(WindowDesc &desc, int window_number, CompanyID company, Colours colour, uint32_t sel, LiveryClass lc, bool ctrl, bool primary, bool group) :
Window(desc), sel(sel), lc(lc), ctrl_pressed(ctrl), primary(primary), group(group)
{
this->InitNested(window_number);
this->owner = (Owner)company;
this->current = colour;
ColoursPacker colourp(colour);
if ((colourp.GetHue() | colourp.GetSaturation() | colourp.GetValue()) == 0) {
auto [rgba, contrast] = GetCompanyColourRGB(colour);
this->current_hsv = ConvertRgbToHsv(rgba);
this->contrast = contrast;
} else {
this->current_hsv = colourp.Hsv();
this->contrast = colourp.GetContrast();
}
this->hue = this->GetScrollbar(WID_SCC_SCROLLBAR_HUE);
this->hue->SetCapacity(HsvColour::HUE_MAX / BUTTON_SIZE);
this->hue->SetCount(HsvColour::HUE_MAX + HsvColour::HUE_MAX / BUTTON_SIZE);
this->sat = this->GetScrollbar(WID_SCC_SCROLLBAR_SAT);
this->sat->SetCapacity(HsvColour::SAT_MAX / BUTTON_SIZE);
this->sat->SetCount(HsvColour::SAT_MAX + HsvColour::SAT_MAX / BUTTON_SIZE);
this->val = this->GetScrollbar(WID_SCC_SCROLLBAR_VAL);
this->val->SetCapacity(HsvColour::VAL_MAX / BUTTON_SIZE);
this->val->SetCount(HsvColour::VAL_MAX + HsvColour::VAL_MAX / BUTTON_SIZE);
this->con = this->GetScrollbar(WID_SCC_SCROLLBAR_CON);
this->con->SetCapacity(CON_MAX / BUTTON_SIZE);
this->con->SetCount(CON_MAX + CON_MAX / BUTTON_SIZE);
this->SetScrollbarPositions();
this->SetPresetColours();
}
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
{
switch (widget) {
case WID_SCC_CAPTION:
return GetString(stringid, this->owner);
default:
return this->Window::GetWidgetString(widget, stringid);
}
}
void DrawWidget(const Rect &r, WidgetID widget) const override
{
HsvColour hsv = this->current_hsv;
switch (widget) {
case WID_SCC_HUE: {
int range = r.Height() - 1;
int mid = CentreBounds(r.left, r.right, 0);
hsv.s = HsvColour::SAT_MAX;
for (int i = 0; i <= range; i++) {
hsv.h = HsvColour::HUE_MAX * i / range;
hsv.v = HsvColour::VAL_MAX;
GfxFillRect(r.left, r.bottom - i, mid - 1, r.bottom - i, ConvertHsvToRgb(hsv), FILLRECT_OPAQUE);
hsv.v = HsvColour::VAL_MAX * 3 / 4;
GfxFillRect(mid, r.bottom - i, r.right, r.bottom - i, ConvertHsvToRgb(hsv), FILLRECT_OPAQUE);
}
/* Mark current value. */
GfxDrawLine(r.left, r.bottom - this->current_hsv.h * range / HsvColour::HUE_MAX, r.right, r.bottom - this->current_hsv.h * range / HsvColour::HUE_MAX, PC_WHITE, 1, 1);
break;
}
case WID_SCC_SAT: {
int width = r.Width() - 1;
int height = r.Height() - 1;
for (int x = 0; x <= width; x++) {
hsv.s = HsvColour::SAT_MAX * x / width;
for (int y = 0; y <= height; y++) {
hsv.v = HsvColour::VAL_MAX * y / height;
GfxFillRect(r.left + x, r.bottom - y, r.left + x, r.bottom - y, ConvertHsvToRgb(hsv), FILLRECT_OPAQUE);
}
}
/* Mark current value. */
GfxDrawLine(r.left + this->current_hsv.s * width / HsvColour::SAT_MAX, r.top, r.left + this->current_hsv.s * width / HsvColour::SAT_MAX, r.bottom, PC_WHITE, 1, 1);
GfxDrawLine(r.left, r.bottom - this->current_hsv.v * height / HsvColour::VAL_MAX, r.right, r.bottom - this->current_hsv.v * height / HsvColour::VAL_MAX, PC_WHITE, 1, 1);
break;
}
case WID_SCC_CON: {
int range = r.Height() - 1;
int mid = CentreBounds(r.left, r.right, 0);
Colour rgb = ConvertHsvToRgb(HsvColour{0, 0, HsvColour::VAL_MAX / 2});
for (int i = 0; i <= range; i++) {
int contrast = i * CON_MAX / width;
GfxFillRect(r.left, r.bottom - i, mid - 1, r.bottom - i, AdjustBrightness(rgb, SHADE_LIGHTEST, contrast), FILLRECT_OPAQUE);
GfxFillRect(mid, r.bottom - i, r.right, r.bottom - i, AdjustBrightness(rgb, SHADE_DARKEST, contrast), FILLRECT_OPAQUE);
}
/* Mark current value. */
GfxDrawLine(r.left, r.bottom - this->contrast * range / CON_MAX, r.right, r.bottom - this->contrast * range / CON_MAX, PC_WHITE, 1, 1);
break;
}
case WID_SCC_OUTPUT: {
int range = r.Height();
/* Pack and unpack to colours, to produce the result. */
Colours colour = this->PackColour();
ColoursPacker colourp(colour);
HsvColour hsv = colourp.Hsv();
uint8_t con = colourp.GetContrast();
for (ColourShade shade = SHADE_BEGIN; shade != SHADE_END; ++shade) {
Colour c = ConvertHsvToRgb(AdjustHsvColourBrightness(hsv, shade, con));
GfxFillRect(r.left, r.bottom - (shade + 1) * range / SHADE_END + 1, r.right, r.bottom - shade * range / SHADE_END, c, FILLRECT_OPAQUE);
}
break;
}
}
}
void OnClick(Point pt, int widget, int click_count) override
{
bool changed = false;
if (widget >= WID_SCC_DEFAULT && widget <= WID_SCC_DEFAULT_LAST && !_ctrl_pressed) {
/* Select colour from default preset. */
this->current = this->GetWidget<NWidgetCore>(widget)->colour;
auto [rgba, contrast] = GetCompanyColourRGB(this->current);
this->current_hsv = ConvertRgbToHsv(rgba);
this->contrast = contrast;
changed = this->SetScrollbarPositions();
}
if (widget >= WID_SCC_PRESETS && widget <= WID_SCC_PRESETS_LAST) {
if (_ctrl_pressed) {
/* Save colour to preset. */
_settings_client.gui.preset_colours[widget - WID_SCC_PRESETS] = this->PackColour();
this->SetPresetColours();
} else {
/* Select colour from preset. */
ColoursPacker cp(_settings_client.gui.preset_colours[widget - WID_SCC_PRESETS]);
this->current_hsv = cp.Hsv();
this->contrast = cp.GetContrast();
changed = this->SetScrollbarPositions();
}
}
if (widget == WID_SCC_HUE) {
/* Click and drag on hue widget */
const NWidgetCore *wi = this->GetWidget<NWidgetCore>(widget);
this->current_hsv.h = std::clamp<int>(HsvColour::HUE_MAX - (pt.y - wi->pos_y) * HsvColour::HUE_MAX / (int)wi->current_y, 0, HsvColour::HUE_MAX);
this->contrast = CON_MAX - this->con->GetPosition();
changed = this->SetScrollbarPositions();
if (click_count > 0) this->mouse_capture_widget = widget;
}
if (widget == WID_SCC_SAT) {
/* Click and drag on saturation/value widget */
const NWidgetCore *wi = this->GetWidget<NWidgetCore>(widget);
this->current_hsv.s = std::clamp<int>((pt.x - wi->pos_x) * HsvColour::SAT_MAX / (int)wi->current_x, 0, HsvColour::SAT_MAX);
this->current_hsv.v = std::clamp<int>(HsvColour::VAL_MAX - (pt.y - wi->pos_y) * HsvColour::VAL_MAX / (int)wi->current_y, 0, HsvColour::VAL_MAX);
this->contrast = CON_MAX - this->con->GetPosition();
changed = this->SetScrollbarPositions();
if (click_count > 0) this->mouse_capture_widget = widget;
}
if (widget == WID_SCC_CON) {
/* Click and drag on contrast widget */
const NWidgetCore *wi = this->GetWidget<NWidgetCore>(widget);
this->contrast = std::clamp<int>(CON_MAX - (pt.y - wi->pos_y) * CON_MAX / (int)wi->current_y, 0, CON_MAX);
changed = this->SetScrollbarPositions();
if (click_count > 0) this->mouse_capture_widget = widget;
}
if (!changed) return;
this->SetDirty();
this->SetTimeout();
}
void OnScrollbarScroll(WidgetID) override
{
/* Update colour from new scrollbar positions. */
this->current_hsv.h = HsvColour::HUE_MAX - this->hue->GetPosition();
this->current_hsv.s = this->sat->GetPosition();
this->current_hsv.v = HsvColour::VAL_MAX - this->val->GetPosition();
this->contrast = CON_MAX - this->con->GetPosition();
this->SetTimeout();
}
Colours PackColour() const
{
Colours colour;
ColoursPacker cp(colour);
cp.SetIndex(this->current);
cp.SetCustom(true);
cp.SetHue(this->current_hsv.h);
cp.SetSaturation(this->current_hsv.s);
cp.SetValue(this->current_hsv.v);
cp.SetContrast(this->contrast);
return colour;
}
void OnTimeout() override
{
Colours colour = this->PackColour();
if (colour != this->current) {
this->current = colour;
if (this->group) {
Command<CMD_SET_GROUP_LIVERY>::Post((GroupID)this->sel, primary, this->current);
} else {
for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
/* Changed colour for the selected scheme, or all visible schemes if CTRL is pressed. */
if (HasBit(this->sel, scheme) || (this->ctrl_pressed && _livery_class[scheme] == this->lc && HasBit(_loaded_newgrf_features.used_liveries, scheme))) {
Command<CMD_SET_COMPANY_COLOUR>::Post(scheme, primary, this->current);
}
}
}
}
}
};
static constexpr NWidgetPart _nested_select_custom_colour_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, WID_SCC_CAPTION), SetStringTip(STR_LIVERY_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY), SetFill(1, 1),
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_wide, 0), SetPadding(WidgetDimensions::unscaled.frametext),
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(NWID_VERTICAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_DARK_BLUE, WID_SCC_DEFAULT + 0), SetFill(1, 1), SetMinimalSize(16, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_PALE_GREEN, WID_SCC_DEFAULT + 1), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_PINK, WID_SCC_DEFAULT + 2), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_SCC_DEFAULT + 3), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_RED, WID_SCC_DEFAULT + 4), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, WID_SCC_DEFAULT + 5), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_SCC_DEFAULT + 6), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_DARK_GREEN, WID_SCC_DEFAULT + 7), SetFill(1, 1),
EndContainer(),
NWidget(NWID_VERTICAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_BLUE, WID_SCC_DEFAULT + 8), SetFill(1, 1), SetMinimalSize(16, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_SCC_DEFAULT + 9), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_SCC_DEFAULT + 10), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_PURPLE, WID_SCC_DEFAULT + 11), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SCC_DEFAULT + 12), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_SCC_DEFAULT + 13), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCC_DEFAULT + 14), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_SCC_DEFAULT + 15), SetFill(1, 1),
EndContainer(),
EndContainer(),
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
NWidget(NWID_VERTICAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_DARK_BLUE, WID_SCC_PRESETS + 0), SetFill(1, 1), SetMinimalSize(16, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_PALE_GREEN, WID_SCC_PRESETS + 1), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_PINK, WID_SCC_PRESETS + 2), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_YELLOW, WID_SCC_PRESETS + 3), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_RED, WID_SCC_PRESETS + 4), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_LIGHT_BLUE, WID_SCC_PRESETS + 5), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREEN, WID_SCC_PRESETS + 6), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_DARK_GREEN, WID_SCC_PRESETS + 7), SetFill(1, 1),
EndContainer(),
NWidget(NWID_VERTICAL, NWidContainerFlag::EqualSize),
NWidget(WWT_PUSHTXTBTN, COLOUR_BLUE, WID_SCC_PRESETS + 8), SetFill(1, 1), SetMinimalSize(16, 0),
NWidget(WWT_PUSHTXTBTN, COLOUR_CREAM, WID_SCC_PRESETS + 9), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_MAUVE, WID_SCC_PRESETS + 10), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_PURPLE, WID_SCC_PRESETS + 11), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, WID_SCC_PRESETS + 12), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_SCC_PRESETS + 13), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCC_PRESETS + 14), SetFill(1, 1),
NWidget(WWT_PUSHTXTBTN, COLOUR_WHITE, WID_SCC_PRESETS + 15), SetFill(1, 1),
EndContainer(),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_INSET, COLOUR_GREY),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCC_HUE), SetMinimalSize(16, 0),
EndContainer(),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCC_SCROLLBAR_HUE),
EndContainer(),
NWidget(NWID_VERTICAL),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_INSET, COLOUR_GREY),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCC_SAT), SetMinimalSize(160, 160),
EndContainer(),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCC_SCROLLBAR_VAL),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(NWID_HSCROLLBAR, COLOUR_GREY, WID_SCC_SCROLLBAR_SAT),
NWidget(NWID_SPACER), SetMinimalSize(11, 11),
EndContainer(),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_INSET, COLOUR_GREY),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCC_CON), SetMinimalSize(16, 0),
EndContainer(),
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCC_SCROLLBAR_CON),
EndContainer(),
NWidget(WWT_INSET, COLOUR_GREY),
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCC_OUTPUT), SetFill(1, 1), SetMinimalSize(16, 0),
EndContainer(),
EndContainer(),
EndContainer(),
};
static WindowDesc _select_custom_colour_desc(
WDP_AUTO, {}, 0, 0,
WC_CUSTOM_COLOUR, WC_NONE,
{},
_nested_select_custom_colour_widgets
);
/** Company livery colour scheme window. */
struct SelectCompanyLiveryWindow : public Window {
private:
@ -608,57 +977,86 @@ private:
GUIGroupList groups{};
Scrollbar *vscroll = nullptr;
void ShowColourDropDownMenu(uint32_t widget)
/**
* Get the first selected livery.
*/
const Livery *GetSelectedLivery() const
{
uint32_t used_colours = 0;
const Livery *livery, *default_livery = nullptr;
bool primary = widget == WID_SCL_PRI_COL_DROPDOWN;
uint8_t default_col = 0;
/* Disallow other company colours for the primary colour */
if (this->livery_class < LC_GROUP_RAIL && HasBit(this->sel, LS_DEFAULT) && primary) {
for (const Company *c : Company::Iterate()) {
if (c->index != _local_company) SetBit(used_colours, c->colour);
}
}
const Company *c = Company::Get(this->window_number);
if (this->livery_class < LC_GROUP_RAIL) {
/* Get the first selected livery to use as the default dropdown item */
LiveryScheme scheme;
for (scheme = LS_BEGIN; scheme < LS_END; scheme++) {
if (HasBit(this->sel, scheme)) break;
}
if (scheme == LS_END) scheme = LS_DEFAULT;
livery = &c->livery[scheme];
if (scheme != LS_DEFAULT) default_livery = &c->livery[LS_DEFAULT];
const Company *c = Company::Get((CompanyID)this->window_number);
return &c->livery[scheme];
} else {
const Group *g = Group::Get(this->sel);
return &g->livery;
}
}
/**
* Get the default selected livery.
*/
const Livery *GetDefaultLivery() const
{
const Company *c = Company::Get((CompanyID)this->window_number);
if (this->livery_class < LC_GROUP_RAIL) {
if (!HasBit(this->sel, LS_DEFAULT)) return &c->livery[LS_DEFAULT];
} else {
const Group *g = Group::Get(this->sel);
livery = &g->livery;
if (g->parent == GroupID::Invalid()) {
default_livery = &c->livery[LS_DEFAULT];
return &c->livery[LS_DEFAULT];
} else {
const Group *pg = Group::Get(g->parent);
default_livery = &pg->livery;
return &pg->livery;
}
}
return nullptr;
}
void ShowColourDropDownMenu(uint32_t widget)
{
uint32_t used_colours = 0;
bool primary = widget == WID_SCL_PRI_COL_DROPDOWN;
/* Disallow other company colours for the primary colour */
if (this->livery_class < LC_GROUP_RAIL && HasBit(this->sel, LS_DEFAULT) && primary) {
for (const Company *c : Company::Iterate()) {
if (c->index != _local_company) SetBit(used_colours, c->colour & 0xF);
}
}
/* Get the first selected livery to use as the default dropdown item */
const Livery *livery = GetSelectedLivery();
const Livery *default_livery = GetDefaultLivery();
DropDownList list;
if (default_livery != nullptr) {
/* Add COLOUR_END to put the colour out of range, but also allow us to show what the default is */
default_col = (primary ? default_livery->colour1 : default_livery->colour2) + COLOUR_END;
list.push_back(std::make_unique<DropDownListColourItem<>>(default_col, false));
list.push_back(std::make_unique<DropDownListColourItem<>>(primary ? default_livery->cached_pal_1cc : default_livery->cached_pal_2cr, STR_COLOUR_DEFAULT, INVALID_COLOUR, false));
}
for (Colours colour = COLOUR_BEGIN; colour != COLOUR_END; colour++) {
list.push_back(std::make_unique<DropDownListColourItem<>>(colour, HasBit(used_colours, colour)));
list.push_back(std::make_unique<DropDownListColourItem<>>(GetColourPalette(colour), GetColourString(colour), colour, HasBit(used_colours, colour)));
}
list.push_back(std::make_unique<DropDownListColourItem<>>(primary ? livery->cached_pal_1cc : livery->cached_pal_2cr, STR_COLOUR_CUSTOM, 0xFF, BlitterFactory::GetCurrentBlitter()->GetScreenDepth() != 32));
uint8_t sel;
int sel;
if (default_livery == nullptr || HasBit(livery->in_use, primary ? 0 : 1)) {
sel = primary ? livery->colour1 : livery->colour2;
Colours col = primary ? livery->colour1 : livery->colour2;
if (ColoursPacker(col).IsCustom()) {
sel = 0xFF;
} else {
sel = ColoursPacker(col).GetIndex();
}
} else {
sel = default_col;
sel = INVALID_COLOUR;
}
ShowDropDownList(this, std::move(list), sel, widget);
}
@ -820,7 +1218,7 @@ public:
if (scheme == LS_END) scheme = LS_DEFAULT;
const Livery *livery = &c->livery[scheme];
if (scheme == LS_DEFAULT || HasBit(livery->in_use, primary ? 0 : 1)) {
colour = STR_COLOUR_DARK_BLUE + (primary ? livery->colour1 : livery->colour2);
colour = GetColourString(primary ? livery->colour1 : livery->colour2);
}
}
} else {
@ -828,7 +1226,7 @@ public:
const Group *g = Group::Get(this->sel);
const Livery *livery = &g->livery;
if (HasBit(livery->in_use, primary ? 0 : 1)) {
colour = STR_COLOUR_DARK_BLUE + (primary ? livery->colour1 : livery->colour2);
colour = GetColourString(primary ? livery->colour1 : livery->colour2);
}
}
}
@ -868,26 +1266,26 @@ public:
int y = ir.top;
const Company *c = Company::Get((CompanyID)this->window_number);
/* Helper function to draw livery info. */
auto draw_livery = [&](std::string_view str, const Livery &livery, bool is_selected, bool is_default_scheme, int indent) {
/* Livery Label. */
DrawString(sch.left + (rtl ? 0 : indent), sch.right - (rtl ? indent : 0), y + text_offs, str, is_selected ? TC_WHITE : TC_BLACK);
/* Text below the first dropdown. */
DrawSprite(SPR_SQUARE, GetColourPalette(livery.colour1), pri_squ.left, y + square_offs);
DrawString(pri.left, pri.right, y + text_offs, (is_default_scheme || HasBit(livery.in_use, 0)) ? STR_COLOUR_DARK_BLUE + livery.colour1 : STR_COLOUR_DEFAULT, is_selected ? TC_WHITE : TC_GOLD);
DrawSprite(SPR_SQUARE, HasBit(livery.in_use, 0) ? livery.cached_pal_1cc : c->livery[LS_DEFAULT].cached_pal_1cc, pri_squ.left, y + square_offs);
DrawString(pri.left, pri.right, y + text_offs, (is_default_scheme || HasBit(livery.in_use, 0)) ? GetColourString(livery.colour1) : STR_COLOUR_DEFAULT, is_selected ? TC_WHITE : TC_GOLD);
/* Text below the second dropdown. */
if (sec.right > sec.left) { // Second dropdown has non-zero size.
DrawSprite(SPR_SQUARE, GetColourPalette(livery.colour2), sec_squ.left, y + square_offs);
DrawString(sec.left, sec.right, y + text_offs, (is_default_scheme || HasBit(livery.in_use, 1)) ? STR_COLOUR_DARK_BLUE + livery.colour2 : STR_COLOUR_DEFAULT, is_selected ? TC_WHITE : TC_GOLD);
DrawSprite(SPR_SQUARE, HasBit(livery.in_use, 1) ? livery.cached_pal_2cr : c->livery[LS_DEFAULT].cached_pal_2cr, sec_squ.left, y + square_offs);
DrawString(sec.left, sec.right, y + text_offs, (is_default_scheme || HasBit(livery.in_use, 1)) ? GetColourString(livery.colour2) : STR_COLOUR_DEFAULT, is_selected ? TC_WHITE : TC_GOLD);
}
y += this->line_height;
};
const Company *c = Company::Get(this->window_number);
if (livery_class < LC_GROUP_RAIL) {
int pos = this->vscroll->GetPosition();
for (LiveryScheme scheme = LS_DEFAULT; scheme < LS_END; scheme++) {
@ -998,6 +1396,16 @@ public:
bool local = this->window_number == _local_company;
if (!local) return;
if (index == 0xFF) {
/* Special case for 'Custom...' option */
int number = ((widget == WID_SCL_PRI_COL_DROPDOWN) ? 1 : 0) | (this->window_number << 1);
if (BringWindowToFrontById(WC_CUSTOM_COLOUR, number)) return;
const Livery *livery = GetSelectedLivery();
new SelectCustomColourWindow(_select_custom_colour_desc, number, (CompanyID)this->window_number, (widget == WID_SCL_PRI_COL_DROPDOWN) ? livery->colour1 : livery->colour2, this->sel, this->livery_class, _ctrl_pressed, widget == WID_SCL_PRI_COL_DROPDOWN, this->livery_class >= LC_GROUP_RAIL);
return;
}
Colours colour = static_cast<Colours>(index);
if (colour >= COLOUR_END) colour = INVALID_COLOUR;
@ -1114,10 +1522,10 @@ void ShowCompanyLiveryWindow(CompanyID company, GroupID group)
/**
* Draws the face of a company manager's face.
* @param cmf the company manager's face
* @param colour the (background) colour of the gradient
* @param palette the (background) colour of the gradient
* @param r position to draw the face
*/
void DrawCompanyManagerFace(const CompanyManagerFace &cmf, Colours colour, const Rect &r)
void DrawCompanyManagerFace(const CompanyManagerFace &cmf, PaletteID palette, const Rect &r)
{
/* Determine offset from centre of drawing rect. */
Dimension d = GetSpriteSize(SPR_GRADIENT);
@ -1148,7 +1556,7 @@ void DrawCompanyManagerFace(const CompanyManagerFace &cmf, Colours colour, const
}
/* Draw the gradient (background) */
DrawSprite(SPR_GRADIENT, GetColourPalette(colour), x, y);
DrawSprite(SPR_GRADIENT, palette, x, y);
/* Thirdly, draw sprites. */
for (auto var : SetBitIterator(active_vars)) {
@ -2105,7 +2513,7 @@ struct CompanyWindow : Window
const Company *c = Company::Get(this->window_number);
switch (widget) {
case WID_C_FACE:
DrawCompanyManagerFace(c->face, c->colour, r);
DrawCompanyManagerFace(c->face, GetCompanyPalette(c->index), r);
break;
case WID_C_FACE_TITLE:
@ -2359,8 +2767,8 @@ struct BuyCompanyWindow : Window {
{
switch (widget) {
case WID_BC_FACE: {
const Company *c = Company::Get(this->window_number);
DrawCompanyManagerFace(c->face, c->colour, r);
const Company *c = Company::Get((CompanyID)this->window_number);
DrawCompanyManagerFace(c->face, GetCompanyPalette(c->index), r);
break;
}

View File

@ -180,6 +180,6 @@ uint32_t MaskCompanyManagerFaceBits(const CompanyManagerFace &cmf, FaceVars vars
std::string FormatCompanyManagerFaceCode(const CompanyManagerFace &cmf);
std::optional<CompanyManagerFace> ParseCompanyManagerFaceCode(std::string_view str);
void DrawCompanyManagerFace(const CompanyManagerFace &cmf, Colours colour, const Rect &r);
void DrawCompanyManagerFace(const CompanyManagerFace &cmf, PaletteID palette, const Rect &r);
#endif /* COMPANY_MANAGER_FACE_H */

View File

@ -1949,7 +1949,7 @@ static bool ConCompanies(std::span<std::string_view> argv)
/* Grab the company name */
std::string company_name = GetString(STR_COMPANY_NAME, c->index);
std::string colour = GetString(STR_COLOUR_DARK_BLUE + _company_colours[c->index]);
std::string colour = GetString(STR_COLOUR_DARK_BLUE + _company_colours[c->index] & 0xF);
IConsolePrint(CC_INFO, "#:{}({}) Company Name: '{}' Year Founded: {} Money: {} Loan: {} Value: {} (T:{}, R:{}, P:{}, S:{}) {}",
c->index + 1, colour, company_name,
c->inaugurated_year, (int64_t)c->money, (int64_t)c->current_loan, (int64_t)CalculateCompanyValue(c),

View File

@ -18,6 +18,7 @@
#include "company_base.h"
#include "company_func.h"
#include "company_manager_face.h"
#include "sprite.h"
#include "strings_func.h"
#include "zoom_func.h"
#include "window_func.h"
@ -188,7 +189,7 @@ public:
switch (widget) {
case WID_EM_FACE: {
const Company *c = Company::Get(this->company);
DrawCompanyManagerFace(c->face, c->colour, r);
DrawCompanyManagerFace(c->face, GetCompanyPalette(c->index), r);
break;
}

View File

@ -87,6 +87,7 @@ struct Group : GroupPool::PoolItem<&_group_pool> {
Group() {}
Group(CompanyID owner, VehicleType vehicle_type) : owner(owner), vehicle_type(vehicle_type) {}
~Group();
};

View File

@ -19,6 +19,7 @@
#include "core/pool_func.hpp"
#include "order_backup.h"
#include "group_cmd.h"
#include "spritecache.h"
#include "table/strings.h"
@ -308,6 +309,7 @@ static void PropagateChildLivery(const Group *g, bool reset_cache)
Group *cg = Group::Get(childgroup);
if (!HasBit(cg->livery.in_use, 0)) cg->livery.colour1 = g->livery.colour1;
if (!HasBit(cg->livery.in_use, 1)) cg->livery.colour2 = g->livery.colour2;
UpdateLivery(cg->livery, false);
PropagateChildLivery(cg, reset_cache);
}
}
@ -323,11 +325,20 @@ void UpdateCompanyGroupLiveries(const Company *c)
if (g->owner == c->index && g->parent == GroupID::Invalid()) {
if (!HasBit(g->livery.in_use, 0)) g->livery.colour1 = c->livery[LS_DEFAULT].colour1;
if (!HasBit(g->livery.in_use, 1)) g->livery.colour2 = c->livery[LS_DEFAULT].colour2;
UpdateLivery(g->livery, false);
PropagateChildLivery(g, false);
}
}
}
Group::~Group()
{
if (CleaningPool()) return;
DeallocateDynamicSprite(this->livery.cached_pal_1cc);
DeallocateDynamicSprite(this->livery.cached_pal_2cc);
DeallocateDynamicSprite(this->livery.cached_pal_2cr);
}
/**
* Create a new vehicle group.
@ -483,8 +494,9 @@ CommandCost CmdAlterGroup(DoCommandFlags flags, AlterGroupMode mode, GroupID gro
if (!HasBit(g->livery.in_use, 0) || !HasBit(g->livery.in_use, 1)) {
/* Update livery with new parent's colours if either colour is default. */
const Livery *livery = GetParentLivery(g);
if (!HasBit(g->livery.in_use, 0)) g->livery.colour1 = livery->colour1;
if (!HasBit(g->livery.in_use, 1)) g->livery.colour2 = livery->colour2;
if (!HasBit(g->livery.in_use, 0)) if (!HasBit(g->livery.in_use, 0)) g->livery.colour1 = livery->colour1;
if (!HasBit(g->livery.in_use, 1)) if (!HasBit(g->livery.in_use, 1)) g->livery.colour2 = livery->colour2;
UpdateLivery(g->livery, false);
PropagateChildLivery(g, true);
MarkWholeScreenDirty();
@ -680,8 +692,6 @@ CommandCost CmdSetGroupLivery(DoCommandFlags flags, GroupID group_id, bool prima
if (g == nullptr || g->owner != _current_company) return CMD_ERROR;
if (colour >= COLOUR_END && colour != INVALID_COLOUR) return CMD_ERROR;
if (flags.Test(DoCommandFlag::Execute)) {
if (primary) {
AssignBit(g->livery.in_use, 0, colour != INVALID_COLOUR);
@ -692,6 +702,7 @@ CommandCost CmdSetGroupLivery(DoCommandFlags flags, GroupID group_id, bool prima
if (colour == INVALID_COLOUR) colour = GetParentLivery(g)->colour2;
g->livery.colour2 = colour;
}
UpdateLivery(g->livery, false);
PropagateChildLivery(g, true);
MarkWholeScreenDirty();

View File

@ -170,6 +170,7 @@ STR_ITEMS :{COMMA}{NBSP}it
STR_CRATES :{COMMA}{NBSP}crate{P "" s}
STR_COLOUR_DEFAULT :Default
STR_COLOUR_CUSTOM :Custom...
###length 17
STR_COLOUR_DARK_BLUE :Dark Blue
STR_COLOUR_PALE_GREEN :Pale Green
@ -2325,6 +2326,11 @@ STR_LIVERY_LARGE_PLANE :Large Aeroplane
STR_LIVERY_PASSENGER_TRAM :Passenger Tram
STR_LIVERY_FREIGHT_TRAM :Freight Tram
STR_LIVERY_HUE :{BLACK}Hue
STR_LIVERY_SATURATION :{BLACK}Saturation
STR_LIVERY_LIGHTNESS :{BLACK}Lightness
STR_LIVERY_CONTRAST :{BLACK}Contrast
# Face selection window
STR_FACE_CAPTION :{WHITE}Face Selection
STR_FACE_CANCEL_TOOLTIP :{BLACK}Cancel new face selection

View File

@ -79,6 +79,10 @@ struct Livery {
uint8_t in_use = 0; ///< Bit 0 set if this livery should override the default livery first colour, Bit 1 for the second colour.
Colours colour1 = COLOUR_BEGIN; ///< First colour, for all vehicles.
Colours colour2 = COLOUR_BEGIN; ///< Second colour, for vehicles with 2CC support.
PaletteID cached_pal_1cc = 0; ///< NOSAVE: cached 1CC palette.
PaletteID cached_pal_2cc = 0; ///< NOSAVE: cached 2CC palette.
PaletteID cached_pal_2cr = 0; ///< NOSAVE: cached reversed 2CC palette.
};
void ResetCompanyLivery(Company *c);

View File

@ -325,7 +325,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyInfo(const Company
p->Send_uint8 (c->index);
p->Send_string(GetString(STR_COMPANY_NAME, c->index));
p->Send_string(GetString(STR_PRESIDENT_NAME, c->index));
p->Send_uint8 (c->colour);
p->Send_uint8 (c->colour & 0xF);
p->Send_bool (true);
p->Send_uint32(c->inaugurated_year.base());
p->Send_bool (c->is_ai);
@ -348,7 +348,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyUpdate(const Compa
p->Send_uint8 (c->index);
p->Send_string(GetString(STR_COMPANY_NAME, c->index));
p->Send_string(GetString(STR_PRESIDENT_NAME, c->index));
p->Send_uint8 (c->colour);
p->Send_uint8 (c->colour & 0xF);
p->Send_bool (true);
p->Send_uint8 (CeilDiv(c->months_of_bankruptcy, 3)); // send as quarters_of_bankruptcy

View File

@ -1678,6 +1678,9 @@ static void AfterLoadGRFs()
InitRailTypes();
InitRoadTypes();
/* Force cached palettes to be refreshed */
ResetVehicleColourMap();
for (Engine *e : Engine::IterateType(VEH_ROAD)) {
if (_gted[e->index].rv_max_speed != 0) {
/* Set RV maximum speed from the mph/0.8 unit value */

View File

@ -17,6 +17,7 @@
#include "water.h"
#include "landscape.h"
#include "company_base.h"
#include "company_func.h"
#include "town.h"
#include "newgrf_animation_base.h"
@ -241,7 +242,7 @@ static uint16_t GetAirportTileCallback(CallbackID callback, uint32_t param1, uin
return object.ResolveCallback(regs100);
}
static void AirportDrawTileLayout(const TileInfo *ti, const DrawTileSpriteSpan &dts, Colours colour)
static void AirportDrawTileLayout(const TileInfo *ti, const DrawTileSpriteSpan &dts, PaletteID cc_pal)
{
SpriteID image = dts.ground.sprite;
SpriteID pal = dts.ground.pal;
@ -250,11 +251,11 @@ static void AirportDrawTileLayout(const TileInfo *ti, const DrawTileSpriteSpan &
if (image == SPR_FLAT_WATER_TILE && IsTileOnWater(ti->tile)) {
DrawWaterClassGround(ti);
} else {
DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, GetColourPalette(colour)));
DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, cc_pal));
}
}
DrawNewGRFTileSeq(ti, &dts, TO_BUILDINGS, 0, GetColourPalette(colour));
DrawNewGRFTileSeq(ti, &dts, TO_BUILDINGS, 0, cc_pal);
}
bool DrawNewAirportTile(TileInfo *ti, Station *st, const AirportTileSpec *airts)
@ -278,7 +279,7 @@ bool DrawNewAirportTile(TileInfo *ti, Station *st, const AirportTileSpec *airts)
auto processor = group->ProcessRegisters(object, nullptr);
auto dts = processor.GetLayout();
AirportDrawTileLayout(ti, dts, Company::Get(st->owner)->colour);
AirportDrawTileLayout(ti, dts, GetCompanyPalette(st->owner));
return true;
}

View File

@ -528,7 +528,7 @@ struct NewsWindow : Window {
case WID_N_MGR_FACE: {
const CompanyNewsInformation *cni = static_cast<const CompanyNewsInformation*>(this->ni->data.get());
DrawCompanyManagerFace(cni->face, cni->colour, r);
DrawCompanyManagerFace(cni->face, cni->colour, r); // XXX TODO this should be a palette
GfxFillRect(r, PALETTE_NEWSPAPER, FILLRECT_RECOLOUR);
break;
}

View File

@ -15,6 +15,7 @@
#include "landscape_type.h"
#include "palette_func.h"
#include "settings_type.h"
#include "sprite.h"
#include "thread.h"
#include "table/palettes.h"
@ -84,6 +85,7 @@ static uint CalculateColourDistance(const Colour &col1, int r2, int g2, int b2)
/* Palette indexes for conversion. See docs/palettes/palette_key.png */
const uint8_t PALETTE_INDEX_CC_START = 198; ///< Palette index of start of company colour remap area.
const uint8_t PALETTE_INDEX_CC_END = PALETTE_INDEX_CC_START + 8; ///< Palette index of end of company colour remap area.
const uint8_t PALETTE_INDEX_CC2_START = 80; ///< Palette index of start of second company colour remap area.
const uint8_t PALETTE_INDEX_START = 1; ///< Palette index of start of defined palette.
const uint8_t PALETTE_INDEX_END = 215; ///< Palette index of end of defined palette.
@ -499,3 +501,43 @@ TextColour PixelColour::ToTextColour() const
}
return tc;
}
std::pair<Colour, uint8_t> GetCompanyColourRGB(Colours colour)
{
static constexpr uint8_t CC_PALETTE_CONTRAST = 90;
PaletteID pal = GetColourPalette(colour);
const RecolourSprite *map = GetRecolourSprite(pal);
Colour rgb = _palette.palette[map->palette[PALETTE_INDEX_CC_START + SHADE_NORMAL]];
return {rgb, CC_PALETTE_CONTRAST};
}
PaletteID CreateCompanyColourRemap(Colours colour1, Colours colour2, bool twocc, PaletteID basemap, PaletteID hint)
{
DeallocateDynamicSprite(hint);
PaletteID pal = AllocateDynamicSprite();
const RecolourSprite *base = GetRecolourSprite(basemap);
RecolourSpriteRGBA *p = new (InjectSprite(SpriteType::Recolour, pal, sizeof(RecolourSpriteRGBA)).data()) RecolourSpriteRGBA();
/* Copy base remap */
p->palette = base->palette;
std::ranges::transform(p->palette, p->rgba.begin(), [](const uint8_t &col) { return _palette.palette[col]; });
auto apply_colour = [](RecolourSpriteRGBA &remap, uint8_t index, Colours colour) {
ColoursPacker cp{colour};
if (!cp.IsCustom()) return;
HsvColour hsv = cp.Hsv();
uint8_t con = cp.GetContrast();
for (ColourShade shade = SHADE_BEGIN; shade != SHADE_END; ++shade) {
remap.rgba[index + shade] = ConvertHsvToRgb(AdjustHsvColourBrightness(hsv, shade, con));
}
};
apply_colour(*p, PALETTE_INDEX_CC_START, colour1);
if (twocc) apply_colour(*p, PALETTE_INDEX_CC2_START, colour2);
return pal;
}

View File

@ -90,6 +90,9 @@ HsvColour AdjustHsvColourBrightness(HsvColour hsv, ColourShade shade, int contra
PixelColour GetColourGradient(Colours colour, ColourShade shade);
void SetColourGradient(Colours colour, ColourShade shade, PixelColour palette_colour);
std::pair<Colour, uint8_t> GetCompanyColourRGB(Colours colour);
PaletteID CreateCompanyColourRemap(Colours rgb1, Colours rgb2, bool twocc, PaletteID basemap, PaletteID hint);
/**
* Return the colour for a particular greyscale level.
* @param level Intensity, 0 = black, 15 = white

View File

@ -3383,6 +3383,8 @@ bool AfterLoadGame()
}
for (Company *c : Company::Iterate()) {
UpdateLivery(c->livery[LS_DEFAULT], true);
_company_palettes[c->index] = c->livery[LS_DEFAULT].cached_pal_1cc;
UpdateCompanyLiveries(c);
}

View File

@ -210,6 +210,7 @@ struct GUISettings {
uint8_t osk_activation; ///< Mouse gesture to trigger the OSK.
Colours starting_colour; ///< default color scheme for the company to start a new game with
Colours starting_colour_secondary; ///< default secondary color scheme for the company to start a new game with
Colours preset_colours[16]; ///< custom colour presets.
bool show_newgrf_name; ///< Show the name of the NewGRF in the build vehicle window
bool show_cargo_in_vehicle_lists; ///< Show the cargoes the vehicles can carry in the list windows
bool auto_remove_signals; ///< automatically remove signals when in the way during rail construction

View File

@ -185,6 +185,6 @@ inline PaletteID GroundSpritePaletteTransform(SpriteID image, PaletteID pal, Pal
* @param colour Colour.
* @return Recolour palette.
*/
static inline PaletteID GetColourPalette(Colours colour) { return PALETTE_RECOLOUR_START + colour; }
static inline PaletteID GetColourPalette(Colours colour) { return PALETTE_RECOLOUR_START + (colour % COLOUR_END); }
#endif /* SPRITE_H */

View File

@ -26,6 +26,7 @@ static const SettingVariant _gui_settings_table[] = {
};
[templates]
SDTC_BOOL = SDTC_BOOL( $var, SettingFlags({$flags}), $def, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $from, $to, $cat, $extra, $startup),
SDTC_LIST = SDTC_LIST( $var, $type, SettingFlags({$flags}), $def, $from, $to, $cat, $extra, $startup),
SDTC_OMANY = SDTC_OMANY( $var, $type, SettingFlags({$flags}), $def, $max, $full, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $from, $to, $cat, $extra, $startup),
SDTC_VAR = SDTC_VAR( $var, $type, SettingFlags({$flags}), $def, $min, $max, $interval, $str, $strhelp, $strval, $pre_cb, $post_cb, $str_cb, $help_cb, $val_cb, $def_cb, $range_cb, $from, $to, $cat, $extra, $startup),
@ -368,6 +369,13 @@ str = STR_CONFIG_SETTING_COMPANY_STARTING_COLOUR_SECONDARY
strhelp = STR_CONFIG_SETTING_COMPANY_STARTING_COLOUR_SECONDARY_HELPTEXT
strval = STR_COLOUR_SECONDARY_DARK_BLUE
[SDTC_LIST]
var = gui.preset_colours
type = SLE_UINT32
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync
def = """"
cat = SC_BASIC
[SDTC_BOOL]
var = gui.auto_remove_signals
flags = SettingFlag::NotInSave, SettingFlag::NoNetworkSync

View File

@ -2045,7 +2045,6 @@ LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_
const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID parent_engine_type, const Vehicle *v, uint8_t livery_setting)
{
const Company *c = Company::Get(company);
LiveryScheme scheme = LS_DEFAULT;
if (livery_setting == LIT_ALL || (livery_setting == LIT_COMPANY && company == _local_company)) {
if (v != nullptr) {
@ -2063,11 +2062,12 @@ const Livery *GetEngineLivery(EngineID engine_type, CompanyID company, EngineID
* whether any _other_ liveries are in use. */
if (c->livery[LS_DEFAULT].in_use != 0) {
/* Determine the livery scheme to use */
scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
LiveryScheme scheme = GetEngineLiveryScheme(engine_type, parent_engine_type, v);
if (c->livery[scheme].in_use != 0) return &c->livery[scheme];
}
}
return &c->livery[scheme];
return &c->livery[LS_DEFAULT];
}
@ -2080,6 +2080,15 @@ static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, Eng
const Engine *e = Engine::Get(engine_type);
/* Default livery for spectators */
static const Livery default_livery = {
0, COLOUR_GREY, COLOUR_GREY,
PALETTE_RECOLOUR_START, SPR_2CCMAP_BASE, SPR_2CCMAP_BASE,
};
bool twocc = e->info.misc_flags.Test(EngineMiscFlag::Uses2CC);
const Livery *livery = Company::IsValidID(company) ? GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries) : &default_livery;
/* Check if we should use the colour map callback */
if (e->info.callback_mask.Test(VehicleCallbackMask::ColourRemap)) {
uint16_t callback = GetVehicleCallback(CBID_VEHICLE_COLOUR_MAPPING, 0, 0, engine_type, v);
@ -2090,6 +2099,15 @@ static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, Eng
/* If bit 14 is set, then the company colours are applied to the
* map else it's returned as-is. */
if (!HasBit(callback, 14)) {
/* Test if this is the standard remap/reversed remap */
if (map == PALETTE_RECOLOUR_START + (livery->colour1 & 0xF)) {
map = livery->cached_pal_1cc;
} else if (map == SPR_2CCMAP_BASE + (livery->colour1 & 0xF) + (livery->colour2 & 0xF) * 16) {
map = livery->cached_pal_2cc;
} else if (map == SPR_2CCMAP_BASE + (livery->colour2 & 0xF) + (livery->colour1 & 0xF) * 16) {
map = livery->cached_pal_2cr;
}
/* Update cache */
if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;
return map;
@ -2097,17 +2115,19 @@ static PaletteID GetEngineColourMap(EngineID engine_type, CompanyID company, Eng
}
}
bool twocc = e->info.misc_flags.Test(EngineMiscFlag::Uses2CC);
if (map == PAL_NONE) map = twocc ? (PaletteID)SPR_2CCMAP_BASE : (PaletteID)PALETTE_RECOLOUR_START;
/* Spectator has news shown too, but has invalid company ID - as well as dedicated server */
if (!Company::IsValidID(company)) return map;
const Livery *livery = GetEngineLivery(engine_type, company, parent_engine_type, v, _settings_client.gui.liveries);
map += livery->colour1;
if (twocc) map += livery->colour2 * 16;
if (map == PALETTE_RECOLOUR_START || map == SPR_2CCMAP_BASE) {
/* Use cached palette when using default remaps */
map = twocc ? livery->cached_pal_2cc : livery->cached_pal_1cc;
} else {
/* Add offsets for custom NewGRF remaps */
map += livery->colour1;
if (twocc) map += livery->colour2 * 16;
}
/* Update cache */
if (v != nullptr) const_cast<Vehicle *>(v)->colourmap = map;

View File

@ -300,8 +300,6 @@ void DrawFrameRect(int left, int top, int right, int bottom, Colours colour, Fra
if (flags.Test(FrameFlag::Transparent)) {
GfxFillRect(left, top, right, bottom, PALETTE_TO_TRANSPARENT, FILLRECT_RECOLOUR);
} else {
assert(colour < COLOUR_END);
const PixelColour dark = GetColourGradient(colour, SHADE_DARK);
const PixelColour medium_dark = GetColourGradient(colour, SHADE_LIGHT);
const PixelColour medium_light = GetColourGradient(colour, SHADE_LIGHTER);

View File

@ -75,6 +75,25 @@ enum CompanyFinancesWidgets : WidgetID {
};
/** Widgets of the #SelectCustomColourWindow class. */
enum SelectCustomColourWidgets {
WID_SCC_CAPTION, ///< Caption of window.
WID_SCC_HUE,
WID_SCC_SCROLLBAR_HUE, ///< Hue scrollbar.
WID_SCC_SAT,
WID_SCC_SCROLLBAR_SAT, ///< Saturation scrollbar.
WID_SCC_VAL,
WID_SCC_SCROLLBAR_VAL, ///< Value scrollbar.
WID_SCC_CON,
WID_SCC_SCROLLBAR_CON, ///< Contrast scrollbar.
WID_SCC_OUTPUT,
WID_SCC_DEFAULT, ///< First default colour.
WID_SCC_DEFAULT_LAST = WID_SCC_DEFAULT + COLOUR_END - 1,
WID_SCC_PRESETS, ///< First preset colour.
WID_SCC_PRESETS_LAST = WID_SCC_PRESETS + COLOUR_END - 1,
};
/** Widgets of the #SelectCompanyLiveryWindow class. */
enum SelectCompanyLiveryWidgets : WidgetID {
WID_SCL_CAPTION, ///< Caption of window.

View File

@ -231,6 +231,12 @@ enum WindowClass : uint16_t {
*/
WC_COMPANY_COLOUR,
/**
* Custom colour selection; %Window numbers:
* - #CompanyID = #SelectCustomColourWidgets
*/
WC_CUSTOM_COLOUR,
/**
* Alter company face window; %Window numbers:
* - #CompanyID = #SelectCompanyManagerFaceWidgets