From f593049cd8286f48497782d8bc0afe787724ad5d Mon Sep 17 00:00:00 2001 From: wushin Date: Mon, 9 Nov 2015 13:46:56 -0600 Subject: Add third gender to account Add Gender to char Make gear work proper with new gender Enable legacy clients to use account gender --- src/admin/ladmin.cpp | 14 ++++++---- src/ast/item_test.cpp | 2 +- src/char/char.cpp | 73 +++++++++++++++++++++++++++++---------------------- src/login/login.cpp | 14 +++------- src/map/atcommand.cpp | 34 ++++++++++++++++++++---- src/map/chrif.cpp | 5 +--- src/map/pc.cpp | 32 +++++++++++++++++++--- src/mmo/enums.hpp | 9 ++++--- tools/protocol.py | 7 ++--- 9 files changed, 124 insertions(+), 66 deletions(-) diff --git a/src/admin/ladmin.cpp b/src/admin/ladmin.cpp index e0d6b06..ac9be1d 100644 --- a/src/admin/ladmin.cpp +++ b/src/admin/ladmin.cpp @@ -205,7 +205,7 @@ namespace admin // // sex // Modify the sex of an account. -// sex testname Male +// sex testname M // // state // Change the state of an account. @@ -490,7 +490,7 @@ void display_help(ZString param) { PRINTF("sex \n"_fmt); PRINTF(" Modify the sex of an account.\n"_fmt); - PRINTF(" sex testname Male\n"_fmt); + PRINTF(" sex testname M\n"_fmt); } else if (command == "state"_s) { @@ -627,7 +627,7 @@ void addaccount(ZString param, int emailflag) if (!name.is_print()) return; - if (!"MF"_s.contains(sex)) + if (!"MFN"_s.contains(sex)) { PRINTF("Illegal gender [%c]. Please input M or F.\n"_fmt, sex); LADMIN_LOG("Illegal gender [%c]. Please input M or F.\n"_fmt, sex); @@ -1291,7 +1291,7 @@ void changesex(ZString param) if (!qsplit(param, &name, &sex_)) { PRINTF("Please input an account name and a sex.\n"_fmt); - PRINTF(" sex testname Male\n"_fmt); + PRINTF(" sex testname M\n"_fmt); LADMIN_LOG("Incomplete parameters to change the sex of an account ('sex' command).\n"_fmt); return; } @@ -1303,7 +1303,7 @@ void changesex(ZString param) return; } - if (!"MF"_s.contains(sex)) + if (!"MFN"_s.contains(sex)) { PRINTF("Illegal gender [%c]. Please input M or F.\n"_fmt, sex); LADMIN_LOG("Illegal gender [%c]. Please input M or F.\n"_fmt, sex); @@ -1750,6 +1750,8 @@ void parse_fromlogin(Session *s) PRINTF("%-5s "_fmt, "Femal"_s); else if (info.sex == SEX::MALE) PRINTF("%-5s "_fmt, "Male"_s); + else if (info.sex == SEX::NEUTRAL) + PRINTF("%-5s "_fmt, "None"_s); else PRINTF("%-5s "_fmt, "Servr"_s); PRINTF("%6d "_fmt, info.login_count); @@ -2347,6 +2349,8 @@ void parse_fromlogin(Session *s) PRINTF(" Sex: Female\n"_fmt); else if (sex == SEX::MALE) PRINTF(" Sex: Male\n"_fmt); + else if (sex == SEX::NEUTRAL) + PRINTF(" Sex: None\n"_fmt); else // doesn't happen anymore PRINTF(" Sex: Server\n"_fmt); PRINTF(" E-mail: %s\n"_fmt, email); diff --git a/src/ast/item_test.cpp b/src/ast/item_test.cpp index 6e2cee7..7bb7193 100644 --- a/src/ast/item_test.cpp +++ b/src/ast/item_test.cpp @@ -127,7 +127,7 @@ namespace item EXPECT_SPAN(p->slot_unused.span, 1,31, 1,32); EXPECT_EQ(p->slot_unused.data, "xx"_s); EXPECT_SPAN(p->gender.span, 1,34, 1,34); - EXPECT_EQ(p->gender.data, SEX::NEUTRAL); + EXPECT_EQ(p->gender.data, SEX::UNSPECIFIED); EXPECT_SPAN(p->loc.span, 1,36, 1,37); EXPECT_EQ(p->loc.data, EPOS::MISC1); EXPECT_SPAN(p->wlv.span, 1,39, 1,40); diff --git a/src/char/char.cpp b/src/char/char.cpp index 0958c5e..bc759d0 100644 --- a/src/char/char.cpp +++ b/src/char/char.cpp @@ -235,6 +235,16 @@ AString mmo_char_tostr(struct CharPair *cp) { p->last_point = char_conf.start_point; } + if (p->sex == SEX::UNSPECIFIED) + { + for (AuthFifoEntry& afi : auth_fifo) + { + if (afi.account_id == k->account_id) + { + p->sex = afi.sex; + } + } + } MString str_p; str_p += STRPRINTF( @@ -251,7 +261,8 @@ AString mmo_char_tostr(struct CharPair *cp) "%d,%d,%d\t" "%d,%d,%d,%d,%d\t" "%s,%d,%d\t" - "%s,%d,%d,%d\t"_fmt, + "%s,%d,%d,%d\t" + "%c\t"_fmt, k->char_id, k->account_id, k->char_num, k->name, @@ -265,10 +276,8 @@ AString mmo_char_tostr(struct CharPair *cp) p->hair, p->hair_color, p->clothes_color, p->weapon, p->shield, p->head_top, p->head_mid, p->head_bottom, p->last_point.map_, p->last_point.x, p->last_point.y, - p->save_point.map_, p->save_point.x, p->save_point.y, p->partner_id); - - // memos were here (no longer supported) - str_p += '\t'; + p->save_point.map_, p->save_point.x, p->save_point.y, p->partner_id, + sex_to_char(p->sex)); for (IOff0 i : IOff0::iter()) { @@ -352,6 +361,7 @@ bool impl_extract(XString str, CharPair *cp) CharData *p = cp->data.get(); uint32_t unused_guild_id, unused_pet_id; + VString<1> sex; XString unused_memos; std::vector inventory; XString unused_cart; @@ -377,13 +387,17 @@ bool impl_extract(XString str, CharPair *cp) // of this, instead of adding a new \t // or putting it elsewhere, like by pet/guild record<','>(&p->save_point.map_, &p->save_point.x, &p->save_point.y, &p->partner_id), - &unused_memos, + &sex, vrec<' '>(&inventory), &unused_cart, vrec<' '>(&skills), vrec<' '>(&vars)))) return false; + if (sex.size() != 1) + p->sex = SEX::UNSPECIFIED; + else + p->sex = sex_from_char(sex.front()); // leftover corruption from Platinum if (hair_style == "-1"_s) { @@ -706,6 +720,7 @@ CharPair *make_new_char(Session *s, CharName name, const Stats6& stats, uint8_t ck.char_num = slot; ck.name = name; cd.species = Species(); + cd.sex = SEX::NEUTRAL; cd.base_level = 1; cd.job_level = 1; cd.base_exp = 0; @@ -978,7 +993,10 @@ int mmo_char_send006b(Session *s, struct char_session_data *sd) sel.stats.dex = saturate(p->attrs[ATTR::DEX]); sel.stats.luk = saturate(p->attrs[ATTR::LUK]); sel.char_num = k->char_num; - sel.unused = 0; + if (p->sex == SEX::UNSPECIFIED) + sel.sex = sd->sex; + else + sel.sex = p->sex; repeat_6b.push_back(info); } @@ -1257,29 +1275,6 @@ void parse_tologin(Session *ls) SEX sex = fixed.sex; if (acc) { - for (CharPair& cp : char_keys) - { - CharKey& ck = cp.key; - CharData& cd = *cp.data; - if (ck.account_id == acc) - { - cd.sex = sex; -// auth_fifo[i].sex = sex; - // to avoid any problem with equipment and invalid sex, equipment is unequiped. - for (IOff0 j : IOff0::iter()) - { - if (cd.inventory[j].nameid - && bool(cd.inventory[j].equip)) - cd.inventory[j].equip = EPOS::ZERO; - } - cd.weapon = ItemLook::NONE; - cd.shield = ItemNameId(); - cd.head_top = ItemNameId(); - cd.head_mid = ItemNameId(); - cd.head_bottom = ItemNameId(); - } - } - // disconnect player if online on char-server disconnect_player(acc); } Packet_Fixed<0x2b0d> fixed_0d; @@ -1984,6 +1979,8 @@ void parse_frommap(Session *ms) break; } case 5: // changesex + case 6: // changesex + case 7: // changesex { if (!acc || isGM(acc).overwhelms(isGM(ck->account_id))) @@ -1992,7 +1989,19 @@ void parse_frommap(Session *ms) { // don't send request if no login-server Packet_Fixed<0x2727> fixed_27; fixed_27.account_id = ck->account_id; - send_fpacket<0x2727, 6>(login_session, fixed_27); + switch (operation) + { + case 5: + fixed_27.sex = SEX::FEMALE; + break; + case 6: + fixed_27.sex = SEX::MALE; + break; + case 7: + fixed_27.sex = SEX::NEUTRAL; + break; + } + send_fpacket<0x2727, 7>(login_session, fixed_27); } else fixed_0f.error = 3; @@ -2437,7 +2446,7 @@ void parse_char(Session *s) fixed_6d.char_select.stats.dex = saturate(cd->attrs[ATTR::DEX]); fixed_6d.char_select.stats.luk = saturate(cd->attrs[ATTR::LUK]); fixed_6d.char_select.char_num = ck->char_num; - fixed_6d.char_select.unused2 = 0; + fixed_6d.char_select.sex = cd->sex; send_fpacket<0x006d, 108>(s, fixed_6d); break; diff --git a/src/login/login.cpp b/src/login/login.cpp index 9310ad3..4ab0773 100644 --- a/src/login/login.cpp +++ b/src/login/login.cpp @@ -441,8 +441,6 @@ bool impl_extract(XString line, AuthData *ad) if (sex.size() != 1) return false; ad->sex = sex_from_char(sex.front()); - if (ad->sex == SEX::NEUTRAL) - return false; if (!e_mail_check(ad->email)) ad->email = DEFAULT_EMAIL; @@ -1181,7 +1179,7 @@ void parse_fromchar(Session *s) case 0x2727: // Change of sex (sex is reversed) { Packet_Fixed<0x2727> fixed; - rv = recv_fpacket<0x2727, 6>(s, fixed); + rv = recv_fpacket<0x2727, 7>(s, fixed); if (rv != RecvResult::Complete) break; @@ -1192,11 +1190,7 @@ void parse_fromchar(Session *s) if (ad.account_id == acc) { { - SEX sex; - if (ad.sex == SEX::FEMALE) - sex = SEX::MALE; - else - sex = SEX::FEMALE; + SEX sex = fixed.sex; LOGIN_LOG("Char-server '%s': Sex change (account: %d, new sex %c, ip: %s).\n"_fmt, server[id].name, acc, sex_to_char(sex), @@ -1504,7 +1498,7 @@ void parse_admin(Session *s) LOGIN_LOG("'ladmin': Attempt to create an invalid account (account or pass is too short, ip: %s)\n"_fmt, ip); } - else if (ma.sex != SEX::FEMALE && ma.sex != SEX::MALE) + else if (ma.sex != SEX::FEMALE && ma.sex != SEX::MALE && ma.sex != SEX::NEUTRAL) { LOGIN_LOG("'ladmin': Attempt to create an invalid account (account: %s, invalid sex, ip: %s)\n"_fmt, ma.userid, ip); @@ -1765,7 +1759,7 @@ void parse_admin(Session *s) { SEX sex = fixed.sex; - if (sex != SEX::FEMALE && sex != SEX::MALE) + if (sex != SEX::FEMALE && sex != SEX::MALE && sex != SEX::NEUTRAL) { LOGIN_LOG("'ladmin': Attempt to give an invalid sex (account: %s, received sex: %c, ip: %s)\n"_fmt, account_name, sex_to_char(sex), ip); diff --git a/src/map/atcommand.cpp b/src/map/atcommand.cpp index 4dd80bb..11ca1a3 100644 --- a/src/map/atcommand.cpp +++ b/src/map/atcommand.cpp @@ -2348,12 +2348,36 @@ ATCE atcommand_char_change_sex(Session *s, dumb_ptr sd, ZString message) { CharName character; + VString<1> gender; + int operation; - if (!asplit(message, &character)) + if (!extract(message, record<' ', 1>(&character, &gender))) + { + clif_displaymessage(s, + "Please, enter a char name (usage: @charchangesex [Gender])."_s); return ATCE::USAGE; - + } + else { - chrif_char_ask_name(sd->status_key.account_id, character, 5, HumanTimeDiff()); + if (sex_from_char(gender.front()) == SEX::FEMALE) + { + operation = 5; + } + else if (sex_from_char(gender.front()) == SEX::MALE) + { + operation = 6; + } + else if (sex_from_char(gender.front()) == SEX::NEUTRAL) + { + operation = 7; + } + else + { + clif_displaymessage(s, + "Please, enter a char name (usage: @charchangesex [Gender])."_s); + return ATCE::USAGE; + } + chrif_char_ask_name(sd->status_key.account_id, character, operation, HumanTimeDiff()); // type: 5 - changesex clif_displaymessage(s, "Character name sends to char-server to ask it."_s); } @@ -5215,9 +5239,9 @@ Map atcommand_info = {"allstats"_s, {"[value]"_s, 60, atcommand_all_stats, "Adjust all stats by value (or maximum)"_s}}, - {"charchangesex"_s, {""_s, + {"charchangesex"_s, {" "_s, 60, atcommand_char_change_sex, - "Flip a characters sex and disconnect them"_s}}, + "Change a characters sex and disconnect them"_s}}, {"block"_s, {""_s, 60, atcommand_char_block, "Permanently block a player's account from the server"_s}}, diff --git a/src/map/chrif.cpp b/src/map/chrif.cpp index ce5669e..7421344 100644 --- a/src/map/chrif.cpp +++ b/src/map/chrif.cpp @@ -554,10 +554,7 @@ void chrif_changedsex(Session *, const Packet_Fixed<0x2b0d>& fixed) { if (sd != nullptr && sd->status.sex != sex) { - if (sd->status.sex == SEX::MALE) - sd->sex = sd->status.sex = SEX::FEMALE; - else if (sd->status.sex == SEX::FEMALE) - sd->sex = sd->status.sex = SEX::MALE; + sd->sex = sd->status.sex = sex; // to avoid any problem with equipment and invalid sex, equipment is unequiped. for (IOff0 i : IOff0::iter()) { diff --git a/src/map/pc.cpp b/src/map/pc.cpp index 254ecb5..8a5127d 100644 --- a/src/map/pc.cpp +++ b/src/map/pc.cpp @@ -627,7 +627,7 @@ int pc_isequip(dumb_ptr sd, IOff0 n) return 1; P item = TRY_UNWRAP(sd->inventory_data[n], return 0); - if (item->sex != SEX::NEUTRAL && sd->status.sex != item->sex) + if (item->sex != SEX::UNSPECIFIED && sd->status.sex != item->sex) return 0; if (item->elv > 0 && sd->status.base_level < item->elv) return 0; @@ -2129,7 +2129,7 @@ int pc_isUseitem(dumb_ptr sd, IOff0 n) if (itemdb_type(nameid) != ItemType::USE) return 0; - if (item->sex != SEX::NEUTRAL && sd->status.sex != item->sex) + if (item->sex != SEX::UNSPECIFIED && sd->status.sex != item->sex) return 0; if (item->elv > 0 && sd->status.base_level < item->elv) return 0; @@ -3493,7 +3493,33 @@ int pc_setparam(dumb_ptr sd, SP type, int val) } break; case SP::SEX: - chrif_char_ask_name(AccountId(), sd->status_key.name, 5, HumanTimeDiff()); + int operation; + switch (val) + { + case 0: + sd->sex = sd->status.sex = SEX::FEMALE; + operation = 5; + break; + case 1: + sd->sex = sd->status.sex = SEX::MALE; + operation = 6; + break; + default: + sd->sex = sd->status.sex = SEX::NEUTRAL; + operation = 7; + break; + } + for (IOff0 j : IOff0::iter()) + { + if (sd->status.inventory[j].nameid + && bool(sd->status.inventory[j].equip)) + pc_unequipitem(sd, j, CalcStatus::LATER); + } + pc_calcstatus(sd, 0); + chrif_save(sd); + sd->login_id1++; + clif_fixpcpos(sd); + chrif_char_ask_name(AccountId(), sd->status_key.name, operation, HumanTimeDiff()); break; case SP::WEIGHT: sd->weight = val; diff --git a/src/mmo/enums.hpp b/src/mmo/enums.hpp index caecc13..9a8f8ea 100644 --- a/src/mmo/enums.hpp +++ b/src/mmo/enums.hpp @@ -113,7 +113,8 @@ enum class SEX : uint8_t MALE = 1, // For items. This is also used as error, sometime. // TODO switch to Option where appropriate. - NEUTRAL = 2, + UNSPECIFIED = 2, + NEUTRAL = 3, }; inline char sex_to_char(SEX sex) @@ -122,7 +123,8 @@ char sex_to_char(SEX sex) { case SEX::FEMALE: return 'F'; case SEX::MALE: return 'M'; - default: return '\0'; + case SEX::NEUTRAL: return 'N'; + default: return 'S'; } } inline @@ -132,7 +134,8 @@ SEX sex_from_char(char c) { case 'F': return SEX::FEMALE; case 'M': return SEX::MALE; - default: return SEX::NEUTRAL; + case 'N': return SEX::NEUTRAL; + default: return SEX::UNSPECIFIED; } } diff --git a/tools/protocol.py b/tools/protocol.py index f7be6e5..8c3a677 100755 --- a/tools/protocol.py +++ b/tools/protocol.py @@ -1638,7 +1638,7 @@ def build_context(): at(98, stats6, 'stats'), at(104, u8, 'char num'), - at(105, u8, 'unused2'), + at(105, sex, 'sex'), ], size=106, ) @@ -4971,8 +4971,9 @@ def build_context(): fixed=[ at(0, u16, 'packet id'), at(2, account_id, 'account id'), + at(6, sex, 'sex'), ], - fixed_size=6, + fixed_size=7, pre=[0x2b0e], post=[0x2723], desc=''' @@ -5406,7 +5407,7 @@ def build_context(): at(6, sex, 'sex'), ], fixed_size=7, - pre=[0x2723], + pre=[NOTHING], post=[0x00ac, 0x01d7, 0x2b01, 0x3011], xpost=[SCRIPT, 0x0080, 0x0081, 0x0088, 0x0091, 0x00a0, 0x00b0, 0x00b1, 0x00be, 0x00c0, 0x00e9, 0x00ee, 0x00fd, 0x0106, 0x010f, 0x0119, 0x013a, 0x0141, 0x0196, 0x01b1, 0x01d8, 0x01da, 0x2b05, 0x3022], desc=''' -- cgit v1.2.3-70-g09d2