[Opcode] Implement SetFace opcode (#2167)

* Implement SetFace opcode

This implements the opcode that's used to update zone clients when a
client updates their face

* Use SetFace in #feature command

Add check for valid number

This adds the #feature eyes command which isn't in the illusion struct
This commit is contained in:
hg 2022-05-11 19:57:20 -04:00 committed by GitHub
parent 29cdd91ca0
commit d8aa8f7e7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 140 additions and 65 deletions

View File

@ -455,6 +455,7 @@ N(OP_ServerListResponse),
N(OP_SessionReady),
N(OP_SetChatServer),
N(OP_SetChatServer2),
N(OP_SetFace),
N(OP_SetGroupTarget),
N(OP_SetGuildMOTD),
N(OP_SetGuildRank),

View File

@ -2316,9 +2316,12 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint32 drakkin_heritage;
/*011*/ uint32 drakkin_tattoo;
/*015*/ uint32 drakkin_details;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 entity_id;
/*024*/
//there are only 10 faces for barbs changing woad just
//increase the face value by ten so if there were 8 woad
//designs then there would be 80 barb faces

View File

@ -2649,11 +2649,11 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint8 unknown007;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 unknown020;
/*020*/ uint32 entity_id;
/*024*/
};
//there are only 10 faces for barbs changing woad just

View File

@ -2622,11 +2622,11 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint8 unknown007;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 unknown020;
/*020*/ uint32 entity_id;
/*024*/
};
//there are only 10 faces for barbs changing woad just

View File

@ -2136,11 +2136,11 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint8 unknown007;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 unknown020;
/*020*/ uint32 entity_id;
/*024*/
};
//there are only 10 faces for barbs changing woad just

View File

@ -2106,11 +2106,11 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint8 unknown007;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 unknown020;
/*020*/ uint32 entity_id;
/*024*/
};
//there are only 10 faces for barbs changing woad just

View File

@ -1464,6 +1464,30 @@ namespace Titanium
FINISH_ENCODE();
}
ENCODE(OP_SetFace)
{
auto emu = reinterpret_cast<FaceChange_Struct*>((*p)->pBuffer);
EQApplicationPacket outapp(OP_Illusion, sizeof(structs::Illusion_Struct));
auto buf = reinterpret_cast<structs::Illusion_Struct*>(outapp.pBuffer);
buf->spawnid = emu->entity_id;
buf->race = -1; // unchanged
buf->gender = -1; // unchanged
buf->texture = -1; // unchanged
buf->helmtexture = -1; // unchanged
buf->face = emu->face;
buf->hairstyle = emu->hairstyle;
buf->haircolor = emu->haircolor;
buf->beard = emu->beard;
buf->beardcolor = emu->beardcolor;
buf->size = 0.0f; // unchanged
safe_delete(*p); // not using the original packet
dest->QueuePacket(&outapp, ack_req);
}
ENCODE(OP_ShopPlayerSell)
{
ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct);

View File

@ -66,6 +66,7 @@ E(OP_ReadBook)
E(OP_RespondAA)
E(OP_SendCharInfo)
E(OP_SendAATable)
E(OP_SetFace)
E(OP_ShopPlayerSell)
E(OP_SpecialMesg)
E(OP_TaskDescription)

View File

