First commit

This commit is contained in:
2025-02-26 13:42:34 +01:00
commit f465c9072c
2467 changed files with 426214 additions and 0 deletions

View File

@@ -0,0 +1 @@
-- Nothing --

View File

@@ -0,0 +1,47 @@
local area = {
fromPos = {x = 33144, y = 32863, z = 7},
toPos = {x = 33154, y = 32873, z = 7}
}
function onSay(player, words, param)
if player:getPosition():isInRange(area.fromPos, area.toPos) then
local tile1 = Tile(Position(33147, 32868, 7))
if tile1 then
local stoneRailing = tile1:getItemById(2212)
if stoneRailing ~= nil then
stoneRailing:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
stoneRailing:remove()
broadcastMessage("The player " .. player:getName() .. " have opened The Serpentine Tower!", MESSAGE_STATUS_WARNING)
end
end
local tile2 = Tile(Position(33149, 32866, 7))
if tile2 then
local stoneRailing = tile2:getItemById(2210)
if stoneRailing ~= nil then
stoneRailing:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
stoneRailing:remove()
end
end
local tile3 = Tile(Position(33151, 32868, 7))
if tile3 then
local stoneRailing = tile3:getItemById(2211)
if stoneRailing ~= nil then
stoneRailing:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
stoneRailing:remove()
end
end
local tile4 = Tile(Position(33149, 32870, 7))
if tile4 then
local stoneRailing = tile4:getItemById(2209)
if stoneRailing ~= nil then
stoneRailing:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
stoneRailing:remove()
end
end
end
return true
end

View File

@@ -0,0 +1,66 @@
local function getSkillId(skillName)
if skillName == "club" then
return SKILL_CLUB
elseif skillName == "sword" then
return SKILL_SWORD
elseif skillName == "axe" then
return SKILL_AXE
elseif skillName:sub(1, 4) == "dist" then
return SKILL_DISTANCE
elseif skillName:sub(1, 6) == "shield" then
return SKILL_SHIELD
elseif skillName:sub(1, 4) == "fish" then
return SKILL_FISHING
else
return SKILL_FIST
end
end
local function getExpForLevel(level)
level = level - 1
return ((50 * level * level * level) - (150 * level * level) + (400 * level)) / 3
end
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local split = param:split(",")
if split[2] == nil then
player:sendCancelMessage("Insufficient parameters.")
return false
end
local target = Player(split[1])
if target == nil then
player:sendCancelMessage("A player with that name is not online.")
return false
end
-- Trim left
split[2] = split[2]:gsub("^%s*(.-)$", "%1")
local count = 1
if split[3] ~= nil then
count = tonumber(split[3])
end
local ch = split[2]:sub(1, 1)
for i = 1, count do
if ch == "l" or ch == "e" then
target:addExperience(getExpForLevel(target:getLevel() + 1) - target:getExperience(), false)
elseif ch == "m" then
target:addManaSpent(target:getVocation():getRequiredManaSpent(target:getBaseMagicLevel() + 1) - target:getManaSpent())
else
local skillId = getSkillId(split[2])
target:addSkillTries(skillId, target:getVocation():getRequiredSkillTries(skillId, target:getSkillLevel(skillId) + 1) - target:getSkillTries(skillId))
end
end
return false
end

View File

@@ -0,0 +1,21 @@
function onSay(player, words, param)
if player:getAccountType() <= ACCOUNT_TYPE_TUTOR then
return true
end
local target = Player(param)
if target == nil then
player:sendCancelMessage("A player with that name is not online.")
return false
end
if target:getAccountType() ~= ACCOUNT_TYPE_NORMAL then
player:sendCancelMessage("You can only promote a normal player to a tutor.")
return false
end
target:setAccountType(ACCOUNT_TYPE_TUTOR)
target:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have been promoted to a tutor by " .. player:getName() .. ".")
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have promoted " .. target:getName() .. " to a tutor.")
return false
end

View File

@@ -0,0 +1,29 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local effect = tonumber(param)
local position = player:getPosition()
local toPositionLow = {z = position.z}
local toPositionHigh = {z = position.z}
toPositionLow.x = position.x - 7
toPositionHigh.x = position.x + 7
for i = -5, 5 do
toPositionLow.y = position.y + i
toPositionHigh.y = toPositionLow.y
position:sendDistanceEffect(toPositionLow, effect)
position:sendDistanceEffect(toPositionHigh, effect)
end
toPositionLow.y = position.y - 5
toPositionHigh.y = position.y + 5
for i = -6, 6 do
toPositionLow.x = position.x + i
toPositionHigh.x = toPositionLow.x
position:sendDistanceEffect(toPositionLow, effect)
position:sendDistanceEffect(toPositionHigh, effect)
end
return false
end

View File

@@ -0,0 +1,57 @@
local banDays = 7
function onSay(cid, words, param)
local player = Player(cid)
if not player:getGroup():getAccess() then
return true
end
local name = param
local reason = ''
local banInfo = ''
local banTime = 0
local banMultiplier = 0
local params = param:split(',')
if params ~= nil then
name = params[1]
reason = string.trim(params[2])
banInfo = string.trim(params[3])
print(banInfo)
end
if banInfo then
if banInfo:find('h') then
banTime = banInfo:sub(0, banInfo:find('h') - 1)
banMultiplier = 3600
elseif banInfo:find('d') then
banTime = banInfo:sub(0, banInfo:find('d') - 1)
banMultiplier = 86400
else
banTime = banDays
banMultiplier = 86400
end
banTime = banTime * banMultiplier
end
local accountId = getAccountNumberByPlayerName(name)
if accountId == 0 then
return false
end
local resultId = db.storeQuery("SELECT 1 FROM `account_bans` WHERE `account_id` = " .. accountId)
if resultId ~= false then
result.free(resultId)
return false
end
local timeNow = os.time()
db:query("INSERT INTO `account_bans` (`account_id`, `reason`, `banned_at`, `expires_at`, `banned_by`) VALUES (" ..
accountId .. ", " .. db.escapeString(reason) .. ", " .. timeNow .. ", " .. timeNow + banTime .. ", " .. player:getGuid() .. ")")
local target = Player(name)
if target ~= nil then
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, target:getName() .. " has been banned.")
target:remove()
else
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, name .. " has been banned.")
end
end

View File

@@ -0,0 +1,11 @@
function onSay(player, words, param)
if not getPlayerFlagValue(player, PlayerFlag_CanBroadcast) then
return true
end
print("> " .. player:getName() .. " broadcasted: \"" .. param .. "\".")
for _, targetPlayer in ipairs(Game.getPlayers()) do
targetPlayer:sendTextMessage(MESSAGE_STATUS_WARNING, param)
end
return false
end

View File

@@ -0,0 +1,4 @@
function onSay(player, words, param)
player:sendTextMessage(MESSAGE_INFO_DESCR, "Buying a house is available only through auction which is available at website: https://tibianus.com/house.php")
return false
end

View File

@@ -0,0 +1,25 @@
local config = {
days = 90,
maxDays = 365,
price = 10000
}
function onSay(player, words, param)
if configManager.getBoolean(configKeys.FREE_PREMIUM) then
return true
end
if player:getPremiumDays() <= config.maxDays then
if player:removeMoney(config.price) then
player:addPremiumDays(config.days)
player:sendTextMessage(MESSAGE_INFO_DESCR, "You have bought " .. config.days .." days of premium account.")
else
player:sendCancelMessage("You don't have enough money, " .. config.maxDays .. " days premium account costs " .. config.price .. " gold coins.")
player:getPosition():sendMagicEffect(CONST_ME_POFF)
end
else
player:sendCancelMessage("You can not buy more than " .. config.maxDays .. " days of premium account.")
player:getPosition():sendMagicEffect(CONST_ME_POFF)
end
return false
end

View File

@@ -0,0 +1,25 @@
local condition = Condition(CONDITION_OUTFIT, CONDITIONID_COMBAT)
condition:setTicks(-1)
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local itemType = ItemType(param)
if itemType:getId() == 0 then
itemType = ItemType(tonumber(param))
if itemType:getId() == 0 then
player:sendCancelMessage("There is no item with that id or name.")
return false
end
end
condition:setOutfit(itemType:getId())
player:addCondition(condition)
return false
end

View File

@@ -0,0 +1,19 @@
local premiumDaysCost = 3
function onSay(player, words, param)
if player:getGroup():getAccess() then
player:setSex(player:getSex() == PLAYERSEX_FEMALE and PLAYERSEX_MALE or PLAYERSEX_FEMALE)
player:sendTextMessage(MESSAGE_INFO_DESCR, "You have changed your sex.")
return false
end
if player:getPremiumDays() >= premiumDaysCost then
player:removePremiumDays(premiumDaysCost)
player:setSex(player:getSex() == PLAYERSEX_FEMALE and PLAYERSEX_MALE or PLAYERSEX_FEMALE)
player:sendTextMessage(MESSAGE_INFO_DESCR, "You have changed your sex for ".. premiumDaysCost .." days of your premium account.")
else
player:sendCancelMessage("You do not have enough premium days, changing sex costs ".. premiumDaysCost .." days of your premium account.")
player:getPosition():sendMagicEffect(CONST_ME_POFF)
end
return false
end

View File

@@ -0,0 +1,15 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local itemCount = cleanMap()
if itemCount > 0 then
player:sendTextMessage(MESSAGE_STATUS_WARNING, "Cleaned " .. itemCount .. " item" .. (itemCount > 1 and "s" or "") .. " from the map.")
end
return false
end

