本帖最后由 angelsclan 于 2024-4-7 22:05 编辑
注意:此脚本需要具有module制作和相应的编译能力
此装备升级系统针对所有的装备都可以升级,材料自定义,无需复制创建装备,升级后的装备ID不变,涉及到的两张表已经包含在附件里面
此功能需要自己写一个module应用升级后的装备属性,代码如下
void OnAfterApplyItemMods(Player* player, Item* item, uint8 slot, bool apply) override
{
//LOG_INFO("entities.player", "player {} OnAfterApplyItemMods【{}】 slot:{} update:{}", player->GetName(), item->GetGUID().GetCounter(), slot, apply ? 'ON' : 'OFF');
//查询装备升级记录
QueryResult result = CharacterDatabase.Query("SELECT instance_guid, upgrade_lvl, owner_guid, increase_ratio FROM _item_upgrade WHERE instance_guid = '{}'", item->GetGUID().GetCounter());
if (result) {
//LOG_INFO("entities.player", "player {} found upgrade【{}】 slot:{} update:{}", player->GetName(), item->GetGUID().GetCounter(), slot, apply ? 'ON' : 'OFF');
ItemTemplate itemInstance = GetUpgradeItemInstance(item, result);
ItemTemplate const* proto = &itemInstance;
/*
std::string name = proto->Name1;
if (ItemLocale const* il = sObjectMgr->GetItemLocale(item->GetEntry()))
{
ObjectMgr::GetLocaleString(il->Name, static_cast<LocaleConstant>(LOCALE_zhCN), name);
}
*/
ApplyUpgradeItemBonuses(player, proto, slot, apply, false);
}
// 处理附魔技能效果
for (uint32 eslot = 0; eslot < MAX_ENCHANTMENT_SLOT; ++eslot){
EnchantmentSlot slotId = EnchantmentSlot(eslot);
uint32 enchant_id = item->GetEnchantmentId(slotId);
if (!enchant_id)
continue;
SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
if (!pEnchant)
continue;
for (int s = 0; s < MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS; ++s)
{
uint32 enchant_display_type = pEnchant->type[s];
uint32 enchant_amount = pEnchant->amount[s];
uint32 enchant_spell_id = pEnchant->spellid[s];
if (enchant_display_type == ITEM_ENCHANTMENT_TYPE_EQUIP_SPELL) {
if (enchant_spell_id)
{
if (apply)
{
// 学习泰坦之握技能
if ((enchant_spell_id == 97001) and !player->HasSpell(enchant_spell_id))
{
player->learnSpell(enchant_spell_id);
}
}else{
// 取消泰坦之握效果
if (enchant_spell_id == 97001 && player->HasSpell(enchant_spell_id))
{
player->removeSpell(enchant_spell_id, player->GetActiveSpecMask(), false);
if (!player->HasTalent(46917, player->GetActiveSpec())){
player->SetCanTitanGrip(false);
//player->AutoUnequipOffhandIfNeed();
}
}
}
}
}
}
}
}
void ApplyUpgradeItemBonuses(Player* player, ItemTemplate const* proto, uint8 slot, bool apply, bool only_level_scale /*= false*/)
{
if (slot >= INVENTORY_SLOT_BAG_END || !proto)
return;
ScalingStatDistributionEntry const* ssd = proto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(proto->ScalingStatDistribution) : nullptr;
if (only_level_scale && !ssd)
return;
// req. check at equip, but allow use for extended range if range limit max level, set proper level
uint32 ssd_level = player->GetLevel();
uint32 CustomScalingStatValue = 0;
sScriptMgr->OnCustomScalingStatValueBefore(player, proto, slot, apply, CustomScalingStatValue);
uint32 ScalingStatValue = proto->ScalingStatValue > 0 ? proto->ScalingStatValue : CustomScalingStatValue;
if (ssd && ssd_level > ssd->MaxLevel)
ssd_level = ssd->MaxLevel;
ScalingStatValuesEntry const* ssv = proto->ScalingStatValue ? sScalingStatValuesStore.LookupEntry(ssd_level) : nullptr;
if (only_level_scale && !ssv)
return;
for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
{
uint32 statType = 0;
int32 val = 0;
// If set ScalingStatDistribution need get stats and values from it
if (ssv)
{
if (ssd)
{
if (ssd->StatMod[i] < 0)
continue;
statType = ssd->StatMod[i];
val = (ssv->getssdMultiplier(ScalingStatValue) * ssd->Modifier[i]) / 10000;
}
else
{
if (i >= proto->StatsCount)
continue;
// OnCustomScalingStatValue(Player* player, ItemTemplate const* proto, uint32& statType, int32& val, uint8 itemProtoStatNumber, uint32 ScalingStatValue, ScalingStatValuesEntry const* ssv)
sScriptMgr->OnCustomScalingStatValue(player, proto, statType, val, i, ScalingStatValue, ssv);
}
}
else
{
if (i >= proto->StatsCount)
continue;
statType = proto->ItemStat[i].ItemStatType;
val = proto->ItemStat[i].ItemStatValue;
}
if (val == 0)
continue;
switch (statType)
{
case ITEM_MOD_MANA:
player->HandleStatModifier(UNIT_MOD_MANA, BASE_VALUE, float(val), apply);
break;
case ITEM_MOD_HEALTH: // modify HP
player->HandleStatModifier(UNIT_MOD_HEALTH, BASE_VALUE, float(val), apply);
break;
case ITEM_MOD_AGILITY: // modify agility
player->HandleStatModifier(UNIT_MOD_STAT_AGILITY, BASE_VALUE, float(val), apply);
player->ApplyStatBuffMod(STAT_AGILITY, float(val), apply);
break;
case ITEM_MOD_STRENGTH: //modify strength
player->HandleStatModifier(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, float(val), apply);
player->ApplyStatBuffMod(STAT_STRENGTH, float(val), apply);
break;
case ITEM_MOD_INTELLECT: //modify intellect
player->HandleStatModifier(UNIT_MOD_STAT_INTELLECT, BASE_VALUE, float(val), apply);
player->ApplyStatBuffMod(STAT_INTELLECT, float(val), apply);
break;
case ITEM_MOD_SPIRIT: //modify spirit
player->HandleStatModifier(UNIT_MOD_STAT_SPIRIT, BASE_VALUE, float(val), apply);
player->ApplyStatBuffMod(STAT_SPIRIT, float(val), apply);
break;
case ITEM_MOD_STAMINA: //modify stamina
player->HandleStatModifier(UNIT_MOD_STAT_STAMINA, BASE_VALUE, float(val), apply);
player->ApplyStatBuffMod(STAT_STAMINA, float(val), apply);
break;
case ITEM_MOD_DEFENSE_SKILL_RATING:
player->ApplyRatingMod(CR_DEFENSE_SKILL, int32(val), apply);
break;
case ITEM_MOD_DODGE_RATING:
player->ApplyRatingMod(CR_DODGE, int32(val), apply);
break;
case ITEM_MOD_PARRY_RATING:
player->ApplyRatingMod(CR_PARRY, int32(val), apply);
break;
case ITEM_MOD_BLOCK_RATING:
player->ApplyRatingMod(CR_BLOCK, int32(val), apply);
break;
case ITEM_MOD_HIT_MELEE_RATING:
player->ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
break;
case ITEM_MOD_HIT_RANGED_RATING:
player->ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
break;
case ITEM_MOD_HIT_SPELL_RATING:
player->ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
break;
case ITEM_MOD_CRIT_MELEE_RATING:
player->ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
break;
case ITEM_MOD_CRIT_RANGED_RATING:
player->ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
break;
case ITEM_MOD_CRIT_SPELL_RATING:
player->ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
break;
case ITEM_MOD_HIT_TAKEN_MELEE_RATING:
player->ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
break;
case ITEM_MOD_HIT_TAKEN_RANGED_RATING:
player->ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
break;
case ITEM_MOD_HIT_TAKEN_SPELL_RATING:
player->ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
break;
case ITEM_MOD_CRIT_TAKEN_MELEE_RATING:
player->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
break;
case ITEM_MOD_CRIT_TAKEN_RANGED_RATING:
player->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
break;
case ITEM_MOD_CRIT_TAKEN_SPELL_RATING:
player->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
break;
case ITEM_MOD_HASTE_MELEE_RATING:
player->ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
break;
case ITEM_MOD_HASTE_RANGED_RATING:
player->ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
break;
case ITEM_MOD_HASTE_SPELL_RATING:
player->ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
break;
case ITEM_MOD_HIT_RATING:
player->ApplyRatingMod(CR_HIT_MELEE, int32(val), apply);
player->ApplyRatingMod(CR_HIT_RANGED, int32(val), apply);
player->ApplyRatingMod(CR_HIT_SPELL, int32(val), apply);
break;
case ITEM_MOD_CRIT_RATING:
player->ApplyRatingMod(CR_CRIT_MELEE, int32(val), apply);
player->ApplyRatingMod(CR_CRIT_RANGED, int32(val), apply);
player->ApplyRatingMod(CR_CRIT_SPELL, int32(val), apply);
break;
case ITEM_MOD_HIT_TAKEN_RATING:
player->ApplyRatingMod(CR_HIT_TAKEN_MELEE, int32(val), apply);
player->ApplyRatingMod(CR_HIT_TAKEN_RANGED, int32(val), apply);
player->ApplyRatingMod(CR_HIT_TAKEN_SPELL, int32(val), apply);
break;
case ITEM_MOD_CRIT_TAKEN_RATING:
case ITEM_MOD_RESILIENCE_RATING:
player->ApplyRatingMod(CR_CRIT_TAKEN_MELEE, int32(val), apply);
player->ApplyRatingMod(CR_CRIT_TAKEN_RANGED, int32(val), apply);
player->ApplyRatingMod(CR_CRIT_TAKEN_SPELL, int32(val), apply);
break;
case ITEM_MOD_HASTE_RATING:
player->ApplyRatingMod(CR_HASTE_MELEE, int32(val), apply);
player->ApplyRatingMod(CR_HASTE_RANGED, int32(val), apply);
player->ApplyRatingMod(CR_HASTE_SPELL, int32(val), apply);
break;
case ITEM_MOD_EXPERTISE_RATING:
player->ApplyRatingMod(CR_EXPERTISE, int32(val), apply);
break;
case ITEM_MOD_ATTACK_POWER:
player->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(val), apply);
player->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply);
break;
case ITEM_MOD_RANGED_ATTACK_POWER:
player->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(val), apply);
break;
// case ITEM_MOD_FERAL_ATTACK_POWER:
// ApplyFeralAPBonus(int32(val), apply);
// break;
case ITEM_MOD_MANA_REGENERATION:
player->ApplyManaRegenBonus(int32(val), apply);
break;
case ITEM_MOD_ARMOR_PENETRATION_RATING:
player->ApplyRatingMod(CR_ARMOR_PENETRATION, int32(val), apply);
break;
case ITEM_MOD_SPELL_POWER:
player->ApplySpellPowerBonus(int32(val), apply);
break;
case ITEM_MOD_HEALTH_REGEN:
player->ApplyHealthRegenBonus(int32(val), apply);
break;
case ITEM_MOD_SPELL_PENETRATION:
player->ApplySpellPenetrationBonus(val, apply);
break;
case ITEM_MOD_BLOCK_VALUE:
player->HandleBaseModValue(SHIELD_BLOCK_VALUE, FLAT_MOD, float(val), apply);
break;
/// @deprecated item mods
case ITEM_MOD_SPELL_HEALING_DONE:
case ITEM_MOD_SPELL_DAMAGE_DONE:
break;
}
}
// 法术强度
if (ssv)
if (int32 spellbonus = ssv->getSpellBonus(ScalingStatValue))
player->ApplySpellPowerBonus(spellbonus, apply);
// If set ScalingStatValue armor get it or use item armor
uint32 armor = proto->Armor;
if (armor)
{
UnitModifierType modType = TOTAL_VALUE;
if (proto->Class == ITEM_CLASS_ARMOR)
{
switch (proto->SubClass)
{
case ITEM_SUBCLASS_ARMOR_CLOTH:
case ITEM_SUBCLASS_ARMOR_LEATHER:
case ITEM_SUBCLASS_ARMOR_MAIL:
case ITEM_SUBCLASS_ARMOR_PLATE:
case ITEM_SUBCLASS_ARMOR_SHIELD:
modType = BASE_VALUE;
break;
}
}
player->HandleStatModifier(UNIT_MOD_ARMOR, modType, float(armor), apply);
}
uint8 attType = Player::GetAttackBySlot(slot);
if (attType != MAX_ATTACK)
{
switch (attType)
{
case BASE_ATTACK:
default:
player->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, BASE_VALUE, float((proto->Damage->DamageMax + proto->Damage->DamageMin) / 2), apply);
break;
case OFF_ATTACK:
player->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, BASE_VALUE, float((proto->Damage->DamageMax + proto->Damage->DamageMin) / 2), apply);
break;
case RANGED_ATTACK:
player->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, BASE_VALUE, float((proto->Damage->DamageMax + proto->Damage->DamageMin) / 2), apply);
break;
}
//player->_ApplyWeaponDamage(slot, proto, nullptr, apply);
}
// Druids get feral AP bonus from weapon dps (also use DPS from ScalingStatValue)
if (player->getClass() == CLASS_DRUID)
{
int32 dpsMod = 0;
int32 feral_bonus = 0;
if (ssv)
{
dpsMod = ssv->getDPSMod(ScalingStatValue);
feral_bonus += ssv->getFeralBonus(ScalingStatValue);
}
feral_bonus += proto->getFeralBonus(dpsMod);
sScriptMgr->OnGetFeralApBonus(player, feral_bonus, dpsMod, proto, ssv);
if (feral_bonus)
player->ApplyFeralAPBonus(feral_bonus, apply);
}
} 复制代码
ItemTemplate GetUpgradeItemInstance(Item* item, QueryResult result)
{
ItemTemplate const* itemTemplate = item->GetTemplate();
ItemTemplate itemInstance = *itemTemplate;
//ItemTemplate temp = LoadUpgradeItem(item);
//ItemTemplate const* proto = &temp;
std::string name = itemTemplate->Name1;
if (ItemLocale const* il = sObjectMgr->GetItemLocale(item->GetEntry()))
{
ObjectMgr::GetLocaleString(il->Name, static_cast<LocaleConstant>(LOCALE_zhCN), name);
}
// 初始化属性
itemInstance.Damage->DamageMin = 0;
itemInstance.Damage->DamageMax = 0;
itemInstance.Armor = 0;
for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i) {
itemInstance.ItemStat[i].ItemStatValue = 0;
}
for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) {
itemInstance.Damage[i].DamageMin = 0;
itemInstance.Damage[i].DamageMax = 0;
}
Field* fields = result->Fetch();
int32 incRatio = fields[3].Get<uint32>();
if (incRatio > 0)
{
// 基础伤害
int32 minDamage = itemTemplate->Damage->DamageMin;
int32 maxDamage = itemTemplate->Damage->DamageMax;
if (minDamage > 0 && maxDamage > 0)
{
itemInstance.Damage->DamageMin = std::ceil((minDamage / static_cast<float>(100)) * incRatio);
itemInstance.Damage->DamageMax = std::ceil((maxDamage / static_cast<float>(100)) * incRatio);
LOG_DEBUG("entities.player", ">>{} 伤害{} => {} - {} => {}", name, minDamage, itemInstance.Damage->DamageMin, maxDamage, itemInstance.Damage->DamageMax);
}
// 属性伤害
/**
for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) {
float minDamage = itemTemplate->Damage[i].DamageMin;
float maxDamage = itemTemplate->Damage[i].DamageMax;
itemInstance.Damage[i].DamageMin = std::ceil((minDamage / static_cast<float>(100)) * incRatio);
itemInstance.Damage[i].DamageMax = std::ceil((maxDamage / static_cast<float>(100)) * incRatio);
}
*/
// 护甲提升
int32 armor = itemTemplate->Armor;
if (armor > 0)
{
itemInstance.Armor = std::ceil((armor / static_cast<float>(100)) * incRatio);
LOG_DEBUG("entities.player", ">>{} 护甲{} => {}", name, itemTemplate->Armor, itemInstance.Armor);
}
// 属性提升
for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
{
int32 val = 0;
if (i >= itemTemplate->StatsCount)
continue;
val = itemTemplate->ItemStat[i].ItemStatValue;
if (val == 0)
continue;
itemInstance.ItemStat[i].ItemStatValue = std::ceil((val / static_cast<float>(100)) * incRatio);
LOG_DEBUG("entities.player", ">>{} 属性:{} {} => {}", name, itemTemplate->ItemStat[i].ItemStatType, val, itemInstance.ItemStat[i].ItemStatValue);
}
}
itemInstance.Delay = 0;
return itemInstance;
} 复制代码