@ -1761,8 +1761,7 @@ struct AdventureRequestResponse_Struct{
struct Illusion_Struct {
/*000*/ uint32 spawnid;
/*004*/ char charname[64];
/*068*/ uint16 race;
/*070*/ char unknown070[2];
/*068*/ int race;
/*072*/ uint8 gender;
/*073*/ uint8 texture;
/*074*/ uint8 helmtexture;

View File

@ -2185,11 +2185,11 @@ struct FaceChange_Struct {
/*004*/ uint8 hairstyle;
/*005*/ uint8 beard;
/*006*/ uint8 face;
/*007*/ uint8 unknown007;
/*007*/ uint8 unused_padding;
/*008*/ uint32 drakkin_heritage;
/*012*/ uint32 drakkin_tattoo;
/*016*/ uint32 drakkin_details;
/*020*/ uint32 unknown020;
/*020*/ uint32 entity_id;
/*024*/
};
//there are only 10 faces for barbs changing woad just

View File

@ -203,6 +203,7 @@ OP_DeleteSpell=0x3358
OP_Surname=0x0423
OP_ClearSurname=0x3fb0
OP_FaceChange=0x5578
OP_SetFace=0x1af3
OP_SenseHeading=0x260a
OP_Action=0x744c
OP_ConsiderCorpse=0x5204

View File

@ -201,6 +201,7 @@ OP_DeleteSpell=0x0736 # C
OP_Surname=0x7547 # C
OP_ClearSurname=0x2edd # C
OP_FaceChange=0x5658 # C
OP_SetFace=0x210a
OP_SenseHeading=0x3887 # C
OP_Action=0x2c27 # C
OP_ConsiderCorpse=0x37a7 # C

View File

@ -198,6 +198,7 @@ OP_DeleteSpell=0x6D7E #Xinu 02/20/09
OP_Surname=0x683E #Xinu 02/21/09
OP_ClearSurname=0x2613
OP_FaceChange=0x482D #Trevius 01/16/09
OP_SetFace=0x49dc
OP_SenseHeading=0x1237 #Trevius 01/16/09
OP_Action=0x5285 #Trevius 01/16/09
OP_ConsiderCorpse=0x4CBB #Xinu 02/20/09

View File

@ -205,6 +205,7 @@ OP_DeleteSpell=0x0698 # C
OP_Surname=0x44ae # C
OP_ClearSurname=0x6705 # C
OP_FaceChange=0x37a7 # C
OP_SetFace=0x6cfa
OP_SenseHeading=0x1b8a # C
OP_Action=0x0f14 # C
OP_ConsiderCorpse=0x0a18 # C

View File

@ -5856,10 +5856,11 @@ void Client::Handle_OP_FaceChange(const EQApplicationPacket *app)
return;
}
// Notify other clients in zone
entity_list.QueueClients(this, app, false);
auto fc = reinterpret_cast<FaceChange_Struct*>(app->pBuffer);
// Notify other clients in zone
SetFaceAppearance(*fc, true);
FaceChange_Struct* fc = (FaceChange_Struct*)app->pBuffer;
m_pp.haircolor = fc->haircolor;
m_pp.beardcolor = fc->beardcolor;
m_pp.eyecolor1 = fc->eyecolor1;
@ -5872,7 +5873,6 @@ void Client::Handle_OP_FaceChange(const EQApplicationPacket *app)
m_pp.drakkin_details = fc->drakkin_details;
Save();
MessageString(Chat::Red, FACE_ACCEPTED);
//Message(Chat::Red, "Facial features updated.");
return;
}

View File