View File

@@ -0,0 +1,17 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
if param == "shutdown" then
Game.setGameState(GAME_STATE_SHUTDOWN)
else
Game.setGameState(GAME_STATE_CLOSED)
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Server is now closed.")
end
return false
end

View File

@@ -0,0 +1,59 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local split = param:split(",")
local itemType = ItemType(split[1])
if itemType:getId() == 0 then
itemType = ItemType(tonumber(split[1]))
if itemType:getId() == 0 then
player:sendCancelMessage("There is no item with that id or name.")
return false
end
end
local keynumber = 0
local count = tonumber(split[2])
if count ~= nil then
if itemType:isStackable() then
count = math.min(10000, math.max(1, count))
elseif itemType:isKey() then
keynumber = count
count = 1
elseif not itemType:hasSubType() then
count = math.min(100, math.max(1, count))
else
count = math.max(1, count)
end
else
count = 1
end
local result = player:addItem(itemType:getId(), count)
if result ~= nil then
if not itemType:isStackable() then
if type(result) == "table" then
for _, item in ipairs(result) do
if itemType:isKey() then
item:setAttribute(ITEM_ATTRIBUTE_KEYNUMBER, keynumber)
end
item:decay()
end
else
if itemType:isKey() then
result:setAttribute(ITEM_ATTRIBUTE_KEYNUMBER, keynumber)
end
result:decay()
end
end
player:getPosition():sendMagicEffect(CONST_ME_MAGIC_GREEN)
end
return false
end

View File

@@ -0,0 +1,62 @@
local function getArticle(str)
return str:find("[AaEeIiOoUuYy]") == 1 and "an" or "a"
end
local function getMonthDayEnding(day)
if day == "01" or day == "21" or day == "31" then
return "st"
elseif day == "02" or day == "22" then
return "nd"
elseif day == "03" or day == "23" then
return "rd"
else
return "th"
end
end
local function getMonthString(m)
return os.date("%B", os.time{year = 1970, month = m, day = 1})
end
function onSay(player, words, param)
local resultId = db.storeQuery("SELECT `id`, `name` FROM `players` WHERE `name` = " .. db.escapeString(param))
if resultId ~= false then
local targetGUID = result.getDataInt(resultId, "id")
local targetName = result.getDataString(resultId, "name")
result.free(resultId)
local str = ""
local breakline = ""
local resultId = db.storeQuery("SELECT `time`, `level`, `killed_by`, `is_player` FROM `player_deaths` WHERE `player_id` = " .. targetGUID .. " ORDER BY `time` DESC")
if resultId ~= false then
repeat
if str ~= "" then
breakline = "\n"
end
local date = os.date("*t", result.getDataInt(resultId, "time"))
local article = ""
local killed_by = result.getDataString(resultId, "killed_by")
if result.getDataInt(resultId, "is_player") == 0 then
article = getArticle(killed_by) .. " "
killed_by = string.lower(killed_by)
end
if date.day < 10 then date.day = "0" .. date.day end
if date.hour < 10 then date.hour = "0" .. date.hour end
if date.min < 10 then date.min = "0" .. date.min end
if date.sec < 10 then date.sec = "0" .. date.sec end
str = str .. breakline .. " " .. date.day .. getMonthDayEnding(date.day) .. " " .. getMonthString(date.month) .. " " .. date.year .. " " .. date.hour .. ":" .. date.min .. ":" .. date.sec .. " Died at Level " .. result.getDataInt(resultId, "level") .. " by " .. article .. killed_by .. "."
until not result.next(resultId)
result.free(resultId)
end
if str == "" then
str = "No deaths."
end
player:popupFYI("Deathlist for player, " .. targetName .. ".\n\n" .. str)
else
player:sendCancelMessage("A player with that name does not exist.")
end
return false
end

View File

@@ -0,0 +1,17 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local position = player:getPosition()
position.z = position.z + 1
local tile = Tile(position)
if tile == nil or tile:getGround() == nil then
player:sendCancelMessage("You cannot teleport there.")
return false
end
player:teleportTo(position)
return false
end

View File

@@ -0,0 +1,28 @@
function onSay(player, words, param)
local party = player:getParty()
if not party then
player:sendCancelMessage("You are not part of a party.")
return false
end
if party:getLeader() ~= player then
player:sendCancelMessage("You are not the leader of the party.")
return false
end
if party:isSharedExperienceActive() then
if player:getCondition(CONDITION_INFIGHT) then
player:sendCancelMessage("You are in fight. Experience sharing not disabled.")
else
party:setSharedExperience(false)
end
else
if player:getCondition(CONDITION_INFIGHT) then
player:sendCancelMessage("You are in fight. Experience sharing not enabled.")
else
party:setSharedExperience(true)
end
end
return false
end

View File

@@ -0,0 +1,22 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GAMEMASTER then
return false
end
local position = player:getPosition()
local isGhost = not player:isInGhostMode()
player:setGhostMode(isGhost)
if isGhost then
player:sendTextMessage(MESSAGE_INFO_DESCR, "You are now invisible.")
position:sendMagicEffect(CONST_ME_POFF)
else
player:sendTextMessage(MESSAGE_INFO_DESCR, "You are visible again.")
position:sendMagicEffect(CONST_ME_TELEPORT)
end
return false
end

View File

@@ -0,0 +1,48 @@
local config = {
['exp'] = {skillKey = 17585, timeKey = 17589},
['skill'] = {skillKey = 17586, timeKey = 17590},
['magic'] = {skillKey = 17587, timeKey = 17591},
['loot'] = {skillKey = 17588, timeKey = 17592} -- TODO
}
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local split = param:split(",")
if split[3] == nil then
player:sendCancelMessage("Insufficient parameters [(exp,skill,loot,magic),percentage,hours].")
return false
end
local skillName = split[1]
local percentage = tonumber(split[2])
local hours = tonumber(split[3])
local globalStorage = config[skillName]
if not globalStorage then
player:sendCancelMessage("Skill name value must be one of the following: exp, skill, loot, magic.")
return false
end
if percentage <= 0 then
player:sendCancelMessage("Percentage value must be higher than 0. For example, 50% means 1.5x higher rate.")
return false
end
if hours <= 0 then
player:sendCancelMessage("Hours value must be higher than 0.")
return false
end
setGlobalStorageValue(globalStorage.skillKey, percentage)
setGlobalStorageValue(globalStorage.timeKey, os.time() + hours * 60 * 60)
broadcastMessage(player:getName() .. " have activated the global " .. percentage .. "% " .. skillName .. " rate boost for next " .. hours .. " " .. (hours == 1 and "hour" or "hours") .. ".", MESSAGE_STATUS_WARNING)
return false
end

View File

@@ -0,0 +1,20 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local split = param:split(",")
local target = Player(split[1])
if target == nil then
player:sendCancelMessage("Player not found.")
return false
end
if target:getGroup():getAccess() then
player:sendCancelMessage("You cannot impersonate this player.")
return false
end
target:say(split[2], TALKTYPE_SAY)
return false
end

View File

