summaryrefslogtreecommitdiff
path: root/src/player_face.h
blob: 752ab455f78cb3dd5570eaf03f8b9a4ec68e7baf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/* $Id$ */

/** @file player_face.h Functionality related to the player's face */

#ifndef PLAYER_FACE_H
#define PLAYER_FACE_H

/** The gender/race combinations that we have faces for */
enum GenderEthnicity {
	GENDER_FEMALE    = 0, ///< This bit set means a female, otherwise male
	ETHNICITY_BLACK  = 1, ///< This bit set means black, otherwise white

	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,
};
DECLARE_ENUM_AS_BIT_SET(GenderEthnicity); ///< See GenderRace as a bitset

/** Bitgroups of the PlayerFace variable */
enum PlayerFaceVariable {
	PFV_GENDER,
	PFV_ETHNICITY,
	PFV_GEN_ETHN,
	PFV_HAS_MOUSTACHE,
	PFV_HAS_TIE_EARRING,
	PFV_HAS_GLASSES,
	PFV_EYE_COLOUR,
	PFV_CHEEKS,
	PFV_CHIN,
	PFV_EYEBROWS,
	PFV_MOUSTACHE,
	PFV_LIPS,
	PFV_NOSE,
	PFV_HAIR,
	PFV_JACKET,
	PFV_COLLAR,
	PFV_TIE_EARRING,
	PFV_GLASSES,
	PFV_END
};
DECLARE_POSTFIX_INCREMENT(PlayerFaceVariable);

/** Information about the valid values of PlayerFace bitgroups as well as the sprites to draw */
struct PlayerFaceBitsInfo {
	byte     offset;               ///< Offset in bits into the PlayerFace
	byte     length;               ///< Number of bits used in the PlayerFace
	byte     valid_values[GE_END]; ///< The number of valid values per gender/ethnicity
	SpriteID first_sprite[GE_END]; ///< The first sprite per gender/ethnicity
};

/** Lookup table for indices into the PlayerFace, valid ranges and sprites */
static const PlayerFaceBitsInfo _pf_info[] = {
	/* Index                   off len   WM  WF  BM  BF         WM     WF     BM     BF */
	/* PFV_GENDER          */ {  0, 1, {  2,  2,  2,  2 }, {     0,     0,     0,     0 } }, ///< 0 = male, 1 = female
	/* PFV_ETHNICITY       */ {  1, 2, {  2,  2,  2,  2 }, {     0,     0,     0,     0 } }, ///< 0 = (Western-)Caucasian, 1 = African(-American)/Black
	/* PFV_GEN_ETHN        */ {  0, 3, {  4,  4,  4,  4 }, {     0,     0,     0,     0 } }, ///< Shortcut to get/set gender _and_ ethnicity
	/* PFV_HAS_MOUSTACHE   */ {  3, 1, {  2,  0,  2,  0 }, {     0,     0,     0,     0 } }, ///< Females do not have a moustache
	/* PFV_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.
	/* PFV_HAS_GLASSES     */ {  4, 1, {  2,  2,  2,  2 }, {     0,     0,     0,     0 } }, ///< Whether to draw glasses or not
	/* PFV_EYE_COLOUR      */ {  5, 2, {  3,  3,  3,  3 }, {     0,     0,     0,     0 } }, ///< Palette modification
	/* PFV_CHEEKS          */ {  0, 0, {  1,  1,  1,  1 }, { 0x325, 0x326, 0x390, 0x3B0 } }, ///< Cheeks are only indexed by their gender/ethnicity
	/* PFV_CHIN            */ {  7, 2, {  4,  1,  2,  2 }, { 0x327, 0x327, 0x391, 0x3B1 } },
	/* PFV_EYEBROWS        */ {  9, 4, { 12, 16, 11, 16 }, { 0x32B, 0x337, 0x39A, 0x3B8 } },
	/* PFV_MOUSTACHE       */ { 13, 2, {  3,  0,  3,  0 }, { 0x367,     0, 0x397,     0 } }, ///< Depends on PFV_HAS_MOUSTACHE
	/* PFV_LIPS            */ { 13, 4, { 12, 10,  9,  9 }, { 0x35B, 0x351, 0x3A5, 0x3C8 } }, ///< Depends on !PFV_HAS_MOUSTACHE
	/* PFV_NOSE            */ { 17, 3, {  8,  4,  4,  5 }, { 0x349, 0x34C, 0x393, 0x3B3 } }, ///< Depends on !PFV_HAS_MOUSTACHE
	/* PFV_HAIR            */ { 20, 4, {  9,  5,  5,  4 }, { 0x382, 0x38B, 0x3D4, 0x3D9 } },
	/* PFV_JACKET          */ { 24, 2, {  3,  3,  3,  3 }, { 0x36B, 0x378, 0x36B, 0x378 } },
	/* PFV_COLLAR          */ { 26, 2, {  4,  4,  4,  4 }, { 0x36E, 0x37B, 0x36E, 0x37B } },
	/* PFV_TIE_EARRING     */ { 28, 3, {  6,  3,  6,  3 }, { 0x372, 0x37F, 0x372, 0x3D1 } }, ///< Depends on PFV_HAS_TIE_EARRING
	/* PFV_GLASSES         */ { 31, 1, {  2,  2,  2,  2 }, { 0x347, 0x347, 0x3AE, 0x3AE } }  ///< Depends on PFV_HAS_GLASSES
};
assert_compile(lengthof(_pf_info) == PFV_END);

