[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
This commit is contained in:
Kinglykrab 2022-11-14 14:05:05 -05:00 committed by GitHub
parent 8a449b0152
commit 31e5622dad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 196 additions and 27 deletions

View File

@ -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; }

View File

@ -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_<Lua_Mob, Lua_Entity>("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)

View File

@ -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

View File

@ -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<uint8, uint8> 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());
}
}

View File

@ -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; }

View File

@ -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<uint8>(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

View File

@ -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);