@@ -0,0 +1,39 @@
function onSay(player, words, param)
if player:getAccountType() == ACCOUNT_TYPE_NORMAL then
return true
end
local target = Player(param)
if not target then
player:sendCancelMessage("Player not found.")
return false
end
if target:getAccountType() > player:getAccountType() then
player:sendCancelMessage("You can not get info about this player.")
return false
end
local targetIp = target:getIp()
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Name: " .. target:getName())
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Access: " .. (target:getGroup():getAccess() and "1" or "0"))
if player:getGroup():getAccess() then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Level: " .. target:getLevel())
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Magic Level: " .. target:getMagicLevel())
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Speed: " .. target:getSpeed())
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Position: " .. string.format("(%0.5d / %0.5d / %0.3d)", target:getPosition().x, target:getPosition().y, target:getPosition().z))
end
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "IP: " .. Game.convertIpToString(targetIp))
local players = {}
for _, targetPlayer in ipairs(Game.getPlayers()) do
if targetPlayer:getIp() == targetIp and targetPlayer ~= target then
players[#players + 1] = targetPlayer:getName() .. " [" .. targetPlayer:getLevel() .. "]"
end
end
if #players > 0 then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Other players on same IP: " .. table.concat(players, ", ") .. ".")
end
return false
end

View File

@@ -0,0 +1,36 @@
local ipBanDays = 7
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local resultId = db.storeQuery("SELECT `account_id`, `lastip` FROM `players` WHERE `name` = " .. db.escapeString(param))
if resultId == false then
return false
end
local targetIp = result.getDataLong(resultId, "lastip")
result.free(resultId)
local targetPlayer = Player(param)
if targetPlayer then
targetIp = targetPlayer:getIp()
targetPlayer:remove()
end
if targetIp == 0 then
return false
end
resultId = db.storeQuery("SELECT 1 FROM `ip_bans` WHERE `ip` = " .. targetIp)
if resultId ~= false then
result.free(resultId)
return false
end
local timeNow = os.time()
db.query("INSERT INTO `ip_bans` (`ip`, `reason`, `banned_at`, `expires_at`, `banned_by`) VALUES (" ..
targetIp .. ", '', " .. timeNow .. ", " .. timeNow + (ipBanDays * 86400) .. ", " .. player:getGuid() .. ")")
return false
end

View File

@@ -0,0 +1,19 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local target = Player(param)
if target == nil then
player:sendCancelMessage("Player not found.")
return false
end
if target:getGroup():getAccess() then
player:sendCancelMessage("You cannot kick this player.")
return false
end
target:remove()
return false
end

View File

@@ -0,0 +1,75 @@
function onSay(player, words, param)
if Game.getWorldType() == WORLD_TYPE_PVP_ENFORCED then
player:showTextDialog(1998, "Your character has not murders history.", false)
return false
end
local today = os.time()
local skullTicks = player:getPlayerKillerEnd()
local lastDay = 0
local lastWeek = 0
local lastMonth = 0
local egibleMurders = 0
local dayTimestamp = today - (24 * 60 * 60)
local weekTimestamp = today - (7 * 24 * 60 * 60)
local monthTimestamp = today - (30 * 24 * 60 * 60)
local killsDayRedSkull = configManager.getNumber(configKeys.KILLS_DAY_RED_SKULL)
local killsWeekRedSkull = configManager.getNumber(configKeys.KILLS_WEEK_RED_SKULL)
local killsMonthRedSkull = configManager.getNumber(configKeys.KILLS_MONTH_RED_SKULL)
local killsDayBanishment = configManager.getNumber(configKeys.KILLS_DAY_BANISHMENT)
local killsWeekBanishment = configManager.getNumber(configKeys.KILLS_WEEK_BANISHMENT)
local killsMonthBanishment = configManager.getNumber(configKeys.KILLS_MONTH_BANISHMENT)
for _, timestamp in pairs(player:getMurderTimestamps()) do
if timestamp > dayTimestamp then
lastDay = lastDay + 1
end
if timestamp > weekTimestamp then
lastWeek = lastWeek + 1
end
egibleMurders = lastMonth + 1
if timestamp <= monthTimestamp then
egibleMurders = lastMonth
end
lastMonth = egibleMurders
end
local message = ""
message = message .. "Default murders\n"
message = message .. "- Daily kills for red skull " .. killsDayRedSkull .. "\n"
message = message .. "- Weekly kills for red skull " .. killsWeekRedSkull .. "\n"
message = message .. "- Monthly kills for red skull " .. (killsMonthRedSkull >= 99999 and "unlimited" or tostring(killsMonthRedSkull)) .. "\n"
message = message .. "- Daily kills for banishment " .. killsDayBanishment .. "\n"
message = message .. "- Weekly kills for banishment " .. killsWeekBanishment .. "\n"
message = message .. "- Monthly kills for banishment " .. (killsMonthBanishment >= 99999 and "unlimited" or tostring(killsMonthBanishment)) .. "\n"
message = message .. "\n"
message = message .. "Last murders within 24 hours " .. lastDay .. "\n"
message = message .. "Last murders within a week " .. lastDay .. "\n"
message = message .. "Last murders within a month " .. lastDay .. "\n"
message = message .. "\n"
message = message .. "Players you may kill for a red skull:\n"
message = message .. "- Within 24 hours " .. killsDayRedSkull - lastDay .. " murders.\n"
message = message .. "- Within a week " .. killsWeekRedSkull - lastWeek .. " murders.\n"
message = message .. "- Within a month " .. killsMonthRedSkull - lastDay .. " murders.\n"
message = message .. "\n"
message = message .. "Players you may kill for a banishment:\n"
message = message .. "- Within 24 hours " .. killsDayBanishment - lastDay .. " murders.\n"
message = message .. "- Within a week " .. killsWeekBanishment - lastWeek .. " murders.\n"
message = message .. "- Within a month " .. killsMonthBanishment - lastDay .. " murders.\n"
player:showTextDialog(1998, message, false)
return false
end

View File

@@ -0,0 +1,21 @@
function onSay(player, words, param)
local position = player:getPosition()
local tile = Tile(position)
local house = tile and tile:getHouse()
if house == nil then
player:sendCancelMessage("You are not inside a house.")
position:sendMagicEffect(CONST_ME_POFF)
return false
end
if house:getOwnerGuid() ~= player:getGuid() then
player:sendCancelMessage("You are not the owner of this house.")
position:sendMagicEffect(CONST_ME_POFF)
return false
end
house:setOwnerGuid(0)
player:sendTextMessage(MESSAGE_INFO_DESCR, "You have successfully left your house.")
position:sendMagicEffect(CONST_ME_POFF)
return false
end

View File

@@ -0,0 +1,15 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local lookType = tonumber(param)
if lookType >= 0 and lookType ~= 1 and lookType ~= 135 and lookType ~= 411 and lookType ~= 415 and lookType ~= 424 and (lookType <= 160 or lookType >= 192) and lookType ~= 439 and lookType ~= 440 and lookType ~= 468 and lookType ~= 469 and (lookType < 474 or lookType > 485) and lookType ~= 501 and lookType ~= 518 and lookType ~= 519 and lookType ~= 520 and lookType ~= 524 and lookType ~= 525 and lookType ~= 536 and lookType ~= 543 and lookType ~= 549 and lookType ~= 576 and lookType ~= 581 and lookType ~= 582 and lookType ~= 597 and lookType ~= 616 and lookType ~= 623 and lookType ~= 625 and (lookType <= 637 or lookType >= 644) and (lookType <= 644 or lookType >= 647) and (lookType <= 651 or lookType >= 664) and lookType <= 699 then
local playerOutfit = player:getOutfit()
playerOutfit.lookType = lookType
player:setOutfit(playerOutfit)
else
player:sendCancelMessage("A look type with that id does not exist.")
end
return false
end

View File

@@ -0,0 +1,33 @@
local config = {
['exp'] = {skillKey = 17585, timeKey = 17589},
['skill'] = {skillKey = 17586, timeKey = 17590},
['magic'] = {skillKey = 17587, timeKey = 17591},
['loot'] = {skillKey = 17588, timeKey = 17592} -- TODO
}
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local i = 0
local function generateMonsterLoad()
if (i <= 1000) then
Game.createMonster("cyclops", {x = 32316, y = 31942, z = 7})
local monster = Game.isMonsterThere({x = 32316, y = 31942, z = 7}, "cyclops")
monster:addHealth(-monster:getMaxHealth())
addEvent(generateMonsterLoad, 1000)
i = i + 1
print(i)
end
end
addEvent(generateMonsterLoad, 1000)
return false
end

View File

@@ -0,0 +1,8 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
player:getPosition():sendMagicEffect(tonumber(param))
return false
end

View File

@@ -0,0 +1,40 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Multiclient Check List:")
local ipList = {}
local players = Game.getPlayers()
for i = 1, #players do
local tmpPlayer = players[i]
local ip = tmpPlayer:getIp()
if ip ~= 0 then
local list = ipList[ip]
if not list then
ipList[ip] = {}
list = ipList[ip]
end
list[#list + 1] = tmpPlayer
end
end
for ip, list in pairs(ipList) do
local listLength = #list
if listLength > 1 then
local tmpPlayer = list[1]
local message = ("%s: %s [%d]"):format(Game.convertIpToString(ip), tmpPlayer:getName(), tmpPlayer:getLevel())
for i = 2, listLength do
tmpPlayer = list[i]
message = ("%s, %s [%d]"):format(message, tmpPlayer:getName(), tmpPlayer:getLevel())
end
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, message .. ".")
end
end
return false
end

View File

@@ -0,0 +1,114 @@
local distanceBetweenPositionsX = 8
local distanceBetweenPositionsY = 8
local addEventDelay = 500
local teleportsPerEvent = 1
local maxEventExecutionTime = 2000
local function teleportToClosestPosition(player, x, y, z)
-- direct to position
local tile = Tile(x, y, z)
if not tile or not tile:getGround() or tile:hasFlag(TILESTATE_TELEPORT) or not player:teleportTo(tile:getPosition()) then
for distance = 1, 3 do
-- try to find some close tile
for changeX = -distance, distance, distance do
for changeY = -distance, distance, distance do
tile = Tile(x + changeX, y + changeY, z)
if tile and tile:getGround() and not tile:hasFlag(TILESTATE_TELEPORT) and player:teleportTo(tile:getPosition()) then
return true
end
end
end
end
return false
end
return true
end
local function sendScanProgress(player, minX, maxX, minY, maxY, x, y, z, lastProgress)
local progress = math.floor(((y - minY + (((x - minX) / (maxX - minX)) * distanceBetweenPositionsY)) / (maxY - minY)) * 100)
if progress ~= lastProgress then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Scan progress: ~' .. progress .. '%')
end
return progress
end
local function minimapScan(cid, minX, maxX, minY, maxY, x, y, z, lastProgress)
local player = Player(cid)
if not player then
--print('Minimap scan stopped - player logged out', cid, minX, maxX, minY, maxY, x, y, z)
return
end
local scanStartTime = os.mtime()
local teleportsDone = 0
while true do
if scanStartTime + maxEventExecutionTime < os.mtime() then
lastProgress = sendScanProgress(player, minX, maxX, minY, maxY, x, y, z, lastProgress)
addEvent(minimapScan, addEventDelay, cid, minX, maxX, minY, maxY, x, y, z, lastProgress)
break
end
x = x + distanceBetweenPositionsX
if x > maxX then
x = minX
y = y + distanceBetweenPositionsY
if y > maxY then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Scan finished: ' .. os.time())
--print('Minimap scan complete', player:getName(), minX, maxX, minY, maxY, x, y, z)
break
end
end
if teleportToClosestPosition(player, x, y, z) then
teleportsDone = teleportsDone + 1
lastProgress = sendScanProgress(player, minX, maxX, minY, maxY, x, y, z, lastProgress)
--print('Minimap scan teleport', player:getName(), minX, maxX, minY, maxY, x, y, z, progress, teleportsDone)
if teleportsDone == teleportsPerEvent then
addEvent(minimapScan, addEventDelay, cid, minX, maxX, minY, maxY, x, y, z, progress)
break
end
end
end
end
local function minimapStart(player, minX, maxX, minY, maxY, x, y, z)
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Scan started: ' .. os.time())
--print('Minimap scan start', player:getName(), minX, maxX, minY, maxY, x, y, z)
minimapScan(player:getId(), minX, maxX, minY, maxY, minX - 5, minY, z)
end
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local positions = param:split(',')
if #positions ~= 5 then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Command requires 5 parameters: /minimap minX, maxX, minY, maxY, z')
return false
end
for key, position in pairs(positions) do
local value = tonumber(position)
if not value then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'Invalid parameter ' .. key .. ': ' .. position)
return false
end
positions[key] = value
end
minimapStart(player, positions[1], positions[2], positions[3], positions[4], positions[1] - distanceBetweenPositionsX, positions[3], positions[5])
return false
end

