301 lines
8.6 KiB
Lua
301 lines
8.6 KiB
Lua
-----------------------------------
|
|
-- Item Utils (Used by Skill Books)
|
|
-----------------------------------
|
|
xi = xi or {}
|
|
---@class xi.itemUtils
|
|
xi.itemUtils = {}
|
|
|
|
---@enum xi.itemUtils.removableEffects
|
|
xi.itemUtils.removableEffects =
|
|
{
|
|
xi.effect.PARALYSIS,
|
|
xi.effect.POISON,
|
|
xi.effect.BLINDNESS,
|
|
xi.effect.SILENCE,
|
|
xi.effect.DISEASE,
|
|
xi.effect.PETRIFICATION,
|
|
xi.effect.BIND,
|
|
xi.effect.WEIGHT,
|
|
xi.effect.ADDLE,
|
|
xi.effect.BURN,
|
|
xi.effect.FROST,
|
|
xi.effect.CHOKE,
|
|
xi.effect.RASP,
|
|
xi.effect.SHOCK,
|
|
xi.effect.DROWN,
|
|
xi.effect.DIA,
|
|
xi.effect.BIO,
|
|
xi.effect.STR_DOWN,
|
|
xi.effect.DEX_DOWN,
|
|
xi.effect.VIT_DOWN,
|
|
xi.effect.AGI_DOWN,
|
|
xi.effect.INT_DOWN,
|
|
xi.effect.MND_DOWN,
|
|
xi.effect.CHR_DOWN,
|
|
xi.effect.MAX_HP_DOWN,
|
|
xi.effect.MAX_MP_DOWN,
|
|
xi.effect.ATTACK_DOWN,
|
|
xi.effect.EVASION_DOWN,
|
|
xi.effect.DEFENSE_DOWN,
|
|
xi.effect.MAGIC_DEF_DOWN,
|
|
xi.effect.INHIBIT_TP,
|
|
xi.effect.MAGIC_ACC_DOWN,
|
|
xi.effect.MAGIC_ATK_DOWN
|
|
}
|
|
|
|
---@nodiscard
|
|
---@param target CBaseEntity
|
|
---@param foodType xi.foodType
|
|
---@return integer
|
|
xi.itemUtils.foodOnItemCheck = function(target, foodType)
|
|
local result = 0
|
|
local targetRace = target:getRace()
|
|
local canEatFish = targetRace == xi.race.MITHRA or target:getMod(xi.mod.EAT_RAW_FISH) == 1
|
|
local canEatMeat = targetRace == xi.race.GALKA or target:getMod(xi.mod.EAT_RAW_MEAT) == 1
|
|
|
|
if
|
|
(foodType == xi.foodType.RAW_FISH and not canEatFish) or
|
|
(foodType == xi.foodType.RAW_MEAT and not canEatMeat)
|
|
then
|
|
result = xi.msg.basic.CANNOT_EAT
|
|
end
|
|
|
|
if target:hasStatusEffect(xi.effect.FOOD) then
|
|
result = xi.msg.basic.IS_FULL
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
---@nodiscard
|
|
---@param target CBaseEntity
|
|
---@return integer
|
|
xi.itemUtils.itemBoxOnItemCheck = function(target)
|
|
local result = 0
|
|
if target:getFreeSlotsCount() == 0 then
|
|
result = xi.msg.basic.ITEM_NO_USE_INVENTORY
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
---@nodiscard
|
|
---@param target CBaseEntity
|
|
---@param skillID integer
|
|
---@return integer
|
|
xi.itemUtils.skillBookCheck = function(target, skillID)
|
|
local skill = skillID
|
|
local mainCap = target:getMaxSkillLevel(target:getMainLvl(), target:getMainJob(), skill) or 0
|
|
local subCap = target:getMaxSkillLevel(target:getSubLvl(), target:getSubJob(), skill) or 0
|
|
local mainDif = (mainCap * 10) / 10 - (target:getCharSkillLevel(skill) * 10) / 100
|
|
local subDif = (subCap * 10) / 10 - (target:getCharSkillLevel(skill) * 10) / 100
|
|
local noSkill = 0
|
|
|
|
if mainCap == 0 then
|
|
noSkill = noSkill + 1
|
|
end
|
|
|
|
if subCap == 0 then
|
|
noSkill = noSkill + 1
|
|
end
|
|
|
|
if noSkill >= 2 then
|
|
return xi.msg.basic.ITEM_UNABLE_TO_USE
|
|
end
|
|
|
|
if mainCap > 0 and mainDif <= 0 then
|
|
return xi.msg.basic.ITEM_UNABLE_TO_USE
|
|
end
|
|
|
|
if subCap > 0 and mainCap == 0 and subDif <= 0 then
|
|
return xi.msg.basic.ITEM_UNABLE_TO_USE
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
---@param target CBaseEntity
|
|
---@param skillID integer
|
|
---@return nil
|
|
xi.itemUtils.skillBookUse = function(target, skillID)
|
|
target:trySkillUp(skillID, target:getMainLvl(), true, true)
|
|
end
|
|
|
|
-- selects an item from a weighted loot table
|
|
---@nodiscard
|
|
---@param lootGroup table
|
|
---@return integer
|
|
xi.itemUtils.pickItemRandom = function(lootGroup)
|
|
local itemId = 0
|
|
-- sum weights
|
|
local max = 0
|
|
for i, entry in pairs(lootGroup) do
|
|
max = max + entry.weight
|
|
|
|
if entry.itemId == nil then
|
|
print(fmt('[ERROR] xi.itemUtils.pickItemRandom has encountered nil item at index {} of lootGroup', i))
|
|
end
|
|
end
|
|
|
|
local roll = math.random(max)
|
|
local current = 0
|
|
|
|
for _, entry in pairs(lootGroup) do
|
|
current = current + entry.weight
|
|
|
|
if current >= roll then
|
|
-- xi.item.NONE gives a chance to drop nothing from a group, don't return nil if the itemId is invalid
|
|
itemId = entry.itemId or 0
|
|
|
|
break
|
|
end
|
|
end
|
|
|
|
return itemId
|
|
end
|
|
|
|
xi.itemUtils.addItemShield = function(target, power, duration, effect, subPower)
|
|
if target:hasStatusEffect(effect) then
|
|
local shield = target:getStatusEffect(effect)
|
|
local activeshieldpower = shield:getPower()
|
|
|
|
if activeshieldpower > power then
|
|
target:messageBasic(xi.msg.basic.NO_EFFECT)
|
|
return
|
|
end
|
|
end
|
|
|
|
target:delStatusEffect(xi.effect.PHYSICAL_SHIELD)
|
|
target:delStatusEffect(xi.effect.MAGIC_SHIELD)
|
|
target:addStatusEffect(effect, { power = power, duration = duration, origin = target, subPower = subPower })
|
|
target:messageBasic(xi.msg.basic.GAINS_EFFECT_OF_STATUS, effect)
|
|
end
|
|
|
|
xi.itemUtils.addItemEffect = function(target, effect, power, duration, subpower)
|
|
if target:hasStatusEffect(effect) then
|
|
local buff = target:getStatusEffect(effect)
|
|
local effectpower = buff:getPower()
|
|
|
|
if effectpower > power then
|
|
target:messageBasic(xi.msg.basic.NO_EFFECT)
|
|
else
|
|
target:addStatusEffect(effect, { power = power, duration = duration, origin = target, subPower = subpower })
|
|
end
|
|
else
|
|
target:addStatusEffect(effect, { power = power, duration = duration, origin = target, subPower = subpower })
|
|
end
|
|
end
|
|
|
|
xi.itemUtils.addTwoItemEffects = function(target, effect1, effect2, power1, power2, duration)
|
|
if target:hasStatusEffect(effect1) then
|
|
local buff = target:getStatusEffect(effect1)
|
|
local effectpower = buff:getPower()
|
|
|
|
if effectpower > power1 then
|
|
target:messageBasic(xi.msg.basic.NO_EFFECT)
|
|
else
|
|
target:addStatusEffect(effect1, { power = power1, duration = duration, origin = target, subPower = power1 })
|
|
end
|
|
else
|
|
target:addStatusEffect(effect1, { power = power1, duration = duration, origin = target, subPower = power1 })
|
|
end
|
|
|
|
if target:hasStatusEffect(effect2) then
|
|
local buff = target:getStatusEffect(effect2)
|
|
local effectpower = buff:getPower()
|
|
|
|
if effectpower > power2 then
|
|
target:messageBasic(xi.msg.basic.NO_EFFECT)
|
|
else
|
|
target:addStatusEffect(effect2, { power = power2, duration = duration, origin = target, subPower = power2 })
|
|
end
|
|
else
|
|
target:addStatusEffect(effect2, { power = power2, duration = duration, origin = target, subPower = power2 })
|
|
end
|
|
end
|
|
|
|
xi.itemUtils.addItemExpEffect = function(target, effect, power, duration, subpower)
|
|
local deleffect = xi.effect.COMMITMENT
|
|
|
|
if effect == deleffect then
|
|
deleffect = xi.effect.DEDICATION
|
|
end
|
|
|
|
if target:hasStatusEffect(effect) then
|
|
local buff = target:getStatusEffect(effect)
|
|
local effectpower = buff:getPower()
|
|
|
|
if effectpower > power then
|
|
target:messageBasic(xi.msg.basic.NO_EFFECT)
|
|
else
|
|
target:delStatusEffectSilent(deleffect)
|
|
target:addStatusEffect(effect, { power = power, duration = duration, origin = target, subPower = subpower })
|
|
end
|
|
else
|
|
target:delStatusEffectSilent(deleffect)
|
|
target:addStatusEffect(effect, { power = power, duration = duration, origin = target, subPower = subpower })
|
|
end
|
|
end
|
|
|
|
xi.itemUtils.removeStatus = function(target, effects)
|
|
for _, effect in ipairs(effects) do
|
|
if target:delStatusEffect(effect) then
|
|
return true
|
|
end
|
|
end
|
|
|
|
if target:eraseStatusEffect() ~= 255 then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
xi.itemUtils.removeMultipleEffects = function(target, effects, count, random)
|
|
local effectsToRemove = effects
|
|
|
|
if random == 1 then -- randomize which effects get removed
|
|
effectsToRemove = utils.shuffle(effects)
|
|
end
|
|
|
|
if count > 0 then
|
|
local removed = 0
|
|
|
|
for i = 0, count do
|
|
if not xi.itemUtils.removeStatus(target, effectsToRemove) then
|
|
break
|
|
end
|
|
|
|
removed = removed + 1
|
|
|
|
if removed >= count then
|
|
break
|
|
end
|
|
end
|
|
|
|
return removed
|
|
end
|
|
end
|
|
|
|
-- for applying pet mods based on arbitrary conditions
|
|
-- I.E. avatar attack only for a particular pet
|
|
-- example usage in fervor_ring.lua
|
|
-- can pass nil for petId to apply to all pets
|
|
xi.itemUtils.handlePetLatentMods = function(owner, petId, mods, give)
|
|
local pet = owner:getPet()
|
|
|
|
if
|
|
pet and
|
|
(not petId or pet:getPetID() == petId)
|
|
then
|
|
if give then
|
|
for _, mod in ipairs(mods) do
|
|
pet:addMod(mod[1], mod[2])
|
|
end
|
|
else
|
|
for _, mod in ipairs(mods) do
|
|
pet:delMod(mod[1], mod[2])
|
|
end
|
|
end
|
|
end
|
|
end
|