mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-08-27 16:39:09 +00:00
Change: New company face definition system and UI. (#14319)
Bits used by company faces are now defined by a variable system instead of being hardcoded, allowing future expansion. The four face types covering gender and skin colour are now separate face styles with their own definitions.
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include "sound_func.h"
|
||||
#include "rail.h"
|
||||
#include "core/pool_func.hpp"
|
||||
#include "core/string_consumer.hpp"
|
||||
#include "settings_func.h"
|
||||
#include "vehicle_base.h"
|
||||
#include "vehicle_func.h"
|
||||
@@ -44,6 +45,7 @@
|
||||
#include "widgets/statusbar_widget.h"
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "table/company_face.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
||||
@@ -53,7 +55,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.
|
||||
CompanyManagerFace _company_manager_face; ///< for company manager face storage in openttd.cfg
|
||||
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
|
||||
|
||||
CompanyPool _company_pool("Company"); ///< Pool of companies.
|
||||
@@ -181,24 +183,12 @@ void DrawCompanyIcon(CompanyID c, int x, int y)
|
||||
*/
|
||||
static bool IsValidCompanyManagerFace(CompanyManagerFace cmf)
|
||||
{
|
||||
if (!AreCompanyManagerFaceBitsValid(cmf, CMFV_GEN_ETHN, GE_WM)) return false;
|
||||
if (cmf.style >= GetNumCompanyManagerFaceStyles()) return false;
|
||||
|
||||
GenderEthnicity ge = (GenderEthnicity)GetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, GE_WM);
|
||||
bool has_moustache = !HasBit(ge, GENDER_FEMALE) && GetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge) != 0;
|
||||
bool has_tie_earring = !HasBit(ge, GENDER_FEMALE) || GetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge) != 0;
|
||||
bool has_glasses = GetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge) != 0;
|
||||
|
||||
if (!AreCompanyManagerFaceBitsValid(cmf, CMFV_EYE_COLOUR, ge)) return false;
|
||||
for (CompanyManagerFaceVariable cmfv = CMFV_CHEEKS; cmfv < CMFV_END; cmfv++) {
|
||||
switch (cmfv) {
|
||||
case CMFV_MOUSTACHE: if (!has_moustache) continue; break;
|
||||
case CMFV_LIPS:
|
||||
case CMFV_NOSE: if (has_moustache) continue; break;
|
||||
case CMFV_TIE_EARRING: if (!has_tie_earring) continue; break;
|
||||
case CMFV_GLASSES: if (!has_glasses) continue; break;
|
||||
default: break;
|
||||
}
|
||||
if (!AreCompanyManagerFaceBitsValid(cmf, cmfv, ge)) return false;
|
||||
/* Test if each enabled part is valid. */
|
||||
FaceVars vars = GetCompanyManagerFaceVars(cmf.style);
|
||||
for (uint var : SetBitIterator(GetActiveFaceVars(cmf, vars))) {
|
||||
if (!vars[var].IsValid(cmf)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -633,11 +623,15 @@ Company *DoStartupNewCompany(bool is_ai, CompanyID company = CompanyID::Invalid(
|
||||
|
||||
/* If starting a player company in singleplayer and a favorite company manager face is selected, choose it. Otherwise, use a random face.
|
||||
* In a network game, we'll choose the favorite face later in CmdCompanyCtrl to sync it to all clients. */
|
||||
if (_company_manager_face != 0 && !is_ai && !_networking) {
|
||||
c->face = _company_manager_face;
|
||||
} else {
|
||||
RandomCompanyManagerFaceBits(c->face, (GenderEthnicity)Random(), false, _random);
|
||||
bool randomise_face = true;
|
||||
if (!_company_manager_face.empty() && !is_ai && !_networking) {
|
||||
auto cmf = ParseCompanyManagerFaceCode(_company_manager_face);
|
||||
if (cmf.has_value()) {
|
||||
randomise_face = false;
|
||||
c->face = std::move(*cmf);
|
||||
}
|
||||
}
|
||||
if (randomise_face) RandomiseCompanyManagerFace(c->face, _random);
|
||||
|
||||
SetDefaultCompanySettings(c->index);
|
||||
ClearEnginesHiddenFlagOfCompany(c->index);
|
||||
@@ -926,7 +920,12 @@ CommandCost CmdCompanyCtrl(DoCommandFlags flags, CompanyCtrlAction cca, CompanyI
|
||||
* its configuration and we are currently in the execution of a command, we have
|
||||
* to circumvent the normal ::Post logic for commands and just send the command.
|
||||
*/
|
||||
if (_company_manager_face != 0) Command<CMD_SET_COMPANY_MANAGER_FACE>::SendNet(STR_NULL, c->index, _company_manager_face);
|
||||
if (!_company_manager_face.empty()) {
|
||||
auto cmf = ParseCompanyManagerFaceCode(_company_manager_face);
|
||||
if (cmf.has_value()) {
|
||||
Command<CMD_SET_COMPANY_MANAGER_FACE>::SendNet(STR_NULL, c->index, cmf->bits, cmf->style);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now that we have a new company, broadcast our company settings to
|
||||
* all clients so everything is in sync */
|
||||
@@ -1049,15 +1048,20 @@ CommandCost CmdCompanyAllowListCtrl(DoCommandFlags flags, CompanyAllowListCtrlAc
|
||||
/**
|
||||
* Change the company manager's face.
|
||||
* @param flags operation to perform
|
||||
* @param cmf face bitmasked
|
||||
* @param bits The bits of company manager face.
|
||||
* @param style The style of the company manager face.
|
||||
* @return the cost of this operation or an error
|
||||
*/
|
||||
CommandCost CmdSetCompanyManagerFace(DoCommandFlags flags, CompanyManagerFace cmf)
|
||||
CommandCost CmdSetCompanyManagerFace(DoCommandFlags flags, uint32_t bits, uint style)
|
||||
{
|
||||
if (!IsValidCompanyManagerFace(cmf)) return CMD_ERROR;
|
||||
CompanyManagerFace tmp_face{style, bits, {}};
|
||||
if (!IsValidCompanyManagerFace(tmp_face)) return CMD_ERROR;
|
||||
|
||||
if (flags.Test(DoCommandFlag::Execute)) {
|
||||
Company::Get(_current_company)->face = cmf;
|
||||
CompanyManagerFace &cmf = Company::Get(_current_company)->face;
|
||||
SetCompanyManagerFaceStyle(cmf, style);
|
||||
cmf.bits = tmp_face.bits;
|
||||
|
||||
MarkWholeScreenDirty();
|
||||
}
|
||||
return CommandCost();
|
||||
@@ -1375,3 +1379,157 @@ CompanyID GetFirstPlayableCompanyID()
|
||||
|
||||
return CompanyID::Begin();
|
||||
}
|
||||
|
||||
static std::vector<FaceSpec> _faces; ///< All company manager face styles.
|
||||
|
||||
/**
|
||||
* Reset company manager face styles to default.
|
||||
*/
|
||||
void ResetFaces()
|
||||
{
|
||||
_faces.clear();
|
||||
_faces.assign(std::begin(_original_faces), std::end(_original_faces));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of company manager face styles.
|
||||
* @return Number of face styles.
|
||||
*/
|
||||
uint GetNumCompanyManagerFaceStyles()
|
||||
{
|
||||
return static_cast<uint>(std::size(_faces));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the definition of a company manager face style.
|
||||
* @param style_index Face style to get.
|
||||
* @return Definition of face style.
|
||||
*/
|
||||
const FaceSpec *GetCompanyManagerFaceSpec(uint style_index)
|
||||
{
|
||||
if (style_index < GetNumCompanyManagerFaceStyles()) return &_faces[style_index];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a company manager face style by label.
|
||||
* @param label Label to find.
|
||||
* @return Index of face style if label is found, otherwise std::nullopt.
|
||||
*/
|
||||
std::optional<uint> FindCompanyManagerFaceLabel(std::string_view label)
|
||||
{
|
||||
auto it = std::ranges::find(_faces, label, &FaceSpec::label);
|
||||
if (it == std::end(_faces)) return std::nullopt;
|
||||
|
||||
return static_cast<uint>(std::distance(std::begin(_faces), it));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the face variables for a face style.
|
||||
* @param style_index Face style to get variables for.
|
||||
* @return Variables for the face style.
|
||||
*/
|
||||
FaceVars GetCompanyManagerFaceVars(uint style)
|
||||
{
|
||||
const FaceSpec *spec = GetCompanyManagerFaceSpec(style);
|
||||
if (spec == nullptr) return {};
|
||||
return spec->GetFaceVars();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a company face style.
|
||||
* Changes both the style index and the label.
|
||||
* @param cmf The CompanyManagerFace to change.
|
||||
* @param style The style to set.
|
||||
*/
|
||||
void SetCompanyManagerFaceStyle(CompanyManagerFace &cmf, uint style)
|
||||
{
|
||||
const FaceSpec *spec = GetCompanyManagerFaceSpec(style);
|
||||
assert(spec != nullptr);
|
||||
|
||||
cmf.style = style;
|
||||
cmf.style_label = spec->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely randomise a company manager face, including style.
|
||||
* @note randomizer should passed be appropriate for server-side or client-side usage.
|
||||
* @param cmf The CompanyManagerFace to randomise.
|
||||
* @param randomizer The randomizer to use.
|
||||
*/
|
||||
void RandomiseCompanyManagerFace(CompanyManagerFace &cmf, Randomizer &randomizer)
|
||||
{
|
||||
SetCompanyManagerFaceStyle(cmf, randomizer.Next(GetNumCompanyManagerFaceStyles()));
|
||||
RandomiseCompanyManagerFaceBits(cmf, GetCompanyManagerFaceVars(cmf.style), randomizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mask company manager face bits to ensure they are all within range.
|
||||
* @note Does not update the CompanyManagerFace itself. Unused bits are cleared.
|
||||
* @param cmf The CompanyManagerFace.
|
||||
* @param style The face variables.
|
||||
* @return The masked face bits.
|
||||
*/
|
||||
uint32_t MaskCompanyManagerFaceBits(const CompanyManagerFace &cmf, FaceVars vars)
|
||||
{
|
||||
CompanyManagerFace face{};
|
||||
|
||||
for (auto var : SetBitIterator(GetActiveFaceVars(cmf, vars))) {
|
||||
vars[var].SetBits(face, vars[var].GetBits(cmf));
|
||||
}
|
||||
|
||||
return face.bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a face code representation of a company manager face.
|
||||
* @param cmf The company manager face.
|
||||
* @return String containing face code.
|
||||
*/
|
||||
std::string FormatCompanyManagerFaceCode(const CompanyManagerFace &cmf)
|
||||
{
|
||||
uint32_t masked_face_bits = MaskCompanyManagerFaceBits(cmf, GetCompanyManagerFaceVars(cmf.style));
|
||||
return fmt::format("{}:{}", cmf.style_label, masked_face_bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a face code into a company manager face.
|
||||
* @param str Face code to parse.
|
||||
* @return Company manager face, or std::nullopt if it could not be parsed.
|
||||
*/
|
||||
std::optional<CompanyManagerFace> ParseCompanyManagerFaceCode(std::string_view str)
|
||||
{
|
||||
if (str.empty()) return std::nullopt;
|
||||
|
||||
CompanyManagerFace cmf;
|
||||
StringConsumer consumer{str};
|
||||
if (consumer.FindChar(':') != StringConsumer::npos) {
|
||||
auto label = consumer.ReadUntilChar(':', StringConsumer::SKIP_ONE_SEPARATOR);
|
||||
|
||||
/* Read numeric part and ensure it's valid. */
|
||||
auto bits = consumer.TryReadIntegerBase<uint32_t>(10, true);
|
||||
if (!bits.has_value() || consumer.AnyBytesLeft()) return std::nullopt;
|
||||
|
||||
/* Ensure style laberl is valid. */
|
||||
auto style = FindCompanyManagerFaceLabel(label);
|
||||
if (!style.has_value()) return std::nullopt;
|
||||
|
||||
SetCompanyManagerFaceStyle(cmf, *style);
|
||||
cmf.bits = *bits;
|
||||
} else {
|
||||
/* No ':' included, treat as numeric-only. This allows old-style codes to be entered. */
|
||||
auto bits = ParseInteger(str, 10, true);
|
||||
if (!bits.has_value()) return std::nullopt;
|
||||
|
||||
/* Old codes use bits 0..1 to represent face style. These map directly to the default face styles. */
|
||||
SetCompanyManagerFaceStyle(cmf, GB(*bits, 0, 2));
|
||||
cmf.bits = *bits;
|
||||
}
|
||||
|
||||
/* Force the face bits to be valid. */
|
||||
FaceVars vars = GetCompanyManagerFaceVars(cmf.style);
|
||||
ScaleAllCompanyManagerFaceBits(cmf, vars);
|
||||
cmf.bits = MaskCompanyManagerFaceBits(cmf, vars);
|
||||
|
||||
return cmf;
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@ CommandCost CmdCompanyAllowListCtrl(DoCommandFlags flags, CompanyAllowListCtrlAc
|
||||
CommandCost CmdGiveMoney(DoCommandFlags flags, Money money, CompanyID dest_company);
|
||||
CommandCost CmdRenameCompany(DoCommandFlags flags, const std::string &text);
|
||||
CommandCost CmdRenamePresident(DoCommandFlags flags, const std::string &text);
|
||||
CommandCost CmdSetCompanyManagerFace(DoCommandFlags flags, CompanyManagerFace cmf);
|
||||
CommandCost CmdSetCompanyManagerFace(DoCommandFlags flags, uint style, uint32_t bits);
|
||||
CommandCost CmdSetCompanyColour(DoCommandFlags flags, LiveryScheme scheme, bool primary, Colours colour);
|
||||
|
||||
DEF_CMD_TRAIT(CMD_COMPANY_CTRL, CmdCompanyCtrl, CommandFlags({CommandFlag::Spectator, CommandFlag::ClientID, CommandFlag::NoEst}), CMDT_SERVER_SETTING)
|
||||
|
@@ -37,7 +37,7 @@ extern CompanyID _local_company;
|
||||
extern CompanyID _current_company;
|
||||
|
||||
extern TypedIndexContainer<std::array<Colours, MAX_COMPANIES>, CompanyID> _company_colours;
|
||||
extern CompanyManagerFace _company_manager_face;
|
||||
extern std::string _company_manager_face;
|
||||
PaletteID GetCompanyPalette(CompanyID company);
|
||||
|
||||
/**
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "currency.h"
|
||||
#include "error.h"
|
||||
#include "gui.h"
|
||||
#include "settings_gui.h"
|
||||
#include "window_gui.h"
|
||||
#include "textbuf_gui.h"
|
||||
#include "viewport_func.h"
|
||||
@@ -1116,45 +1117,46 @@ void ShowCompanyLiveryWindow(CompanyID company, GroupID group)
|
||||
* @param colour the (background) colour of the gradient
|
||||
* @param r position to draw the face
|
||||
*/
|
||||
void DrawCompanyManagerFace(CompanyManagerFace cmf, Colours colour, const Rect &r)
|
||||
void DrawCompanyManagerFace(const CompanyManagerFace &cmf, Colours colour, const Rect &r)
|
||||
{
|
||||
GenderEthnicity ge = (GenderEthnicity)GetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, GE_WM);
|
||||
|
||||
/* Determine offset from centre of drawing rect. */
|
||||
Dimension d = GetSpriteSize(SPR_GRADIENT);
|
||||
int x = CentreBounds(r.left, r.right, d.width);
|
||||
int y = CentreBounds(r.top, r.bottom, d.height);
|
||||
|
||||
bool has_moustache = !HasBit(ge, GENDER_FEMALE) && GetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge) != 0;
|
||||
bool has_tie_earring = !HasBit(ge, GENDER_FEMALE) || GetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge) != 0;
|
||||
bool has_glasses = GetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge) != 0;
|
||||
PaletteID pal;
|
||||
FaceVars vars = GetCompanyManagerFaceVars(cmf.style);
|
||||
|
||||
/* Modify eye colour palette only if 2 or more valid values exist */
|
||||
if (_cmf_info[CMFV_EYE_COLOUR].valid_values[ge] < 2) {
|
||||
pal = PAL_NONE;
|
||||
} else {
|
||||
switch (GetCompanyManagerFaceBits(cmf, CMFV_EYE_COLOUR, ge)) {
|
||||
/* First determine which parts are enabled. */
|
||||
uint64_t active_vars = GetActiveFaceVars(cmf, vars);
|
||||
|
||||
std::unordered_map<uint8_t, PaletteID> palettes;
|
||||
|
||||
/* Second, get palettes. */
|
||||
for (auto var : SetBitIterator(active_vars)) {
|
||||
if (vars[var].type != FaceVarType::Palette) continue;
|
||||
|
||||
PaletteID pal = PAL_NONE;
|
||||
switch (vars[var].GetBits(cmf)) {
|
||||
default: NOT_REACHED();
|
||||
case 0: pal = PALETTE_TO_BROWN; break;
|
||||
case 1: pal = PALETTE_TO_BLUE; break;
|
||||
case 2: pal = PALETTE_TO_GREEN; break;
|
||||
}
|
||||
for (uint8_t affected_var : SetBitIterator(std::get<uint64_t>(vars[var].data))) {
|
||||
palettes[affected_var] = pal;
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw the gradient (background) */
|
||||
DrawSprite(SPR_GRADIENT, GetColourPalette(colour), x, y);
|
||||
|
||||
for (CompanyManagerFaceVariable cmfv = CMFV_CHEEKS; cmfv < CMFV_END; cmfv++) {
|
||||
switch (cmfv) {
|
||||
case CMFV_MOUSTACHE: if (!has_moustache) continue; break;
|
||||
case CMFV_LIPS:
|
||||
case CMFV_NOSE: if (has_moustache) continue; break;
|
||||
case CMFV_TIE_EARRING: if (!has_tie_earring) continue; break;
|
||||
case CMFV_GLASSES: if (!has_glasses) continue; break;
|
||||
default: break;
|
||||
}
|
||||
DrawSprite(GetCompanyManagerFaceSprite(cmf, cmfv, ge), (cmfv == CMFV_EYEBROWS) ? pal : PAL_NONE, x, y);
|
||||
/* Thirdly, draw sprites. */
|
||||
for (auto var : SetBitIterator(active_vars)) {
|
||||
if (vars[var].type != FaceVarType::Sprite) continue;
|
||||
|
||||
auto it = palettes.find(var);
|
||||
PaletteID pal = (it == std::end(palettes)) ? PAL_NONE : it->second;
|
||||
DrawSprite(vars[var].GetSprite(cmf), pal, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1165,153 +1167,40 @@ static constexpr NWidgetPart _nested_select_company_manager_face_widgets[] = {
|
||||
NWidget(WWT_CAPTION, COLOUR_GREY, WID_SCMF_CAPTION), SetStringTip(STR_FACE_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
|
||||
NWidget(WWT_IMGBTN, COLOUR_GREY, WID_SCMF_TOGGLE_LARGE_SMALL), SetSpriteTip(SPR_LARGE_SMALL_WINDOW, STR_FACE_ADVANCED_TOOLTIP), SetAspect(WidgetDimensions::ASPECT_TOGGLE_SIZE),
|
||||
EndContainer(),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_SCMF_SELECT_FACE),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPadding(2),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PANEL, COLOUR_GREY, WID_SCMF_SELECT_FACE),
|
||||
/* Left side */
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
|
||||
NWidget(NWID_HORIZONTAL), SetPIPRatio(1, 0, 1),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_FACE), SetMinimalSize(92, 119), SetFill(1, 0),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0), SetPadding(4),
|
||||
NWidget(WWT_EMPTY, INVALID_COLOUR, WID_SCMF_FACE), SetMinimalSize(92, 119), SetFill(1, 0),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_RANDOM_NEW_FACE), SetFill(1, 0), SetStringTip(STR_FACE_NEW_FACE_BUTTON, STR_FACE_NEW_FACE_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON), SetFill(1, 0), SetStringTip(STR_FACE_ADVANCED, STR_FACE_ADVANCED_TOOLTIP),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_LOADSAVE), // Load/number/save buttons under the portrait in the advanced view.
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, 0, 0), SetPIPRatio(1, 0, 1),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_SPACER), SetFill(1, 1), SetResize(0, 1),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LOAD), SetFill(1, 0), SetStringTip(STR_FACE_LOAD, STR_FACE_LOAD_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_FACECODE), SetFill(1, 0), SetStringTip(STR_FACE_FACECODE, STR_FACE_FACECODE_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_SAVE), SetFill(1, 0), SetStringTip(STR_FACE_SAVE, STR_FACE_SAVE_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
/* Right side */
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON), SetFill(1, 0), SetStringTip(STR_FACE_ADVANCED, STR_FACE_ADVANCED_TOOLTIP),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_MALEFEMALE), // Simple male/female face setting.
|
||||
NWidget(NWID_VERTICAL), SetPIPRatio(1, 0, 1),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_MALE), SetFill(1, 0), SetStringTip(STR_FACE_MALE_BUTTON, STR_FACE_MALE_TOOLTIP),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_FEMALE), SetFill(1, 0), SetStringTip(STR_FACE_FEMALE_BUTTON, STR_FACE_FEMALE_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_PARTS), // Advanced face parts setting.
|
||||
NWidget(NWID_VERTICAL), SetPIP(0, WidgetDimensions::unscaled.vsep_normal, 0),
|
||||
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_MALE2), SetFill(1, 0), SetStringTip(STR_FACE_MALE_BUTTON, STR_FACE_MALE_TOOLTIP),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_FEMALE2), SetFill(1, 0), SetStringTip(STR_FACE_FEMALE_BUTTON, STR_FACE_FEMALE_TOOLTIP),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_ETHNICITY_EUR), SetFill(1, 0), SetStringTip(STR_FACE_EUROPEAN, STR_FACE_EUROPEAN_TOOLTIP),
|
||||
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCMF_ETHNICITY_AFR), SetFill(1, 0), SetStringTip(STR_FACE_AFRICAN, STR_FACE_AFRICAN_TOOLTIP),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_EYECOLOUR), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_MOUSTACHE_EARRING), SetToolTip(STR_FACE_MOUSTACHE_EARRING_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAS_GLASSES_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_GLASSES), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAS_GLASSES), SetToolTip(STR_FACE_GLASSES_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_HAIR_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_HAIR), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_L), SetArrowWidgetTypeTip(AWV_DECREASE, STR_FACE_HAIR_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_HAIR), SetToolTip(STR_FACE_HAIR_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_HAIR_R), SetArrowWidgetTypeTip(AWV_INCREASE, STR_FACE_HAIR_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYEBROWS_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_EYEBROWS), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_L), SetArrowWidgetTypeTip(AWV_DECREASE, STR_FACE_EYEBROWS_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYEBROWS), SetToolTip(STR_FACE_EYEBROWS_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYEBROWS_R), SetArrowWidgetTypeTip(AWV_INCREASE, STR_FACE_EYEBROWS_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_EYECOLOUR_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_EYECOLOUR), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_L), SetArrowWidgetTypeTip(AWV_DECREASE, STR_FACE_EYECOLOUR_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR), SetToolTip(STR_FACE_EYECOLOUR_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_EYECOLOUR_R), SetArrowWidgetTypeTip(AWV_INCREASE, STR_FACE_EYECOLOUR_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_GLASSES_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_GLASSES), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_L), SetArrowWidgetTypeTip(AWV_DECREASE, STR_FACE_GLASSES_TOOLTIP_2),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_GLASSES), SetToolTip(STR_FACE_GLASSES_TOOLTIP_2), SetTextStyle(TC_WHITE),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_GLASSES_R), SetArrowWidgetTypeTip(AWV_INCREASE, STR_FACE_GLASSES_TOOLTIP_2),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_NOSE_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_NOSE), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_L), SetArrowWidgetTypeTip(AWV_DECREASE, STR_FACE_NOSE_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_NOSE), SetToolTip(STR_FACE_NOSE_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_NOSE_R), SetArrowWidgetTypeTip(AWV_INCREASE, STR_FACE_NOSE_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_LIPS_MOUSTACHE_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_MOUSTACHE), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_L), SetArrowWidgetTypeTip(AWV_DECREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE), SetToolTip(STR_FACE_LIPS_MOUSTACHE_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_LIPS_MOUSTACHE_R), SetArrowWidgetTypeTip(AWV_INCREASE, STR_FACE_LIPS_MOUSTACHE_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_CHIN_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_CHIN), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_L), SetArrowWidgetTypeTip(AWV_DECREASE, STR_FACE_CHIN_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CHIN), SetToolTip(STR_FACE_CHIN_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_CHIN_R), SetArrowWidgetTypeTip(AWV_INCREASE, STR_FACE_CHIN_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_JACKET_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_JACKET), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_L), SetArrowWidgetTypeTip(AWV_DECREASE, STR_FACE_JACKET_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_JACKET), SetToolTip(STR_FACE_JACKET_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_JACKET_R), SetArrowWidgetTypeTip(AWV_INCREASE, STR_FACE_JACKET_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_COLLAR_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_COLLAR), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_L), SetArrowWidgetTypeTip(AWV_DECREASE, STR_FACE_COLLAR_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_COLLAR), SetToolTip(STR_FACE_COLLAR_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_COLLAR_R), SetArrowWidgetTypeTip(AWV_INCREASE, STR_FACE_COLLAR_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0),
|
||||
NWidget(WWT_TEXT, INVALID_COLOUR, WID_SCMF_TIE_EARRING_TEXT), SetFill(1, 0),
|
||||
SetStringTip(STR_FACE_EARRING), SetTextStyle(TC_GOLD), SetAlignment(SA_VERT_CENTER | SA_RIGHT),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_L), SetArrowWidgetTypeTip(AWV_DECREASE, STR_FACE_TIE_EARRING_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING), SetToolTip(STR_FACE_TIE_EARRING_TOOLTIP), SetTextStyle(TC_WHITE),
|
||||
NWidget(WWT_PUSHARROWBTN, COLOUR_GREY, WID_SCMF_TIE_EARRING_R), SetArrowWidgetTypeTip(AWV_INCREASE, STR_FACE_TIE_EARRING_TOOLTIP),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
/* Right side */
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_PARTS), // Advanced face parts setting.
|
||||
NWidget(NWID_VERTICAL),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_SCMF_STYLE), SetResize(1, 0), SetFill(1, 0), SetMatrixDataTip(1, 1),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
NWidget(WWT_MATRIX, COLOUR_GREY, WID_SCMF_PARTS), SetResize(1, 1), SetFill(1, 1), SetMatrixDataTip(1, 0), SetScrollbar(WID_SCMF_PARTS_SCROLLBAR),
|
||||
NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCMF_PARTS_SCROLLBAR),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
NWidget(NWID_HORIZONTAL, NWidContainerFlag::EqualSize),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CANCEL), SetFill(1, 0), SetStringTip(STR_BUTTON_CANCEL, STR_FACE_CANCEL_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_ACCEPT), SetFill(1, 0), SetStringTip(STR_BUTTON_OK, STR_FACE_OK_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_CANCEL), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_BUTTON_CANCEL, STR_FACE_CANCEL_TOOLTIP),
|
||||
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCMF_ACCEPT), SetFill(1, 0), SetResize(1, 0), SetStringTip(STR_BUTTON_OK, STR_FACE_OK_TOOLTIP),
|
||||
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_SCMF_SEL_RESIZE),
|
||||
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
@@ -1320,43 +1209,34 @@ class SelectCompanyManagerFaceWindow : public Window
|
||||
{
|
||||
CompanyManagerFace face{}; ///< company manager face bits
|
||||
bool advanced = false; ///< advanced company manager face selection window
|
||||
uint selected_var = UINT_MAX; ///< Currently selected face variable. `UINT_MAX` for none, `UINT_MAX - 1` means style is clicked instead.
|
||||
uint click_state = 0; ///< Click state on selected face variable.
|
||||
int line_height = 0; ///< Height of each face variable row.
|
||||
|
||||
GenderEthnicity ge{}; ///< Gender and ethnicity.
|
||||
bool is_female = false; ///< Female face.
|
||||
bool is_moust_male = false; ///< Male face with a moustache.
|
||||
|
||||
Dimension yesno_dim{}; ///< Dimension of a yes/no button of a part in the advanced face window.
|
||||
Dimension number_dim{}; ///< Dimension of a number widget of a part in the advanced face window.
|
||||
std::vector<const FaceVar *> face_vars; ///< Visible face variables.
|
||||
|
||||
/**
|
||||
* Get the string for the value of face control buttons.
|
||||
*
|
||||
* @param widget_index index of this widget in the window
|
||||
* @param stringid formatting string for the button.
|
||||
* @param val the value which will be displayed
|
||||
* @param is_bool_widget is it a bool button
|
||||
* Make face bits valid and update visible face variables.
|
||||
*/
|
||||
std::string GetFaceString(WidgetID widget_index, uint8_t val, bool is_bool_widget) const
|
||||
{
|
||||
const NWidgetCore *nwi_widget = this->GetWidget<NWidgetCore>(widget_index);
|
||||
if (nwi_widget->IsDisabled()) return {};
|
||||
|
||||
/* If it a bool button write yes or no. */
|
||||
if (is_bool_widget) return GetString((val != 0) ? STR_FACE_YES : STR_FACE_NO);
|
||||
|
||||
/* Else write the value + 1. */
|
||||
return GetString(STR_JUST_INT, val + 1);
|
||||
}
|
||||
|
||||
void UpdateData()
|
||||
{
|
||||
this->ge = (GenderEthnicity)GB(this->face, _cmf_info[CMFV_GEN_ETHN].offset, _cmf_info[CMFV_GEN_ETHN].length); // get the gender and ethnicity
|
||||
this->is_female = HasBit(this->ge, GENDER_FEMALE); // get the gender: 0 == male and 1 == female
|
||||
this->is_moust_male = !is_female && GetCompanyManagerFaceBits(this->face, CMFV_HAS_MOUSTACHE, this->ge) != 0; // is a male face with moustache
|
||||
FaceVars vars = GetCompanyManagerFaceVars(this->face.style);
|
||||
ScaleAllCompanyManagerFaceBits(this->face, vars);
|
||||
|
||||
this->GetWidget<NWidgetCore>(WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT)->SetString(this->is_female ? STR_FACE_EARRING : STR_FACE_MOUSTACHE);
|
||||
this->GetWidget<NWidgetCore>(WID_SCMF_TIE_EARRING_TEXT)->SetString(this->is_female ? STR_FACE_EARRING : STR_FACE_TIE);
|
||||
this->GetWidget<NWidgetCore>(WID_SCMF_LIPS_MOUSTACHE_TEXT)->SetString(this->is_moust_male ? STR_FACE_MOUSTACHE : STR_FACE_LIPS);
|
||||
uint64_t active_vars = GetActiveFaceVars(this->face, vars);
|
||||
/* Exclude active parts which have no string. */
|
||||
for (auto var : SetBitIterator(active_vars)) {
|
||||
if (vars[var].name == STR_NULL) ClrBit(active_vars, var);
|
||||
}
|
||||
|
||||
/* Rebuild the sorted list of face variable pointers. */
|
||||
this->face_vars.clear();
|
||||
for (auto var : SetBitIterator(active_vars)) {
|
||||
this->face_vars.emplace_back(&vars[var]);
|
||||
}
|
||||
std::ranges::sort(this->face_vars, std::less{}, &FaceVar::position);
|
||||
|
||||
this->GetScrollbar(WID_SCMF_PARTS_SCROLLBAR)->SetCount(std::size(this->face_vars));
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -1372,16 +1252,20 @@ public:
|
||||
this->UpdateData();
|
||||
}
|
||||
|
||||
void OnInit() override
|
||||
{
|
||||
this->line_height = std::max(SETTING_BUTTON_HEIGHT, GetCharacterHeight(FS_NORMAL)) + WidgetDimensions::scaled.matrix.Vertical();
|
||||
}
|
||||
|
||||
/**
|
||||
* Select planes to display to the user with the #NWID_SELECTION widgets #WID_SCMF_SEL_LOADSAVE, #WID_SCMF_SEL_MALEFEMALE, and #WID_SCMF_SEL_PARTS.
|
||||
* @param advanced Display advanced face management window.
|
||||
*/
|
||||
void SelectDisplayPlanes(bool advanced)
|
||||
{
|
||||
this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_LOADSAVE)->SetDisplayedPlane(advanced ? 0 : SZSP_NONE);
|
||||
this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_LOADSAVE)->SetDisplayedPlane(advanced ? 0 : SZSP_HORIZONTAL);
|
||||
this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_PARTS)->SetDisplayedPlane(advanced ? 0 : SZSP_NONE);
|
||||
this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_MALEFEMALE)->SetDisplayedPlane(advanced ? SZSP_NONE : 0);
|
||||
this->GetWidget<NWidgetCore>(WID_SCMF_RANDOM_NEW_FACE)->SetString(advanced ? STR_FACE_RANDOM : STR_FACE_NEW_FACE_BUTTON);
|
||||
this->GetWidget<NWidgetStacked>(WID_SCMF_SEL_RESIZE)->SetDisplayedPlane(advanced ? 0 : SZSP_NONE);
|
||||
|
||||
NWidgetCore *wi = this->GetWidget<NWidgetCore>(WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON);
|
||||
if (advanced) {
|
||||
@@ -1391,185 +1275,99 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void OnInit() override
|
||||
static StringID GetLongestString(StringID a, StringID b)
|
||||
{
|
||||
/* Size of the boolean yes/no button. */
|
||||
Dimension yesno_dim = maxdim(GetStringBoundingBox(STR_FACE_YES), GetStringBoundingBox(STR_FACE_NO));
|
||||
yesno_dim.width += WidgetDimensions::scaled.framerect.Horizontal();
|
||||
yesno_dim.height += WidgetDimensions::scaled.framerect.Vertical();
|
||||
/* Size of the number button + arrows. */
|
||||
Dimension number_dim = {0, 0};
|
||||
for (int val = 1; val <= 12; val++) {
|
||||
number_dim = maxdim(number_dim, GetStringBoundingBox(GetString(STR_JUST_INT, val)));
|
||||
}
|
||||
uint arrows_width = GetSpriteSize(SPR_ARROW_LEFT).width + GetSpriteSize(SPR_ARROW_RIGHT).width + 2 * (WidgetDimensions::scaled.imgbtn.Horizontal());
|
||||
number_dim.width += WidgetDimensions::scaled.framerect.Horizontal() + arrows_width;
|
||||
number_dim.height += WidgetDimensions::scaled.framerect.Vertical();
|
||||
/* Compute width of both buttons. */
|
||||
yesno_dim.width = std::max(yesno_dim.width, number_dim.width);
|
||||
number_dim.width = yesno_dim.width - arrows_width;
|
||||
return GetStringBoundingBox(a).width > GetStringBoundingBox(b).width ? a : b;
|
||||
}
|
||||
|
||||
this->yesno_dim = yesno_dim;
|
||||
this->number_dim = number_dim;
|
||||
static uint GetMaximumFacePartsWidth()
|
||||
{
|
||||
StringID yes_no = GetLongestString(STR_FACE_YES, STR_FACE_NO);
|
||||
|
||||
uint width = 0;
|
||||
for (uint style_index = 0; style_index != GetNumCompanyManagerFaceStyles(); ++style_index) {
|
||||
FaceVars vars = GetCompanyManagerFaceVars(style_index);
|
||||
for (const auto &info : vars) {
|
||||
if (info.name == STR_NULL) continue;
|
||||
if (info.type == FaceVarType::Toggle) {
|
||||
width = std::max(width, GetStringBoundingBox(GetString(STR_FACE_SETTING_TOGGLE, info.name, yes_no)).width);
|
||||
} else {
|
||||
uint64_t max_digits = GetParamMaxValue(info.valid_values);
|
||||
width = std::max(width, GetStringBoundingBox(GetString(STR_FACE_SETTING_NUMERIC, info.name, max_digits, max_digits)).width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Include width of button and spacing. */
|
||||
width += SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_wide + WidgetDimensions::scaled.frametext.Horizontal();
|
||||
return width;
|
||||
}
|
||||
|
||||
void UpdateWidgetSize(WidgetID widget, Dimension &size, [[maybe_unused]] const Dimension &padding, [[maybe_unused]] Dimension &fill, [[maybe_unused]] Dimension &resize) override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT:
|
||||
size = maxdim(size, GetStringBoundingBox(STR_FACE_EARRING));
|
||||
size = maxdim(size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
|
||||
break;
|
||||
|
||||
case WID_SCMF_TIE_EARRING_TEXT:
|
||||
size = maxdim(size, GetStringBoundingBox(STR_FACE_EARRING));
|
||||
size = maxdim(size, GetStringBoundingBox(STR_FACE_TIE));
|
||||
break;
|
||||
|
||||
case WID_SCMF_LIPS_MOUSTACHE_TEXT:
|
||||
size = maxdim(size, GetStringBoundingBox(STR_FACE_LIPS));
|
||||
size = maxdim(size, GetStringBoundingBox(STR_FACE_MOUSTACHE));
|
||||
break;
|
||||
|
||||
case WID_SCMF_FACE:
|
||||
size = maxdim(size, GetScaledSpriteSize(SPR_GRADIENT));
|
||||
break;
|
||||
|
||||
case WID_SCMF_HAS_MOUSTACHE_EARRING:
|
||||
case WID_SCMF_HAS_GLASSES:
|
||||
size = this->yesno_dim;
|
||||
case WID_SCMF_STYLE:
|
||||
size.height = this->line_height;
|
||||
break;
|
||||
|
||||
case WID_SCMF_EYECOLOUR:
|
||||
case WID_SCMF_CHIN:
|
||||
case WID_SCMF_EYEBROWS:
|
||||
case WID_SCMF_LIPS_MOUSTACHE:
|
||||
case WID_SCMF_NOSE:
|
||||
case WID_SCMF_HAIR:
|
||||
case WID_SCMF_JACKET:
|
||||
case WID_SCMF_COLLAR:
|
||||
case WID_SCMF_TIE_EARRING:
|
||||
case WID_SCMF_GLASSES:
|
||||
size = this->number_dim;
|
||||
case WID_SCMF_PARTS:
|
||||
fill.height = resize.height = this->line_height;
|
||||
size.width = GetMaximumFacePartsWidth();
|
||||
size.height = resize.height * 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnPaint() override
|
||||
{
|
||||
/* lower the non-selected gender button */
|
||||
this->SetWidgetsLoweredState(!this->is_female, WID_SCMF_MALE, WID_SCMF_MALE2);
|
||||
this->SetWidgetsLoweredState( this->is_female, WID_SCMF_FEMALE, WID_SCMF_FEMALE2);
|
||||
|
||||
/* advanced company manager face selection window */
|
||||
|
||||
/* lower the non-selected ethnicity button */
|
||||
this->SetWidgetLoweredState(WID_SCMF_ETHNICITY_EUR, !HasBit(this->ge, ETHNICITY_BLACK));
|
||||
this->SetWidgetLoweredState(WID_SCMF_ETHNICITY_AFR, HasBit(this->ge, ETHNICITY_BLACK));
|
||||
|
||||
|
||||
/* Disable dynamically the widgets which CompanyManagerFaceVariable has less than 2 options
|
||||
* (or in other words you haven't any choice).
|
||||
* If the widgets depend on a HAS-variable and this is false the widgets will be disabled, too. */
|
||||
|
||||
/* Eye colour buttons */
|
||||
this->SetWidgetsDisabledState(_cmf_info[CMFV_EYE_COLOUR].valid_values[this->ge] < 2,
|
||||
WID_SCMF_EYECOLOUR, WID_SCMF_EYECOLOUR_L, WID_SCMF_EYECOLOUR_R);
|
||||
|
||||
/* Chin buttons */
|
||||
this->SetWidgetsDisabledState(_cmf_info[CMFV_CHIN].valid_values[this->ge] < 2,
|
||||
WID_SCMF_CHIN, WID_SCMF_CHIN_L, WID_SCMF_CHIN_R);
|
||||
|
||||
/* Eyebrows buttons */
|
||||
this->SetWidgetsDisabledState(_cmf_info[CMFV_EYEBROWS].valid_values[this->ge] < 2,
|
||||
WID_SCMF_EYEBROWS, WID_SCMF_EYEBROWS_L, WID_SCMF_EYEBROWS_R);
|
||||
|
||||
/* Lips or (if it a male face with a moustache) moustache buttons */
|
||||
this->SetWidgetsDisabledState(_cmf_info[this->is_moust_male ? CMFV_MOUSTACHE : CMFV_LIPS].valid_values[this->ge] < 2,
|
||||
WID_SCMF_LIPS_MOUSTACHE, WID_SCMF_LIPS_MOUSTACHE_L, WID_SCMF_LIPS_MOUSTACHE_R);
|
||||
|
||||
/* Nose buttons | male faces with moustache haven't any nose options */
|
||||
this->SetWidgetsDisabledState(_cmf_info[CMFV_NOSE].valid_values[this->ge] < 2 || this->is_moust_male,
|
||||
WID_SCMF_NOSE, WID_SCMF_NOSE_L, WID_SCMF_NOSE_R);
|
||||
|
||||
/* Hair buttons */
|
||||
this->SetWidgetsDisabledState(_cmf_info[CMFV_HAIR].valid_values[this->ge] < 2,
|
||||
WID_SCMF_HAIR, WID_SCMF_HAIR_L, WID_SCMF_HAIR_R);
|
||||
|
||||
/* Jacket buttons */
|
||||
this->SetWidgetsDisabledState(_cmf_info[CMFV_JACKET].valid_values[this->ge] < 2,
|
||||
WID_SCMF_JACKET, WID_SCMF_JACKET_L, WID_SCMF_JACKET_R);
|
||||
|
||||
/* Collar buttons */
|
||||
this->SetWidgetsDisabledState(_cmf_info[CMFV_COLLAR].valid_values[this->ge] < 2,
|
||||
WID_SCMF_COLLAR, WID_SCMF_COLLAR_L, WID_SCMF_COLLAR_R);
|
||||
|
||||
/* Tie/earring buttons | female faces without earring haven't any earring options */
|
||||
this->SetWidgetsDisabledState(_cmf_info[CMFV_TIE_EARRING].valid_values[this->ge] < 2 ||
|
||||
(this->is_female && GetCompanyManagerFaceBits(this->face, CMFV_HAS_TIE_EARRING, this->ge) == 0),
|
||||
WID_SCMF_TIE_EARRING, WID_SCMF_TIE_EARRING_L, WID_SCMF_TIE_EARRING_R);
|
||||
|
||||
/* Glasses buttons | faces without glasses haven't any glasses options */
|
||||
this->SetWidgetsDisabledState(_cmf_info[CMFV_GLASSES].valid_values[this->ge] < 2 || GetCompanyManagerFaceBits(this->face, CMFV_HAS_GLASSES, this->ge) == 0,
|
||||
WID_SCMF_GLASSES, WID_SCMF_GLASSES_L, WID_SCMF_GLASSES_R);
|
||||
|
||||
this->DrawWidgets();
|
||||
}
|
||||
|
||||
std::string GetWidgetString(WidgetID widget, StringID stringid) const override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_SCMF_HAS_MOUSTACHE_EARRING: {
|
||||
CompanyManagerFaceVariable facepart = this->is_female ? CMFV_HAS_TIE_EARRING : CMFV_HAS_MOUSTACHE;
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, facepart, this->ge), true);
|
||||
}
|
||||
|
||||
case WID_SCMF_TIE_EARRING:
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, CMFV_TIE_EARRING, this->ge), false);
|
||||
|
||||
case WID_SCMF_LIPS_MOUSTACHE: {
|
||||
CompanyManagerFaceVariable facepart = this->is_moust_male ? CMFV_MOUSTACHE : CMFV_LIPS;
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, facepart, this->ge), false);
|
||||
}
|
||||
|
||||
case WID_SCMF_HAS_GLASSES:
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, CMFV_HAS_GLASSES, this->ge), true );
|
||||
|
||||
case WID_SCMF_HAIR:
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, CMFV_HAIR, this->ge), false);
|
||||
|
||||
case WID_SCMF_EYEBROWS:
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, CMFV_EYEBROWS, this->ge), false);
|
||||
|
||||
case WID_SCMF_EYECOLOUR:
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, CMFV_EYE_COLOUR, this->ge), false);
|
||||
|
||||
case WID_SCMF_GLASSES:
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, CMFV_GLASSES, this->ge), false);
|
||||
|
||||
case WID_SCMF_NOSE:
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, CMFV_NOSE, this->ge), false);
|
||||
|
||||
case WID_SCMF_CHIN:
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, CMFV_CHIN, this->ge), false);
|
||||
|
||||
case WID_SCMF_JACKET:
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, CMFV_JACKET, this->ge), false);
|
||||
|
||||
case WID_SCMF_COLLAR:
|
||||
return this->GetFaceString(widget, GetCompanyManagerFaceBits(this->face, CMFV_COLLAR, this->ge), false);
|
||||
|
||||
default:
|
||||
return this->Window::GetWidgetString(widget, stringid);
|
||||
}
|
||||
}
|
||||
|
||||
void DrawWidget(const Rect &r, WidgetID widget) const override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_SCMF_FACE:
|
||||
DrawCompanyManagerFace(this->face, Company::Get(this->window_number)->colour, r);
|
||||
break;
|
||||
|
||||
case WID_SCMF_STYLE: {
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.frametext, RectPadding::zero).WithHeight(this->line_height);
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
|
||||
Rect br = ir.CentreTo(ir.Width(), SETTING_BUTTON_HEIGHT).WithWidth(SETTING_BUTTON_WIDTH, rtl);
|
||||
Rect tr = ir.Shrink(RectPadding::zero, WidgetDimensions::scaled.matrix).CentreTo(ir.Width(), GetCharacterHeight(FS_NORMAL)).Indent(SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_wide, rtl);
|
||||
|
||||
DrawArrowButtons(br.left, br.top, COLOUR_YELLOW, this->selected_var == UINT_MAX - 1 ? this->click_state : 0, true, true);
|
||||
DrawString(tr, GetString(STR_FACE_SETTING_NUMERIC, STR_FACE_STYLE, this->face.style + 1, GetNumCompanyManagerFaceStyles()), TC_WHITE);
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_SCMF_PARTS: {
|
||||
Rect ir = r.Shrink(WidgetDimensions::scaled.frametext, RectPadding::zero).WithHeight(this->line_height);
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
|
||||
FaceVars vars = GetCompanyManagerFaceVars(this->face.style);
|
||||
|
||||
auto [first, last] = this->GetScrollbar(WID_SCMF_PARTS_SCROLLBAR)->GetVisibleRangeIterators(this->face_vars);
|
||||
for (auto it = first; it != last; ++it) {
|
||||
const uint8_t var = static_cast<uint8_t>(*it - vars.data());
|
||||
const FaceVar &facevar = **it;
|
||||
|
||||
Rect br = ir.CentreTo(ir.Width(), SETTING_BUTTON_HEIGHT).WithWidth(SETTING_BUTTON_WIDTH, rtl);
|
||||
Rect tr = ir.Shrink(RectPadding::zero, WidgetDimensions::scaled.matrix).CentreTo(ir.Width(), GetCharacterHeight(FS_NORMAL)).Indent(SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_wide, rtl);
|
||||
|
||||
uint val = vars[var].GetBits(this->face);
|
||||
if (facevar.type == FaceVarType::Toggle) {
|
||||
DrawBoolButton(br.left, br.top, COLOUR_YELLOW, COLOUR_GREY, val == 1, true);
|
||||
DrawString(tr, GetString(STR_FACE_SETTING_TOGGLE, facevar.name, val == 1 ? STR_FACE_YES : STR_FACE_NO), TC_WHITE);
|
||||
} else {
|
||||
DrawArrowButtons(br.left, br.top, COLOUR_YELLOW, this->selected_var == var ? this->click_state : 0, true, true);
|
||||
DrawString(tr, GetString(STR_FACE_SETTING_NUMERIC, facevar.name, val + 1, facevar.valid_values), TC_WHITE);
|
||||
}
|
||||
|
||||
ir = ir.Translate(0, this->line_height);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1586,7 +1384,7 @@ public:
|
||||
|
||||
/* OK button */
|
||||
case WID_SCMF_ACCEPT:
|
||||
Command<CMD_SET_COMPANY_MANAGER_FACE>::Post(this->face);
|
||||
Command<CMD_SET_COMPANY_MANAGER_FACE>::Post(this->face.bits, this->face.style);
|
||||
[[fallthrough]];
|
||||
|
||||
/* Cancel button */
|
||||
@@ -1595,101 +1393,119 @@ public:
|
||||
break;
|
||||
|
||||
/* Load button */
|
||||
case WID_SCMF_LOAD:
|
||||
this->face = _company_manager_face;
|
||||
ScaleAllCompanyManagerFaceBits(this->face);
|
||||
case WID_SCMF_LOAD: {
|
||||
auto cmf = ParseCompanyManagerFaceCode(_company_manager_face);
|
||||
if (cmf.has_value()) this->face = *cmf;
|
||||
ShowErrorMessage(GetEncodedString(STR_FACE_LOAD_DONE), {}, WL_INFO);
|
||||
this->UpdateData();
|
||||
this->SetDirty();
|
||||
break;
|
||||
}
|
||||
|
||||
/* 'Company manager face number' button, view and/or set company manager face number */
|
||||
case WID_SCMF_FACECODE:
|
||||
ShowQueryString(GetString(STR_JUST_INT, this->face), STR_FACE_FACECODE_CAPTION, 10 + 1, this, CS_NUMERAL, {});
|
||||
ShowQueryString(FormatCompanyManagerFaceCode(this->face), STR_FACE_FACECODE_CAPTION, 128, this, CS_ALPHANUMERAL, {});
|
||||
break;
|
||||
|
||||
/* Save button */
|
||||
case WID_SCMF_SAVE:
|
||||
_company_manager_face = this->face;
|
||||
_company_manager_face = FormatCompanyManagerFaceCode(this->face);
|
||||
ShowErrorMessage(GetEncodedString(STR_FACE_SAVE_DONE), {}, WL_INFO);
|
||||
break;
|
||||
|
||||
/* Toggle gender (male/female) button */
|
||||
case WID_SCMF_MALE:
|
||||
case WID_SCMF_FEMALE:
|
||||
case WID_SCMF_MALE2:
|
||||
case WID_SCMF_FEMALE2:
|
||||
SetCompanyManagerFaceBits(this->face, CMFV_GENDER, this->ge, (widget == WID_SCMF_FEMALE || widget == WID_SCMF_FEMALE2));
|
||||
ScaleAllCompanyManagerFaceBits(this->face);
|
||||
this->UpdateData();
|
||||
this->SetDirty();
|
||||
break;
|
||||
|
||||
/* Randomize face button */
|
||||
case WID_SCMF_RANDOM_NEW_FACE:
|
||||
RandomCompanyManagerFaceBits(this->face, this->ge, this->advanced, _interactive_random);
|
||||
RandomiseCompanyManagerFace(this->face, _interactive_random);
|
||||
this->UpdateData();
|
||||
this->SetDirty();
|
||||
break;
|
||||
|
||||
/* Toggle ethnicity (european/african) button */
|
||||
case WID_SCMF_ETHNICITY_EUR:
|
||||
case WID_SCMF_ETHNICITY_AFR:
|
||||
SetCompanyManagerFaceBits(this->face, CMFV_ETHNICITY, this->ge, widget - WID_SCMF_ETHNICITY_EUR);
|
||||
ScaleAllCompanyManagerFaceBits(this->face);
|
||||
this->UpdateData();
|
||||
this->SetDirty();
|
||||
break;
|
||||
case WID_SCMF_STYLE: {
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
Rect ir = this->GetWidget<NWidgetCore>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.frametext, RectPadding::zero);
|
||||
Rect br = ir.WithWidth(SETTING_BUTTON_WIDTH, rtl);
|
||||
|
||||
default:
|
||||
/* Here all buttons from WID_SCMF_HAS_MOUSTACHE_EARRING to WID_SCMF_GLASSES_R are handled.
|
||||
* First it checks which CompanyManagerFaceVariable is being changed, and then either
|
||||
* a: invert the value for boolean variables, or
|
||||
* b: it checks inside of IncreaseCompanyManagerFaceBits() if a left (_L) butten is pressed and then decrease else increase the variable */
|
||||
if (widget >= WID_SCMF_HAS_MOUSTACHE_EARRING && widget <= WID_SCMF_GLASSES_R) {
|
||||
CompanyManagerFaceVariable cmfv; // which CompanyManagerFaceVariable shall be edited
|
||||
|
||||
if (widget < WID_SCMF_EYECOLOUR_L) { // Bool buttons
|
||||
switch (widget - WID_SCMF_HAS_MOUSTACHE_EARRING) {
|
||||
default: NOT_REACHED();
|
||||
case 0: cmfv = this->is_female ? CMFV_HAS_TIE_EARRING : CMFV_HAS_MOUSTACHE; break; // Has earring/moustache button
|
||||
case 1: cmfv = CMFV_HAS_GLASSES; break; // Has glasses button
|
||||
}
|
||||
SetCompanyManagerFaceBits(this->face, cmfv, this->ge, !GetCompanyManagerFaceBits(this->face, cmfv, this->ge));
|
||||
ScaleAllCompanyManagerFaceBits(this->face);
|
||||
} else { // Value buttons
|
||||
switch ((widget - WID_SCMF_EYECOLOUR_L) / 3) {
|
||||
default: NOT_REACHED();
|
||||
case 0: cmfv = CMFV_EYE_COLOUR; break; // Eye colour buttons
|
||||
case 1: cmfv = CMFV_CHIN; break; // Chin buttons
|
||||
case 2: cmfv = CMFV_EYEBROWS; break; // Eyebrows buttons
|
||||
case 3: cmfv = this->is_moust_male ? CMFV_MOUSTACHE : CMFV_LIPS; break; // Moustache or lips buttons
|
||||
case 4: cmfv = CMFV_NOSE; break; // Nose buttons
|
||||
case 5: cmfv = CMFV_HAIR; break; // Hair buttons
|
||||
case 6: cmfv = CMFV_JACKET; break; // Jacket buttons
|
||||
case 7: cmfv = CMFV_COLLAR; break; // Collar buttons
|
||||
case 8: cmfv = CMFV_TIE_EARRING; break; // Tie/earring buttons
|
||||
case 9: cmfv = CMFV_GLASSES; break; // Glasses buttons
|
||||
}
|
||||
/* 0 == left (_L), 1 == middle or 2 == right (_R) - button click */
|
||||
IncreaseCompanyManagerFaceBits(this->face, cmfv, this->ge, (((widget - WID_SCMF_EYECOLOUR_L) % 3) != 0) ? 1 : -1);
|
||||
}
|
||||
this->UpdateData();
|
||||
this->SetDirty();
|
||||
uint num_styles = GetNumCompanyManagerFaceStyles();
|
||||
this->selected_var = UINT_MAX - 1;
|
||||
if (IsInsideBS(pt.x, br.left, SETTING_BUTTON_WIDTH / 2)) {
|
||||
SetCompanyManagerFaceStyle(this->face, (this->face.style + num_styles - 1) % num_styles);
|
||||
this->click_state = 1;
|
||||
} else if (IsInsideBS(pt.x, br.left + SETTING_BUTTON_WIDTH / 2, SETTING_BUTTON_WIDTH / 2)) {
|
||||
SetCompanyManagerFaceStyle(this->face, (this->face.style + 1) % num_styles);
|
||||
this->click_state = 2;
|
||||
}
|
||||
|
||||
this->UpdateData();
|
||||
this->SetTimeout();
|
||||
this->SetDirty();
|
||||
break;
|
||||
}
|
||||
|
||||
case WID_SCMF_PARTS: {
|
||||
bool rtl = _current_text_dir == TD_RTL;
|
||||
Rect ir = this->GetWidget<NWidgetCore>(widget)->GetCurrentRect().Shrink(WidgetDimensions::scaled.frametext, RectPadding::zero);
|
||||
Rect br = ir.WithWidth(SETTING_BUTTON_WIDTH, rtl);
|
||||
|
||||
this->selected_var = UINT_MAX;
|
||||
|
||||
FaceVars vars = GetCompanyManagerFaceVars(this->face.style);
|
||||
auto it = this->GetScrollbar(WID_SCMF_PARTS_SCROLLBAR)->GetScrolledItemFromWidget(this->face_vars, pt.y, this, widget, 0, this->line_height);
|
||||
if (it == std::end(this->face_vars)) break;
|
||||
|
||||
this->selected_var = static_cast<uint8_t>(*it - vars.data());
|
||||
const auto &facevar = **it;
|
||||
|
||||
if (facevar.type == FaceVarType::Toggle) {
|
||||
if (!IsInsideBS(pt.x, br.left, SETTING_BUTTON_WIDTH)) break;
|
||||
facevar.ChangeBits(this->face, 1);
|
||||
this->UpdateData();
|
||||
} else {
|
||||
if (IsInsideBS(pt.x, br.left, SETTING_BUTTON_WIDTH / 2)) {
|
||||
facevar.ChangeBits(this->face, -1);
|
||||
this->click_state = 1;
|
||||
} else if (IsInsideBS(pt.x, br.left + SETTING_BUTTON_WIDTH / 2, SETTING_BUTTON_WIDTH / 2)) {
|
||||
facevar.ChangeBits(this->face, 1);
|
||||
this->click_state = 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->SetTimeout();
|
||||
this->SetDirty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnResize() override
|
||||
{
|
||||
if (auto *wid = this->GetWidget<NWidgetResizeBase>(WID_SCMF_PARTS); wid != nullptr) {
|
||||
/* Workaround for automatic widget sizing ignoring resize steps. Manually ensure parts matrix is a
|
||||
* multiple of its resize step. This trick only works here as the window itself is not resizable. */
|
||||
if (wid->UpdateVerticalSize((wid->current_y + wid->resize_y - 1) / wid->resize_y * wid->resize_y)) {
|
||||
this->ReInit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->GetScrollbar(WID_SCMF_PARTS_SCROLLBAR)->SetCapacityFromWidget(this, WID_SCMF_PARTS);
|
||||
}
|
||||
|
||||
void OnTimeout() override
|
||||
{
|
||||
this->click_state = 0;
|
||||
this->selected_var = UINT_MAX;
|
||||
this->SetDirty();
|
||||
}
|
||||
|
||||
void OnQueryTextFinished(std::optional<std::string> str) override
|
||||
{
|
||||
if (!str.has_value()) return;
|
||||
/* Set a new company manager face number */
|
||||
if (!str->empty()) {
|
||||
auto val = ParseInteger(*str, 10, true);
|
||||
if (!val.has_value()) return;
|
||||
this->face = *val;
|
||||
ScaleAllCompanyManagerFaceBits(this->face);
|
||||
/* Parse new company manager face number */
|
||||
auto cmf = ParseCompanyManagerFaceCode(*str);
|
||||
if (cmf.has_value()) {
|
||||
this->face = *cmf;
|
||||
ShowErrorMessage(GetEncodedString(STR_FACE_FACECODE_SET), {}, WL_INFO);
|
||||
this->UpdateData();
|
||||
this->SetDirty();
|
||||
|
@@ -12,158 +12,142 @@
|
||||
|
||||
#include "core/random_func.hpp"
|
||||
#include "core/bitmath_func.hpp"
|
||||
#include "table/sprites.h"
|
||||
#include "strings_type.h"
|
||||
#include "company_type.h"
|
||||
#include "gfx_type.h"
|
||||
|
||||
/** The gender/race combinations that we have faces for */
|
||||
enum GenderEthnicity : uint8_t {
|
||||
GENDER_FEMALE = 0, ///< This bit set means a female, otherwise male
|
||||
ETHNICITY_BLACK = 1, ///< This bit set means black, otherwise white
|
||||
#include "table/strings.h"
|
||||
|
||||
GE_WM = 0, ///< A male of Caucasian origin (white)
|
||||
GE_WF = 1 << GENDER_FEMALE, ///< A female of Caucasian origin (white)
|
||||
GE_BM = 1 << ETHNICITY_BLACK, ///< A male of African origin (black)
|
||||
GE_BF = 1 << ETHNICITY_BLACK | 1 << GENDER_FEMALE, ///< A female of African origin (black)
|
||||
GE_END,
|
||||
enum class FaceVarType : uint8_t {
|
||||
Sprite,
|
||||
Palette,
|
||||
Toggle,
|
||||
};
|
||||
DECLARE_ENUM_AS_BIT_SET(GenderEthnicity) ///< See GenderRace as a bitset
|
||||
|
||||
/** Bitgroups of the CompanyManagerFace variable */
|
||||
enum CompanyManagerFaceVariable : uint8_t {
|
||||
CMFV_GENDER,
|
||||
CMFV_ETHNICITY,
|
||||
CMFV_GEN_ETHN,
|
||||
CMFV_HAS_MOUSTACHE,
|
||||
CMFV_HAS_TIE_EARRING,
|
||||
CMFV_HAS_GLASSES,
|
||||
CMFV_EYE_COLOUR,
|
||||
CMFV_CHEEKS,
|
||||
CMFV_CHIN,
|
||||
CMFV_EYEBROWS,
|
||||
CMFV_MOUSTACHE,
|
||||
CMFV_LIPS,
|
||||
CMFV_NOSE,
|
||||
CMFV_HAIR,
|
||||
CMFV_COLLAR,
|
||||
CMFV_JACKET,
|
||||
CMFV_TIE_EARRING,
|
||||
CMFV_GLASSES,
|
||||
CMFV_END,
|
||||
};
|
||||
DECLARE_INCREMENT_DECREMENT_OPERATORS(CompanyManagerFaceVariable)
|
||||
|
||||
/** Information about the valid values of CompanyManagerFace bitgroups as well as the sprites to draw */
|
||||
struct CompanyManagerFaceBitsInfo {
|
||||
uint8_t offset; ///< Offset in bits into the CompanyManagerFace
|
||||
uint8_t length; ///< Number of bits used in the CompanyManagerFace
|
||||
uint8_t valid_values[GE_END]; ///< The number of valid values per gender/ethnicity
|
||||
SpriteID first_sprite[GE_END]; ///< The first sprite per gender/ethnicity
|
||||
};
|
||||
struct FaceVar {
|
||||
FaceVarType type;
|
||||
uint8_t position; ///< Position in UI.
|
||||
uint8_t offset; ///< Offset in bits into the CompanyManagerFace
|
||||
uint8_t length; ///< Number of bits used in the CompanyManagerFace
|
||||
uint8_t valid_values; ///< The number of valid values
|
||||
std::variant<SpriteID, uint64_t, std::pair<uint64_t, uint64_t>> data; ///< The first sprite
|
||||
StringID name = STR_NULL;
|
||||
|
||||
/** Lookup table for indices into the CompanyManagerFace, valid ranges and sprites */
|
||||
static const CompanyManagerFaceBitsInfo _cmf_info[] = {
|
||||
/* Index off len WM WF BM BF WM WF BM BF
|
||||
* CMFV_GENDER */ { 0, 1, { 2, 2, 2, 2 }, { 0, 0, 0, 0 } }, ///< 0 = male, 1 = female
|
||||
/* CMFV_ETHNICITY */ { 1, 2, { 2, 2, 2, 2 }, { 0, 0, 0, 0 } }, ///< 0 = (Western-)Caucasian, 1 = African(-American)/Black
|
||||
/* CMFV_GEN_ETHN */ { 0, 3, { 4, 4, 4, 4 }, { 0, 0, 0, 0 } }, ///< Shortcut to get/set gender _and_ ethnicity
|
||||
/* CMFV_HAS_MOUSTACHE */ { 3, 1, { 2, 0, 2, 0 }, { 0, 0, 0, 0 } }, ///< Females do not have a moustache
|
||||
/* CMFV_HAS_TIE_EARRING */ { 3, 1, { 0, 2, 0, 2 }, { 0, 0, 0, 0 } }, ///< Draw the earring for females or not. For males the tie is always drawn.
|
||||
/* CMFV_HAS_GLASSES */ { 4, 1, { 2, 2, 2, 2 }, { 0, 0, 0, 0 } }, ///< Whether to draw glasses or not
|
||||
/* CMFV_EYE_COLOUR */ { 5, 2, { 3, 3, 1, 1 }, { 0, 0, 0, 0 } }, ///< Palette modification
|
||||
/* CMFV_CHEEKS */ { 0, 0, { 1, 1, 1, 1 }, { 0x325, 0x326, 0x390, 0x3B0 } }, ///< Cheeks are only indexed by their gender/ethnicity
|
||||
/* CMFV_CHIN */ { 7, 2, { 4, 1, 2, 2 }, { 0x327, 0x327, 0x391, 0x3B1 } },
|
||||
/* CMFV_EYEBROWS */ { 9, 4, { 12, 16, 11, 16 }, { 0x32B, 0x337, 0x39A, 0x3B8 } },
|
||||
/* CMFV_MOUSTACHE */ { 13, 2, { 3, 0, 3, 0 }, { 0x367, 0, 0x397, 0 } }, ///< Depends on CMFV_HAS_MOUSTACHE
|
||||
/* CMFV_LIPS */ { 13, 4, { 12, 10, 9, 9 }, { 0x35B, 0x351, 0x3A5, 0x3C8 } }, ///< Depends on !CMFV_HAS_MOUSTACHE
|
||||
/* CMFV_NOSE */ { 17, 3, { 8, 4, 4, 5 }, { 0x349, 0x34C, 0x393, 0x3B3 } }, ///< Depends on !CMFV_HAS_MOUSTACHE
|
||||
/* CMFV_HAIR */ { 20, 4, { 9, 5, 5, 5 }, { 0x382, 0x38B, 0x3D4, 0x3D9 } },
|
||||
/* CMFV_COLLAR */ { 26, 2, { 4, 4, 4, 4 }, { 0x36E, 0x37B, 0x36E, 0x37B } },
|
||||
/* CMFV_JACKET */ { 24, 2, { 3, 3, 3, 3 }, { 0x36B, 0x378, 0x36B, 0x378 } },
|
||||
/* CMFV_TIE_EARRING */ { 28, 3, { 6, 3, 6, 3 }, { 0x372, 0x37F, 0x372, 0x3D1 } }, ///< Depends on CMFV_HAS_TIE_EARRING
|
||||
/* CMFV_GLASSES */ { 31, 1, { 2, 2, 2, 2 }, { 0x347, 0x347, 0x3AE, 0x3AE } } ///< Depends on CMFV_HAS_GLASSES
|
||||
};
|
||||
/** Make sure the table's size is right. */
|
||||
static_assert(lengthof(_cmf_info) == CMFV_END);
|
||||
|
||||
/**
|
||||
* Gets the company manager's face bits for the given company manager's face variable
|
||||
* @param cmf the face to extract the bits from
|
||||
* @param cmfv the face variable to get the data of
|
||||
* @param ge the gender and ethnicity of the face
|
||||
* @pre _cmf_info[cmfv].valid_values[ge] != 0
|
||||
* @return the requested bits
|
||||
*/
|
||||
inline uint GetCompanyManagerFaceBits(CompanyManagerFace cmf, CompanyManagerFaceVariable cmfv, [[maybe_unused]] GenderEthnicity ge)
|
||||
{
|
||||
assert(_cmf_info[cmfv].valid_values[ge] != 0);
|
||||
|
||||
return GB(cmf, _cmf_info[cmfv].offset, _cmf_info[cmfv].length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the company manager's face bits for the given company manager's face variable
|
||||
* @param cmf the face to write the bits to
|
||||
* @param cmfv the face variable to write the data of
|
||||
* @param ge the gender and ethnicity of the face
|
||||
* @param val the new value
|
||||
* @pre val < _cmf_info[cmfv].valid_values[ge]
|
||||
*/
|
||||
inline void SetCompanyManagerFaceBits(CompanyManagerFace &cmf, CompanyManagerFaceVariable cmfv, [[maybe_unused]] GenderEthnicity ge, uint val)
|
||||
{
|
||||
assert(val < _cmf_info[cmfv].valid_values[ge]);
|
||||
|
||||
SB(cmf, _cmf_info[cmfv].offset, _cmf_info[cmfv].length, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase/Decrease the company manager's face variable by the given amount.
|
||||
* The value wraps around to stay in the valid range.
|
||||
*
|
||||
* @param cmf the company manager face to write the bits to
|
||||
* @param cmfv the company manager face variable to write the data of
|
||||
* @param ge the gender and ethnicity of the company manager's face
|
||||
* @param amount the amount which change the value
|
||||
*
|
||||
* @pre 0 <= val < _cmf_info[cmfv].valid_values[ge]
|
||||
*/
|
||||
inline void IncreaseCompanyManagerFaceBits(CompanyManagerFace &cmf, CompanyManagerFaceVariable cmfv, GenderEthnicity ge, int8_t amount)
|
||||
{
|
||||
int8_t val = GetCompanyManagerFaceBits(cmf, cmfv, ge) + amount; // the new value for the cmfv
|
||||
|
||||
/* scales the new value to the correct scope */
|
||||
while (val < 0) {
|
||||
val += _cmf_info[cmfv].valid_values[ge];
|
||||
/**
|
||||
* Gets the company manager's face bits.
|
||||
* @param cmf The face to extract the bits from.
|
||||
* @return the requested bits
|
||||
*/
|
||||
inline uint GetBits(const CompanyManagerFace &cmf) const
|
||||
{
|
||||
return GB(cmf.bits, this->offset, this->length);
|
||||
}
|
||||
val %= _cmf_info[cmfv].valid_values[ge];
|
||||
|
||||
SetCompanyManagerFaceBits(cmf, cmfv, ge, val); // save the new value
|
||||
}
|
||||
/**
|
||||
* Sets the company manager's face bits.
|
||||
* @param cmf The face to write the bits to.
|
||||
* @param val The new value.
|
||||
*/
|
||||
inline void SetBits(CompanyManagerFace &cmf, uint val) const
|
||||
{
|
||||
SB(cmf.bits, this->offset, this->length, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase/Decrease the company manager's face variable by the given amount.
|
||||
* The value wraps around to stay in the valid range.
|
||||
* @param cmf The face to write the bits to.
|
||||
* @param amount the amount to change the value
|
||||
*/
|
||||
inline void ChangeBits(CompanyManagerFace &cmf, int8_t amount) const
|
||||
{
|
||||
int8_t val = this->GetBits(cmf) + amount; // the new value for the cmfv
|
||||
|
||||
/* scales the new value to the correct scope */
|
||||
while (val < 0) {
|
||||
val += this->valid_values;
|
||||
}
|
||||
val %= this->valid_values;
|
||||
|
||||
this->SetBits(cmf, val); // save the new value
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the company manager's face bits have a valid range
|
||||
* @param cmf The face to check.
|
||||
* @return true if and only if the bits are valid
|
||||
*/
|
||||
inline bool IsValid(const CompanyManagerFace &cmf) const
|
||||
{
|
||||
return GB(cmf.bits, this->offset, this->length) < this->valid_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales a company manager's face bits variable to the correct scope
|
||||
* @param vars The face variables of the face style.
|
||||
* @pre val < (1U << length), i.e. val has a value of 0..2^(bits used for this variable)-1
|
||||
* @return the scaled value
|
||||
*/
|
||||
inline uint ScaleBits(uint val) const
|
||||
{
|
||||
assert(val < (1U << this->length));
|
||||
return (val * this->valid_values) >> this->length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sprite to draw.
|
||||
* @param cmf The face to extract the data from
|
||||
* @pre vars[var].type == FaceVarType::Sprite.
|
||||
* @return sprite to draw
|
||||
*/
|
||||
inline SpriteID GetSprite(const CompanyManagerFace &cmf) const
|
||||
{
|
||||
assert(this->type == FaceVarType::Sprite);
|
||||
return std::get<SpriteID>(this->data) + this->GetBits(cmf);
|
||||
}
|
||||
};
|
||||
|
||||
using FaceVars = std::span<const FaceVar>;
|
||||
|
||||
struct FaceSpec {
|
||||
std::string label;
|
||||
std::variant<FaceVars, std::vector<FaceVar>> face_vars;
|
||||
|
||||
inline FaceVars GetFaceVars() const
|
||||
{
|
||||
struct visitor {
|
||||
FaceVars operator()(FaceVars vars) const { return vars; }
|
||||
FaceVars operator()(const std::vector<FaceVar> &vars) const { return vars; }
|
||||
};
|
||||
return std::visit(visitor{}, this->face_vars);
|
||||
}
|
||||
};
|
||||
|
||||
void ResetFaces();
|
||||
uint GetNumCompanyManagerFaceStyles();
|
||||
std::optional<uint> FindCompanyManagerFaceLabel(std::string_view label);
|
||||
const FaceSpec *GetCompanyManagerFaceSpec(uint style_index);
|
||||
FaceVars GetCompanyManagerFaceVars(uint style_index);
|
||||
|
||||
/**
|
||||
* Checks whether the company manager's face bits have a valid range
|
||||
* @param cmf the face to extract the bits from
|
||||
* @param cmfv the face variable to get the data of
|
||||
* @param ge the gender and ethnicity of the face
|
||||
* @return true if and only if the bits are valid
|
||||
* Get a bitmask of currently active face variables.
|
||||
* Face variables can be inactive due to toggles in the face variables.
|
||||
* @param cmf The company manager face.
|
||||
* @param vars The face variables of the face.
|
||||
* @return Currently active face variables for the face.
|
||||
*/
|
||||
inline bool AreCompanyManagerFaceBitsValid(CompanyManagerFace cmf, CompanyManagerFaceVariable cmfv, GenderEthnicity ge)
|
||||
inline uint64_t GetActiveFaceVars(const CompanyManagerFace &cmf, FaceVars vars)
|
||||
{
|
||||
return GB(cmf, _cmf_info[cmfv].offset, _cmf_info[cmfv].length) < _cmf_info[cmfv].valid_values[ge];
|
||||
}
|
||||
uint64_t active_vars = (1ULL << std::size(vars)) - 1ULL;
|
||||
|
||||
/**
|
||||
* Scales a company manager's face bits variable to the correct scope
|
||||
* @param cmfv the face variable to write the data of
|
||||
* @param ge the gender and ethnicity of the face
|
||||
* @param val the to value to scale
|
||||
* @pre val < (1U << _cmf_info[cmfv].length), i.e. val has a value of 0..2^(bits used for this variable)-1
|
||||
* @return the scaled value
|
||||
*/
|
||||
inline uint ScaleCompanyManagerFaceValue(CompanyManagerFaceVariable cmfv, GenderEthnicity ge, uint val)
|
||||
{
|
||||
assert(val < (1U << _cmf_info[cmfv].length));
|
||||
for (const auto &info : vars) {
|
||||
if (info.type != FaceVarType::Toggle) continue;
|
||||
const auto &[off, on] = std::get<std::pair<uint64_t, uint64_t>>(info.data);
|
||||
active_vars &= ~(HasBit(cmf.bits, info.offset) ? on : off);
|
||||
}
|
||||
|
||||
return (val * _cmf_info[cmfv].valid_values[ge]) >> _cmf_info[cmfv].length;
|
||||
return active_vars;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,69 +155,31 @@ inline uint ScaleCompanyManagerFaceValue(CompanyManagerFaceVariable cmfv, Gender
|
||||
*
|
||||
* @param cmf the company manager's face to write the bits to
|
||||
*/
|
||||
inline void ScaleAllCompanyManagerFaceBits(CompanyManagerFace &cmf)
|
||||
inline void ScaleAllCompanyManagerFaceBits(CompanyManagerFace &cmf, FaceVars vars)
|
||||
{
|
||||
IncreaseCompanyManagerFaceBits(cmf, CMFV_ETHNICITY, GE_WM, 0); // scales the ethnicity
|
||||
|
||||
GenderEthnicity ge = (GenderEthnicity)GB(cmf, _cmf_info[CMFV_GEN_ETHN].offset, _cmf_info[CMFV_GEN_ETHN].length); // gender & ethnicity of the face
|
||||
|
||||
/* Is a male face with moustache. Need to reduce CPU load in the loop. */
|
||||
bool is_moust_male = !HasBit(ge, GENDER_FEMALE) && GetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge) != 0;
|
||||
|
||||
for (CompanyManagerFaceVariable cmfv = CMFV_EYE_COLOUR; cmfv < CMFV_END; cmfv++) { // scales all other variables
|
||||
|
||||
/* The moustache variable will be scaled only if it is a male face with has a moustache */
|
||||
if (cmfv != CMFV_MOUSTACHE || is_moust_male) {
|
||||
IncreaseCompanyManagerFaceBits(cmf, cmfv, ge, 0);
|
||||
}
|
||||
for (auto var : SetBitIterator(GetActiveFaceVars(cmf, vars))) {
|
||||
vars[var].ChangeBits(cmf, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a random new face.
|
||||
* If it is for the advanced company manager's face window then the new face have the same gender
|
||||
* and ethnicity as the old one, else the gender is equal and the ethnicity is random.
|
||||
*
|
||||
* @param cmf the company manager's face to write the bits to
|
||||
* @param ge the gender and ethnicity of the old company manager's face
|
||||
* @param adv if it for the advanced company manager's face window
|
||||
* @param randomizer the source of random to use for creating the manager face
|
||||
*
|
||||
* @pre scale 'ge' to a valid gender/ethnicity combination
|
||||
* Make a random new face without changing the face style.
|
||||
* @param cmf The company manager's face to write the bits to
|
||||
* @param vars The face variables.
|
||||
* @param randomizer The source of random to use for creating the manager face
|
||||
*/
|
||||
inline void RandomCompanyManagerFaceBits(CompanyManagerFace &cmf, GenderEthnicity ge, bool adv, Randomizer &randomizer)
|
||||
inline void RandomiseCompanyManagerFaceBits(CompanyManagerFace &cmf, FaceVars vars, Randomizer &randomizer)
|
||||
{
|
||||
cmf = randomizer.Next(); // random all company manager's face bits
|
||||
|
||||
/* scale ge: 0 == GE_WM, 1 == GE_WF, 2 == GE_BM, 3 == GE_BF (and maybe in future: ...) */
|
||||
ge = (GenderEthnicity)((uint)ge % GE_END);
|
||||
|
||||
/* set the gender (and ethnicity) for the new company manager's face */
|
||||
if (adv) {
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, ge, ge);
|
||||
} else {
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_GENDER, ge, HasBit(ge, GENDER_FEMALE));
|
||||
}
|
||||
|
||||
/* scales all company manager's face bits to the correct scope */
|
||||
ScaleAllCompanyManagerFaceBits(cmf);
|
||||
cmf.bits = randomizer.Next();
|
||||
ScaleAllCompanyManagerFaceBits(cmf, vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sprite to draw for the given company manager's face variable
|
||||
* @param cmf the face to extract the data from
|
||||
* @param cmfv the face variable to get the sprite of
|
||||
* @param ge the gender and ethnicity of the face
|
||||
* @pre _cmf_info[cmfv].valid_values[ge] != 0
|
||||
* @return sprite to draw
|
||||
*/
|
||||
inline SpriteID GetCompanyManagerFaceSprite(CompanyManagerFace cmf, CompanyManagerFaceVariable cmfv, GenderEthnicity ge)
|
||||
{
|
||||
assert(_cmf_info[cmfv].valid_values[ge] != 0);
|
||||
void SetCompanyManagerFaceStyle(CompanyManagerFace &cmf, uint style);
|
||||
void RandomiseCompanyManagerFace(CompanyManagerFace &cmf, Randomizer &randomizer);
|
||||
uint32_t MaskCompanyManagerFaceBits(const CompanyManagerFace &cmf, FaceVars vars);
|
||||
std::string FormatCompanyManagerFaceCode(const CompanyManagerFace &cmf);
|
||||
std::optional<CompanyManagerFace> ParseCompanyManagerFaceCode(std::string_view str);
|
||||
|
||||
return _cmf_info[cmfv].first_sprite[ge] + GB(cmf, _cmf_info[cmfv].offset, _cmf_info[cmfv].length);
|
||||
}
|
||||
|
||||
void DrawCompanyManagerFace(CompanyManagerFace face, Colours colour, const Rect &r);
|
||||
void DrawCompanyManagerFace(const CompanyManagerFace &cmf, Colours colour, const Rect &r);
|
||||
|
||||
#endif /* COMPANY_MANAGER_FACE_H */
|
||||
|
@@ -49,7 +49,12 @@ public:
|
||||
};
|
||||
|
||||
struct Company;
|
||||
typedef uint32_t CompanyManagerFace; ///< Company manager face bits, info see in company_manager_face.h
|
||||
|
||||
struct CompanyManagerFace {
|
||||
uint style = 0; ///< Company manager face style.
|
||||
uint32_t bits = 0; ///< Company manager face bits, meaning is dependent on style.
|
||||
std::string style_label; ///< Face style label.
|
||||
};
|
||||
|
||||
/** The reason why the company was removed. */
|
||||
enum CompanyRemoveReason : uint8_t {
|
||||
|
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "widgets/error_widget.h"
|
||||
|
||||
#include "table/sprites.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
#include "safeguards.h"
|
||||
|
@@ -2318,12 +2318,7 @@ STR_LIVERY_FREIGHT_TRAM :Freight Tram
|
||||
STR_FACE_CAPTION :{WHITE}Face Selection
|
||||
STR_FACE_CANCEL_TOOLTIP :{BLACK}Cancel new face selection
|
||||
STR_FACE_OK_TOOLTIP :{BLACK}Accept new face selection
|
||||
STR_FACE_RANDOM :{BLACK}Randomise
|
||||
|
||||
STR_FACE_MALE_BUTTON :{BLACK}Male
|
||||
STR_FACE_MALE_TOOLTIP :{BLACK}Select male faces
|
||||
STR_FACE_FEMALE_BUTTON :{BLACK}Female
|
||||
STR_FACE_FEMALE_TOOLTIP :{BLACK}Select female faces
|
||||
STR_FACE_NEW_FACE_BUTTON :{BLACK}New Face
|
||||
STR_FACE_NEW_FACE_TOOLTIP :{BLACK}Generate random new face
|
||||
STR_FACE_ADVANCED :{BLACK}Advanced
|
||||
@@ -2333,44 +2328,31 @@ STR_FACE_SIMPLE_TOOLTIP :{BLACK}Simple f
|
||||
STR_FACE_LOAD :{BLACK}Load
|
||||
STR_FACE_LOAD_TOOLTIP :{BLACK}Load favourite face
|
||||
STR_FACE_LOAD_DONE :{WHITE}Your favourite face has been loaded from the OpenTTD configuration file
|
||||
STR_FACE_FACECODE :{BLACK}Player face no.
|
||||
STR_FACE_FACECODE_TOOLTIP :{BLACK}View and/or set face number of the company president
|
||||
STR_FACE_FACECODE_CAPTION :{WHITE}View and/or set president face number
|
||||
STR_FACE_FACECODE_SET :{WHITE}New face number code has been set
|
||||
STR_FACE_FACECODE_ERR :{WHITE}Couldn't set president face number - must be a number between 0 and 4,294,967,295!
|
||||
STR_FACE_FACECODE :{BLACK}Player face code
|
||||
STR_FACE_FACECODE_TOOLTIP :{BLACK}View and/or set face code of the company president
|
||||
STR_FACE_FACECODE_CAPTION :{WHITE}View and/or set president face code
|
||||
STR_FACE_FACECODE_SET :{WHITE}New president face has been set
|
||||
STR_FACE_FACECODE_ERR :{WHITE}Couldn't set president face code - must be a valid label and number
|
||||
STR_FACE_SAVE :{BLACK}Save
|
||||
STR_FACE_SAVE_TOOLTIP :{BLACK}Save favourite face
|
||||
STR_FACE_SAVE_DONE :{WHITE}This face will be saved as your favourite in the OpenTTD configuration file
|
||||
STR_FACE_EUROPEAN :{BLACK}European
|
||||
STR_FACE_EUROPEAN_TOOLTIP :{BLACK}Select European faces
|
||||
STR_FACE_AFRICAN :{BLACK}African
|
||||
STR_FACE_AFRICAN_TOOLTIP :{BLACK}Select African faces
|
||||
STR_FACE_SETTING_TOGGLE :{STRING} {ORANGE}{STRING}
|
||||
STR_FACE_SETTING_NUMERIC :{STRING} {ORANGE}{NUM} / {NUM}
|
||||
STR_FACE_YES :Yes
|
||||
STR_FACE_NO :No
|
||||
STR_FACE_MOUSTACHE_EARRING_TOOLTIP :{BLACK}Enable moustache or earring
|
||||
STR_FACE_STYLE :Style:
|
||||
STR_FACE_HAIR :Hair:
|
||||
STR_FACE_HAIR_TOOLTIP :{BLACK}Change hair
|
||||
STR_FACE_EYEBROWS :Eyebrows:
|
||||
STR_FACE_EYEBROWS_TOOLTIP :{BLACK}Change eyebrows
|
||||
STR_FACE_EYECOLOUR :Eye colour:
|
||||
STR_FACE_EYECOLOUR_TOOLTIP :{BLACK}Change eye colour
|
||||
STR_FACE_GLASSES :Glasses:
|
||||
STR_FACE_GLASSES_TOOLTIP :{BLACK}Enable glasses
|
||||
STR_FACE_GLASSES_TOOLTIP_2 :{BLACK}Change glasses
|
||||
STR_FACE_NOSE :Nose:
|
||||
STR_FACE_NOSE_TOOLTIP :{BLACK}Change nose
|
||||
STR_FACE_LIPS :Lips:
|
||||
STR_FACE_MOUSTACHE :Moustache:
|
||||
STR_FACE_LIPS_MOUSTACHE_TOOLTIP :{BLACK}Change lips or moustache
|
||||
STR_FACE_CHIN :Chin:
|
||||
STR_FACE_CHIN_TOOLTIP :{BLACK}Change chin
|
||||
STR_FACE_JACKET :Jacket:
|
||||
STR_FACE_JACKET_TOOLTIP :{BLACK}Change jacket
|
||||
STR_FACE_COLLAR :Collar:
|
||||
STR_FACE_COLLAR_TOOLTIP :{BLACK}Change collar
|
||||
STR_FACE_TIE :Tie:
|
||||
STR_FACE_EARRING :Earring:
|
||||
STR_FACE_TIE_EARRING_TOOLTIP :{BLACK}Change tie or earring
|
||||
|
||||
# Matches ServerGameType
|
||||
###length 3
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "core/backup_type.hpp"
|
||||
#include "core/container_func.hpp"
|
||||
#include "company_manager_face.h"
|
||||
#include "debug.h"
|
||||
#include "fileio_func.h"
|
||||
#include "engine_func.h"
|
||||
@@ -463,6 +464,8 @@ void ResetNewGRFData()
|
||||
/* Reset canal sprite groups and flags */
|
||||
_water_feature.fill({});
|
||||
|
||||
ResetFaces();
|
||||
|
||||
/* Reset the snowline table. */
|
||||
ClearSnowLine();
|
||||
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#define NEWS_TYPE_H
|
||||
|
||||
#include "core/enum_type.hpp"
|
||||
#include "company_type.h"
|
||||
#include "engine_type.h"
|
||||
#include "industry_type.h"
|
||||
#include "gfx_type.h"
|
||||
@@ -164,7 +165,7 @@ struct CompanyNewsInformation : NewsAllocatedData {
|
||||
std::string other_company_name; ///< The name of the company taking over this one
|
||||
|
||||
StringID title;
|
||||
uint32_t face; ///< The face of the president
|
||||
CompanyManagerFace face; ///< The face of the president
|
||||
Colours colour; ///< The colour related to the company
|
||||
|
||||
CompanyNewsInformation(StringID title, const struct Company *c, const struct Company *other = nullptr);
|
||||
|
@@ -1625,7 +1625,28 @@ bool AfterLoadGame()
|
||||
}
|
||||
}
|
||||
|
||||
if (IsSavegameVersionBefore(SLV_49)) for (Company *c : Company::Iterate()) c->face = ConvertFromOldCompanyManagerFace(c->face);
|
||||
if (IsSavegameVersionBefore(SLV_49)) {
|
||||
/* Perform conversion of very old face bits. */
|
||||
for (Company *c : Company::Iterate()) {
|
||||
c->face = ConvertFromOldCompanyManagerFace(c->face.bits);
|
||||
}
|
||||
} else if (IsSavegameVersionBefore(SLV_FACE_STYLES)) {
|
||||
/* Convert old gender and ethnicity bits to face style. */
|
||||
for (Company *c : Company::Iterate()) {
|
||||
SetCompanyManagerFaceStyle(c->face, GB(c->face.bits, 0, 2));
|
||||
}
|
||||
} else {
|
||||
/* Look up each company face style by its label. */
|
||||
for (Company *c : Company::Iterate()) {
|
||||
auto style = FindCompanyManagerFaceLabel(c->face.style_label);
|
||||
if (style.has_value()) {
|
||||
SetCompanyManagerFaceStyle(c->face, *style);
|
||||
} else {
|
||||
/* Style no longer exists, pick an entirely new face. */
|
||||
RandomiseCompanyManagerFace(c->face, _random);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsSavegameVersionBefore(SLV_52)) {
|
||||
for (auto t : Map::Iterate()) {
|
||||
|
@@ -24,6 +24,20 @@
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
/**
|
||||
* Search for a face variable by type and name.
|
||||
* @param style Face style to find variable in.
|
||||
* @param type Type of variable to look up.
|
||||
* @param name Name (string) of variable to look up.
|
||||
* @return Face variable if present, otherwise nullptr.
|
||||
*/
|
||||
static const FaceVar *FindFaceVar(FaceVars style, FaceVarType type, StringID name)
|
||||
{
|
||||
auto it = std::ranges::find_if(style, [type, name](const FaceVar &facevar) { return facevar.type == type && facevar.name == name; });
|
||||
if (it != std::end(style)) return &*it;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an old company manager's face format to the new company manager's face format
|
||||
*
|
||||
@@ -44,49 +58,65 @@
|
||||
*/
|
||||
CompanyManagerFace ConvertFromOldCompanyManagerFace(uint32_t face)
|
||||
{
|
||||
CompanyManagerFace cmf = 0;
|
||||
GenderEthnicity ge = GE_WM;
|
||||
CompanyManagerFace cmf{};
|
||||
|
||||
if (HasBit(face, 31)) SetBit(ge, GENDER_FEMALE);
|
||||
if (HasBit(face, 27) && (HasBit(face, 26) == HasBit(face, 19))) SetBit(ge, ETHNICITY_BLACK);
|
||||
if (HasBit(face, 31)) cmf.style += 1;
|
||||
if (HasBit(face, 27) && (HasBit(face, 26) == HasBit(face, 19))) cmf.style += 2;
|
||||
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_GEN_ETHN, ge, ge);
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_HAS_GLASSES, ge, GB(face, 28, 3) <= 1);
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_EYE_COLOUR, ge, HasBit(ge, ETHNICITY_BLACK) ? 0 : ClampU(GB(face, 20, 3), 5, 7) - 5);
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_CHIN, ge, ScaleCompanyManagerFaceValue(CMFV_CHIN, ge, GB(face, 4, 2)));
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_EYEBROWS, ge, ScaleCompanyManagerFaceValue(CMFV_EYEBROWS, ge, GB(face, 6, 4)));
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_HAIR, ge, ScaleCompanyManagerFaceValue(CMFV_HAIR, ge, GB(face, 16, 4)));
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_JACKET, ge, ScaleCompanyManagerFaceValue(CMFV_JACKET, ge, GB(face, 20, 2)));
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_COLLAR, ge, ScaleCompanyManagerFaceValue(CMFV_COLLAR, ge, GB(face, 22, 2)));
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_GLASSES, ge, GB(face, 28, 1));
|
||||
const FaceSpec *spec = GetCompanyManagerFaceSpec(cmf.style);
|
||||
FaceVars vars = spec->GetFaceVars();
|
||||
|
||||
cmf.style_label = spec->label;
|
||||
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Toggle, STR_FACE_GLASSES); var != nullptr) var->SetBits(cmf, GB(face, 28, 3) <= 1);
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Palette, STR_FACE_EYECOLOUR); var != nullptr) var->SetBits(cmf, ClampU(GB(face, 20, 3), 5, 7) - 5);
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_CHIN); var != nullptr) var->SetBits(cmf, var->ScaleBits(GB(face, 4, 2)));
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_EYEBROWS); var != nullptr) var->SetBits(cmf, var->ScaleBits(GB(face, 6, 4)));
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_HAIR); var != nullptr) var->SetBits(cmf, var->ScaleBits(GB(face, 16, 4)));
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_JACKET); var != nullptr) var->SetBits(cmf, var->ScaleBits(GB(face, 20, 2)));
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_COLLAR); var != nullptr) var->SetBits(cmf, var->ScaleBits(GB(face, 22, 2)));
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_GLASSES); var != nullptr) var->SetBits(cmf, GB(face, 28, 1));
|
||||
|
||||
uint lips = GB(face, 10, 4);
|
||||
if (!HasBit(ge, GENDER_FEMALE) && lips < 4) {
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_HAS_MOUSTACHE, ge, true);
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_MOUSTACHE, ge, std::max(lips, 1U) - 1);
|
||||
if (cmf.style != 1 && cmf.style != 3 && lips < 4) {
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Toggle, STR_FACE_MOUSTACHE); var != nullptr) var->SetBits(cmf, true);
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_MOUSTACHE); var != nullptr) var->SetBits(cmf, std::max(lips, 1U) - 1);
|
||||
} else {
|
||||
if (!HasBit(ge, GENDER_FEMALE)) {
|
||||
lips = lips * 15 / 16;
|
||||
lips -= 3;
|
||||
if (HasBit(ge, ETHNICITY_BLACK) && lips > 8) lips = 0;
|
||||
} else {
|
||||
lips = ScaleCompanyManagerFaceValue(CMFV_LIPS, ge, lips);
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_LIPS); var != nullptr) {
|
||||
if (cmf.style == 0 || cmf.style == 2) {
|
||||
lips = lips * 15 / 16;
|
||||
lips -= 3;
|
||||
if (cmf.style == 2 && lips > 8) lips = 0;
|
||||
} else {
|
||||
lips = var->ScaleBits(lips);
|
||||
}
|
||||
var->SetBits(cmf, lips);
|
||||
}
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_LIPS, ge, lips);
|
||||
|
||||
uint nose = GB(face, 13, 3);
|
||||
if (ge == GE_WF) {
|
||||
nose = (nose * 3 >> 3) * 3 >> 2; // There is 'hole' in the nose sprites for females
|
||||
} else {
|
||||
nose = ScaleCompanyManagerFaceValue(CMFV_NOSE, ge, nose);
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_NOSE); var != nullptr) {
|
||||
uint nose = GB(face, 13, 3);
|
||||
if (cmf.style == 1) {
|
||||
nose = (nose * 3 >> 3) * 3 >> 2; // There is 'hole' in the nose sprites for women
|
||||
} else {
|
||||
nose = var->ScaleBits(nose);
|
||||
}
|
||||
var->SetBits(cmf, nose);
|
||||
}
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_NOSE, ge, nose);
|
||||
}
|
||||
|
||||
uint tie_earring = GB(face, 24, 4);
|
||||
if (!HasBit(ge, GENDER_FEMALE) || tie_earring < 3) { // Not all females have an earring
|
||||
if (HasBit(ge, GENDER_FEMALE)) SetCompanyManagerFaceBits(cmf, CMFV_HAS_TIE_EARRING, ge, true);
|
||||
SetCompanyManagerFaceBits(cmf, CMFV_TIE_EARRING, ge, HasBit(ge, GENDER_FEMALE) ? tie_earring : ScaleCompanyManagerFaceValue(CMFV_TIE_EARRING, ge, tie_earring / 2));
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_TIE); var != nullptr) {
|
||||
uint tie = GB(face, 24, 4);
|
||||
var->SetBits(cmf, var->ScaleBits(tie / 2));
|
||||
}
|
||||
|
||||
if (auto var = FindFaceVar(vars, FaceVarType::Sprite, STR_FACE_EARRING); var != nullptr) {
|
||||
uint earring = GB(face, 24, 4);
|
||||
if (earring < 3) { // Not all women have an earring
|
||||
if (auto has_earring = FindFaceVar(vars, FaceVarType::Toggle, STR_FACE_EARRING)) {
|
||||
has_earring->SetBits(cmf, true);
|
||||
var->SetBits(cmf, earring);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmf;
|
||||
@@ -470,7 +500,8 @@ static const SaveLoad _company_desc[] = {
|
||||
SLE_CONDVECTOR(CompanyProperties, allow_list, SLE_STR, SLV_COMPANY_ALLOW_LIST, SLV_COMPANY_ALLOW_LIST_V2),
|
||||
SLEG_CONDSTRUCTLIST("allow_list", SlAllowListData, SLV_COMPANY_ALLOW_LIST_V2, SL_MAX_VERSION),
|
||||
|
||||
SLE_VAR(CompanyProperties, face, SLE_UINT32),
|
||||
SLE_VARNAME(CompanyProperties, face.bits, "face", SLE_UINT32),
|
||||
SLE_CONDSSTRNAME(CompanyProperties, face.style_label, "face_style", SLE_STR, SLV_FACE_STYLES, SL_MAX_VERSION),
|
||||
|
||||
/* money was changed to a 64 bit field in savegame version 1. */
|
||||
SLE_CONDVAR(CompanyProperties, money, SLE_VAR_I64 | SLE_FILE_I32, SL_MIN_VERSION, SLV_1),
|
||||
|
@@ -939,7 +939,7 @@ static bool LoadOldCompanyEconomy(LoadgameState &ls, int)
|
||||
static const OldChunks _company_chunk[] = {
|
||||
OCL_VAR ( OC_UINT16, 1, &_old_string_id ),
|
||||
OCL_SVAR( OC_UINT32, Company, name_2 ),
|
||||
OCL_SVAR( OC_UINT32, Company, face ),
|
||||
OCL_SVAR( OC_UINT32, Company, face.bits ),
|
||||
OCL_VAR ( OC_UINT16, 1, &_old_string_id_2 ),
|
||||
OCL_SVAR( OC_UINT32, Company, president_name_2 ),
|
||||
|
||||
@@ -992,9 +992,9 @@ static bool LoadOldCompany(LoadgameState &ls, int num)
|
||||
|
||||
if (_savegame_type == SGT_TTO) {
|
||||
/* adjust manager's face */
|
||||
if (HasBit(c->face, 27) && GB(c->face, 26, 1) == GB(c->face, 19, 1)) {
|
||||
if (HasBit(c->face.bits, 27) && GB(c->face.bits, 26, 1) == GB(c->face.bits, 19, 1)) {
|
||||
/* if face would be black in TTD, adjust tie colour and thereby face colour */
|
||||
ClrBit(c->face, 27);
|
||||
ClrBit(c->face.bits, 27);
|
||||
}
|
||||
|
||||
/* Company name */
|
||||
|
@@ -403,6 +403,8 @@ enum SaveLoadVersion : uint16_t {
|
||||
SLV_FIX_SCC_ENCODED_NEGATIVE, ///< 353 PR#14049 Fix encoding of negative parameters.
|
||||
SLV_ORDERS_OWNED_BY_ORDERLIST, ///< 354 PR#13948 Orders stored in OrderList, pool removed.
|
||||
|
||||
SLV_FACE_STYLES, ///< 355 PR#14319 Addition of face styles, replacing gender and ethnicity.
|
||||
|
||||
SL_MAX_VERSION, ///< Highest possible saveload version
|
||||
};
|
||||
|
||||
|
@@ -108,12 +108,18 @@
|
||||
EnforcePrecondition(false, gender == GENDER_MALE || gender == GENDER_FEMALE);
|
||||
EnforcePrecondition(false, GetPresidentGender(ScriptCompany::COMPANY_SELF) != gender);
|
||||
|
||||
Randomizer &randomizer = ScriptObject::GetRandomizer();
|
||||
CompanyManagerFace cmf;
|
||||
GenderEthnicity ge = (GenderEthnicity)((gender == GENDER_FEMALE ? (1 << ::GENDER_FEMALE) : 0) | (randomizer.Next() & (1 << ETHNICITY_BLACK)));
|
||||
RandomCompanyManagerFaceBits(cmf, ge, false, randomizer);
|
||||
assert(GetNumCompanyManagerFaceStyles() >= 2); /* At least two styles are needed to fake a gender. */
|
||||
|
||||
return ScriptObject::Command<CMD_SET_COMPANY_MANAGER_FACE>::Do(cmf);
|
||||
/* Company faces no longer have a defined gender, so pick a random face style instead. */
|
||||
Randomizer &randomizer = ScriptObject::GetRandomizer();
|
||||
CompanyManagerFace cmf{};
|
||||
do {
|
||||
cmf.style = randomizer.Next(GetNumCompanyManagerFaceStyles());
|
||||
} while ((HasBit(cmf.style, 0) ? GENDER_FEMALE : GENDER_MALE) != gender);
|
||||
|
||||
RandomiseCompanyManagerFaceBits(cmf, GetCompanyManagerFaceVars(cmf.style), randomizer);
|
||||
|
||||
return ScriptObject::Command<CMD_SET_COMPANY_MANAGER_FACE>::Do(cmf.style, cmf.bits);
|
||||
}
|
||||
|
||||
/* static */ ScriptCompany::Gender ScriptCompany::GetPresidentGender(ScriptCompany::CompanyID company)
|
||||
@@ -121,8 +127,10 @@
|
||||
company = ResolveCompanyID(company);
|
||||
if (company == ScriptCompany::COMPANY_INVALID) return GENDER_INVALID;
|
||||
|
||||
GenderEthnicity ge = (GenderEthnicity)GetCompanyManagerFaceBits(Company::Get(ScriptCompany::FromScriptCompanyID(company))->face, CMFV_GEN_ETHN, GE_WM);
|
||||
return HasBit(ge, ::GENDER_FEMALE) ? GENDER_FEMALE : GENDER_MALE;
|
||||
/* Company faces no longer have a defined gender, so fake one based on the style index. This might not match
|
||||
* the face appearance. */
|
||||
const auto &cmf = ::Company::Get(ScriptCompany::FromScriptCompanyID(company))->face;
|
||||
return HasBit(cmf.style, 0) ? GENDER_FEMALE : GENDER_MALE;
|
||||
}
|
||||
|
||||
/* static */ Money ScriptCompany::GetQuarterlyIncome(ScriptCompany::CompanyID company, SQInteger quarter)
|
||||
|
@@ -11,6 +11,7 @@ add_files(
|
||||
build_industry.h
|
||||
cargo_const.h
|
||||
clear_land.h
|
||||
company_face.h
|
||||
control_codes.h
|
||||
elrail_data.h
|
||||
engines.h
|
||||
|
98
src/table/company_face.h
Normal file
98
src/table/company_face.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 table/company_face.h
|
||||
* This file contains all definitions for default company faces.
|
||||
*/
|
||||
|
||||
#ifndef TABLE_COMPANY_FACE_H
|
||||
#define TABLE_COMPANY_FACE_H
|
||||
|
||||
#include "../company_manager_face.h"
|
||||
|
||||
/* Definitions for default face variables.
|
||||
* Faces are drawn in the listed order, so sprite layers must be ordered
|
||||
* according to how the face should be rendered. */
|
||||
|
||||
/** Variables of first masculine face. */
|
||||
static constexpr FaceVar _face_style_1[] = {
|
||||
{FaceVarType::Toggle, 3, 3, 1, 2, std::pair{1ULL << 6, 1ULL << 7 | 1ULL << 8}, STR_FACE_MOUSTACHE},
|
||||
{FaceVarType::Toggle, 11, 4, 1, 2, std::pair{1ULL << 13, 0ULL}, STR_FACE_GLASSES},
|
||||
{FaceVarType::Palette, 2, 5, 2, 3, uint64_t{1ULL << 5}, STR_FACE_EYECOLOUR},
|
||||
{FaceVarType::Sprite, 0, 0, 0, 1, SpriteID{0x325}},
|
||||
{FaceVarType::Sprite, 7, 7, 2, 4, SpriteID{0x327}, STR_FACE_CHIN},
|
||||
{FaceVarType::Sprite, 1, 9, 4, 12, SpriteID{0x32B}, STR_FACE_EYEBROWS},
|
||||
{FaceVarType::Sprite, 4, 13, 2, 3, SpriteID{0x367}, STR_FACE_MOUSTACHE},
|
||||
{FaceVarType::Sprite, 6, 13, 4, 12, SpriteID{0x35B}, STR_FACE_LIPS},
|
||||
{FaceVarType::Sprite, 5, 17, 3, 8, SpriteID{0x349}, STR_FACE_NOSE},
|
||||
{FaceVarType::Sprite, 0, 20, 4, 9, SpriteID{0x382}, STR_FACE_HAIR},
|
||||
{FaceVarType::Sprite, 9, 26, 2, 4, SpriteID{0x36E}, STR_FACE_COLLAR},
|
||||
{FaceVarType::Sprite, 8, 24, 2, 3, SpriteID{0x36B}, STR_FACE_JACKET},
|
||||
{FaceVarType::Sprite, 10, 28, 3, 6, SpriteID{0x372}, STR_FACE_TIE},
|
||||
{FaceVarType::Sprite, 12, 31, 1, 2, SpriteID{0x347}, STR_FACE_GLASSES},
|
||||
};
|
||||
|
||||
/** Variables of first feminine face. */
|
||||
static constexpr FaceVar _face_style_2[] = {
|
||||
{FaceVarType::Toggle, 9, 3, 1, 2, std::pair{1ULL << 11, 0}, STR_FACE_EARRING},
|
||||
{FaceVarType::Toggle, 7, 4, 1, 2, std::pair{1ULL << 12, 0}, STR_FACE_GLASSES},
|
||||
{FaceVarType::Palette, 2, 5, 2, 3, uint64_t{1ULL << 5}, STR_FACE_EYECOLOUR},
|
||||
{FaceVarType::Sprite, 0, 0, 0, 1, SpriteID{0x326}},
|
||||
{FaceVarType::Sprite, 0, 0, 0, 1, SpriteID{0x327}},
|
||||
{FaceVarType::Sprite, 1, 9, 4, 16, SpriteID{0x337}, STR_FACE_EYEBROWS},
|
||||
{FaceVarType::Sprite, 4, 13, 4, 10, SpriteID{0x351}, STR_FACE_LIPS},
|
||||
{FaceVarType::Sprite, 3, 17, 3, 4, SpriteID{0x34C}, STR_FACE_NOSE},
|
||||
{FaceVarType::Sprite, 0, 20, 4, 5, SpriteID{0x38B}, STR_FACE_HAIR},
|
||||
{FaceVarType::Sprite, 6, 26, 2, 4, SpriteID{0x37B}, STR_FACE_COLLAR},
|
||||
{FaceVarType::Sprite, 5, 24, 2, 3, SpriteID{0x378}, STR_FACE_JACKET},
|
||||
{FaceVarType::Sprite, 10, 28, 3, 3, SpriteID{0x37F}, STR_FACE_EARRING},
|
||||
{FaceVarType::Sprite, 8, 31, 1, 2, SpriteID{0x347}, STR_FACE_GLASSES},
|
||||
};
|
||||
|
||||
/** Variables of second masculine face. */
|
||||
static constexpr FaceVar _face_style_3[] = {
|
||||
{FaceVarType::Toggle, 2, 3, 1, 2, std::pair{1ULL << 5, 1ULL << 6 | 1ULL << 7}, STR_FACE_MOUSTACHE},
|
||||
{FaceVarType::Toggle, 10, 4, 1, 2, std::pair{1ULL << 12, 0ULL}, STR_FACE_GLASSES},
|
||||
{FaceVarType::Sprite, 0, 0, 0, 1, SpriteID{0x390}},
|
||||
{FaceVarType::Sprite, 6, 7, 2, 2, SpriteID{0x391}, STR_FACE_CHIN},
|
||||
{FaceVarType::Sprite, 1, 9, 4, 11, SpriteID{0x39A}, STR_FACE_EYEBROWS},
|
||||
{FaceVarType::Sprite, 3, 13, 2, 3, SpriteID{0x397}, STR_FACE_MOUSTACHE},
|
||||
{FaceVarType::Sprite, 5, 13, 4, 9, SpriteID{0x3A5}, STR_FACE_LIPS},
|
||||
{FaceVarType::Sprite, 4, 17, 3, 4, SpriteID{0x393}, STR_FACE_NOSE},
|
||||
{FaceVarType::Sprite, 0, 20, 4, 5, SpriteID{0x3D4}, STR_FACE_HAIR},
|
||||
{FaceVarType::Sprite, 8, 26, 2, 4, SpriteID{0x36E}, STR_FACE_COLLAR},
|
||||
{FaceVarType::Sprite, 7, 24, 2, 3, SpriteID{0x36B}, STR_FACE_JACKET},
|
||||
{FaceVarType::Sprite, 9, 28, 3, 6, SpriteID{0x372}, STR_FACE_TIE},
|
||||
{FaceVarType::Sprite, 11, 31, 1, 2, SpriteID{0x3AE}, STR_FACE_GLASSES},
|
||||
};
|
||||
|
||||
/** Variables of second feminine face. */
|
||||
static constexpr FaceVar _face_style_4[] = {
|
||||
{FaceVarType::Toggle, 9, 3, 1, 2, std::pair{1ULL << 10, 0ULL}, STR_FACE_EARRING},
|
||||
{FaceVarType::Toggle, 7, 4, 1, 2, std::pair{1ULL << 11, 0ULL}, STR_FACE_GLASSES},
|
||||
{FaceVarType::Sprite, 0, 0, 0, 1, SpriteID{0x3B0}},
|
||||
{FaceVarType::Sprite, 4, 7, 2, 2, SpriteID{0x3B1}, STR_FACE_CHIN},
|
||||
{FaceVarType::Sprite, 1, 9, 4, 16, SpriteID{0x3B8}, STR_FACE_EYEBROWS},
|
||||
{FaceVarType::Sprite, 3, 13, 4, 9, SpriteID{0x3C8}, STR_FACE_LIPS},
|
||||
{FaceVarType::Sprite, 2, 17, 3, 5, SpriteID{0x3B3}, STR_FACE_NOSE},
|
||||
{FaceVarType::Sprite, 0, 20, 4, 5, SpriteID{0x3D9}, STR_FACE_HAIR},
|
||||
{FaceVarType::Sprite, 6, 26, 2, 4, SpriteID{0x37B}, STR_FACE_COLLAR},
|
||||
{FaceVarType::Sprite, 5, 24, 2, 3, SpriteID{0x378}, STR_FACE_JACKET},
|
||||
{FaceVarType::Sprite, 10, 28, 3, 3, SpriteID{0x3D1}, STR_FACE_EARRING},
|
||||
{FaceVarType::Sprite, 8, 31, 1, 2, SpriteID{0x3AE}, STR_FACE_GLASSES},
|
||||
};
|
||||
|
||||
/** Original face styles. */
|
||||
static FaceSpec _original_faces[] = {
|
||||
{"default/face1", _face_style_1},
|
||||
{"default/face2", _face_style_2},
|
||||
{"default/face3", _face_style_3},
|
||||
{"default/face4", _face_style_4},
|
||||
};
|
||||
|
||||
#endif /* TABLE_COMPANY_FACE_H */
|
@@ -268,13 +268,11 @@ min = 1
|
||||
max = 512
|
||||
cat = SC_EXPERT
|
||||
|
||||
[SDTG_VAR]
|
||||
[SDTG_SSTR]
|
||||
name = ""player_face""
|
||||
type = SLE_UINT32
|
||||
type = SLE_STR
|
||||
var = _company_manager_face
|
||||
def = 0
|
||||
min = 0
|
||||
max = 0xFFFFFFFF
|
||||
def = """"
|
||||
cat = SC_BASIC
|
||||
|
||||
[SDTG_VAR]
|
||||
|
@@ -97,8 +97,6 @@ enum SelectCompanyLiveryWidgets : WidgetID {
|
||||
|
||||
/**
|
||||
* Widgets of the #SelectCompanyManagerFaceWindow class.
|
||||
* Do not change the order of the widgets from WID_SCMF_HAS_MOUSTACHE_EARRING to WID_SCMF_GLASSES_R,
|
||||
* this order is needed for the WE_CLICK event of DrawFaceStringLabel().
|
||||
*/
|
||||
enum SelectCompanyManagerFaceWidgets : WidgetID {
|
||||
WID_SCMF_CAPTION, ///< Caption of window.
|
||||
@@ -106,65 +104,18 @@ enum SelectCompanyManagerFaceWidgets : WidgetID {
|
||||
WID_SCMF_SELECT_FACE, ///< Select face.
|
||||
WID_SCMF_CANCEL, ///< Cancel.
|
||||
WID_SCMF_ACCEPT, ///< Accept.
|
||||
WID_SCMF_MALE, ///< Male button in the simple view.
|
||||
WID_SCMF_FEMALE, ///< Female button in the simple view.
|
||||
WID_SCMF_MALE2, ///< Male button in the advanced view.
|
||||
WID_SCMF_FEMALE2, ///< Female button in the advanced view.
|
||||
WID_SCMF_SEL_LOADSAVE, ///< Selection to display the load/save/number buttons in the advanced view.
|
||||
WID_SCMF_SEL_MALEFEMALE, ///< Selection to display the male/female buttons in the simple view.
|
||||
WID_SCMF_SEL_PARTS, ///< Selection to display the buttons for setting each part of the face in the advanced view.
|
||||
WID_SCMF_SEL_PARTS, ///< Selection to display the buttons for setting each part of the face in the advanced view.
|
||||
WID_SCMF_SEL_RESIZE, ///< Selection to display the resize button.
|
||||
WID_SCMF_RANDOM_NEW_FACE, ///< Create random new face.
|
||||
WID_SCMF_TOGGLE_LARGE_SMALL_BUTTON, ///< Toggle for large or small.
|
||||
WID_SCMF_FACE, ///< Current face.
|
||||
WID_SCMF_LOAD, ///< Load face.
|
||||
WID_SCMF_FACECODE, ///< Get the face code.
|
||||
WID_SCMF_SAVE, ///< Save face.
|
||||
WID_SCMF_HAS_MOUSTACHE_EARRING_TEXT, ///< Text about moustache and earring.
|
||||
WID_SCMF_TIE_EARRING_TEXT, ///< Text about tie and earring.
|
||||
WID_SCMF_LIPS_MOUSTACHE_TEXT, ///< Text about lips and moustache.
|
||||
WID_SCMF_HAS_GLASSES_TEXT, ///< Text about glasses.
|
||||
WID_SCMF_HAIR_TEXT, ///< Text about hair.
|
||||
WID_SCMF_EYEBROWS_TEXT, ///< Text about eyebrows.
|
||||
WID_SCMF_EYECOLOUR_TEXT, ///< Text about eyecolour.
|
||||
WID_SCMF_GLASSES_TEXT, ///< Text about glasses.
|
||||
WID_SCMF_NOSE_TEXT, ///< Text about nose.
|
||||
WID_SCMF_CHIN_TEXT, ///< Text about chin.
|
||||
WID_SCMF_JACKET_TEXT, ///< Text about jacket.
|
||||
WID_SCMF_COLLAR_TEXT, ///< Text about collar.
|
||||
WID_SCMF_ETHNICITY_EUR, ///< Text about ethnicity european.
|
||||
WID_SCMF_ETHNICITY_AFR, ///< Text about ethnicity african.
|
||||
WID_SCMF_HAS_MOUSTACHE_EARRING, ///< Has moustache or earring.
|
||||
WID_SCMF_HAS_GLASSES, ///< Has glasses.
|
||||
WID_SCMF_EYECOLOUR_L, ///< Eyecolour left.
|
||||
WID_SCMF_EYECOLOUR, ///< Eyecolour.
|
||||
WID_SCMF_EYECOLOUR_R, ///< Eyecolour right.
|
||||
WID_SCMF_CHIN_L, ///< Chin left.
|
||||
WID_SCMF_CHIN, ///< Chin.
|
||||
WID_SCMF_CHIN_R, ///< Chin right.
|
||||
WID_SCMF_EYEBROWS_L, ///< Eyebrows left.
|
||||
WID_SCMF_EYEBROWS, ///< Eyebrows.
|
||||
WID_SCMF_EYEBROWS_R, ///< Eyebrows right.
|
||||
WID_SCMF_LIPS_MOUSTACHE_L, ///< Lips / Moustache left.
|
||||
WID_SCMF_LIPS_MOUSTACHE, ///< Lips / Moustache.
|
||||
WID_SCMF_LIPS_MOUSTACHE_R, ///< Lips / Moustache right.
|
||||
WID_SCMF_NOSE_L, ///< Nose left.
|
||||
WID_SCMF_NOSE, ///< Nose.
|
||||
WID_SCMF_NOSE_R, ///< Nose right.
|
||||
WID_SCMF_HAIR_L, ///< Hair left.
|
||||
WID_SCMF_HAIR, ///< Hair.
|
||||
WID_SCMF_HAIR_R, ///< Hair right.
|
||||
WID_SCMF_JACKET_L, ///< Jacket left.
|
||||
WID_SCMF_JACKET, ///< Jacket.
|
||||
WID_SCMF_JACKET_R, ///< Jacket right.
|
||||
WID_SCMF_COLLAR_L, ///< Collar left.
|
||||
WID_SCMF_COLLAR, ///< Collar.
|
||||
WID_SCMF_COLLAR_R, ///< Collar right.
|
||||
WID_SCMF_TIE_EARRING_L, ///< Tie / Earring left.
|
||||
WID_SCMF_TIE_EARRING, ///< Tie / Earring.
|
||||
WID_SCMF_TIE_EARRING_R, ///< Tie / Earring right.
|
||||
WID_SCMF_GLASSES_L, ///< Glasses left.
|
||||
WID_SCMF_GLASSES, ///< Glasses.
|
||||
WID_SCMF_GLASSES_R, ///< Glasses right.
|
||||
WID_SCMF_STYLE, ///< Style selector widget.
|
||||
WID_SCMF_PARTS, ///< Face configuration parts widget.
|
||||
WID_SCMF_PARTS_SCROLLBAR, ///< Scrollbar for configuration parts widget.
|
||||
};
|
||||
|
||||
/** Widgets of the #CompanyInfrastructureWindow class. */
|
||||
|
Reference in New Issue
Block a user