mirror of
https://github.com/ErikasKontenis/SabrehavenServer.git
synced 2025-10-13 14:24:55 +02:00
added mounts, auras, wings and shop. Packet compression does not work extendedOpCodes does not work but shop is because of extra configuration
This commit is contained in:
6
data/XML/auras.xml
Normal file
6
data/XML/auras.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<auras>
|
||||
<!-- <aura id="1" clientid="368" name="Widow Queen" speed="20" />
|
||||
<aura id="2" clientid="369" name="Racing Bird" speed="20" />
|
||||
<aura id="3" clientid="370" name="War Bear" speed="20" /> -->
|
||||
</auras>
|
106
data/XML/mounts.xml
Normal file
106
data/XML/mounts.xml
Normal file
@@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mounts>
|
||||
<mount id="1" clientid="368" name="Widow Queen" speed="20" premium="no" />
|
||||
<!--
|
||||
<mount id="2" clientid="369" name="Racing Bird" speed="20" premium="yes" />
|
||||
<mount id="3" clientid="370" name="War Bear" speed="20" premium="yes" />
|
||||
<mount id="4" clientid="371" name="Black Sheep" speed="20" premium="yes" />
|
||||
<mount id="5" clientid="372" name="Midnight Panther" speed="20" premium="yes" />
|
||||
<mount id="6" clientid="373" name="Draptor" speed="20" premium="yes" />
|
||||
<mount id="7" clientid="374" name="Titanica" speed="20" premium="yes" />
|
||||
<mount id="8" clientid="375" name="Tin Lizzard" speed="20" premium="yes" />
|
||||
<mount id="9" clientid="376" name="Blazebringer" speed="20" premium="yes" />
|
||||
<mount id="10" clientid="377" name="Rapid Boar" speed="20" premium="yes" />
|
||||
<mount id="11" clientid="378" name="Stampor" speed="20" premium="yes" />
|
||||
<mount id="12" clientid="379" name="Undead Cavebear" speed="20" premium="yes" />
|
||||
<mount id="13" clientid="387" name="Donkey" speed="20" premium="yes" />
|
||||
<mount id="14" clientid="388" name="Tiger Slug" speed="20" premium="yes" />
|
||||
<mount id="15" clientid="389" name="Uniwheel" speed="20" premium="yes" />
|
||||
<mount id="16" clientid="390" name="Crystal Wolf" speed="20" premium="yes" />
|
||||
<mount id="17" clientid="392" name="War Horse" speed="20" premium="yes" />
|
||||
<mount id="18" clientid="401" name="Kingly Deer" speed="20" premium="yes" />
|
||||
<mount id="19" clientid="402" name="Tamed Panda" speed="20" premium="yes" />
|
||||
<mount id="20" clientid="405" name="Dromedary" speed="20" premium="yes" />
|
||||
<mount id="21" clientid="406" name="Scorpion King" speed="20" premium="yes" />
|
||||
<mount id="22" clientid="421" name="Rented Horse" speed="20" premium="no" />
|
||||
<mount id="23" clientid="426" name="Armoured War Horse" speed="20" premium="no" />
|
||||
<mount id="24" clientid="427" name="Shadow Draptor" speed="20" premium="yes" />
|
||||
<mount id="25" clientid="437" name="Rented Horse" speed="20" premium="no" />
|
||||
<mount id="26" clientid="438" name="Rented Horse" speed="20" premium="no" />
|
||||
<mount id="27" clientid="447" name="Lady Bug" speed="20" premium="yes" />
|
||||
<mount id="28" clientid="450" name="Manta Ray" speed="20" premium="yes" />
|
||||
<mount id="29" clientid="502" name="Ironblight" speed="20" premium="yes" />
|
||||
<mount id="30" clientid="503" name="Magma Crawler" speed="20" premium="yes" />
|
||||
<mount id="31" clientid="506" name="Dragonling" speed="20" premium="yes" />
|
||||
<mount id="32" clientid="515" name="Gnarlhound" speed="20" premium="yes" />
|
||||
<mount id="33" clientid="521" name="Crimson Ray" speed="20" premium="no" />
|
||||
<mount id="34" clientid="522" name="Steelbeak" speed="20" premium="no" />
|
||||
<mount id="35" clientid="526" name="Water Buffalo" speed="20" premium="yes" />
|
||||
<mount id="36" clientid="546" name="Tombstinger" speed="20" premium="no" />
|
||||
<mount id="37" clientid="547" name="Platesaurian" speed="20" premium="no" />
|
||||
<mount id="38" clientid="548" name="Ursagrodon" speed="20" premium="yes" />
|
||||
<mount id="39" clientid="559" name="The Hellgrip" speed="20" premium="yes" />
|
||||
<mount id="40" clientid="571" name="Noble Lion" speed="20" premium="yes" />
|
||||
<mount id="41" clientid="572" name="Desert King" speed="20" premium="no" />
|
||||
<mount id="42" clientid="580" name="Shock Head" speed="20" premium="yes" />
|
||||
<mount id="43" clientid="606" name="Walker" speed="20" premium="yes" />
|
||||
<mount id="44" clientid="621" name="Azudocus" speed="20" premium="no" />
|
||||
<mount id="45" clientid="622" name="Carpacosaurus" speed="20" premium="no" />
|
||||
<mount id="46" clientid="624" name="Death Crawler" speed="20" premium="no" />
|
||||
<mount id="47" clientid="626" name="Flamesteed" speed="20" premium="no" />
|
||||
<mount id="48" clientid="627" name="Jade Lion" speed="20" premium="no" />
|
||||
<mount id="49" clientid="628" name="Jade Pincer" speed="20" premium="no" />
|
||||
<mount id="50" clientid="629" name="Nethersteed" speed="20" premium="no" />
|
||||
<mount id="51" clientid="630" name="Tempest" speed="20" premium="no" />
|
||||
<mount id="52" clientid="631" name="Winter King" speed="20" premium="no" />
|
||||
<mount id="53" clientid="644" name="Doombringer" speed="20" premium="no" />
|
||||
<mount id="54" clientid="647" name="Woodland Prince" speed="20" premium="no" />
|
||||
<mount id="55" clientid="648" name="Hailstorm Fury" speed="20" premium="no" />
|
||||
<mount id="56" clientid="649" name="Siegebreaker" speed="20" premium="no" />
|
||||
<mount id="57" clientid="650" name="Poisonbane" speed="20" premium="no" />
|
||||
<mount id="58" clientid="651" name="Blackpelt" speed="20" premium="no" />
|
||||
<mount id="59" clientid="669" name="Golden Dragonfly" speed="20" premium="no" />
|
||||
<mount id="60" clientid="670" name="Steel Bee" speed="20" premium="no" />
|
||||
<mount id="61" clientid="671" name="Copper Fly" speed="20" premium="no" />
|
||||
<mount id="62" clientid="672" name="Tundra Rambler" speed="20" premium="no" />
|
||||
<mount id="63" clientid="673" name="Highland Yak" speed="20" premium="no" />
|
||||
<mount id="64" clientid="674" name="Glacier Vagabond" speed="20" premium="no" />
|
||||
<mount id="65" clientid="688" name="Flying Divan" speed="20" premium="no" />
|
||||
<mount id="66" clientid="689" name="Magic Carpet" speed="20" premium="no" />
|
||||
<mount id="67" clientid="690" name="Floating Kashmir" speed="20" premium="no" />
|
||||
<mount id="68" clientid="691" name="Ringtail Waccoon" speed="20" premium="no" />
|
||||
<mount id="69" clientid="692" name="Night Waccoon" speed="20" premium="no" />
|
||||
<mount id="70" clientid="693" name="Emerald Waccoon" speed="20" premium="no" />
|
||||
<mount id="71" clientid="682" name="Glooth Glider" speed="20" premium="yes" />
|
||||
<mount id="72" clientid="685" name="Shadow Hart" speed="20" premium="no" />
|
||||
<mount id="73" clientid="686" name="Black Stag" speed="20" premium="no" />
|
||||
<mount id="74" clientid="687" name="Emperor Deer" speed="20" premium="no" />
|
||||
<mount id="75" clientid="726" name="Flitterkatzen" speed="20" premium="no" />
|
||||
<mount id="76" clientid="727" name="Venompaw" speed="20" premium="no" />
|
||||
<mount id="77" clientid="728" name="Batcat" speed="20" premium="no" />
|
||||
<mount id="78" clientid="734" name="Sea Devil" speed="20" premium="no" />
|
||||
<mount id="79" clientid="735" name="Coralripper" speed="20" premium="no" />
|
||||
<mount id="80" clientid="736" name="Plumfish" speed="20" premium="no" />
|
||||
<mount id="81" clientid="738" name="Gorongra" speed="20" premium="no" />
|
||||
<mount id="82" clientid="739" name="Noctungra" speed="20" premium="no" />
|
||||
<mount id="83" clientid="740" name="Silverneck" speed="20" premium="no" />
|
||||
<mount id="84" clientid="761" name="Slagsnare" speed="20" premium="no" />
|
||||
<mount id="85" clientid="762" name="Nightstinger" speed="20" premium="no" />
|
||||
<mount id="86" clientid="763" name="Razorcreep" speed="20" premium="no" />
|
||||
<mount id="87" clientid="848" name="Rift Runner" speed="20" premium="yes" />
|
||||
<mount id="88" clientid="849" name="Nightdweller" speed="20" premium="no" />
|
||||
<mount id="89" clientid="850" name="Frostflare" speed="20" premium="no" />
|
||||
<mount id="90" clientid="851" name="Cinderhoof" speed="20" premium="no" />
|
||||
<mount id="91" clientid="868" name="Mouldpincer" speed="20" premium="no" />
|
||||
<mount id="92" clientid="869" name="Bloodcurl" speed="20" premium="no" />
|
||||
<mount id="93" clientid="870" name="Leafscuttler" speed="20" premium="no" />
|
||||
<mount id="94" clientid="883" name="Sparkion" speed="20" premium="yes" />
|
||||
<mount id="95" clientid="886" name="Swamp Snapper" speed="20" premium="no" />
|
||||
<mount id="96" clientid="887" name="Mould Shell" speed="20" premium="no" />
|
||||
<mount id="97" clientid="888" name="Reed Lurker" speed="20" premium="no" />
|
||||
<mount id="98" clientid="889" name="Neon Sparkid" speed="20" premium="yes" />
|
||||
<mount id="99" clientid="890" name="Vortexion" speed="20" premium="yes" />
|
||||
<mount id="100" clientid="901" name="Ivory Fang" speed="20" premium="no" />
|
||||
<mount id="101" clientid="902" name="Shadow Claw" speed="20" premium="no" />
|
||||
<mount id="102" clientid="903" name="Snow Pelt" speed="20" premium="no" /> -->
|
||||
</mounts>
|
4
data/XML/shaders.xml
Normal file
4
data/XML/shaders.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<shaders>
|
||||
<shader id="1" name="Rainbow Outfit" premium="no" />
|
||||
</shaders>
|
6
data/XML/wings.xml
Normal file
6
data/XML/wings.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<wings>
|
||||
<!-- <wing id="1" clientid="368" name="Widow Queen" speed="20" />
|
||||
<wing id="2" clientid="369" name="Racing Bird" speed="20" />
|
||||
<wing id="3" clientid="370" name="War Bear" speed="20" /> -->
|
||||
</wings>
|
@@ -16,4 +16,6 @@
|
||||
|
||||
<!-- Svargrond Arena: Killing a boss -->
|
||||
<event type="kill" name="SvargrondArenaKill" script="arena_kill.lua" />
|
||||
|
||||
<event type="extendedopcode" name="Shop" script="shop.lua" />
|
||||
</creaturescripts>
|
||||
|
@@ -324,6 +324,7 @@ function onLogin(player)
|
||||
player:registerEvent("InquisitionUngreez")
|
||||
player:registerEvent("InquisitionBosses")
|
||||
player:registerEvent("SvargrondArenaKill")
|
||||
|
||||
player:registerEvent("Shop")
|
||||
|
||||
return true
|
||||
end
|
||||
|
362
data/creaturescripts/scripts/shop.lua
Normal file
362
data/creaturescripts/scripts/shop.lua
Normal file
@@ -0,0 +1,362 @@
|
||||
-- BETA VERSION, net tested yet
|
||||
-- Instruction:
|
||||
-- creaturescripts.xml <event type="extendedopcode" name="Shop" script="shop.lua" />
|
||||
-- and in login.lua player:registerEvent("Shop")
|
||||
-- create sql table shop_history
|
||||
-- set variables
|
||||
-- set up function init(), add there items and categories, follow examples
|
||||
-- set up callbacks at the bottom to add player item/outfit/whatever you want
|
||||
|
||||
local SHOP_EXTENDED_OPCODE = 201
|
||||
local SHOP_OFFERS = {}
|
||||
local SHOP_CALLBACKS = {}
|
||||
local SHOP_CATEGORIES = nil
|
||||
local SHOP_BUY_URL = "http://otland.net" -- can be empty
|
||||
local SHOP_AD = { -- can be nil
|
||||
image = "https://s3.envato.com/files/62273611/PNG%20Blue/Banner%20blue%20468x60.png",
|
||||
url = "http://otclient.ovh",
|
||||
text = ""
|
||||
}
|
||||
local MAX_PACKET_SIZE = 50000
|
||||
|
||||
--[[ SQL TABLE
|
||||
CREATE TABLE `shop_history` (
|
||||
`id` int(11) NOT NULL,
|
||||
`account` int(11) NOT NULL,
|
||||
`player` int(11) NOT NULL,
|
||||
`date` datetime NOT NULL,
|
||||
`title` varchar(100) NOT NULL,
|
||||
`cost` int(11) NOT NULL,
|
||||
`details` varchar(500) NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
ALTER TABLE `shop_history`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
ALTER TABLE `shop_history`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
]]--
|
||||
|
||||
function init()
|
||||
-- print(json.encode(g_game.getLocalPlayer():getOutfit())) -- in console in otclient, will print current outfit and mount
|
||||
|
||||
SHOP_CATEGORIES = {}
|
||||
|
||||
local category1 = addCategory({
|
||||
type="item",
|
||||
item=ItemType(6561):getId(),
|
||||
count=1,
|
||||
name="Items"
|
||||
})
|
||||
local category2 = addCategory({
|
||||
type="outfit",
|
||||
name="Outfits",
|
||||
outfit={
|
||||
mount=0,
|
||||
feet=0,
|
||||
legs=0,
|
||||
body=0,
|
||||
type=136,
|
||||
auxType=0,
|
||||
addons=0,
|
||||
head=0,
|
||||
rotating=false
|
||||
}
|
||||
})
|
||||
local category3 = addCategory({
|
||||
type="outfit",
|
||||
name="Mounts",
|
||||
outfit={
|
||||
mount=0,
|
||||
feet=0,
|
||||
legs=0,
|
||||
body=0,
|
||||
type=368,
|
||||
auxType=0,
|
||||
addons=0,
|
||||
head=0,
|
||||
rotating=false
|
||||
}
|
||||
})
|
||||
local category4 = addCategory({
|
||||
type="item",
|
||||
item=ItemType(5919):getId(),
|
||||
count=1,
|
||||
name="Addon Items"
|
||||
})
|
||||
local category5 = addCategory({
|
||||
type="item",
|
||||
item=ItemType(4835):getId(),
|
||||
count=1,
|
||||
name="Quest Items"
|
||||
})
|
||||
local category6 = addCategory({
|
||||
type="item",
|
||||
item=ItemType(3734):getId(),
|
||||
count=1,
|
||||
name="Decorations"
|
||||
})
|
||||
-- local category4 = addCategory({
|
||||
-- type="image",
|
||||
-- image="/data/images/game/states/electrified.png",
|
||||
-- name="Category with local image"
|
||||
-- })
|
||||
|
||||
|
||||
category1.addItem(50, 6561, 1, "Ceremonial Ankh", "gives you all blessings")
|
||||
category1.addItem(20, 5908, 1, "Obsidian Knife", "")
|
||||
category1.addItem(200, 5797, 1, "Dice", "")
|
||||
category1.addItem(100, 6549, 1, "Green Djinn Access", "The magical powder will bless you with the power to convince the green djinns")
|
||||
category1.addItem(100, 6551, 1, "Blue Djinn Access", "The magical powder will bless you with the power to convince the blue djinns")
|
||||
category1.addItem(100, 3252, 1, "Postman Access", "The magical horn will grant you the trustworthy postman rank")
|
||||
|
||||
category2.addOutfit(150, {
|
||||
mount=0,
|
||||
feet=0,
|
||||
legs=0,
|
||||
body=0,
|
||||
type=162,
|
||||
auxType=0,
|
||||
addons=0,
|
||||
head=0,
|
||||
rotating=true
|
||||
}, "Monk", "")
|
||||
|
||||
category3.addOutfit(75, {
|
||||
mount=1,
|
||||
feet=0,
|
||||
legs=0,
|
||||
body=0,
|
||||
type=368,
|
||||
auxType=0,
|
||||
addons=0,
|
||||
head=0,
|
||||
rotating=true
|
||||
}, "Widow Queen", "")
|
||||
|
||||
category4.addItem(150, 5919, 1, "Dragon Claw", "It is the claw of Demodras")
|
||||
|
||||
category5.addItem(30, 4835, 1, "Snake Destroyer", "")
|
||||
|
||||
category6.addItem(10, 3734, 10, "10x Blood Herb", "")
|
||||
|
||||
--category4.addImage(10000, "/data/images/game/states/haste.png", "Offer with local image", "another local image\n/data/images/game/states/haste.png")
|
||||
--category4.addImage(10000, "http://otclient.ovh/images/freezing.png", "Offer with remote image and custom buy action", "blalasdasd image\nhttp://otclient.ovh/images/freezing.png", customImageBuyAction)
|
||||
end
|
||||
|
||||
function addCategory(data)
|
||||
data['offers'] = {}
|
||||
table.insert(SHOP_CATEGORIES, data)
|
||||
table.insert(SHOP_CALLBACKS, {})
|
||||
local index = #SHOP_CATEGORIES
|
||||
return {
|
||||
addItem = function(cost, itemId, count, title, description, callback)
|
||||
if not callback then
|
||||
callback = defaultItemBuyAction
|
||||
end
|
||||
table.insert(SHOP_CATEGORIES[index]['offers'], {
|
||||
cost=cost,
|
||||
type="item",
|
||||
item=ItemType(itemId):getId(), -- displayed
|
||||
itemId=itemId,
|
||||
count=count,
|
||||
title=title,
|
||||
description=description
|
||||
})
|
||||
table.insert(SHOP_CALLBACKS[index], callback)
|
||||
end,
|
||||
addOutfit = function(cost, outfit, title, description, callback)
|
||||
if not callback then
|
||||
callback = defaultOutfitBuyAction
|
||||
end
|
||||
table.insert(SHOP_CATEGORIES[index]['offers'], {
|
||||
cost=cost,
|
||||
type="outfit",
|
||||
outfit=outfit,
|
||||
title=title,
|
||||
description=description
|
||||
})
|
||||
table.insert(SHOP_CALLBACKS[index], callback)
|
||||
end,
|
||||
addImage = function(cost, image, title, description, callback)
|
||||
if not callback then
|
||||
callback = defaultImageBuyAction
|
||||
end
|
||||
table.insert(SHOP_CATEGORIES[index]['offers'], {
|
||||
cost=cost,
|
||||
type="image",
|
||||
image=image,
|
||||
title=title,
|
||||
description=description
|
||||
})
|
||||
table.insert(SHOP_CALLBACKS[index], callback)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
function getPoints(player)
|
||||
local points = 0
|
||||
local resultId = db.storeQuery("SELECT `points` FROM `znote_accounts` WHERE `id` = " .. player:getAccountId())
|
||||
if resultId ~= false then
|
||||
points = result.getDataInt(resultId, "points")
|
||||
result.free(resultId)
|
||||
end
|
||||
return points
|
||||
end
|
||||
|
||||
function getStatus(player)
|
||||
local status = {
|
||||
ad = SHOP_AD,
|
||||
points = getPoints(player),
|
||||
buyUrl = SHOP_BUY_URL
|
||||
}
|
||||
return status
|
||||
end
|
||||
|
||||
function sendJSON(player, action, data, forceStatus)
|
||||
local status = nil
|
||||
if not player:getStorageValue(1150001) or player:getStorageValue(1150001) + 10 < os.time() or forceStatus then
|
||||
status = getStatus(player)
|
||||
end
|
||||
player:setStorageValue(1150001, os.time())
|
||||
|
||||
|
||||
local buffer = json.encode({action = action, data = data, status = status})
|
||||
local s = {}
|
||||
for i=1, #buffer, MAX_PACKET_SIZE do
|
||||
s[#s+1] = buffer:sub(i,i+MAX_PACKET_SIZE - 1)
|
||||
end
|
||||
local msg = NetworkMessage()
|
||||
if #s == 1 then
|
||||
msg:addByte(50)
|
||||
msg:addByte(SHOP_EXTENDED_OPCODE)
|
||||
msg:addString(s[1])
|
||||
msg:sendToPlayer(player)
|
||||
return
|
||||
end
|
||||
-- split message if too big
|
||||
msg:addByte(50)
|
||||
msg:addByte(SHOP_EXTENDED_OPCODE)
|
||||
msg:addString("S" .. s[1])
|
||||
msg:sendToPlayer(player)
|
||||
for i=2,#s - 1 do
|
||||
msg = NetworkMessage()
|
||||
msg:addByte(50)
|
||||
msg:addByte(SHOP_EXTENDED_OPCODE)
|
||||
msg:addString("P" .. s[i])
|
||||
msg:sendToPlayer(player)
|
||||
end
|
||||
msg = NetworkMessage()
|
||||
msg:addByte(50)
|
||||
msg:addByte(SHOP_EXTENDED_OPCODE)
|
||||
msg:addString("E" .. s[#s])
|
||||
msg:sendToPlayer(player)
|
||||
end
|
||||
|
||||
function sendMessage(player, title, msg, forceStatus)
|
||||
sendJSON(player, "message", {title=title, msg=msg}, forceStatus)
|
||||
end
|
||||
|
||||
function onExtendedOpcode(player, opcode, buffer)
|
||||
if opcode ~= SHOP_EXTENDED_OPCODE then
|
||||
return false
|
||||
end
|
||||
local status, json_data = pcall(function() return json.decode(buffer) end)
|
||||
if not status then
|
||||
return false
|
||||
end
|
||||
|
||||
local action = json_data['action']
|
||||
local data = json_data['data']
|
||||
if not action or not data then
|
||||
return false
|
||||
end
|
||||
|
||||
if SHOP_CATEGORIES == nil then
|
||||
init()
|
||||
end
|
||||
|
||||
if action == 'init' then
|
||||
sendJSON(player, "categories", SHOP_CATEGORIES)
|
||||
elseif action == 'buy' then
|
||||
processBuy(player, data)
|
||||
elseif action == "history" then
|
||||
sendHistory(player)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function processBuy(player, data)
|
||||
local categoryId = tonumber(data["category"])
|
||||
local offerId = tonumber(data["offer"])
|
||||
local offer = SHOP_CATEGORIES[categoryId]['offers'][offerId]
|
||||
local callback = SHOP_CALLBACKS[categoryId][offerId]
|
||||
if not offer or not callback or data["title"] ~= offer["title"] or data["cost"] ~= offer["cost"] then
|
||||
sendJSON(player, "categories", SHOP_CATEGORIES) -- refresh categories, maybe invalid
|
||||
return sendMessage(player, "Error!", "Invalid offer")
|
||||
end
|
||||
local points = getPoints(player)
|
||||
if not offer['cost'] or offer['cost'] > points or points < 1 then
|
||||
return sendMessage(player, "Error!", "You don't have enough points to buy " .. offer['title'] .."!", true)
|
||||
end
|
||||
local status = callback(player, offer)
|
||||
if status == true then
|
||||
db.query("UPDATE `znote_accounts` set `points` = `points` - " .. offer['cost'] .. " WHERE `id` = " .. player:getAccountId())
|
||||
db.asyncQuery("INSERT INTO `shop_history` (`account`, `player`, `date`, `title`, `cost`, `details`) VALUES ('" .. player:getAccountId() .. "', '" .. player:getGuid() .. "', NOW(), " .. db.escapeString(offer['title']) .. ", " .. db.escapeString(offer['cost']) .. ", " .. db.escapeString(json.encode(offer)) .. ")")
|
||||
return sendMessage(player, "Success!", "You bought " .. offer['title'] .."!", true)
|
||||
end
|
||||
if status == nil or status == false then
|
||||
status = "Unknown error while buying " .. offer['title']
|
||||
end
|
||||
sendMessage(player, "Error!", status)
|
||||
end
|
||||
|
||||
function sendHistory(player)
|
||||
if player:getStorageValue(1150002) and player:getStorageValue(1150002) + 10 > os.time() then
|
||||
return -- min 10s delay
|
||||
end
|
||||
player:setStorageValue(1150002, os.time())
|
||||
|
||||
local history = {}
|
||||
local resultId = db.storeQuery("SELECT * FROM `shop_history` WHERE `account` = " .. player:getAccountId() .. " order by `id` DESC")
|
||||
|
||||
if resultId ~= false then
|
||||
repeat
|
||||
local details = result.getDataString(resultId, "details")
|
||||
local status, json_data = pcall(function() return json.decode(details) end)
|
||||
if not status then
|
||||
json_data = {
|
||||
type = "image",
|
||||
title = result.getDataString(resultId, "title"),
|
||||
cost = result.getDataInt(resultId, "cost")
|
||||
}
|
||||
end
|
||||
table.insert(history, json_data)
|
||||
history[#history]["description"] = "Bought on " .. result.getDataString(resultId, "date") .. " for " .. result.getDataInt(resultId, "cost") .. " points."
|
||||
until not result.next(resultId)
|
||||
result.free(resultId)
|
||||
end
|
||||
|
||||
sendJSON(player, "history", history)
|
||||
end
|
||||
|
||||
-- BUY CALLBACKS
|
||||
-- May be useful: print(json.encode(offer))
|
||||
|
||||
function defaultItemBuyAction(player, offer)
|
||||
-- todo: check if has capacity
|
||||
if player:addItem(offer["itemId"], offer["count"], false) then
|
||||
return true
|
||||
end
|
||||
return "Can't add item! Do you have enough space?"
|
||||
end
|
||||
|
||||
function defaultOutfitBuyAction(player, offer)
|
||||
return "default outfit buy action is not implemented"
|
||||
end
|
||||
|
||||
function defaultImageBuyAction(player, offer)
|
||||
return "default image buy action is not implemented"
|
||||
end
|
||||
|
||||
function customImageBuyAction(player, offer)
|
||||
return "custom image buy action is not implemented. Offer: " .. offer['title']
|
||||
end
|
@@ -185,7 +185,8 @@ function Player:onLook(thing, position, distance)
|
||||
|
||||
if thing:isCreature() then
|
||||
if thing:isPlayer() then
|
||||
description = string.format("%s\nIP: %s.", description, Game.convertIpToString(thing:getIp()))
|
||||
local client = thing:getClient()
|
||||
description = string.format("%s\nIP: %s PING: %i FPS: %i.", description, Game.convertIpToString(thing:getIp()), client.ping, client.fps)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -208,7 +209,8 @@ function Player:onLookInBattleList(creature, distance)
|
||||
)
|
||||
|
||||
if creature:isPlayer() then
|
||||
description = string.format("%s\nIP: %s", description, Game.convertIpToString(creature:getIp()))
|
||||
local client = thing:getClient()
|
||||
description = string.format("%s\nIP: %s PING: %i FPS: %i.", description, Game.convertIpToString(thing:getIp()), client.ping, client.fps)
|
||||
end
|
||||
end
|
||||
self:sendTextMessage(MESSAGE_INFO_DESCR, description)
|
||||
|
@@ -11,4 +11,5 @@ dofile('data/lib/core/teleport.lua')
|
||||
dofile('data/lib/core/tile.lua')
|
||||
dofile('data/lib/core/vocation.lua')
|
||||
dofile('data/lib/core/guildwars.lua')
|
||||
dofile('data/lib/core/svargrondArenaQuest.lua')
|
||||
dofile('data/lib/core/svargrondArenaQuest.lua')
|
||||
dofile('data/lib/core/json.lua')
|
399
data/lib/core/json.lua
Normal file
399
data/lib/core/json.lua
Normal file
@@ -0,0 +1,399 @@
|
||||
-- add to lib/core, later add dofile in lib/core/core.lua
|
||||
|
||||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2018 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
json = { _version = "0.1.1" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\\\",
|
||||
[ "\"" ] = "\\\"",
|
||||
[ "\b" ] = "\\b",
|
||||
[ "\f" ] = "\\f",
|
||||
[ "\n" ] = "\\n",
|
||||
[ "\r" ] = "\\r",
|
||||
[ "\t" ] = "\\t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if val[1] ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local has_unicode_escape = false
|
||||
local has_surrogate_escape = false
|
||||
local has_escape = false
|
||||
local last
|
||||
for j = i + 1, #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
end
|
||||
|
||||
if last == 92 then -- "\\" (escape char)
|
||||
if x == 117 then -- "u" (unicode escape sequence)
|
||||
local hex = str:sub(j + 1, j + 5)
|
||||
if not hex:find("%x%x%x%x") then
|
||||
decode_error(str, j, "invalid unicode escape in string")
|
||||
end
|
||||
if hex:find("^[dD][89aAbB]") then
|
||||
has_surrogate_escape = true
|
||||
else
|
||||
has_unicode_escape = true
|
||||
end
|
||||
else
|
||||
local c = string.char(x)
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
has_escape = true
|
||||
end
|
||||
last = nil
|
||||
|
||||
elseif x == 34 then -- '"' (end of string)
|
||||
local s = str:sub(i + 1, j - 1)
|
||||
if has_surrogate_escape then
|
||||
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_unicode_escape then
|
||||
s = s:gsub("\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_escape then
|
||||
s = s:gsub("\\.", escape_char_map_inv)
|
||||
end
|
||||
return s, j + 1
|
||||
|
||||
else
|
||||
last = x
|
||||
end
|
||||
end
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
@@ -25,7 +25,17 @@ local reloadTypes = {
|
||||
|
||||
["monster"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" },
|
||||
["monsters"] = { targetType = RELOAD_TYPE_MONSTERS, name = "monsters" },
|
||||
["mount"] = RELOAD_TYPE_MOUNTS,
|
||||
|
||||
["aura"] = RELOAD_TYPE_AURAS,
|
||||
["auras"] = RELOAD_TYPE_AURAS,
|
||||
|
||||
["wing"] = RELOAD_TYPE_WINGS,
|
||||
["wings"] = RELOAD_TYPE_WINGS,
|
||||
|
||||
["shader"] = RELOAD_TYPE_SHADERS,
|
||||
["shaders"] = RELOAD_TYPE_SHADERS,
|
||||
|
||||
["move"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
|
||||
["movement"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
|
||||
["movements"] = { targetType = RELOAD_TYPE_MOVEMENTS, name = "movements" },
|
||||
|
Reference in New Issue
Block a user