Diff
checker
텍스트
텍스트
이미지
문서
Excel
폴더
Legal
Enterprise
데스크톱
요금제
로그인
데스크톱 앱 다운로드
텍스트 비교
두 텍스트 파일의 차이점을 찾아보세요
도구
기록
실시간 편집
변경 없는 행 숨기기
줄바꿈 비활성화
레이아웃
나란히 보기
합쳐 보기
비교 단위
스마트
단어
글자
구문 강조
언어 선택
제외
텍스트 변환
첫 변경으로
수정
Diffchecker Desktop
가장 안전하게 Diffchecker를 사용하는 방법. 데스크톱 앱을 사용하면 비교 데이터가 외부로 전송되지 않습니다!
데스크톱 앱 받기
Untitled Diff
생성일
3년 전
비교 결과 만료 없음
초기화
내보내기
공유
설명
0 삭제
행
총
삭제
글자
총
삭제
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
362 행
복사
8 추가
행
총
추가
글자
총
추가
이 기능을 계속 사용하려면 업그레이드해 주세요
Diff
checker
Pro
요금제 보기
369 행
복사
-- BETA VERSION, net tested yet
-- BETA VERSION, net tested yet
-- Instruction:
-- Instruction:
-- creaturescripts.xml <event type="extendedopcode" name="Shop" script="shop.lua" />
-- creaturescripts.xml <event type="extendedopcode" name="Shop" script="shop.lua" />
-- and in login.lua player:registerEvent("Shop")
-- and in login.lua player:registerEvent("Shop")
-- create sql table shop_history
-- create sql table shop_history
-- set variables
-- set variables
-- set up function init(), add there items and categories, follow examples
-- 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
-- set up callbacks at the bottom to add player item/outfit/whatever you want
local SHOP_EXTENDED_OPCODE = 201
local SHOP_EXTENDED_OPCODE = 201
local SHOP_OFFERS = {}
local SHOP_OFFERS = {}
local SHOP_CALLBACKS = {}
local SHOP_CALLBACKS = {}
local SHOP_CATEGORIES = nil
local SHOP_CATEGORIES = nil
local SHOP_BUY_URL = "http://otland.net" -- can be empty
local SHOP_BUY_URL = "http://otland.net" -- can be empty
local SHOP_AD = { -- can be nil
local SHOP_AD = { -- can be nil
image = "https://s3.envato.com/files/62273611/PNG%20Blue/Banner%20blue%20468x60.png",
image = "https://s3.envato.com/files/62273611/PNG%20Blue/Banner%20blue%20468x60.png",
url = "http://otclient.ovh",
url = "http://otclient.ovh",
text = ""
text = ""
}
}
local MAX_PACKET_SIZE = 50000
local MAX_PACKET_SIZE = 50000
--[[ SQL TABLE
--[[ SQL TABLE
CREATE TABLE `shop_history` (
CREATE TABLE `shop_history` (
`id` int(11) NOT NULL,
`id` int(11) NOT NULL,
`account` int(11) NOT NULL,
`account` int(11) NOT NULL,
`player` int(11) NOT NULL,
`player` int(11) NOT NULL,
`date` datetime NOT NULL,
`date` datetime NOT NULL,
`title` varchar(100) NOT NULL,
`title` varchar(100) NOT NULL,
`cost` int(11) NOT NULL,
`cost` int(11) NOT NULL,
`details` varchar(500) NOT NULL
`details` varchar(500) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `shop_history`
ALTER TABLE `shop_history`
ADD PRIMARY KEY (`id`);
ADD PRIMARY KEY (`id`);
ALTER TABLE `shop_history`
ALTER TABLE `shop_history`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
]]--
]]--
function init()
function init()
-- print(json.encode(g_game.getLocalPlayer():getOutfit())) -- in console in otclient, will print current outfit and mount
-- print(json.encode(g_game.getLocalPlayer():getOutfit())) -- in console in otclient, will print current outfit and mount
SHOP_CATEGORIES = {}
SHOP_CATEGORIES = {}
local category1 = addCategory({
local category1 = addCategory({
type="item",
type="item",
item=ItemType(2160):getClientId(),
item=ItemType(2160):getClientId(),
count=100,
count=100,
name="Items"
name="Items"
})
})
local category2 = addCategory({
local category2 = addCategory({
type="outfit",
type="outfit",
name="Outfits",
name="Outfits",
outfit={
outfit={
mount=0,
mount=0,
feet=114,
feet=114,
legs=114,
legs=114,
body=116,
body=116,
type=143,
type=143,
auxType=0,
auxType=0,
addons=3,
addons=3,
head=2,
head=2,
rotating=true
rotating=true
}
}
})
})
local category3 = addCategory({
local category3 = addCategory({
type="image",
type="image",
image="http://otclient.ovh/images/137.png",
image="http://otclient.ovh/images/137.png",
name="Category with http image"
name="Category with http image"
})
})
local category4 = addCategory({
local category4 = addCategory({
type="image",
type="image",
image="/data/images/game/states/electrified.png",
image="/data/images/game/states/electrified.png",
name="Category with local image"
name="Category with local image"
})
})
category1.addItem(1, 2160, 1, "1 Crystal coin", "description of cristal coin")
category1.addItem(1, 2160, 1, "1 Crystal coin", "description of cristal coin")
category1.addItem(5, 2160, 5, "5 Crystal coin", "description of cristal coin")
category1.addItem(5, 2160, 5, "5 Crystal coin", "description of cristal coin")
category1.addItem(50, 2160, 50, "50 Crystal coin", "description of cristal coin")
category1.addItem(50, 2160, 50, "50 Crystal coin", "description of cristal coin")
category1.addItem(90, 2160, 100, "100 Crystal coin", "description of cristal coin")
category1.addItem(90, 2160, 100, "100 Crystal coin", "description of cristal coin")
category1.addItem(200, 2493, 1, "Demon helmet1", "woo\ndemon helmet\nnice, you should buy it")
category1.addItem(200, 2493, 1, "Demon helmet1", "woo\ndemon helmet\nnice, you should buy it")
category1.addItem(1, 2160, 1, "1 Crystal coin1", "description of cristal coin")
category1.addItem(1, 2160, 1, "1 Crystal coin1", "description of cristal coin")
category1.addItem(5, 2160, 5, "5 Crystal coin1", "description of cristal coin")
category1.addItem(5, 2160, 5, "5 Crystal coin1", "description of cristal coin")
category1.addItem(50, 2160, 50, "50 Crystal coin1", "description of cristal coin")
category1.addItem(50, 2160, 50, "50 Crystal coin1", "description of cristal coin")
category1.addItem(90, 2160, 100, "100 Crystal coin1", "description of cristal coin")
category1.addItem(90, 2160, 100, "100 Crystal coin1", "description of cristal coin")
category1.addItem(200, 2493, 1, "Demon helmet2", "woo\ndemon helmet\nnice, you should buy it")
category1.addItem(200, 2493, 1, "Demon helmet2", "woo\ndemon helmet\nnice, you should buy it")
category1.addItem(1, 2160, 1, "1 Crystal coin3", "description of cristal coin")
category1.addItem(1, 2160, 1, "1 Crystal coin3", "description of cristal coin")
category1.addItem(5, 2160, 5, "5 Crystal coin3", "description of cristal coin")
category1.addItem(5, 2160, 5, "5 Crystal coin3", "description of cristal coin")
category1.addItem(50, 2160, 50, "50 Crystal coin3", "description of cristal coin")
category1.addItem(50, 2160, 50, "50 Crystal coin3", "description of cristal coin")
category1.addItem(90, 2160, 100, "100 Crystal coin3", "description of cristal coin")
category1.addItem(90, 2160, 100, "100 Crystal coin3", "description of cristal coin")
category1.addItem(200, 2493, 1, "Demon helmet3", "wooxD\ndemon helmet\nnice, you should buy it")
category1.addItem(200, 2493, 1, "Demon helmet3", "wooxD\ndemon helmet\nnice, you should buy it")
category2.addOutfit(500, {
category2.addOutfit(500, {
mount=0,
mount=0,
feet=114,
feet=114,
legs=114,
legs=114,
body=116,
body=116,
type=143,
type=143,
auxType=0,
auxType=0,
addons=3,
addons=3,
head=2,
head=2,
rotating=true
rotating=true
}, "title of this cool outfit or whatever", "this is your new cool outfit. You can buy it here.\nsrlsy")
}, "title of this cool outfit or whatever", "this is your new cool outfit. You can buy it here.\nsrlsy")
category2.addOutfit(100, {
category2.addOutfit(100, {
mount=682,
mount=682,
feet=0,
feet=0,
legs=0,
legs=0,
body=0,
body=0,
type=143,
type=143,
auxType=0,
auxType=0,
addons=0,
addons=0,
head=0,
head=0,
rotating=true
rotating=true
}, "MOUNT!!!", "DOUBLE CLICK TO BUY THIS MOUNT. IDK NAME")
}, "MOUNT!!!", "DOUBLE CLICK TO BUY THIS MOUNT. IDK NAME")
category2.addOutfit(100, {
category2.addOutfit(100, {
mount=0,
mount=0,
feet=0,
feet=0,
legs=0,
legs=0,
body=0,
body=0,
type=35,
type=35,
auxType=0,
auxType=0,
addons=0,
addons=0,
head=0,
head=0,
rotating=true
rotating=true
}, "Demon outfit", "Want be a demon?\nNo problem")
}, "Demon outfit", "Want be a demon?\nNo problem")
category2.addOutfit(100, {
category2.addOutfit(100, {
mount=0,
mount=0,
feet=0,
feet=0,
legs=0,
legs=0,
body=0,
body=0,
type=35,
type=35,
auxType=0,
auxType=0,
addons=0,
addons=0,
head=0,
head=0,
rotating=false
rotating=false
}, "Demon outfit2", "This one is not rotating")
}, "Demon outfit2", "This one is not rotating")
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, "/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)
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
print(category1)
end
function addCategory(data)
function addCategory(data)
복사
복사됨
복사
복사됨
print("[addCategory] input data: " .. data)
data['offers'] = {}
data['offers'] = {}
table.insert(SHOP_CATEGORIES, data)
table.insert(SHOP_CATEGORIES, data)
table.insert(SHOP_CALLBACKS, {})
table.insert(SHOP_CALLBACKS, {})
local index = #SHOP_CATEGORIES
local index = #SHOP_CATEGORIES
return {
return {
addItem = function(cost, itemId, count, title, description, callback)
addItem = function(cost, itemId, count, title, description, callback)
if not callback then
if not callback then
callback = defaultItemBuyAction
callback = defaultItemBuyAction
end
end
table.insert(SHOP_CATEGORIES[index]['offers'], {
table.insert(SHOP_CATEGORIES[index]['offers'], {
cost=cost,
cost=cost,
type="item",
type="item",
item=ItemType(itemId):getClientId(), -- displayed
item=ItemType(itemId):getClientId(), -- displayed
itemId=itemId,
itemId=itemId,
count=count,
count=count,
title=title,
title=title,
description=description
description=description
})
})
table.insert(SHOP_CALLBACKS[index], callback)
table.insert(SHOP_CALLBACKS[index], callback)
end,
end,
addOutfit = function(cost, outfit, title, description, callback)
addOutfit = function(cost, outfit, title, description, callback)
if not callback then
if not callback then
callback = defaultOutfitBuyAction
callback = defaultOutfitBuyAction
end
end
table.insert(SHOP_CATEGORIES[index]['offers'], {
table.insert(SHOP_CATEGORIES[index]['offers'], {
cost=cost,
cost=cost,
type="outfit",
type="outfit",
outfit=outfit,
outfit=outfit,
title=title,
title=title,
description=description
description=description
})
})
table.insert(SHOP_CALLBACKS[index], callback)
table.insert(SHOP_CALLBACKS[index], callback)
end,
end,
addImage = function(cost, image, title, description, callback)
addImage = function(cost, image, title, description, callback)
if not callback then
if not callback then
callback = defaultImageBuyAction
callback = defaultImageBuyAction
end
end
table.insert(SHOP_CATEGORIES[index]['offers'], {
table.insert(SHOP_CATEGORIES[index]['offers'], {
cost=cost,
cost=cost,
type="image",
type="image",
image=image,
image=image,
title=title,
title=title,
description=description
description=description
})
})
table.insert(SHOP_CALLBACKS[index], callback)
table.insert(SHOP_CALLBACKS[index], callback)
복사
복사됨
복사
복사됨
print("[addCategory] SHOP_CATEGORIES at end of function: " .. SHOP_CATEGORIES)
end
end
}
}
end
end
function getPoints(player)
function getPoints(player)
local points = 0
local points = 0
local resultId = db.storeQuery("SELECT `premium_points` FROM `accounts` WHERE `id` = " .. player:getAccountId())
local resultId = db.storeQuery("SELECT `premium_points` FROM `accounts` WHERE `id` = " .. player:getAccountId())
if resultId ~= false then
if resultId ~= false then
points = result.getDataInt(resultId, "premium_points")
points = result.getDataInt(resultId, "premium_points")
result.free(resultId)
result.free(resultId)
end
end
return points
return points
end
end
function getStatus(player)
function getStatus(player)
local status = {
local status = {
ad = SHOP_AD,
ad = SHOP_AD,
points = getPoints(player),
points = getPoints(player),
buyUrl = SHOP_BUY_URL
buyUrl = SHOP_BUY_URL
}
}
return status
return status
end
end
function sendJSON(player, action, data, forceStatus)
function sendJSON(player, action, data, forceStatus)
local status = nil
local status = nil
if not player:getStorageValue(1150001) or player:getStorageValue(1150001) + 10 < os.time() or forceStatus then
if not player:getStorageValue(1150001) or player:getStorageValue(1150001) + 10 < os.time() or forceStatus then
status = getStatus(player)
status = getStatus(player)
end
end
player:setStorageValue(1150001, os.time())
player:setStorageValue(1150001, os.time())
local buffer = json.encode({action = action, data = data, status = status})
local buffer = json.encode({action = action, data = data, status = status})
복사
복사됨
복사
복사됨
print("[sendJSON] buffer: " .. buffer )
local s = {}
local s = {}
for i=1, #buffer, MAX_PACKET_SIZE do
for i=1, #buffer, MAX_PACKET_SIZE do
s[#s+1] = buffer:sub(i,i+MAX_PACKET_SIZE - 1)
s[#s+1] = buffer:sub(i,i+MAX_PACKET_SIZE - 1)
end
end
복사
복사됨
복사
복사됨
local msg = NetworkMessage()
local msg = NetworkMessage()
if #s == 1 then
if #s == 1 then
msg:addByte(50)
msg:addByte(50)
msg:addByte(SHOP_EXTENDED_OPCODE)
msg:addByte(SHOP_EXTENDED_OPCODE)
msg:addString(s[1])
msg:addString(s[1])
msg:sendToPlayer(player)
msg:sendToPlayer(player)
return
return
end
end
-- split message if too big
-- split message if too big
msg:addByte(50)
msg:addByte(50)
msg:addByte(SHOP_EXTENDED_OPCODE)
msg:addByte(SHOP_EXTENDED_OPCODE)
msg:addString("S" .. s[1])
msg:addString("S" .. s[1])
msg:sendToPlayer(player)
msg:sendToPlayer(player)
for i=2,#s - 1 do
for i=2,#s - 1 do
msg = NetworkMessage()
msg = NetworkMessage()
msg:addByte(50)
msg:addByte(50)
msg:addByte(SHOP_EXTENDED_OPCODE)
msg:addByte(SHOP_EXTENDED_OPCODE)
msg:addString("P" .. s[i])
msg:addString("P" .. s[i])
msg:sendToPlayer(player)
msg:sendToPlayer(player)
end
end
msg = NetworkMessage()
msg = NetworkMessage()
msg:addByte(50)
msg:addByte(50)
msg:addByte(SHOP_EXTENDED_OPCODE)
msg:addByte(SHOP_EXTENDED_OPCODE)
msg:addString("E" .. s[#s])
msg:addString("E" .. s[#s])
msg:sendToPlayer(player)
msg:sendToPlayer(player)
end
end
function sendMessage(player, title, msg, forceStatus)
function sendMessage(player, title, msg, forceStatus)
sendJSON(player, "message", {title=title, msg=msg}, forceStatus)
sendJSON(player, "message", {title=title, msg=msg}, forceStatus)
end
end
function onExtendedOpcode(player, opcode, buffer)
function onExtendedOpcode(player, opcode, buffer)
if opcode ~= SHOP_EXTENDED_OPCODE then
if opcode ~= SHOP_EXTENDED_OPCODE then
return false
return false
end
end
local status, json_data = pcall(function() return json.decode(buffer) end)
local status, json_data = pcall(function() return json.decode(buffer) end)
if not status then
if not status then
return false
return false
end
end
local action = json_data['action']
local action = json_data['action']
local data = json_data['data']
local data = json_data['data']
if not action or not data then
if not action or not data then
return false
return false
end
end
if SHOP_CATEGORIES == nil then
if SHOP_CATEGORIES == nil then
init()
init()
end
end
if action == 'init' then
if action == 'init' then
복사
복사됨
복사
복사됨
print("init action sending json with categories: " .. json.encode(SHOP_CATEGORIES))
sendJSON(player, "categories", SHOP_CATEGORIES)
sendJSON(player, "categories", SHOP_CATEGORIES)
elseif action == 'buy' then
elseif action == 'buy' then
processBuy(player, data)
processBuy(player, data)
elseif action == "history" then
elseif action == "history" then
sendHistory(player)
sendHistory(player)
end
end
return true
return true
end
end
function processBuy(player, data)
function processBuy(player, data)
local categoryId = tonumber(data["category"])
local categoryId = tonumber(data["category"])
local offerId = tonumber(data["offer"])
local offerId = tonumber(data["offer"])
local offer = SHOP_CATEGORIES[categoryId]['offers'][offerId]
local offer = SHOP_CATEGORIES[categoryId]['offers'][offerId]
local callback = SHOP_CALLBACKS[categoryId][offerId]
local callback = SHOP_CALLBACKS[categoryId][offerId]
if not offer or not callback or data["title"] ~= offer["title"] or data["cost"] ~= offer["cost"] then
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
sendJSON(player, "categories", SHOP_CATEGORIES) -- refresh categories, maybe invalid
return sendMessage(player, "Error!", "Invalid offer")
return sendMessage(player, "Error!", "Invalid offer")
end
end
local points = getPoints(player)
local points = getPoints(player)
if not offer['cost'] or offer['cost'] > points or points < 1 then
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)
return sendMessage(player, "Error!", "You don't have enough points to buy " .. offer['title'] .."!", true)
end
end
local status = callback(player, offer)
local status = callback(player, offer)
if status == true then
if status == true then
db.query("UPDATE `accounts` set `premium_points` = `premium_points` - " .. offer['cost'] .. " WHERE `id` = " .. player:getAccountId())
db.query("UPDATE `accounts` set `premium_points` = `premium_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)) .. ")")
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)
return sendMessage(player, "Success!", "You bought " .. offer['title'] .."!", true)
end
end
if status == nil or status == false then
if status == nil or status == false then
status = "Unknown error while buying " .. offer['title']
status = "Unknown error while buying " .. offer['title']
end
end
sendMessage(player, "Error!", status)
sendMessage(player, "Error!", status)
end
end
function sendHistory(player)
function sendHistory(player)
if player:getStorageValue(1150002) and player:getStorageValue(1150002) + 10 > os.time() then
if player:getStorageValue(1150002) and player:getStorageValue(1150002) + 10 > os.time() then
return -- min 10s delay
return -- min 10s delay
end
end
player:setStorageValue(1150002, os.time())
player:setStorageValue(1150002, os.time())
local history = {}
local history = {}
local resultId = db.storeQuery("SELECT * FROM `shop_history` WHERE `account` = " .. player:getAccountId() .. " order by `id` DESC")
local resultId = db.storeQuery("SELECT * FROM `shop_history` WHERE `account` = " .. player:getAccountId() .. " order by `id` DESC")
if resultId ~= false then
if resultId ~= false then
repeat
repeat
local details = result.getDataString(resultId, "details")
local details = result.getDataString(resultId, "details")
local status, json_data = pcall(function() return json.decode(details) end)
local status, json_data = pcall(function() return json.decode(details) end)
if not status then
if not status then
json_data = {
json_data = {
type = "image",
type = "image",
title = result.getDataString(resultId, "title"),
title = result.getDataString(resultId, "title"),
cost = result.getDataInt(resultId, "cost")
cost = result.getDataInt(resultId, "cost")
}
}
end
end
table.insert(history, json_data)
table.insert(history, json_data)
history[#history]["description"] = "Bought on " .. result.getDataString(resultId, "date") .. " for " .. result.getDataInt(resultId, "cost") .. " points."
history[#history]["description"] = "Bought on " .. result.getDataString(resultId, "date") .. " for " .. result.getDataInt(resultId, "cost") .. " points."
until not result.next(resultId)
until not result.next(resultId)
result.free(resultId)
result.free(resultId)
end
end
sendJSON(player, "history", history)
sendJSON(player, "history", history)
end
end
-- BUY CALLBACKS
-- BUY CALLBACKS
-- May be useful: print(json.encode(offer))
-- May be useful: print(json.encode(offer))
function defaultItemBuyAction(player, offer)
function defaultItemBuyAction(player, offer)
-- todo: check if has capacity
-- todo: check if has capacity
if player:addItem(offer["itemId"], offer["count"], false) then
if player:addItem(offer["itemId"], offer["count"], false) then
return true
return true
end
end
return "Can't add item! Do you have enough space?"
return "Can't add item! Do you have enough space?"
end
end
function defaultOutfitBuyAction(player, offer)
function defaultOutfitBuyAction(player, offer)
return "default outfit buy action is not implemented"
return "default outfit buy action is not implemented"
end
end
function defaultImageBuyAction(player, offer)
function defaultImageBuyAction(player, offer)
return "default image buy action is not implemented"
return "default image buy action is not implemented"
end
end
function customImageBuyAction(player, offer)
function customImageBuyAction(player, offer)
return "custom image buy action is not implemented. Offer: " .. offer['title']
return "custom image buy action is not implemented. Offer: " .. offer['title']
end
end
저장된 비교 결과
원본
파일 열기
-- 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(2160):getClientId(), count=100, name="Items" }) local category2 = addCategory({ type="outfit", name="Outfits", outfit={ mount=0, feet=114, legs=114, body=116, type=143, auxType=0, addons=3, head=2, rotating=true } }) local category3 = addCategory({ type="image", image="http://otclient.ovh/images/137.png", name="Category with http image" }) local category4 = addCategory({ type="image", image="/data/images/game/states/electrified.png", name="Category with local image" }) category1.addItem(1, 2160, 1, "1 Crystal coin", "description of cristal coin") category1.addItem(5, 2160, 5, "5 Crystal coin", "description of cristal coin") category1.addItem(50, 2160, 50, "50 Crystal coin", "description of cristal coin") category1.addItem(90, 2160, 100, "100 Crystal coin", "description of cristal coin") category1.addItem(200, 2493, 1, "Demon helmet1", "woo\ndemon helmet\nnice, you should buy it") category1.addItem(1, 2160, 1, "1 Crystal coin1", "description of cristal coin") category1.addItem(5, 2160, 5, "5 Crystal coin1", "description of cristal coin") category1.addItem(50, 2160, 50, "50 Crystal coin1", "description of cristal coin") category1.addItem(90, 2160, 100, "100 Crystal coin1", "description of cristal coin") category1.addItem(200, 2493, 1, "Demon helmet2", "woo\ndemon helmet\nnice, you should buy it") category1.addItem(1, 2160, 1, "1 Crystal coin3", "description of cristal coin") category1.addItem(5, 2160, 5, "5 Crystal coin3", "description of cristal coin") category1.addItem(50, 2160, 50, "50 Crystal coin3", "description of cristal coin") category1.addItem(90, 2160, 100, "100 Crystal coin3", "description of cristal coin") category1.addItem(200, 2493, 1, "Demon helmet3", "wooxD\ndemon helmet\nnice, you should buy it") category2.addOutfit(500, { mount=0, feet=114, legs=114, body=116, type=143, auxType=0, addons=3, head=2, rotating=true }, "title of this cool outfit or whatever", "this is your new cool outfit. You can buy it here.\nsrlsy") category2.addOutfit(100, { mount=682, feet=0, legs=0, body=0, type=143, auxType=0, addons=0, head=0, rotating=true }, "MOUNT!!!", "DOUBLE CLICK TO BUY THIS MOUNT. IDK NAME") category2.addOutfit(100, { mount=0, feet=0, legs=0, body=0, type=35, auxType=0, addons=0, head=0, rotating=true }, "Demon outfit", "Want be a demon?\nNo problem") category2.addOutfit(100, { mount=0, feet=0, legs=0, body=0, type=35, auxType=0, addons=0, head=0, rotating=false }, "Demon outfit2", "This one is not rotating") 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):getClientId(), -- 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 `premium_points` FROM `accounts` WHERE `id` = " .. player:getAccountId()) if resultId ~= false then points = result.getDataInt(resultId, "premium_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 `accounts` set `premium_points` = `premium_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
수정본
파일 열기
-- 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(2160):getClientId(), count=100, name="Items" }) local category2 = addCategory({ type="outfit", name="Outfits", outfit={ mount=0, feet=114, legs=114, body=116, type=143, auxType=0, addons=3, head=2, rotating=true } }) local category3 = addCategory({ type="image", image="http://otclient.ovh/images/137.png", name="Category with http image" }) local category4 = addCategory({ type="image", image="/data/images/game/states/electrified.png", name="Category with local image" }) category1.addItem(1, 2160, 1, "1 Crystal coin", "description of cristal coin") category1.addItem(5, 2160, 5, "5 Crystal coin", "description of cristal coin") category1.addItem(50, 2160, 50, "50 Crystal coin", "description of cristal coin") category1.addItem(90, 2160, 100, "100 Crystal coin", "description of cristal coin") category1.addItem(200, 2493, 1, "Demon helmet1", "woo\ndemon helmet\nnice, you should buy it") category1.addItem(1, 2160, 1, "1 Crystal coin1", "description of cristal coin") category1.addItem(5, 2160, 5, "5 Crystal coin1", "description of cristal coin") category1.addItem(50, 2160, 50, "50 Crystal coin1", "description of cristal coin") category1.addItem(90, 2160, 100, "100 Crystal coin1", "description of cristal coin") category1.addItem(200, 2493, 1, "Demon helmet2", "woo\ndemon helmet\nnice, you should buy it") category1.addItem(1, 2160, 1, "1 Crystal coin3", "description of cristal coin") category1.addItem(5, 2160, 5, "5 Crystal coin3", "description of cristal coin") category1.addItem(50, 2160, 50, "50 Crystal coin3", "description of cristal coin") category1.addItem(90, 2160, 100, "100 Crystal coin3", "description of cristal coin") category1.addItem(200, 2493, 1, "Demon helmet3", "wooxD\ndemon helmet\nnice, you should buy it") category2.addOutfit(500, { mount=0, feet=114, legs=114, body=116, type=143, auxType=0, addons=3, head=2, rotating=true }, "title of this cool outfit or whatever", "this is your new cool outfit. You can buy it here.\nsrlsy") category2.addOutfit(100, { mount=682, feet=0, legs=0, body=0, type=143, auxType=0, addons=0, head=0, rotating=true }, "MOUNT!!!", "DOUBLE CLICK TO BUY THIS MOUNT. IDK NAME") category2.addOutfit(100, { mount=0, feet=0, legs=0, body=0, type=35, auxType=0, addons=0, head=0, rotating=true }, "Demon outfit", "Want be a demon?\nNo problem") category2.addOutfit(100, { mount=0, feet=0, legs=0, body=0, type=35, auxType=0, addons=0, head=0, rotating=false }, "Demon outfit2", "This one is not rotating") 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) print(category1) end function addCategory(data) print("[addCategory] input data: " .. 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):getClientId(), -- 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) print("[addCategory] SHOP_CATEGORIES at end of function: " .. SHOP_CATEGORIES) end } end function getPoints(player) local points = 0 local resultId = db.storeQuery("SELECT `premium_points` FROM `accounts` WHERE `id` = " .. player:getAccountId()) if resultId ~= false then points = result.getDataInt(resultId, "premium_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}) print("[sendJSON] buffer: " .. buffer ) 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 print("init action sending json with categories: " .. json.encode(SHOP_CATEGORIES)) 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 `accounts` set `premium_points` = `premium_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
비교하기