View File

@@ -0,0 +1,31 @@
local maxPlayersPerMessage = 10
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local hasAccess = player:getGroup():getAccess()
local players = Game.getPlayers()
local onlineList = {}
for _, targetPlayer in ipairs(players) do
if hasAccess or not targetPlayer:isInGhostMode() then
table.insert(onlineList, ("%s [%d]"):format(targetPlayer:getName(), targetPlayer:getLevel()))
end
end
local playersOnline = #onlineList
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, ("%d players online."):format(playersOnline))
for i = 1, playersOnline, maxPlayersPerMessage do
local j = math.min(i + maxPlayersPerMessage - 1, playersOnline)
local msg = table.concat(onlineList, ", ", i, j) .. "."
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, msg)
end
return false
end

View File

@@ -0,0 +1,13 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
Game.setGameState(GAME_STATE_NORMAL)
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Server is now open.")
return false
end

View File

@@ -0,0 +1,30 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local tile = Tile(player:getPosition())
local house = tile and tile:getHouse()
if house == nil then
player:sendCancelMessage("You are not inside a house.")
return false
end
if param == "" or param == "none" then
house:setOwnerGuid(0)
return false
end
local targetPlayer = Player(param)
if targetPlayer == nil then
player:sendCancelMessage("Player not found.")
return false
end
house:setOwnerGuid(targetPlayer:getGuid())
return false
end

View File

@@ -0,0 +1,308 @@
local weaponSkillsConfig = {
[WEAPON_SWORD] = SKILL_SWORD,
[WEAPON_CLUB] = SKILL_CLUB,
[WEAPON_AXE] = SKILL_AXE,
[WEAPON_DISTANCE] = SKILL_DISTANCE
}
local skillNameConfig = {
[SKILL_SWORD] = "Sword Fighting",
[SKILL_CLUB] = "Club Fighting",
[SKILL_AXE] = "Axe Fighting",
[SKILL_DISTANCE] = "Distance Fighting",
[SKILL_FIST] = "Fist Fighting"
}
local function ltrim(s)
if s == nil then
return s
end
return s:match'^%s*(.*)'
end
local function getWeapon(player)
local itemLeft = player:getSlotItem(CONST_SLOT_LEFT)
if itemLeft and itemLeft:getType():getWeaponType() ~= WEAPON_NONE and itemLeft:getType():getWeaponType() ~= WEAPON_SHIELD and itemLeft:getType():getWeaponType() ~= WEAPON_AMMO and itemLeft:getType():getWeaponType() ~= WEAPON_WAND then
return itemLeft:getType()
end
local itemRight = player:getSlotItem(CONST_SLOT_RIGHT)
if itemRight and itemRight:getType():getWeaponType() ~= WEAPON_NONE and itemRight:getType():getWeaponType() ~= WEAPON_SHIELD and itemRight:getType():getWeaponType() ~= WEAPON_AMMO and itemRight:getType():getWeaponType() ~= WEAPON_WAND then
return itemRight:getType()
end
end
local function getAmmunition(player)
local item = player:getSlotItem(CONST_SLOT_AMMO)
if item and item:getType():getWeaponType() == WEAPON_AMMO then
return item:getType()
end
end
local function getAttack(weapon, ammunition)
local attack = 7
if weapon ~= nil then
attack = weapon:getAttack()
if weapon:getWeaponType() == WEAPON_DISTANCE and weapon:getAmmoType() ~= 0 then
if ammunition ~= nil and ammunition:getAmmoType() == weapon:getAmmoType() then
attack = attack + ammunition:getAttack()
end
end
end
return attack
end
local function getDamageFormula(attack, attackSkill, fightMode)
local damage = attack
if fightMode == FIGHTMODE_ATTACK then
damage = math.floor(damage + 2 * damage / 10)
elseif fightMode == FIGHTMODE_DEFENSE then
damage = math.floor(damage - 4 * damage / 10)
end
local formula = math.floor((5 * (attackSkill) + 50) * damage)
return formula
end
local function getDamage(attack, attackSkill, fightMode, random1, random2)
local formula = getDamageFormula(attack, attackSkill, fightMode)
local randResult = math.floor(random1 % 100);
local damage = -math.floor((math.ceil(formula * ((random2 % 100 + randResult) / 2) / 10000.)));
return damage
end
local function setVocationDamageIncrease(vocationId, damage)
if vocationId == 4 or vocationId == 8 then
local knightCloseAttackDamageIncreasePercent = configManager.getNumber(configKeys.KNIGHT_CLOSE_ATTACK_DAMAGE_INCREASE_PERCENT)
if knightCloseAttackDamageIncreasePercent ~= -1 then
damage = math.floor(damage + damage * knightCloseAttackDamageIncreasePercent / 100);
end
elseif vocationId == 3 or vocationId == 7 then
local paladinRangeAttackDamageIncreasePercent = configManager.getNumber(configKeys.PALADIN_RANGE_ATTACK_DAMAGE_INCREASE_PERCENT)
if paladinRangeAttackDamageIncreasePercent ~= -1 then
damage = math.floor(damage + damage * paladinRangeAttackDamageIncreasePercent / 100);
end
end
return damage
end
local function getDefense(creature, random1, random2)
local totalDefense = creature:getType():getDefense() + 1
local defenseSkill = creature:getType():getSkill()
local formula = math.floor((5 * (defenseSkill) + 50) * totalDefense)
local randresult = math.floor(random1 % 100)
return math.floor(formula * ((random2 % 100 + randresult) / 2) / 10000.)
end
local function rshift(x, by)
return math.floor(x / 2 ^ by)
end
local function getArmor(creature, rand)
local armor = creature:getType():getArmor()
if armor > 1 then
return rand % rshift(armor, 1) + rshift(armor, 1);
end
return armor
end
local function setPhysicalDamageBlock(creature, isCloseRange, damage, random1, random2, randomArmor)
if bit.band(creature:getType():getCombatImmunities(), COMBAT_PHYSICALDAMAGE) == COMBAT_PHYSICALDAMAGE then
return 0
end
if isCloseRange then
damage = damage + getDefense(creature, random1, random2)
end
if damage >= 0 then
return 0
end
if damage < 0 then
damage = damage + getArmor(creature, randomArmor)
if damage >= 0 then
return 0
end
end
return damage
end
local function isThrowableHit(weapon, ammunition, skillValue, rand)
local distance = 75 -- we consider distance is always the best
local hitChance = 75 -- throwables and such
if weapon:getAmmoType() ~= 0 then
hitChance = 90 -- bows and crossbows
if ammunition == nil or ammunition:getAmmoType() ~= weapon:getAmmoType() then
hitChance = -1 -- no ammo or invalid ammo
end
end
if rand % distance <= skillValue then
return rand % 100 <= hitChance
end
return false
end
local function getTotalDamage(creature, weapon, ammunition, vocation, attack, skillValue, fightMode)
local damage = setVocationDamageIncrease(vocation:getId(), getDamage(attack, skillValue, fightMode, os.rand(), os.rand()))
local minDamage = setVocationDamageIncrease(vocation:getId(), getDamage(attack, skillValue, fightMode, 0, 0))
local maxDamage = setVocationDamageIncrease(vocation:getId(), getDamage(attack, skillValue, fightMode, 99, 99))
if weapon ~= nil and weapon:getWeaponType() == WEAPON_DISTANCE then
if isThrowableHit(weapon, ammunition, skillValue, os.rand()) then
damage = setPhysicalDamageBlock(creature, false, damage, os.rand(), os.rand(), os.rand())
else
damage = 0
end
minDamage = 0
if isThrowableHit(weapon, ammunition, skillValue, 0) then
maxDamage = setPhysicalDamageBlock(creature, false, maxDamage, 0, 0, 0)
else
maxDamage = 0
end
else
damage = setPhysicalDamageBlock(creature, true, damage, os.rand(), os.rand(), os.rand())
minDamage = setPhysicalDamageBlock(creature, true, minDamage, 99, 99, rshift(creature:getType():getArmor(), 1) + 1)
maxDamage = setPhysicalDamageBlock(creature, true, maxDamage, 0, 0, 0)
end
local container = {}
container[0] = damage
container[1] = minDamage
container[2] = maxDamage
return container
end
function onSay(player, words, param)
local split = param:split(",")
local creatureName = ltrim(split[1])
local skillValueNumber = ltrim(split[2])
local vocationName = ltrim(split[3])
local weaponName = ltrim(split[4])
local ammunitionName = ltrim(split[5])
local creature = Creature("Troll")
if creatureName ~= nil then
creature = Creature(creatureName)
if creature == nil then
player:sendCancelMessage("The monster does not exist.")
return false
end
else
creatureName = creature:getName()
end
local weapon = getWeapon(player)
if weaponName ~= nil then
weapon = ItemType(weaponName)
if weapon == nil or weapon:getWeaponType() == WEAPON_NONE or weapon:getWeaponType() == WEAPON_SHIELD or weapon:getWeaponType() == WEAPON_AMMO or weapon:getWeaponType() == WEAPON_WAND then
player:sendCancelMessage("The weapon does not exist.")
return false
end
else
if weapon ~= nil then
weaponName = weapon:getName()
end
end
local ammunition = getAmmunition(player)
if ammunitionName ~= nil then
ammunition = ItemType(ammunitionName)
if ammunition == nil or ammunition:getWeaponType() ~= WEAPON_AMMO then
player:sendCancelMessage("The ammunition does not exist.")
return false
end
else
if ammunition ~= nil then
ammunitionName = ammunition:getName()
end
end
local skillType = SKILL_FIST
local attack = getAttack(weapon, ammunition)
if weapon ~= nil then
skillType = weaponSkillsConfig[weapon:getWeaponType()]
end
local skillValue = player:getSkillLevel(skillType)
if skillValueNumber ~= nil then
if tonumber(skillValueNumber) > 0 then
if tonumber(skillValueNumber) <= 300 then
skillValue = tonumber(skillValueNumber)
else
player:sendCancelMessage("The skill value has to be no bigger than 300.")
return false
end
else
player:sendCancelMessage("The skill value has to be a number and greater than zero.")
return false
end
end
local vocation = player:getVocation()
if vocationName ~= nil then
vocation = Vocation(vocationName)
if vocation == nil then
player:sendCancelMessage("The vocation does not exist.")
return false
end
else
vocationName = vocation:getName()
end
local commandStr = "Executing command: !physicaldamage " .. creatureName .. ", " .. skillValue .. ", " .. vocationName .. ""
if weapon ~= nil then
commandStr = commandStr .. ", " .. weaponName
end
if ammunition ~= nil and weapon ~= nil and weapon:getWeaponType() == WEAPON_DISTANCE then
commandStr = commandStr .. ", " .. ammunitionName
end
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, commandStr)
local offensiveDamageContainer = getTotalDamage(creature, weapon, ammunition, vocation, attack, skillValue, FIGHTMODE_ATTACK)
local balancedDamageContainer = getTotalDamage(creature, weapon, ammunition, vocation, attack, skillValue, FIGHTMODE_BALANCED)
local defensiveDamageContainer = getTotalDamage(creature, weapon, ammunition, vocation, attack, skillValue, FIGHTMODE_DEFENSE)
local message = ""
message = message .. "Vocation: " .. vocation:getName() .. "\n"
message = message .. "Skill Name: " .. skillNameConfig[skillType] .. ", Skill Value: " .. skillValue .. "\n"
message = message .. "Weapon: " .. (weaponName or 'none') .. " (atk: " .. attack .. ")\n"
message = message .. "Creature: " .. creatureName .. " (arm: " .. creature:getType():getArmor() .. ", def: " .. creature:getType():getDefense() .. ", skill: " .. creature:getType():getSkill() .. ")\n"
message = message .. "\nOffensive Fighting Damage\n"
message = message .. "Min: " .. offensiveDamageContainer[1] .. ", Max: " .. offensiveDamageContainer[2] .. "\n"
message = message .. "\nBalanced Fighting Damage\n"
message = message .. "Min: " .. balancedDamageContainer[1] .. ", Max: " .. balancedDamageContainer[2] .. "\n"
message = message .. "\nDefensive Fighting Damage\n"
message = message .. "Min: " .. defensiveDamageContainer[1] .. ", Max: " .. defensiveDamageContainer[2] .. "\n"
message = message .. "\nFirst 100 Hits Damage Simulator in Offensive Fighting\n"
local creatureHealth = creature:getType():getMaxHealth()
local creatureHitsTillDeath = 1
for i=1,100 do
local damageContainer = getTotalDamage(creature, weapon, ammunition, vocation, attack, skillValue, FIGHTMODE_ATTACK)
message = message .. "Hit: " .. i .. ", Damage: " .. damageContainer[0] .. "\n"
creatureHealth = creatureHealth + damageContainer[0]
if creatureHealth > 0 then
creatureHitsTillDeath = creatureHitsTillDeath + 1
end
end
if creatureHealth <= 0 then
message = message .. "\nIt would take you approximately " .. creatureHitsTillDeath .. " hits to slain " .. creature:getName() .. ".\n"
else
message = message .. "\nIt would take you more than 100 hits to slain " .. creature:getName() .. ".\n"
end
player:showTextDialog(weapon and weapon:getId() or 2950, message, false)
return false
end

