2022-04-11 17:10:46 +03:00

658 lines
18 KiB
Lua

local monsterOfTheDay = {
[8430] = { monster = 'Crocodile' },
[8431] = { monster = 'Tarantula' },
[8432] = { monster = 'Carniphila' },
[8433] = { monster = 'Merlkin' },
[8434] = { monster = 'Kongra' },
[8435] = { monster = 'Sibang' },
[8436] = { monster = 'Thornback Tortoise' },
[8437] = { monster = 'Gargoyle' },
[8438] = { monster = 'Quara Constrictor Scout' },
[8439] = { monster = 'Quara Hydromancer Scout' },
[8440] = { monster = 'Quara Mantassin Scout' },
[8441] = { monster = 'Quara Pincher Scout' },
[8442] = { monster = 'Quara Predator Scout' },
[8443] = { monster = 'Ancient Scarab' },
[8444] = { monster = 'Wyvern' },
[8445] = { monster = 'Bonebeast' },
[8446] = { monster = 'Dragon' },
[8447] = { monster = 'Quara Constrictor' },
[8448] = { monster = 'Quara Hydromancer' },
[8449] = { monster = 'Quara Mantassin' },
[8450] = { monster = 'Quara Pincher' },
[8451] = { monster = 'Quara Predator' },
[8452] = { monster = 'Giant Spider' },
[8453] = { monster = 'Banshee' },
[8454] = { monster = 'Lich' },
[8455] = { monster = 'Acolyte of The Cult' },
[8456] = { monster = 'Adept of The Cult' },
[8457] = { monster = 'Enlightened of The Cult' },
[8458] = { monster = 'Novice of The Cult' },
[8459] = { monster = 'Hydra' },
[8460] = { monster = 'Serpent Spawn' },
[8461] = { monster = 'Behemoth' },
[8462] = { monster = 'Dragon Lord' },
[8463] = { monster = 'Hand of Cursed Fate' },
[8464] = { monster = 'Juggernaut' },
[8465] = { monster = 'Frost Troll' },
[8466] = { monster = 'Swamp Troll' },
[8467] = { monster = 'Rat' },
[8468] = { monster = 'Cave Rat' },
[8469] = { monster = 'Wolf' },
[8470] = { monster = 'Winter Wolf' },
[8471] = { monster = 'Wasp' },
[8472] = { monster = 'Larva' },
[8473] = { monster = 'Dwarf' },
[8474] = { monster = 'Skeleton' },
[8475] = { monster = 'Ghoul' },
[8476] = { monster = 'Elf' },
[8477] = { monster = 'Elf Scout' },
[8478] = { monster = 'Elf Arcanist' },
[8479] = { monster = 'Bug' },
[8480] = { monster = 'Smuggler' },
[8481] = { monster = 'Wild Warrior' },
[8482] = { monster = 'Bandit' },
[8483] = { monster = 'Hyaena' },
[8484] = { monster = 'Lion' },
[8485] = { monster = 'Bear' },
[8486] = { monster = 'Slime' },
[8487] = { monster = 'Beholder' },
[8488] = { monster = 'Elder Beholder' },
[8489] = { monster = 'Green Djinn' },
[8490] = { monster = 'Blue Djinn' },
[8491] = { monster = 'Marid' },
[8492] = { monster = 'Efreet' },
[8493] = { monster = 'Pirate Skeleton' },
[8494] = { monster = 'Pirate Marauder' },
[8495] = { monster = 'Pirate Cutthroat' },
[8496] = { monster = 'Pirate Ghost' },
[8497] = { monster = 'Pirate Buccaneer' },
[8498] = { monster = 'Pirate Corsair' },
[8499] = { monster = 'Orc Spearman' },
[8500] = { monster = 'Orc Shaman' },
[8501] = { monster = 'Orc Rider' },
[8502] = { monster = 'Orc Warrior' },
[8503] = { monster = 'Orc Berserker' },
[8504] = { monster = 'Minotaur Archer' },
[8505] = { monster = 'Minotaur Guard' },
[8506] = { monster = 'Minotaur Mage' },
[8507] = { monster = 'Lizard Templar' },
[8508] = { monster = 'Lizard Sentinel' },
[8509] = { monster = 'Lizard Snakecharmer' },
[8510] = { monster = 'Dwarf Soldier' },
[8511] = { monster = 'Dwarf Guard' },
[8512] = { monster = 'Dwarf Geomancer' },
[8513] = { monster = 'Ghost' },
[8514] = { monster = 'Demon Skeleton' },
[8515] = { monster = 'Vampire' },
[8516] = { monster = 'Orc Leader' },
[8517] = { monster = 'Orc Warlord' },
[8518] = { monster = 'Hero' },
[8519] = { monster = 'Necromancer' },
[8520] = { monster = 'Priestess' },
[8521] = { monster = 'Nightmare' },
[8522] = { monster = 'Warlock' },
[8523] = { monster = 'Demon' },
[8524] = { monster = 'Troll' },
[8525] = { monster = 'Goblin' },
[8526] = { monster = 'Rotworm' },
[8527] = { monster = 'Carrion Worm' },
[8528] = { monster = 'Cyclops' },
[8529] = { monster = 'Amazon' },
[8530] = { monster = 'Minotaur' },
[8531] = { monster = 'Orc' }
}
local trainingStatues = {17724, 17725, 17726, 17727, 17728}
function Player:onLook(thing, position, distance)
local description = "You see " .. thing:getDescription(distance)
if thing:isItem() and thing:getId() == 2028 then
if isInArray(trainingStatues, thing:getActionId()) then
local trainingTime = math.min(43200 / 2, self:getOfflineTrainingTime() / 1000)
local text = "You have"
local hours = math.floor(trainingTime / 3600)
if hours > 1 then
text = string.format("%s %d hours", text, hours)
elseif hours == 1 then
text = string.format("%s 1 hour", text)
end
local minutes = math.floor((trainingTime % 3600) / 60)
if minutes ~= 0 then
if hours ~= 0 then
text = string.format("%s and", text)
end
if minutes > 1 then
text = string.format("%s %d minutes", text, minutes)
else
text = string.format("%s 1 minute", text)
end
end
text = string.format(" %s offline training time remaining.", text)
description = string.format(description .. "%s", text)
end
end
if self:getGroup():getAccess() then
if thing:isItem() then
description = string.format("%s\nItem ID: %d", description, thing:getId())
local actionId = thing:getActionId()
if actionId ~= 0 then
description = string.format("%s, Action ID: %d", description, actionId)
end
local uniqueId = thing:getAttribute(ITEM_ATTRIBUTE_MOVEMENTID)
if uniqueId > 0 and uniqueId < 65536 then
description = string.format("%s, Movement ID: %d", description, uniqueId)
end
local itemType = thing:getType()
local transformEquipId = itemType:getTransformEquipId()
local transformDeEquipId = itemType:getTransformDeEquipId()
if transformEquipId ~= 0 then
description = string.format("%s\nTransforms to: %d (onEquip)", description, transformEquipId)
elseif transformDeEquipId ~= 0 then
description = string.format("%s\nTransforms to: %d (onDeEquip)", description, transformDeEquipId)
end
local decayId = itemType:getDecayId()
if decayId ~= -1 then
description = string.format("%s\nDecays to: %d", description, decayId)
end
if thing:getAttribute(ITEM_ATTRIBUTE_DECAYSTATE) == 1 then
description = string.format("%s\nDecaying in %d minutes (%d seconds).", description, thing:getAttribute(ITEM_ATTRIBUTE_DURATION) / 1000 / 60, thing:getAttribute(ITEM_ATTRIBUTE_DURATION) / 1000)
end
elseif thing:isCreature() then
local str = "%s\nHealth: %d / %d"
if thing:isPlayer() and thing:getMaxMana() > 0 then
str = string.format("%s, Mana: %d / %d", str, thing:getMana(), thing:getMaxMana())
end
description = string.format(str, description, thing:getHealth(), thing:getMaxHealth()) .. "."
end
local position = thing:getPosition()
description = string.format(
"%s\nPosition: %d, %d, %d",
description, position.x, position.y, position.z
)
if thing:isCreature() then
if thing:isPlayer() then
description = string.format("%s\nIP: %s.", description, Game.convertIpToString(thing:getIp()))
end
end
end
self:sendTextMessage(MESSAGE_INFO_DESCR, description)
end
function Player:onLookInBattleList(creature, distance)
local description = "You see " .. creature:getDescription(distance)
if self:getGroup():getAccess() then
local str = "%s\nHealth: %d / %d"
if creature:isPlayer() and creature:getMaxMana() > 0 then
str = string.format("%s, Mana: %d / %d", str, creature:getMana(), creature:getMaxMana())
end
description = string.format(str, description, creature:getHealth(), creature:getMaxHealth()) .. "."
local position = creature:getPosition()
description = string.format(
"%s\nPosition: %d, %d, %d",
description, position.x, position.y, position.z
)
if creature:isPlayer() then
description = string.format("%s\nIP: %s", description, Game.convertIpToString(creature:getIp()))
end
end
self:sendTextMessage(MESSAGE_INFO_DESCR, description)
end
function Player:onLookInTrade(partner, item, distance)
self:sendTextMessage(MESSAGE_INFO_DESCR, "You see " .. item:getDescription(distance))
end
local showAtkWeaponTypes = {WEAPON_CLUB, WEAPON_SWORD, WEAPON_AXE, WEAPON_DISTANCE}
local showDefWeaponTypes = {WEAPON_CLUB, WEAPON_SWORD, WEAPON_AXE, WEAPON_DISTANCE, WEAPON_SHIELD}
function Player:onLookInMarket(itemType)
local response = NetworkMessage()
response:addByte(0xF8)
response:addU16(itemType:getId())
-- armor
do
local armor = itemType:getArmor()
if armor > 0 then
response:addString(armor)
else
response:addU16(0)
end
end
-- weapon data (will be reused)
local weaponType = itemType:getWeaponType()
-- attack
do
local showAtk = table.contains(showAtkWeaponTypes, weaponType)
if showAtk then
local atkAttrs = {}
local atk = itemType:getAttack()
if itemType:isBow() then
if atk ~= 0 then
atkAttrs[#atkAttrs + 1] = string.format("%+d", atk)
end
local hitChance = itemType:getHitChance()
if hitChance ~= 0 then
atkAttrs[#atkAttrs + 1] = string.format("chance to hit %+d%%", hitChance)
end
atkAttrs[#atkAttrs + 1] = string.format("%d fields", itemType:getShootRange())
else
atkAttrs[#atkAttrs + 1] = atk
local elementDmg = 0
if elementDmg ~= 0 then
atkAttrs[#atkAttrs] = string.format("%d physical %+d %s", atkAttrs[#atkAttrs], elementDmg, getCombatName(itemType:getElementType()))
end
end
response:addString(table.concat(atkAttrs, ", "))
else
response:addU16(0)
end
end
-- container slots
do
if itemType:isContainer() then
response:addString(itemType:getCapacity())
else
response:addU16(0)
end
end
-- defense
do
local showDef = table.contains(showDefWeaponTypes, weaponType)
if showDef then
local def = itemType:getDefense()
if weaponType == WEAPON_DISTANCE then
-- throwables
if ammoType ~= AMMO_ARROW and ammoType ~= AMMO_BOLT then
response:addString(def)
else
response:addU16(0)
end
else
-- extra def
--local xD = itemType:getExtraDefense()
--if xD ~= 0 then
-- def = string.format("%d %+d", def, xD)
--end
response:addString(def)
end
else
response:addU16(0)
end
end
-- description
do
local desc = itemType:getDescription()
if desc and #desc > 0 then
response:addString(desc)
else
response:addU16(0)
end
end
-- duration
do
local duration = itemType:getDuration()
if duration == 0 then
local transferType = itemType:getTransformEquipId()
if transferType ~= 0 then
transferType = ItemType(transferType)
duration = transferType and transferType:getDuration() or duration
end
end
if duration > 0 then
response:addString(Game.getCountdownString(duration, true, true))
else
response:addU16(0)
end
end
-- item abilities (will be reused)
local abilities = itemType:getAbilities()
-- element 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
response:addString(table.concat(protections, ", "))
else
response:addU16(0)
end
end
-- level req
do
local minLevel = itemType:getMinReqLevel()
if minLevel > 0 then
response:addString(minLevel)
else
response:addU16(0)
end
end
-- magic level req
do
local minMagicLevel = itemType:getMinReqMagicLevel()
if minMagicLevel > 0 then
response:addString(minMagicLevel)
else
response:addU16(0)
end
end
-- vocation
do
local vocations = itemType:getVocationString()
if vocations and vocations:len() > 0 then
response:addString(vocations)
else
response:addU16(0)
end
end
-- rune words
do
local spellName = itemType:getRuneSpellName()
if spellName and spellName:len() > 0 then
response:addString(spellName)
else
response:addU16(0)
end
end
-- "skill boost" category
do
-- skill boost
local skillBoosts = {}
if abilities.manaGain > 0 or abilities.healthGain > 0 or abilities.regeneration then
skillBoosts[#skillBoosts + 1] = "faster regeneration"
end
-- invisibility
if abilities.invisible then
skillBoosts[#skillBoosts + 1] = "invisibility"
end
-- magic shield (classic)
if abilities.manaShield then
skillBoosts[#skillBoosts + 1] = "magic shield"
end
-- stats (hp/mp/soul/ml)
for stat, value in pairs(abilities.stats) do
if value ~= 0 then
skillBoosts[#skillBoosts + 1] = string.format("%s %+d", getStatName(stat - 1), value)
end
end
-- stats but in %
for stat, value in pairs(abilities.statsPercent) do
if value ~= 0 then
skillBoosts[#skillBoosts + 1] = string.format("%s %+d%%", getStatName(stat - 1), value)
end
end
-- speed
if abilities.speed ~= 0 then
skillBoosts[#skillBoosts + 1] = string.format("speed %+d", abilities.speed)
end
-- skills
for skill, value in pairs(abilities.skills) do
if value ~= 0 then
skillBoosts[#skillBoosts + 1] = string.format("%s %+d", getSkillName(skill - 1), value)
end
end
-- add to response
if #skillBoosts > 0 then
response:addString(table.concat(skillBoosts, ", "))
else
response:addU16(0)
end
end
-- charges
do
if itemType:hasShowCharges() then
response:addString(itemType:getCharges())
else
response:addU16(0)
end
end
-- weapon type
do
if itemType:isWeapon() then
response:addString(itemType:getWeaponString())
else
response:addU16(0)
end
end
-- weight
response:addString(string.format("%0.2f", itemType:getWeight() / 100))
-- buy stats
do
local stats = itemType:getMarketBuyStatistics()
if stats then
response:addByte(0x01)
response:addU32(stats.numTransactions)
response:addU64(stats.totalPrice)
response:addU64(stats.highestPrice)
response:addU64(stats.lowestPrice)
else
response:addByte(0x00)
end
end
-- sell stats
do
local stats = itemType:getMarketSellStatistics()
if stats then
response:addByte(0x01)
response:addU32(stats.numTransactions)
response:addU64(stats.totalPrice)
response:addU64(stats.highestPrice)
response:addU64(stats.lowestPrice)
else
response:addByte(0x00)
end
end
response:sendToPlayer(self)
end
function Player:onMoveItem(item, count, fromPosition, toPosition, fromCylinder, toCylinder)
return true
end
function Player:onItemMoved(item, count, fromPosition, toPosition, fromCylinder, toCylinder)
end
function Player:onMoveCreature(creature, fromPosition, toPosition)
return true
end
function Player:onReportBug(message, position, category)
if self:getAccountType() == ACCOUNT_TYPE_NORMAL then
return false
end
local name = self:getName()
local file = io.open("data/reports/bugs/" .. name .. " report.txt", "a")
if not file then
self:sendTextMessage(MESSAGE_EVENT_DEFAULT, "There was an error when processing your report, please contact a gamemaster.")
return true
end
io.output(file)
io.write("------------------------------\n")
io.write("Name: " .. name)
if category == BUG_CATEGORY_MAP then
io.write(" [Map position: " .. position.x .. ", " .. position.y .. ", " .. position.z .. "]")
end
local playerPosition = self:getPosition()
io.write(" [Player Position: " .. playerPosition.x .. ", " .. playerPosition.y .. ", " .. playerPosition.z .. "]\n")
io.write("Comment: " .. message .. "\n")
io.close(file)
self:sendTextMessage(MESSAGE_EVENT_DEFAULT, "Your report has been sent to " .. configManager.getString(configKeys.SERVER_NAME) .. ".")
return true
end
function Player:onTurn(direction)
return true
end
function Player:onTradeRequest(target, item)
return true
end
function Player:onTradeAccept(target, item, targetItem)
return true
end
local soulCondition = Condition(CONDITION_SOUL, CONDITIONID_DEFAULT)
soulCondition:setTicks(4 * 60 * 1000)
soulCondition:setParameter(CONDITION_PARAM_SOULGAIN, 1)
local function useStamina(player)
local staminaMinutes = player:getStamina()
if staminaMinutes == 0 then
return
end
local playerId = player:getId()
local currentTime = os.time()
local timePassed = currentTime - nextUseStaminaTime[playerId]
if timePassed <= 0 then
return
end
if timePassed > 60 then
if staminaMinutes > 2 then
staminaMinutes = staminaMinutes - 2
else
staminaMinutes = 0
end
nextUseStaminaTime[playerId] = currentTime + 120
else
staminaMinutes = staminaMinutes - 1
nextUseStaminaTime[playerId] = currentTime + 60
end
player:setStamina(staminaMinutes)
end
function Player:onGainExperience(source, exp, rawExp)
if not source or source:isPlayer() then
return exp
end
-- Soul regeneration
local vocation = self:getVocation()
if self:getSoul() < vocation:getMaxSoul() and exp >= self:getLevel() then
soulCondition:setParameter(CONDITION_PARAM_SOULTICKS, vocation:getSoulGainTicks() * 1000)
self:addCondition(soulCondition)
end
-- Apply experience stage multiplier
if (vocation:getId() > 0 or self:getLevel() < 8) then
exp = exp * Game.getExperienceStage(self:getLevel())
end
-- Stamina modifier
if configManager.getBoolean(configKeys.STAMINA_SYSTEM) then
useStamina(self)
if self:getStorageValue(17582) > os.time() then
exp = exp * 1.1
end
local staminaMinutes = self:getStamina()
if staminaMinutes <= 840 then
exp = exp * 0.5
end
end
if getGlobalStorageValue(17589) > os.time() then
exp = exp * (1 + getGlobalStorageValue(17585) / 100)
end
if source:isMonster() and not source:getMaster() then
if getGlobalStorageValue(8420) > 0 then
if source:getName():lower() == monsterOfTheDay[getGlobalStorageValue(8420)].monster:lower() then
exp = exp * (1 + getGlobalStorageValue(8421) / 100)
end
end
end
if self:isPremium() then
exp = exp * (1 + 10 / 100)
end
return exp
end
function Player:onLoseExperience(exp)
return exp
end
function Player:onGainSkillTries(skill, tries)
if APPLY_SKILL_MULTIPLIER == false then
return tries
end
if skill == SKILL_MAGLEVEL then
tries = tries * configManager.getNumber(configKeys.RATE_MAGIC)
if getGlobalStorageValue(17591) > os.time() then
tries = tries * (1 + getGlobalStorageValue(17587) / 100)
end
return tries
end
tries = tries * configManager.getNumber(configKeys.RATE_SKILL)
if getGlobalStorageValue(17590) > os.time() then
tries = tries * (1 + getGlobalStorageValue(17586) / 100)
end
return tries
end