@ -3,7 +3,7 @@
void command_feature(Client *c, const Seperator *sep)
{
int arguments = sep->argnum;
if (arguments < 2) {
if (arguments < 2 || !sep->IsNumber(2)) {
auto feature_save_link = EQ::SayLinkEngine::GenerateQuestSaylink(
"#npcedit featuresave",
false,
@ -13,6 +13,7 @@ void command_feature(Client *c, const Seperator *sep)
c->Message(Chat::White, "Usage: #feature beard [Beard] - Change your or your target's Beard");
c->Message(Chat::White, "Usage: #feature beardcolor [Beard Color] - Change your or your target's Beard Color");
c->Message(Chat::White, "Usage: #feature details [Details] - Change your or your target's Drakkin Details");
c->Message(Chat::White, "Usage: #feature eyes [Eye Color] - Change your or your target's Eyes");
c->Message(Chat::White, "Usage: #feature face [Face] - Change your or your target's Face");
c->Message(Chat::White, "Usage: #feature gender [Gender] - Change your or your target's Gender");
c->Message(Chat::White, "Usage: #feature hair [Hair] - Change your or your target's Hair");
@ -38,6 +39,7 @@ void command_feature(Client *c, const Seperator *sep)
bool is_beard = !strcasecmp(sep->arg[1], "beard");
bool is_beard_color = !strcasecmp(sep->arg[1], "beardcolor");
bool is_details = !strcasecmp(sep->arg[1], "details");
bool is_eyes = !strcasecmp(sep->arg[1], "eyes");
bool is_face = !strcasecmp(sep->arg[1], "face");
bool is_gender = !strcasecmp(sep->arg[1], "gender");
bool is_hair = !strcasecmp(sep->arg[1], "hair");
@ -53,6 +55,7 @@ void command_feature(Client *c, const Seperator *sep)
!is_beard &&
!is_beard_color &&
!is_details &&
!is_eyes &&
!is_face &&
!is_gender &&
!is_hair &&
@ -73,6 +76,7 @@ void command_feature(Client *c, const Seperator *sep)
c->Message(Chat::White, "Usage: #feature beard [Beard] - Change your or your target's Beard");
c->Message(Chat::White, "Usage: #feature beardcolor [Beard Color] - Change your or your target's Beard Color");
c->Message(Chat::White, "Usage: #feature details [Details] - Change your or your target's Drakkin Details");
c->Message(Chat::White, "Usage: #feature eyes [Eye Color] - Change your or your target's Eyes");
c->Message(Chat::White, "Usage: #feature face [Face] - Change your or your target's Face");
c->Message(Chat::White, "Usage: #feature gender [Gender] - Change your or your target's Gender");
c->Message(Chat::White, "Usage: #feature hair [Hair] - Change your or your target's Hair");
@ -93,57 +97,64 @@ void command_feature(Client *c, const Seperator *sep)
return;
}
auto beard = target->GetBeard();
auto beard_color = target->GetBeardColor();
auto details = target->GetDrakkinDetails();
auto face = target->GetLuclinFace();
FaceChange_Struct face{};
face.haircolor = target->GetHairColor();
face.beardcolor = target->GetBeardColor();
face.eyecolor1 = target->GetEyeColor1();
face.eyecolor2 = target->GetEyeColor2();
face.hairstyle = target->GetHairStyle();
face.face = target->GetLuclinFace();
face.beard = target->GetBeard();
face.drakkin_heritage = target->GetDrakkinHeritage();
face.drakkin_tattoo = target->GetDrakkinTattoo();
face.drakkin_details = target->GetDrakkinDetails();
auto gender = target->GetGender();
auto hair = target->GetHairStyle();
auto hair_color = target->GetHairColor();
auto helm_texture = target->GetHelmTexture();
auto heritage = target->GetDrakkinHeritage();
auto race = target->GetModel();
auto size = target->GetSize();
auto tattoo = target->GetDrakkinTattoo();
auto texture = target->GetTexture();
std::string feature_changed;
float value_changed;
float value_changed = 0.0f;
if (is_beard) {
beard = static_cast<uint8>(std::stoul(sep->arg[2]));
face.beard = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Beard";
value_changed = beard;
value_changed = face.beard;
} else if (is_beard_color) {
beard_color = static_cast<uint8>(std::stoul(sep->arg[2]));
face.beardcolor = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Beard Color";
value_changed = beard_color;
value_changed = face.beardcolor;
} else if (is_details) {
if (target->GetRace() != DRAKKIN) {
c->Message(Chat::White, "You must target a Drakkin to use this command.");
return;
}
details = static_cast<uint8>(std::stoul(sep->arg[2]));
face.drakkin_details = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Drakkin Details";
value_changed = details;
value_changed = static_cast<float>(face.drakkin_details);
} else if (is_eyes) {
face.eyecolor1 = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Eyes";
value_changed = face.eyecolor1; // eyecolor2 isn't used
} else if (is_face) {
face = static_cast<uint8>(std::stoul(sep->arg[2]));
face.face = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Face";
value_changed = face;
value_changed = face.face;
} else if (is_gender) {
gender = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Gender";
value_changed = gender;
} else if (is_hair) {
hair = static_cast<uint8>(std::stoul(sep->arg[2]));
face.hairstyle = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Hair";
value_changed = hair;
value_changed = face.hairstyle;
} else if (is_hair_color) {
hair_color = static_cast<uint8>(std::stoul(sep->arg[2]));
face.haircolor = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Hair Color";
value_changed = hair_color;
value_changed = face.haircolor;
} else if (is_helm) {
helm_texture = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Helmet Texture";
@ -154,16 +165,16 @@ void command_feature(Client *c, const Seperator *sep)
return;
}
heritage = static_cast<uint8>(std::stoul(sep->arg[2]));
face.drakkin_heritage = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Drakkin Heritage";
value_changed = heritage;
value_changed = static_cast<float>(face.drakkin_heritage);
} else if (is_race) {
race = static_cast<uint16>(std::stoul(sep->arg[2]));
feature_changed = "Race";
value_changed = race;
} else if (is_size) {
size = std::stof(sep->arg[2]);
if (size < 0 || size > 255) {
c->Message(Chat::White, "Usage: #feature size [Size] - Change your or your target's Size temporarily (Valid values are 0 to 255, decimal increments are allowed.)");
return;
@ -177,33 +188,43 @@ void command_feature(Client *c, const Seperator *sep)
return;
}
tattoo = static_cast<uint8>(std::stoul(sep->arg[2]));
face.drakkin_tattoo = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Drakkin Tattoos";
value_changed = tattoo;
value_changed = static_cast<float>(face.drakkin_tattoo);
} else if (is_texture) {
texture = static_cast<uint8>(std::stoul(sep->arg[2]));
feature_changed = "Texture";
value_changed = texture;
}
target->SendIllusionPacket(
race,
gender,
texture,
helm_texture,
hair_color,
beard_color,
target->GetEyeColor1(),
target->GetEyeColor2(),
hair,
face,
beard,
0xFF,
heritage,
tattoo,
details,
size
);
// For now face number is not set through SetFace. This is because the
// client may not update face features after being set to an invalid face
// until a specific valid face number is re-sent (needs more research)
if (!is_gender && !is_helm && !is_race && !is_size && !is_texture && !is_face)
{
target->SetFaceAppearance(face);
}
else
{
target->SendIllusionPacket(
race,
gender,
texture,
helm_texture,
face.haircolor,
face.beardcolor,
target->GetEyeColor1(),
target->GetEyeColor2(),
face.hairstyle,
face.face,
face.beard,
0xFF,
face.drakkin_heritage,
face.drakkin_tattoo,
face.drakkin_details,
size
);
}
c->Message(
Chat::White,

View File

@ -2606,6 +2606,27 @@ void Mob::SendIllusionPacket(
);
}
void Mob::SetFaceAppearance(const FaceChange_Struct& face, bool skip_sender)
{
haircolor = face.haircolor;
beardcolor = face.beardcolor;
eyecolor1 = face.eyecolor1;
eyecolor2 = face.eyecolor2;
hairstyle = face.hairstyle;
luclinface = face.face;
beard = face.beard;
drakkin_heritage = face.drakkin_heritage;
drakkin_tattoo = face.drakkin_tattoo;
drakkin_details = face.drakkin_details;
EQApplicationPacket outapp(OP_SetFace, sizeof(FaceChange_Struct));
memcpy(outapp.pBuffer, &face, sizeof(FaceChange_Struct));
auto buf = reinterpret_cast<FaceChange_Struct*>(outapp.pBuffer);
buf->entity_id = GetID();
entity_list.QueueClients(this, &outapp, skip_sender);
}
bool Mob::RandomizeFeatures(bool send_illusion, bool set_variables)
{
if (IsPlayerRace(GetRace())) {

View File

@ -834,6 +834,7 @@ public:
uint8 in_hairstyle = 0xFF, uint8 in_luclinface = 0xFF, uint8 in_beard = 0xFF, uint8 in_aa_title = 0xFF,
uint32 in_drakkin_heritage = 0xFFFFFFFF, uint32 in_drakkin_tattoo = 0xFFFFFFFF,
uint32 in_drakkin_details = 0xFFFFFFFF, float in_size = -1.0f, bool send_appearance_effects = true);
void SetFaceAppearance(const FaceChange_Struct& face, bool skip_sender = false);
bool RandomizeFeatures(bool send_illusion = true, bool set_variables = true);
virtual void Stun(int duration);
virtual void UnStun();