View File

@@ -0,0 +1,20 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local position = player:getPosition()
local monster = Game.createMonster(param, position)
if monster ~= nil then
monster:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
position:sendMagicEffect(CONST_ME_MAGIC_RED)
else
player:sendCancelMessage("There is not enough room.")
position:sendMagicEffect(CONST_ME_POFF)
end
return false
end

View File

@@ -0,0 +1,20 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local position = player:getPosition()
local npc = Game.createNpc(param, position)
if npc ~= nil then
npc:setMasterPos(position)
position:sendMagicEffect(CONST_ME_MAGIC_RED)
else
player:sendCancelMessage("There is not enough room.")
position:sendMagicEffect(CONST_ME_POFF)
end
return false
end

View File

@@ -0,0 +1,20 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local position = player:getPosition()
local monster = Game.createMonster(param, position)
if monster ~= nil then
monster:setMaster(player)
position:sendMagicEffect(CONST_ME_MAGIC_RED)
else
player:sendCancelMessage("There is not enough room.")
position:sendMagicEffect(CONST_ME_POFF)
end
return false
end

View File

@@ -0,0 +1,10 @@
function onSay(player, words, param)
if player:getGroup():getAccess() and param ~= "" then
local split = param:split(",")
player:teleportTo(Position(split[1], split[2], split[3]))
else
local position = player:getPosition()
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Your current position is: " .. position.x .. ", " .. position.y .. ", " .. position.z .. ".")
end
return false
end

View File

@@ -0,0 +1,34 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local searchItemId = tonumber(param)
for _, house in pairs(Game.getHouses()) do
for _, tile in pairs(house:getTiles()) do
for _, item in pairs(tile:getItems()) do
if item ~= nil then
local isFound = false
if item:isContainer() then
local items = item:getItemsById(searchItemId)
isFound = #items > 0
else
isFound = item:getId() == searchItemId
end
if isFound then
local position = item:getPosition()
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Item position is: " .. position.x .. ", " .. position.y .. ", " .. position.z .. ".")
end
end
end
end
end
return false
end

View File

@@ -0,0 +1,13 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
Game.startRaid(param)
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Raid with name " .. param .. " started.")
return false
end

View File

