mirror of https://github.com/OpenTTD/OpenTTD
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.pull/12131/head
parent
ebc74c8905
commit
a46a3a97f3
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
Loading…
Reference in New Issue