summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThorbjørn Lindeijer <bjorn@lindeijer.nl>2025-07-17 15:09:11 +0200
committerThorbjørn Lindeijer <bjorn@lindeijer.nl>2025-08-04 20:51:09 +0200
commitbc29cbb38913abf66c794a134afe62ab8acf70b4 (patch)
treec38bc839b42865ace4d1cda5ba2a67d5078d2cac
parente6dceba196e5bc8e305101c63add0c76a3912ff0 (diff)
downloadmana-bc29cbb38913abf66c794a134afe62ab8acf70b4.tar.gz
mana-bc29cbb38913abf66c794a134afe62ab8acf70b4.tar.bz2
mana-bc29cbb38913abf66c794a134afe62ab8acf70b4.tar.xz
mana-bc29cbb38913abf66c794a134afe62ab8acf70b4.zip
Added support for the hidden/neutral gender
TMWA supports a "neutral" gender, which in Mana client maps to "hidden". In The Mana World, it means a player character is not displayed as clearly male or female, using a dedicated base sprite with more underwear. It is not possible to choose the neutral gender on account registration or character creation. In TMW, these choices are not actually relevant anyway and could probably be removed. Fixes #114
-rw-r--r--src/gui/charcreatedialog.cpp4
-rw-r--r--src/net/charhandler.h2
-rw-r--r--src/net/manaserv/charhandler.cpp4
-rw-r--r--src/net/manaserv/charhandler.h2
-rw-r--r--src/net/tmwa/beinghandler.cpp8
-rw-r--r--src/net/tmwa/charserverhandler.cpp17
-rw-r--r--src/net/tmwa/charserverhandler.h3
-rw-r--r--src/net/tmwa/gamehandler.cpp2
-rw-r--r--src/net/tmwa/loginhandler.cpp2
-rw-r--r--src/net/tmwa/protocol.h22
-rw-r--r--src/net/tmwa/token.h22
11 files changed, 56 insertions, 32 deletions
diff --git a/src/gui/charcreatedialog.cpp b/src/gui/charcreatedialog.cpp
index cff7d822..6b46d2ac 100644
--- a/src/gui/charcreatedialog.cpp
+++ b/src/gui/charcreatedialog.cpp
@@ -191,8 +191,10 @@ void CharCreateDialog::action(const gcn::ActionEvent &event)
0 : mHairStylesIds.at(mHairStyleId);
int hairColor = mHairColorsIds.empty() ?
0 : mHairColorsIds.at(mHairColorId);
+
+ Gender gender = mFemale->isSelected() ? Gender::Female : Gender::Male;
Net::getCharHandler()->newCharacter(getName(), characterSlot,
- mFemale->isSelected(),
+ gender,
hairStyle, hairColor,
mAttributes);
}
diff --git a/src/net/charhandler.h b/src/net/charhandler.h
index 3f0c096f..c04cf81e 100644
--- a/src/net/charhandler.h
+++ b/src/net/charhandler.h
@@ -62,7 +62,7 @@ class CharHandler
virtual void chooseCharacter(Net::Character *character) = 0;
virtual void newCharacter(const std::string &name, int slot,
- bool gender, int hairstyle, int hairColor,
+ Gender gender, int hairstyle, int hairColor,
const std::vector<int> &stats) = 0;
virtual void deleteCharacter(Net::Character *character) = 0;
diff --git a/src/net/manaserv/charhandler.cpp b/src/net/manaserv/charhandler.cpp
index cc0ea17e..8d7c5511 100644
--- a/src/net/manaserv/charhandler.cpp
+++ b/src/net/manaserv/charhandler.cpp
@@ -339,7 +339,7 @@ void CharHandler::chooseCharacter(Net::Character *character)
void CharHandler::newCharacter(const std::string &name,
int slot,
- bool gender,
+ Gender gender,
int hairstyle,
int hairColor,
const std::vector<int> &stats)
@@ -349,7 +349,7 @@ void CharHandler::newCharacter(const std::string &name,
msg.writeString(name);
msg.writeInt8(hairstyle);
msg.writeInt8(hairColor);
- msg.writeInt8(gender);
+ msg.writeInt8(gender == Gender::Female);
msg.writeInt8(slot);
for (int stat : stats)
diff --git a/src/net/manaserv/charhandler.h b/src/net/manaserv/charhandler.h
index e962bdfa..c26194ee 100644
--- a/src/net/manaserv/charhandler.h
+++ b/src/net/manaserv/charhandler.h
@@ -59,7 +59,7 @@ class CharHandler final : public MessageHandler, public Net::CharHandler
void chooseCharacter(Net::Character *character) override;
void newCharacter(const std::string &name, int slot,
- bool gender, int hairstyle, int hairColor,
+ Gender gender, int hairstyle, int hairColor,
const std::vector<int> &stats) override;
void deleteCharacter(Net::Character *character) override;
diff --git a/src/net/tmwa/beinghandler.cpp b/src/net/tmwa/beinghandler.cpp
index 690b0d87..d2fd4695 100644
--- a/src/net/tmwa/beinghandler.cpp
+++ b/src/net/tmwa/beinghandler.cpp
@@ -217,7 +217,8 @@ void BeingHandler::handleMessage(MessageIn &msg)
return;
int id;
- short job, gender;
+ short job;
+ SEX sex;
float speed;
Uint16 headTop, headMid, headBottom;
Uint16 shoes, gloves;
@@ -305,12 +306,11 @@ void BeingHandler::handleMessage(MessageIn &msg)
msg.readInt16(); // manner
opt3 = msg.readInt16();
msg.readInt8(); // karma
- gender = msg.readInt8();
+ sex = static_cast<SEX>(msg.readInt8());
if (dstBeing->getType() == ActorSprite::PLAYER)
{
- dstBeing->setGender(gender == 0 ? Gender::Female
- : Gender::Male);
+ dstBeing->setGender(sexToGender(sex));
// Set these after the gender, as the sprites may be gender-specific
dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1,
hairDB.getHairColor(hairColor));
diff --git a/src/net/tmwa/charserverhandler.cpp b/src/net/tmwa/charserverhandler.cpp
index fcd47b74..e8562bf9 100644
--- a/src/net/tmwa/charserverhandler.cpp
+++ b/src/net/tmwa/charserverhandler.cpp
@@ -36,6 +36,7 @@
#include "net/tmwa/messageout.h"
#include "net/tmwa/network.h"
#include "net/tmwa/protocol.h"
+#include "net/tmwa/token.h"
#include "resources/attributes.h"
#include "resources/chardb.h"
@@ -241,7 +242,7 @@ void CharServerHandler::readPlayerData(MessageIn &msg, Net::Character *character
const uint16_t weapon = msg.readInt16();
auto *tempPlayer = new LocalPlayer(id, race);
- tempPlayer->setGender(token.sex);
+ tempPlayer->setGender(sexToGender(token.sex));
tempPlayer->setSprite(SPRITE_SHOE, shoe);
tempPlayer->setSprite(SPRITE_GLOVES, gloves);
@@ -267,8 +268,9 @@ void CharServerHandler::readPlayerData(MessageIn &msg, Net::Character *character
character->data.mStats[i + STRENGTH].base = msg.readInt8();
character->slot = msg.readInt8(); // character slot
- const uint8_t sex = msg.readInt8();
- tempPlayer->setGender(sex ? Gender::Male : Gender::Female);
+ const Gender gender = sexToGender(static_cast<SEX>(msg.readInt8()));
+ if (gender != Gender::Unspecified)
+ tempPlayer->setGender(gender);
}
void CharServerHandler::setCharSelectDialog(CharSelectDialog *window)
@@ -306,7 +308,10 @@ void CharServerHandler::setCharCreateDialog(CharCreateDialog *window)
sumStat = Attributes::getCreationPoints();
mCharCreateDialog->setAttributes(attributes, sumStat, minStat, maxStat);
- mCharCreateDialog->setDefaultGender(token.sex);
+
+ const Gender gender = sexToGender(token.sex);
+ if (gender != Gender::Unspecified)
+ mCharCreateDialog->setDefaultGender(gender);
}
void CharServerHandler::requestCharacters()
@@ -324,7 +329,7 @@ void CharServerHandler::chooseCharacter(Net::Character *character)
}
void CharServerHandler::newCharacter(const std::string &name, int slot,
- bool gender, int hairstyle, int hairColor,
+ Gender /*gender*/, int hairstyle, int hairColor,
const std::vector<int> &stats)
{
MessageOut outMsg(CMSG_CHAR_CREATE);
@@ -400,7 +405,7 @@ void CharServerHandler::connect()
// [Fate] The next word is unused by the old char server, so we squeeze in
// mana client version information
outMsg.writeInt16(CLIENT_PROTOCOL_VERSION);
- outMsg.writeInt8(token.sex == Gender::Male ? 1 : 0);
+ outMsg.writeInt8(static_cast<uint8_t>(token.sex));
// We get 4 useless bytes before the real answer comes in (what are these?)
mNetwork->skip(4);
diff --git a/src/net/tmwa/charserverhandler.h b/src/net/tmwa/charserverhandler.h
index b0d3e970..cb79f969 100644
--- a/src/net/tmwa/charserverhandler.h
+++ b/src/net/tmwa/charserverhandler.h
@@ -24,7 +24,6 @@
#include "net/charhandler.h"
#include "net/tmwa/messagehandler.h"
-#include "net/tmwa/token.h"
class LoginData;
@@ -53,7 +52,7 @@ class CharServerHandler final : public MessageHandler, public Net::CharHandler
void chooseCharacter(Net::Character *character) override;
- void newCharacter(const std::string &name, int slot, bool gender,
+ void newCharacter(const std::string &name, int slot, Gender gender,
int hairstyle, int hairColor,
const std::vector<int> &stats) override;
diff --git a/src/net/tmwa/gamehandler.cpp b/src/net/tmwa/gamehandler.cpp
index a88e377e..f16a133b 100644
--- a/src/net/tmwa/gamehandler.cpp
+++ b/src/net/tmwa/gamehandler.cpp
@@ -140,7 +140,7 @@ void GameHandler::connect()
outMsg.writeInt32(mCharID);
outMsg.writeInt32(token.session_ID1);
outMsg.writeInt32(token.session_ID2);
- outMsg.writeInt8(token.sex == Gender::Male ? 1 : 0);
+ outMsg.writeInt8(static_cast<uint8_t>(token.sex));
// We get 4 useless bytes before the real answer comes in (what are these?)
mNetwork->skip(4);
diff --git a/src/net/tmwa/loginhandler.cpp b/src/net/tmwa/loginhandler.cpp
index b6e3d518..fba8c9f8 100644
--- a/src/net/tmwa/loginhandler.cpp
+++ b/src/net/tmwa/loginhandler.cpp
@@ -124,7 +124,7 @@ void LoginHandler::handleMessage(MessageIn &msg)
mToken.account_ID = msg.readInt32();
mToken.session_ID2 = msg.readInt32();
msg.skip(30); // unused
- mToken.sex = msg.readInt8() ? Gender::Male : Gender::Female;
+ mToken.sex = static_cast<SEX>(msg.readInt8());
for (int i = 0; i < worldCount; i++)
{
diff --git a/src/net/tmwa/protocol.h b/src/net/tmwa/protocol.h
index 532ac90e..1f91616f 100644
--- a/src/net/tmwa/protocol.h
+++ b/src/net/tmwa/protocol.h
@@ -21,6 +21,8 @@
#pragma once
+#include "being.h"
+
#include <cstdint>
namespace TmwAthena {
@@ -93,6 +95,26 @@ enum class DIR : uint8_t
COUNT,
};
+enum class SEX : uint8_t
+{
+ FEMALE = 0,
+ MALE = 1,
+ UNSPECIFIED = 2,
+ NEUTRAL = 3,
+};
+
+inline Gender sexToGender(SEX sex)
+{
+ switch (sex)
+ {
+ case SEX::FEMALE: return Gender::Female;
+ case SEX::MALE: return Gender::Male;
+ case SEX::UNSPECIFIED: return Gender::Unspecified;
+ case SEX::NEUTRAL: return Gender::Hidden;
+ }
+ return Gender::Unspecified;
+}
+
enum NpcCommand
{
NPC_REQUEST_LANG = 0,
diff --git a/src/net/tmwa/token.h b/src/net/tmwa/token.h
index b563bf65..c24fbebb 100644
--- a/src/net/tmwa/token.h
+++ b/src/net/tmwa/token.h
@@ -19,22 +19,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "being.h"
+#include "net/tmwa/protocol.h"
#pragma once
+namespace TmwAthena {
+
struct Token
{
- int account_ID;
- int session_ID1;
- int session_ID2;
- Gender sex;
-
- void clear()
- {
- account_ID = 0;
- session_ID1 = 0;
- session_ID2 = 0;
- sex = Gender::Unspecified;
- }
+ int account_ID = 0;
+ int session_ID1 = 0;
+ int session_ID2 = 0;
+ SEX sex = SEX::UNSPECIFIED;
};
+
+} // namespace TmwAthena