/**
 * Gets the player's face bits for the given player face variable
 * @param pf  the face to extract the bits from
 * @param pfv the face variable to get the data of
 * @param ge  the gender and ethnicity of the face
 * @pre _pf_info[pfv].valid_values[ge] != 0
 * @return the requested bits
 */
static inline uint GetPlayerFaceBits(PlayerFace pf, PlayerFaceVariable pfv, GenderEthnicity ge)
{
	assert(_pf_info[pfv].valid_values[ge] != 0);

	return GB(pf, _pf_info[pfv].offset, _pf_info[pfv].length);
}

/**
 * Sets the player's face bits for the given player face variable
 * @param pf  the face to write the bits to
 * @param pfv the face variable to write the data of
 * @param ge  the gender and ethnicity of the face
 * @param val the new value
 * @pre val < _pf_info[pfv].valid_values[ge]
 */
static inline void SetPlayerFaceBits(PlayerFace &pf, PlayerFaceVariable pfv, GenderEthnicity ge, uint val)
{
	assert(val < _pf_info[pfv].valid_values[ge]);

	SB(pf, _pf_info[pfv].offset, _pf_info[pfv].length, val);
}

/**
 * Checks whether the player bits have a valid range
 * @param pf  the face to extract the bits from
 * @param pfv 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
 */
static inline bool ArePlayerFaceBitsValid(PlayerFace pf, PlayerFaceVariable pfv, GenderEthnicity ge)
{
	return GB(pf, _pf_info[pfv].offset, _pf_info[pfv].length) < _pf_info[pfv].valid_values[ge];
}

/**
 * Scales a player face bits variable to the correct scope
 * @param pfv 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 << _pf_info[pfv].length), i.e. val has a value of 0..2^(bits used for this variable)-1
 * @return the scaled value
 */
static inline uint ScalePlayerFaceValue(PlayerFaceVariable pfv, GenderEthnicity ge, uint val)
{
	assert(val < (1U << _pf_info[pfv].length));

	return (val * _pf_info[pfv].valid_values[ge]) >> _pf_info[pfv].length;
}

/**
 * Gets the sprite to draw for the given player face variable
 * @param pf  the face to extract the data from
 * @param pfv the face variable to get the sprite of
 * @param ge  the gender and ethnicity of the face
 * @pre _pf_info[pfv].valid_values[ge] != 0
 * @return sprite to draw
 */
static inline SpriteID GetPlayerFaceSprite(PlayerFace pf, PlayerFaceVariable pfv, GenderEthnicity ge)
{
	assert(_pf_info[pfv].valid_values[ge] != 0);

	return _pf_info[pfv].first_sprite[ge] + GB(pf, _pf_info[pfv].offset, _pf_info[pfv].length);
}

void DrawPlayerFace(PlayerFace face, int color, int x, int y);
PlayerFace ConvertFromOldPlayerFace(uint32 face);
bool IsValidPlayerFace(PlayerFace pf);

#endif /* PLAYER_FACE_H */