mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-28 13:01:31 +00:00
[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:
parent
8a449b0152
commit
31e5622dad
@ -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; }
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
105
zone/mob.cpp
105
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<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());
|
||||
}
|
||||
}
|
||||
|
||||
29
zone/mob.h
29
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; }
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user