mirror of
https://github.com/otland/forgottenserver
synced 2026-08-15 16:32:07 -04:00
782 lines
21 KiB
Lua
782 lines
21 KiB
Lua
function Item.getType(self)
|
|
return ItemType(self:getId())
|
|
end
|
|
|
|
function Item:getClassification()
|
|
return self:getType():getClassification()
|
|
end
|
|
|
|
function Item.isContainer(self)
|
|
return false
|
|
end
|
|
|
|
function Item.isCreature(self)
|
|
return false
|
|
end
|
|
|
|
function Item.isMonster(self)
|
|
return false
|
|
end
|
|
|
|
function Item.isNpc(self)
|
|
return false
|
|
end
|
|
|
|
function Item.isPlayer(self)
|
|
return false
|
|
end
|
|
|
|
function Item.isTeleport(self)
|
|
return false
|
|
end
|
|
|
|
function Item.isPodium(self)
|
|
return false
|
|
end
|
|
|
|
function Item.isTile(self)
|
|
return false
|
|
end
|
|
|
|
function Item:isItemType()
|
|
return false
|
|
end
|
|
|
|
do
|
|
local aux = {
|
|
["Defense"] = {key = ITEM_ATTRIBUTE_DEFENSE},
|
|
["ExtraDefense"] = {key = ITEM_ATTRIBUTE_EXTRADEFENSE},
|
|
["Attack"] = {key = ITEM_ATTRIBUTE_ATTACK},
|
|
["AttackSpeed"] = {key = ITEM_ATTRIBUTE_ATTACK_SPEED},
|
|
["HitChance"] = {key = ITEM_ATTRIBUTE_HITCHANCE},
|
|
["ShootRange"] = {key = ITEM_ATTRIBUTE_SHOOTRANGE},
|
|
["Armor"] = {key = ITEM_ATTRIBUTE_ARMOR},
|
|
["Duration"] = {key = ITEM_ATTRIBUTE_DURATION, cmp = function(v) return v > 0 end},
|
|
["Text"] = {key = ITEM_ATTRIBUTE_TEXT, cmp = function(v) return v ~= "" end},
|
|
["Date"] = {key = ITEM_ATTRIBUTE_DATE},
|
|
["Writer"] = {key = ITEM_ATTRIBUTE_WRITER, cmp = function(v) return v ~= "" end}
|
|
}
|
|
|
|
function setAuxFunctions()
|
|
for name, def in pairs(aux) do
|
|
Item["get" .. name] = function(self)
|
|
local attr = self:getAttribute(def.key)
|
|
if def.cmp and def.cmp(attr) then
|
|
return attr
|
|
elseif not def.cmp and attr and attr ~= 0 then
|
|
return attr
|
|
end
|
|
local default = ItemType["get" .. name]
|
|
return default and default(self:getType()) or nil
|
|
end
|
|
end
|
|
end
|
|
setAuxFunctions()
|
|
end
|
|
|
|
do
|
|
StringStream = {}
|
|
|
|
setmetatable(StringStream, {
|
|
__call = function(self)
|
|
local obj = {}
|
|
return setmetatable(obj, {__index = StringStream})
|
|
end
|
|
})
|
|
|
|
function StringStream.append(self, str, ...)
|
|
self[#self + 1] = string.format(str, ...)
|
|
end
|
|
|
|
function StringStream.concat(self, sep)
|
|
return table.concat(self, sep)
|
|
end
|
|
|
|
local function internalItemGetNameDescription(it, item, subType, addArticle)
|
|
subType = subType or (item and item:getSubType() or -1)
|
|
local ss = StringStream()
|
|
local obj = item or it
|
|
local name = obj:getName()
|
|
if name ~= "" then
|
|
if it:isStackable() and subType > 1 then
|
|
if it:hasShowCount() then
|
|
ss:append("%d ", subType)
|
|
end
|
|
ss:append("%s", obj:getPluralName())
|
|
else
|
|
if addArticle and obj:getArticle() ~= "" then
|
|
ss:append("%s ", obj:getArticle())
|
|
end
|
|
ss:append("%s", obj:getName())
|
|
end
|
|
else
|
|
ss:append("an item of type %d", obj:getId())
|
|
end
|
|
return ss:concat()
|
|
end
|
|
|
|
function Item:getNameDescription(subType, addArticle)
|
|
return internalItemGetNameDescription(self:getType(), self, subType, addArticle)
|
|
end
|
|
|
|
function ItemType:getNameDescription(subType, addArticle)
|
|
return internalItemGetNameDescription(self, nil, subType, addArticle)
|
|
end
|
|
end
|
|
|
|
do
|
|
-- Lua item descriptions created by Zbizu
|
|
local housePriceVisible = configManager.getBoolean(configKeys.HOUSE_DOOR_SHOW_PRICE)
|
|
local pricePerSQM = configManager.getNumber(configKeys.HOUSE_PRICE)
|
|
|
|
local showAtkWeaponTypes = {WEAPON_CLUB, WEAPON_SWORD, WEAPON_AXE, WEAPON_DISTANCE}
|
|
local showDefWeaponTypes = {WEAPON_CLUB, WEAPON_SWORD, WEAPON_AXE, WEAPON_DISTANCE, WEAPON_SHIELD}
|
|
local suppressedConditionNames = {
|
|
-- NOTE: these names are made up just to match dwarven ring attribute style
|
|
[CONDITION_POISON] = "antidote",
|
|
[CONDITION_FIRE] = "non-inflammable",
|
|
[CONDITION_ENERGY] = "antistatic",
|
|
[CONDITION_BLEEDING] = "antiseptic",
|
|
[CONDITION_HASTE] = "heavy",
|
|
[CONDITION_PARALYZE] = "slow immunity",
|
|
[CONDITION_DRUNK] = "hard drinking",
|
|
[CONDITION_DROWN] = "water breathing",
|
|
[CONDITION_FREEZING] = "warm",
|
|
[CONDITION_DAZZLED] = "dazzle immunity",
|
|
[CONDITION_CURSED] = "curse immunity"
|
|
}
|
|
|
|
-- first argument: Item, itemType or item id
|
|
local function internalItemGetDescription(item, lookDistance, subType, addArticle)
|
|
-- optional, but true by default
|
|
if addArticle == nil then
|
|
addArticle = true
|
|
end
|
|
|
|
local isVirtual = false -- check if we're inspecting a real item or itemType only
|
|
local itemType
|
|
|
|
-- determine the kind of item data to process
|
|
if tonumber(item) then
|
|
-- number in function argument
|
|
-- pull data from itemType instead
|
|
isVirtual = true
|
|
itemType = ItemType(item)
|
|
if not itemType then
|
|
return
|
|
end
|
|
|
|
-- polymorphism for item attributes (atk, def, etc)
|
|
item = itemType
|
|
elseif item:isItemType() then
|
|
isVirtual = true
|
|
itemType = item
|
|
else
|
|
itemType = item:getType()
|
|
end
|
|
|
|
-- use default values if not provided
|
|
lookDistance = lookDistance or -1
|
|
subType = subType or (not isVirtual and item:getSubType() or -1)
|
|
|
|
-- possibility of picking the item up (will be reused later)
|
|
local isPickupable = itemType:isMovable() and itemType:isPickupable()
|
|
|
|
-- has uniqueId tag
|
|
local isUnique = false
|
|
if not isVirtual then
|
|
local uid = item:getUniqueId()
|
|
if uid > 100 and uid < 0xFFFF then
|
|
isUnique = true
|
|
end
|
|
end
|
|
|
|
-- read item name with article
|
|
local itemName = item:getNameDescription(subType, addArticle)
|
|
|
|
-- things that will go in parenthesis "(...)"
|
|
local descriptions = {}
|
|
|
|
-- spell words
|
|
do
|
|
local spellName = itemType:getRuneSpellName()
|
|
if spellName then
|
|
descriptions[#descriptions + 1] = string.format('"%s"', spellName)
|
|
end
|
|
end
|
|
|
|
-- container capacity
|
|
do
|
|
-- show container capacity only on non-quest containers
|
|
if not isUnique then
|
|
local isContainer = itemType:isContainer()
|
|
if isContainer then
|
|
descriptions[#descriptions + 1] = string.format("Vol:%d", itemType:getCapacity())
|
|
end
|
|
end
|
|
end
|
|
|
|
-- actionId (will be reused)
|
|
local actionId = 0
|
|
if not isVirtual then
|
|
actionId = item:getActionId()
|
|
end
|
|
|
|
-- key
|
|
do
|
|
if not isVirtual and itemType:isKey() then
|
|
descriptions[#descriptions + 1] = string.format("Key:%0.4d", actionId)
|
|
end
|
|
end
|
|
|
|
-- weapon type (will be reused)
|
|
local weaponType = itemType:getWeaponType()
|
|
|
|
-- attack attributes
|
|
do
|
|
local attack = item:getAttack()
|
|
|
|
-- bows and crossbows
|
|
-- range, attack, hit%
|
|
if itemType:isBow() then
|
|
descriptions[#descriptions + 1] = string.format("Range:%d", item:getShootRange())
|
|
|
|
if attack ~= 0 then
|
|
descriptions[#descriptions + 1] = string.format("Atk+%d", attack)
|
|
end
|
|
|
|
local hitPercent = item:getHitChance()
|
|
if hitPercent ~= 0 then
|
|
descriptions[#descriptions + 1] = string.format("Hit%%+%d", hitPercent)
|
|
end
|
|
|
|
-- melee weapons and missiles
|
|
-- atk x physical +y% element
|
|
elseif table.contains(showAtkWeaponTypes, weaponType) then
|
|
local atkString = string.format("Atk:%d", attack)
|
|
local elementDmg = itemType:getElementDamage()
|
|
if elementDmg ~= 0 then
|
|
atkString = string.format("%s physical %+d %s", atkString, elementDmg, getCombatName(itemType:getElementType()))
|
|
end
|
|
|
|
descriptions[#descriptions + 1] = atkString
|
|
end
|
|
end
|
|
|
|
-- attack speed
|
|
do
|
|
local atkSpeed = item:getAttackSpeed()
|
|
if atkSpeed ~= 0 then
|
|
descriptions[#descriptions + 1] = string.format("AS:%0.2f/turn", 2000 / atkSpeed)
|
|
end
|
|
end
|
|
|
|
-- defense attributes
|
|
do
|
|
local showDef = table.contains(showDefWeaponTypes, weaponType)
|
|
local ammoType = itemType:getAmmoType()
|
|
if showDef then
|
|
local defense = item:getDefense()
|
|
local defAttrs = {}
|
|
if weaponType == WEAPON_DISTANCE then
|
|
-- throwables
|
|
if ammoType ~= AMMO_ARROW and ammoType ~= AMMO_BOLT then
|
|
defAttrs[#defAttrs + 1] = defense
|
|
end
|
|
else
|
|
defAttrs[#defAttrs + 1] = defense
|
|
end
|
|
|
|
-- extra def
|
|
local xD = item:getExtraDefense()
|
|
if xD ~= 0 then
|
|
defAttrs[#defAttrs + 1] = string.format("%+d", xD)
|
|
end
|
|
|
|
if #defAttrs > 0 then
|
|
descriptions[#descriptions + 1] = string.format("Def:%s", table.concat(defAttrs, " "))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- armor
|
|
do
|
|
local arm = item:getArmor()
|
|
if arm > 0 then
|
|
descriptions[#descriptions + 1] = string.format("Arm:%d", arm)
|
|
end
|
|
end
|
|
|
|
-- abilities (will be reused)
|
|
local abilities = itemType:getAbilities()
|
|
|
|
-- stats: hp/mp/soul/magic level
|
|
do
|
|
local stats = {}
|
|
-- flat buffs
|
|
for stat, value in pairs(abilities.stats) do
|
|
stats[stat] = {name = getStatName(stat-1)}
|
|
if value ~= 0 then
|
|
stats[stat].flat = value
|
|
end
|
|
end
|
|
|
|
-- percent buffs
|
|
for stat, value in pairs(abilities.statsPercent) do
|
|
if value ~= 0 then
|
|
stats[stat].percent = value
|
|
end
|
|
end
|
|
|
|
-- display the buffs
|
|
for _, statData in pairs(stats) do
|
|
local displayValues = {}
|
|
if statData.flat then
|
|
displayValues[#displayValues + 1] = statData.flat
|
|
end
|
|
|
|
if statData.percent then
|
|
displayValues[#displayValues + 1] = statData.percent
|
|
end
|
|
|
|
-- desired format examples:
|
|
-- +5%
|
|
-- +20 and 5%
|
|
if #displayValues > 0 then
|
|
displayValues[1] = string.format("%+d", displayValues[1])
|
|
descriptions[#descriptions + 1] = string.format("%s %s", statData.name, table.concat(displayValues, " and "))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- skill boosts
|
|
do
|
|
for skill, value in pairs(abilities.skills) do
|
|
if value ~= 0 then
|
|
descriptions[#descriptions + 1] = string.format("%s %+d", getSkillName(skill-1), value)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- element magic level
|
|
do
|
|
for element, value in pairs(abilities.specialMagicLevel) do
|
|
if value ~= 0 then
|
|
descriptions[#descriptions + 1] = string.format("%s magic level %+d", getCombatName(2^(element-1)), value)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- special skills
|
|
do
|
|
for skill, value in pairs(abilities.specialSkills) do
|
|
if value ~= 0 then
|
|
-- add + symbol to special skill "amount" fields
|
|
if skill-1 < 6 and skill % 2 == 0 and not (skill-1 == SPECIALSKILL_CRITICALHITAMOUNT) then
|
|
value = string.format("%+d", value/100)
|
|
elseif skill-1 < 6 and skill % 2 == 1 then
|
|
value = string.format("%+d", value)
|
|
elseif skill-1 >= 6 then
|
|
-- fatal, dodge, momentum coming from the item natively
|
|
-- (stats coming from tier are near tier info)
|
|
value = string.format("%0.2f", value/100)
|
|
end
|
|
|
|
descriptions[#descriptions + 1] = string.format("%s %s%%", getSpecialSkillName(skill-1), value)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- cleave
|
|
-- perfect shot
|
|
-- to do
|
|
|
|
-- protections
|
|
do
|
|
local protections = {}
|
|
for element, value in pairs(abilities.absorbPercent) do
|
|
if value ~= 0 then
|
|
protections[#protections + 1] = string.format("%s %+d%%", getCombatName(2^(element-1)), value)
|
|
end
|
|
end
|
|
|
|
if #protections > 0 then
|
|
descriptions[#descriptions + 1] = string.format("protection %s", table.concat(protections, ", "))
|
|
end
|
|
end
|
|
|
|
-- damage reflection
|
|
-- to do
|
|
|
|
-- magic shield (classic)
|
|
if abilities.manaShield then
|
|
descriptions[#descriptions + 1] = "magic shield"
|
|
end
|
|
|
|
-- magic shield capacity +flat and x%
|
|
-- to do
|
|
|
|
-- regeneration
|
|
if abilities.manaGain > 0 or abilities.healthGain > 0 or abilities.regeneration then
|
|
descriptions[#descriptions + 1] = "faster regeneration"
|
|
end
|
|
|
|
-- invisibility
|
|
if abilities.invisible then
|
|
descriptions[#descriptions + 1] = "invisibility"
|
|
end
|
|
|
|
-- condition immunities
|
|
do
|
|
local suppressions = abilities.conditionSuppressions
|
|
for conditionId, conditionName in pairs(suppressedConditionNames) do
|
|
if bit.band(abilities.conditionSuppressions, conditionId) ~= 0 then
|
|
descriptions[#descriptions + 1] = conditionName
|
|
end
|
|
end
|
|
end
|
|
|
|
-- speed
|
|
if abilities.speed ~= 0 then
|
|
descriptions[#descriptions + 1] = string.format("speed %+d", math.floor(abilities.speed / 2))
|
|
end
|
|
|
|
-- collecting attributes finished
|
|
-- build the output text
|
|
local response = {itemName}
|
|
|
|
-- item group (will be reused)
|
|
local itemGroup = itemType:getGroup()
|
|
|
|
-- fluid type
|
|
do
|
|
if (itemGroup == ITEM_GROUP_FLUID or itemGroup == ITEM_GROUP_SPLASH) and subType > 0 then
|
|
local subTypeName = getSubTypeName(subType)
|
|
response[#response + 1] = string.format(" of %s", (subTypeName ~= '' and subTypeName or "unknown"))
|
|
end
|
|
end
|
|
|
|
-- door check (will be reused)
|
|
local isDoor = itemType:isDoor()
|
|
|
|
-- door info
|
|
if isDoor then
|
|
if not isVirtual then
|
|
local minLevelDoor = itemType:getLevelDoor()
|
|
if minLevelDoor ~= 0 then
|
|
if actionId >= minLevelDoor then
|
|
response[#response + 1] = string.format(" for level %d", actionId - minLevelDoor)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- primary attributes parenthesis
|
|
if #descriptions > 0 then
|
|
response[#response + 1] = string.format(" (%s)", table.concat(descriptions, ", "))
|
|
end
|
|
|
|
-- podium description
|
|
if not isVirtual and itemType:isPodium() then
|
|
local outfit = item:getOutfit()
|
|
local hasOutfit = item:hasFlag(PODIUM_SHOW_OUTFIT)
|
|
local hasMount = item:hasFlag(PODIUM_SHOW_MOUNT)
|
|
if outfit then
|
|
local podiumParts = {}
|
|
local outfitType = Outfit(outfit.lookType)
|
|
if outfitType then
|
|
if hasOutfit then
|
|
podiumParts[#podiumParts + 1] = string.format("%s outfit", outfitType.name)
|
|
end
|
|
|
|
-- search mount
|
|
local mountName = getMountNameByLookType(outfit.lookMount)
|
|
if mountName and hasMount then
|
|
podiumParts[#podiumParts + 1] = string.format("%s mount", mountName)
|
|
end
|
|
else
|
|
-- search mount
|
|
local mountName = getMountNameByLookType(outfit.lookMount)
|
|
local isEnabled = mountName and hasMount
|
|
if not mountName then
|
|
mountName = getMountNameByLookType(outfit.lookType)
|
|
isEnabled = mountName and hasOutfit
|
|
end
|
|
|
|
if mountName and isEnabled then
|
|
podiumParts[#podiumParts + 1] = string.format("%s mount", mountName)
|
|
end
|
|
end
|
|
|
|
if #podiumParts > 0 then
|
|
response[#response + 1] = string.format(" displaying the %s", table.concat(podiumParts, " on the "))
|
|
end
|
|
end
|
|
end
|
|
|
|
-- charges and duration
|
|
do
|
|
local expireInfo = {}
|
|
|
|
-- charges
|
|
if itemType:hasShowCharges() then
|
|
local charges = item:getCharges()
|
|
expireInfo[#expireInfo + 1] = string.format("has %d charge%s left", charges, (charges ~= 1 and "s" or ""))
|
|
end
|
|
|
|
-- duration
|
|
if itemType:hasShowDuration() then
|
|
local currentDuration = item:getDuration()
|
|
if isVirtual then
|
|
currentDuration = currentDuration * 1000
|
|
end
|
|
|
|
local maxDuration = itemType:getDuration() * 1000
|
|
if maxDuration == 0 then
|
|
local transferType = itemType:getTransformEquipId()
|
|
if transferType ~= 0 then
|
|
transferType = ItemType(transferType)
|
|
maxDuration = transferType and transferType:getDuration() * 1000 or maxDuration
|
|
end
|
|
end
|
|
|
|
if currentDuration == maxDuration then
|
|
expireInfo[#expireInfo + 1] = "is brand-new"
|
|
elseif currentDuration ~= 0 then
|
|
expireInfo[#expireInfo + 1] = string.format("will expire in %s", Game.getCountdownString(math.floor(currentDuration/1000), true, true))
|
|
end
|
|
end
|
|
|
|
if #expireInfo > 0 then
|
|
response[#response + 1] = string.format(" that %s", table.concat(expireInfo, " and "))
|
|
end
|
|
end
|
|
|
|
-- dot after primary attributes info
|
|
response[#response + 1] = "."
|
|
|
|
-- empty fluid container suffix
|
|
if itemGroup == ITEM_GROUP_FLUID and subType < 1 then
|
|
response[#response + 1] = " It is empty."
|
|
end
|
|
|
|
-- house door
|
|
if isDoor and not isVirtual then
|
|
local tile = item:getTile()
|
|
if tile then
|
|
local house = tile:getHouse()
|
|
if house then
|
|
local houseName = house:getName()
|
|
local houseOwnerName = house:getOwnerName()
|
|
local isForSale = false
|
|
|
|
if not houseOwnerName or houseOwnerName:len() == 0 then
|
|
houseOwnerName = "Nobody"
|
|
isForSale = true
|
|
end
|
|
|
|
response[#response + 1] = string.format(" It belongs to house '%s'. %s owns this house.", houseName, houseOwnerName)
|
|
if housePriceVisible and isForSale and pricePerSQM > 0 then
|
|
response[#response + 1] = string.format(" It costs %d gold coins.", pricePerSQM * house:getTileCount())
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
--[[
|
|
-- imbuements (to do)
|
|
-- \nImbuements: (Basic Strike 2:30h, Basic Void 2:30h, Empty Slot).
|
|
|
|
|
|
-- item class (placeholder)
|
|
-- Classification: x.
|
|
-- Disabled until classification system is implemented.
|
|
|
|
do
|
|
local classification = itemType:getClassification()
|
|
if classification == 0 then
|
|
classification = "other"
|
|
end
|
|
response[#response + 1] = string.format("\nClassification: %s.", classification)
|
|
end
|
|
]]
|
|
|
|
-- item count (will be reused later)
|
|
local count = isVirtual and 1 or item:getCount()
|
|
|
|
-- wield info
|
|
do
|
|
if itemType:isRune() then
|
|
local rune = Spell(itemType:getId())
|
|
if rune then
|
|
local runeLevel = rune:runeLevel()
|
|
local runeMagLevel = rune:runeMagicLevel()
|
|
local runeVocMap = rune:vocation()
|
|
|
|
local hasLevel = runeLevel > 0
|
|
local hasMLvl = runeMagLevel > 0
|
|
local hasVoc = #runeVocMap > 0
|
|
if hasLevel or hasMLvl or hasVoc then
|
|
local vocAttrs = {}
|
|
if not hasVoc then
|
|
vocAttrs[#vocAttrs + 1] = "players"
|
|
else
|
|
for _, vocName in ipairs(runeVocMap) do
|
|
local vocation = Vocation(vocName)
|
|
if vocation and vocation:getPromotion() then
|
|
vocAttrs[#vocAttrs + 1] = vocName
|
|
end
|
|
end
|
|
end
|
|
|
|
if #vocAttrs > 1 then
|
|
vocAttrs[#vocAttrs - 1] = string.format("and %s", vocAttrs[#vocAttrs - 1])
|
|
end
|
|
|
|
local levelInfo = {}
|
|
if hasLevel then
|
|
levelInfo[#levelInfo + 1] = string.format("level %d", runeLevel)
|
|
end
|
|
|
|
if hasMLvl then
|
|
levelInfo[#levelInfo + 1] = string.format("magic level %d", runeMagLevel)
|
|
end
|
|
|
|
local levelStr = ""
|
|
if #levelInfo > 0 then
|
|
levelStr = string.format(" of %s or higher", table.concat(levelInfo, " and "))
|
|
end
|
|
|
|
response[#response + 1] = string.format(
|
|
"\n%s can only be used properly by %s%s.",
|
|
(count > 1 and "They" or "It"),
|
|
table.concat(vocAttrs, ", "),
|
|
levelStr
|
|
)
|
|
end
|
|
end
|
|
else
|
|
local wieldInfo = itemType:getWieldInfo()
|
|
if wieldInfo ~= 0 then
|
|
local wieldAttrs = {}
|
|
if bit.band(wieldInfo, WIELDINFO_PREMIUM) ~= 0 then
|
|
wieldAttrs[#wieldAttrs + 1] = "premium"
|
|
end
|
|
|
|
local vocStr = itemType:getVocationString()
|
|
if vocStr ~= '' then
|
|
wieldAttrs[#wieldAttrs + 1] = vocStr
|
|
else
|
|
wieldAttrs[#wieldAttrs + 1] = "players"
|
|
end
|
|
|
|
local levelInfo = {}
|
|
if bit.band(wieldInfo, WIELDINFO_LEVEL) ~= 0 then
|
|
levelInfo[#levelInfo + 1] = string.format("level %d", itemType:getMinReqLevel())
|
|
end
|
|
|
|
if bit.band(wieldInfo, WIELDINFO_MAGLV) ~= 0 then
|
|
levelInfo[#levelInfo + 1] = string.format("magic level %d", itemType:getMinReqMagicLevel())
|
|
end
|
|
|
|
if #levelInfo > 0 then
|
|
wieldAttrs[#wieldAttrs + 1] = string.format("of %s or higher", table.concat(levelInfo, " and "))
|
|
end
|
|
|
|
response[#response + 1] = string.format(
|
|
"\n%s can only be wielded properly by %s.",
|
|
(count > 1 and "They" or "It"),
|
|
table.concat(wieldAttrs, " ")
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
if lookDistance <= 1 then
|
|
local weight = item:getWeight()
|
|
if isPickupable and not isUnique then
|
|
response[#response + 1] = string.format("\n%s %0.2f oz.", (count == 1 or not itemType:hasShowCount()) and "It weighs" or "They weigh", weight / 100)
|
|
end
|
|
end
|
|
|
|
-- item text
|
|
if not isVirtual and itemType:hasAllowDistRead() then
|
|
local text = item:getText()
|
|
if text and text:len() > 0 then
|
|
if lookDistance <= 4 then
|
|
local writer = item:getAttribute(ITEM_ATTRIBUTE_WRITER)
|
|
local writeDate = item:getAttribute(ITEM_ATTRIBUTE_DATE)
|
|
local writeInfo = {}
|
|
if writer and writer:len() > 0 then
|
|
writeInfo[#writeInfo + 1] = string.format("\n%s wrote", writer)
|
|
|
|
if writeDate and writeDate > 0 then
|
|
writeInfo[#writeInfo + 1] = string.format(" on %s", os.date("%d %b %Y", writeDate))
|
|
end
|
|
end
|
|
|
|
if #writeInfo > 0 then
|
|
response[#response + 1] = string.format("%s:\n%s", table.concat(writeInfo, ""), text)
|
|
else
|
|
response[#response + 1] = string.format("\nYou read: %s", text)
|
|
end
|
|
else
|
|
response[#response + 1] = "\nYou are too far away to read it."
|
|
end
|
|
else
|
|
response[#response + 1] = "\nNothing is written on it."
|
|
end
|
|
end
|
|
|
|
-- item description
|
|
local isBed = itemType:isBed()
|
|
if lookDistance <= 1 or (not isVirtual and (isDoor or isBed)) then
|
|
-- custom item description
|
|
local desc = not isVirtual and item:getSpecialDescription()
|
|
|
|
-- native item description
|
|
if not desc or desc == "" then
|
|
desc = itemType:getDescription()
|
|
end
|
|
|
|
-- level door description
|
|
if isDoor and lookDistance <= 1 and (not desc or desc == "") and itemType:getLevelDoor() ~= 0 then
|
|
desc = "Only the worthy may pass."
|
|
end
|
|
|
|
if desc and desc:len() > 0 then
|
|
response[#response + 1] = string.format("\n%s", desc)
|
|
end
|
|
else
|
|
if lookDistance <= 4 then
|
|
local desc = not isVirtual and item:getSpecialDescription()
|
|
|
|
if not desc or desc == "" then
|
|
desc = itemType:getDescription()
|
|
end
|
|
|
|
if desc and desc:len() > 0 then
|
|
response[#response + 1] = string.format("\n%s", desc)
|
|
end
|
|
else
|
|
response[#response + 1] = "\nYou are too far away to read it."
|
|
end
|
|
end
|
|
|
|
-- pickupable items with store flag
|
|
if not isVirtual and isPickupable and item:isStoreItem() then
|
|
response[#response + 1] = "\nThis item cannot be traded."
|
|
end
|
|
|
|
-- turn response into a single string
|
|
return table.concat(response, "")
|
|
end
|
|
|
|
function Item:getDescription(lookDistance, subType, addArticle)
|
|
return internalItemGetDescription(self, lookDistance, subType, addArticle)
|
|
end
|
|
|
|
function ItemType:getItemDescription(lookDistance, subType, addArticle)
|
|
return internalItemGetDescription(self, lookDistance, subType, addArticle)
|
|
end
|
|
end
|