@@ -0,0 +1,68 @@
local reloadTypes = {
["all"] = { targetType = RELOAD_TYPE_ALL, name = "all" },
["action"] = { targetType = RELOAD_TYPE_ACTIONS, name = "actions" },
["actions"] = { targetType = RELOAD_TYPE_ACTIONS, name = "actions" },
["chat"] = { targetType = RELOAD_TYPE_CHAT, name = "chatchannels" },
["channel"] = { targetType = RELOAD_TYPE_CHAT, name = "chatchannels" },
["chatchannels"] = { targetType = RELOAD_TYPE_CHAT, name = "chatchannels" },
["config"] = { targetType = RELOAD_TYPE_CONFIG, name = "config" },
["configuration"] = { targetType = RELOAD_TYPE_CONFIG, name = "config" },
["creaturescript"] = { targetType = RELOAD_TYPE_CREATURESCRIPTS, name = "creature scripts" },
["creaturescripts"] = { targetType = RELOAD_TYPE_CREATURESCRIPTS, name = "creature scripts" },
["events"] = { targetType = RELOAD_TYPE_EVENTS, name = "events" },
["global"] = { targetType = RELOAD_TYPE_GLOBAL, name = "global.lua" },
["globalevent"] = { targetType = RELOAD_TYPE_GLOBALEVENTS, name = "globalevents" },
["globalevents"] = { targetType = RELOAD_TYPE_GLOBALEVENTS, name = "globalevents" },
["items"] = { targetType = RELOAD_TYPE_ITEMS, name = "items" },
["monster"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" },
["monsters"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" },
["move"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
["movement"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
["movements"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
["npc"] = { targetType = RELOAD_TYPE_NPCS, name = "npcs" },
["npcs"] = { targetType = RELOAD_TYPE_NPCS, name = "npcs" },
["quest"] = { targetType = RELOAD_TYPE_QUESTS, name = "quests" },
["quests"] = { targetType = RELOAD_TYPE_QUESTS, name = "quests" },
["raid"] = { targetType = RELOAD_TYPE_RAIDS, name = "raids" },
["raids"] = { targetType = RELOAD_TYPE_RAIDS, name = "raids" },
["spell"] = { targetType = RELOAD_TYPE_SPELLS, name = "spells" },
["spells"] = { targetType = RELOAD_TYPE_SPELLS, name = "spells" },
["talk"] = { targetType = RELOAD_TYPE_TALKACTIONS, name = "talk actions" },
["talkaction"] = { targetType = RELOAD_TYPE_TALKACTIONS, name = "talk actions" },
["talkactions"] = { targetType = RELOAD_TYPE_TALKACTIONS, name = "talk actions" }
}
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GAMEMASTER then
return false
end
local reloadType = reloadTypes[param and param:lower()]
if not reloadType then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Reload type not found.")
return false
end
Game.reload(reloadType.targetType)
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, string.format("Reloaded %s.", reloadType.name))
return true
end

View File

@@ -0,0 +1,22 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
local position = player:getPosition()
local tile = Tile(position)
local house = tile and tile:getHouse()
if house == nil then
player:sendCancelMessage("You are not inside a house.")
position:sendMagicEffect(CONST_ME_POFF)
return false
end
house:setOwnerGuid(0)
player:sendTextMessage(MESSAGE_INFO_DESCR, "You have successfully removed this house.")
return false
end

View File

@@ -0,0 +1,27 @@
function onSay(player, words, param)
if player:getAccountType() <= ACCOUNT_TYPE_TUTOR then
return true
end
local resultId = db.storeQuery("SELECT `name`, `account_id`, (SELECT `type` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `account_type` FROM `players` WHERE `name` = " .. db.escapeString(param))
if resultId == false then
player:sendCancelMessage("A player with that name does not exist.")
return false
end
if result.getDataInt(resultId, "account_type") ~= ACCOUNT_TYPE_TUTOR then
player:sendCancelMessage("You can only demote a tutor to a normal player.")
return false
end
local target = Player(param)
if target ~= nil then
target:setAccountType(ACCOUNT_TYPE_NORMAL)
else
db.query("UPDATE `accounts` SET `type` = " .. ACCOUNT_TYPE_NORMAL .. " WHERE `id` = " .. result.getDataInt(resultId, "account_id"))
end
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have demoted " .. result.getDataString(resultId, "name") .. " to a normal player.")
result.free(resultId)
return false
end

View File

@@ -0,0 +1,33 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local position = player:getPosition()
position:getNextPosition(player:getDirection())
local tile = Tile(position)
if not tile then
player:sendCancelMessage("Object not found.")
return false
end
local thing = tile:getTopVisibleThing(player)
if not thing then
player:sendCancelMessage("Thing not found.")
return false
end
if thing:isCreature() then
thing:remove()
elseif thing:isItem() then
if thing == tile:getGround() then
player:sendCancelMessage("You may not remove a ground tile.")
return false
end
thing:remove(tonumber(param) or -1)
end
position:sendMagicEffect(CONST_ME_MAGIC_RED)
return false
end

View File

@@ -0,0 +1,23 @@
function onSay(player, words, param)
local function timeSave(delay, msg)
broadcastMessage(msg, MESSAGE_STATUS_WARNING)
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, msg)
addEvent(saveServer, delay)
end
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
if isNumber(param) then
local delay = tonumber(param) * 1000
timeSave(delay, "Saving server in " .. tonumber(param) .. " seconds.")
else
saveServer()
end
end

View File

@@ -0,0 +1,19 @@
function onSay(player, words, param)
local tradePartner = Player(param)
if not tradePartner or tradePartner == player then
player:sendCancelMessage("Trade player not found.")
return false
end
local house = player:getTile():getHouse()
if not house then
player:sendCancelMessage("You must stand in your house to initiate the trade.")
return false
end
local returnValue = house:startTrade(player, tradePartner)
if returnValue ~= RETURNVALUE_NOERROR then
player:sendCancelMessage(returnValue)
end
return false
end

View File

@@ -0,0 +1,279 @@
local area = {
fromPos = {x = 33140, y = 32859, z = 7},
toPos = {x = 33155, y = 32874, z = 4}
}
local availablePlayerTeleportPositions = {
Position(33139, 32859, 7),
Position(33139, 32860, 7),
Position(33139, 32861, 7),
Position(33139, 32862, 7),
Position(33139, 32863, 7),
Position(33139, 32864, 7),
Position(33139, 32865, 7),
Position(33139, 32866, 7),
Position(33139, 32867, 7),
Position(33139, 32868, 7),
Position(33139, 32869, 7),
Position(33139, 32870, 7),
Position(33139, 32871, 7),
Position(33139, 32872, 7),
Position(33139, 32873, 7),
Position(33139, 32874, 7),
Position(33139, 32875, 7),
Position(33140, 32875, 7),
Position(33143, 32875, 7),
Position(33144, 32875, 7),
Position(33146, 32875, 7),
Position(33148, 32875, 7),
Position(33149, 32875, 7),
Position(33150, 32875, 7),
Position(33155, 32875, 7),
Position(33156, 32875, 7),
Position(33156, 32874, 7),
Position(33156, 32873, 7),
Position(33156, 32872, 7),
Position(33156, 32871, 7),
Position(33156, 32870, 7),
Position(33156, 32869, 7),
Position(33156, 32868, 7),
Position(33156, 32867, 7),
Position(33156, 32866, 7),
Position(33156, 32865, 7),
Position(33156, 32864, 7),
Position(33156, 32863, 7),
Position(33156, 32862, 7),
Position(33156, 32861, 7),
Position(33156, 32860, 7),
Position(33156, 32859, 7),
Position(33156, 32858, 7)
}
local downstairsIds = {451, 466, 465, 467}
local earthquakeEffects = {CONST_ME_POFF, CONST_ME_EXPLOSIONHIT, CONST_ME_EXPLOSIONAREA, CONST_ME_FIREAREA, CONST_ME_ENERGYHIT, CONST_ME_BLOCKHIT}
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
if player:getAccountType() < ACCOUNT_TYPE_GOD then
return false
end
teleportPlayersToSerpentineTower()
addEvent(wave1, 20000)
addEvent(wave2, 40000)
addEvent(wave3, 100000)
addEvent(wave4, 160000)
addEvent(wave5, 170000)
addEvent(wave6, 175000)
addEvent(wave7, 180000)
addEvent(wave8, 195000)
addEvent(wave9, 205000)
return false
end
function teleportPlayersToSerpentineTower()
for _, player in ipairs(Game.getPlayers()) do
player:setStorageValue(17596, 1)
local teleportPosition = availablePlayerTeleportPositions[math.random(#availablePlayerTeleportPositions)]
player:teleportTo(teleportPosition)
player:getPosition():sendMonsterSay("accersi " .. player:getName())
end
end
function wave1()
broadcastMessage("LOOK AT MY EYES! ... THE EYES! ... LET ME OUT! ...", MESSAGE_STATUS_WARNING)
earthquakeTower(area.fromPos, area.toPos)
end
function wave2()
broadcastMessage("Ankrahmun: The Academy of Magic Arts are reporting that Ankrahmun city is experiencing issues! Please stay safe in the protection zones, NOW!", MESSAGE_STATUS_WARNING)
earthquakeTower(area.fromPos, area.toPos)
end
function wave3()
broadcastMessage("Ankrahmun: All Tibianus PEOPLE. THIS IS NOT A PRACTICE. Leave our city NOW!", MESSAGE_STATUS_WARNING)
earthquakeTower(area.fromPos, area.toPos)
end
function wave4()
broadcastMessage("THE EYES ARE EVERYWHERE!", MESSAGE_STATUS_WARNING)
earthquakeTower(area.fromPos, area.toPos)
end
function wave5()
Position(33149, 32868, 7):sendMonsterSay("accersi Tothdral")
local tothdral = Creature("Tothdral")
tothdral:teleportTo(Position(33149, 32867, 7))
tothdral:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
end
function wave6()
Position(33147, 32870, 7):sendMonsterSay("The Serpentine Tower Secret Is Real!")
Position(33151, 32870, 7):sendMonsterSay("The Serpentine Tower Secret Is Real!")
Position(33151, 32866, 7):sendMonsterSay("The Serpentine Tower Secret Is Real!")
Position(33147, 32866, 7):sendMonsterSay("The Serpentine Tower Secret Is Real!")
local tile = Tile(Position(33149, 32868, 7))
if tile then
local obelisk = tile:getItemById(2199)
if obelisk ~= nil then
obelisk:getPosition():sendMagicEffect(CONST_ME_TELEPORT)
obelisk:remove()
local hole = Game.createItem(5731, 1, tile:getPosition())
hole:setAttribute(ITEM_ATTRIBUTE_MOVEMENTID, 17596)
end
end
end
function wave7()
Position(33147, 32870, 7):sendMonsterSay("The Serpentine Tower Secret Is Real!")
Position(33151, 32870, 7):sendMonsterSay("The Serpentine Tower Secret Is Real!")
Position(33151, 32866, 7):sendMonsterSay("The Serpentine Tower Secret Is Real!")
Position(33147, 32866, 7):sendMonsterSay("The Serpentine Tower Secret Is Real!")
Position(33149, 32868, 7):sendMonsterSay("LET ME OUT!")
end
function wave8()
broadcastMessage("Ankrahmun: WE ARE LOST FOREVER!", MESSAGE_STATUS_WARNING)
for xx = area.fromPos.x, area.toPos.x do
for yy = area.fromPos.y, area.toPos.y do
local position = Position(xx, yy, 7)
local tile = Tile(position)
if tile then
local ground = tile:getGround()
if ground ~= nil and ground:getId() == 231 then
ground:transform(2144)
end
end
end
end
Game.createItem(2199, 1, Position(33140, 32859, 7))
Position(33140, 32859, 7):sendMagicEffect(CONST_ME_TELEPORT)
Game.createItem(2199, 1, Position(33140, 32874, 7))
Position(33140, 32874, 7):sendMagicEffect(CONST_ME_TELEPORT)
Game.createItem(2199, 1, Position(33155, 32874, 7))
Position(33155, 32874, 7):sendMagicEffect(CONST_ME_TELEPORT)
Game.createItem(2199, 1, Position(33155, 32859, 7))
Position(33155, 32859, 7):sendMagicEffect(CONST_ME_TELEPORT)
Game.createItem(2199, 1, Position(33149, 32863, 7))
Position(33149, 32863, 7):sendMagicEffect(CONST_ME_TELEPORT)
Game.createItem(2199, 1, Position(33153, 32866, 7))
Position(33153, 32866, 7):sendMagicEffect(CONST_ME_TELEPORT)
Game.createItem(2199, 1, Position(33153, 32871, 7))
Position(33153, 32871, 7):sendMagicEffect(CONST_ME_TELEPORT)
Game.createItem(2199, 1, Position(33149, 32873, 7))
Position(33149, 32873, 7):sendMagicEffect(CONST_ME_TELEPORT)
Game.createItem(2199, 1, Position(33145, 32871, 7))
Position(33145, 32871, 7):sendMagicEffect(CONST_ME_TELEPORT)
Game.createItem(2199, 1, Position(33145, 32866, 7))
Position(33145, 32866, 7):sendMagicEffect(CONST_ME_TELEPORT)
end
function wave9()
broadcastMessage("Ankrahmun: PROTEGO MAXIMA!", MESSAGE_STATUS_WARNING)
for _, player in ipairs(Game.getPlayers()) do
player:teleportTo(player:getTown():getTemplePosition())
end
end
function earthquakeTower(frompos, topos)
for zz = frompos.z, topos.z, -1 do
if zz == 6 then
topos.x = topos.x + 1
topos.y = topos.y + 1
end
for xx = frompos.x, topos.x do
for yy = frompos.y, topos.y do
local position = Position(xx, yy, zz)
removeFloorItems(position)
copyHigherFloorItems(position)
end
end
if zz == 6 then
topos.x = topos.x - 1
topos.y = topos.y - 1
end
end
end
function removeFloorItems(position)
local tile = Tile(position)
if tile then
-- If any creature is in area then teleport it to safe zone to properly work with tile items
local creature = tile:getTopCreature()
if creature then
local teleportPosition = availablePlayerTeleportPositions[math.random(#availablePlayerTeleportPositions)]
creature:teleportTo(teleportPosition)
creature:getPosition():sendMonsterSay("The Gods Protecting You!")
Game.sendMagicEffect(teleportPosition, 11)
if creature:isPlayer() then
if creature:getStorageValue(17596) ~= 2 then
creature:setStorageValue(17596, 2)
end
end
end
local currentFloorItem = tile:getItemByType(0)
while currentFloorItem ~= nil do
currentFloorItem:remove()
currentFloorItem = tile:getItemByType(0)
end
local items = tile:getItems()
if items ~= nil then
for _, item in pairs(items) do
item:remove()
end
end
-- Create sand floor only for ground level
if position.z == 7 then
Game.createTile(position)
Game.createItem(231, 1, position)
end
end
end
function copyHigherFloorItems(position)
local higherFloorPosition = {x = position.x, y = position.y, z = position.z - 1}
local tile = Tile(higherFloorPosition)
if tile then
local higherFloorItem = tile:getItemByType(0)
while(higherFloorItem ~= nil) do
if position.z ~= 7 or isInArray(downstairsIds, higherFloorItem:getId()) == false then
Game.createItem(higherFloorItem:getId(), 1, position)
end
higherFloorItem:remove()
higherFloorItem = tile:getItemByType(0)
end
local items = tile:getItems()
if items ~= nil then
for _, item in pairs(items) do
if position.z ~= 7 or isInArray(downstairsIds, item:getId()) == false then
Game.createItem(item:getId(), 1, position)
end
item:remove()
end
end
local effectRandomness = math.random(5)
if effectRandomness == 5 then
local earthquakeEffect = earthquakeEffects[math.random(#earthquakeEffects)]
tile:getPosition():sendMagicEffect(earthquakeEffect)
end
end
end
-- TODO: Implement that bitch npc is busy and says that I don't understand what is happening. The Academy of Magic Arts are not commenting the current situation.

View File

@@ -0,0 +1,8 @@
function onSay(player, words, param)
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Server Info:"
.. "\nExp rate: " .. Game.getExperienceStage(player:getLevel())
.. "\nSkill rate: " .. configManager.getNumber(configKeys.RATE_SKILL)
.. "\nMagic rate: " .. configManager.getNumber(configKeys.RATE_MAGIC)
.. "\nLoot rate: " .. configManager.getNumber(configKeys.RATE_LOOT))
return false
end

View File

@@ -0,0 +1,15 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local split = param:split(",")
if split[2] == nil then
player:sendCancelMessage("Insufficient parameters.")
return false
end
player:setStorageValue(tonumber(split[1]), tonumber(split[2]))
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "[Storage Value] " .. split[1] .. " changed it's value to " .. split[2] .. ".")
return false
end

View File

@@ -0,0 +1,24 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local creature = Creature(param)
if not creature then
player:sendCancelMessage("A creature with that name could not be found.")
return false
end
local oldPosition = creature:getPosition()
local newPosition = creature:getClosestFreePosition(player:getPosition(), false)
if newPosition.x == 0 then
player:sendCancelMessage("You can not teleport " .. creature:getName() .. ".")
return false
elseif creature:teleportTo(newPosition) then
if not creature:isInGhostMode() then
oldPosition:sendMagicEffect(CONST_ME_POFF)
newPosition:sendMagicEffect(CONST_ME_TELEPORT)
end
end
return false
end

View File

@@ -0,0 +1,8 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
player:teleportTo(player:getTown():getTemplePosition())
return false
end

View File

@@ -0,0 +1,28 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local steps = tonumber(param)
if not steps then
return false
end
local position = player:getPosition()
position:getNextPosition(player:getDirection(), steps)
position = player:getClosestFreePosition(position, false)
if position.x == 0 then
player:sendCancelMessage("You cannot teleport there.")
return false
end
local tile = Tile(position)
if tile == nil or tile:getGround() == nil then
player:sendCancelMessage("You cannot teleport there.")
return false
end
player:teleportTo(position)
return false
end

View File

@@ -0,0 +1,14 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local target = Creature(param)
if target == nil then
player:sendCancelMessage("Creature not found.")
return false
end
player:teleportTo(target:getPosition())
return false
end

View File

@@ -0,0 +1,9 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local split = param:split(",")
local position = {x = split[1], y = split[2], z = split[3]}
return player:teleportTo(position)
end

View File

@@ -0,0 +1,18 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local town = Town(param)
if town == nil then
town = Town(tonumber(param))
end
if town == nil then
player:sendCancelMessage("Town not found.")
return false
end
player:teleportTo(town:getTemplePosition())
return false
end

View File

@@ -0,0 +1,16 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local resultId = db.storeQuery("SELECT `account_id`, `lastip` FROM `players` WHERE `name` = " .. db.escapeString(param))
if resultId == false then
return false
end
db.asyncQuery("DELETE FROM `account_bans` WHERE `account_id` = " .. result.getDataInt(resultId, "account_id"))
db.asyncQuery("DELETE FROM `ip_bans` WHERE `ip` = " .. result.getDataInt(resultId, "lastip"))
result.free(resultId)
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, param .. " has been unbanned.")
return false
end

View File

@@ -0,0 +1,17 @@
function onSay(player, words, param)
if not player:getGroup():getAccess() then
return true
end
local position = player:getPosition()
position.z = position.z - 1
local tile = Tile(position)
if tile == nil or tile:getGround() == nil then
player:sendCancelMessage("You cannot teleport there.")
return false
end
player:teleportTo(position)
return false
end

View File

@@ -0,0 +1,8 @@
function onSay(player, words, param)
local uptime = getWorldUpTime()
local hours = math.floor(uptime / 3600)
local minutes = math.floor((uptime - (3600 * hours)) / 60)
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Uptime: " .. hours .. " hours and " .. minutes .. " minutes.")
return false
end

View File

@@ -0,0 +1,10 @@
function onSay(player, words, param)
if player:getStorageValue(17742) ~= 1 then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Vials auto removing enabled.")
player:setStorageValue(17742, 1)
else
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Vials auto removing disabled.")
player:setStorageValue(17742, 0)
end
return false
end

View File

@@ -0,0 +1,60 @@
function onSay(player, words, param)
if not player:getGuild() or player:getGuildLevel() < 3 then
player:sendCancelMessage("You cannot execute this talkaction.")
return true
end
local t = param:split(",")
if not t[2] then
player:sendCancelMessage("Not enough param(s).")
return true
end
local enemyGuildName = string.trim(t[2])
local enemyGuild = Guild(getGuildId(enemyGuildName))
if not enemyGuild then
player:sendCancelMessage("Guild \"" .. enemyGuildName .. "\" does not exists or nobody is online from the guild.")
return true
end
if enemyGuild:getId() == player:getGuild():getId() then
player:sendCancelMessage("You cannot perform war action on your own guild.")
return true
end
local warCommand = string.trim(t[1])
if isInArray({"accept", "reject", "cancel"}, warCommand) then
local pendingWarId, bounty = 0
if warCommand == "cancel" then
pendingWarId, bounty = guildwars:getPendingInvitation(player:getGuild():getId(), enemyGuild:getId())
else
pendingWarId, bounty = guildwars:getPendingInvitation(enemyGuild:getId(), player:getGuild():getId())
end
if pendingWarId == 0 then
player:sendCancelMessage("Currently there's no pending invitation for a war with " .. enemyGuild:getName() .. ".")
return true
end
if warCommand == "reject" then
guildwars:rejectWar(pendingWarId, player:getGuild(), enemyGuild, bounty)
elseif warCommand == "cancel" then
guildwars:cancelWar(pendingWarId, player:getGuild(), enemyGuild, bounty)
else
guildwars:startWar(player, pendingWarId, player:getGuild(), enemyGuild, bounty)
end
return true
end
if warCommand == "invite" then
guildwars:invite(player, player:getGuild(), enemyGuild, tonumber(t[3] and string.trim(t[3]) or 100), tonumber(t[4] and string.trim(t[4]) or 0))
return true
end
return true
end

View File

@@ -0,0 +1,130 @@
-- Znote Shop v1.0 for Znote AAC on TFS 1.1
function onSay(player, words, param)
local storage = 54073 -- Make sure to select non-used storage. This is used to prevent SQL load attacks.
local cooldown = 15 -- in seconds.
if player:getStorageValue(storage) <= os.time() then
player:setStorageValue(storage, os.time() + cooldown)
local type_desc = {
"itemids",
"pending premium (skip)",
"pending gender change (skip)",
"pending character name change (skip)",
"Outfit and addons",
"Mounts",
"Instant house purchase"
}
print("Player: " .. player:getName() .. " triggered !shop talkaction.")
-- Create the query
local orderQuery = db.storeQuery("SELECT `id`, `type`, `itemid`, `count` FROM `znote_shop_orders` WHERE `account_id` = " .. player:getAccountId() .. ";")
local served = false
-- Detect if we got any results
if orderQuery ~= false then
repeat
-- Fetch order values
local q_id = result.getNumber(orderQuery, "id")
local q_type = result.getNumber(orderQuery, "type")
local q_itemid = result.getNumber(orderQuery, "itemid")
local q_count = result.getNumber(orderQuery, "count")
print("Processing type "..q_type..": ".. type_desc[q_type])
-- ORDER TYPE 1 (Regular item shop products)
if q_type == 1 then
served = true
-- Get wheight
if player:getFreeCapacity() >= ItemType(q_itemid):getWeight(q_count) then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
player:addItem(q_itemid, q_count)
player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received " .. q_count .. " x " .. ItemType(q_itemid):getName() .. "!")
else
player:sendTextMessage(MESSAGE_STATUS_WARNING, "Need more CAP!")
end
end
-- ORDER TYPE 5 (Outfit and addon)
if q_type == 5 then
served = true
local itemid = q_itemid
local outfits = {}
if itemid > 1000 then
local first = math.floor(itemid/1000)
table.insert(outfits, first)
itemid = itemid - (first * 1000)
end
table.insert(outfits, itemid)
for _, outfitId in pairs(outfits) do
-- Make sure player don't already have this outfit and addon
if not player:hasOutfit(outfitId, q_count) then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
player:addOutfit(outfitId)
player:addOutfitAddon(outfitId, q_count)
player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new outfit!")
else
player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this outfit and addon!")
end
end
end
-- ORDER TYPE 6 (Mounts)
if q_type == 6 then
served = true
-- Make sure player don't already have this outfit and addon
if not player:hasMount(q_itemid) then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
player:addMount(q_itemid)
player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new mount!")
else
player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this mount!")
end
end
-- ORDER TYPE 7 (Direct house purchase)
if q_type == 7 then
served = true
local house = House(q_itemid)
-- Logged in player is not neccesarily the player that bough the house. So we need to load player from db.
print(q_count)
local buyerQuery = db.storeQuery("SELECT `name` FROM `players` WHERE `id` = "..q_count.." LIMIT 1")
if buyerQuery ~= false then
local buyerName = result.getDataString(buyerQuery, "name")
result.free(buyerQuery)
if house then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
house:setOwnerGuid(q_count)
player:sendTextMessage(MESSAGE_INFO_DESCR, "You have successfully bought the house "..house:getName().." on "..buyerName..", be sure to have the money for the rent in the bank.")
print("Process complete. [".. buyerName .."] has recieved house: ["..house:getName().."]")
end
end
end
-- Add custom order types here
-- Type 1 is for itemids (Already coded here)
-- Type 2 is for premium (Coded on web)
-- Type 3 is for gender change (Coded on web)
-- Type 4 is for character name change (Coded on web)
-- Type 5 is for character outfit and addon (Already coded here)
-- Type 6 is for mounts (Already coded here)
-- So use type 7+ for custom stuff, like etc packages.
-- if q_type == 7 then
-- end
until not result.next(orderQuery)
result.free(orderQuery)
if not served then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You have no orders to process in-game.")
end
else
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You have no orders.")
end
else
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Can only be executed once every " .. cooldown .. " seconds. Remaining cooldown: " .. player:getStorageValue(storage) - os.time())
end
return false
end

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<talkactions>
<!-- commands -->
<talkaction words="/ban" separator=" " script="ban.lua" />
<talkaction words="/ipban" separator=" " script="ipban.lua" />
<talkaction words="/unban" separator=" " script="unban.lua" />
<talkaction words="/up" script="up.lua" />
<talkaction words="/down" script="down.lua" />
<talkaction words="/c" separator=" " script="teleport_creature_here.lua" />
<talkaction words="/goto" separator=" " script="teleport_to_creature.lua" />
<talkaction words="/gotopos" separator=" " script="teleport_to_pos.lua" />
<talkaction words="/owner" separator=" " script="owner.lua" />
<talkaction words="/t" script="teleport_home.lua" />
<talkaction words="/town" separator=" " script="teleport_to_town.lua" />
<talkaction words="/a" separator=" " script="teleport_ntiles.lua" />
<talkaction words="/pos" separator=" " script="position.lua" />
<talkaction words="/info" separator=" " script="info.lua" />
<talkaction words="/r" separator=" " script="removething.lua" />
<talkaction words="/kick" separator=" " script="kick.lua" />
<talkaction words="/openserver" script="openserver.lua" />
<talkaction words="/closeserver" separator=" " script="closeserver.lua" />
<talkaction words="/raid" separator=" " script="raid.lua" />
<talkaction words="/B" separator=" " script="broadcast.lua" />
<talkaction words="/m" separator=" " script="place_monster.lua" />
<talkaction words="/i" separator=" " script="create_item.lua" />
<talkaction words="/s" separator=" " script="place_npc.lua" />
<talkaction words="/addtutor" separator=" " script="add_tutor.lua" />
<talkaction words="/removetutor" separator=" " script="remove_tutor.lua" />
<talkaction words="/looktype" separator=" " script="looktype.lua" />
<talkaction words="/summon" separator=" " script="place_summon.lua" />
<talkaction words="/reload" separator=" " script="reload.lua" />
<talkaction words="/save" script="save.lua" />
<talkaction words="/chameleon" separator=" " script="chameleon.lua" />
<talkaction words="/addskill" separator=" " script="add_skill.lua" />
<talkaction words="/mccheck" script="mccheck.lua" />
<talkaction words="/ghost" script="ghost.lua" />
<talkaction words="/clean" script="clean.lua" />
<talkaction words="/storagevalue" separator=" " script="storagevalue.lua" />
<talkaction words="/minimap" separator=" " script="minimap_scan.lua" />
<talkaction words="/removehouse" separator=" " script="remove_house.lua" />
<talkaction words="/globalboost" separator=" " script="global_rate_boost.lua" />
<talkaction words="/loottest" script="loot_test.lua" />
<talkaction words="/event" script="serpentine_tower_event.lua" />
<talkaction words="/queryhouses" separator=" " script="query_houses.lua" />
<talkaction words="/impersonate" separator=" " script="impersonate.lua" />
<!-- player talkactions -->
<talkaction words="!buyhouse" script="buyhouse.lua"/>
<talkaction words="!sellhouse" separator=" " script="sellhouse.lua" />
<talkaction words="!leavehouse" script="leavehouse.lua"/>
<talkaction words="!uptime" script="uptime.lua"/>
<talkaction words="!deathlist" script="deathlist.lua"/>
<talkaction words="!kills" script="kills.lua"/>
<talkaction words="!frags" script="kills.lua"/>
<talkaction words="!online" script="online.lua"/>
<talkaction words="!serverinfo" script="serverinfo.lua"/>
<talkaction words="!share" script="experienceshare.lua"/>
<talkaction words="!shop" script="znoteshop.lua"/>
<talkaction words="!physicaldamage" separator=" " script="physical_damage.lua"/>
<talkaction words="!war" separator=" " script="war.lua"/>
<talkaction words="!vial" script="vial.lua"/>
<talkaction words="469" script="469.lua"/>
<!-- test talkactions -->
<talkaction words="!z" separator=" " script="magiceffect.lua"/>
<talkaction words="!x" separator=" " script="animationeffect.lua"/>
</talkactions>