From 31e5622dad2364660b2121bd2f152dd0acae08eb Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 14 Nov 2022 14:05:05 -0500 Subject: [PATCH] [Quest API] Add CloneAppearance() to Perl/Lua. (#2531) * [Quest API] Add CloneAppearance() to Perl/Lua. # Perl - Add `$client->CloneAppearance(other)` to Perl. - Add `$client->CloneAppearance(other, clone_name)` to Perl. # Lua - Add `client:CloneAppearance(other)` to Lua. - Add `client:CloneAppearance(other, clone_name)` to Lua. # Notes - Allows operators to easily clone appearance between mobs in a script without relying on a plugin or module. * Update mob_appearance.cpp * Update mob.cpp --- common/item_instance.h | 2 + zone/lua_mob.cpp | 12 +++++ zone/lua_mob.h | 2 + zone/mob.cpp | 105 ++++++++++++++++++++++++++++++++++++++++ zone/mob.h | 29 ++++++++--- zone/mob_appearance.cpp | 61 +++++++++++++++-------- zone/perl_mob.cpp | 12 +++++ 7 files changed, 196 insertions(+), 27 deletions(-) diff --git a/common/item_instance.h b/common/item_instance.h index e3e33e7c2..f71e4f63c 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -148,6 +148,8 @@ namespace EQ const ItemData* GetItem() const; const ItemData* GetUnscaledItem() const; + const uint8 GetItemType() const { return m_item ? m_item->ItemType : 255; } // Return 255 so you know there's no valid item + int16 GetCharges() const { return m_charges; } void SetCharges(int16 charges) { m_charges = charges; } diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 71a81edea..ac0352f10 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -2478,6 +2478,16 @@ Lua_Mob Lua_Mob::GetUltimateOwner() { return Lua_Mob(self->GetUltimateOwner()); } +void Lua_Mob::CloneAppearance(Lua_Mob other) { + Lua_Safe_Call_Void(); + self->CloneAppearance(other); +} + +void Lua_Mob::CloneAppearance(Lua_Mob other, bool clone_name) { + Lua_Safe_Call_Void(); + self->CloneAppearance(other, clone_name); +} + luabind::scope lua_register_mob() { return luabind::class_("Mob") .def(luabind::constructor<>()) @@ -2557,6 +2567,8 @@ luabind::scope lua_register_mob() { .def("CheckLoSToLoc", (bool(Lua_Mob::*)(double,double,double,double))&Lua_Mob::CheckLoSToLoc) .def("CheckNumHitsRemaining", &Lua_Mob::CheckNumHitsRemaining) .def("ClearSpecialAbilities", (void(Lua_Mob::*)(void))&Lua_Mob::ClearSpecialAbilities) + .def("CloneAppearance", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CloneAppearance) + .def("CloneAppearance", (void(Lua_Mob::*)(Lua_Mob,bool))&Lua_Mob::CloneAppearance) .def("CombatRange", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::CombatRange) .def("Damage", (void(Lua_Mob::*)(Lua_Mob,int64,int,int))&Lua_Mob::Damage) .def("Damage", (void(Lua_Mob::*)(Lua_Mob,int64,int,int,bool))&Lua_Mob::Damage) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 8f24d0b9b..445e42578 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -463,6 +463,8 @@ public: int GetBuffStatValueBySpell(int spell_id, const char* identifier); void SetBuffDuration(int spell_id); void SetBuffDuration(int spell_id, int duration); + void CloneAppearance(Lua_Mob other); + void CloneAppearance(Lua_Mob other, bool clone_name); }; #endif diff --git a/zone/mob.cpp b/zone/mob.cpp index 8614d30e2..fd003e760 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -6899,3 +6899,108 @@ std::string Mob::GetMobDescription() GetID() ); } + +uint8 Mob::ConvertItemTypeToSkillID(uint8 item_type) +{ + if (item_type >= EQ::item::ItemTypeCount) { + return EQ::skills::SkillHandtoHand; + } + + std::map convert_item_types_map = { + { EQ::item::ItemType1HSlash, EQ::skills::Skill1HSlashing }, + { EQ::item::ItemType2HSlash, EQ::skills::Skill2HSlashing }, + { EQ::item::ItemType1HPiercing, EQ::skills::Skill1HPiercing }, + { EQ::item::ItemType2HPiercing, EQ::skills::Skill2HPiercing }, + { EQ::item::ItemType1HBlunt, EQ::skills::Skill1HBlunt }, + { EQ::item::ItemType2HBlunt, EQ::skills::Skill2HBlunt }, + { EQ::item::ItemTypeBow, EQ::skills::SkillArchery }, + { EQ::item::ItemTypeSmallThrowing, EQ::skills::SkillThrowing }, + { EQ::item::ItemTypeLargeThrowing, EQ::skills::SkillThrowing }, + { EQ::item::ItemTypeShield, EQ::skills::SkillBash }, + { EQ::item::ItemTypeArmor, EQ::skills::SkillHandtoHand }, + { EQ::item::ItemTypeMartial, EQ::skills::SkillHandtoHand } + }; + + const auto& s = convert_item_types_map.find(item_type); + if (s != convert_item_types_map.end()) { + return s->second; + } + + return EQ::skills::SkillHandtoHand; +} + +void Mob::CloneAppearance(Mob* other, bool clone_name) +{ + if (!other) { + return; + } + + SendIllusionPacket( + other->GetRace(), + other->GetGender(), + other->GetTexture(), + other->GetHelmTexture(), + other->GetHairColor(), + other->GetBeardColor(), + other->GetEyeColor1(), + other->GetEyeColor2(), + other->GetHairStyle(), + other->GetBeard(), + 0xFF, + other->GetRace() == DRAKKIN ? other->GetDrakkinHeritage() : 0xFFFFFFFF, + other->GetRace() == DRAKKIN ? other->GetDrakkinTattoo() : 0xFFFFFFFF, + other->GetRace() == DRAKKIN ? other->GetDrakkinDetails() : 0xFFFFFFFF, + other->GetSize() + ); + + for ( + uint8 slot = EQ::textures::armorHead; + slot <= EQ::textures::armorFeet; + slot++ + ) { + auto color = 0; + auto material = 0; + if (other->IsClient()) { + color = other->CastToClient()->GetEquipmentColor(slot); + material = other->CastToClient()->GetEquipmentMaterial(slot); + } else { + color = other->GetArmorTint(slot); + material = !slot ? other->GetHelmTexture() : other->GetTexture(); + } + + WearChange(slot, material, color); + } + + WearChange( + EQ::textures::weaponPrimary, + other->GetEquipmentMaterial(EQ::textures::weaponPrimary), + other->GetEquipmentColor(EQ::textures::weaponPrimary) + ); + + WearChange( + EQ::textures::weaponSecondary, + other->GetEquipmentMaterial(EQ::textures::weaponSecondary), + other->GetEquipmentColor(EQ::textures::weaponSecondary) + ); + + if (IsNPC()) { + auto primary_skill = ( + other->IsNPC() ? + other->CastToNPC()->GetPrimSkill() : + ConvertItemTypeToSkillID(other->GetEquipmentType(EQ::textures::weaponSecondary)) + ); + + auto secondary_skill = ( + other->IsNPC() ? + other->CastToNPC()->GetSecSkill() : + ConvertItemTypeToSkillID(other->GetEquipmentType(EQ::textures::weaponSecondary)) + ); + + CastToNPC()->SetPrimSkill(primary_skill); + CastToNPC()->SetSecSkill(secondary_skill); + } + + if (clone_name) { + TempName(other->GetCleanName()); + } +} diff --git a/zone/mob.h b/zone/mob.h index ddffa38d8..e92fa4936 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -489,9 +489,11 @@ public: inline void SetDuelWeaponsEquiped(bool val) { has_duelweaponsequiped = val; } bool CanFacestab() { return can_facestab; } void SetFacestab(bool val) { can_facestab = val; } + virtual uint8 ConvertItemTypeToSkillID(uint8 item_type); virtual uint16 GetSkill(EQ::skills::SkillType skill_num) const { return 0; } virtual uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const { return(0); } virtual int32 GetEquipmentMaterial(uint8 material_slot) const; + virtual uint8 GetEquipmentType(uint8 material_slot) const; virtual int32 GetHerosForgeModel(uint8 material_slot) const; virtual uint32 GetEquipmentColor(uint8 material_slot) const; virtual uint32 IsEliteMaterialItem(uint8 material_slot) const; @@ -840,11 +842,26 @@ public: int64 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false, uint16 casterid = 0, Mob *caster = nullptr); uint8 IsFocusEffect(uint16 spellid, int effect_index, bool AA=false,uint32 aa_effect=0); - void SendIllusionPacket(uint16 in_race, uint8 in_gender = 0xFF, uint8 in_texture = 0xFF, uint8 in_helmtexture = 0xFF, - uint8 in_haircolor = 0xFF, uint8 in_beardcolor = 0xFF, uint8 in_eyecolor1 = 0xFF, uint8 in_eyecolor2 = 0xFF, - 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 SendIllusionPacket( + uint16 in_race, + uint8 in_gender = 0xFF, + uint8 in_texture = 0xFF, + uint8 in_helmtexture = 0xFF, + uint8 in_haircolor = 0xFF, + uint8 in_beardcolor = 0xFF, + uint8 in_eyecolor1 = 0xFF, + uint8 in_eyecolor2 = 0xFF, + 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 CloneAppearance(Mob* other, bool clone_name = false); 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); @@ -1476,7 +1493,7 @@ protected: bool no_target_hotkey; bool rare_spawn; int32 heroic_strikethrough; - + uint32 m_PlayerState; uint32 GetPlayerState() { return m_PlayerState; } void AddPlayerState(uint32 new_state) { m_PlayerState |= new_state; } diff --git a/zone/mob_appearance.cpp b/zone/mob_appearance.cpp index 7cff37de4..582c16711 100644 --- a/zone/mob_appearance.cpp +++ b/zone/mob_appearance.cpp @@ -18,8 +18,9 @@ * */ +#include "../common/data_verification.h" #include "../common/eqemu_logsys.h" - +#include "../common/item_data.h" #include "../common/misc_functions.h" #include "../common/spdat.h" #include "../common/strings.h" @@ -225,39 +226,31 @@ int32 Mob::GetEquipmentMaterial(uint8 material_slot) const auto item = database.GetItem(GetEquippedItemFromTextureSlot(material_slot)); - if (item != nullptr) { + if (item) { + const auto is_equipped_weapon = EQ::ValueWithin(material_slot, EQ::textures::weaponPrimary, EQ::textures::weaponSecondary); - /** - * Handle primary / secondary texture - */ - bool is_primary_or_secondary_weapon = - material_slot == EQ::textures::weaponPrimary || - material_slot == EQ::textures::weaponSecondary; - - if (is_primary_or_secondary_weapon) { + if (is_equipped_weapon) { if (IsClient()) { - - int16 inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(material_slot); + const auto inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(material_slot); if (inventory_slot == INVALID_INDEX) { return 0; } - const EQ::ItemInstance *item_instance = CastToClient()->m_inv[inventory_slot]; - if (item_instance) { - if (item_instance->GetOrnamentationAug(ornamentation_augment_type)) { - item = item_instance->GetOrnamentationAug(ornamentation_augment_type)->GetItem(); + const auto* inst = CastToClient()->m_inv[inventory_slot]; + if (inst) { + if (inst->GetOrnamentationAug(ornamentation_augment_type)) { + item = inst->GetOrnamentationAug(ornamentation_augment_type)->GetItem(); if (item && strlen(item->IDFile) > 2) { - equipment_material = atoi(&item->IDFile[2]); + equipment_material = std::stoi(&item->IDFile[2]); } - } - else if (item_instance->GetOrnamentationIDFile()) { - equipment_material = item_instance->GetOrnamentationIDFile(); + } else if (inst->GetOrnamentationIDFile()) { + equipment_material = inst->GetOrnamentationIDFile(); } } } if (equipment_material == 0 && strlen(item->IDFile) > 2) { - equipment_material = atoi(&item->IDFile[2]); + equipment_material = std::stoi(&item->IDFile[2]); } } else { @@ -268,6 +261,32 @@ int32 Mob::GetEquipmentMaterial(uint8 material_slot) const return equipment_material; } +uint8 Mob::GetEquipmentType(uint8 material_slot) const +{ + auto item_type = static_cast(EQ::item::ItemType2HBlunt); + auto item = database.GetItem(GetEquippedItemFromTextureSlot(material_slot)); + + if (item) { + const auto is_equipped_weapon = EQ::ValueWithin(material_slot, EQ::textures::weaponPrimary, EQ::textures::weaponSecondary); + + if (is_equipped_weapon) { + if (IsClient()) { + const auto inventory_slot = EQ::InventoryProfile::CalcSlotFromMaterial(material_slot); + if (inventory_slot == INVALID_INDEX) { + return item_type; + } + + const auto* inst = CastToClient()->m_inv[inventory_slot]; + if (inst) { + item_type = inst->GetItemType(); + } + } + } + } + + return item_type; +} + /** * @param material_slot * @return diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index f6f27d44b..724e6a133 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -2457,6 +2457,16 @@ Mob* Perl_Mob_GetUltimateOwner(Mob* self) // @categories Script Utility, Pet return self->GetUltimateOwner(); } +void Perl_Mob_CloneAppearance(Mob* self, Mob* other) // @categories Script Utility +{ + self->CloneAppearance(other); +} + +void Perl_Mob_CloneAppearance(Mob* self, Mob* other, bool clone_name) // @categories Script Utility +{ + self->CloneAppearance(other, clone_name); +} + #ifdef BOTS Bot* Perl_Mob_CastToBot(Mob* self) { @@ -2537,6 +2547,8 @@ void perl_register_mob() package.add("CheckLoSToLoc", (bool(*)(Mob*, float, float, float, float))&Perl_Mob_CheckLoSToLoc); package.add("ClearFeignMemory", &Perl_Mob_ClearFeignMemory); package.add("ClearSpecialAbilities", &Perl_Mob_ClearSpecialAbilities); + package.add("CloneAppearance", (void(*)(Mob*, Mob*))&Perl_Mob_CloneAppearance); + package.add("CloneAppearance", (void(*)(Mob*, Mob*, bool))&Perl_Mob_CloneAppearance); package.add("CombatRange", &Perl_Mob_CombatRange); package.add("Damage", (void(*)(Mob*, Mob*, int64, uint16_t, int))&Perl_Mob_Damage); package.add("Damage", (void(*)(Mob*, Mob*, int64, uint16_t, int, bool))&Perl_Mob_Damage);