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,12 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true
[*.{php,css,html,xml,lua,js}]
indent_style = tab
indent_size = 4

16
app/ZnoteAAC/.gitattributes vendored Normal file
View File

@@ -0,0 +1,16 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Declare files that will always have LF line endings on checkout.
*.php text eol=lf
*.lua text eol=lf
*.html text eol=lf
*.css text eol=lf
*.js text eol=lf
*.xml text eol=lf
*.sql text eol=crlf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary

3
app/ZnoteAAC/.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: Znote

3
app/ZnoteAAC/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.cache.php
engine/cache/*
.idea

5
app/ZnoteAAC/.htaccess Normal file
View File

@@ -0,0 +1,5 @@
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /characterprofile.php?name=$1

21
app/ZnoteAAC/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Stefan André Brannfjell
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.

View File

@@ -0,0 +1,4 @@
Step 1: Copy firstitems.lua to /data/creaturescripts/scripts/ folder
-- Edit firstitems.lua with item IDs you want characters to start with on your server.
Step 2: Restart OT server, and it should work. :)

View File

@@ -0,0 +1,77 @@
function onLogin(cid)
local storage = 30055 -- storage value
local sorcItems = {
2460, -- Brass helmet
2465, -- Brass armor
2190, -- Wand of vortex
2511, -- Brass shield
2478, -- Brass legs
2643, -- Leather boots
1988, -- Brown backpack
2050 -- torch
}
local druidItems = {
2460, -- Brass helmet
2465, -- Brass armor
2511, -- Brass shield
2182, -- Snakebite rod
2478, -- Brass legs
2643, -- Leather boots
1988, -- Brown backpack
2050 -- torch
}
local pallyItems = {
2460, -- Brass helmet
2465, -- Brass armor
2456, -- Bow
2478, -- Brass legs
2643, -- Leather boots
1988, -- Brown backpack
}
local kinaItems = {
2460, -- Brass helmet
2465, -- Brass armor
2511, -- Brass shield
2412, -- Katana
2478, -- Brass legs
2643, -- Leather boots
1988, -- Brown backpack
2050 -- torch
}
if getPlayerStorageValue(cid, storage) == -1 then
setPlayerStorageValue(cid, storage, 1)
if getPlayerVocation(cid) == 1 then
-- Sorcerer
for i = 1, table.getn(sorcItems), 1 do
doPlayerAddItem(cid, sorcItems[i], 1, false)
end
elseif getPlayerVocation(cid) == 2 then
-- Druid
for i = 1, table.getn(druidItems), 1 do
doPlayerAddItem(cid, druidItems[i], 1, false)
end
elseif getPlayerVocation(cid) == 3 then
-- Paladin
for i = 1, table.getn(pallyItems), 1 do
doPlayerAddItem(cid, pallyItems[i], 1, false)
end
-- 8 arrows
doPlayerAddItem(cid, 2544, 8, false)
elseif getPlayerVocation(cid) == 4 then
-- Knight
for i = 1, table.getn(kinaItems), 1 do
doPlayerAddItem(cid, kinaItems[i], 1, false)
end
end
-- Common for all
doPlayerAddItem(cid, 2674, 5, false) -- 5 apples
doPlayerAddItem(cid, 2120, 1, false) -- 1 rope
end
return true
end

View File

@@ -0,0 +1 @@
<talkaction words="!shop" script="znoteshop.lua"/>

View File

@@ -0,0 +1,103 @@
-- Znote Shop v1.1 for Znote AAC on TFS 0.2.13+ Mystic Spirit.
function onSay(cid, 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 getPlayerStorageValue(cid, storage) <= os.time() then
setPlayerStorageValue(cid, storage, os.time() + cooldown)
local accid = getAccountNumberByPlayerName(getCreatureName(cid))
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: " .. getCreatureName(cid) .. " triggered !shop talkaction.")
-- Create the query
local orderQuery = db.storeQuery("SELECT `id`, `type`, `itemid`, `count` FROM `znote_shop_orders` WHERE `account_id` = " .. accid .. ";")
local served = false
-- Detect if we got any results
if orderQuery ~= false then
-- Fetch order values
local q_id = result.getDataInt(orderQuery, "id")
local q_type = result.getDataInt(orderQuery, "type")
local q_itemid = result.getDataInt(orderQuery, "itemid")
local q_count = result.getDataInt(orderQuery, "count")
local description = "Unknown or custom type"
if type_desc[q_type] ~= nil then
description = type_desc[q_type]
end
print("Processing type "..q_type..": ".. description)
-- ORDER TYPE 1 (Regular item shop products)
if q_type == 1 then
served = true
-- Get weight
local playerCap = getPlayerFreeCap(cid)
local itemweight = getItemWeight(q_itemid, q_count)
if playerCap >= itemweight then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
doPlayerAddItem(cid, q_itemid, q_count)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Congratulations! You have received ".. q_count .." "..getItemName(q_itemid).."(s)!")
else
doPlayerSendTextMessage(cid, 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 canPlayerWearOutfit(cid, outfitId, q_count) then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
doPlayerAddOutfit(cid,outfitId,q_count)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Congratulations! You have received a new outfit!")
else
doPlayerSendTextMessage(cid, MESSAGE_STATUS_WARNING, "You already have this outfit and addon!")
end
end
end
-- ORDER TYPE 6 (Mounts)
-- Not supported on TFS 0.2
-- 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 (Not for TFS 0.2)
-- Type 7 is for Instant house purchase (Not for TFS 0.2)
-- So use type 8+ for custom stuff, like etc packages.
-- if q_type == 8 then
-- end
result.free(orderQuery)
if not served then
doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "You have no orders to process in-game.")
end
else
doPlayerSendTextMessage(cid, MESSAGE_STATUS_WARNING, "You have no orders.")
end
else
doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Can only be executed once every "..cooldown.." seconds. Remaining cooldown: ".. getPlayerStorageValue(cid, storage) - os.time())
end
return false
end

View File

@@ -0,0 +1,10 @@
Step 1: Copy firstitems.lua to /data/creaturescripts/scripts/ folder
-- Edit firstitems.lua with item IDs you want characters to start with on your server.
Step 2: Edit the /data/creaturescripts/creaturescripts.XML file
- ADD: <event type="login" name="firstItems" event="script" value="firstitems.lua"/>
Step 3: Edit the /data/creaturescripts/scripts/login.lua file
- ADD: registerCreatureEvent(cid, "firstItems")
Step 4: Restart OT server, and it should work. :)

View File

@@ -0,0 +1,77 @@
function onLogin(cid)
local storage = 30055 -- storage value
local sorcItems = {
2460, -- Brass helmet
2465, -- Brass armor
2190, -- Wand of vortex
2511, -- Brass shield
2478, -- Brass legs
2643, -- Leather boots
1988, -- Brown backpack
2050 -- torch
}
local druidItems = {
2460, -- Brass helmet
2465, -- Brass armor
2511, -- Brass shield
2182, -- Snakebite rod
2478, -- Brass legs
2643, -- Leather boots
1988, -- Brown backpack
2050 -- torch
}
local pallyItems = {
2460, -- Brass helmet
2465, -- Brass armor
2456, -- Bow
2478, -- Brass legs
2643, -- Leather boots
1988, -- Brown backpack
}
local kinaItems = {
2460, -- Brass helmet
2465, -- Brass armor
2511, -- Brass shield
2412, -- Katana
2478, -- Brass legs
2643, -- Leather boots
1988, -- Brown backpack
2050 -- torch
}
if getPlayerStorageValue(cid, storage) == -1 then
setPlayerStorageValue(cid, storage, 1)
if getPlayerVocation(cid) == 1 then
-- Sorcerer
for i = 1, table.getn(sorcItems), 1 do
doPlayerAddItem(cid, sorcItems[i], 1, false)
end
elseif getPlayerVocation(cid) == 2 then
-- Druid
for i = 1, table.getn(druidItems), 1 do
doPlayerAddItem(cid, druidItems[i], 1, false)
end
elseif getPlayerVocation(cid) == 3 then
-- Paladin
for i = 1, table.getn(pallyItems), 1 do
doPlayerAddItem(cid, pallyItems[i], 1, false)
end
-- 8 arrows
doPlayerAddItem(cid, 2544, 8, false)
elseif getPlayerVocation(cid) == 4 then
-- Knight
for i = 1, table.getn(kinaItems), 1 do
doPlayerAddItem(cid, kinaItems[i], 1, false)
end
end
-- Common for all
doPlayerAddItem(cid, 2674, 5, false) -- 5 apples
doPlayerAddItem(cid, 2120, 1, false) -- 1 rope
end
return true
end

View File

@@ -0,0 +1,7 @@
1. Add below line to XML file: data/creaturescripts/creaturescripts.xml
<event type="login" name="znote_syncoutfits" event="script" value="syncoutfit.lua"/>
2. Register event in login.lua: data/creaturescripts/scripts/login.lua
registerCreatureEvent(cid, "znote_syncoutfits")
3. Place Lua file syncoutfit.lua in folder: data/creaturescripts/scripts/

View File

@@ -0,0 +1,39 @@
-- Sync outfits that player own with Znote AAC
-- So its possible to see which full sets player
-- has in characterprofile.php
znote_outfit_list = {
{ -- Female (girl) outfits
136,137,138,139,140,141,142,147,148,
149,150,155,156,157,158,252,269,270,
279,288,324,329,336,366,431,433,464,
466,471,513,514,542,575,578,618,620,
632,635,636,664,666,683,694,696,698,
724,732,745,749,759,845,852,874,885,
900
},
{ -- Male (boy) outfits
128,129,130,131,132,133,134,143,144,
145,146,151,152,153,154,251,268,273,
278,289,325,328,335,367,430,432,463,
465,472,512,516,541,574,577,610,619,
633,634,637,665,667,684,695,697,699,
725,733,746,750,760,846,853,873,884,
899
}
}
function onLogin(cid)
-- storage_value + 1000 storages (highest outfit id) must not be used in other script.
-- Must be identical to Znote AAC config.php: $config['EQ_shower'] -> storage_value
local storage_value = 10000
-- Loop through outfits
for _, outfit in pairs(znote_outfit_list[getPlayerSex(cid)+1]) do
if canPlayerWearOutfit(cid,outfit,3) then
if getPlayerStorageValue(cid,storage_value + outfit) ~= 3 then
setPlayerStorageValue(cid,storage_value + outfit, 3)
end
end
end
return true
end

View File

@@ -0,0 +1,118 @@
-- Znote Shop v1.1 for Znote AAC on TFS 0.3.6+ Crying Damson. [Alternative]
function onSay(cid, 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 getPlayerStorageValue(cid, storage) <= os.time() then
setPlayerStorageValue(cid, storage, os.time() + cooldown)
local accid = getAccountNumberByPlayerName(getCreatureName(cid))
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: " .. getCreatureName(cid) .. " triggered !shop talkaction.")
-- Create the query
local orderQuery = db.storeQuery("SELECT `id`, `type`, `itemid`, `count` FROM `znote_shop_orders` WHERE `account_id` = " .. accid .. ";")
local served = false
-- Detect if we got any results
if orderQuery ~= false then
repeat
-- Fetch order values
local q_id = result.getDataInt(orderQuery, "id")
local q_type = result.getDataInt(orderQuery, "type")
local q_itemid = result.getDataInt(orderQuery, "itemid")
local q_count = result.getDataInt(orderQuery, "count")
local description = "Unknown or custom type"
if type_desc[q_type] ~= nil then
description = type_desc[q_type]
end
print("Processing type "..q_type..": ".. description)
-- ORDER TYPE 1 (Regular item shop products)
if q_type == 1 then
served = true
-- Get wheight
local playerCap = getPlayerFreeCap(cid)
local itemweight = getItemWeightById(q_itemid, q_count)
if playerCap >= itemweight then
local delete = db.storeQuery("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
result.free(delete)
doPlayerAddItem(cid, q_itemid, q_count)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Congratulations! You have recieved ".. q_count .." "..getItemNameById(q_itemid).."(s)!")
else
doPlayerSendTextMessage(cid, 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 canPlayerWearOutfit(cid, outfitId, q_count) then
local delete = db.storeQuery("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
result.free(delete)
doPlayerAddOutfit(cid,outfitId,q_count)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Congratulations! You have received a new outfit!")
else
doPlayerSendTextMessage(cid, 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 getPlayerMount(cid, q_itemid) then -- Failed to find a proper hasMount 0.3 function?
local delete = db.storeQuery("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
result.free(delete)
doPlayerAddMount(cid, q_itemid)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Congratulations! You have received a new mount!")
else
doPlayerSendTextMessage(cid, MESSAGE_STATUS_WARNING, "You already have this mount!")
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)
-- Type 7 is for Instant house purchase (Not for TFS 0.3)
-- So use type 8+ for custom stuff, like etc packages.
-- if q_type == 8 then
-- end
until not result.next(orderQuery)
result.free(orderQuery)
if not served then
doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "You have no orders to process in-game.")
end
else
doPlayerSendTextMessage(cid, MESSAGE_STATUS_WARNING, "You have no orders.")
end
else
doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Can only be executed once every "..cooldown.." seconds. Remaining cooldown: ".. getPlayerStorageValue(cid, storage) - os.time())
end
return false
end

View File

@@ -0,0 +1 @@
<talkaction words="!shop" event="script" value="znoteshop.lua"/>

View File

@@ -0,0 +1,128 @@
-- Znote Shop v1.1 for Znote AAC on TFS 0.3.6+ Crying Damson.
function onSay(cid, 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 getPlayerStorageValue(cid, storage) <= os.time() then
setPlayerStorageValue(cid, storage, os.time() + cooldown)
local accid = getAccountNumberByPlayerName(getCreatureName(cid))
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: " .. getCreatureName(cid) .. " triggered !shop talkaction.")
-- Create the query
local orderQuery = db.storeQuery("SELECT `id`, `type`, `itemid`, `count` FROM `znote_shop_orders` WHERE `account_id` = " .. accid .. ";")
local served = false
-- Detect if we got any results
if orderQuery ~= false then
repeat
-- Fetch order values
local q_id = result.getDataInt(orderQuery, "id")
local q_type = result.getDataInt(orderQuery, "type")
local q_itemid = result.getDataInt(orderQuery, "itemid")
local q_count = result.getDataInt(orderQuery, "count")
local description = "Unknown or custom type"
if type_desc[q_type] ~= nil then
description = type_desc[q_type]
end
print("Processing type "..q_type..": ".. description)
-- ORDER TYPE 1 (Regular item shop products)
if q_type == 1 then
served = true
-- Get weight
local playerCap = getPlayerFreeCap(cid)
local itemweight = getItemWeightById(q_itemid, q_count)
if playerCap >= itemweight and getTileInfo(getCreaturePosition(cid)).protection then
-- backpack check
local backpack = getPlayerSlotItem(cid, 3)
local gotItem = false
if(backpack and backpack.itemid > 0) then
local received = doAddContainerItem(getPlayerSlotItem(cid, 3).uid, q_itemid,q_count)
if(received ~= false) then
db.executeQuery("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Congratulations! You have received ".. q_count .." "..getItemNameById(q_itemid).."(s)!")
gotItem = true
end
end
if(not gotItem) then
doPlayerSendTextMessage(cid, MESSAGE_STATUS_WARNING, "You have no available space in backpack to receive that item.")
end
else
doPlayerSendTextMessage(cid, MESSAGE_STATUS_WARNING, "Need more CAP and Need ProtectZone!")
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 canPlayerWearOutfit(cid, outfitId, q_count) then
db.executeQuery("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
doPlayerAddOutfit(cid,outfitId,q_count)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Congratulations! You have received a new outfit!")
else
doPlayerSendTextMessage(cid, 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 getPlayerMount(cid, q_itemid) then -- Failed to find a proper hasMount 0.3 function?
db.executeQuery("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";")
doPlayerAddMount(cid, q_itemid)
doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Congratulations! You have received a new mount!")
else
doPlayerSendTextMessage(cid, MESSAGE_STATUS_WARNING, "You already have this mount!")
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)
-- Type 7 is for Instant house purchase (Not for TFS 0.3)
-- So use type 8+ for custom stuff, like etc packages.
-- if q_type == 8 then
-- end
until not result.next(orderQuery)
result.free(orderQuery)
if not served then
doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "You have no orders to process in-game.")
end
else
doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "You have no orders.")
end
else
doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "Can only be executed once every "..cooldown.." seconds. Remaining cooldown: ".. getPlayerStorageValue(cid, storage) - os.time())
end
return false
end

View File

@@ -0,0 +1,4 @@
Step 1: Copy firstitems.lua to /data/creaturescripts/scripts/ folder
-- Edit firstitems.lua with item IDs you want characters to start with on your server.
Step 2: Restart OT server, and it should work. :)

View File

@@ -0,0 +1,114 @@
-- With Rookgaard
--[[
local firstItems = {2050, 2382} -- torch and club
function onLogin(player)
if player:getLastLoginSaved() <= 0 then
for i = 1, #firstItems do
player:addItem(firstItems[i], 1)
end
player:addItem(player:getSex() == 0 and 2651 or 2650, 1) -- coat
player:addItem(ITEM_BAG, 1)
player:addItem(2674, 1) -- red apple
end
return true
end
]]--
-- Without Rookgaard
local config = {
[1] = { -- Sorcerer
items = {
{2175, 1}, -- spellbook
{2190, 1}, -- wand of vortex
{8819, 1}, -- magician's robe
{8820, 1}, -- mage hat
{2468, 1}, -- studded legs
{2643, 1}, -- leather boots
{2661, 1} -- scarf
},
container = {
{2120, 1}, -- rope
{2554, 1}, -- shovel
{7620, 1} -- mana potion
}
},
[2] = { -- Druid
items = {
{2175, 1}, -- spellbook
{2182, 1}, -- snakebite rod
{8819, 1}, -- magician's robe
{8820, 1}, -- mage hat
{2468, 1}, -- studded legs
{2643, 1}, -- leather boots
{2661, 1} -- scarf
},
container = {
{2120, 1}, -- rope
{2554, 1}, -- shovel
{7620, 1} -- mana potion
}
},
[3] = { -- Paladin
items = {
{2525, 1}, -- dwarven shield
{2389, 5}, -- 5 spears
{2660, 1}, -- ranger's cloak
{8923, 1}, -- ranger legs
{2643, 1}, -- leather boots
{2661, 1}, -- scarf
{2480, 1} -- legion helmet
},
container = {
{2120, 1}, -- rope
{2554, 1}, -- shovel
{7618, 1}, -- health potion
{2456, 1}, -- bow
{2544, 50} -- 50 arrows
}
},
[4] = { -- Knight
items = {
{2525, 1}, -- dwarven shield
{8601, 1}, -- steel axe
{2465, 1}, -- brass armor
{2460, 1}, -- brass helmet
{2478, 1}, -- brass legs
{2643, 1}, -- leather boots
{2661, 1} -- scarf
},
container = {
{8602, 1}, -- jagged sword
{2439, 1}, -- daramanian mace
{2120, 1}, -- rope
{2554, 1}, -- shovel
{7618, 1} -- health potion
}
}
}
function onLogin(player)
local targetVocation = config[player:getVocation():getId()]
if not targetVocation then
return true
end
if player:getLastLoginSaved() ~= 0 then
return true
end
for i = 1, #targetVocation.items do
player:addItem(targetVocation.items[i][1], targetVocation.items[i][2])
end
local backpack = player:addItem(1988)
if not backpack then
return true
end
for i = 1, #targetVocation.container do
backpack:addItem(targetVocation.container[i][1], targetVocation.container[i][2])
end
return true
end

View File

@@ -0,0 +1,3 @@
Step 1: Replace the one that comes with tfs with this one
Step 2: Restart OT server, and it should work. :)

View File

@@ -0,0 +1,122 @@
local deathListEnabled = true
local maxDeathRecords = 5
local function sendWarStatus(guildId, enemyGuildId, warId, playerName, killerName)
local guild, enemyGuild = Guild(guildId), Guild(enemyGuildId)
if not guild or not enemyGuild then
return
end
local resultId = db.storeQuery("SELECT `guild_wars`.`id`, (SELECT `limit` FROM `znote_guild_wars` WHERE `znote_guild_wars`.`id` = `guild_wars`.`id`) AS `limit`, (SELECT COUNT(1) FROM `guildwar_kills` WHERE `guildwar_kills`.`warid` = `guild_wars`.`id` AND `guildwar_kills`.`killerguild` = `guild_wars`.`guild1`) guild1_kills, (SELECT COUNT(1) FROM `guildwar_kills` WHERE `guildwar_kills`.`warid` = `guild_wars`.`id` AND `guildwar_kills`.`killerguild` = `guild_wars`.`guild2`) guild2_kills FROM `guild_wars` WHERE (`guild1` = " .. guildId .. " OR `guild2` = " .. guildId .. ") AND `status` = 1 AND `id` = " .. warId)
if resultId then
local guild1_kills = result.getNumber(resultId, "guild1_kills")
local guild2_kills = result.getNumber(resultId, "guild2_kills")
local limit = result.getNumber(resultId, "limit")
result.free(resultId)
local members = guild:getMembersOnline()
for i = 1, #members do
members[i]:sendChannelMessage("", string.format("%s was killed by %s. The new score is %d:%d frags (limit: %d)", playerName, killerName, guild1_kills, guild2_kills, limit), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
end
local enemyMembers = enemyGuild:getMembersOnline()
for i = 1, #enemyMembers do
enemyMembers[i]:sendChannelMessage("", string.format("%s was killed by %s. The new score is %d:%d frags (limit: %d)", playerName, killerName, guild1_kills, guild2_kills, limit), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
end
if guild1_kills >= limit or guild2_kills >= limit then
db.query("UPDATE `guild_wars` SET `status` = 4, `ended` = " .. os.time() .. " WHERE `status` = 1 AND `id` = " .. warId)
Game.broadcastMessage(string.format("%s has just won the war against %s.", guild:getName(), enemyGuild:getName()), MESSAGE_EVENT_ADVANCE)
end
end
end
function onDeath(player, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified)
local playerId = player:getId()
if nextUseStaminaTime[playerId] then
nextUseStaminaTime[playerId] = nil
end
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are dead.")
if not deathListEnabled then
return
end
local byPlayer = 0
local killerName
if killer then
if killer:isPlayer() then
byPlayer = 1
else
local master = killer:getMaster()
if master and master ~= killer and master:isPlayer() then
killer = master
byPlayer = 1
end
end
killerName = killer:getName()
else
killerName = "field item"
end
local byPlayerMostDamage = 0
local mostDamageKillerName
if mostDamageKiller then
if mostDamageKiller:isPlayer() then
byPlayerMostDamage = 1
else
local master = mostDamageKiller:getMaster()
if master and master ~= mostDamageKiller and master:isPlayer() then
mostDamageKiller = master
byPlayerMostDamage = 1
end
end
mostDamageName = mostDamageKiller:getName()
else
mostDamageName = "field item"
end
local playerGuid = player:getGuid()
db.query("INSERT INTO `player_deaths` (`player_id`, `time`, `level`, `killed_by`, `is_player`, `mostdamage_by`, `mostdamage_is_player`, `unjustified`, `mostdamage_unjustified`) VALUES (" .. playerGuid .. ", " .. os.time() .. ", " .. player:getLevel() .. ", " .. db.escapeString(killerName) .. ", " .. byPlayer .. ", " .. db.escapeString(mostDamageName) .. ", " .. byPlayerMostDamage .. ", " .. (lastHitUnjustified and 1 or 0) .. ", " .. (mostDamageUnjustified and 1 or 0) .. ")")
local resultId = db.storeQuery("SELECT `player_id` FROM `player_deaths` WHERE `player_id` = " .. playerGuid)
local deathRecords = 0
local tmpResultId = resultId
while tmpResultId ~= false do
tmpResultId = result.next(resultId)
deathRecords = deathRecords + 1
end
if resultId ~= false then
result.free(resultId)
end
local limit = deathRecords - maxDeathRecords
if limit > 0 then
db.asyncQuery("DELETE FROM `player_deaths` WHERE `player_id` = " .. playerGuid .. " ORDER BY `time` LIMIT " .. limit)
end
if byPlayer == 1 then
local targetGuild = player:getGuild()
targetGuild = targetGuild and targetGuild:getId() or 0
if targetGuild ~= 0 then
local killerGuild = killer:getGuild()
killerGuild = killerGuild and killerGuild:getId() or 0
if killerGuild ~= 0 and targetGuild ~= killerGuild and isInWar(playerId, killer:getId()) then
local warId = false
resultId = db.storeQuery("SELECT `id` FROM `guild_wars` WHERE `status` = 1 AND ((`guild1` = " .. killerGuild .. " AND `guild2` = " .. targetGuild .. ") OR (`guild1` = " .. targetGuild .. " AND `guild2` = " .. killerGuild .. "))")
if resultId ~= false then
warId = result.getNumber(resultId, "id")
result.free(resultId)
end
if warId ~= false then
local playerName = player:getName()
db.asyncQuery("INSERT INTO `guildwar_kills` (`killer`, `target`, `killerguild`, `targetguild`, `time`, `warid`) VALUES (" .. db.escapeString(killerName) .. ", " .. db.escapeString(playerName) .. ", " .. killerGuild .. ", " .. targetGuild .. ", " .. os.time() .. ", " .. warId .. ")")
addEvent(sendWarStatus, 1000, killerGuild, targetGuild, warId, playerName, killerName)
end
end
end
end
end

View File

@@ -0,0 +1,4 @@
1. Add below line to XML file: data/creaturescripts/creaturescripts.xml
<event type="login" name="znote_syncoutfits" script="syncoutfit.lua" />
2. Place Lua file syncoutfit.lua in folder: data/creaturescripts/scripts/

View File

@@ -0,0 +1,46 @@
-- Sync outfits that player own with Znote AAC
-- So its possible to see which full sets player
-- has in characterprofile.php
znote_outfit_list = {
{ -- Female outfits
136, 137, 138, 139, 140, 141, 142, 147, 148,
149, 150, 155, 156, 157, 158, 252, 269, 270,
279, 288, 324, 329, 336, 366, 431, 433, 464,
466, 471, 513, 514, 542, 575, 578, 618, 620,
632, 635, 636, 664, 666, 683, 694, 696, 698,
724, 732, 745, 749, 759, 845, 852, 874, 885,
900, 973, 975, 1020, 1024, 1043, 1050, 1057,
1070, 1095, 1103, 1128, 1147, 1162, 1174,
1187, 1203, 1205, 1207, 1211, 1246, 1244,
1252, 1271, 1280, 1283, 1289, 1293, 1332
},
{ -- Male outfits
128, 129, 130, 131, 132, 133, 134, 143, 144,
145, 146, 151, 152, 153, 154, 251, 268, 273,
278, 289, 325, 328, 335, 367, 430, 432, 463,
465, 472, 512, 516, 541, 574, 577, 610, 619,
633, 634, 637, 665, 667, 684, 695, 697, 699,
725, 733, 746, 750, 760, 846, 853, 873, 884,
899, 908, 931, 955, 957, 962, 964, 966, 968,
970, 972, 974, 1021, 1023, 1042, 1051, 1056,
1069, 1094, 1102, 1127, 1146, 1161, 1173,
1186, 1202, 1204, 1206, 1210, 1245, 1243,
1251, 1270, 1279, 1282, 1288, 1292, 1331
}
}
function onLogin(player)
-- storage_value + 1000 storages (highest outfit id) must not be used in other script.
-- Must be identical to Znote AAC config.php: $config['EQ_shower'] -> storage_value
local storage_value = 10000
-- Loop through outfits
for _, outfit in pairs(znote_outfit_list[player:getSex() + 1]) do
if player:hasOutfit(outfit,3) then
if player:getStorageValue(storage_value + outfit) ~= 3 then
player:setStorageValue(storage_value + outfit, 3)
end
end
end
return true
end

View File

@@ -0,0 +1,64 @@
-- getEternalStorage and setEternalStorage
-- can be added to data/global.lua if you want to use eternal storage for another purpose than this.
-- Regular TFS global storage values get reset every time server reboots. This does not.
local function getEternalStorage(key, parser)
local value = result.getString(db.storeQuery("SELECT `value` FROM `znote_global_storage` WHERE `key` = ".. key .. ";"), "value")
if not value then
if parser then
return false
else
return -1
end
end
result.free(value)
return tonumber(value) or value
end
local function setEternalStorage(key, value)
if getEternalStorage(key, true) then
db.query("UPDATE `znote_global_storage` SET `value` = '".. value .. "' WHERE `key` = ".. key .. ";")
else
db.query("INSERT INTO `znote_global_storage` (`key`, `value`) VALUES (".. key ..", ".. value ..");")
end
return true
end
-- SQL Query to execute: --
--[[
ALTER TABLE `znote_players` ADD `exphist_lastexp` BIGINT NOT NULL DEFAULT '0',
ADD `exphist1` BIGINT NOT NULL DEFAULT '0',
ADD `exphist2` BIGINT NOT NULL DEFAULT '0',
ADD `exphist3` BIGINT NOT NULL DEFAULT '0',
ADD `exphist4` BIGINT NOT NULL DEFAULT '0',
ADD `exphist5` BIGINT NOT NULL DEFAULT '0',
ADD `exphist6` BIGINT NOT NULL DEFAULT '0',
ADD `exphist7` BIGINT NOT NULL DEFAULT '0',
ADD `onlinetimetoday` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime1` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime2` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime3` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime4` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime5` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime6` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime7` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetimeall` INT UNSIGNED NOT NULL DEFAULT '0';
]]--
-- after that execute: --
--[[
UPDATE `znote_players` AS `z` INNER JOIN `players` AS `p` ON `p`.`id`=`z`.`player_id` SET `z`.`exphist_lastexp`=`p`.`experience`;
]]--
-- TFS 1.X (data/globalevents.xml)
-- <!-- Power Gamers -->
-- <globalevent name="PowerGamers" interval="60000" script="powergamers.lua"/>
function onThink(interval, lastExecution, thinkInterval)
if tonumber(os.date("%d")) ~= getEternalStorage(23856) then
setEternalStorage(23856, (tonumber(os.date("%d"))))
db.query("UPDATE `znote_players` SET `onlinetime7`=`onlinetime6`, `onlinetime6`=`onlinetime5`, `onlinetime5`=`onlinetime4`, `onlinetime4`=`onlinetime3`, `onlinetime3`=`onlinetime2`, `onlinetime2`=`onlinetime1`, `onlinetime1`=`onlinetimetoday`, `onlinetimetoday`=0;")
db.query("UPDATE `znote_players` `z` INNER JOIN `players` `p` ON `p`.`id`=`z`.`player_id` SET `z`.`exphist7`=`z`.`exphist6`, `z`.`exphist6`=`z`.`exphist5`, `z`.`exphist5`=`z`.`exphist4`, `z`.`exphist4`=`z`.`exphist3`, `z`.`exphist3`=`z`.`exphist2`, `z`.`exphist2`=`z`.`exphist1`, `z`.`exphist1`=`p`.`experience`-`z`.`exphist_lastexp`, `z`.`exphist_lastexp`=`p`.`experience`;")
end
db.query("UPDATE `znote_players` SET `onlinetimetoday` = `onlinetimetoday` + 60, `onlinetimeall` = `onlinetimeall` + 60 WHERE `player_id` IN (SELECT `player_id` FROM `players_online` WHERE `players_online`.`player_id` = `znote_players`.`player_id`)")
return true
end

View File

@@ -0,0 +1,160 @@
-- <globalevent name="Znote Shop" interval="30000" script="znoteshop.lua"/>
-- Znote Auto Shop v2.1 for Znote AAC on TFS 1.2+
function onThink(interval, lastExecution)
local shopTypes = {1,5,7}
-- If game support mount orders
if Game.getClientVersion().min >= 870 then
table.insert(shopTypes, 6);
end
local orderQuery = db.storeQuery([[
SELECT
MIN(`po`.`player_id`) AS `player_id`,
`shop`.`id`,
`shop`.`type`,
`shop`.`itemid`,
`shop`.`count`
FROM `players_online` AS `po`
INNER JOIN `players` AS `p`
ON `po`.`player_id` = `p`.`id`
INNER JOIN `znote_shop_orders` AS `shop`
ON `p`.`account_id` = `shop`.`account_id`
WHERE `shop`.`type` IN(]] .. table.concat(shopTypes, ",") .. [[)
GROUP BY `shop`.`id`
]])
-- Detect if we got any results
if orderQuery ~= false then
local type_desc = {
"itemids",
"pending premium (skip)",
"pending gender change (skip)",
"pending character name change (skip)",
"Outfit and addons",
"Mounts",
"Instant house purchase"
}
repeat
local player_id = result.getNumber(orderQuery, 'player_id')
local orderId = result.getNumber(orderQuery, 'id')
local orderType = result.getNumber(orderQuery, 'type')
local orderItemId = result.getNumber(orderQuery, 'itemid')
local orderCount = result.getNumber(orderQuery, 'count')
local served = false
local player = Player(player_id)
if player ~= nil then
local description = "Unknown or custom type"
if type_desc[orderType] ~= nil then
description = type_desc[orderType]
end
print("Processing type "..orderType..": ".. description)
print("Processing shop order for: [".. player:getName() .."] type "..orderType..": ".. description)
local tile = Tile(player:getPosition())
if tile ~= nil and tile:hasFlag(TILESTATE_PROTECTIONZONE) then
-- ORDER TYPE 1 (Regular item shop products)
if orderType == 1 then
served = true
local itemType = ItemType(orderItemId)
-- Get weight
if player:getFreeCapacity() >= itemType:getWeight(orderCount) then
local backpack = player:getSlotItem(CONST_SLOT_BACKPACK)
-- variable = (condition) and (return if true) or (return if false)
local needslots = itemType:isStackable() and math.floor(orderCount / 100) + 1 or orderCount
if backpack ~= nil and backpack:getEmptySlots(false) >= needslots then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";")
player:addItem(orderItemId, orderCount)
player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. "!")
print("Process complete. [".. player:getName() .."] has received " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".")
else -- not enough slots
player:sendTextMessage(MESSAGE_STATUS_WARNING, "Your main backpack is full. You need to free up "..needslots.." available slots to get " .. orderCount .. " " .. ItemType(orderItemId):getName() .. "!")
print("Process canceled. [".. player:getName() .."] need more space in his backpack to get " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".")
end
else -- not enough cap
player:sendTextMessage(MESSAGE_STATUS_WARNING, "You need more CAP to carry this order!")
print("Process canceled. [".. player:getName() .."] need more cap to carry " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".")
end
end
-- ORDER TYPE 5 (Outfit and addon)
if orderType == 5 then
served = true
local itemid = orderItemId
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, orderCount) then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";")
player:addOutfit(outfitId)
player:addOutfitAddon(outfitId, orderCount)
player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new outfit!")
print("Process complete. [".. player:getName() .."] has received outfit: ["..outfitId.."] with addon: ["..orderCount.."]")
else -- Already has outfit
player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this outfit and addon!")
print("Process canceled. [".. player:getName() .."] already have outfit: ["..outfitId.."] with addon: ["..orderCount.."].")
end
end
end
-- ORDER TYPE 6 (Mounts)
if orderType == 6 then
served = true
-- Make sure player don't already have this outfit and addon
if not player:hasMount(orderItemId) then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";")
player:addMount(orderItemId)
player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new mount!")
print("Process complete. [".. player:getName() .."] has received mount: ["..orderItemId.."]")
else -- Already has mount
player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this mount!")
print("Process canceled. [".. player:getName() .."] already have mount: ["..orderItemId.."].")
end
end
-- ORDER TYPE 7 (Direct house purchase)
if orderType == 7 then
served = true
local house = House(orderItemId)
-- Logged in player is not necessarily the player that bough the house. So we need to load player from db.
local buyerQuery = db.storeQuery("SELECT `name` FROM `players` WHERE `id` = "..orderCount.." LIMIT 1")
if buyerQuery ~= false then
local buyerName = result.getString(buyerQuery, "name")
result.free(buyerQuery)
if house then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";")
house:setOwnerGuid(orderCount)
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 received house: ["..house:getName().."]")
else
print("Process canceled. Failed to load house with ID: "..orderItemId)
end
else
print("Process canceled. Failed to load player with ID: "..orderCount)
end
end
if not served then -- If this order hasn't been processed yet (missing type handling?)
print("Znote shop: Type ["..orderType.."] not properly processed. Missing Lua code?")
end
else -- Not in protection zone
player:sendTextMessage(MESSAGE_INFO_DESCR, 'You have a pending shop order, please enter protection zone.')
print("Skipped one shop order. Reason: Player: [".. player:getName() .."] is not inside protection zone.")
end
else -- player not logged in
print("Skipped one shop order. Reason: Player with id [".. player_id .."] is not online.")
end
until not result.next(orderQuery)
result.free(orderQuery)
end
return true
end

View File

@@ -0,0 +1,3 @@
Step 1: Put script on data/script folder (edit content if necessary)
Step 2: Restart OT server, and it should work. :)

View File

@@ -0,0 +1,99 @@
local creatureevent = CreatureEvent("FirstItems")
local config = {
[1] = { -- Sorcerer
items = {
{2175, 1}, -- spellbook
{2190, 1}, -- wand of vortex
{8819, 1}, -- magician's robe
{8820, 1}, -- mage hat
{2468, 1}, -- studded legs
{2643, 1}, -- leather boots
{2661, 1} -- scarf
},
container = {
{2120, 1}, -- rope
{2554, 1}, -- shovel
{7620, 1} -- mana potion
}
},
[2] = { -- Druid
items = {
{2175, 1}, -- spellbook
{2182, 1}, -- snakebite rod
{8819, 1}, -- magician's robe
{8820, 1}, -- mage hat
{2468, 1}, -- studded legs
{2643, 1}, -- leather boots
{2661, 1} -- scarf
},
container = {
{2120, 1}, -- rope
{2554, 1}, -- shovel
{7620, 1} -- mana potion
}
},
[3] = { -- Paladin
items = {
{2525, 1}, -- dwarven shield
{2389, 5}, -- 5 spears
{2660, 1}, -- ranger's cloak
{8923, 1}, -- ranger legs
{2643, 1}, -- leather boots
{2661, 1}, -- scarf
{2480, 1} -- legion helmet
},
container = {
{2120, 1}, -- rope
{2554, 1}, -- shovel
{7618, 1}, -- health potion
{2456, 1}, -- bow
{2544, 50} -- 50 arrows
}
},
[4] = { -- Knight
items = {
{2525, 1}, -- dwarven shield
{8601, 1}, -- steel axe
{2465, 1}, -- brass armor
{2460, 1}, -- brass helmet
{2478, 1}, -- brass legs
{2643, 1}, -- leather boots
{2661, 1} -- scarf
},
container = {
{8602, 1}, -- jagged sword
{2439, 1}, -- daramanian mace
{2120, 1}, -- rope
{2554, 1}, -- shovel
{7618, 1} -- health potion
}
}
}
function creatureevent.onLogin(player)
local targetVocation = config[player:getVocation():getId()]
if not targetVocation then
return true
end
if player:getLastLoginSaved() ~= 0 then
return true
end
for i = 1, #targetVocation.items do
player:addItem(targetVocation.items[i][1], targetVocation.items[i][2])
end
local backpack = player:addItem(1988) -- backpack
if not backpack then
return true
end
for i = 1, #targetVocation.container do
backpack:addItem(targetVocation.container[i][1], targetVocation.container[i][2])
end
return true
end
creatureevent:register()

View File

@@ -0,0 +1,17 @@
local creatureevent = CreatureEvent("FirstItemsRook")
local firstItems = {2050, 2382} -- torch and club
function creatureevent.onLogin(player)
if player:getLastLoginSaved() <= 0 then
for i = 1, #firstItems do
player:addItem(firstItems[i], 1)
end
player:addItem(player:getSex() == 0 and 2651 or 2650, 1) -- coat
player:addItem(ITEM_BAG, 1)
player:addItem(2674, 1) -- red apple
end
return true
end
creatureevent:register()

View File

@@ -0,0 +1,135 @@
local deathListEnabled = true
local maxDeathRecords = 5
local function sendWarStatus(guildId, enemyGuildId, warId, playerName, killerName)
local guild, enemyGuild = Guild(guildId), Guild(enemyGuildId)
if not guild or not enemyGuild then
return
end
local resultId = db.storeQuery("SELECT `guild_wars`.`id`, (SELECT `limit` FROM `znote_guild_wars` WHERE `znote_guild_wars`.`id` = `guild_wars`.`id`) AS `limit`, (SELECT COUNT(1) FROM `guildwar_kills` WHERE `guildwar_kills`.`warid` = `guild_wars`.`id` AND `guildwar_kills`.`killerguild` = `guild_wars`.`guild1`) guild1_kills, (SELECT COUNT(1) FROM `guildwar_kills` WHERE `guildwar_kills`.`warid` = `guild_wars`.`id` AND `guildwar_kills`.`killerguild` = `guild_wars`.`guild2`) guild2_kills FROM `guild_wars` WHERE (`guild1` = " .. guildId .. " OR `guild2` = " .. guildId .. ") AND `status` = 1 AND `id` = " .. warId)
if resultId then
local guild1_kills = result.getNumber(resultId, "guild1_kills")
local guild2_kills = result.getNumber(resultId, "guild2_kills")
local limit = result.getNumber(resultId, "limit")
result.free(resultId)
local members = guild:getMembersOnline()
for i = 1, #members do
members[i]:sendChannelMessage("", string.format("%s was killed by %s. The new score is %d:%d frags (limit: %d)", playerName, killerName, guild1_kills, guild2_kills, limit), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
end
local enemyMembers = enemyGuild:getMembersOnline()
for i = 1, #enemyMembers do
enemyMembers[i]:sendChannelMessage("", string.format("%s was killed by %s. The new score is %d:%d frags (limit: %d)", playerName, killerName, guild1_kills, guild2_kills, limit), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD)
end
if guild1_kills >= limit or guild2_kills >= limit then
db.query("UPDATE `guild_wars` SET `status` = 4, `ended` = " .. os.time() .. " WHERE `status` = 1 AND `id` = " .. warId)
Game.broadcastMessage(string.format("%s has just won the war against %s.", guild:getName(), enemyGuild:getName()), MESSAGE_EVENT_ADVANCE)
end
end
end
local creatureevent = CreatureEvent("PlayerDeath")
function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified)
local playerId = player:getId()
if nextUseStaminaTime[playerId] then
nextUseStaminaTime[playerId] = nil
end
player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are dead.")
if not deathListEnabled then
return
end
local byPlayer = 0
local killerName
if killer then
if killer:isPlayer() then
byPlayer = 1
else
local master = killer:getMaster()
if master and master ~= killer and master:isPlayer() then
killer = master
byPlayer = 1
end
end
killerName = killer:getName()
else
killerName = "field item"
end
local byPlayerMostDamage = 0
local mostDamageKillerName
if mostDamageKiller then
if mostDamageKiller:isPlayer() then
byPlayerMostDamage = 1
else
local master = mostDamageKiller:getMaster()
if master and master ~= mostDamageKiller and master:isPlayer() then
mostDamageKiller = master
byPlayerMostDamage = 1
end
end
mostDamageName = mostDamageKiller:getName()
else
mostDamageName = "field item"
end
local playerGuid = player:getGuid()
db.query("INSERT INTO `player_deaths` (`player_id`, `time`, `level`, `killed_by`, `is_player`, `mostdamage_by`, `mostdamage_is_player`, `unjustified`, `mostdamage_unjustified`) VALUES (" .. playerGuid .. ", " .. os.time() .. ", " .. player:getLevel() .. ", " .. db.escapeString(killerName) .. ", " .. byPlayer .. ", " .. db.escapeString(mostDamageName) .. ", " .. byPlayerMostDamage .. ", " .. (lastHitUnjustified and 1 or 0) .. ", " .. (mostDamageUnjustified and 1 or 0) .. ")")
local resultId = db.storeQuery("SELECT `player_id` FROM `player_deaths` WHERE `player_id` = " .. playerGuid)
local deathRecords = 0
local tmpResultId = resultId
while tmpResultId ~= false do
tmpResultId = result.next(resultId)
deathRecords = deathRecords + 1
end
if resultId ~= false then
result.free(resultId)
end
local limit = deathRecords - maxDeathRecords
if limit > 0 then
db.asyncQuery("DELETE FROM `player_deaths` WHERE `player_id` = " .. playerGuid .. " ORDER BY `time` LIMIT " .. limit)
end
if byPlayer == 1 then
local targetGuild = player:getGuild()
targetGuild = targetGuild and targetGuild:getId() or 0
if targetGuild ~= 0 then
local killerGuild = killer:getGuild()
killerGuild = killerGuild and killerGuild:getId() or 0
if killerGuild ~= 0 and targetGuild ~= killerGuild and isInWar(playerId, killer:getId()) then
local warId = false
resultId = db.storeQuery("SELECT `id` FROM `guild_wars` WHERE `status` = 1 AND ((`guild1` = " .. killerGuild .. " AND `guild2` = " .. targetGuild .. ") OR (`guild1` = " .. targetGuild .. " AND `guild2` = " .. killerGuild .. "))")
if resultId ~= false then
warId = result.getNumber(resultId, "id")
result.free(resultId)
end
if warId ~= false then
local playerName = player:getName()
db.asyncQuery("INSERT INTO `guildwar_kills` (`killer`, `target`, `killerguild`, `targetguild`, `time`, `warid`) VALUES (" .. db.escapeString(killerName) .. ", " .. db.escapeString(playerName) .. ", " .. killerGuild .. ", " .. targetGuild .. ", " .. os.time() .. ", " .. warId .. ")")
addEvent(sendWarStatus, 1000, killerGuild, targetGuild, warId, playerName, killerName)
end
end
end
end
end
creatureevent:register()
local creatureeventLogin = CreatureEvent("creatureeventLogin")
function creatureeventLogin.onLogin(player)
player:registerEvent("PlayerDeath")
return true
end
creatureeventLogin:register()

View File

@@ -0,0 +1,65 @@
-- getEternalStorage and setEternalStorage
-- can be added to data/global.lua if you want to use eternal storage for another purpose than this.
-- Regular TFS global storage values get reset every time server reboots. This does not.
local function getEternalStorage(key, parser)
local value = result.getString(db.storeQuery("SELECT `value` FROM `znote_global_storage` WHERE `key` = ".. key .. ";"), "value")
if not value then
if parser then
return false
else
return -1
end
end
result.free(value)
return tonumber(value) or value
end
local function setEternalStorage(key, value)
if getEternalStorage(key, true) then
db.query("UPDATE `znote_global_storage` SET `value` = '".. value .. "' WHERE `key` = ".. key .. ";")
else
db.query("INSERT INTO `znote_global_storage` (`key`, `value`) VALUES (".. key ..", ".. value ..");")
end
return true
end
-- SQL Query to execute: --
--[[
ALTER TABLE `znote_players` ADD `exphist_lastexp` BIGINT NOT NULL DEFAULT '0',
ADD `exphist1` BIGINT NOT NULL DEFAULT '0',
ADD `exphist2` BIGINT NOT NULL DEFAULT '0',
ADD `exphist3` BIGINT NOT NULL DEFAULT '0',
ADD `exphist4` BIGINT NOT NULL DEFAULT '0',
ADD `exphist5` BIGINT NOT NULL DEFAULT '0',
ADD `exphist6` BIGINT NOT NULL DEFAULT '0',
ADD `exphist7` BIGINT NOT NULL DEFAULT '0',
ADD `onlinetimetoday` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime1` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime2` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime3` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime4` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime5` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime6` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetime7` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0',
ADD `onlinetimeall` INT UNSIGNED NOT NULL DEFAULT '0';
]]--
-- after that execute: --
--[[
UPDATE `znote_players` AS `z` INNER JOIN `players` AS `p` ON `p`.`id`=`z`.`player_id` SET `z`.`exphist_lastexp`=`p`.`experience`;
]]--
local globalevent = GlobalEvent("PowerGamers")
function globalevent.onThink(...)
if tonumber(os.date("%d")) ~= getEternalStorage(23856) then
setEternalStorage(23856, (tonumber(os.date("%d"))))
db.query("UPDATE `znote_players` SET `onlinetime7`=`onlinetime6`, `onlinetime6`=`onlinetime5`, `onlinetime5`=`onlinetime4`, `onlinetime4`=`onlinetime3`, `onlinetime3`=`onlinetime2`, `onlinetime2`=`onlinetime1`, `onlinetime1`=`onlinetimetoday`, `onlinetimetoday`=0;")
db.query("UPDATE `znote_players` `z` INNER JOIN `players` `p` ON `p`.`id`=`z`.`player_id` SET `z`.`exphist7`=`z`.`exphist6`, `z`.`exphist6`=`z`.`exphist5`, `z`.`exphist5`=`z`.`exphist4`, `z`.`exphist4`=`z`.`exphist3`, `z`.`exphist3`=`z`.`exphist2`, `z`.`exphist2`=`z`.`exphist1`, `z`.`exphist1`=`p`.`experience`-`z`.`exphist_lastexp`, `z`.`exphist_lastexp`=`p`.`experience`;")
end
db.query("UPDATE `znote_players` SET `onlinetimetoday` = `onlinetimetoday` + 60, `onlinetimeall` = `onlinetimeall` + 60 WHERE `player_id` IN (SELECT `player_id` FROM `players_online` WHERE `players_online`.`player_id` = `znote_players`.`player_id`)")
return true
end
globalevent:interval(60000)
globalevent:register()

View File

@@ -0,0 +1,21 @@
local talkaction = TalkAction("!report")
function talkaction.onSay(player)
local storage = 6708 -- You can change the storage if its already in use
local delaytime = 30 -- Exhaust In Seconds.
if param == '' then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Command param required.")
return true
end
if player:getStorageValue(storage) <= os.time() then
player:sendTextMessage(MESSAGE_INFO_DESCR, "Your report has been received successfully!")
db.query("INSERT INTO `znote_player_reports` (`id` ,`name` ,`posx` ,`posy` ,`posz` ,`report_description` ,`date`)VALUES (NULL , " .. db.escapeString(player:getName()) .. ", '" .. player:getPosition().x .. "', '" .. player:getPosition().y .. "', '" .. player:getPosition().z .. "', " .. db.escapeString(param) .. ", '" .. os.time() .. "')")
player:setStorageValue(storage, os.time() + delaytime)
else
player:sendTextMessage(MESSAGE_STATUS_WARNING, "You have to wait " .. player:getStorageValue(storage) - os.time() .. " seconds to report again.")
end
return true
end
talkaction:separator(" ")
talkaction:register()

View File

@@ -0,0 +1,163 @@
local globalevent = GlobalEvent("ShopSystemGlobal")
function globalevent.onThink(...)
local shopTypes = {1,5,7}
-- If game support mount orders
if Game.getClientVersion().min >= 870 then
table.insert(shopTypes, 6);
end
local orderQuery = db.storeQuery([[
SELECT
MIN(`po`.`player_id`) AS `player_id`,
`shop`.`id`,
`shop`.`type`,
`shop`.`itemid`,
`shop`.`count`
FROM `players_online` AS `po`
INNER JOIN `players` AS `p`
ON `po`.`player_id` = `p`.`id`
INNER JOIN `znote_shop_orders` AS `shop`
ON `p`.`account_id` = `shop`.`account_id`
WHERE `shop`.`type` IN(]] .. table.concat(shopTypes, ",") .. [[)
GROUP BY `shop`.`id`
]])
-- Detect if we got any results
if orderQuery ~= false then
local type_desc = {
"itemids",
"pending premium (skip)",
"pending gender change (skip)",
"pending character name change (skip)",
"Outfit and addons",
"Mounts",
"Instant house purchase"
}
repeat
local player_id = result.getNumber(orderQuery, 'player_id')
local orderId = result.getNumber(orderQuery, 'id')
local orderType = result.getNumber(orderQuery, 'type')
local orderItemId = result.getNumber(orderQuery, 'itemid')
local orderCount = result.getNumber(orderQuery, 'count')
local served = false
local player = Player(player_id)
if player ~= nil then
local description = "Unknown or custom type"
if type_desc[orderType] ~= nil then
description = type_desc[orderType]
end
print("Processing type "..orderType..": ".. description)
print("Processing shop order for: [".. player:getName() .."] type "..orderType..": ".. description)
local tile = Tile(player:getPosition())
if tile ~= nil and tile:hasFlag(TILESTATE_PROTECTIONZONE) then
-- ORDER TYPE 1 (Regular item shop products)
if orderType == 1 then
served = true
local itemType = ItemType(orderItemId)
-- Get weight
if player:getFreeCapacity() >= itemType:getWeight(orderCount) then
local backpack = player:getSlotItem(CONST_SLOT_BACKPACK)
-- variable = (condition) and (return if true) or (return if false)
local needslots = itemType:isStackable() and math.floor(orderCount / 100) + 1 or orderCount
if backpack ~= nil and backpack:getEmptySlots(false) >= needslots then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";")
player:addItem(orderItemId, orderCount)
player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. "!")
print("Process complete. [".. player:getName() .."] has received " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".")
else -- not enough slots
player:sendTextMessage(MESSAGE_STATUS_WARNING, "Your main backpack is full. You need to free up "..needslots.." available slots to get " .. orderCount .. " " .. ItemType(orderItemId):getName() .. "!")
print("Process canceled. [".. player:getName() .."] need more space in his backpack to get " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".")
end
else -- not enough cap
player:sendTextMessage(MESSAGE_STATUS_WARNING, "You need more CAP to carry this order!")
print("Process canceled. [".. player:getName() .."] need more cap to carry " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".")
end
end
-- ORDER TYPE 5 (Outfit and addon)
if orderType == 5 then
served = true
local itemid = orderItemId
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, orderCount) then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";")
player:addOutfit(outfitId)
player:addOutfitAddon(outfitId, orderCount)
player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new outfit!")
print("Process complete. [".. player:getName() .."] has received outfit: ["..outfitId.."] with addon: ["..orderCount.."]")
else -- Already has outfit
player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this outfit and addon!")
print("Process canceled. [".. player:getName() .."] already have outfit: ["..outfitId.."] with addon: ["..orderCount.."].")
end
end
end
-- ORDER TYPE 6 (Mounts)
if orderType == 6 then
served = true
-- Make sure player don't already have this outfit and addon
if not player:hasMount(orderItemId) then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";")
player:addMount(orderItemId)
player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new mount!")
print("Process complete. [".. player:getName() .."] has received mount: ["..orderItemId.."]")
else -- Already has mount
player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this mount!")
print("Process canceled. [".. player:getName() .."] already have mount: ["..orderItemId.."].")
end
end
-- ORDER TYPE 7 (Direct house purchase)
if orderType == 7 then
served = true
local house = House(orderItemId)
-- Logged in player is not necessarily the player that bough the house. So we need to load player from db.
local buyerQuery = db.storeQuery("SELECT `name` FROM `players` WHERE `id` = "..orderCount.." LIMIT 1")
if buyerQuery ~= false then
local buyerName = result.getString(buyerQuery, "name")
result.free(buyerQuery)
if house then
db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";")
house:setOwnerGuid(orderCount)
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 received house: ["..house:getName().."]")
else
print("Process canceled. Failed to load house with ID: "..orderItemId)
end
else
print("Process canceled. Failed to load player with ID: "..orderCount)
end
end
if not served then -- If this order hasn't been processed yet (missing type handling?)
print("Znote shop: Type ["..orderType.."] not properly processed. Missing Lua code?")
end
else -- Not in protection zone
player:sendTextMessage(MESSAGE_INFO_DESCR, 'You have a pending shop order, please enter protection zone.')
print("Skipped one shop order. Reason: Player: [".. player:getName() .."] is not inside protection zone.")
end
else -- player not logged in
print("Skipped one shop order. Reason: Player with id [".. player_id .."] is not online.")
end
until not result.next(orderQuery)
result.free(orderQuery)
end
return true
end
globalevent:interval(30000)
globalevent:register()

View File

@@ -0,0 +1,137 @@
local talkaction = TalkAction("!shop")
function talkaction.onSay(player)
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")
local description = "Unknown or custom type"
if type_desc[q_type] ~= nil then
description = type_desc[q_type]
end
print("Processing type "..q_type..": ".. description)
-- ORDER TYPE 1 (Regular item shop products)
if q_type == 1 then
served = true
-- Get weight
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
if Game.getClientVersion().min >= 870 then
-- 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
end
-- ORDER TYPE 7 (Direct house purchase)
if q_type == 7 then
served = true
local house = House(q_itemid)
-- Logged in player is not necessarily the player that bough the house. So we need to load player from db.
local buyerQuery = db.storeQuery("SELECT `name` FROM `players` WHERE `id` = "..q_count.." LIMIT 1")
if buyerQuery ~= false then
local buyerName = result.getString(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 received 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)
-- Type 7 is for Instant house purchase (Already coded here)
-- So use type 8+ for custom stuff, like etc packages.
-- if q_type == 8 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
talkaction:register()

View File

@@ -0,0 +1,50 @@
local creatureevent = CreatureEvent("SincOutfit")
-- Sync outfits that player own with Znote AAC
-- So its possible to see which full sets player
-- has in characterprofile.php
znote_outfit_list = {
{ -- Female outfits
136, 137, 138, 139, 140, 141, 142, 147, 148,
149, 150, 155, 156, 157, 158, 252, 269, 270,
279, 288, 324, 329, 336, 366, 431, 433, 464,
466, 471, 513, 514, 542, 575, 578, 618, 620,
632, 635, 636, 664, 666, 683, 694, 696, 698,
724, 732, 745, 749, 759, 845, 852, 874, 885,
900, 973, 975, 1020, 1024, 1043, 1050, 1057,
1070, 1095, 1103, 1128, 1147, 1162, 1174,
1187, 1203, 1205, 1207, 1211, 1246, 1244,
1252, 1271, 1280, 1283, 1289, 1293, 1332
},
{ -- Male outfits
128, 129, 130, 131, 132, 133, 134, 143, 144,
145, 146, 151, 152, 153, 154, 251, 268, 273,
278, 289, 325, 328, 335, 367, 430, 432, 463,
465, 472, 512, 516, 541, 574, 577, 610, 619,
633, 634, 637, 665, 667, 684, 695, 697, 699,
725, 733, 746, 750, 760, 846, 853, 873, 884,
899, 908, 931, 955, 957, 962, 964, 966, 968,
970, 972, 974, 1021, 1023, 1042, 1051, 1056,
1069, 1094, 1102, 1127, 1146, 1161, 1173,
1186, 1202, 1204, 1206, 1210, 1245, 1243,
1251, 1270, 1279, 1282, 1288, 1292, 1331
}
}
function creatureevent.onLogin(player)
-- storage_value + 1000 storages (highest outfit id) must not be used in other script.
-- Must be identical to Znote AAC config.php: $config['EQ_shower'] -> storage_value
local storage_value = 10000
-- Loop through outfits
for _, outfit in pairs(znote_outfit_list[player:getSex() + 1]) do
if player:hasOutfit(outfit,3) then
if player:getStorageValue(storage_value + outfit) ~= 3 then
player:setStorageValue(storage_value + outfit, 3)
end
end
end
return true
end
creatureevent:register()

View File

@@ -0,0 +1,68 @@
-- Znote LoginWebService (version 1) for protocol 11, 12+
-- Move file to this location: data/scripts/znote_login.lua
-- And restart OT server, it should auto load script.
-- Requires updated version of Znote AAC. (18. June 2020)
-- This script will help Znote AAC connect players to this game server.
local znote_loginWebService = GlobalEvent("znote_loginWebService")
function znote_loginWebService.onStartup()
print(" ")
print("=============================")
print("= Znote AAC loginWebService =")
print("=============================")
local configLua = {
["SERVER_NAME"] = configManager.getString(configKeys.SERVER_NAME),
["IP"] = configManager.getString(configKeys.IP),
["GAME_PORT"] = configManager.getNumber(configKeys.GAME_PORT)
}
local configSQL = {
["SERVER_NAME"] = false,
["IP"] = false,
["GAME_PORT"] = false
}
local webStorage = db.storeQuery([[
SELECT
`key`,
`value`
FROM `znote_global_storage`
WHERE `key` IN('SERVER_NAME', 'IP', 'GAME_PORT')
]])
if webStorage ~= false then
repeat
local key = result.getString(webStorage, 'key')
local value = result.getString(webStorage, 'value')
configSQL[key] = value
until not result.next(webStorage)
result.free(webStorage)
end
local inserts = {}
if configSQL.SERVER_NAME == false then
table.insert(inserts, "('SERVER_NAME',".. db.escapeString(configLua.SERVER_NAME) ..")")
elseif configSQL.SERVER_NAME ~= configLua.SERVER_NAME then
db.query("UPDATE `znote_global_storage` SET `value`=".. db.escapeString(configLua.SERVER_NAME) .." WHERE `key`='SERVER_NAME';")
print("= Updated [SERVER_NAME] FROM [" .. configSQL.SERVER_NAME .. "] to [" .. configLua.SERVER_NAME .. "]")
end
if configSQL.IP == false then
table.insert(inserts, "('IP',".. db.escapeString(configLua.IP) ..")")
elseif configSQL.IP ~= configLua.IP then
db.query("UPDATE `znote_global_storage` SET `value`=".. db.escapeString(configLua.IP) .." WHERE `key`='IP';")
print("= Updated [IP] FROM [" .. configSQL.IP .. "] to [" .. configLua.IP .. "]")
end
if configSQL.GAME_PORT == false then
table.insert(inserts, "('GAME_PORT',".. db.escapeString(configLua.GAME_PORT) ..")")
elseif configSQL.GAME_PORT ~= tostring(configLua.GAME_PORT) then
db.query("UPDATE `znote_global_storage` SET `value`=".. db.escapeString(configLua.GAME_PORT) .." WHERE `key`='GAME_PORT';")
print("= Updated [GAME_PORT] FROM [" .. configSQL.GAME_PORT .. "] to [" .. configLua.GAME_PORT .. "]")
end
if #inserts > 0 then
db.query("INSERT INTO `znote_global_storage` (`key`,`value`) VALUES "..table.concat(inserts,',')..";")
print("= Fixed " .. #inserts .. " missing configurations.")
end
print("=============================")
print("= SERVER_NAME: " .. configLua.SERVER_NAME)
print("= IP: " .. configLua.IP)
print("= GAME_PORT: " .. configLua.GAME_PORT)
print("=============================")
print(" ")
end
znote_loginWebService:register()

View File

@@ -0,0 +1,18 @@
-- <talkaction words="!report" separator=" " script="adminreport.lua"/>
-- Coded by Dark ShaoOz, modified by Znote
function onSay(player, words, param)
local storage = 6708 -- You can change the storage if its already in use
local delaytime = 30 -- Exhaust In Seconds.
if param == '' then
player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Command param required.")
return true
end
if player:getStorageValue(storage) <= os.time() then
player:sendTextMessage(MESSAGE_INFO_DESCR, "Your report has been received successfully!")
db.query("INSERT INTO `znote_player_reports` (`id` ,`name` ,`posx` ,`posy` ,`posz` ,`report_description` ,`date`)VALUES (NULL , " .. db.escapeString(player:getName()) .. ", '" .. player:getPosition().x .. "', '" .. player:getPosition().y .. "', '" .. player:getPosition().z .. "', " .. db.escapeString(param) .. ", '" .. os.time() .. "')")
player:setStorageValue(storage, os.time() + delaytime)
else
player:sendTextMessage(MESSAGE_STATUS_WARNING, "You have to wait " .. player:getStorageValue(storage) - os.time() .. " seconds to report again.")
end
return true
end

View File

@@ -0,0 +1,135 @@
-- <talkaction words="!shop" script="znoteshop.lua"/>
-- Znote Shop v1.1 for Znote AAC on TFS 1.2+
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")
local description = "Unknown or custom type"
if type_desc[q_type] ~= nil then
description = type_desc[q_type]
end
print("Processing type "..q_type..": ".. description)
-- ORDER TYPE 1 (Regular item shop products)
if q_type == 1 then
served = true
-- Get weight
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
if Game.getClientVersion().min >= 870 then
-- 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
end
-- ORDER TYPE 7 (Direct house purchase)
if q_type == 7 then
served = true
local house = House(q_itemid)
-- Logged in player is not necessarily the player that bough the house. So we need to load player from db.
local buyerQuery = db.storeQuery("SELECT `name` FROM `players` WHERE `id` = "..q_count.." LIMIT 1")
if buyerQuery ~= false then
local buyerName = result.getString(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 received 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)
-- Type 7 is for Instant house purchase (Already coded here)
-- So use type 8+ for custom stuff, like etc packages.
-- if q_type == 8 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

168
app/ZnoteAAC/README.md Normal file
View File

@@ -0,0 +1,168 @@
ZnoteAAC
========
[![CodeFactor](https://www.codefactor.io/repository/github/znote/znoteaac/badge)](https://www.codefactor.io/repository/github/znote/znoteaac)
### What is Znote AAC?
Znote AAC is a full-fledged website used together with an Open Tibia(OT) server.
It aims to be super easy to install and compatible with all the popular OT distributions.
It is created in PHP with a simple custom procedural framework.
### Where do I download?
We use github to distribute our versions, stable are tagged as releases, while development is the latest commit.
* [Stable](https://github.com/Znote/ZnoteAAC/releases)
* [Development](https://github.com/Znote/ZnoteAAC/archive/master.zip)
### Requirements
* PHP Version 5.6 or higher. Mostly tested on 5.6 and 7.4. Most web stacks ships with this as default these days.
### Optionals
* For email registration verification and account recovery: [PHPMailer](https://github.com/PHPMailer/PHPMailer/releases) Version 6.x, extracted and renamed to just "PHPMailer" in Znote AAC directory.
* PHP extension curl for PHPMailer, paypal and google reCaptcha services.
* PHP extension openssl for google reCaptcha services.
* PHP extension gd for guild logos.
### Installation instructions
1: Extract the .zip file to your web directory (Example: C:\UniServ\www\ )
Without modifying config.php, enter the website and wait for mysql connection error.
This will show you the rest of the instructions as well as the mysql schema.
2: Edit config.php and:
- modify $config['ServerEngine'] with correct TFS version you are running. (TFS_02, TFS_03, TFS_10, OTHIRE).
- modify $config['page_admin_access'] with your admin account username(s).
3: Before inserting correct SQL connection details, visit the website ( http://127.0.0.1/ ), it will generate a mysql schema you should import to your OT servers database.
4: Follow the steps on the website and import the SQL schema for Znote AAC, and edit config.php with correct mysql details.
5: IF you have existing database from active OT server, enter the folder called "special" and convert the database for Znote AAC support ( http://127.0.0.1/special/ )
6: Enjoy Znote AAC. You can look around [HERE](https://otland.net/forums/website-applications.118/) for plugins and resources to Znote AAC, for instance various free templates to use.
7: Please note that you need PHP cURL enabled to make Paypal payments work.
8: You may need to change directory access rights of /engine/cache to allow writing.
### Features:
Znote AAC is very rich feature wise, here is an attempt at summarizing what we offer.
#### Server distribution compatibility:
- [Znote AAC 1.6](https://github.com/Znote/ZnoteAAC/releases/tag/1.6)
- OTHire
- TFS 0.2
- TFS 0.3/4
- TFS 1.3
- Distributions based on these (such as OTX).
- Znote AAC 2.0 [v2 dev branch](https://github.com/Znote/ZnoteAAC/tree/v2)
- TFS 1.4
- OTservBR-Global
#### General
- Server wide latest death list
- Server wide latest kills list
- Server information with PvP settings, skill rates, experience stages (parses config.lua and stages.xml file)
- Spells page with vocation filters (parses spells.xml file)
- Item list showing equippable items (parses items.xml file)
#### Account & login:
- Basic account registration
- Change password and email
- reCaptcha antibot(spam) system
- Email verification & lost account interface
- Two-factor authentication support
- Hide characters from character list
- Support helpdesk (tickets)
#### Create character:
- Supports custom vocations, starting skills, available towns
- Character firstitems through provided Lua script
- Soft character deletion
#### House:
- Houses list with towns filter
- House bidding
- Direct house purchase with shop points
#### Character profile
- General information such as name, vocation, level, guild membership etc...
- Obtained achievement list
- Player comments
- Death list
- Quest progression
- Character list
- EQ shower, skills, full outfits
#### Guilds
- Configurable level and account type restrictions to create guild
- Create and disband guilds
- Invite and revoke players to guild
- Change name of guild positions
- Add nickname to guild members
- Guild forum board accessible only for guild members & admin.
- Upload guild image
- Guild description
- Invite, accept and cancel war declarations
- View ongoing guild wars
#### Item market
- Want to buy list
- Want to sell list
- Item search
- Compare item offer with other similar offers, as well as transaction history
#### Downloads
- Page with download links to client version and IP changer
- Tutorial on how to connect to server
#### Achievement system
- List of all achievements and character obtained achievements in their profile.
#### Highscores
- Vocation & skill type filters
#### Buy shop points / digital currency
- PayPal payment gateway
- PayGol (SMS) payment gateway
- PagSeguro payment gateway
#### Shop system
- Items
- Premium days
- Change character gender
- Change character name
- Outfits
- Mounts
- Custom offer types. (basic Lua knowledge required)
#### Forum
- Create custom discussion boards
- Level restriction to post
- Player outfit as avatars
- Player position
- Guildboards
- Feedback board where all threads are only visible for admins.
- Hide thread, close thread, stick thread
- Forum search
#### Cache system
- Offload SQL load and CPU usage by loading treated data from a flatfile instead of raw SQL queries.
#### Administration
- Delete character
- Ban character and/or account
- Change password of account
- Give character in-game position
- Give shop points to character
- Teleport a player or all players to home town, specific town or specific position.
- Edit level and skills of player
- View in-game bug reports and feedback on forum
- Overview of shop transactions and their status
- Moderate user submitted images to the gallery
- Create news with a feature rich text editor
- Add changelogs
- Load and update server and spells information
- Helpdesk
### TODO List:
* Check [Milestones](https://github.com/Znote/ZnoteAAC/milestones)

View File

@@ -0,0 +1,62 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
if ($config['Ach'] == true) {
?>
<center><h3>Achievements on <?php echo $config['site_title'] ?></h3></center>
<div class="panel-body">
<table class="table table-striped table-bordered table-condensed">
<tr>
<td width="10%">Grade</td>
<td width="17%">Name</td>
<td>Description</td>
<td width="7%">Secret</td>
<td width="2%">Points</td>
</tr>
<style>
#wtf {
margin-left:0px;
}
</style>
<tr>
<?php
foreach ($config['achievements'] as $key => $achName) {
// Set defaults
if (!isset($achName['secret'])) $achName['secret'] = false;
if (!isset($achName['img'])) $achName['img'] = 'https://i.imgur.com/ZqWp1TE.png';
if (($achName['points'] >= 1) and ($achName['points'] <= 3) and (!$achName['img'])) {
echo '<td><center><img id="wtf" src="https://i.imgur.com/TUCGsr3.gif"></center></td>';
} elseif (($achName['points'] >= 4) and ($achName['points'] <= 6) and (!$achName['img'])) {
echo '<td><center><img id="wtf" src="https://i.imgur.com/TUCGsr3.gif"><img id="wtf" src="https://i.imgur.com/TUCGsr3.gif"></center></td>';
} elseif (($achName['points'] >= 7) and ($achName['points'] <= 9) and (!$achName['img'])) {
echo '<td><center><img id="wtf" src="https://i.imgur.com/TUCGsr3.gif"><img id="wtf" src="https://i.imgur.com/TUCGsr3.gif"><img id="wtf" src="https://i.imgur.com/TUCGsr3.gif"></center></td>';
} elseif (($achName['points'] >= 10) and (!$achName['img'])) {
echo '<td><center><img id="wtf" src="https://i.imgur.com/TUCGsr3.gif"><img id="wtf" src="https://i.imgur.com/TUCGsr3.gif"><img id="wtf" src="https://i.imgur.com/TUCGsr3.gif"></center></td>';
} else {
echo '<td><img id="wtf" src="' .$achName['img']. '"><br><br></td>';
}
echo '<td>' .$achName[0]. '</td>';
echo '<td>' .$achName[1]. '</td>';
if ($achName['secret'] == true) {
echo '<td><img id="wtf" src="https://i.imgur.com/NbPRl7b.gif"></td>';
echo '<td>'. $achName['points'] .'</td>';
} else {
echo '<td></td><td>'. $achName['points'] .'</td>';
}
echo '</tr>';
}
?>
</table>
</div>
</div>
<?php
include 'layout/overall/footer.php';
}
else{
echo 'This page has been disabled, this page can be enabled at config';
}
?>

348
app/ZnoteAAC/admin.php Normal file
View File

@@ -0,0 +1,348 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
if(!isset($_SESSION['csrf_token'])){
$_SESSION['csrf_token'] = bin2hex(random_bytes_compat(5, $crypto_strong));
if(!$crypto_strong){
// we don't really care, the csrf token doesn't really have to be cryptographically strong.
}
}
protect_page();
admin_only($user_data);
// Encryption (if select field has $key 0, it will return false, so add $enc + $key will return 100, subtract and you get 0, not false).
$enc = 100;
// Don't bother to think about cross site scripting here, since they can't access the page unless they are admin anyway.
// start
if (empty($_POST) === false) {
if(empty($_POST['csrf_token'])){
http_response_code(400);
die("error: missing csrf token!");
}
if(!hash_equals($_POST['csrf_token'],$_SESSION['csrf_token'])){
http_response_code(400);
die("error: csrf token invalid!");
}
// BAN system!
if (!empty($_POST['ban_char']) && !empty($_POST['ban_type']) && !empty($_POST['ban_action']) && !empty($_POST['ban_reason']) && !empty($_POST['ban_time']) && !empty($_POST['ban_comment'])) {
if (user_character_exist($_POST['ban_char'])) {
// Decrypt and store values
$charname = $_POST['ban_char'];
$typeid = (int)$_POST['ban_type'] - $enc;
$actionid = (int)$_POST['ban_action'] - $enc;
$reasonid = (int)$_POST['ban_type'] - $enc;
$time = (int)$_POST['ban_time'] - $enc;
$comment = $_POST['ban_comment'];
//var_dump($charname, $typeid, $actionid, $reasonid, $time, $comment);
if (set_rule_violation($charname, $typeid, $actionid, $reasonid, $time, $comment)) {
$errors[] = 'Violation entry has been set for '. hhb_tohtml($charname) .'.';
} else {
$errors[] = 'Website character name: '. hhb_tohtml($config['website_char']) .' does not exist. Create this character name or configure another name in config.php';
$errors[] = 'Website failed to recognize a character it can represent while inserting a rule violation.';
}
} else {
$errors[] = 'Character '. hhb_tohtml(getValue($_POST['ban_char'])) .' does not exist.';
}
}
// Delete character:
if (empty($_POST['del_name']) === false) {
if (user_character_exist($_POST['del_name'])) {
user_delete_character(user_character_id($_POST['del_name']));
$errors[] = 'Character '. hhb_tohtml(getValue($_POST['del_name'])) .' permanently deleted.';
} else {
$errors[] = 'Character '. hhb_tohtml(getValue($_POST['del_name'])) .' does not exist.';
}
}
// Reset password for char name
if (empty($_POST['reset_pass']) === false && empty($_POST['new_pass']) === false) {
// reset_pass = character name
if (user_character_exist($_POST['reset_pass'])) {
$acc_id = user_character_account_id($_POST['reset_pass']);
if ($acc_id != $session_user_id) {
if ($config['ServerEngine'] == 'TFS_02' || $config['ServerEngine'] == 'TFS_10' || $config['ServerEngine'] == 'OTHIRE') {
user_change_password($acc_id, $_POST['new_pass']);
} else if ($config['ServerEngine'] == 'TFS_03') {
user_change_password03($acc_id, $_POST['new_pass']);
}
$errors[] = 'The password to the account of character name: '. hhb_tohtml(getValue($_POST['reset_pass'])) .' has been set to: '. hhb_tohtml(getValue($_POST['new_pass'])) .'.';
} else {
header('Location: changepassword.php');
exit();
}
}
}
// Give points to character
if (empty($_POST['points_char']) === false && empty($_POST['points_value']) === false) {
$char = sanitize($_POST['points_char']);
$points = (int)$_POST['points_value'];
data_dump($_POST, false, "post data");
$account = mysql_select_single("SELECT `account_id` FROM `players` WHERE `name`='$char' LIMIT 1;");
data_dump($account, false, "fetching account id from players table");
$znote_account = mysql_select_single("SELECT `id`, `points` FROM `znote_accounts` WHERE `account_id`='". $account['account_id'] ."';");
data_dump($znote_account, false, "Fetching existing points from znote_accounts");
data_dump(
array(
'Old:' => $znote_account['points'],
'New:' => $points,
'Total:' => ($znote_account['points'] + $points)
),
false,
"Points calculation:");
$points += $znote_account['points'];
mysql_update("UPDATE `znote_accounts` SET `points`='$points' WHERE `account_id`='". $account['account_id'] ."';");
}
// Set character position
if (empty($_POST['position_name']) === false && empty($_POST['position_type']) === false) {
if (user_character_exist($_POST['position_name'])) {
if (array_key_exists($_POST['position_type'], $config['ingame_positions'])) {
if ($config['ServerEngine'] == 'TFS_02' || $config['ServerEngine'] == 'TFS_10' || $config['ServerEngine'] == 'OTHIRE') {
set_ingame_position($_POST['position_name'], $_POST['position_type']);
} else if ($config['ServerEngine'] == 'TFS_03') {
set_ingame_position03($_POST['position_name'], $_POST['position_type']);
}
$pos = 'Undefined';
foreach ($config['ingame_positions'] as $key=>$value) {
if ($key == $_POST['position_type']) {
$pos = $value;
}
}
$errors[] = 'Character '. hhb_tohtml(getValue($_POST['position_name'])) .' recieved the ingame position: '. hhb_tohtml($pos) .'.';
}
} else {
$errors[] = 'Character '. hhb_tohtml(getValue($_POST['position_name'])) .' does not exist.';
}
}
// Teleport Player
if (isset($_POST['from']) && in_array($_POST['from'], ['all', 'only'])) {
$from = $_POST['from'];
if ($from === 'only') {
if (empty($_POST['player_name']) || !user_character_exist($_POST['player_name'])) {
$errors[] = 'Character '. hhb_tohtml(getValue($_POST['player_name'])) .' does not exist.';
}
}
if (!sizeof($errors)) {
$to = $_POST['to'];
$teleportQuery = 'UPDATE `players` SET ';
if ($to == 'home') {
$teleportQuery .= '`posx` = 0, `posy` = 0, `posz` = 0 ';
} else if ($to == 'town') {
$teleportQuery .= '`posx` = 0, `posy` = 0, `posz` = 0, `town_id` = ' . (int) getValue($_POST['town']) . ' ';
} else if ($to == 'xyz') {
$teleportQuery .= '`posx` = ' . (int) getValue($_POST['x']) . ', `posy` = ' . (int) getValue($_POST['y']) . ', `posz` = ' . (int) getValue($_POST['z']) . ' ';
}
if ($from === 'only') {
$teleportQuery .= ' WHERE `name` = \'' . getValue($_POST['player_name']). '\'';
}
mysql_update($teleportQuery);
}
}
// If empty post
}
// Display whatever output we figure out to add
if (empty($errors) === false){
echo '<font color="red"><b>';
echo output_errors($errors);
echo '</b></font>';
}
// end
?>
<h1>Admin Page.</h1>
<p>
<?php
$basic = user_znote_data('version', 'installed', 'cached');
if ($basic['version'] !== $version) {
mysql_update("UPDATE `znote` SET `version`='$version';");
$basic = user_znote_data('version', 'installed', 'cached');
}
echo "Running Znote AAC Version: ". hhb_tohtml($basic['version']) .".<br>";
echo "Last cached on: ". hhb_tohtml(getClock($basic['cached'], true)) .".<br>";
?>
</p>
<ul>
<li>
<b>Permanently delete/erase character from database:</b>
<form type="submit" action="" method="post">
<input type="hidden" name="csrf_token" value="<?php echo hhb_tohtml($_SESSION['csrf_token']);?>" />
<input type="text" name="del_name" placeholder="Character name...">
</form>
</li>
<li>
<b>Ban character and/or account:</b>
<form action="" method="post">
<input type="hidden" name="csrf_token" value="<?php echo hhb_tohtml($_SESSION['csrf_token']);?>" />
<table>
<!-- row 1 -->
<tr>
<td>
<input type="text" name="ban_char" placeholder="Character name...">
</td>
</tr>
<!-- row 2 -->
<tr>
<td>
<select name="ban_type">
<?php
foreach ($config['ban_type'] as $key=>$value) {
echo "<option value=\"". hhb_tohtml($enc + $key) ."\">". hhb_tohtml($value) ."</option>";
}
?>
</select>
<select name="ban_action">
<?php
foreach ($config['ban_action'] as $key=>$value) {
echo "<option value=\"". hhb_tohtml($enc + $key) ."\">". hhb_tohtml($value) ."</option>";
}
?>
</select>
<select name="ban_time">
<?php
foreach ($config['ban_time'] as $key=>$value) {
echo "<option value=\"". hhb_tohtml($enc + $key) ."\">". hhb_tohtml($value) ."</option>";
}
?>
</select>
</td>
</tr>
<!-- row 3 -->
<tr>
<td>
Ban reason:
<select name="ban_reason">
<?php
foreach ($config['ban_reason'] as $key=>$value) {
echo "<option value=\"". hhb_tohtml($enc + $key) ."\">". hhb_tohtml($value) ."</option>";
}
?>
</select>
</td>
</tr>
<!-- row 4 -->
<tr>
<td>
Violation comment: (max 60 cols).
<input type="text" name="ban_comment" maxlength="60" placeholder="Ban for botting rotworms.">
<input type="submit" value="Set Violation">
</td>
</tr>
</table>
</form>
</li>
<li>
<b>Reset password to the account of character name:</b>
<form action="" method="post">
<input type="hidden" name="csrf_token" value="<?php echo hhb_tohtml($_SESSION['csrf_token']);?>" />
<input type="text" name="reset_pass" placeholder="Character name">
<input type="text" name="new_pass" placeholder="New password">
<input type="submit" value="Change Password">
</form>
</li>
<li>
<b>Set character name to position:</b>
<?php
if ($config['ServerEngine'] == 'TFS_03' && count($config['ingame_positions']) == 5) {
?>
<font color="red">ERROR: You forgot to add (Senior Tutor) rank in config.php!</font>
<?php
}
?>
<form action="" method="post">
<input type="hidden" name="csrf_token" value="<?php echo hhb_tohtml($_SESSION['csrf_token']);?>" />
<input type="text" name="position_name" placeholder="Character name">
<select name="position_type">
<?php
foreach ($config['ingame_positions'] as $key=>$value) {
echo "<option value=\"". hhb_tohtml($key) ."\">". hhb_tohtml($value) ."</option>";
}
?>
</select>
<input type="submit" value="Set Position">
</form>
</li>
<li>
<b>Give shop points to character:</b>
<form action="" method="post">
<input type="hidden" name="csrf_token" value="<?php echo hhb_tohtml($_SESSION['csrf_token']);?>" />
<input type="text" name="points_char" placeholder="Character name">
<input type="text" name="points_value" placeholder="Points">
<input type="submit" value="Give Points">
</form>
</li>
<li>
<b>Teleport Player</b>
<form action="" method="post">
<input type="hidden" name="csrf_token" value="<?php echo hhb_tohtml($_SESSION['csrf_token']);?>" />
<table>
<tr>
<td>Type:</td>
<td>
<select name="from">
<option value="all">All</option>
<option value="only">Only</option>
</select>
</td>
</tr>
<tr>
<td>Player</td>
<td><input type="text" name="player_name" placeholder="Player Name"></td>
</tr>
<tr>
<td>To</td>
<td>
<select name="to">
<option value="home">Hometown</option>
<option value="town">Specific Town</option>
<option value="xyz">Specific Position</option>
</select>
</td>
</tr>
<tr>
<td>Town</td>
<td>
<select name="town">
<?php
foreach($config['towns'] as $townId => $townName) {
echo '<option value="' . hhb_tohtml($townId) . '">' . hhb_tohtml($townName) . '</option>';
}
?>
</select>
</td>
</tr>
<tr>
<td>Position</td>
<td>
<input type="text" name="x" placeholder="Position X">
<input type="text" name="y" placeholder="Position Y">
<input type="text" name="z" placeholder="Position Z">
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="teleport"></td></td>
</tr>
</tr>
</table>
</form>
</li>
</ul>
<div id="twitter"><?php include 'twtrNews.php'; ?></div>
<?php include 'layout/overall/footer.php';

View File

@@ -0,0 +1,232 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
protect_page();
admin_only($user_data);
$auction = $config['shop_auction'];
$step = $auction['step'];
$step_duration = $auction['step_duration'];
$loadOutfits = ($config['show_outfits']['highscores']) ? true : false;
function toDuration($is) {
$duration['day'] = $is / (24 * 60 * 60);
if (($duration['day'] - (int)$duration['day']) > 0)
$duration['hour'] = ($duration['day'] - (int)$duration['day']) * 24;
if (isset($duration['hour'])) {
if (($duration['hour'] - (int)$duration['hour']) > 0)
$duration['minute'] = ($duration['hour'] - (int)$duration['hour']) * 60;
if (isset($duration['minute'])) {
if (($duration['minute'] - (int)$duration['minute']) > 0)
$duration['second'] = ($duration['minute'] - (int)$duration['minute']) * 60;
}
}
$tmp = array();
foreach ($duration as $type => $value) {
if ($value >= 1) {
$pluralType = ((int)$value === 1) ? $type : $type . 's';
if ($type !== 'second') $tmp[] = (int)$value . " $pluralType";
else $tmp[] = (int)$value . " $pluralType";
}
}
return implode(', ', $tmp);
}
// start
// Passive check to see if bid period has expired and someone won a deal
$time = time();
$expired_auctions = mysql_select_multi("
SELECT `id`
FROM `znote_auction_player`
WHERE `sold` = 0
AND `time_end` < {$time}
AND `bidder_account_id` > 0
");
//data_dump($expired_auctions, $this_account_id, "expired_auctions");
if ($expired_auctions !== false) {
$soldIds = array();
foreach ($expired_auctions as $a) {
$soldIds[] = $a['id'];
}
if (!empty($soldIds)) {
mysql_update("
UPDATE `znote_auction_player`
SET `sold`=1
WHERE `id` IN(".implode(',', $soldIds).")
LIMIT ".COUNT($soldIds).";
");
}
}
// end passive check
// Pending auctions
$pending = mysql_select_multi("
SELECT
`za`.`id` AS `zaid`,
`za`.`price`,
`za`.`bid`,
`za`.`time_begin`,
`za`.`time_end`,
`p`.`id` AS `player_id`,
`p`.`name`,
`p`.`vocation`,
`p`.`level`,
`p`.`lookbody` AS `body`,
`p`.`lookfeet` AS `feet`,
`p`.`lookhead` AS `head`,
`p`.`looklegs` AS `legs`,
`p`.`looktype` AS `type`,
`p`.`lookaddons` AS `addons`
FROM `znote_auction_player` za
INNER JOIN `players` p
ON `za`.`player_id` = `p`.`id`
WHERE `p`.`account_id` = {$auction['storage_account_id']}
AND `za`.`claimed` = 0
AND `za`.`sold` = 1
ORDER BY `za`.`time_end` desc
");
// ongoing auctions
$ongoing = mysql_select_multi("
SELECT
`za`.`id` AS `zaid`,
`za`.`price`,
`za`.`bid`,
`za`.`time_begin`,
`za`.`time_end`,
`p`.`vocation`,
`p`.`level`,
`p`.`lookbody` AS `body`,
`p`.`lookfeet` AS `feet`,
`p`.`lookhead` AS `head`,
`p`.`looklegs` AS `legs`,
`p`.`looktype` AS `type`,
`p`.`lookaddons` AS `addons`
FROM `znote_auction_player` za
INNER JOIN `players` p
ON `za`.`player_id` = `p`.`id`
WHERE `p`.`account_id` = {$auction['storage_account_id']}
AND `za`.`sold` = 0
ORDER BY `za`.`time_end` desc;
");
// Completed auctions
$completed = mysql_select_multi("
SELECT
`za`.`id` AS `zaid`,
`za`.`price`,
`za`.`bid`,
`za`.`time_begin`,
`za`.`time_end`,
`p`.`id` AS `player_id`,
`p`.`name`,
`p`.`vocation`,
`p`.`level`,
`p`.`lookbody` AS `body`,
`p`.`lookfeet` AS `feet`,
`p`.`lookhead` AS `head`,
`p`.`looklegs` AS `legs`,
`p`.`looktype` AS `type`,
`p`.`lookaddons` AS `addons`
FROM `znote_auction_player` za
INNER JOIN `players` p
ON `za`.`player_id` = `p`.`id`
WHERE `za`.`claimed` = 1
ORDER BY `za`.`time_end` desc
");
?>
<h1>Character Auction History</h1>
<p><strong>Let players sell, buy and bid on characters.</strong>
<br>Creates a deeper shop economy, encourages players to spend more money in shop for points.
<br>Pay to win/progress mechanic, but also lets people who can barely afford points to gain it
<br>by leveling characters to sell. It can also discourages illegal/risky third-party account
<br>services. Since players can buy officially & support the server, dodgy competitors have to sell for cheaper.
<br>Without admin interference this is organic to each individual community economy inflation.</p>
<?php data_dump($config['shop_auction'], false, "config.php: shop_auction") ?>
<h2>Pending orders to be claimed</h2>
<?php if ($pending !== false): ?>
<table class="auction_char">
<tr class="yellow">
<td>Player</td>
<td>Level</td>
<td>Vocation</td>
<td>Price</td>
<td>Bid</td>
</tr>
<?php foreach($pending as $character): ?>
<tr>
<td><a href="/characterprofile.php?name=<?php echo $character['name']; ?>"><?php echo $character['name']; ?></a></td>
<td><?php echo $character['level']; ?></td>
<td><?php echo vocation_id_to_name($character['vocation']); ?></td>
<td><?php echo $character['price']; ?></td>
<td><?php echo $character['bid']; ?></td>
</tr>
<tr>
<td style="text-align: right;"><strong>Added:</strong></td>
<td><?php echo getClock($character['time_begin'], true); ?></td>
<td style="text-align: right;"><strong>Ended:</strong></td>
<td colspan="2"><?php echo getClock($character['time_end'], true); ?></td>
</tr>
<tr class="yellow">
<td colspan="5"></td>
</tr>
<?php endforeach; ?>
</table>
<?php endif; ?>
<h2>Ongoing auctions</h2>
<?php if (is_array($ongoing) && !empty($ongoing)): ?>
<table class="auction_char">
<tr class="yellow">
<td>Level</td>
<td>Vocation</td>
<td>Details</td>
<td>Price</td>
<td>Bid</td>
<td>Added</td>
<td>Type</td>
</tr>
<?php foreach($ongoing as $character): ?>
<tr>
<td><?php echo $character['level']; ?></td>
<td><?php echo vocation_id_to_name($character['vocation']); ?></td>
<td><a href="/auctionChar.php?action=view&zaid=<?php echo $character['zaid']; ?>">VIEW</a></td>
<td><?php echo $character['price']; ?></td>
<td><?php echo $character['bid']; ?></td>
<td><?php
$ended = (time() > $character['time_end']) ? true : false;
echo getClock($character['time_begin'], true);
?>
</td>
<td><?php echo ($ended) ? 'Instant' : 'Bidding<br>('.toDuration(($character['time_end'] - time())).')'; ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php endif; ?>
<h2>Completed auctions</h2>
<?php
if ($completed !== false): ?>
<table class="auction_char">
<tr class="yellow">
<td>Player</td>
<td>Level</td>
<td>Vocation</td>
<td>Price</td>
<td>Bid</td>
</tr>
<?php foreach($completed as $character): ?>
<tr>
<td><a href="/characterprofile.php?name=<?php echo $character['name']; ?>"><?php echo $character['name']; ?></a></td>
<td><?php echo $character['level']; ?></td>
<td><?php echo vocation_id_to_name($character['vocation']); ?></td>
<td><?php echo $character['price']; ?></td>
<td><?php echo $character['bid']; ?></td>
</tr>
<tr>
<td style="text-align: right;"><strong>Added:</strong></td>
<td><?php echo getClock($character['time_begin'], true); ?></td>
<td style="text-align: right;"><strong>Ended:</strong></td>
<td colspan="2"><?php echo getClock($character['time_end'], true); ?></td>
</tr>
<tr class="yellow">
<td colspan="5"></td>
</tr>
<?php endforeach; ?>
</table>
<?php endif;
// end
include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,146 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
protect_page();
admin_only($user_data);
// start
// Delete
if (isset($_POST['delete'])) {
$data = explode(":", $_POST['delete']);
echo 'Image '. $data[0] .' deleted.';
updateImage($data[0], 3);
}
// Remove
if (isset($_POST['remove'])) {
$data = explode(":", $_POST['remove']);
$did = (int)$data[0];
echo 'Image '. $did .' removed.';
$delhash = $_POST['delhash'];
$imgurClientID = $config['gallery']['Client ID'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.imgur.com/3/image/{$delhash}");
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
"Authorization: Client-ID {$imgurClientID}"
));
$response = json_decode(curl_exec($ch));
mysql_delete("DELETE FROM `znote_images` WHERE `id`='$did' LIMIT 1;");
}
// Accept
if (isset($_POST['accept'])) {
$data = explode(":", $_POST['accept']);
echo 'Image '. $data[0] .' accepted and is now public.';
updateImage($data[0], 2);
}
// Wether we accept or delete, re-create the cache
if (isset($_POST['accept']) || isset($_POST['delete'])) {
$cache = new Cache('engine/cache/gallery');
$images = fetchImages(2);
if ($images != false) {
$data = array();
foreach ($images as $image) {
$row['title'] = $image['title'];
$row['desc'] = $image['desc'];
$row['date'] = $image['date'];
$row['image'] = $image['image'];
$data[] = $row;
}
} else $data = "";
$cache->setContent($data);
$cache->save();
}
?><h1>Images in need of moderation:</h1><?php
$images = fetchImages(1);
if ($images != false) {
foreach($images as $image) {
?>
<table>
<tr class="yellow">
<td><h2><?php echo $image['title']; ?><form action="" method="post"><input type="submit" name="accept" value="<?php echo $image['id']; ?>:Accept Image"/></form><form action="" method="post"><input type="submit" name="delete" value="<?php echo $image['id']; ?>:Delete Image"/></form></h2></td>
</tr>
<tr>
<td>
<a href="<?php echo $image['image']; ?>"><img src="<?php echo $image['image']; ?>" alt="<?php echo $image['title']; ?>" style="max-width: 100%;"/></a>
</td>
</tr>
<tr>
<td>
<?php
$descr = str_replace("\\r", "", $image['desc']);
$descr = str_replace("\\n", "<br />", $descr);
?>
<p><?php echo $descr; ?></p>
</td>
</tr>
</table>
<?php }
} else echo '<h2>All good, no new images to moderate.</h2>';
?><h1>Public Images:</h1><?php
$images = fetchImages(2);
if ($images != false) {
foreach($images as $image) {
?>
<table>
<tr class="yellow">
<td><h2><?php echo $image['title']; ?><form action="" method="post"><input type="submit" name="delete" value="<?php echo $image['id']; ?>:Delete Image"/></form></h2></td>
</tr>
<tr>
<td>
<a href="<?php echo $image['image']; ?>"><img src="<?php echo $image['image']; ?>" alt="<?php echo $image['title']; ?>" style="max-width: 100%;"/></a>
</td>
</tr>
<tr>
<td>
<?php
$descr = str_replace("\\r", "", $image['desc']);
$descr = str_replace("\\n", "<br />", $descr);
?>
<p><?php echo $descr; ?></p>
</td>
</tr>
</table>
<?php }
} else echo '<h2>There are currently no public images.</h2>';
?><h1>Deleted Images:</h1><?php
$images = fetchImages(3);
if ($images != false) {
foreach($images as $image) {
?>
<table>
<tr class="yellow">
<td><h2><?php echo $image['title']; ?><form action="" method="post">
<input type="submit" name="accept" value="<?php echo $image['id']; ?>:Recover Image"/>
<input type="hidden" name="delhash" value="<?php echo $image['delhash']; ?>">
<input type="submit" name="remove" value="<?php echo $image['id']; ?>:Remove Image"/>
</form></h2></td>
</tr>
<tr>
<td>
<a href="<?php echo $image['image']; ?>"><img src="<?php echo $image['image']; ?>" alt="<?php echo $image['title']; ?>" style="max-width: 100%;"/></a>
</td>
</tr>
<tr>
<td>
<?php
$descr = str_replace("\\r", "", $image['desc']);
$descr = str_replace("\\n", "<br />", $descr);
?>
<p><?php echo $descr; ?></p>
</td>
</tr>
</table>
<?php }
} else echo '<h2>There are currently no deleted images.</h2>';
// end
include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,147 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
protect_page();
admin_only($user_data);
// Declare as int
$view = (isset($_GET['view']) && (int)$_GET['view'] > 0) ? (int)$_GET['view'] : false;
if ($view !== false){
if (!empty($_POST['reply_text'])) {
sanitize($_POST['reply_text']);
// Save ticket reply on database
$query = array(
'tid' => $view,
'username'=> getValue($_POST['username']),
'message' => getValue($_POST['reply_text']),
'created' => time(),
);
$fields = '`'. implode('`, `', array_keys($query)) .'`';
$data = '\''. implode('\', \'', $query) .'\'';
mysql_insert("INSERT INTO `znote_tickets_replies` ($fields) VALUES ($data)");
mysql_update("UPDATE `znote_tickets` SET `status`='Staff-Reply' WHERE `id`='$view' LIMIT 1;");
} else if (!empty($_POST['admin_ticket_close'])) {
$ticketId = (int) $_POST['admin_ticket_id'];
mysql_update("UPDATE `znote_tickets` SET `status` = 'CLOSED' WHERE `id` ='$ticketId' LIMIT 1;");
} else if (!empty($_POST['admin_ticket_open'])) {
$ticketId = (int) $_POST['admin_ticket_id'];
mysql_update("UPDATE `znote_tickets` SET `status` = 'Open' WHERE `id` ='$ticketId' LIMIT 1;");
} else if (!empty($_POST['admin_ticket_delete'])) {
$ticketId = (int) $_POST['admin_ticket_id'];
mysql_delete("DELETE FROM `znote_tickets` WHERE `id`='$ticketId' LIMIT 1;");
header("Location: admin_helpdesk.php");
}
$ticketData = mysql_select_single("SELECT * FROM znote_tickets WHERE id='$view' LIMIT 1;");
?>
<h1>View Ticket #<?php echo $ticketData['id']; ?></h1>
<table class="znoteTable ThreadTable table table-striped">
<tr class="yellow">
<th>
<?php
echo getClock($ticketData['creation'], true);
?>
- Created by:
<?php
echo $ticketData['username'];
?>
</th>
</tr>
<tr>
<td>
<p><?php echo nl2br($ticketData['message']); ?></p>
</td>
</tr>
</table>
<?php
$replies = mysql_select_multi("SELECT * FROM znote_tickets_replies WHERE tid='$view' ORDER BY `created`;");
if ($replies !== false) {
foreach($replies as $reply) {
?>
<table class="znoteTable ThreadTable table table-striped">
<tr class="yellow">
<th>
<?php
echo getClock($reply['created'], true);
?>
- Posted by:
<?php
echo $reply['username'];
?>
</th>
</tr>
<tr>
<td>
<p><?php echo nl2br($reply['message']); ?></p>
</td>
</tr>
</table>
<?php
}
}
?>
<!-- Open/Close Ticket -->
<table class="znoteTable ThreadTable table table-striped">
<tr>
<td>
<form action="" method="post" align="center">
<input type="hidden" name="admin_ticket_id" value="<?php echo $ticketData['id']; ?>">
<?php if ($ticketData['status'] !== 'CLOSED') { ?>
<input type="submit" name="admin_ticket_close" value="Close Ticket" class="btn btn-warning">
<?php } else { ?>
<input type="submit" name="admin_ticket_open" value="Open Ticket" class="btn btn-success">
<?php } ?>
</form>
</td>
<td>
<form action="" method="post" align="center" onClick="return confirm('Are you sure you want to delete this ticket?');">
<input type="hidden" name="admin_ticket_id" value="<?php echo $ticketData['id']; ?>">
<input type="submit" name="admin_ticket_delete" value="Delete Ticket" class="btn btn-danger">
</form>
</td>
</tr>
</table>
<?php if ($ticketData['status'] !== 'CLOSED') { ?>
<hr class="bighr">
<form action="" method="post">
<input type="hidden" name="username" value="ADMIN"><br>
<textarea class="forumReply" name="reply_text" style="width: 610px; height: 150px"></textarea><br>
<input name="" type="submit" value="Post Reply" class="btn btn-primary">
</form>
<?php } ?>
<?php
} else {
?>
<h1>Latest Tickets</h1>
<?php
$tickets = mysql_select_multi("SELECT id,subject,creation,status FROM znote_tickets ORDER BY creation DESC");
if ($tickets !== false) {
?>
<table>
<tr class="yellow">
<td>ID:</td>
<td>Subject:</td>
<td>Creation:</td>
<td>Status:</td>
</tr>
<?php
foreach ($tickets as $ticket) {
echo '<tr class="special">';
echo '<td>'. $ticket['id'] .'</td>';
echo '<td><a href="admin_helpdesk.php?view='. $ticket['id'] .'">'. $ticket['subject'] .'</a></td>';
echo '<td>'. getClock($ticket['creation'], true) .'</td>';
echo '<td>'. $ticket['status'] .'</td>';
echo '</tr>';
}
?>
</table>
<?php
} else echo 'No helpdesk tickets has been submitted.';
}
include 'layout/overall/footer.php';
?>

152
app/ZnoteAAC/admin_news.php Normal file
View File

@@ -0,0 +1,152 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
protect_page();
admin_only($user_data);
// Recieving POST
if (empty($_POST) === false) {
list($action, $id) = explode('!', sanitize($_POST['option']));
// Delete
if ($action === 'd') {
echo '<font color="green"><b>News deleted!</b></font>';
mysql_delete("DELETE FROM `znote_news` WHERE `id`='$id';");
$cache = new Cache('engine/cache/news');
$news = fetchAllNews();
$cache->setContent($news);
$cache->save();
}
// Add news
if ($action === 'a') {
// fetch data
$char_array = user_character_list($user_data['id']);
?>
<script src="engine/js/nicedit.js" type="text/javascript"></script>
<script type="text/javascript">bkLib.onDomLoaded(nicEditors.allTextAreas);</script>
<form action="" method="post">
<input type="hidden" name="option" value="i!0">
Select character:<select name="selected_char">
<?php
$count = 0;
if ($char_array !== false) {
foreach ($char_array as $name) {
$name = $name['name'];
$charD = user_character_data(user_character_id($name), 'group_id', 'id');
if ($charD['group_id'] > 1) {
echo '<option value="'. user_character_id($name) .'">'. $name .'</option>';
$count++;
}
}
}
?>
</select>
<input type="text" name="title" value="" placeholder="Title"> [youtube]wK0w0x62PjA[/youtube] <br />
<textarea name="text" id="area1" cols="75" rows="10" placeholder="Contents..." style="width: 100%"></textarea><br />
<input type="submit" value="Create News">
</form>
<?php
if ($count === 0) echo "<font size='6' color='red'>ERROR: NO GMs or Tutors on this account!</font>";
}
// Insert news
if ($action === 'i') {
echo '<font color="green"><b>News created successfully!</b></font>';
list($charid, $title, $text) = array((int)$_POST['selected_char'], mysql_znote_escape_string($_POST['title']), mysql_znote_escape_string($_POST['text']));
$date = time();
mysql_insert("INSERT INTO `znote_news` (`title`, `text`, `date`, `pid`) VALUES ('$title', '$text', '$date', '$charid');");
// Reload the cache.
$cache = new Cache('engine/cache/news');
$news = fetchAllNews();
$cache->setContent($news);
$cache->save();
}
// Save
if ($action === 's') {
echo '<font color="green"><b>News successfully updated!</b></font>';
list($title, $text) = array(mysql_znote_escape_string($_POST['title']), mysql_znote_escape_string($_POST['text']));
mysql_update("UPDATE `znote_news` SET `title`='$title',`text`='$text' WHERE `id`='$id';");
$cache = new Cache('engine/cache/news');
$news = fetchAllNews();
$cache->setContent($news);
$cache->save();
}
// Edit
if ($action === 'e') {
$news = fetchAllNews();
$edit = array();
foreach ($news as $n) if ($n['id'] == $id) $edit = $n;
?>
<script src="engine/js/nicedit.js" type="text/javascript"></script>
<script type="text/javascript">bkLib.onDomLoaded(nicEditors.allTextAreas);</script>
<form action="" method="post">
<input type="hidden" name="option" value="s!<?php echo $id; ?>">
<input type="text" name="title" value="<?php echo $edit['title']; ?>"><br />
<textarea name="text" cols="75" rows="10" style="width: 100%"><?php echo $edit['text']; ?></textarea><br />
<input type="submit" value="Save Changes">
</form>
<br>
<p>
[b]<b>Bold Text</b>[/b]<br>
[size=5]Size 5 text[/size]<br>
[img]<a href="https://imgur.com/" target="_BLANK">Direct Image Link</a>[/img]<br>
[center]Cented Text[/center]<br>
[link]<a href="https://youtube.com/" target="_BLANK">https://youtube.com/</a>[/link]<br>
[link=https://youtube.com/]<a href="http://youtube.com/" target="_BLANK">Click to View youtube</a>[/link]<br>
[color=<font color="green">GREEN</font>]<font color="green">Green Text!</font>[/color]<br>
[*]* Noted text [/*]
</p>
<?php
}
}
?>
<h1>News admin panel</h1>
<form action="" method="post">
<input type="hidden" name="option" value="a!0">
<input type="submit" value="Create new article">
</form>
<?php
// pre stuff
$news = fetchAllNews();
if ($news !== false) {
?>
<table id="news">
<tr class="yellow">
<td>Date</td>
<td>By</td>
<td>Title</td>
<td>Edit</td>
<td>Delete</td>
</tr>
<?php
foreach ($news as $n) {
echo '<tr>';
echo '<td>'. getClock($n['date'], true) .'</td>';
echo '<td><a href="characterprofile.php?name='. $n['name'] .'">'. $n['name'] .'</a></td>';
echo '<td>'. $n['title'] .'</td>';
echo '<td>';
// edit
?>
<form action="" method="post">
<input type="hidden" name="option" value="e!<?php echo $n['id']; ?>">
<input type="submit" value="Edit">
</form>
<?php
echo '</td>';
echo '<td>';
// delete
?>
<form action="" method="post">
<input type="hidden" name="option" value="d!<?php echo $n['id']; ?>">
<input type="submit" value="Delete">
</form>
<?php
echo '</td>';
echo '</tr>';
}
?>
</table>
<?php
}
include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,243 @@
<?php
require_once 'engine/init.php';
protect_page();
admin_only($user_data);
include 'layout/overall/header.php';
// Report status types. When a player make new report it will be default to 0.
// Feel free to add/remove and change name/color of status types.
$statusTypes = array(
0 => '<font color="purple">Reported</font>',
1 => '<font color="darkblue">To-Do List</font>',
2 => '<font color="red">Confirmed bug</font>',
3 => '<font color="grey">Invalid</font>',
4 => '<font color="grey">Rejected</font>',
5 => '<font color="green"><b>Fixed</b></font>'
);
// Which status IDs should give option to add to changelog?
$statusChangeLog = array(0,5);
// Autohide rows that have these status IDs:
$hideStatus = array(3, 4, 5);
// Fetch data from SQL
$reportsData = mysql_select_multi('SELECT id, name, posx, posy, posz, report_description, date, status FROM znote_player_reports ORDER BY id DESC;');
// If SQL data is not empty
if ($reportsData !== false) {
// Order reports array by ID for easy reference later on.
$reports = array();
for ($i = 0; $i < count($reportsData); $i++)
foreach ($statusTypes as $key => $value)
if ($key == $reportsData[$i]['status'])
$reports[$key][$reportsData[$i]['id']] = $reportsData[$i];
}
// POST logic (Update report and give player points)
if (!empty($_POST)) {
// Fetch POST data
$playerName = getValue($_POST['playerName']);
$status = getValue($_POST['status']);
$price = getValue($_POST['price']);
$customPoints = getValue($_POST['customPoints']);
$reportId = getValue($_POST['id']);
$changelogReportId = (int)$_POST['changelogReportId'];
$changelogValue = &$_POST['changelogValue'];
$changelogText = getValue($_POST['changelogText']);
$changelogStatus = ($changelogReportId !== false && $changelogValue === '2' && $changelogText !== false) ? true : false;
if ($customPoints !== false) $price = (int)($price + $customPoints);
// Update SQL
mysql_update("UPDATE `znote_player_reports` SET `status`='$status' WHERE `id`='$reportId' LIMIT 1;");
echo "<h1>Report status updated to ".$statusTypes[(int)$status] ."!</h1>";
// Update local array representation
foreach ($reports as $sid => $sa)
foreach ($sa as $rid => $ra)
if ($reportId == $rid) {
$reports[$status][$reportId] = $reports[$sid][$rid];
$reports[$status][$reportId]['status'] = $status;
unset($reports[$sid][$rid]);
}
// If we should do anything with changelog:
if ($changelogStatus) {
$time = time();
// Check if changelog exist (`id`, `text`, `time`, `report_id`, `status`)
$changelog = mysql_select_single("SELECT * FROM `znote_changelog` WHERE `report_id`='$changelogReportId' LIMIT 1;");
// If changelog exist
$updatechangelog = false;
if ($changelog !== false) {
// Update it
mysql_update("UPDATE `znote_changelog` SET `text`='$changelogText', `time`='$time' WHERE `id`='".$changelog['id']."' LIMIT 1;");
echo "<h2>Changelog message updated!</h2>";
$updatechangelog = true;
} else {
// Create it
mysql_insert("INSERT INTO `znote_changelog` (`text`, `time`, `report_id`, `status`)
VALUES ('$changelogText', '$time', '$changelogReportId', '$status');");
echo "<h2>Changelog message created!</h2>";
$updatechangelog = true;
}
if ($updatechangelog) {
// Cache changelog
$cache = new Cache('engine/cache/changelog');
$cache->setContent(mysql_select_multi("SELECT `id`, `text`, `time`, `report_id`, `status` FROM `znote_changelog` ORDER BY `id` DESC;"));
$cache->save();
}
}
// If we should give user price
if ($price > 0) {
$account = mysql_select_single("SELECT `a`.`id`, `a`.`email` FROM `accounts` AS `a`
INNER JOIN `players` AS `p` ON `p`.`account_id` = `a`.`id`
WHERE `p`.`name` = '$playerName' LIMIT 1;");
if ($account !== false) {
// transaction log
mysql_insert("INSERT INTO `znote_paypal` VALUES ('', '$reportId', 'report@admin.".$user_data['name']." to ".$account['email']."', '".$account['id']."', '0', '".$price."')");
// Process payment
$data = mysql_select_single("SELECT `points` AS `old_points` FROM `znote_accounts` WHERE `account_id`='".$account['id']."';");
// Give points to user
$new_points = $data['old_points'] + $price;
mysql_update("UPDATE `znote_accounts` SET `points`='$new_points' WHERE `account_id`='".$account['id']."'");
// Remind GM that he sent points to character
echo "<font color='green' size='5'>".$playerName." has been granted ".$price." points for his reports.</font>";
}
}
// GET logic (Edit report data and specify how many [if any] points to give to user)
} elseif (!empty($_GET)) {
// Fetch GET data
$action = getValue($_GET['action']);
$playerName = getValue($_GET['name']);
$reportId = getValue($_GET['id']);
// Fetch the report we intend to modify
foreach ($reports as $sid => $sa)
foreach ($sa as $rid => $ra)
if ($rid == $reportId)
$report = $reports[$sid][$reportId];
// Create HTML form
?>
<div style="width: 300px; margin: auto;">
<form action="admin_reports.php" method="POST">
Player: <a target="_BLANK" href="characterprofile.php?name=<?php echo $report['name']; ?>"><?php echo $report['name']; ?></a>
<input type="hidden" name="playerName" value="<?php echo $report['name']; ?>">
<input type="hidden" name="id" value="<?php echo $report['id']; ?>">
<br>Set status:
<select name="status">
<?php
foreach ($statusTypes as $sid => $sname)
echo ($sid != $report['status']) ? "<option value='$sid'>$sname</option>" : "<option value='$sid' selected>$sname</option>";
?>
</select><br>
Give user points:
<select name="price">
<option value='0'>0</option>
<?php
foreach ($config['paypal_prices'] as $price)
echo "<option value='$price'>$price</option>";
?>
</select> + <input name="customPoints" type="text" style="width: 50px;" placeholder="0"><br>
<?php
if (in_array($report['status'], $statusChangeLog)) {
?>
<br>
<input type="hidden" name="changelogReportId" value="<?php echo $report['id']; ?>">
Add / update changelog message? <select name="changelogValue">
<option value="1">No</option>
<option value="2">Yes</option>
</select><br>
<textarea rows="7" cols="40" maxlength="254" name="changelogText"></textarea>
<?php
}
?>
<br>
<input type="submit" value="Update Report" style="width: 100%;">
</form>
</div>
<?php
}
// If SQL data is not empty
if ($reportsData !== false) {
// Render HTML
?>
<center>
<?php
foreach ($reports as $statusId => $statusArray) {
?>
<h2 class="statusType"><?php echo $statusTypes[$statusId]; ?> (<span id="status-<?php echo $statusId; ?>">Visible</span>)</h2>
<table class="table tbl" border="0" cellspacing="1" cellpadding="4" width="100%">
<thead>
<tr class="yellow" onclick="javascript:toggle('<?php echo $statusId; ?>')">
<td width="38%">Info</td>
<td>Description</td>
</tr>
</thead>
<?php
foreach ($statusArray as $reportId => $report) {
?>
<tbody class="row<?php echo $report['status']; ?>">
<tr>
<td>
<b>Report ID:</b> #<?php echo $report['id']; ?>
<br><b>Name:</b> <a href="characterprofile.php?name=<?php echo $report['name']; ?>"><?php echo $report['name']; ?></a>
<br><b>Position:</b> <input type="text" disabled value="/pos <?php echo $report['posx'].', '.$report['posy'].', '.$report['posz']; ?>">
<br><b>Reported:</b> <?php echo getClock($report['date'], true, true); ?>
<br><b>Status:</b> <?php echo $statusTypes[$report['status']]; ?>. <a href="?action=edit&name=<?php echo $report['name'].'&id='.$report['id']; ?>">Edit</a>
</td>
<td><?php echo $report['report_description']; ?></td>
</tr>
</tbody>
<?php
}
?></table><?php
}
?>
</center>
<?php
} else echo "<h2>No reports submitted.</h2>";
?>
<style>
tr.yellow[onclick] td {
font-weight: bold;
color: white;
text-align: center;
}
tbody[class^=row] td:last-of-type {
text-align: center;
}
</style>
<script type="text/javascript">
// Hide and show tables
// Written in clean javascript to make it cross-layout compatible.
function toggle(statusId) {
var divStatus = 'row' + statusId,
msgStatus = 'status-' + statusId;
// Change visibility status
statusElement = document.getElementById(msgStatus);
statusElement.innerHTML = (statusElement.innerHTML == 'Visible') ? 'Hidden' : 'Visible';
// Show/hide elements.
var elements = document.getElementsByClassName(divStatus);
for (var i = 0; i < elements.length; i++)
elements[i].style.display = (elements[i].style.display == 'none') ? 'table-header-group' : 'none';
}
<?php // Hide configured tables by default
foreach ($hideStatus as $statusId)
echo "toggle($statusId);";
?>
var st = document.body.querySelectorAll('.statusType');
for(i = 0; i < st.length; i++)
st[i].addEventListener('click', function(e) {
toggle(e.currentTarget.querySelector('span').id.match(/(\d)+/)[0]);
});
</script>
<?php include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,70 @@
<?php
require_once 'engine/init.php';
include 'layout/overall/header.php';
protect_page();
admin_only($user_data);
$orders = mysql_select_multi('SELECT * FROM `znote_shop_orders` ORDER BY `id` DESC;');
$order_types = array(1 => 'Item', 2 => 'Premium Days', 3 => 'Gender Change', 4 => 'Name Change', 5 => 'Outfits', 6 =>'Mounts');
$items = getItemList();
?>
<h1>Shop Logs</h1>
<h2>Pending Orders</h2>
<p>These are pending orders, like items bought, but not received or used yet.</p>
<table>
<thead>
<th>Id</th>
<th>Account</th>
<th>Type</th>
<th>Item</th>
<th>Count</th>
<th>Date</th>
</thead>
<tbody>
<?php foreach(($orders ? $orders : array()) as $order) { ?>
<tr>
<td><?php echo $order['id']; ?></td>
<td><?php echo user_account_id_from_name($order['account_id']); ?></td>
<td><?php echo $order_types[$order['type']] ?></td>
<td><?php echo '(' . $order['itemid'] . ') ', (isset($items[$order['itemid']])) ? $items[$order['itemid']] : ''; ?></td>
<td><?php echo $order['count'] ?></td>
<td><?php echo date('Y/m/d H:i', $order['time']) ?></td>
</tr>
<?php } ?>
</tbody>
</table>
<?php
$orders = mysql_select_multi('SELECT * FROM `znote_shop_logs` ORDER BY `id` DESC;');
$order_types = array(1 => 'Item', 2 => 'Premium Days', 3 => 'Gender Change', 4 => 'Name Change', 5 => 'Outfit', 6 =>'Mount', 7 =>'Custom');
?>
<h2>Order History</h2>
<p>This list contains all transactions bought in the shop.</p>
<table>
<thead>
<th>Id</th>
<th>Account</th>
<th>Type</th>
<th>Item</th>
<th>Count</th>
<th>points</th>
<th>Date</th>
</thead>
<tbody>
<?php foreach(($orders ? $orders : array()) as $order) { ?>
<tr>
<td><?php echo $order['id']; ?></td>
<td><?php echo $order['account_id']; ?></td>
<td><?php echo $order_types[$order['type']] ?></td>
<td><?php echo '(' . $order['itemid'] . ') ', (isset($items[$order['itemid']])) ? $items[$order['itemid']] : ''; ?></td>
<td><?php echo $order['count'] ?></td>
<td><?php echo $order['points'] ?></td>
<td><?php echo getClock($order['time'], true, false); ?></td>
</tr>
<?php } ?>
</tbody>
</table>
<?php
include 'layout/overall/footer.php';
?>

View File

@@ -0,0 +1,189 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
protect_page();
admin_only($user_data);
// PREP: Create a function that echos player skills
function playerSkill($skills, $id) {
if (!$skills) return 0;
else {
return $skills[$id]['value'];
}
}
// UPDATE SKILLS POST
if (isset($_POST['pid']) && (int)$_POST['pid'] > 0) {
$pid = (int)$_POST['pid'];
if ($config['ServerEngine'] != 'TFS_10') $status = user_is_online($pid);
else $status = user_is_online_10($pid);
if (!$status) {
// New player level
$level = (int)$_POST['level'];
// Fetch stat gain for vocation
$statgain = $config['vocations_gain'][(int)$_POST['vocation']];
$playercnf = $config['player'];
/*
if ((int)$_POST['vocation'] !== 0) {
// Fetch base level and stats:
$baselevel = $config['level'];
$basehealth = $config['health'];
$basemana = $config['mana'];
$basecap = $config['cap'];
} else { // No vocation stats
// Fetch base level and stats:
$baselevel = $config['nvlevel'];
$basehealth = $config['nvHealth'];
$basemana = $config['nvMana'];
$basecap = $config['nvCap'];
}
*/
$LevelsFromBase = $level - $playercnf['base']['level'];
$newhp = $playercnf['base']['health'] + ($statgain['hp'] * $LevelsFromBase);
$newmp = $playercnf['base']['mana'] + ($statgain['mp'] * $LevelsFromBase);
$newcap = $playercnf['base']['cap'] + ($statgain['cap'] * $LevelsFromBase);
// Calibrate hp/mana/cap
if ($config['ServerEngine'] != 'TFS_10') {
mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['fist'] ."' WHERE `player_id`='$pid' AND `skillid`='0' LIMIT 1;");
mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['club'] ."' WHERE `player_id`='$pid' AND `skillid`='1' LIMIT 1;");
mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['sword'] ."' WHERE `player_id`='$pid' AND `skillid`='2' LIMIT 1;");
mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['axe'] ."' WHERE `player_id`='$pid' AND `skillid`='3' LIMIT 1;");
mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['dist'] ."' WHERE `player_id`='$pid' AND `skillid`='4' LIMIT 1;");
mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['shield'] ."' WHERE `player_id`='$pid' AND `skillid`='5' LIMIT 1;");
mysql_update("UPDATE `player_skills` SET `value`='". (int)$_POST['fish'] ."' WHERE `player_id`='$pid' AND `skillid`='6' LIMIT 1;");
mysql_update("UPDATE `players` SET `maglevel`='". (int)$_POST['magic'] ."' WHERE `id`='$pid' LIMIT 1;");
mysql_update("UPDATE `players` SET `vocation`='". (int)$_POST['vocation'] ."' WHERE `id`='$pid' LIMIT 1;");
mysql_update("UPDATE `players` SET `level`='". $level ."' WHERE `id`='$pid' LIMIT 1;");
mysql_update("UPDATE `players` SET `experience`='". level_to_experience($level) ."' WHERE `id`='$pid' LIMIT 1;");
// Update HP/mana/cap accordingly to level & vocation
mysql_update("UPDATE `players` SET `health`='". $newhp ."', `healthmax`='". $newhp ."', `mana`='". $newmp ."', `manamax`='". $newmp ."', `cap`='". $newcap ."' WHERE `id`='$pid' LIMIT 1;");
} else {
mysql_update("UPDATE `players` SET `health`='". $newhp ."', `healthmax`='". $newhp ."', `mana`='". $newmp ."', `manamax`='". $newmp ."', `cap`='". $newcap ."', `vocation`='". (int)$_POST['vocation'] ."', `skill_fist`='". (int)$_POST['fist'] ."', `skill_club`='". (int)$_POST['club'] ."', `skill_sword`='". (int)$_POST['sword'] ."', `skill_axe`='". (int)$_POST['axe'] ."', `skill_dist`='". (int)$_POST['dist'] ."', `skill_shielding`='". (int)$_POST['shield'] ."', `skill_fishing`='". (int)$_POST['fish'] ."', `maglevel`='". (int)$_POST['magic'] ."', `level`='". $level ."', `experience`='". level_to_experience($level) ."' WHERE `id`='$pid' LIMIT 1;");
}
?>
<h1>Player skills updated!</h1>
<?php
} else {
?>
<font color="red" size="7">Player must be offline!</font>
<?php
}
}
// Stage 1: Fetch name
if (isset($_GET['name'])) {
$name = getValue($_GET['name']);
} else $name = false;
//if (isset($_POST['name'])) $name = getValue($_POST['name']);
// Stage 2: Fetch user id and skills
$skills = false;
$pid = 0;
if ($name !== false) {
if (user_character_exist($name)) {
$pid = user_character_id($name);
if ($config['ServerEngine'] != 'TFS_10') {
$skills = mysql_select_multi("SELECT `value` FROM `player_skills` WHERE `player_id`='$pid' LIMIT 7;");
$player = mysql_select_single("SELECT `maglevel`, `level`, `vocation` FROM `players` WHERE `id`='$pid' LIMIT 1;");
$skills[] = array('value' => $player['maglevel']);
$skills[] = array('value' => $player['level']);
$skills[] = array('value' => $player['vocation']);
} else {
$player = mysql_select_single("SELECT `skill_fist`, `skill_club`, `skill_sword`, `skill_axe`, `skill_dist`, `skill_shielding`, `skill_fishing`, `maglevel`, `level`, `vocation` FROM `players` WHERE `id`='$pid' LIMIT 1;");
$skills = array(
0 => array('value' => $player['skill_fist']),
1 => array('value' => $player['skill_club']),
2 => array('value' => $player['skill_sword']),
3 => array('value' => $player['skill_axe']),
4 => array('value' => $player['skill_dist']),
5 => array('value' => $player['skill_shielding']),
6 => array('value' => $player['skill_fishing']),
7 => array('value' => $player['maglevel']),
8 => array('value' => $player['level']),
9 => array('value' => $player['vocation'])
);
}
//data_dump($skills, false, "Player skills");
} else $name = false;
}
?>
<form action="" method="<?php if (!$name) echo "get"; else echo "post";?>">
<input type="hidden" name="pid" value="<?php echo $pid; ?>">
<table class="table">
<tr class="yellow">
<td colspan="2"><center><font size="6">Player skills administration</font></center></td>
</tr>
<tr>
<td>
<input name="name" type="text" placeholder="Character name" <?php if ($name !== false) echo "value='$name' disabled";?>>
<br><br>
Vocation:<br>
<select name="vocation" <?php if (!$name) echo "disabled";?>>
<?php
$vocations = $config['vocations'];
foreach ($vocations as $vid => $vname) {
?>
<option value="<?php echo $vid; ?>" <?php if ($vid == playerSkill($skills, 9)) echo "selected"?> ><?php echo $vname['name']; ?></option>
<?php
}
?>
</select>
<br><br>
Fist fighting:<br>
<input name="fist" type="text" <?php if (!$name) echo "disabled";?> value="<?php echo playerSkill($skills, 0); ?>">
<br><br>
Club fighting:<br>
<input name="club" type="text" <?php if (!$name) echo "disabled";?> value="<?php echo playerSkill($skills, 1); ?>">
<br><br>
Sword fighting:<br>
<input name="sword" type="text" <?php if (!$name) echo "disabled";?> value="<?php echo playerSkill($skills, 2); ?>">
<br><br>
Axe fighting:<br>
<input name="axe" type="text" <?php if (!$name) echo "disabled";?> value="<?php echo playerSkill($skills, 3); ?>">
<br><br>
</td>
<td>
Dist fighting:<br>
<input name="dist" type="text" <?php if (!$name) echo "disabled";?> value="<?php echo playerSkill($skills, 4); ?>">
<br><br>
Shield fighting:<br>
<input name="shield" type="text" <?php if (!$name) echo "disabled";?> value="<?php echo playerSkill($skills, 5); ?>">
<br><br>
Fish fighting:<br>
<input name="fish" type="text" <?php if (!$name) echo "disabled";?> value="<?php echo playerSkill($skills, 6); ?>">
<br><br>
Level:<br>
<input name="level" type="text" <?php if (!$name) echo "disabled";?> value="<?php echo playerSkill($skills, 8); ?>">
<br><br>
Magic level:<br>
<input name="magic" type="text" <?php if (!$name) echo "disabled";?> value="<?php echo playerSkill($skills, 7); ?>">
<br><br>
</td>
</tr>
<tr>
<td colspan="2">
<?php
if (!$name) {
?>
<input class="btn btn-primary" type="submit" value="Fetch character skills info">
<?php
} else {
?>
<input class="btn btn-success" type="submit" value="UPDATE SKILLS">
<?php
}
?>
</td>
</tr>
</table>
<a href="admin_skills.php">Reset fields / search new character</a>
</form>
<?php
// end
include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,9 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
protect_page();
admin_only($user_data);
// start
// end
include 'layout/overall/footer.php'; ?>

47
app/ZnoteAAC/api/api.php Normal file
View File

@@ -0,0 +1,47 @@
<?php
// Verify the PHP version, gives tutorial if fail.
if (version_compare(phpversion(), '5.6', '<')) die('PHP version 5.6 or higher is required.');
if (!isset($filepath)) $filepath = '../';
$version = '1.6';
session_start();
ob_start();
require_once $filepath.'config.php';
require_once $filepath.'engine/database/connect.php';
require_once $filepath.'engine/function/general.php';
require_once $filepath.'engine/function/cache.php';
// Initiate default config if nothing is specified (outdated config file)
if (!isset($config['api']['debug'])) $config['api']['debug'] = false;
$response = array(
'version' => array(
'znote' => $version,
'ot' => $config['ServerEngine']
),
);
if (isset($moduleVersion)) $response['version']['module'] = $moduleVersion;
function UseClass($name = false, $module = false, $path = false) {
if ($name !== false) {
if (!is_array($name)) {
if (!$module) $module = $name;
if (!$path) require_once "modules/base/{$module}/class/{$name}.php";
else require_once "{$path}/{$name}.php";
} else {
foreach ($name as $class) {
if (!$module) $module = $class;
if (!$path) require_once "modules/base/{$module}/class/{$class}.php";
else require_once "{$path}/{$class}.php";
}
}
} else die('Error in function UseClass: class parameter is false.');
}
function SendResponse($response) {
global $config;
if ($config['api']['debug'] || isset($_GET['debug'])) data_dump($response, false, "Response (debug mode)");
else echo json_encode($response);
}
?>

View File

@@ -0,0 +1,55 @@
<?php $filepath = '../'; require_once 'module.php';
// Autofetch API modules
$directory = 'modules';
$plugins = array();
// Load base
$plugins['base'] = array(
'player' => 'test.php'
);
$iterator = new DirectoryIterator($directory);
foreach($iterator as $entity) {
if($entity->isDot())
continue;
$iterator = new DirectoryIterator($entity->getPathname());
foreach($iterator as $entity) {
if($entity->isFile()) {
$file_extension = pathinfo($entity->getFilename(), PATHINFO_EXTENSION);
if ($file_extension == 'php') {
$path = explode('/', $entity->getPathname());
if (count($path) === 1) $path = explode('\\', $entity->getPathname());
$plugins[$path[1]] = $path[2];
}
}
}
}
$response['modules'] = $plugins;
$response['data']['title'] = $config['site_title'];
$response['data']['slogan'] = $config['site_title_context'];
$response['data']['time'] = getClock(time(), false, true);
$response['data']['time_formatted'] = getClock(time(), true, true);
// Account count
$accounts = mysql_select_single("SELECT COUNT('id') AS `count` FROM `accounts`;");
$response['data']['accounts'] = ($accounts !== false) ? (int)$accounts['count'] : 0;
// Player count
$players = mysql_select_single("SELECT COUNT('id') AS `count` FROM `players`;");
$response['data']['players'] = ($players !== false) ? (int)$players['count'] : 0;
// online player count
if ($config['ServerEngine'] != 'TFS_10') {
$online = mysql_select_single("SELECT COUNT('id') AS `count`, COUNT(DISTINCT `lastip`) AS `unique` FROM `players` WHERE `online`='1';");
} else {
$online = mysql_select_single("SELECT COUNT(`o`.`player_id`) AS `count`, COUNT(DISTINCT `p`.`lastip`) AS `unique` FROM `players_online` AS `o` INNER JOIN `players` AS `p` ON `o`.`player_id` = `p`.`id`;");
}
$response['data']['online'] = ($online !== false) ? (int)$online['count'] : 0;
$response['data']['online_unique_ip'] = ($online !== false) ? (int)$online['unique'] : 0;
$response['data']['client'] = $config['client'];
$response['data']['port'] = $config['port'];
$response['data']['guildwar'] = $config['guildwar_enabled'];
$response['data']['forum'] = $config['forum']['enabled'];
SendResponse($response);
?>

View File

@@ -0,0 +1,3 @@
<?php if (!isset($filepath)) $filepath = '../../../';
$moduleVersion = 1;
require 'api.php'; ?>

View File

@@ -0,0 +1,376 @@
<?php
class Player {
protected $_playerdata = array(
'id' => null,
'name' => null,
'world_id' => null,
'group_id' => null,
'account_id' => null,
'level' => null,
'vocation' => null,
'health' => null,
'healthmax' => null,
'experience' => null,
'lookbody' => null,
'lookfeet' => null,
'lookhead' => null,
'looklegs' => null,
'looktype' => null,
'lookaddons' => null,
'maglevel' => null,
'mana' => null,
'manamax' => null,
'manaspent' => null,
'soul' => null,
'town_id' => null,
'posx' => null,
'posy' => null,
'posz' => null,
'conditions' => null,
'cap' => null,
'sex' => null,
'lastlogin' => null,
'lastip' => null,
'save' => null,
'skull' => null,
'skulltime' => null,
'rank_id' => null,
'guildnick' => null,
'lastlogout' => null,
'blessings' => null,
'balance' => null,
'stamina' => null,
'direction' => null,
'loss_experience' => null,
'loss_mana' => null,
'loss_skills' => null,
'loss_containers' => null,
'loss_items' => null,
'premend' => null,
'online' => null,
'marriage' => null,
'promotion' => null,
'deleted' => null,
'description' => null,
'onlinetime' => null,
'deletion' => null,
'offlinetraining_time' => null,
'offlinetraining_skill' => null,
'skill_fist' => null,
'skill_fist_tries' => null,
'skill_club' => null,
'skill_club_tries' => null,
'skill_sword' => null,
'skill_sword_tries' => null,
'skill_axe' => null,
'skill_axe_tries' => null,
'skill_dist' => null,
'skill_dist_tries' => null,
'skill_shielding' => null,
'skill_shielding_tries' => null,
'skill_fishing' => null,
'skill_fishing_tries' => null,
);
protected $_znotedata = array(
'comment' => null,
'created' => null,
'hide_char' => null,
);
protected $_name_id = false;
protected $_querylog = array();
protected $_errors = array();
public function __construct($name_id_array, $fields = false, $query = true) {
if (!is_array($name_id_array)) $this->_name_id = $name_id_array;
if ($name_id_array !== false) {
// Fetch player by name or id
if (is_string($name_id_array) || is_integer($name_id_array)) {
if ($query) {
$this->update($this->mysql_select($name_id_array, $fields));
}
}
// Load these player data.
if (is_array($name_id_array)) {
if (isset($name_id_array['id'])) $this->_name_id = $name_id_array['id'];
elseif (isset($name_id_array['name'])) $this->_name_id = $name_id_array['name'];
$this->update($name_id_array);
}
} else die("Player construct takes arguments: string or id for fetch, array for load.");
}
/**
* Return all player data, or the fields specified in param $fields.
*
* @param array $fields
* @access public
* @return mixed (array 'field' => 'value', or false (bool))
**/
public function fetch($fields = false) {
if (is_string($fields)) $fields = array($fields);
// Return all data that is not null.
if (!$fields) {
$returndata = array();
foreach ($this->_playerdata as $field => $value) {
if (!is_null($value)) $returndata[$field] = $value;
}
foreach ($this->_znotedata as $field => $value) {
if (!is_null($value)) $returndata[$field] = $value;
}
return $returndata;
} else {
// The return array
$returndata = array();
// Array containing null fields, we need to fetch these from db later on.
$missingValues = array();
// Populate the two above arrays
foreach ($fields as $field) {
if (array_key_exists($field, $this->_playerdata)) {
if (is_null($this->_playerdata[$field])) $missingValues[] = $field;
else $returndata[$field] = $this->_playerdata[$field];
} elseif (array_key_exists($field, $this->_znotedata)) {
if (is_null($this->_znotedata[$field])) $missingValues[] = $field;
else $returndata[$field] = $this->_znotedata[$field];
}
}
// See if we are missing any values
if (!empty($missingValues)) {
// Query for this data
$data = $this->mysql_select($this->_name_id, $missingValues);
// Update this object
$this->update($data);
foreach ($data as $field => $value) {
$returndata[$field] = $value;
}
}
return $returndata;
}
return false;
}
/**
* Update player data.
*
* @param array $fields
* @access public
* @return mixed (array, boolean)
**/
public function update($data) {
if (is_array($data) && !empty($data)) {
foreach ($data as $field => $value) {
if (array_key_exists($field, $this->_playerdata)) {
$this->_playerdata[$field] = $value;
} elseif (array_key_exists($field, $this->_znotedata)) {
$this->_znotedata[$field] = $value;
}
}
return true;
}
return false;
}
public function getErrors() {
return (!empty($this->_errors)) ? $this->_errors : false;
}
public function dumpErrors() {
if ($this->getErrors() !== false)
data_dump($this->getErrors(), false, "Errors detected in player class:");
}
/**
* Select player data from mysql.
*
* @param mixed (int, string) $name_id, array $fields
* @access private
* @return mixed (array, boolean)
**/
private function mysql_select($name_id, $fields = false) {
$table = 'players';
$znote_table = 'znote_players';
$znote_fields = array();
// Dynamic fields logic
switch (gettype($fields)) {
case 'boolean':
$field_elements = '*';
$znote_fields = array('comment', 'created', 'hide_char');
break;
case 'string':
$fields = array($fields);
case 'array':
// Get rid of fields related to znote_
foreach ($fields as $key => $field) {
if (!array_key_exists($field, $this->_playerdata)) {
$znote_fields[] = $field;
unset($fields[$key]);
}
}
//Since we use for loop later, we need to reindex the array if we unset something.
if (!empty($znote_fields)) $fields = array_values($fields);
// Add 'id' field if its not already there.
if (!in_array('id', $fields)) $fields[] = 'id';
// Loop through every field and generate the sql string
for ($i = 0; $i < count($fields); $i++) {
if ($i === 0) $field_elements = "`". getValue($fields[$i]) ."`";
else $field_elements .= ", `". getValue($fields[$i]) ."`";
}
break;
}
// Value logic
if (is_integer($name_id)) {
$name_id = (int)$name_id;
$where = "`id` = '{$name_id}'";
} else {
$name_id = getValue($name_id);
$where = "`name` = '{$name_id}'";
}
$query = "SELECT {$field_elements} FROM `{$table}` WHERE {$where} LIMIT 1;";
// Log query to player object
$this->_querylog[] = $query;
// Fetch from players table
$data = mysql_select_single($query);
if (isset($data['conditions'])) unset($data['conditions']);
// Fetch from znote_players table if neccesary
if (!empty($znote_fields)) {
// Loop through every field and generate the sql string
for ($i = 0; $i < count($znote_fields); $i++) {
if ($i === 0) $field_elements = "`". getValue($znote_fields[$i]) ."`";
else $field_elements .= ", `". getValue($znote_fields[$i]) ."`";
}
$query = "SELECT {$field_elements} FROM `{$znote_table}` WHERE `player_id`='".$data['id']."' LIMIT 1;";
$this->_querylog[] = $query;
$zdata = mysql_select_single($query);
foreach ($zdata as $field => $value) $data[$field] = $value;
}
return $data;
}
/**
* Create player.
*
* @param none
* @access public
* @return bool $status
**/
public function create() {
// If player already have an id, the player already exist.
if (is_null($this->_playerdata['id']) && is_string($this->_playerdata['name'])) {
// Confirm player does not exist
$name = format_character_name($this->_playerdata['name']);
$name = validate_name($name);
$name = sanitize($name);
$exist = mysql_select_single("SELECT `id` FROM `players` WHERE `name`='{$name}' LIMIT 1;");
if ($exist !== false) {
$this->errors[] = "A player with the name [{$name}] already exist.";
return false;
}
$config = fullConfig();
if (user_character_exist($_POST['name']) !== false) {
$errors[] = 'Sorry, that character name already exist.';
}
if (!preg_match("/^[a-zA-Z_ ]+$/", $_POST['name'])) {
$errors[] = 'Your name may only contain a-z, A-Z and spaces.';
}
if (strlen($_POST['name']) < $config['minL'] || strlen($_POST['name']) > $config['maxL']) {
$errors[] = 'Your character name must be between ' . $config['minL'] . ' - ' . $config['maxL'] . ' characters long.';
}
// name restriction
$resname = explode(" ", $_POST['name']);
foreach($resname as $res) {
if(in_array(strtolower($res), $config['invalidNameTags'])) {
$errors[] = 'Your username contains a restricted word.';
}
else if(strlen($res) == 1) {
$errors[] = 'Too short words in your name.';
}
}
// Validate vocation id
if (!in_array((int)$_POST['selected_vocation'], $config['available_vocations'])) {
$errors[] = 'Permission Denied. Wrong vocation.';
}
// Validate town id
if (!in_array((int)$_POST['selected_town'], $config['available_towns'])) {
$errors[] = 'Permission Denied. Wrong town.';
}
// Validate gender id
if (!in_array((int)$_POST['selected_gender'], array(0, 1))) {
$errors[] = 'Permission Denied. Wrong gender.';
}
if (vocation_id_to_name($_POST['selected_vocation']) === false) {
$errors[] = 'Failed to recognize that vocation, does it exist?';
}
if (town_id_to_name($_POST['selected_town']) === false) {
$errors[] = 'Failed to recognize that town, does it exist?';
}
if (gender_exist($_POST['selected_gender']) === false) {
$errors[] = 'Failed to recognize that gender, does it exist?';
}
// Char count
$char_count = user_character_list_count($session_user_id);
if ($char_count >= $config['max_characters']) {
$errors[] = 'Your account is not allowed to have more than '. $config['max_characters'] .' characters.';
}
if (validate_ip(getIP()) === false && $config['validate_IP'] === true) {
$errors[] = 'Failed to recognize your IP address. (Not a valid IPv4 address).';
}
echo "create player";
// Make sure all neccesary values are set
//Register
$character_data = array(
'name' => format_character_name($_POST['name']),
'account_id'=> $session_user_id,
'vocation' => $_POST['selected_vocation'],
'town_id' => $_POST['selected_town'],
'sex' => $_POST['selected_gender'],
'lastip' => getIPLong(),
'created' => time()
);
array_walk($character_data, 'array_sanitize');
$cnf = fullConfig();
if ($character_data['sex'] == 1) {
$outfit_type = $cnf['maleOutfitId'];
} else {
$outfit_type = $cnf['femaleOutfitId'];
}
// Create the player
} else {
echo "Player already exist.";
return false;
}
}
}
/*
$this->_file = $file . self::EXT;
$this->setExpiration(config('cache_lifespan'));
$this->_lifespan = $span;
*/

View File

@@ -0,0 +1,13 @@
<?php $filepath = '../../../../'; require_once '../../../module.php';
// Configure module version number
$response['version']['module'] = 1;
UseClass('player');
$player = new Player(1129);
$response['player'] = $player->fetch('name');
$response['test'] = $player->fetch('level');
SendResponse($response);
?>

View File

@@ -0,0 +1,21 @@
<?php require_once '../../module.php';
// Configure module version number
$response['version']['module'] = 1;
// Fetch number of rows
$rows = (isset($_GET['rows']) && (int)$_GET['rows'] > 0) ? (int)getValue($_GET['rows']) : 10;
// Show which configuration is used
$response['config']['rows'] = $rows;
// Fetch top 10 players
$players = mysql_select_multi("SELECT `p`.`name`, `p`.`level`, `p`.`experience`, `p`.`vocation`, `p`.`lastlogin`, `z`.`created` FROM `players` AS `p` INNER JOIN `znote_players` AS `z` ON `p`.`id` = `z`.`player_id` WHERE `p`.`group_id`<'2' ORDER BY `p`.`experience` DESC LIMIT $rows;");
for ($i = 0; $i < count($players); $i++) {
$players[$i]['vocation_name'] = $config['vocations'][$players[$i]['vocation']];
}
$response['data']['players'] = $players;
SendResponse($response);
?>

View File

@@ -0,0 +1,44 @@
<?php require_once '../../module.php';
// Blank/empty module, nice code to start with when making custom stuff.
// Configure module version number
$response['version']['module'] = 1;
/* Do PHP logic, you got access to:
-Znote AAC sql functions:
:mysql_select_single("QUERY");
:mysql_select_multi("QUERY");
:mysql_update("QUERY"), mysql_insert("QUERY"), mysql_delete("QUERY")
-Config values
:etc $config['vocations']
-Cache system
:Sample:
$cache = new Cache('engine/cache/api/ApiModuleName');
if ($cache->hasExpired()) {
$players = mysql_select_multi("SELECT `name`, `level`, `experience` FROM `players` ORDER BY `experience` DESC LIMIT 5;");
$cache->setContent($players);
$cache->save();
} else {
$players = $cache->load();
}
-Functions found in general.php
:When fetching GET or POST from parameters, ALWAYS use getValue($value)
:Etc if you want to fetch character name from url, do it like this:
$playername = getValue($_GET['name']);
if ($playername !== false) {
// $playername either contains player name, or false if failed to fetch name from GET.
}
:getValue is often used in 3 ways: Fetch GET and POST values, or sanitize/secure any value you wish.
:Check ZnoteAAC\engine\function\general.php for full list of available functions.
*/
// Save the results of previous logic to the response
$response['data']['title'] = "The fabulous blank page!";
// Send the response through JSON API
SendResponse($response);
?>

View File

@@ -0,0 +1,15 @@
<?php require_once '../../module.php';
// Configure module version number
$response['version']['module'] = 1;
// Fetch towns
$response['data']['towns'] = $config['towns'];
// Fetch towns available under character creation
foreach ($config['available_towns'] as $id) {
$response['data']['available'][$id] = $response['data']['towns'][$id];
}
SendResponse($response);
?>

View File

@@ -0,0 +1,958 @@
<?php require_once 'engine/init.php';
protect_page();
include 'layout/overall/header.php';
// Convert a seconds integer value into days, hours, minutes and seconds string.
function toDuration($is) {
$duration['day'] = $is / (24 * 60 * 60);
if (($duration['day'] - (int)$duration['day']) > 0)
$duration['hour'] = ($duration['day'] - (int)$duration['day']) * 24;
if (isset($duration['hour'])) {
if (($duration['hour'] - (int)$duration['hour']) > 0)
$duration['minute'] = ($duration['hour'] - (int)$duration['hour']) * 60;
if (isset($duration['minute'])) {
if (($duration['minute'] - (int)$duration['minute']) > 0)
$duration['second'] = ($duration['minute'] - (int)$duration['minute']) * 60;
}
}
$tmp = array();
foreach ($duration as $type => $value) {
if ($value >= 1) {
$pluralType = ((int)$value === 1) ? $type : $type . 's';
if ($type !== 'second') $tmp[] = (int)$value . " $pluralType";
else $tmp[] = (int)$value . " $pluralType";
}
}
return implode(', ', $tmp);
}
?>
<h1>Character auction</h1>
<?php
// Import from config:
$auction = $config['shop_auction'];
$loadOutfits = ($config['show_outfits']['highscores']) ? true : false;
$this_account_id = (int)$session_user_id;
$is_admin = is_admin($user_data);
// If character auction is enabled in config.php
if ($auction['characterAuction']) {
if ($config['ServerEngine'] != 'TFS_10') {
echo "<p>Character shop auction system is currently only available for ServerEngine TFS_10.</p>";
include 'layout/overall/footer.php';
die();
}
if ((int)$auction['storage_account_id'] === (int)$this_account_id) {
echo "<p>The storage account cannot use the character auction.</p>";
include 'layout/overall/footer.php';
die();
}
$step = $auction['step'];
$step_duration = $auction['step_duration'];
$actions = array(
'list', // list all available players in auction
'view', // view a specific player
'create', // select which character to add and initial price
'add', // add character to list
'bid', // Bid or buy a specific player
'refund', // Refund a player you added back to your account
'claim' // Claim a character you won through purchase or bid
);
// Default action is list, but $_GET or $_POST will override it.
$action = 'list';
// Load selected string from actions array based on input, strict whitelist validation
if (isset( $_GET['action']) && in_array( $_GET['action'], $actions)) {
$action = $actions[array_search( $_GET['action'], $actions, true)];
}
if (isset($_POST['action']) && in_array($_POST['action'], $actions)) {
$action = $actions[array_search($_POST['action'], $actions, true)];
}
// Passive check to see if bid period has expired and someone won a deal
$time = time();
$expired_auctions = mysql_select_multi("
SELECT
`id`,
`original_account_id`,
(`bid`+`deposit`) as `points`
FROM `znote_auction_player`
WHERE `sold` = 0
AND `time_end` < {$time}
AND `bidder_account_id` > 0
");
//data_dump($expired_auctions, $this_account_id, "expired_auctions");
if ($expired_auctions !== false) {
$soldIds = array();
foreach ($expired_auctions as $a) {
$soldIds[] = $a['id'];
}
if (!empty($soldIds)) {
mysql_update("
UPDATE `znote_auction_player`
SET `sold` = 1
WHERE `id` IN(".implode(',', $soldIds).")
LIMIT ".COUNT($soldIds).";
");
// Transfer points to seller account
foreach ($expired_auctions as $a) {
mysql_update("
UPDATE `znote_accounts`
SET `points` = (`points`+{$a['points']})
WHERE `account_id` = {$a['original_account_id']};
");
}
}
}
// end passive check
// If we bid or buy a character
// silently continues to list if buy, back to view if bid
if ($action === 'bid') {
//data_dump($_POST, false, "Bid or buying:");
$zaid = (isset($_POST['zaid']) && (int)$_POST['zaid'] > 0) ? (int)$_POST['zaid'] : false;
$price = (isset($_POST['price']) && (int)$_POST['price'] > 0) ? (int)$_POST['price'] : false;
$action = 'list';
if ($zaid !== false && $price !== false) {
// The account of the buyer, if he can afford what he is trying to pay
$account = mysql_select_single("
SELECT
`a`.`id`,
`za`.`points`
FROM `accounts` a
INNER JOIN `znote_accounts` za
ON `a`.`id` = `za`.`account_id`
WHERE `a`.`id`= {$this_account_id}
AND `za`.`points` >= {$price}
LIMIT 1;
");
//data_dump($account, false, "Buyer account:");
// The character to buy, presuming it isn't sold, buyer isn't the owner, buyer can afford it
if ($account !== false) {
$character = mysql_select_single("
SELECT
`za`.`id` AS `zaid`,
`za`.`player_id`,
`za`.`original_account_id`,
`za`.`bidder_account_id`,
`za`.`time_begin`,
`za`.`time_end`,
`za`.`price`,
`za`.`bid`,
`za`.`deposit`,
`za`.`sold`
FROM `znote_auction_player` za
WHERE `za`.`id` = {$zaid}
AND `za`.`sold` = 0
AND `za`.`original_account_id` != {$this_account_id}
AND `za`.`price` <= {$price}
AND `za`.`bid`+{$step} <= {$price}
LIMIT 1
");
//data_dump($character, false, "Character to buy:");
if ($character !== false) {
// If auction already have a previous bidder, refund him his points
if ($character['bid'] > 0 && $character['bidder_account_id'] > 0) {
mysql_update("
UPDATE `znote_accounts`
SET `points` = `points`+{$character['bid']}
WHERE `account_id` = {$character['bidder_account_id']}
LIMIT 1;
");
// If previous bidder is not you, increase bidding period by 1 hour
// (Extending bid war to give bidding competitor a chance to retaliate)
if ((int)$character['bidder_account_id'] !== (int)$account['id']) {
mysql_update("
UPDATE `znote_auction_player`
SET `time_end` = `time_end`+{$step_duration}
WHERE `id` = {$character['zaid']}
LIMIT 1;
");
}
}
// Remove points from buyer
mysql_update("
UPDATE `znote_accounts`
SET `points` = `points`-{$price}
WHERE `account_id` = {$account['id']}
LIMIT 1;
");
// Update auction, and set new bidder data
$time = time();
mysql_update("
UPDATE `znote_auction_player`
SET
`bidder_account_id` = {$account['id']},
`bid` = {$price},
`sold` = CASE WHEN {$time} >= `time_end` THEN 1 ELSE 0 END
WHERE `id` = {$character['zaid']}
LIMIT 1;
");
// If character is sold, give points to seller
if (time() >= $character['time_end']) {
mysql_update("
UPDATE `znote_accounts`
SET `points` = (`points`+{$character['deposit']}+{$price})
WHERE `account_id` = {$character['original_account_id']}
LIMIT 1;
");
} else {
// If character is not sold, this is a bidding war, we want to send user back to view.
$action = 'view';
}
// Note: Transferring character to the new account etc happens later in $action = 'claim'
}
}
}
}
// See a specific character in auction,
// silently fallback to list if he doesn't exist or is already sold
if ($action === 'view') { // View a character in the auction
if (!isset($zaid)) {
$zaid = (isset($_GET['zaid']) && (int)$_GET['zaid'] > 0) ? (int)$_GET['zaid'] : false;
}
if ($zaid !== false) {
// Retrieve basic character information
$character = mysql_select_single("
SELECT
`za`.`id` AS `zaid`,
`za`.`player_id`,
`za`.`original_account_id`,
`za`.`bidder_account_id`,
`za`.`time_begin`,
`za`.`time_end`,
CASE WHEN `za`.`price` > `za`.`bid`
THEN `za`.`price`
ELSE `za`.`bid`+{$step}
END AS `price`,
CASE WHEN `za`.`original_account_id` = {$this_account_id}
THEN 1
ELSE 0
END AS `own`,
CASE WHEN `za`.`original_account_id` = {$this_account_id}
THEN `p`.`name`
ELSE ''
END AS `name`,
CASE WHEN `za`.`original_account_id` = {$this_account_id}
THEN `za`.`bid`
ELSE 0
END AS `bid`,
CASE WHEN `za`.`original_account_id` = {$this_account_id}
THEN `za`.`deposit`
ELSE 0
END AS `deposit`,
`p`.`vocation`,
`p`.`level`,
`p`.`balance`,
`p`.`lookbody` AS `body`,
`p`.`lookfeet` AS `feet`,
`p`.`lookhead` AS `head`,
`p`.`looklegs` AS `legs`,
`p`.`looktype` AS `type`,
`p`.`lookaddons` AS `addons`,
`p`.`maglevel` AS `magic`,
`p`.`skill_fist` AS `fist`,
`p`.`skill_club` AS `club`,
`p`.`skill_sword` AS `sword`,
`p`.`skill_axe` AS `axe`,
`p`.`skill_dist` AS `dist`,
`p`.`skill_shielding` AS `shielding`,
`p`.`skill_fishing` AS `fishing`
FROM `znote_auction_player` za
INNER JOIN `players` p
ON `za`.`player_id` = `p`.`id`
WHERE `za`.`id` = {$zaid}
AND `za`.`sold` = 0
LIMIT 1;
");
//data_dump($character, false, "Character info");
if (is_array($character) && !empty($character)) {
// If the end of the bid is in the future, the bid is currently ongoing
$bidding_period = ((int)$character['time_end']+1 > time()) ? true : false;
$player_items = mysql_select_multi("
SELECT `itemtype`, SUM(`count`) AS `count`
FROM `player_items`
WHERE `player_id` = {$character['player_id']}
GROUP BY `itemtype`
ORDER BY MIN(`pid`) ASC
");
$depot_items = mysql_select_multi("
SELECT `itemtype`, SUM(`count`) AS `count`
FROM `player_depotitems`
WHERE `player_id` = {$character['player_id']}
GROUP BY `itemtype`
ORDER BY MIN(`pid`) ASC
");
$account = mysql_select_single("
SELECT `points`
FROM `znote_accounts`
WHERE `account_id` = {$this_account_id}
AND `points` >= {$character['price']}
LIMIT 1;
");
?>
<p>Detailed character information. <a href="/auctionChar.php?action=list">Go back to list.</a></p>
<!-- Basic info -->
<table class="auction_char">
<tr class="yellow">
<td>Level</td>
<td>Vocation</td>
<?php if ($loadOutfits): ?>
<td>Image</td>
<?php endif; ?>
<td>Bank</td>
<td>Price</td>
</tr>
<tr>
<td><?php echo $character['level']; ?></td>
<td><?php echo vocation_id_to_name($character['vocation']); ?></td>
<?php if ($loadOutfits): ?>
<td class="outfitColumn">
<img src="<?php echo $config['show_outfits']['imageServer']; ?>?id=<?php echo $character['type']; ?>&addons=<?php echo $character['addons']; ?>&head=<?php echo $character['head']; ?>&body=<?php echo $character['body']; ?>&legs=<?php echo $character['legs']; ?>&feet=<?php echo $character['feet']; ?>" alt="img">
</td>
<?php endif; ?>
<td><?php echo $character['balance']; ?></td>
<td><?php echo $character['price']; ?> points</td>
</tr>
<?php if ($bidding_period): ?>
<tr>
<td colspan="<?php echo ($loadOutfits) ? 5 : 4; ?>">
<p><strong>Remaining bid period:</strong> <?php echo toDuration((int)$character['time_end']-time()); ?>.</p>
</td>
</tr>
<?php endif; ?>
</table>
<!-- Bid on character -->
<?php
if ($character['own'] == 0) {
if (is_array($account) && !empty($account)): ?>
<p>You have <strong><?php echo $account['points']; ?></strong> shop points remaining.</p>
<?php if ((int)$character['bidder_account_id'] === $this_account_id): ?>
<p><strong>So far so good!</strong>
<br>You currently have the highest bid at: <?php echo (int)$character['price']-$step; ?>
</p>
<p>If nobody bids higher than you, this character will be yours in:
<br><?php echo toDuration((int)$character['time_end']-time()); ?>.
</p>
<?php endif; ?>
<form action="/auctionChar.php" method="POST">
<input type="hidden" name="action" value="bid">
<input type="hidden" name="zaid" value="<?php echo $character['zaid']; ?>">
<input type="number" name="price" min="<?php echo $character['price']; ?>" max="<?php echo $account['points']; ?>" step="5" value="<?php echo $character['price']; ?>" <?php if (!$bidding_period) echo 'disabled'; ?>>
<?php if (!$bidding_period): /* Because above input is disabled */ ?>
<input type="hidden" name="price" value="<?php echo $character['price']; ?>">
<?php endif; ?>
<input type="submit" value="<?php echo ($bidding_period) ? 'Bid' : 'Buy'; ?>">
</form>
<?php else: ?>
<?php if ((int)$character['bidder_account_id'] === $this_account_id): ?>
<p><strong>So far so good!</strong>
<br>You currently have the highest bid at: <?php echo (int)$character['price']-$step; ?>
</p>
<p>If nobody bids higher than you, this character will be yours in:
<br><?php echo toDuration((int)$character['time_end']-time()); ?>.
</p>
<?php else: ?>
<p>You cannot afford to buy this character.</p>
<?php endif; ?>
<?php endif;
} else {
?>
<p><strong>You are the seller of this character.</strong>
<br><strong>Name:</strong> <a href="/characterprofile.php?name=<?php echo $character['name']; ?>"><?php echo $character['name']; ?></a>
<br><strong>Price:</strong> <?php echo $character['price']; ?>
<br><strong>Bid:</strong> <?php echo $character['bid']; ?>
<br><strong>Deposit:</strong> <?php echo $character['deposit']; ?>
<?php if (!$bidding_period): ?>
<p>The bidding period has ended, you can wait until someone decides to instantly buy it, or you can reclaim your character to your account.</p>
<form action="/auctionChar.php" method="POST">
<input type="hidden" name="action" value="refund">
<input type="hidden" name="zaid" value="<?php echo $character['zaid']; ?>">
<input type="submit" value="Reclaim character back to your account">
</form>
<?php else: ?>
<p>The bidding period will last for <?php echo toDuration($character['time_end']-time()); ?>. After this period, you can reclaim your character if nobody has bid on it.</p>
<?php endif; ?>
</p>
<?php
}
?>
<!-- SKILLS -->
<table class="auction_skills">
<tr class="yellow"><td colspan="4">Character skills:</td></tr>
<tr><td>magic</td><td><?php echo $character['magic']; ?></td></tr>
<tr><td>fist</td><td><?php echo $character['fist']; ?></td></tr>
<tr><td>club</td><td><?php echo $character['club']; ?></td></tr>
<tr><td>sword</td><td><?php echo $character['sword']; ?></td></tr>
<tr><td>axe</td><td><?php echo $character['axe']; ?></td></tr>
<tr><td>dist</td><td><?php echo $character['dist']; ?></td></tr>
<tr><td>shielding</td><td><?php echo $character['shielding']; ?></td></tr>
<tr><td>fishing</td><td><?php echo $character['fishing']; ?></td></tr>
</table>
<?php
$server = $config['shop']['imageServer'];
$imageType = $config['shop']['imageType'];
$items = getItemList();
?>
<!-- Player items -->
<?php if (is_array($player_items) && !empty($player_items)): ?>
<table>
<tr class="yellow">
<td colspan="3">Player items:</td>
</tr>
<tr class="yellow">
<td>Image</td>
<td>Item</td>
<td>Count</td>
</tr>
<?php foreach($player_items as $item): ?>
<tr>
<td><img src="<?php echo "http://".$server."/".$item['itemtype'].".".$imageType; ?>" alt="Item Image"></td>
<td><a href="/market.php?compare=<?php echo $item['itemtype']; ?>" target="_BLANK"><?php echo (isset($items[$item['itemtype']])) ? $items[$item['itemtype']] : $item['itemtype']; ?></a></td>
<td><?php echo $item['count']; ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php endif; ?>
<!-- Depot items -->
<?php if (is_array($depot_items) && !empty($depot_items)): ?>
<table>
<tr class="yellow">
<td colspan="3">Depot items:</td>
</tr>
<tr class="yellow">
<td>Image</td>
<td>Item</td>
<td>Count</td>
</tr>
<?php foreach($depot_items as $item): ?>
<tr>
<td><img src="<?php echo "http://".$server."/".$item['itemtype'].".".$imageType; ?>" alt="Item Image"></td>
<td><a href="/market.php?compare=<?php echo $item['itemtype']; ?>" target="_BLANK"><?php echo (isset($items[$item['itemtype']])) ? $items[$item['itemtype']] : $item['itemtype']; ?></a></td>
<td><?php echo $item['count']; ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php endif;
} else {
$action = 'list';
}
}
}
// If we are adding a character to the list
// silently continues to list
if ($action === 'add') {
$pid = (isset($_POST['pid']) && (int)$_POST['pid'] > 0) ? (int)$_POST['pid'] : false;
$cost = (isset($_POST['cost']) && (int)$_POST['cost'] > 0) ? (int)$_POST['cost'] : false;
$deposit = (int)$cost * ($auction['deposit'] / 100);
$password = SHA1($_POST['password']);
// Verify values
$status = false;
$account = false;
if ($pid > 0 && $cost >= $auction['lowestPrice']) {
$account = mysql_select_single("
SELECT `a`.`id`, `a`.`password`, `za`.`points`
FROM `accounts` a
INNER JOIN `znote_accounts` za
ON `a`.`id` = `za`.`account_id`
WHERE `a`.`id`= {$this_account_id}
AND `a`.`password`='{$password}'
AND `za`.`points` >= {$deposit}
LIMIT 1
;");
if (isset($account['password']) && $account['password'] === $password) {
// Check if player exist, is offline and not already in auction
// And is not a tutor or a GM+.
$player = mysql_select_single("
SELECT `p`.`id`, `p`.`name`,
CASE
WHEN `po`.`player_id` IS NULL
THEN 0
ELSE 1
END AS `online`,
CASE
WHEN `za`.`player_id` IS NULL
THEN 0
ELSE 1
END AS `alreadyInAuction`
FROM `players` p
LEFT JOIN `players_online` po
ON `p`.`id` = `po`.`player_id`
LEFT JOIN `znote_auction_player` za
ON `p`.`id` = `za`.`player_id`
AND `p`.`account_id` = `za`.`original_account_id`
AND `za`.`claimed` = 0
WHERE `p`.`id` = {$pid}
AND `p`.`account_id` = {$this_account_id}
AND `p`.`group_id` = 1
LIMIT 1
;");
// Verify storage account ID exist
$storage_account = mysql_select_single("
SELECT `id`
FROM `accounts`
WHERE `id`={$auction['storage_account_id']}
LIMIT 1;
");
if ($storage_account === false) {
data_dump($auction, false, "Configured storage_account_id in config.php does not exist!");
} else {
if (isset($player['online']) && $player['online'] == 0) {
if (isset($player['alreadyInAuction']) && $player['alreadyInAuction'] == 0) {
$status = true;
}
}
}
}
}
if ($status) {
$time_begin = time();
$time_end = $time_begin + ($auction['biddingDuration']);
// Insert row to znote_auction_player
mysql_insert("
INSERT INTO `znote_auction_player` (
`player_id`,
`original_account_id`,
`bidder_account_id`,
`time_begin`,
`time_end`,
`price`,
`bid`,
`deposit`,
`sold`,
`claimed`
) VALUES (
{$pid},
{$this_account_id},
0,
{$time_begin},
{$time_end},
{$cost},
0,
{$deposit},
0,
0
);
");
// Move player to storage account
mysql_update("
UPDATE `players`
SET `account_id` = {$auction['storage_account_id']}
WHERE `id` = {$pid}
LIMIT 1;
");
// Hide character from public character list (in pidprofile.php)
mysql_update("
UPDATE `znote_players`
SET `hide_char` = 1
WHERE `player_id` = {$pid}
LIMIT 1;
");
// Remove deposit from account
$afterDeposit = $account['points'] - $deposit;
mysql_update("
UPDATE `znote_accounts`
SET `points` = {$afterDeposit}
WHERE `account_id` = {$account['id']}
LIMIT 1;
");
}
$action = 'list';
}
// If we are refunding a player back to its original owner
// silently continues to list
if ($action === 'refund') {
$zaid = (isset($_POST['zaid']) && (int)$_POST['zaid'] > 0) ? (int)$_POST['zaid'] : false;
//data_dump($_POST, false, "POST");
if ($zaid !== false) {
$time = time();
// If original account is the one trying to get it back,
// and bidding period is over,
// and its not labeled as sold
// and nobody has bid on it
$character = mysql_select_single("
SELECT `player_id`
FROM `znote_auction_player`
WHERE `id`= {$zaid}
AND `original_account_id` = {$this_account_id}
AND `time_end` <= {$time}
AND `bidder_account_id` = 0
AND `bid` = 0
AND `sold` = 0
LIMIT 1
");
//data_dump($character, false, "Character");
if ($character !== false) {
// Move character to buyer account and give it a new name
mysql_update("
UPDATE `players`
SET `account_id` = {$this_account_id}
WHERE `id` = {$character['player_id']}
LIMIT 1;
");
// Set label to sold
mysql_update("
UPDATE `znote_auction_player`
SET `sold` = 1
WHERE `id`= {$zaid}
LIMIT 1;
");
// Show character in public character list (in characterprofile.php)
mysql_update("
UPDATE `znote_players`
SET `hide_char` = 0
WHERE `player_id` = {$character['player_id']}
LIMIT 1;
");
}
}
$action = 'list';
}
// If we are claiming a character
// If validation fails then explain why, but then head over to list regardless of status
if ($action === 'claim') {
$zaid = (isset($_POST['zaid']) && (int)$_POST['zaid'] > 0) ? (int)$_POST['zaid'] : false;
$name = (isset($_POST['name']) && !empty($_POST['name'])) ? getValue($_POST['name']) : false;
$errors = array();
//data_dump($_POST, $name, "Post data:");
if ($zaid === false) {
$errors[] = 'We are unable to find this auction order.';
}
if ((int)$auction['storage_account_id'] === $this_account_id) {
$errors[] = 'Silly you! You cannot claim characters with the storage account configured in <br>$config[\'shop_auction\'][\'storage_account_id\']<br>because you already have those characters in your account! :P';
if ($is_admin) {
$errors[] = "ADMIN: The storage account in config.php should not be the same as the admin account.";
}
}
if ($name === false) {
$errors[] = 'Please give the character a name.';
} else {
// begin name validation
$name = validate_name($name);
if (user_character_exist($name) !== false) {
$errors[] = 'Sorry, that character name already exist.';
}
if (!preg_match("/^[a-zA-Z_ ]+$/", $name)) {
$errors[] = 'Your name may only contain a-z, A-Z and spaces.';
}
if (strlen($name) < $config['minL'] || strlen($name) > $config['maxL']) {
$errors[] = 'Your character name must be between ' . $config['minL'] . ' - ' . $config['maxL'] . ' characters long.';
}
// name restriction
$resname = explode(" ", $name);
foreach($resname as $res) {
if(in_array(strtolower($res), $config['invalidNameTags'])) {
$errors[] = 'Your username contains a restricted word.';
}
else if(strlen($res) == 1) {
$errors[] = 'Too short words in your name.';
}
}
$name = format_character_name($name);
// end name validation
if (empty($errors)) {
// Make sure you have access to claim this zaid character.
// And that you haven't already claimed it.
// And that the character isn't online...
$character = mysql_select_single("
SELECT
`za`.`id` AS `zaid`,
`za`.`player_id`,
`p`.`account_id`
FROM `znote_auction_player` za
INNER JOIN `players` p
ON `za`.`player_id` = `p`.`id`
LEFT JOIN `players_online` po
ON `p`.`id` = `po`.`player_id`
WHERE `za`.`id` = {$zaid}
AND `za`.`sold` = 1
AND `p`.`account_id` != {$this_account_id}
AND `za`.`bidder_account_id` = {$this_account_id}
AND `po`.`player_id` IS NULL
");
//data_dump($character, false, "Character");
if ($character !== false) {
// Set character to claimed
mysql_update("
UPDATE `znote_auction_player`
SET `claimed`='1'
WHERE `id` = {$character['zaid']}
");
// Move character to buyer account and give it a new name
mysql_update("
UPDATE `players`
SET `name` = '{$name}',
`account_id` = {$this_account_id}
WHERE `id` = {$character['player_id']}
LIMIT 1;
");
// Show character in public character list (in characterprofile.php)
mysql_update("
UPDATE `znote_players`
SET `hide_char` = 0
WHERE `player_id` = {$character['player_id']}
LIMIT 1;
");
// Remove character from other players VIP lists
mysql_delete("
DELETE FROM `account_viplist`
WHERE `player_id` = {$character['player_id']}
");
// Remove the character deathlist
mysql_delete("
DELETE FROM `player_deaths`
WHERE `player_id` = {$character['player_id']}
");
} else {
$errors[] = "You either don't have access to claim this character, or you have already claimed it, or this character isn't sold yet, or we were unable to find this auction order.";
if ($is_admin) {
$errors[] = "ADMIN: ... Or character is online.";
}
}
}
}
if (!empty($errors)) {
//data_dump($errors, false, "Errors:");
?>
<table class="auction_error">
<tr class="yellow">
<td>#</td>
<td>Issues occurred while claiming your name</td>
</tr>
<?php foreach($errors as $i => $error): ?>
<tr>
<td><?php echo $i+1; ?></td>
<td><?php echo $error; ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php
}
$action = 'list';
}
// List characters currently in the auction
if ($action === 'list') {
// If this account have successfully bought or won an auction
// Intercept the list action and let the user do claim actions
$pending = mysql_select_multi("
SELECT
`za`.`id` AS `zaid`,
CASE WHEN `za`.`price` > `za`.`bid`
THEN `za`.`price`
ELSE `za`.`bid`
END AS `price`,
`za`.`time_begin`,
`za`.`time_end`,
`p`.`vocation`,
`p`.`level`,
`p`.`lookbody` AS `body`,
`p`.`lookfeet` AS `feet`,
`p`.`lookhead` AS `head`,
`p`.`looklegs` AS `legs`,
`p`.`looktype` AS `type`,
`p`.`lookaddons` AS `addons`
FROM `znote_auction_player` za
INNER JOIN `players` p
ON `za`.`player_id` = `p`.`id`
WHERE `p`.`account_id` = {$auction['storage_account_id']}
AND `za`.`claimed` = 0
AND `za`.`sold` = 1
AND `za`.`bidder_account_id` = {$this_account_id}
ORDER BY `p`.`level` desc
");
//data_dump($pending, false, "Pending characters:");
if ($pending !== false) {
?>
<h2>Congratulations!</h2>
<p>You have <?php echo (COUNT($pending) > 1) ? 'characters' : 'a character'; ?> ready to claim!</p>
<?php foreach($pending as $character): ?>
<table class="auction_char">
<tr class="yellow">
<td>Level</td>
<td>Vocation</td>
<td>Details</td>
<td>Price</td>
</tr>
<tr>
<td><?php echo $character['level']; ?></td>
<td><?php echo vocation_id_to_name($character['vocation']); ?></td>
<td><a href="/auctionChar.php?action=view&zaid=<?php echo $character['zaid']; ?>">VIEW</a></td>
<td><?php echo $character['price']; ?></td>
</tr>
<tr>
<?php if ($loadOutfits): ?>
<td class="outfitColumn">
<img src="<?php echo $config['show_outfits']['imageServer']; ?>?id=<?php echo $character['type']; ?>&addons=<?php echo $character['addons']; ?>&head=<?php echo $character['head']; ?>&body=<?php echo $character['body']; ?>&legs=<?php echo $character['legs']; ?>&feet=<?php echo $character['feet']; ?>" alt="img">
</td>
<?php endif; ?>
<td colspan="3">
<p>Hello master, what should my new name be?</p>
<form action="/auctionChar.php" method="POST">
<input type="hidden" name="action" value="claim">
<input type="hidden" name="zaid" value="<?php echo $character['zaid']; ?>">
<input type="text" name="name">
<input type="submit" value="Claim character">
</form>
</td>
</tr>
</table>
<?php endforeach; ?>
<h2>Ongoing auctions:</h2>
<?php
}
// Show the list
$characters = mysql_select_multi("
SELECT
`za`.`id` AS `zaid`,
CASE WHEN `za`.`price` > `za`.`bid`
THEN `za`.`price`
ELSE `za`.`bid`+{$step}
END AS `price`,
`za`.`time_begin`,
`za`.`time_end`,
`p`.`vocation`,
`p`.`level`,
`p`.`lookbody` AS `body`,
`p`.`lookfeet` AS `feet`,
`p`.`lookhead` AS `head`,
`p`.`looklegs` AS `legs`,
`p`.`looktype` AS `type`,
`p`.`lookaddons` AS `addons`
FROM `znote_auction_player` za
INNER JOIN `players` p
ON `za`.`player_id` = `p`.`id`
WHERE `p`.`account_id` = {$auction['storage_account_id']}
AND `za`.`sold` = 0
ORDER BY `p`.`level` desc;
");
//data_dump($characters, false, "List characters");
if ($is_admin) {
?>
<p>Admin: <a href="/admin_auction.php">Character auction history</a></p>
<?php
}
if (is_array($characters) && !empty($characters)):
?>
<table class="auction_char">
<tr class="yellow">
<td>Level</td>
<td>Vocation</td>
<?php if ($loadOutfits): ?>
<td>Image</td>
<?php endif; ?>
<td>Details</td>
<td>Price</td>
<td>Added</td>
<td>Type</td>
</tr>
<?php foreach($characters as $character): ?>
<tr>
<td><?php echo $character['level']; ?></td>
<td><?php echo vocation_id_to_name($character['vocation']); ?></td>
<?php if ($loadOutfits): ?>
<td class="outfitColumn">
<img src="<?php echo $config['show_outfits']['imageServer']; ?>?id=<?php echo $character['type']; ?>&addons=<?php echo $character['addons']; ?>&head=<?php echo $character['head']; ?>&body=<?php echo $character['body']; ?>&legs=<?php echo $character['legs']; ?>&feet=<?php echo $character['feet']; ?>" alt="img">
</td>
<?php endif; ?>
<td><a href="/auctionChar.php?action=view&zaid=<?php echo $character['zaid']; ?>">VIEW</a></td>
<td><?php echo $character['price']; ?></td>
<td><?php
$ended = (time() > $character['time_end']) ? true : false;
echo getClock($character['time_begin'], true);
?>
</td>
<td><?php echo ($ended) ? 'Instant' : 'Bidding<br>('.toDuration(($character['time_end'] - time())).')'; ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php
endif;
?>
<p><a href="/auctionChar.php?action=create">Add a character to the auction</a>.</p>
<?php
} elseif ($action === 'create') { // Add player to auction view
$minToCreate = (int)ceil(($auction['lowestPrice'] / 100) * $auction['deposit']);
$own_characters = mysql_select_multi("
SELECT
`p`.`id`,
`p`.`name`,
`p`.`level`,
`p`.`vocation`,
`a`.`points`
FROM `players` p
INNER JOIN `znote_accounts` a
ON `p`.`account_id` = `a`.`account_id`
LEFT JOIN `znote_auction_player` za
ON `p`.`id` = `za`.`player_id`
AND `p`.`account_id` = `za`.`original_account_id`
AND `za`.`claimed` = 0
LEFT JOIN `players_online` po
ON `p`.`id` = `po`.`player_id`
WHERE `p`.`account_id`={$this_account_id}
AND `za`.`player_id` IS NULL
AND `po`.`player_id` IS NULL
AND `p`.`level` >= {$auction['lowestLevel']}
AND `a`.`points` >= $minToCreate
;");
//data_dump($own_characters, false, "own_chars");
if (is_array($own_characters) && !empty($own_characters)) {
$max = ($own_characters[0]['points'] / $auction['deposit']) * 100;
?>
<p><a href="/auctionChar.php?action=list">Go back to list.</a></p>
<form action="/auctionChar.php" method="POST">
<input type="hidden" name="action" value="add">
<p>Character: (Must be offline)</p>
<select name="pid">
<?php if(is_array($own_characters) && !empty($own_characters))
foreach($own_characters as $char): ?>
<option value="<?php echo $char['id']; ?>">
<?php echo "Level: ", $char['level'], " ", vocation_id_to_name($char['vocation']), ": ", $char['name']; ?>
</option>
<?php endforeach; ?>
</select>
<p><strong>Shop points:</strong>
<br>Your current points: <?php echo $own_characters[0]['points']; ?>
<br>Minimum: <?php echo $auction['lowestPrice']; ?>
<br>deposit: <?php echo $auction['deposit']; ?>%
<br>Your maximum: <?php echo $max; ?>
</p>
<p><strong>Deposit information:</strong>
<br>To ensure you as the seller is a legitimate account, and to encourage fair prices you have to temporarily invest <?php echo $auction['deposit']; ?>% of the selling price as a deposit.
</p>
<p>Once the auction has completed, the deposit fee will be refunded back to your account.</p>
<p>If you wish to reclaim your character, you can do it after the bidding period if nobody has placed an offer on it. But if you do this you will not get the deposit back. It is therefore advisable that you create a good and appealing offer to our community.</p>
<p>Sell price:</p>
<input type="number" name="cost" min="<?php echo $auction['lowestPrice']; ?>" max="<?php echo $max; ?>" step="5" placeholder="<?php echo $auction['lowestPrice']; ?> - <?php echo $max; ?>">
<br>
<p>Verify with your password:</p>
<input type="password" name="password">
<br>
<input type="submit" value="Sell character">
</form>
<?php
} else {
?>
<p><a href="/auctionChar.php?action=list">Go back to list.</a></p>
<p>Your account does not follow the required rules to sell characters.
<br>1. Minimum level: <?php echo $auction['lowestLevel']; ?>
<br>2. Minimum already earned shop points: <?php echo $minToCreate; ?>
<br>3. Eligible characters must be offline.
</p>
<?php
}
}
} else echo "<p>Character shop auctioning system is disabled.</p>";
include 'layout/overall/footer.php'; ?>

6
app/ZnoteAAC/blank.php Normal file
View File

@@ -0,0 +1,6 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php'; ?>
<h1>Blank</h1>
<p>This is a blank sample page.</p>
<?php include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,97 @@
<?php require_once 'engine/init.php';
protect_page();
include 'layout/overall/header.php';
// Import from config:
$pagseguro = $config['pagseguro'];
$paypal = $config['paypal'];
$prices = $config['paypal_prices'];
if ($paypal['enabled']) {
?>
<h1>Buy Points</h1>
<h2>Buy points using Paypal:</h2>
<table id="buypointsTable" class="table table-striped table-hover">
<tr class="yellow">
<th>Price:</th>
<th>Points:</th>
<?php if ($paypal['showBonus']) { ?>
<th>Bonus:</th>
<?php } ?>
<th>Action:</th>
</tr>
<?php
foreach ($prices as $price => $points) {
echo '<tr class="special">';
echo '<td>'. $price .'('. $paypal['currency'] .')</td>';
echo '<td>'. $points .'</td>';
if ($paypal['showBonus']) echo '<td>'. calculate_discount(($paypal['points_per_currency'] * $price), $points) .' bonus</td>';
?>
<td>
<form action="https://www.paypal.com/cgi-bin/webscr" method="POST">
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" name="business" value="<?php echo hhb_tohtml($paypal['email']); ?>">
<input type="hidden" name="item_name" value="<?php echo $points .' shop points on '. hhb_tohtml($config['site_title']); ?>">
<input type="hidden" name="item_number" value="1">
<input type="hidden" name="amount" value="<?php echo $price; ?>">
<input type="hidden" name="no_shipping" value="1">
<input type="hidden" name="no_note" value="1">
<input type="hidden" name="currency_code" value="<?php echo hhb_tohtml($paypal['currency']); ?>">
<input type="hidden" name="lc" value="GB">
<input type="hidden" name="bn" value="PP-BuyNowBF">
<input type="hidden" name="return" value="<?php echo hhb_tohtml($paypal['success']); ?>">
<input type="hidden" name="cancel_return" value="<?php echo hhb_tohtml($paypal['failed']); ?>">
<input type="hidden" name="rm" value="2">
<input type="hidden" name="notify_url" value="<?php echo hhb_tohtml($paypal['ipn']); ?>" />
<input type="hidden" name="custom" value="<?php echo (int)$session_user_id; ?>">
<input type="submit" value=" PURCHASE ">
</form>
</td>
<?php
echo '</tr>';
}
?>
</table>
<?php } ?>
<?php
if ($config['pagseguro']['enabled'] == true) {
?>
<h2>Buy points using Pagseguro:</h2>
<form target="pagseguro" action="https://<?=hhb_tohtml($pagseguro['urls']['www'])?>/checkout/checkout.jhtml" method="post">
<input type="hidden" name="email_cobranca" value="<?=hhb_tohtml($pagseguro['email'])?>">
<input type="hidden" name="tipo" value="CP">
<input type="hidden" name="moeda" value="<?=hhb_tohtml($pagseguro['currency'])?>">
<input type="hidden" name="ref_transacao" value="<?php echo (int)$session_user_id; ?>">
<input type="hidden" name="item_id_1" value="1">
<input type="hidden" name="item_descr_1" value="<?=hhb_tohtml($pagseguro['product_name'])?>">
<input type="number" name="item_quant_1" min="1" step="4" value="1">
<input type="hidden" name="item_peso_1" value="0">
<input type="hidden" name="item_valor_1" value="<?=$pagseguro['price']?>">
<input type="submit" value=" PURCHASE ">
</form>
<br>
<?php } ?>
<?php
if ($config['paygol']['enabled'] == true) {
?>
<!-- PayGol Form using Post method -->
<h2>Buy points using Paygol:</h2>
<?php $paygol = $config['paygol']; ?>
<p><?php echo $paygol['price'] ." ". hhb_tohtml($paygol['currency']) ."~ for ". $paygol['points'] ." points:"; ?></p>
<form name="pg_frm" method="post" action="http://www.paygol.com/micropayment/paynow" >
<input type="hidden" name="pg_serviceid" value="<?php echo hhb_tohtml($paygol['serviceID']); ?>">
<input type="hidden" name="pg_currency" value="<?php echo hhb_tohtml($paygol['currency']); ?>">
<input type="hidden" name="pg_name" value="<?php echo hhb_tohtml($paygol['name']); ?>">
<input type="hidden" name="pg_custom" value="<?php echo hhb_tohtml($session_user_id); ?>">
<input type="hidden" name="pg_price" value="<?php echo $paygol['price']; ?>">
<input type="hidden" name="pg_return_url" value="<?php echo hhb_tohtml($paygol['returnURL']); ?>">
<input type="hidden" name="pg_cancel_url" value="<?php echo hhb_tohtml($paygol['cancelURL']); ?>">
<input type="image" name="pg_button" src="https://www.paygol.com/micropayment/img/buttons/150/black_en_pbm.png" border="0" alt="Make payments with PayGol: the easiest way!" title="Make payments with PayGol: the easiest way!">
</form>
<?php }
if (!$config['paypal']['enabled'] && !$config['paygol']['enabled'] && !$config['pagseguro']['enabled']) echo '<h1>Buy Points system disabled.</h1><p>Sorry, this functionality is disabled.</p>';
include 'layout/overall/footer.php'; ?>

115
app/ZnoteAAC/changelog.php Normal file
View File

@@ -0,0 +1,115 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
$updateCache = false;
if (user_logged_in()) {
if (is_admin($user_data)) {
// variables
$status = true;
if (isset($_POST['changelogId'])) $changelogId = (int)$_POST['changelogId'];
else $status = false;
if (isset($_POST['changelogText'])) $changelogText = getValue($_POST['changelogText']);
else $status = false;
if (isset($_POST['action'])) $action = (int)$_POST['action'];
else $action = 0;
// POST delete
if (isset($_POST['delete'])) {
$delete = isset($_POST['delete']) ? (int)$_POST['delete'] : 0;
if ($delete && $action == 1) {
mysql_delete("DELETE FROM `znote_changelog` WHERE `id`='$delete' LIMIT 1;");
echo "<h2>Changelog message deleted!</h2>";
$updateCache = true;
}
} else {
if ($status) {
// POST update
if ($changelogId > 0) {
mysql_update("UPDATE `znote_changelog` SET `text`='$changelogText' WHERE `id`='$changelogId' LIMIT 1;");
echo "<h2>Changelog message updated!</h2>";
$updateCache = true;
} else {
// POST create
$time = time();
mysql_insert("INSERT INTO `znote_changelog` (`text`, `time`, `report_id`, `status`) VALUES ('$changelogText', '$time', '0', '35');");
echo "<h2>Changelog message created!</h2>";
$updateCache = true;
}
}
}
if ($action === 2) {
$old = mysql_select_single("SELECT `text` FROM `znote_changelog` WHERE `id`='$changelogId' LIMIT 1;");
}
// HTML to create or update
?>
<h3>Add or update changelog</h3>
<form action="" method="POST">
<input name="changelogId" type="hidden" value="<?php echo ($action === 2) ? $changelogId : 0; ?>">
<textarea rows="7" cols="40" maxlength="254" name="changelogText"><?php echo ($action === 2) ? $old['text'] : ''; ?></textarea><br>
<input type="submit" value="Add or update changelog">
</form>
<?php
}
}
?>
<h1>Changelog</h1>
<?php
$cache = new Cache('engine/cache/changelog');
$cache->useMemory(false);
if ($updateCache === true) {
$changelogs = mysql_select_multi("SELECT `id`, `text`, `time`, `report_id`, `status` FROM `znote_changelog` ORDER BY `id` DESC;");
$cache->setContent($changelogs);
$cache->save();
} else {
$changelogs = $cache->load();
}
if (isset($changelogs) && !empty($changelogs) && $changelogs !== false) {
?>
<table id="changelogTable">
<tr class="yellow">
<td>Changelogs</td>
<?php
if (user_logged_in())
if (is_admin($user_data)) {
echo "<td>Delete</td><td>Update</td>";
}
?>
</tr>
<?php
foreach ($changelogs as $changelog) {
?>
<tr>
<td><b><?php echo getClock((isset($changelog['time'])) ? $changelog['time'] : 0, true, true); ?></b><br><?php echo $changelog['text']; ?></td>
<?php
if (user_logged_in())
if (is_admin($user_data)) {
?>
<td>
<form action="" method="POST">
<input name="delete" type="hidden" value="<?php echo $changelog['id']; ?>">
<input name="action" type="hidden" value="1">
<input type="submit" value="DELETE">
</form>
</td>
<td>
<form action="" method="POST">
<input name="changelogId" type="hidden" value="<?php echo $changelog['id']; ?>">
<input name="action" type="hidden" value="2">
<input type="submit" value="UPDATE">
</form>
</td>
<?php
}
?>
</tr>
<?php
}
?>
</table>
<?php
} else {
?>
<h2>Currently no change logs submitted.</h2>
<?php
}
include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,91 @@
<?php require_once 'engine/init.php';
protect_page();
if (empty($_POST) === false) {
/* Token used for cross site scripting security */
if (!Token::isValid($_POST['token'])) {
$errors[] = 'Token is invalid.';
}
$required_fields = array('current_password', 'new_password', 'new_password_again');
foreach($_POST as $key=>$value) {
if (empty($value) && in_array($key, $required_fields) === true) {
$errors[] = 'You need to fill in all fields.';
break 1;
}
}
$pass_data = user_data($session_user_id, 'password');
//$pass_data['password'];
// $_POST['']
// .3 compatibility
if ($config['ServerEngine'] == 'TFS_03' && $config['salt'] === true) {
$salt = user_data($session_user_id, 'salt');
}
if (sha1($_POST['current_password']) === $pass_data['password'] || $config['ServerEngine'] == 'TFS_03' && $config['salt'] === true && sha1($salt['salt'].$_POST['current_password']) === $pass_data['password']) {
if (trim($_POST['new_password']) !== trim($_POST['new_password_again'])) {
$errors[] = 'Your new passwords do not match.';
} else if (strlen($_POST['new_password']) < 6) {
$errors[] = 'Your new passwords must be at least 6 characters.';
} else if (strlen($_POST['new_password']) > 100) {
$errors[] = 'Your new passwords must be less than 100 characters.';
}
} else {
$errors[] = 'Your current password is incorrect.';
}
}
include 'layout/overall/header.php'; ?>
<h1>Change Password:</h1>
<?php
if (isset($_GET['success']) && empty($_GET['success'])) {
echo 'Your password has been changed.<br>You will need to login again with the new password.';
session_destroy();
header("refresh:2;url=index.php");
exit();
} else {
if (empty($_POST) === false && empty($errors) === true) {
//Posted the form without errors
if ($config['ServerEngine'] == 'TFS_02' || $config['ServerEngine'] == 'TFS_10' || $config['ServerEngine'] == 'OTHIRE') {
user_change_password($session_user_id, $_POST['new_password']);
} else if ($config['ServerEngine'] == 'TFS_03') {
user_change_password03($session_user_id, $_POST['new_password']);
}
header('Location: changepassword.php?success');
} else if (empty($errors) === false){
echo '<font color="red"><b>';
echo output_errors($errors);
echo '</b></font>';
}
?>
<form action="" method="post">
<ul>
<li>
Current password:<br>
<input type="password" name="current_password">
</li>
<li>
New password:<br>
<input type="password" name="new_password">
</li>
<li>
New password again:<br>
<input type="password" name="new_password_again">
</li>
<?php
/* Form file */
Token::create();
?>
<li>
<input type="submit" value="Change password">
</li>
</ul>
</form>
<?php
}
include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,978 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
if ($config['log_ip']) {
znote_visitor_insert_detailed_data(4);
}
if (isset($_GET['name']) === true && empty($_GET['name']) === false) {
$name = getValue($_GET['name']);
$user_id = user_character_exist($name);
if ($user_id !== false) {
$loadOutfits = $config['show_outfits']['characterprofile'];
if ($config['ServerEngine'] == 'TFS_10') {
if (!$loadOutfits) {
$profile_data = user_character_data($user_id, 'account_id', 'name', 'level', 'group_id', 'vocation', 'health', 'healthmax', 'experience', 'mana', 'manamax', 'sex', 'lastlogin');
} else { // Load outfits
if ($config['client'] < 780) {
$profile_data = user_character_data($user_id, 'account_id', 'name', 'level', 'group_id', 'vocation', 'health', 'healthmax', 'experience', 'mana', 'manamax', 'sex', 'lastlogin', 'lookbody', 'lookfeet', 'lookhead', 'looklegs', 'looktype');
} else {
$profile_data = user_character_data($user_id, 'account_id', 'name', 'level', 'group_id', 'vocation', 'health', 'healthmax', 'experience', 'mana', 'manamax', 'sex', 'lastlogin', 'lookbody', 'lookfeet', 'lookhead', 'looklegs', 'looktype', 'lookaddons');
}
}
$profile_data['online'] = user_is_online_10($user_id);
if ($config['Ach']) {
$user_id = (int) $user_id;
$achievementPoints = mysql_select_single("SELECT SUM(`value`) AS `sum` FROM `player_storage` WHERE `key` LIKE '30___' AND `player_id`={$user_id} LIMIT 1");
}
} else { // TFS 0.2, 0.3
if (!$loadOutfits) {
$profile_data = user_character_data($user_id, 'name', 'account_id', 'level', 'group_id', 'vocation', 'health', 'healthmax', 'experience', 'mana', 'manamax', 'lastlogin', 'online', 'sex');
} else { // Load outfits
if ($config['ServerEngine'] !== 'OTHIRE') {
if ($config['client'] < 780) {
$profile_data = user_character_data($user_id, 'name', 'account_id', 'level', 'group_id', 'vocation', 'health', 'healthmax', 'experience', 'mana', 'manamax', 'lastlogin', 'online', 'sex', 'lookbody', 'lookfeet', 'lookhead', 'looklegs', 'looktype');
} else {
$profile_data = user_character_data($user_id, 'name', 'account_id', 'level', 'group_id', 'vocation', 'health', 'healthmax', 'experience', 'mana', 'manamax', 'lastlogin', 'online', 'sex', 'lookbody', 'lookfeet', 'lookhead', 'looklegs', 'looktype', 'lookaddons');
}
} else {
$profile_data = user_character_data($user_id, 'name', 'account_id', 'level', 'group_id', 'vocation', 'health', 'healthmax', 'experience', 'mana', 'manamax', 'lastlogin', 'online', 'sex', 'lookbody', 'lookfeet', 'lookhead', 'looklegs', 'looktype');
}
}
}
$profile_znote_data = user_znote_character_data($user_id, 'created', 'hide_char', 'comment');
$guild_exist = false;
if (get_character_guild_rank($user_id) > 0) {
$guild_exist = true;
$guild = get_player_guild_data($user_id);
$guild_name = get_guild_name($guild['guild_id']);
}
?>
<!-- PROFILE MARKUP HERE-->
<table id="characterProfileTable">
<thead>
<tr class="yellow">
<th>
<?php if ($loadOutfits): ?>
<div class="outfit">
<img src="<?php echo $config['show_outfits']['imageServer']; ?>?id=<?php echo $profile_data['looktype']; ?>&addons=<?php echo $profile_data['lookaddons']; ?>&head=<?php echo $profile_data['lookhead']; ?>&body=<?php echo $profile_data['lookbody']; ?>&legs=<?php echo $profile_data['looklegs']; ?>&feet=<?php echo $profile_data['lookfeet']; ?>" alt="img">
</div>
<?php endif;
$flags = $config['country_flags'];
if ($flags['enabled'] && $flags['characterprofile']) {
$account_data = user_znote_account_data($profile_data['account_id'], 'flag');
if (strlen($account_data['flag']) > 0):
?><!-- Player country data -->
<div class="flag">
<img src="<?php echo $flags['server'] . '/' . $account_data['flag']; ?>.png">
</div>
<?php
endif;
}
?>
</th>
<th>
<h1><?php echo $profile_data['name']; ?></h1>
</th>
</tr>
</thead>
<tbody>
<!-- Player Position -->
<?php if ($profile_data['group_id'] > 1): ?>
<tr>
<td>Position</td>
<td><?php echo group_id_to_name($profile_data['group_id']); ?></td>
</tr>
<?php endif;
// pending deletion?
$deletion_time = mysql_select_single("SELECT `time` FROM `znote_deleted_characters` WHERE `character_name`='{$name}' AND `done` = '0' LIMIT 1;");
if ($deletion_time !== false): ?>
<tr>
<td colspan="2" style="color: red;">Flagged for deletion by owner after <?php echo $deletion_time['time']; ?>.</td>
</tr>
<?php endif; ?>
<!-- Player male / female -->
<tr>
<td>Sex</td>
<td><?php echo ($profile_data['sex'] == 1) ? 'Male' : 'Female'; ?></td>
</tr>
<!-- Player level -->
<tr>
<td>Level</td>
<td><?php echo $profile_data['level']; ?></td>
</tr>
<!-- Player vocation -->
<tr>
<td>Vocation</td>
<td><?php echo vocation_id_to_name($profile_data['vocation']); ?></td>
</tr>
<!-- Player guild -->
<?php if ($guild_exist): ?>
<tr>
<td>Guild</td>
<td><b><?php echo $guild['rank_name']; ?> </b> of <a href="guilds.php?name=<?php echo $guild_name; ?>"><?php echo $guild_name; ?></a></td>
</tr>
<?php endif; ?>
<!-- Player last login -->
<tr>
<td>Last Login</td>
<td><?php echo ($profile_data['lastlogin'] != 0) ? getClock($profile_data['lastlogin'], true, true) : 'Never.'; ?></td>
</tr>
<!-- Achievement start -->
<?php if ($config['Ach'] && (int)$achievementPoints['sum'] > 0): ?>
<tr>
<td>Achievement Points</td>
<td><?php echo (int)$achievementPoints['sum']; ?></td>
</tr>
<?php endif; ?>
<!-- Display house start -->
<?php
if ($config['ServerEngine'] !== 'TFS_02') {
// Compatibility fix
$column_town_id = array(
'OTHIRE' => 'townid',
'TFS_03' => 'town'
// Default: town_id
);
$column_town_id = (isset($column_town_id[$config['ServerEngine']]))
? $column_town_id[$config['ServerEngine']]
: 'town_id';
$houses = mysql_select_multi("
SELECT `id`, `owner`, `name`, `{$column_town_id}` AS `town_id`
FROM `houses`
WHERE `owner` = {$user_id};
");
if ($houses !== false) {
foreach ($houses as $h): ?>
<tr>
<td>House</td>
<td><?php echo $h['name'] . ', ' . $config['towns'][$h['town_id']]; ?></td>
</tr>
<?php endforeach;
}
}
?>
<!-- Display player status -->
<tr class="status_<?php echo ($profile_data['online']) ? 'online' : 'offline'; ?>">
<td>Status</td>
<td><?php echo ($profile_data['online']) ? 'online' : 'offline'; ?></td>
</tr>
<!-- Player created -->
<tr>
<td>Created</td>
<td><?php echo getClock($profile_znote_data['created'], true); ?></td>
</tr>
<!-- EQ shower -->
<?php if ($config['EQ_shower']['enabled']): ?>
<tr>
<?php
// Item image server
$imageServer = $config['shop']['imageServer'];
$imageType = $config['shop']['imageType'];
$PEQ = mysql_select_multi("
SELECT
`player_id`,
`pid`,
`itemtype`,
`count`
FROM `player_items`
WHERE `player_id`={$user_id}
AND `pid`<'11'
");
$soulStamina = (in_array($config['ServerEngine'], ['TFS_10']))
? " `soul`, `stamina`,"
: " `p`.`soul`, `p`.`stamina`,";
if ($config['client'] < 780) {
$soulStamina = " 0 AS `soul`, 0 AS `stamina`,";
}
$player_query = (in_array($config['ServerEngine'], ['TFS_10']))
? /* true */ "SELECT
`health`, `healthmax`,
`mana`, `manamax`,
`cap`,
`experience`, `level`,
{$soulStamina}
`maglevel`,
`skill_fist`,
`skill_club`,
`skill_sword`,
`skill_axe`,
`skill_dist`,
`skill_shielding`,
`skill_fishing`
FROM `players`
WHERE `id`={$user_id}
LIMIT 1;"
: /* false */ "SELECT
`p`.`health`, `p`.`healthmax`,
`p`.`mana`, `p`.`manamax`,
`p`.`cap`,
`p`.`experience`, `p`.`level`,
{$soulStamina}
`p`.`maglevel`,
`fist`.`value` AS `skill_fist`,
`club`.`value` AS `skill_club`,
`sword`.`value` AS `skill_sword`,
`axe`.`value` AS `skill_axe`,
`dist`.`value` AS `skill_dist`,
`shield`.`value` AS `skill_shielding`,
`fish`.`value` AS `skill_fishing`
FROM `players` AS `p`
LEFT JOIN `player_skills` AS `fist` ON `p`.`id` = `fist`.`player_id` AND `fist`.`skillid` = 0
LEFT JOIN `player_skills` AS `club` ON `p`.`id` = `club`.`player_id` AND `club`.`skillid` = 1
LEFT JOIN `player_skills` AS `sword` ON `p`.`id` = `sword`.`player_id` AND `sword`.`skillid` = 2
LEFT JOIN `player_skills` AS `axe` ON `p`.`id` = `axe`.`player_id` AND `axe`.`skillid` = 3
LEFT JOIN `player_skills` AS `dist` ON `p`.`id` = `dist`.`player_id` AND `dist`.`skillid` = 4
LEFT JOIN `player_skills` AS `shield` ON `p`.`id` = `shield`.`player_id` AND `shield`.`skillid` = 5
LEFT JOIN `player_skills` AS `fish` ON `p`.`id` = `fish`.`player_id` AND `fish`.`skillid` = 6
WHERE `p`.`id`= {$user_id}
LIMIT 1;";
$playerstats = mysql_select_single($player_query);
$playerstats['experience'] = number_format($playerstats['experience'],0,'',',');
$playerstats['stamina'] = number_format($playerstats['stamina']/60,2,':','');
$bar_length = 100;
$bar_health = (int)($bar_length * ($playerstats['health'] / $playerstats['healthmax']));
if ($playerstats['manamax'] > 0) {
$bar_mana = (int)($bar_length * ($playerstats['mana'] / $playerstats['manamax']));
}
else {
$bar_mana = 100;
}
$outfit_server = $config['show_outfits']['imageServer'];
$outfit_storage = $config['EQ_shower']['storage_value'];
$male_outfits = array(
[128,129,130,131,132],
[133,134,143,144,145],
[146,151,152,153,154],
[251,268,273,278,289],
[325,328,335,367,430],
[432,463,465,472,512],
//516,541,574,577,610,619,633,634,637,665,667,684,695,697,699,725,733,746,750,760,846,853,873,884,899
);
$female_outfits = array(
[136,137,138,139,140],
[141,142,147,148,149],
[150,155,156,157,158],
[252,269,270,279,288],
[324,329,336,366,431],
[433,464,466,471,513],
//514,542,575,578,618,620,632,635,636,664,666,683,694,696,698,724,732,745,749,759,845,852,874,885,900
);
$featured_outfits = ($profile_data['sex'] == 1) ? $male_outfits : $female_outfits;
$outfit_list = array();
$outfit_rows = COUNT($featured_outfits);
$outfit_columns = COUNT($featured_outfits[0]);
foreach ($featured_outfits as $row) {
if (COUNT($row) > $outfit_columns) {
$outfit_columns = COUNT($row);
}
foreach ($row as $column) {
$outfit_list[] = $column;
}
}
$highest_outfit_id = MAX($outfit_list);
$outfit_storage_max = $outfit_storage + $highest_outfit_id + 1;
$player_outfits = array();
$storage_sql = mysql_select_multi("
SELECT `key`, `value`
FROM `player_storage`
WHERE `player_id`={$user_id}
AND `key` > {$outfit_storage}
AND `key` < {$outfit_storage_max}
");
if ($storage_sql !== false && !empty($storage_sql)) {
foreach ($storage_sql as $row) {
$player_outfits[$row['key']] = $row['value'];
}
}
$aquired_outfits = array();
foreach ($outfit_list as $outfit_id) {
$outfit_key = $outfit_storage + $outfit_id;
if (isset($player_outfits[$outfit_key]) && $player_outfits[$outfit_key] == 3) {
$aquired_outfits[$outfit_id] = true;
}
}
?>
<td colspan="2" id="piv">
<div id="piv_flex">
<?php if ($config['EQ_shower']['equipment']): ?>
<div id="piv_i">
<img class="bg" src="/engine/img/outfit.png">
<div id="piv_lifebar"></div><div id="piv_lifetext"><span><?php echo $playerstats['health']; ?></span></div>
<div id="piv_manabar"></div><div id="piv_manatext"><span><?php echo $playerstats['mana']; ?></span></div>
<?php if ($PEQ !== false && !empty($PEQ)): foreach($PEQ as $item): ?>
<div class="itm itm-<?php echo $item['pid']; ?>">
<img src="<?php echo "http://{$imageServer}/".$item['itemtype'].".{$imageType}"; ?>">
</div>
<?php endforeach; endif; ?>
<span id="piv_cap">Cap:<br><?php echo $playerstats['cap']; ?></span>
<?php if ($loadOutfits): ?>
<div class="inventory_outfit">
<img src="<?php echo $config['show_outfits']['imageServer']; ?>?id=<?php echo $profile_data['looktype']; ?>&addons=<?php echo $profile_data['lookaddons']; ?>&head=<?php echo $profile_data['lookhead']; ?>&body=<?php echo $profile_data['lookbody']; ?>&legs=<?php echo $profile_data['looklegs']; ?>&feet=<?php echo $profile_data['lookfeet']; ?>" alt="img">
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ($config['EQ_shower']['skills']): ?>
<div id="piv_s">
<img class="bg" src="/engine/img/skillsbackground.png">
<span id="s_exp" class="txt"><?php echo $playerstats['experience']; ?></span>
<span id="s_lvl" class="txt"><?php echo $playerstats['level']; ?></span>
<span id="s_hp" class="txt"><?php echo number_format($playerstats['health'],0,'',','); ?></span>
<span id="s_mp" class="txt"><?php echo number_format($playerstats['mana'],0,'',','); ?></span>
<span id="s_soul" class="txt"><?php echo $playerstats['soul']; ?></span>
<span id="s_cap" class="txt"><?php echo number_format($playerstats['cap'],0,'',','); ?></span>
<span id="s_stamina" class="txt"><?php echo $playerstats['stamina']; ?></span>
<span id="s_maglevel" class="txt"><?php echo $playerstats['maglevel']; ?></span>
<span id="s_skill_fist" class="txt"><?php echo $playerstats['skill_fist']; ?></span>
<span id="s_skill_club" class="txt"><?php echo $playerstats['skill_club']; ?></span>
<span id="s_skill_sword" class="txt"><?php echo $playerstats['skill_sword']; ?></span>
<span id="s_skill_axe" class="txt"><?php echo $playerstats['skill_axe']; ?></span>
<span id="s_skill_dist" class="txt"><?php echo $playerstats['skill_dist']; ?></span>
<span id="s_skill_shielding" class="txt"><?php echo $playerstats['skill_shielding']; ?></span>
<span id="s_skill_fishing" class="txt"><?php echo $playerstats['skill_fishing']; ?></span>
</div>
<?php endif; ?>
<?php if ($config['EQ_shower']['outfits']): ?>
<div id="piv_o">
<div class="bg">
<div class="bg_t">
<div class="t_m"></div>
<div class="t_l"></div>
<div class="t_r"></div>
</div>
<div class="bg_m">
<div class="m_l"></div>
<div class="m_m"></div>
<div class="m_r"></div>
</div>
<div class="bg_b">
<div class="b_m"></div>
<div class="b_l"></div>
<div class="b_r"></div>
</div>
</div>
<div id="piv_o_container">
<?php foreach ($featured_outfits as $row): foreach($row as $outfit_id): $g = (isset($aquired_outfits[$outfit_id])) ? "" : "grayimg"; ?>
<img class="o <?php echo $g; ?>" src="<?php echo $outfit_server . "?id=" . $outfit_id; ?>&addons=3&head=0&body=0&legs=0&feet=0">
<?php endforeach; endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<!-- Inventory style positioning -->
<style type="text/css">
#piv {
background-image: url("/engine/img/o/m_m.png");
}
#piv_flex {
display: flex;
flex-wrap: wrap;
/*align-items: center;*/
justify-content: space-between;
width: 100%;
font-family: Verdana,Geneva,sans-serif;
font-size: 7.0pt;
line-height: 1;
color: rgb(201,201,201);
}
#piv_i, #piv_s, #piv_o {
position: relative;
}
#piv_i {
width: 126px;
height: 207px;
}
#piv_s {
width: 184px;
height: 232px;
}
#piv_o {
width: <?php echo 16 + $outfit_columns * 40; ?>px;
height: <?php echo 29 + $outfit_rows * 33; ?>px;
}
#piv_flex img {
position: absolute;
}
#piv_i .inventory_outfit {
position: absolute;
top: 130px;
left: -24px;
}
#piv_lifebar {
position: absolute;
border-radius: 6px;
top: 6px;
left: 14px;
height: 11px;
/*width: 95px;*/
width: <?php echo $bar_health; ?>px;
background-image: url("/engine/img/lifebarra.png");
}
#piv_manabar {
position: absolute;
border-radius: 6px;
top: 19px;
left: 14px;
height: 11px;
/*width: 95px;*/
width: <?php echo $bar_mana; ?>px;
background-image: url("/engine/img/manabar.png");
}
#piv_lifetext,
#piv_manatext {
position: absolute;
display: block;
left: 15px;
width: <?php echo $bar_length; ?>px;
text-align: center;
}
#piv_lifetext {
top: 7px;
}
#piv_manatext {
top: 20px;
}
#piv_lifetext span,
#piv_manatext span {
background-color: rgba(0,0,0,0.7);
border-radius: 3px;
}
#piv_flex .itm {
background-image: url("/engine/img/bg.png");
width: 32px;
height: 32px;
position: absolute;
}
#piv_flex .itm-1 { left: 48px; top: 39px; }
#piv_flex .itm-2 { left: 11px; top: 53px; }
#piv_flex .itm-3 { left: 85px; top: 53px; }
#piv_flex .itm-4 { left: 48px; top: 76px; }
#piv_flex .itm-5 { left: 85px; top: 90px; }
#piv_flex .itm-6 { left: 11px; top: 90px; }
#piv_flex .itm-7 { left: 48px; top: 113px; }
#piv_flex .itm-8 { left: 48px; top: 150px; }
#piv_flex .itm-9 { left: 11px; top: 127px; }
#piv_flex .itm-10 { left: 85px; top: 127px; }
#piv_cap {
position: absolute;
top: 162px;
left: 85px;
min-width: 32px;
text-align: center;
}
#piv_s .txt {
position: absolute;
}
#s_exp { right: 22px; top: 16px; }
#s_lvl { right: 22px; top: 30px; }
#s_hp { right: 22px; top: 44px; }
#s_mp { right: 22px; top: 58px; }
#s_soul { right: 22px; top: 71px; }
#s_cap { right: 22px; top: 86px; }
#s_stamina { right: 22px; top: 100px; }
#s_maglevel { right: 22px; top: 114px; }
#s_skill_fist { right: 22px; top: 132px; }
#s_skill_club { right: 22px; top: 146px; }
#s_skill_sword { right: 22px; top: 160px; }
#s_skill_axe { right: 22px; top: 174px; }
#s_skill_dist { right: 22px; top: 188px; }
#s_skill_shielding { right: 22px; top: 202px; }
#s_skill_fishing { right: 22px; top: 215px; }
/* Dynamically render background container size for outfits */
#piv_o .bg {
width: inherit;
height: inherit;
position: absolute;
left: 0;
top: 0;
}
#piv_o .bg_t {
height: 21px;
width: 100%;
}
#piv_o .bg_m {
width: 100%;
height: <?php echo $outfit_rows * 33; ?>px;
}
#piv_o .t_l {
position: absolute;
left: 0;
top: 0;
background-image: url("/engine/img/o/t_l.png");
width: 8px;
height: 21px;
}
#piv_o .t_m {
position: absolute;
right: 0;
top: 0;
background-image: url("/engine/img/o/t_m.png");
width: 100%;
height: 21px;
}
#piv_o .t_r {
position: absolute;
right: 0;
top: 0;
background-image: url("/engine/img/o/t_r.png");
width: 50px;
height: 21px;
}
#piv_o .m_l {
background-image: url("/engine/img/o/m_l.png");
width: 8px;
height: inherit;
float: left;
}
#piv_o .m_m {
background-image: url("/engine/img/o/m_m.png");
width: calc(100% - 16px);
height: inherit;
float: left;
}
#piv_o .m_r {
background-image: url("/engine/img/o/m_r.png");
width: 8px;
height: inherit;
float: left;
}
#piv_o .b_l {
position: absolute;
left: 0;
bottom: 0;
background-image: url("/engine/img/o/b_l.png");
width: 8px;
height: 8px;
}
#piv_o .b_m {
position: absolute;
right: 0;
bottom: 0;
background-image: url("/engine/img/o/b_m.png");
width: 100%;
height: 8px;
}
#piv_o .b_r {
position: absolute;
right: 0;
bottom: 0;
background-image: url("/engine/img/o/b_r.png");
width: 8px;
height: 8px;
}
/* Render outfit player sprites */
#piv_o_container {
height: inherit;
width: inherit;
}
#piv_o_container .o {
position: absolute;
right: 0;
bottom: 0;
}
/* Outfit column positions */
<?php for ($column = 1; $column <= $outfit_columns; $column++): ?>
#piv_o_container .o:nth-child(<?php echo $outfit_columns.'n+'.$column;?>) { right: <?php echo 10 + 40 * ($outfit_columns-$column); ?>px; }
<?php endfor; ?>
/* Outfit row positions */
<?php for ($row = 1; $row <= $outfit_rows; $row++): ?>
#piv_o_container .o:nth-child(n+<?php echo $outfit_columns * ($row-1)+1; ?>):nth-child(-n+<?php echo $outfit_columns*$row; ?>) { bottom: <?php echo 10 + 33 * ($outfit_rows-$row); ?>px; }
<?php endfor; ?>
#piv_o_container .o.grayimg {
filter: none;
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
-o-filter: grayscale(100%);
opacity: .5;
filter: alpha(opacity=50);
margin-left: -25pt;
margin-top: -25px;
}
</style>
</td>
</tr>
<?php endif; ?>
<!-- End EQ shower -->
</tbody>
</table>
<!-- Player Comment -->
<?php if (!empty($profile_znote_data['comment'])): ?>
<table class="comment">
<thead>
<tr class="yellow">
<td><font class="profile_font" name="profile_font_comment">Comment:</font></td>
</tr>
</thead>
<tbody>
<tr>
<td><?php echo preg_replace('/\v+|\\\r\\\n/','<br/>',$profile_znote_data['comment']); ?></td>
</tr>
</tbody>
</table>
<?php endif; ?>
<!-- Achievements start -->
<?php if ($config['Ach']):
$achievements = mysql_select_multi("
SELECT `player_id`, `value`, `key`
FROM `player_storage`
WHERE `player_id`='$user_id'
AND `key` LIKE '30___';
");
$c_achs = $config['achievements'];
$toggle = array(
'show' => '<a href="#show">Show</a>',
'hide' => '<a href="#hide">Hide</a>'
);
if ($achievements !== false): ?>
<h3>Achievements: <label id="ac_label_hide" for="ac_toggle_hide"><?php echo $toggle['show']; ?></label></h3>
<!-- <div id="accordion">
<h3>Show/hide player achievements</h3>
<div>
</div>
</div><br> -->
<input type="checkbox" id="ac_toggle_hide" name="ac_toggle_hide">
<table class="achievements">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Points</th>
</tr>
</thead>
<tbody>
<?php foreach($achievements as $a): ?>
<tr>
<td><?php echo $c_achs[$a['key']][0]; ?></td>
<td><?php echo $c_achs[$a['key']][1]; ?></td>
<td><?php echo $a['value']; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<style type="text/css">
table.achievements,
#ac_toggle_hide {
display: none;
}
#ac_toggle_hide:checked + table.achievements {
display: table;
}
</style>
<script type="text/javascript">
document.getElementById("ac_label_hide").addEventListener("click", function(event){
event.preventDefault();
if (document.getElementById("ac_label_hide").innerHTML == "<?php echo str_replace('"', '\"', $toggle['show']); ?>") {
document.getElementById("ac_label_hide").innerHTML = "<?php echo str_replace('"', '\"', $toggle['hide']); ?>";
document.getElementById("ac_toggle_hide").checked = true;
} else {
document.getElementById("ac_label_hide").innerHTML = "<?php echo str_replace('"', '\"', $toggle['show']); ?>";
document.getElementById("ac_toggle_hide").checked = false;
}
});
</script>
<?php endif; ?>
<?php endif; ?>
<!-- DEATH LIST -->
<table class="deathlist">
<thead>
<tr class="yellow">
<th colspan="2">Death List</th>
</tr>
</thead>
<tbody>
<?php
if ($config['ServerEngine'] == 'TFS_10') {
$deaths = mysql_select_multi("
SELECT
`player_id`,
`time`,
`level`,
`killed_by`,
`is_player`,
`mostdamage_by`,
`mostdamage_is_player`,
`unjustified`,
`mostdamage_unjustified`
FROM `player_deaths`
WHERE `player_id`=$user_id
ORDER BY `time` DESC
LIMIT 10;
");
if ($deaths) {
foreach ($deaths as $d) {
$lasthit = ($d['is_player'])
? "<a href='characterprofile.php?name=".$d['killed_by']."'>".$d['killed_by']."</a>"
: $d['killed_by'];
?>
<tr>
<td><?php echo getClock($d['time'], true, true); ?></td>
<td>
<?php
echo "Killed at level ".$d['level']." by {$lasthit}";
if ($d['unjustified']) {
echo " <font color='red' style='font-style: italic;'>(unjustified)</font>";
}
$mostdmg = ($d['mostdamage_by'] !== $d['killed_by']) ? true : false;
if ($mostdmg) {
$mostdmg = ($d['mostdamage_is_player'])
? "<a href='characterprofile.php?name=".$d['mostdamage_by']."'>".$d['mostdamage_by']."</a>"
: $d['mostdamage_by'];
echo "<br>and by $mostdmg.";
if ($d['mostdamage_unjustified']) {
echo " <font color='red' style='font-style: italic;'>(unjustified)</font>";
}
} else {
echo " <b>(soloed)</b>";
}
?>
</td>
</tr>
<?php
}
} else {
?>
<tr>
<td colspan="2">This player has never died.</td>
</tr>
<?php
}
} elseif ($config['ServerEngine'] == 'TFS_02') {
$array = user_fetch_deathlist($user_id);
if ($array) {
foreach ($array as $value):
if ($value['is_player'] == 1) {
$value['killed_by'] = 'player: <a href="characterprofile.php?name='. $value['killed_by'] .'">'. $value['killed_by'] .'</a>';
} else {
$value['killed_by'] = 'monster: '. $value['killed_by'] .'.';
}
?>
<tr>
<td><?php echo getClock($value['time'], true, true); ?></td>
<td><?php echo 'Killed at level '. $value['level'] .' by '. $value['killed_by']; ?></td>
</tr>
<?php endforeach;
} else {
?>
<tr>
<td colspan="2">This player has never died.</td>
</tr>
<?php
}
} elseif (in_array($config['ServerEngine'], array('TFS_03', 'OTHIRE'))) {
//mysql_select_single("SELECT * FROM players WHERE name='TEST DEBUG';");
$array = user_fetch_deathlist03($user_id);
if ($array) {
// Design and present the list
foreach ($array as $value):
$value[3] = user_get_killer_id(user_get_kid($value['id']));
if ($value[3] !== false && $value[3] >= 1) {
$namedata = user_character_data((int)$value[3], 'name');
if ($namedata !== false) {
$value[3] = $namedata['name'];
$value[3] = 'player: <a href="characterprofile.php?name='. $value[3] .'">'. $value[3] .'</a>';
} else {
$value[3] = 'deleted player.';
}
} else {
$value[3] = user_get_killer_m_name(user_get_kid($value['id']));
if ($value[3] === false) {
$value[3] = 'deleted player.';
}
}
?>
<tr>
<td><?php echo getClock($value['date'], true, true); ?></td>
<td><?php echo 'Killed at level '. $value['level'] .' by '. $value[3]; ?></td>
</tr>
<?php endforeach;
} else {
?>
<tr>
<td colspan="2">This player has never died.</td>
</tr>
<?php
}
}
?>
</tbody>
</table>
<!-- QUEST PROGRESSION -->
<?php
$totalquests = 0;
$completedquests = 0;
$firstrun = 1;
if ($config['EnableQuests'] == true) {
$sqlquests = mysql_select_multi("
SELECT `player_id`, `key`, `value`
FROM player_storage
WHERE `player_id` = {$user_id}
");
if (isset($config['quests']) && !empty($config['quests'])) {
foreach ($config['quests'] as $cquest) {
$totalquests = $totalquests + 1;
if ($sqlquests !== false) {
foreach ($sqlquests as $dbquest) {
if ($cquest[0] == $dbquest['key'] && $cquest[1] == $dbquest['value']) {
$completedquests = $completedquests + 1;
}
}
}
if ($cquest[3] == 1) {
if ($completedquests != 0) {
if ($firstrun == 1): ?>
<b> Quest progression </b>
<table id="characterprofileQuest" class="table table-striped table-hover">
<thead>
<tr class="yellow">
<th>Quest:</th>
<th>progression:</th>
</tr>
</thead>
<tbody>
<?php
$firstrun = 0;
endif;
$completed = $completedquests / $totalquests * 100;
?>
<tr>
<td><?php echo $cquest[2]; ?></td>
<td id="progress">
<span id="percent"><?php echo round($completed); ?>%</span>
<div id="bar" style="width: '.$completed.'%"></div>
</td>
</tr>
<?php
}
$completedquests = 0;
$totalquests = 0;
}
}
}
}
if ($firstrun == 0): ?>
</tbody></table>
<?php endif; ?>
<!-- END QUEST PROGRESSION -->
<!-- CHARACTER LIST -->
<?php
// Backward compatibility
$select_online = "CASE WHEN `l`.`player_id` IS NULL THEN 0 else 1 END as `online`";
$join_online = "LEFT JOIN `players_online` as `l` ON `p`.`id` = `l`.`player_id`";
if ($config['ServerEngine'] != 'TFS_10') {
$select_online = "`p`.`online`";
$join_online = "";
}
// Load other visible characters
$otherChars = mysql_select_multi("
SELECT
`p`.`id`,
`p`.`name`,
`p`.`level`,
`p`.`vocation`,
`p`.`lastlogin`,
{$select_online}
FROM `players` as `o`
JOIN `players` as `p`
ON `o`.`account_id` = `p`.`account_id`
LEFT JOIN `znote_players` as `z`
ON `p`.`id` = `z`.`player_id`
LEFT JOIN `znote_players` as `z2`
ON `o`.`id` = `z2`.`player_id`
{$join_online}
WHERE `o`.`id` = {$user_id}
AND `p`.`id` != `o`.`id`
AND `z`.`hide_char` = 0
AND `z2`.`hide_char` = 0
ORDER BY `p`.`experience` DESC;
");
// Render table if there are any characters to show
if ($otherChars !== false) {
?>
<li>
<b>Other visible characters on this account:</b><br>
<table id="characterprofileTable" class="table table-striped table-hover">
<tr class="yellow">
<th>Name:</th>
<th>Level:</th>
<th>Vocation:</th>
<th>Last login:</th>
<th>Status:</th>
</tr>
<?php
// Add character rows
foreach ($otherChars as $char):
?>
<tr>
<td><a href="characterprofile.php?name=<?php echo $char['name']; ?>"><?php echo $char['name']; ?></a></td>
<td><?php echo (int)$char['level']; ?></td>
<td><?php echo vocation_id_to_name($char['vocation']); ?></td>
<td><?php echo ($char['lastlogin'] != 0) ? getClock($char['lastlogin'], true, true) : 'Never.'; ?></td>
<td><?php echo ($char['online']) ? 'online' : 'offline'; ?></td>
</tr>
<?php
endforeach;
?>
</table>
</li>
<?php
}
?>
<!-- END CHARACTER LIST -->
<p class="address">Address: <a href="<?php echo ($config['htwrite']) ? "//" . $_SERVER['HTTP_HOST']."/" . $profile_data['name'] : "//" . $_SERVER['HTTP_HOST'] . "/characterprofile.php?name=" . $profile_data['name']; ?>"><?php echo ($config['htwrite']) ? $_SERVER['HTTP_HOST']."/". $profile_data['name'] : $_SERVER['HTTP_HOST']."/characterprofile.php?name=". $profile_data['name']; ?></a></p>
<?php
} else {
echo htmlentities(strip_tags($name, ENT_QUOTES)) . ' does not exist.';
}
} else {
header('Location: index.php');
}
include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,249 @@
<?php
/**
* List of countries, following ISO 3166 standard.
*
*/
$config['countries'] = array
(
'af' => 'Afghanistan',
'al' => 'Albania',
'dz' => 'Algeria',
'as' => 'American Samoa',
'ad' => 'Andorra',
'ao' => 'Angola',
'ai' => 'Anguilla',
'aq' => 'Antarctica',
'ag' => 'Antigua and Barbuda',
'ar' => 'Argentina',
'am' => 'Armenia',
'aw' => 'Aruba',
'au' => 'Australia',
'at' => 'Austria',
'az' => 'Azerbaijan',
'bs' => 'Bahamas',
'bh' => 'Bahrain',
'bd' => 'Bangladesh',
'bb' => 'Barbados',
'by' => 'Belarus',
'be' => 'Belgium',
'bz' => 'Belize',
'bj' => 'Benin',
'bm' => 'Bermuda',
'bt' => 'Bhutan',
'bo' => 'Bolivia',
'ba' => 'Bosnia and Herzegovina',
'bw' => 'Botswana',
'bv' => 'Bouvet Island',
'br' => 'Brazil',
'io' => 'British Indian Ocean Territory',
'bn' => 'Brunei Darussalam',
'bg' => 'Bulgaria',
'bf' => 'Burkina Faso',
'bi' => 'Burundi',
'kh' => 'Cambodia',
'cm' => 'Cameroon',
'ca' => 'Canada',
'cv' => 'Cape Verde',
'ky' => 'Cayman Islands',
'cf' => 'Central African Republic',
'td' => 'Chad',
'cl' => 'Chile',
'cn' => 'China',
'cx' => 'Christmas Island',
'cc' => 'Cocos (Keeling) Islands',
'co' => 'Colombia',
'km' => 'Comoros',
'cg' => 'Congo',
'cd' => 'Congo, the Democratic Republic of the',
'ck' => 'Cook Islands',
'cr' => 'Costa Rica',
'ci' => 'Cote D\'Ivoire',
'hr' => 'Croatia',
'cu' => 'Cuba',
'cy' => 'Cyprus',
'cz' => 'Czech Republic',
'dk' => 'Denmark',
'dj' => 'Djibouti',
'dm' => 'Dominica',
'do' => 'Dominican Republic',
'ec' => 'Ecuador',
'eg' => 'Egypt',
'sv' => 'El Salvador',
'gq' => 'Equatorial Guinea',
'er' => 'Eritrea',
'ee' => 'Estonia',
'et' => 'Ethiopia',
'fk' => 'Falkland Islands (Malvinas)',
'fo' => 'Faroe Islands',
'fj' => 'Fiji',
'fi' => 'Finland',
'fr' => 'France',
'gf' => 'French Guiana',
'pf' => 'French Polynesia',
'tf' => 'French Southern Territories',
'ga' => 'Gabon',
'gm' => 'Gambia',
'ge' => 'Georgia',
'de' => 'Germany',
'gh' => 'Ghana',
'gi' => 'Gibraltar',
'gr' => 'Greece',
'gl' => 'Greenland',
'gd' => 'Grenada',
'gp' => 'Guadeloupe',
'gu' => 'Guam',
'gt' => 'Guatemala',
'gn' => 'Guinea',
'gw' => 'Guinea-Bissau',
'gy' => 'Guyana',
'ht' => 'Haiti',
'hm' => 'Heard Island and Mcdonald Islands',
'va' => 'Holy See (Vatican City State)',
'hn' => 'Honduras',
'hk' => 'Hong Kong',
'hu' => 'Hungary',
'is' => 'Iceland',
'in' => 'India',
'id' => 'Indonesia',
'ir' => 'Iran, Islamic Republic of',
'iq' => 'Iraq',
'ie' => 'Ireland',
'il' => 'Israel',
'it' => 'Italy',
'jm' => 'Jamaica',
'jp' => 'Japan',
'jo' => 'Jordan',
'kz' => 'Kazakhstan',
'ke' => 'Kenya',
'ki' => 'Kiribati',
'kp' => 'Korea, Democratic People\'s Republic of',
'kr' => 'Korea, Republic of',
'kw' => 'Kuwait',
'kg' => 'Kyrgyzstan',
'la' => 'Lao People\'s Democratic Republic',
'lv' => 'Latvia',
'lb' => 'Lebanon',
'ls' => 'Lesotho',
'lr' => 'Liberia',
'ly' => 'Libyan Arab Jamahiriya',
'li' => 'Liechtenstein',
'lt' => 'Lithuania',
'lu' => 'Luxembourg',
'mo' => 'Macao',
'mk' => 'Macedonia, the Former Yugoslav Republic of',
'mg' => 'Madagascar',
'mw' => 'Malawi',
'my' => 'Malaysia',
'mv' => 'Maldives',
'ml' => 'Mali',
'mt' => 'Malta',
'mh' => 'Marshall Islands',
'mq' => 'Martinique',
'mr' => 'Mauritania',
'mu' => 'Mauritius',
'yt' => 'Mayotte',
'mx' => 'Mexico',
'fm' => 'Micronesia, Federated States of',
'md' => 'Moldova, Republic of',
'mc' => 'Monaco',
'mn' => 'Mongolia',
'ms' => 'Montserrat',
'ma' => 'Morocco',
'mz' => 'Mozambique',
'mm' => 'Myanmar',
'na' => 'Namibia',
'nr' => 'Nauru',
'np' => 'Nepal',
'nl' => 'Netherlands',
'an' => 'Netherlands Antilles',
'nc' => 'New Caledonia',
'nz' => 'New Zealand',
'ni' => 'Nicaragua',
'ne' => 'Niger',
'ng' => 'Nigeria',
'nu' => 'Niue',
'nf' => 'Norfolk Island',
'mp' => 'Northern Mariana Islands',
'no' => 'Norway',
'om' => 'Oman',
'pk' => 'Pakistan',
'pw' => 'Palau',
'ps' => 'Palestinian Territory, Occupied',
'pa' => 'Panama',
'pg' => 'Papua New Guinea',
'py' => 'Paraguay',
'pe' => 'Peru',
'ph' => 'Philippines',
'pn' => 'Pitcairn',
'pl' => 'Poland',
'pt' => 'Portugal',
'pr' => 'Puerto Rico',
'qa' => 'Qatar',
're' => 'Reunion',
'ro' => 'Romania',
'ru' => 'Russian Federation',
'rw' => 'Rwanda',
'sh' => 'Saint Helena',
'kn' => 'Saint Kitts and Nevis',
'lc' => 'Saint Lucia',
'pm' => 'Saint Pierre and Miquelon',
'vc' => 'Saint Vincent and the Grenadines',
'ws' => 'Samoa',
'sm' => 'San Marino',
'st' => 'Sao Tome and Principe',
'sa' => 'Saudi Arabia',
'sn' => 'Senegal',
'cs' => 'Serbia and Montenegro',
'sc' => 'Seychelles',
'sl' => 'Sierra Leone',
'sg' => 'Singapore',
'sk' => 'Slovakia',
'si' => 'Slovenia',
'sb' => 'Solomon Islands',
'so' => 'Somalia',
'za' => 'South Africa',
'gs' => 'South Georgia and the South Sandwich Islands',
'es' => 'Spain',
'lk' => 'Sri Lanka',
'sd' => 'Sudan',
'sr' => 'Suriname',
'sj' => 'Svalbard and Jan Mayen',
'sz' => 'Swaziland',
'se' => 'Sweden',
'ch' => 'Switzerland',
'sy' => 'Syrian Arab Republic',
'tw' => 'Taiwan, Province of China',
'tj' => 'Tajikistan',
'tz' => 'Tanzania, United Republic of',
'th' => 'Thailand',
'tl' => 'Timor-Leste',
'tg' => 'Togo',
'tk' => 'Tokelau',
'to' => 'Tonga',
'tt' => 'Trinidad and Tobago',
'tn' => 'Tunisia',
'tr' => 'Turkey',
'tm' => 'Turkmenistan',
'tc' => 'Turks and Caicos Islands',
'tv' => 'Tuvalu',
'ug' => 'Uganda',
'ua' => 'Ukraine',
'ae' => 'United Arab Emirates',
'gb' => 'United Kingdom',
'us' => 'United States',
'um' => 'United States Minor Outlying Islands',
'uy' => 'Uruguay',
'uz' => 'Uzbekistan',
'vu' => 'Vanuatu',
've' => 'Venezuela',
'vn' => 'Viet Nam',
'vg' => 'Virgin Islands, British',
'vi' => 'Virgin Islands, U.s.',
'wf' => 'Wallis and Futuna',
'eh' => 'Western Sahara',
'ye' => 'Yemen',
'zm' => 'Zambia',
'zw' => 'Zimbabwe'
);
?>

1084
app/ZnoteAAC/config.php Normal file

File diff suppressed because one or more lines are too long

6
app/ZnoteAAC/contact.php Normal file
View File

@@ -0,0 +1,6 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php'; ?>
<h1>Contact</h1>
<p>TODO: Edit the contact details here.</p>
<?php include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,169 @@
<?php require_once 'engine/init.php';
protect_page();
include 'layout/overall/header.php';
if (empty($_POST) === false) {
// $_POST['']
$required_fields = array('name', 'selected_town');
foreach($_POST as $key=>$value) {
if (empty($value) && in_array($key, $required_fields) === true) {
$errors[] = 'You need to fill in all fields.';
break 1;
}
}
// check errors (= user exist, pass long enough
if (empty($errors) === true) {
if (!Token::isValid($_POST['token'])) {
$errors[] = 'Token is invalid.';
}
$_POST['name'] = validate_name($_POST['name']);
if ($_POST['name'] === false) {
$errors[] = 'Your name can not contain more than 2 words.';
} else {
if (user_character_exist($_POST['name']) !== false) {
$errors[] = 'Sorry, that character name already exist.';
}
if (!preg_match("/^[a-zA-Z ]+$/", $_POST['name'])) {
$errors[] = 'Your name may only contain a-z, A-Z and spaces.';
}
if (strlen($_POST['name']) < $config['minL'] || strlen($_POST['name']) > $config['maxL']) {
$errors[] = 'Your character name must be between ' . $config['minL'] . ' - ' . $config['maxL'] . ' characters long.';
}
// name restriction
$resname = explode(" ", $_POST['name']);
$username = $_POST['name'];
foreach($resname as $res) {
if(in_array(strtolower($res), $config['invalidNameTags'])) {
$errors[] = 'Your username contains a restricted word.';
}
if(strlen($res) == 1) {
$errors[] = 'Too short words in your name.';
}
}
if(in_array(strtolower($username), $config['creatureNameTags'])) {
$errors[] = 'Your username contains a creature name.';
}
// Validate vocation id
if (!in_array((int)$_POST['selected_vocation'], $config['available_vocations'])) {
$errors[] = 'Permission Denied. Wrong vocation.';
}
// Validate town id
if (!in_array((int)$_POST['selected_town'], $config['available_towns'])) {
$errors[] = 'Permission Denied. Wrong town.';
}
// Validate gender id
if (!in_array((int)$_POST['selected_gender'], array(0, 1))) {
$errors[] = 'Permission Denied. Wrong gender.';
}
if (vocation_id_to_name($_POST['selected_vocation']) === false) {
$errors[] = 'Failed to recognize that vocation, does it exist?';
}
if (town_id_to_name($_POST['selected_town']) === false) {
$errors[] = 'Failed to recognize that town, does it exist?';
}
if (gender_exist($_POST['selected_gender']) === false) {
$errors[] = 'Failed to recognize that gender, does it exist?';
}
// Char count
$char_count = user_character_list_count($session_user_id);
if ($char_count >= $config['max_characters'] && !is_admin($user_data)) {
$errors[] = 'Your account is not allowed to have more than '. $config['max_characters'] .' characters.';
}
if (validate_ip(getIP()) === false && $config['validate_IP'] === true) {
$errors[] = 'Failed to recognize your IP address. (Not a valid IPv4 address).';
}
}
}
}
?>
<h1>Create Character</h1>
<?php
if (isset($_GET['success']) && empty($_GET['success'])) {
echo 'Congratulations! Your character has been created. See you in-game!';
} else {
if (empty($_POST) === false && empty($errors) === true) {
if ($config['log_ip']) {
znote_visitor_insert_detailed_data(2);
}
//Register
$character_data = array(
'name' => format_character_name($_POST['name']),
'account_id'=> $session_user_id,
'vocation' => $_POST['selected_vocation'],
'town_id' => $_POST['selected_town'],
'sex' => $_POST['selected_gender'],
'lastip' => getIPLong(),
'created' => time()
);
user_create_character($character_data);
header('Location: createcharacter.php?success');
exit();
//End register
} else if (empty($errors) === false){
echo '<font color="red"><b>';
echo output_errors($errors);
echo '</b></font>';
}
?>
<form action="" method="post">
<ul>
<li>
Name:<br>
<input type="text" name="name">
</li>
<li>
<!-- Available vocations to select from when creating character -->
Vocation:<br>
<select name="selected_vocation">
<?php foreach ($config['available_vocations'] as $id) { ?>
<option value="<?php echo $id; ?>"><?php echo vocation_id_to_name($id); ?></option>
<?php } ?>
</select>
</li>
<li>
<!-- Available genders to select from when creating character -->
Gender:<br>
<select name="selected_gender">
<option value="1">Male(boy)</option>
<option value="0">Female(girl)</option>
</select>
</li>
<?php
$available_towns = $config['available_towns'];
if (count($available_towns) > 1):
?>
<li>
<!-- Available towns to select from when creating character -->
Town:<br>
<select name="selected_town">
<?php
foreach ($available_towns as $tid):
?>
<option value="<?php echo $tid; ?>"><?php echo town_id_to_name($tid); ?></option>
<?php
endforeach;
?>
</select>
</li>
<?php
else:
?>
<input type="hidden" name="selected_town" value="<?php echo end($available_towns); ?>">
<?php
endif;
/* Form file */
Token::create();
?>
<li>
<input type="submit" value="Create Character">
</li>
</ul>
</form>
<?php
}
include 'layout/overall/footer.php'; ?>

86
app/ZnoteAAC/credits.php Normal file
View File

@@ -0,0 +1,86 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php'; ?>
<h1>Znote AAC</h1>
<p>This website is powered by the <a href="https://github.com/Znote/ZnoteAAC">Znote AAC</a> engine.</p>
<p>An OT website (<strong>A</strong>utomatic <strong>A</strong>ccount <strong>C</strong>reator) created by <a href="https://otland.net/members/znote.5993/">Znote</a> from the OT forum community <a href="https://otland.net">otland.net</a>.</p>
<p>Znote AAC is an open source project where everyone can help with development.</p>
<h2>Developers:</h2>
<?php // If CURL isn't enabled show default version.
if(!function_exists('curl_version')):
?>
<p>See the full list of developers <a href="https://github.com/Znote/ZnoteAAC/graphs/contributors">HERE</a>.</p>
<?php else:
// CURL enabled. Lets create an API web request to github.
$request = curl_init();
curl_setopt($request, CURLOPT_URL, 'https://api.github.com/repos/Znote/ZnoteAAC/contributors');
curl_setopt($request, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($request, CURLOPT_USERAGENT, 'ZnoteAAC'); // GitHub requires user agent header.
curl_setopt($request, CURLOPT_SSL_VERIFYPEER, false);
// Load contributors and close the request.
$developers = json_decode(curl_exec($request), true); // Sorted by contributions.
curl_close($request);
?>
<div class="developers">
<?php foreach ($developers as $developer): ?>
<div class="developer">
<div class="avatar"><img src="<?php echo $developer['avatar_url']; ?>" alt="Avatar of: <?php echo $developer['login']; ?>"></div>
<p class="username"><a href="<?php echo $developer['html_url']; ?>"><?php echo $developer['login']; ?></a>
<br>Updates: <?php echo $developer['contributions']; ?></p>
</div>
<?php endforeach; ?>
</div>
<style type="text/css">
/* Credits.php specific CSS alterations */
.developers {
width: 100%;
}
.developers:after {
content: '';
display: block;
clear: both;
}
.developer {
width: calc(20% - 16px);
float: left;
padding: 0 8px 16px;
}
.developer img {
width: 100%;
}
.username {
margin: 8px 0 0;
text-align: center;
overflow: hidden;
}
</style>
<?php
endif;
?>
<h2>Thanks to: (in no particular order)</h2>
<p>
<a href="https://otland.net/members/chris.13882/">Chris</a> - PHP OOP file samples, testing, bugfixing.
<br><a href="https://otland.net/members/kiwi-dan.152/">Kiwi Dan</a> - Researching TFS 0.2 for me, participation in developement.
<br><a href="https://otland.net/members/amoaz.26626/">Amoaz</a> - Pentesting and security tips.
<br><a href="https://otland.net/members/evan.40401/">Evan</a>, <a href="https://otland.net/members/gremlee.12075/">Gremlee</a> - Researching TFS 0.3, constructive feedback, suggestion and participation.
<br><a href="https://otland.net/members/att3.98289/">ATT3</a> - Reporting and fixing bugs, TFS 1.0 research.
<br><a href="https://otland.net/members/mark.1/">Mark</a> - Old repository, TFS distributions which this AAC was primarily built for.
<br><a href="https://github.com/tedbro">Tedbro</a>, <a href="https://github.com/exura">Exura</a>, <a href="https://github.com/PrinterLUA">PrinterLUA</a> - Reporting bugs.
<br><a href="https://github.com/Nottinghster">Nottinghster</a> - OTHIRE distribution compatibility.
</p>
<style>
.contributors {
margin-top: 10px;
padding: 5px;
border: 1px solid rgb(184, 184, 184);
display: inline-flex;
width: 100%;
}
.contributor {
padding: 10px;
text-align: center;
}
</style>
<?php include 'layout/overall/footer.php'; ?>

39
app/ZnoteAAC/deaths.php Normal file
View File

@@ -0,0 +1,39 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php';
$cache = new Cache('engine/cache/deaths');
if ($cache->hasExpired()) {
if ($config['ServerEngine'] == 'TFS_02' || $config['ServerEngine'] == 'TFS_10') {
$deaths = fetchLatestDeaths();
} else if ($config['ServerEngine'] == 'TFS_03' || $config['ServerEngine'] == 'OTHIRE') {
$deaths = fetchLatestDeaths_03(30);
}
$cache->setContent($deaths);
$cache->save();
} else {
$deaths = $cache->load();
}
if ($deaths) {
?>
<h1>Latest Deaths</h1>
<table id="deathsTable" class="table table-striped">
<tr class="yellow">
<th>Victim</th>
<th>Time</th>
<th>Killer</th>
</tr>
<?php foreach ($deaths as $death) {
echo '<tr>';
echo "<td>At level ". $death['level'] .": <a href='characterprofile.php?name=". $death['victim'] ."'>". $death['victim'] ."</a></td>";
echo "<td>". getClock($death['time'], true) ."</td>";
if ($death['is_player'] == 1) echo "<td>Player: <a href='characterprofile.php?name=". $death['killed_by'] ."'>". $death['killed_by'] ."</a></td>";
else if ($death['is_player'] == 0) {
if ($config['ServerEngine'] == 'TFS_03') echo "<td>Monster: ". ucfirst(str_replace("a ", "", $death['killed_by'])) ."</td>";
else echo "<td>Monster: ". ucfirst($death['killed_by']) ."</td>";
}
else echo "<td>". $death['killed_by'] ."</td>";
echo '</tr>';
} ?>
</table>
<?php
} else echo 'No deaths exist.';
include 'layout/overall/footer.php'; ?>

View File

@@ -0,0 +1,31 @@
<?php require_once 'engine/init.php'; include 'layout/overall/header.php'; ?>
<h1>Downloads</h1>
<p>In order to play, you need an compatible IP changer and a Tibia client.</p>
<p>Download IP changer <a href="https://github.com/jo3bingham/tibia-ip-changer/releases/latest">HERE</a>.</p>
<p>Download Tibia client <?php echo ($config['client'] / 100); ?> for windows <a href="<?php echo $config['client_download']; ?>">HERE</a>.</p>
<p>Download Tibia client <?php echo ($config['client'] / 100); ?> for linux <a href="<?php echo $config['client_download_linux']; ?>">HERE</a>.</p>
<h2>How to connect and play:</h2>
<ol>
<li>
<a href="<?php echo $config['client_download']; ?>">Download</a> and install the tibia client if you havent already.
</li>
<li>
<a href="https://github.com/jo3bingham/tibia-ip-changer/releases/latest">Download</a> and run the IP changer.
</li>
<li>
In the IP changer, change Client Path to the tibia.exe file where you installed the client.</strong>
</li>
<li>
In the IP changer, write this in the IP field: <?php echo $_SERVER['SERVER_NAME']; ?>
</li>
<li>
Now you can successfully login on the tibia client and play clicking on <strong>Apply</strong>.<br>
If you do not have an account to login with, you need to register an account <a href="register.php">HERE</a>.
</li>
</ol>
<?php
include 'layout/overall/footer.php'; ?>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,745 @@
<?xml version="1.0" encoding="UTF-8"?>
<spells>
<instant group="attack" spellid="62" name="Annihilation" words="exori gran ico" lvl="110" mana="300" prem="1" range="1" needtarget="1" blockwalls="1" needweapon="1" exhaustion="30000" groupcooldown="4000" needlearn="0" script="attack/annihilation.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="attack" spellid="169" name="Apprentice's Strike" words="exori min flam" lvl="8" mana="6" prem="0" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="attack/apprentices strike.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="80" name="Berserk" words="exori" lvl="35" mana="115" prem="1" needweapon="1" exhaustion="4000" groupcooldown="2000" needlearn="0" script="attack/berserk.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="support" spellid="133" name="Blood Rage" words="utito tempo" lvl="60" mana="290" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/blood rage.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="attack" spellid="61" name="Brutal Strike" words="exori ico" lvl="16" mana="30" prem="1" range="1" needtarget="1" blockwalls="1" needweapon="1" exhaustion="6000" groupcooldown="2000" needlearn="0" script="attack/brutal strike.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="support" spellid="90" name="Cancel Invisibility" words="exana ina" lvl="26" mana="200" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/cancel invisibility.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="support" spellid="93" name="Challenge" words="exeta res" lvl="20" mana="30" prem="1" aggressive="0" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/challenge.lua">
<vocation name="Elite Knight" />
</instant>
<instant group="support" spellid="131" name="Charge" words="utani tempo hur" lvl="25" mana="100" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/charge.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="support" spellid="38" name="Creature Illusion" words="utevo res ina" lvl="23" mana="100" aggressive="0" params="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="Illusion">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</instant>
<instant group="healing" spellid="144" name="Cure Bleeding" words="exana kor" lvl="45" mana="30" aggressive="0" selftarget="1" exhaustion="6000" groupcooldown="1000" needlearn="0" script="healing/cure bleeding.lua">
<vocation name="Druid" />
<vocation name="Knight" />
<vocation name="Elder Druid" />
<vocation name="Elite Knight" />
</instant>
<instant group="healing" spellid="145" name="Cure Burning" words="exana flam" lvl="30" mana="30" aggressive="0" selftarget="1" exhaustion="6000" groupcooldown="1000" needlearn="0" script="healing/cure burning.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="healing" spellid="147" name="Cure Curse" words="exana mort" lvl="80" mana="40" aggressive="0" selftarget="1" exhaustion="6000" groupcooldown="1000" needlearn="0" script="healing/cure curse.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="healing" spellid="146" name="Cure Electrification" words="exana vis" lvl="22" mana="30" aggressive="0" selftarget="1" exhaustion="6000" groupcooldown="1000" needlearn="0" script="healing/cure electrification.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="healing" spellid="29" name="Cure Poison" words="exana pox" lvl="10" mana="30" aggressive="0" selftarget="1" exhaustion="6000" groupcooldown="1000" needlearn="0" script="healing/antidote.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Knight" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
<vocation name="Elite Knight" />
</instant>
<instant group="attack" spellid="139" name="Curse" words="utori mort" lvl="75" mana="30" range="3" aggressive="1" blockwalls="1" needtarget="1" exhaustion="50000" groupcooldown="2000" needlearn="0" script="attack/curse.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="attack" spellid="87" name="Death Strike" words="exori mort" lvl="16" mana="20" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="attack/death strike.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="attack" spellid="124" name="Divine Caldera" words="exevo mas san" lvl="50" mana="160" prem="1" selftarget="1" exhaustion="4000" groupcooldown="2000" needlearn="0" script="attack/divine caldera.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="healing" spellid="125" name="Divine Healing" words="exura san" lvl="35" mana="160" prem="0" selftarget="1" aggressive="0" exhaustion="1000" groupcooldown="1000" needlearn="0" script="healing/divine healing.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="attack" spellid="122" name="Divine Missile" words="exori san" lvl="40" mana="20" prem="1" range="4" casterTargetOrDirection="1" needlearn="0" blockwalls="1" exhaustion="2000" groupcooldown="2000" script="attack/divine missile.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="attack" spellid="140" name="Electrify" words="utori vis" lvl="34" mana="30" range="3" aggressive="1" blockwalls="1" needtarget="1" exhaustion="30000" groupcooldown="2000" needlearn="0" script="attack/electrify.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="support" spellid="129" name="Enchant Party" words="utori mas sio" lvl="32" mana="120" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="party/enchant.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="attack" spellid="22" name="Energy Beam" words="exevo vis lux" lvl="23" mana="40" direction="1" exhaustion="4000" groupcooldown="2000" needlearn="0" script="attack/energy beam.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="attack" spellid="88" name="Energy Strike" words="exori vis" lvl="12" mana="20" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="attack/energy strike.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="13" name="Energy Wave" words="exevo vis hur" lvl="38" mana="170" direction="1" exhaustion="8000" groupcooldown="2000" needlearn="0" script="attack/energy wave.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="attack" spellid="142" name="Envenom" words="utori pox" lvl="50" mana="30" range="3" aggressive="1" blockwalls="1" needtarget="1" exhaustion="40000" groupcooldown="2000" needlearn="0" script="attack/envenom.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="118" name="Eternal Winter" words="exevo gran mas frigo" lvl="60" mana="1050" prem="1" selftarget="1" exhaustion="40000" groupcooldown="4000" needlearn="0" script="attack/eternal winter.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="111" name="Ethereal Spear" words="exori con" lvl="23" mana="25" prem="1" range="7" needtarget="1" exhaustion="2000" groupcooldown="2000" blockwalls="1" needlearn="0" script="attack/ethereal spear.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="attack" spellid="105" name="Fierce Berserk" words="exori gran" lvl="90" mana="340" prem="1" needweapon="1" exhaustion="6000" groupcooldown="2000" needlearn="0" script="attack/fierce berserk.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="support" spellid="20" name="Find Person" words="exiva" lvl="8" mana="20" aggressive="0" playernameparam="1" params="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="searchPlayer">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Knight" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
<vocation name="Elite Knight" />
</instant>
<instant group="attack" spellid="19" name="Fire Wave" words="exevo flam hur" lvl="18" mana="25" direction="1" exhaustion="4000" groupcooldown="2000" needlearn="0" script="attack/fire wave.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="attack" spellid="89" name="Flame Strike" words="exori flam" lvl="14" mana="20" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="attack/flame strike.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</instant>
<instant group="support" spellid="42" name="Food" words="exevo pan" lvl="14" mana="120" soul="1" exhaustion="2000" groupcooldown="2000" aggressive="0" needlearn="0" script="support/food.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="59" name="Front Sweep" words="exori min" lvl="70" mana="200" prem="1" needweapon="1" direction="1" exhaustion="6000" groupcooldown="2000" needlearn="0" script="attack/front sweep.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="attack" spellid="23" name="Great Energy Beam" words="exevo gran vis lux" lvl="29" mana="110" direction="1" exhaustion="6000" groupcooldown="2000" needlearn="0" script="attack/great energy beam.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="support" spellid="11" name="Great Light" words="utevo gran lux" lvl="13" mana="60" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/great light.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Knight" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
<vocation name="Elite Knight" />
</instant>
<instant group="attack" spellid="106" name="Groundshaker" words="exori mas" lvl="33" mana="160" prem="1" needweapon="1" exhaustion="8000" groupcooldown="2000" needlearn="0" script="attack/groundshaker.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="support" spellid="6" name="Haste" words="utani hur" lvl="14" mana="60" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/haste.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Knight" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
<vocation name="Elite Knight" />
</instant>
<instant group="healing" spellid="84" name="Heal Friend" words="exura sio" lvl="18" mana="140" prem="1" aggressive="0" blockwalls="1" needtarget="1" playernameparam="1" params="1" exhaustion="1000" groupcooldown="1000" needlearn="0" script="healing/heal friend.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="support" spellid="128" name="Heal Party" words="utura mas sio" lvl="32" mana="120" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="party/heal.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="24" name="Hell's Core" words="exevo gran mas flam" lvl="60" mana="1100" prem="1" exhaustion="40000" groupcooldown="4000" selftarget="1" needlearn="0" script="attack/hells core.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="attack" spellid="143" name="Holy Flash" words="utori san" lvl="70" mana="30" range="3" aggressive="1" blockwalls="1" needtarget="1" exhaustion="40000" groupcooldown="2000" needlearn="0" script="attack/holy flash.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="attack" spellid="112" name="Ice Strike" words="exori frigo" lvl="15" mana="20" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="attack/ice strike.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="121" name="Ice Wave" words="exevo frigo hur" lvl="18" mana="25" direction="1" exhaustion="4000" groupcooldown="2000" needlearn="0" script="attack/ice wave.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="138" name="Ignite" words="utori flam" lvl="26" mana="30" range="3" aggressive="1" blockwalls="1" needtarget="1" exhaustion="30000" groupcooldown="2000" needlearn="0" script="attack/ignite.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="attack" spellid="141" name="Inflict Wound" words="utori kor" lvl="40" mana="30" range="1" aggressive="1" blockwalls="1" needtarget="1" exhaustion="30000" groupcooldown="2000" needlearn="0" script="attack/inflict wound.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="healing" spellid="2" name="Intense Healing" words="exura gran" lvl="20" mana="70" aggressive="0" selftarget="1" exhaustion="1000" groupcooldown="1000" needlearn="0" script="healing/intense healing.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
</instant>
<instant group="healing" spellid="160" name="Intense Recovery" words="utura gran" lvl="100" mana="165" aggressive="0" selftarget="1" exhaustion="60000" groupcooldown="1000" needlearn="0" script="healing/intense recovery.lua">
<vocation name="Paladin" />
<vocation name="Knight" />
<vocation name="Royal Paladin" />
<vocation name="Elite Knight" />
</instant>
<instant group="healing" spellid="158" name="Intense Wound Cleansing" words="exura gran ico" lvl="80" mana="200" prem="1" selftarget="1" aggressive="0" exhaustion="600000" groupcooldown="1000" needlearn="0" script="healing/intense wound cleansing.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="support" spellid="45" name="Invisibility" words="utana vid" lvl="35" mana="440" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/invisible.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</instant>
<instant group="support" spellid="81" name="Levitate" words="exani hur" lvl="12" mana="50" prem="1" aggressive="0" exhaustion="2000" groupcooldown="2000" params="1" needlearn="0" function="Levitate">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Knight" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
<vocation name="Elite Knight" />
</instant>
<instant group="support" spellid="10" name="Light" words="utevo lux" lvl="8" mana="20" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/light.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Knight" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
<vocation name="Elite Knight" />
</instant>
<instant group="healing" spellid="1" name="Light Healing" words="exura" lvl="8" mana="20" aggressive="0" selftarget="1" exhaustion="1000" groupcooldown="1000" needlearn="0" script="healing/light healing.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
</instant>
<instant group="attack" spellid="149" name="Lightning" words="exori amp vis" lvl="55" mana="60" prem="1" range="4" casterTargetOrDirection="1" blockwalls="1" exhaustion="8000" groupcooldown="2000" needlearn="0" script="attack/lightning.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="support" spellid="76" name="Magic Rope" words="exani tera" lvl="9" mana="20" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/magic rope.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Knight" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
<vocation name="Elite Knight" />
</instant>
<instant group="support" spellid="44" name="Magic Shield" words="utamo vita" lvl="14" mana="50" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/magic shield.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</instant>
<instant group="healing" spellid="82" name="Mass Healing" words="exura gran mas res" lvl="36" mana="150" prem="1" aggressive="0" exhaustion="2000" groupcooldown="1000" needlearn="0" script="healing/mass healing.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="148" name="Physical Strike" words="exori moe ico" lvl="16" mana="20" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="attack/physical strike.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="167" name="Practise Fire Wave" words="exevo dis flam hur" lvl="1" mana="5" direction="1" exhaustion="4000" groupcooldown="2000" needlearn="0" script="attack/practise fire wave.lua">
<vocation name="None" />
</instant>
<instant group="healing" spellid="166" name="Practise Healing" words="exura dis" lvl="1" mana="5" aggressive="0" selftarget="1" exhaustion="1000" groupcooldown="1000" needlearn="0" script="healing/practise healing.lua">
<vocation name="None" />
</instant>
<instant group="support" spellid="127" name="Protect Party" words="utamo mas sio" lvl="32" mana="90" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="party/protect.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="support" spellid="132" name="Protector" words="utamo tempo" lvl="55" mana="200" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/protector.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="attack" spellid="119" name="Rage of the Skies" words="exevo gran mas vis" lvl="55" mana="600" selftarget="1" prem="1" exhaustion="40000" groupcooldown="4000" needlearn="0" script="attack/rage of the skies.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="healing" spellid="159" name="Recovery" words="utura" lvl="50" mana="75" aggressive="0" selftarget="1" exhaustion="60000" groupcooldown="1000" needlearn="0" script="healing/recovery.lua">
<vocation name="Paladin" />
<vocation name="Knight" />
<vocation name="Royal Paladin" />
<vocation name="Elite Knight" />
</instant>
<instant group="healing" spellid="36" name="Salvation" words="exura gran san" lvl="60" mana="210" prem="1" selftarget="1" aggressive="0" exhaustion="1000" groupcooldown="1000" needlearn="0" script="healing/salvation.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="support" spellid="135" name="Sharpshooter" words="utito tempo san" lvl="60" mana="450" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/sharpshooter.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="attack" spellid="151" name="Strong Energy Strike" words="exori gran vis" lvl="80" mana="60" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="8000" groupcooldown="2000" needlearn="0" script="attack/strong energy strike.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="attack" spellid="57" name="Strong Ethereal Spear" words="exori gran con" lvl="90" mana="55" prem="1" range="7" needtarget="1" exhaustion="8000" groupcooldown="2000" blockwalls="1" needlearn="0" script="attack/strong ethereal spear.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="attack" spellid="150" name="Strong Flame Strike" words="exori gran flam" lvl="70" mana="60" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="8000" groupcooldown="2000" needlearn="0" script="attack/strong flame strike.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="support" spellid="39" name="Strong Haste" words="utani gran hur" lvl="20" mana="100" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/strong haste.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="152" name="Strong Ice Strike" words="exori gran frigo" lvl="80" mana="60" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="8000" groupcooldown="2000" needlearn="0" script="attack/strong ice strike.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="43" name="Strong Ice Wave" words="exevo gran frigo hur" lvl="40" mana="170" direction="1" exhaustion="8000" groupcooldown="2000" needlearn="0" script="attack/strong ice wave.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="153" name="Strong Terra Strike" words="exori gran tera" lvl="70" mana="60" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="8000" groupcooldown="2000" needlearn="0" script="attack/strong terra strike.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="support" spellid="9" name="Summon Creature" words="utevo res" lvl="25" params="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="summonMonster">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</instant>
<instant group="support" spellid="134" name="Swift Foot" words="utamo tempo san" lvl="55" mana="400" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/swift foot.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</instant>
<instant group="attack" spellid="113" name="Terra Strike" words="exori tera" lvl="13" mana="20" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="attack/terra strike.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="120" name="Terra Wave" words="exevo tera hur" lvl="38" mana="210" direction="1" exhaustion="4000" groupcooldown="2000" needlearn="0" script="attack/terra wave.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="support" spellid="126" name="Train Party" words="utito mas sio" lvl="32" mana="60" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="party/train.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="attack" spellid="155" name="Ultimate Energy Strike" words="exori max vis" lvl="100" mana="100" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="30000" groupcooldown="4000" needlearn="0" script="attack/ultimate energy strike.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="attack" spellid="154" name="Ultimate Flame Strike" words="exori max flam" lvl="90" mana="100" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="30000" groupcooldown="4000" needlearn="0" script="attack/ultimate flame strike.lua">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</instant>
<instant group="healing" spellid="3" name="Ultimate Healing" words="exura vita" lvl="30" mana="160" aggressive="0" selftarget="1" exhaustion="1000" groupcooldown="1000" needlearn="0" script="healing/ultimate healing.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="156" name="Ultimate Ice Strike" words="exori max frigo" lvl="100" mana="100" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="30000" groupcooldown="4000" needlearn="0" script="attack/ultimate ice strike.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="support" spellid="75" name="Ultimate Light" words="utevo vis lux" lvl="26" mana="140" prem="1" aggressive="0" selftarget="1" exhaustion="2000" groupcooldown="2000" needlearn="0" script="support/ultimate light.lua">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</instant>
<instant group="attack" spellid="157" name="Ultimate Terra Strike" words="exori max tera" lvl="90" mana="100" prem="1" range="3" casterTargetOrDirection="1" blockwalls="1" exhaustion="30000" groupcooldown="4000" needlearn="0" script="attack/ultimate terra strike.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<instant group="healing" spellid="123" name="Wound Cleansing" words="exura ico" lvl="10" mana="40" prem="0" selftarget="1" aggressive="0" exhaustion="1000" groupcooldown="1000" needlearn="0" script="healing/wound cleansing.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="attack" spellid="107" name="Whirlwind Throw" words="exori hur" lvl="28" mana="40" prem="1" range="5" needtarget="1" blockwalls="1" needweapon="1" exhaustion="6000" groupcooldown="2000" needlearn="0" script="attack/whirlwind throw.lua">
<vocation name="Knight" />
<vocation name="Elite Knight" />
</instant>
<instant group="attack" spellid="56" name="Wrath of Nature" words="exevo gran mas tera" lvl="55" mana="700" prem="1" selftarget="1" exhaustion="40000" groupcooldown="4000" needlearn="0" script="attack/wrath of nature.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</instant>
<!-- Attack Runes -->
<rune group="attack" spellid="26" name="Poison Field" id="2285" allowfaruse="1" charges="3" lvl="14" maglv="0" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/poison field.lua" />
<rune group="attack" spellid="91" name="Poison Bomb" id="2286" allowfaruse="1" charges="2" lvl="25" maglv="4" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/poison bomb.lua" />
<rune group="attack" spellid="32" name="Poison Wall" id="2289" allowfaruse="1" charges="4" lvl="29" maglv="5" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/poison wall.lua" />
<rune group="attack" spellid="25" name="Fire Field" id="2301" allowfaruse="1" charges="3" lvl="15" maglv="1" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/fire field.lua" />
<rune group="attack" spellid="17" name="Firebomb" id="2305" allowfaruse="1" charges="2" lvl="27" maglv="5" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/fire bomb.lua" />
<rune group="attack" spellid="28" name="Fire Wall" id="2303" allowfaruse="1" charges="4" lvl="33" maglv="6" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/fire wall.lua" />
<rune group="attack" spellid="50" name="Soulfire" id="2308" allowfaruse="1" charges="3" lvl="27" maglv="7" exhaustion="2000" groupcooldown="2000" needtarget="1" blocktype="solid" script="attack/soul fire.lua" />
<rune group="attack" spellid="15" name="Fireball" id="2302" allowfaruse="1" charges="5" lvl="27" maglv="4" exhaustion="2000" groupcooldown="2000" needtarget="1" blocktype="solid" script="attack/fireball.lua" />
<rune group="attack" spellid="16" name="Great Fireball" id="2304" allowfaruse="1" charges="4" lvl="30" maglv="4" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/great fireball.lua" />
<rune group="attack" spellid="27" name="Energy Field" id="2277" allowfaruse="1" charges="3" lvl="18" maglv="3" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/energy field.lua" />
<rune group="attack" spellid="55" name="Energybomb" id="2262" allowfaruse="1" charges="2" lvl="37" maglv="10" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/energy bomb.lua" />
<rune group="attack" spellid="33" name="Energy Wall" id="2279" allowfaruse="1" charges="4" lvl="41" maglv="9" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/energy wall.lua" />
<rune group="attack" spellid="7" name="Light Magic Missile" id="2287" allowfaruse="1" charges="10" lvl="15" exhaustion="2000" groupcooldown="2000" maglv="0" needtarget="1" blocktype="solid" script="attack/light magic missile.lua" />
<!--<rune group="attack" spellid="7" name="Lightest Magic Missile" id="2287" allowfaruse="1" charges="10" lvl="1" exhaustion="2000" groupcooldown="2000" maglv="0" needtarget="1" blocktype="solid" script="attack/lightest magic missile.lua" />-->
<rune group="attack" spellid="8" name="Heavy Magic Missile" id="2311" allowfaruse="1" charges="10" lvl="25" exhaustion="2000" groupcooldown="2000" maglv="3" needtarget="1" blocktype="solid" script="attack/heavy magic missile.lua" />
<rune group="attack" spellid="18" name="Explosion" id="2313" allowfaruse="1" charges="6" lvl="31" maglv="6" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="attack/explosion.lua" />
<rune group="attack" spellid="21" name="Sudden Death" id="2268" allowfaruse="1" charges="3" lvl="45" maglv="15" exhaustion="2000" groupcooldown="2000" needtarget="1" blocktype="solid" script="attack/sudden death.lua" />
<rune group="attack" spellid="114" name="Icicle" id="2271" allowfaruse="1" charges="5" lvl="28" maglv="4" exhaustion="2000" groupcooldown="2000" needtarget="1" script="attack/icicle.lua" />
<rune group="attack" spellid="115" name="Avalanche" id="2274" allowfaruse="1" charges="4" lvl="30" maglv="4" exhaustion="2000" groupcooldown="2000" script="attack/avalanche.lua" />
<rune group="attack" spellid="116" name="Stone Shower" id="2288" allowfaruse="1" charges="4" lvl="28" maglv="4" exhaustion="2000" groupcooldown="2000" script="attack/stone shower.lua" />
<rune group="attack" spellid="117" name="Thunderstorm" id="2315" allowfaruse="1" charges="4" lvl="28" maglv="4" exhaustion="2000" groupcooldown="2000" script="attack/thunderstorm.lua" />
<rune group="attack" spellid="77" name="Stalagmite" id="2292" allowfaruse="1" charges="10" lvl="24" maglv="3" exhaustion="2000" groupcooldown="2000" needtarget="1" script="attack/stalagmite.lua" />
<rune group="attack" spellid="130" name="Holy Missile" id="2295" allowfaruse="1" charges="5" lvl="27" maglv="4" exhaustion="2000" groupcooldown="2000" needtarget="1" blocktype="solid" script="attack/holy missile.lua">
<vocation name="Paladin" />
<vocation name="Royal Paladin" showInDescription="0" />
</rune>
<rune group="attack" spellid="86" name="Magic Wall" id="2293" allowfaruse="1" charges="3" lvl="32" maglv="9" exhaustion="2000" groupcooldown="2000" blocktype="all" script="support/magic wall rune.lua" />
<!-- Healing Runes -->
<rune group="healing" spellid="31" name="Antidote Rune" id="2266" allowfaruse="1" charges="1" lvl="15" maglv="0" exhaustion="2000" groupcooldown="2000" aggressive="0" needtarget="1" blocktype="solid" script="healing/antidote rune.lua" />
<rune group="healing" spellid="4" name="Intense Healing Rune" id="2265" allowfaruse="1" charges="1" lvl="15" maglv="1" exhaustion="2000" groupcooldown="2000" aggressive="0" needtarget="1" blocktype="solid" script="healing/intense healing rune.lua" />
<rune group="healing" spellid="5" name="Ultimate Healing Rune" id="2273" allowfaruse="1" charges="1" lvl="24" maglv="4" exhaustion="2000" groupcooldown="2000" aggressive="0" needtarget="1" blocktype="solid" script="healing/ultimate healing rune.lua" />
<!-- Summon Runes -->
<rune group="support" spellid="12" name="Convince Creature" id="2290" allowfaruse="1" charges="1" lvl="16" maglv="5" exhaustion="2000" groupcooldown="2000" needtarget="1" blocktype="solid" function="convince" />
<rune group="support" spellid="83" name="Animate Dead" id="2316" allowfaruse="1" charges="1" lvl="27" maglv="4" exhaustion="2000" groupcooldown="2000" blocktype="solid" script="support/animate dead rune.lua" />
<!-- Support Runes -->
<rune group="support" spellid="78" name="Desintegrate" id="2310" allowfaruse="0" charges="3" lvl="21" maglv="4" exhaustion="2000" groupcooldown="2000" range="1" script="support/desintegrate rune.lua" />
<rune group="support" spellid="30" name="Destroy Field" id="2261" allowfaruse="1" charges="3" lvl="17" maglv="3" exhaustion="2000" groupcooldown="2000" aggressive="0" range="5" script="support/destroy field rune.lua" />
<rune group="support" spellid="14" name="Chameleon" id="2291" allowfaruse="1" charges="1" lvl="27" maglv="4" exhaustion="2000" groupcooldown="2000" aggressive="0" selftarget="1" blocktype="solid" function="chameleon" />
<rune group="support" spellid="94" name="Wild Growth" id="2269" allowfaruse="1" charges="2" lvl="27" maglv="8" exhaustion="2000" groupcooldown="2000" blocktype="all" script="support/wild growth rune.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" showInDescription="0" />
</rune>
<rune group="support" spellid="54" name="Paralyze" id="2278" allowfaruse="1" charges="1" lvl="54" maglv="18" exhaustion="2000" groupcooldown="2000" mana="1400" needtarget="1" blocktype="solid" script="support/paralyze rune.lua">
<vocation name="Druid" />
<vocation name="Elder Druid" showInDescription="0" />
</rune>
<!-- Conjure Spells -->
<conjure group="support" name="Blank Rune" words="adori blank" lvl="20" mana="50" soul="1" conjureId="2260" conjureCount="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</conjure>
<conjure group="support" spellid="51" name="Conjure Arrow" words="exevo con" lvl="13" mana="100" soul="1" conjureId="2544" conjureCount="10" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureItem">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</conjure>
<conjure group="support" spellid="48" name="Conjure Poisoned Arrow" words="exevo con pox" lvl="16" mana="130" soul="2" conjureId="2545" conjureCount="7" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureItem">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</conjure>
<conjure group="support" spellid="79" name="Conjure Bolt" words="exevo con mort" lvl="17" mana="140" soul="2" prem="1" conjureId="2543" conjureCount="5" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureItem">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</conjure>
<conjure group="support" spellid="108" name="Conjure Sniper Arrow" words="exevo con hur" lvl="24" mana="160" soul="3" prem="1" conjureId="7364" conjureCount="5" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureItem">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</conjure>
<conjure group="support" spellid="49" name="Conjure Explosive Arrow" words="exevo con flam" lvl="25" mana="290" soul="3" conjureId="2546" conjureCount="8" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureItem">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</conjure>
<conjure group="support" spellid="109" name="Conjure Piercing Bolt" words="exevo con grav" lvl="33" mana="180" soul="3" prem="1" conjureId="7363" conjureCount="5" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureItem">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</conjure>
<conjure group="support" spellid="92" name="Enchant Staff" words="exeta vis" lvl="41" mana="80" prem="1" conjureId="2433" reagentId="2401" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureItem">
<vocation name="Master Sorcerer" />
</conjure>
<conjure group="support" spellid="110" name="Enchant Spear" words="exeta con" lvl="45" mana="350" soul="3" prem="1" conjureId="7367" reagentId="2389" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureItem">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</conjure>
<conjure group="support" spellid="95" name="Conjure Power Bolt" words="exevo con vis" lvl="59" mana="700" soul="4" prem="1" conjureId="2547" conjureCount="10" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureItem">
<vocation name="Royal Paladin" />
</conjure>
<conjure name="Poison Field" words="adevo grav pox" lvl="14" mana="200" soul="1" reagentId="2260" conjureId="2285" conjureCount="3" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Light Magic Missile" words="adori min vis" lvl="15" mana="120" soul="1" reagentId="2260" conjureId="2287" conjureCount="10" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<!--<conjure name="Lightest Magic Missile" words="adori dis min vis" lvl="1" mana="5" soul="0" reagentId="2260" conjureId="2287" conjureCount="10" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="None" />
</conjure>-->
<conjure name="Fire Field" words="adevo grav flam" lvl="15" mana="240" soul="1" reagentId="2260" conjureId="2301" conjureCount="3" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Fireball" words="adori flam" lvl="27" mana="460" soul="3" prem="1" reagentId="2260" conjureId="2302" conjureCount="5" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</conjure>
<conjure name="Energy Field" words="adevo grav vis" lvl="18" mana="320" soul="2" reagentId="2260" conjureId="2277" conjureCount="3" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Stalagmite" words="adori tera" lvl="24" mana="400" soul="2" prem="2" reagentId="2260" conjureId="2292" conjureCount="10" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Great Fireball" words="adori mas flam" lvl="30" mana="530" soul="3" reagentId="2260" conjureId="2304" conjureCount="4" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</conjure>
<conjure name="Heavy Magic Missile" words="adori vis" lvl="25" mana="350" soul="2" reagentId="2260" conjureId="2311" conjureCount="10" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Poison Bomb" words="adevo mas pox" lvl="25" mana="520" soul="2" prem="1" reagentId="2260" conjureId="2286" conjureCount="2" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Firebomb" words="adevo mas flam" lvl="27" mana="600" soul="4" reagentId="2260" conjureId="2305" conjureCount="2" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Soulfire" words="adevo res flam" lvl="27" mana="600" soul="3" prem="1" reagentId="2260" conjureId="2308" conjureCount="3" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Poison Wall" words="adevo mas grav pox" lvl="29" mana="640" soul="3" reagentId="2260" conjureId="2289" conjureCount="4" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Explosion" words="adevo mas hur" lvl="31" mana="570" soul="4" reagentId="2260" conjureId="2313" conjureCount="6" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Fire Wall" words="adevo mas grav flam" lvl="33" mana="780" soul="4" reagentId="2260" conjureId="2303" conjureCount="4" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Energybomb" words="adevo mas vis" lvl="37" mana="880" soul="5" prem="1" reagentId="2260" conjureId="2262" conjureCount="2" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</conjure>
<conjure name="Energy Wall" words="adevo mas grav vis" lvl="41" mana="1000" soul="5" reagentId="2260" conjureId="2279" conjureCount="4" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Sudden Death" words="adori gran mort" lvl="45" mana="985" soul="5" reagentId="2260" conjureId="2268" conjureCount="3" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</conjure>
<conjure name="Antidote Rune" words="adana pox" lvl="15" mana="200" soul="1" reagentId="2260" conjureId="2266" conjureCount="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Intense Healing Rune" words="adura gran" lvl="15" mana="240" soul="2" reagentId="2260" conjureId="2265" conjureCount="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Ultimate Healing Rune" words="adura vita" lvl="24" mana="400" soul="3" reagentId="2260" conjureId="2273" conjureCount="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Convince Creature" words="adeta sio" lvl="16" mana="200" soul="3" reagentId="2260" conjureId="2290" conjureCount="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Animate Dead" words="adana mort" lvl="27" mana="600" soul="5" prem="1" reagentId="2260" conjureId="2316" conjureCount="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Chameleon" words="adevo ina" lvl="27" mana="600" soul="2" reagentId="2260" conjureId="2291" conjureCount="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Destroy Field" words="adito grav" lvl="17" mana="120" soul="2" reagentId="2260" conjureId="2261" conjureCount="3" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
</conjure>
<conjure name="Desintegrate" words="adito tera" lvl="21" mana="200" soul="3" prem="1" reagentId="2260" conjureId="2310" conjureCount="3" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Druid" />
<vocation name="Paladin" />
<vocation name="Master Sorcerer" />
<vocation name="Elder Druid" />
<vocation name="Royal Paladin" />
</conjure>
<conjure name="Magic Wall" words="adevo grav tera" lvl="32" mana="750" soul="5" prem="1" reagentId="2260" conjureId="2293" conjureCount="3" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</conjure>
<conjure name="Wild Growth" words="adevo grav vita" lvl="27" mana="600" soul="5" prem="1" reagentId="2260" conjureId="2269" conjureCount="2" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Paralyze" words="adana ani" lvl="54" mana="1400" soul="3" prem="1" reagentId="2260" conjureId="2278" conjureCount="1" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Icicle" words="adori frigo" lvl="28" mana="460" soul="3" prem="1" reagentId="2260" conjureId="2271" conjureCount="5" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Avalanche" words="adori mas frigo" lvl="30" mana="530" soul="3" reagentId="2260" conjureId="2274" conjureCount="4" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Stone Shower" words="adori mas tera" lvl="28" mana="430" soul="3" prem="1" reagentId="2260" conjureId="2288" conjureCount="4" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Druid" />
<vocation name="Elder Druid" />
</conjure>
<conjure name="Thunderstorm" words="adori mas vis" lvl="28" mana="430" soul="3" prem="1" reagentId="2260" conjureId="2315" conjureCount="4" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Sorcerer" />
<vocation name="Master Sorcerer" />
</conjure>
<conjure name="Holy Missile" words="adori san" lvl="27" mana="350" soul="3" prem="1" reagentId="2260" conjureId="2295" conjureCount="5" exhaustion="2000" groupcooldown="2000" needlearn="0" function="conjureRune">
<vocation name="Paladin" />
<vocation name="Royal Paladin" />
</conjure>
<!-- House Spells -->
<instant spellid="71" name="House Guest List" words="aleta sio" selftarget="1" aggressive="0" function="editHouseGuest" />
<instant spellid="72" name="House Subowner List" words="aleta som" selftarget="1" aggressive="0" function="editHouseSubOwner" />
<instant spellid="73" name="House Door List" words="aleta grav" selftarget="1" aggressive="0" function="editHouseDoor" />
<instant spellid="74" name="House Kick" words="alana sio" params="1" aggressive="0" function="houseKick" />
<!-- Monster Spells -->
<instant name="ghastly dragon curse" words="###1" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/ghastly dragon curse.lua" />
<instant name="djinn electrify" words="###2" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/djinn electrify.lua" />
<instant name="energy elemental electrify" words="###3" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/energy elemental electrify.lua" />
<instant name="massive energy elemental electrify" words="###4" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/massive energy elemental electrify.lua" />
<instant name="massive fire elemental soulfire" words="###5" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/massive fire elemental soulfire.lua" />
<instant name="hellfire fighter soulfire" words="###6" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/hellfire fighter soulfire.lua" />
<instant name="hellspawn soulfire" words="###7" aggressive="1" blockwalls="1" range="7" needtarget="1" needlearn="1" script="monster/hellspawn soulfire.lua" />
<instant name="spectre drown" words="###8" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/spectre drown.lua" />
<instant name="phantasm drown" words="###9" direction="1" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/phantasm drown.lua" />
<instant name="undead dragon curse" words="###10" direction="1" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/undead dragon curse.lua" />
<instant name="draken abomination curse" words="###11" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/draken abomination curse.lua" />
<instant name="lancer beetle curse" words="###12" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/lancer beetle curse.lua" />
<instant name="lizard magistratus curse" words="###13" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/lizard magistratus curse.lua" />
<instant name="death blob curse" words="###14" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/death blob curse.lua" />
<instant name="choking fear drown" words="###15" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/choking fear drown.lua" />
<instant name="blightwalker curse" words="###16" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/blightwalker curse.lua" />
<instant name="sea serpent drown" words="###17" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/sea serpent drown.lua" />
<instant name="young sea serpent drown" words="###18" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/young sea serpent drown.lua" />
<instant name="quara constrictor freeze" words="###19" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/quara constrictor freeze.lua" />
<instant name="quara constrictor electrify" words="###20" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/quara constrictor electrify.lua" />
<instant name="cliff strider electrify" words="###21" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/cliff strider electrify.lua" />
<instant name="war golem electrify" words="###22" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/war golem electrify.lua" />
<instant name="souleater drown" words="###23" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/souleater drown.lua" />
<instant name="mutated bat curse" words="###24" direction="1" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/mutated bat curse.lua" />
<instant name="vulcongra soulfire" words="###25" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/vulcongra soulfire.lua" />
<instant name="lava golem soulfire" words="###26" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/lava golem soulfire.lua" />
<instant name="magma crawler soulfire" words="###27" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/magma crawler soulfire.lua" />
<instant name="war golem skill reducer" words="###28" direction="1" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/war golem skill reducer.lua" />
<instant name="silencer skill reducer" words="###29" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/silencer skill reducer.lua" />
<instant name="warlock skill reducer" words="###30" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/warlock skill reducer.lua" />
<instant name="cliff strider skill reducer" words="###31" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/cliff strider skill reducer.lua" />
<instant name="stampor skill reducer" words="###32" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/stampor skill reducer.lua" />
<instant name="diabolic imp skill reducer" words="###33" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/diabolic imp skill reducer.lua" />
<instant name="dark torturer skill reducer" words="###34" direction="1" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/dark torturer skill reducer.lua" />
<instant name="forest fury skill reducer" words="###35" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/forest fury skill reducer.lua" />
<instant name="demon outcast skill reducer" words="###36" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/demon outcast skill reducer.lua" />
<instant name="fury skill reducer" words="###37" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/fury skill reducer.lua" />
<instant name="barbarian brutetamer skill reducer" words="###38" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/barbarian brutetamer skill reducer.lua" />
<instant name="deepling spellsinger skill reducer" words="###39" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/deepling spellsinger skill reducer.lua" />
<instant name="werewolf skill reducer" words="###40" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/werewolf skill reducer.lua" />
<instant name="ice golem skill reducer" words="###41" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/ice golem skill reducer.lua" />
<instant name="feversleep skill reducer" words="###42" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/feversleep skill reducer.lua" />
<instant name="shock head skill reducer 1" words="###43" aggressive="1" blockwalls="1" needtarget="1" needlearn="1" script="monster/shock head skill reducer 1.lua" />
<instant name="shock head skill reducer 2" words="###44" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/shock head skill reducer 2.lua" />
<instant name="enslaved dwarf skill reducer 1" words="###45" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/enslaved dwarf skill reducer 1.lua" />
<instant name="enslaved dwarf skill reducer 2" words="###46" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/enslaved dwarf skill reducer 2.lua" />
<instant name="djinn cancel invisibility" words="###47" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/djinn cancel invisibility.lua" />
<instant name="betrayed wraith skill reducer" words="###48" direction="1" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/betrayed wraith skill reducer.lua" />
<instant name="pirate corsair skill reducer" words="###49" direction="1" aggressive="1" blockwalls="1" needtarget="0" needlearn="1" script="monster/pirate corsair skill reducer.lua" />
</spells>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<stages>
<config enabled="0" />
<stage minlevel="1" maxlevel="8" multiplier="7" />
<stage minlevel="9" maxlevel="20" multiplier="6" />
<stage minlevel="21" maxlevel="50" multiplier="5" />
<stage minlevel="51" maxlevel="100" multiplier="4" />
<stage minlevel="101" multiplier="5" />
</stages>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,91 @@
<?php
$time = time();
if (!isset($version)) $version = '1.6';
if (!function_exists("elapsedTime")) {
function elapsedTime($l_start = false, $l_time = false) {
if ($l_start === false) global $l_start;
if ($l_time === false) global $l_time;
$l_time = microtime(true);
return round(($l_time - $l_start), 4);
}
}
// ALTER TABLE `znote_accounts` ADD `active_email` TINYINT(4) NOT NULL DEFAULT '0' AFTER `active`;
$install = "
<h2>Install:</h2>
<ol>
<li>
<p>
Make sure you have imported TFS database. (OTdir/schema.sql OR OTdir/schemas/mysql.sql OR OTdir/forgottenserver.sql)
</p>
</li>
<li>Import the <a href='/engine/database/znote_schema.sql'>Znote AAC schema</a> to a <b>TFS database in phpmyadmin</b>.</li>
<li>
<p>
Edit config.php with correct mysql connection details.
</p>
</li>
</ol>
";
$connect = new mysqli($config['sqlHost'], $config['sqlUser'], $config['sqlPassword'], $config['sqlDatabase']);
if ($connect->connect_errno) {
die("Failed to connect to MySQL: (" . $connect->connect_errno . ") " . $connect->connect_error . $install);
}
function mysql_znote_escape_string($escapestr) {
global $connect;
return mysqli_real_escape_string($connect, $escapestr);
}
// Select single row from database
function mysql_select_single($query) {
global $connect;
global $aacQueries;
$aacQueries++;
global $accQueriesData;
$accQueriesData[] = "[" . elapsedTime() . "] " . $query;
$result = mysqli_query($connect,$query) or die(var_dump($query)."<br>(query - <font color='red'>SQL error</font>) <br>Type: <b>select_single</b> (select single row from database)<br><br>".mysqli_error($connect));
$row = mysqli_fetch_assoc($result);
return !empty($row) ? $row : false;
}
// Selecting multiple rows from database.
function mysql_select_multi($query){
global $connect;
global $aacQueries;
$aacQueries++;
global $accQueriesData;
$accQueriesData[] = "[" . elapsedTime() . "] " . $query;
$array = array();
$results = mysqli_query($connect,$query) or die(var_dump($query)."<br>(query - <font color='red'>SQL error</font>) <br>Type: <b>select_multi</b> (select multiple rows from database)<br><br>".mysqli_error($connect));
while($row = mysqli_fetch_assoc($results)) {
$array[] = $row;
}
return !empty($array) ? $array : false;
}
//////
// Query database without expecting returned results
// - mysql update
function mysql_update($query){ voidQuery($query); }
// mysql insert
function mysql_insert($query){ voidQuery($query); }
// mysql delete
function mysql_delete($query){ voidQuery($query); }
// Send a void query
function voidQuery($query) {
global $connect;
global $aacQueries;
$aacQueries++;
global $accQueriesData;
$accQueriesData[] = "[" . elapsedTime() . "] " . $query;
mysqli_query($connect,$query) or die(var_dump($query)."<br>(query - <font color='red'>SQL error</font>) <br>Type: <b>voidQuery</b> (voidQuery is used for update, insert or delete from database)<br><br>".mysqli_error($connect));
}
?>

View File

@@ -0,0 +1,312 @@
-- Start of Znote AAC database schema
SET @znote_version = '1.6';
CREATE TABLE IF NOT EXISTS `znote` (
`id` int NOT NULL AUTO_INCREMENT,
`version` varchar(30) NOT NULL COMMENT 'Znote AAC version',
`installed` int NOT NULL,
`cached` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_accounts` (
`id` int NOT NULL AUTO_INCREMENT,
`account_id` int NOT NULL,
`ip` bigint UNSIGNED NOT NULL,
`created` int NOT NULL,
`points` int DEFAULT 0,
`cooldown` int DEFAULT 0,
`active` tinyint NOT NULL DEFAULT '0',
`active_email` tinyint NOT NULL DEFAULT '0',
`activekey` int NOT NULL DEFAULT '0',
`flag` varchar(20) NOT NULL,
`secret` char(16) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_news` (
`id` int NOT NULL AUTO_INCREMENT,
`title` varchar(30) NOT NULL,
`text` text NOT NULL,
`date` int NOT NULL,
`pid` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_images` (
`id` int NOT NULL AUTO_INCREMENT,
`title` varchar(30) NOT NULL,
`desc` text NOT NULL,
`date` int NOT NULL,
`status` int NOT NULL,
`image` varchar(50) NOT NULL,
`delhash` varchar(30) NOT NULL,
`account_id` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_paypal` (
`id` int NOT NULL AUTO_INCREMENT,
`txn_id` varchar(30) NOT NULL,
`email` varchar(255) NOT NULL,
`accid` int NOT NULL,
`price` int NOT NULL,
`points` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_paygol` (
`id` int NOT NULL AUTO_INCREMENT,
`account_id` int NOT NULL,
`price` int NOT NULL,
`points` int NOT NULL,
`message_id` varchar(255) NOT NULL,
`service_id` varchar(255) NOT NULL,
`shortcode` varchar(255) NOT NULL,
`keyword` varchar(255) NOT NULL,
`message` varchar(255) NOT NULL,
`sender` varchar(255) NOT NULL,
`operator` varchar(255) NOT NULL,
`country` varchar(255) NOT NULL,
`currency` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_players` (
`id` int NOT NULL AUTO_INCREMENT,
`player_id` int NOT NULL,
`created` int NOT NULL,
`hide_char` tinyint NOT NULL,
`comment` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_player_reports` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`posx` int NOT NULL,
`posy` int NOT NULL,
`posz` int NOT NULL,
`report_description` varchar(255) NOT NULL,
`date` int NOT NULL,
`status` tinyint NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_changelog` (
`id` int NOT NULL AUTO_INCREMENT,
`text` varchar(255) NOT NULL,
`time` int NOT NULL,
`report_id` int NOT NULL,
`status` tinyint NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_shop` (
`id` int NOT NULL AUTO_INCREMENT,
`type` int NOT NULL,
`itemid` int DEFAULT NULL,
`count` int NOT NULL DEFAULT '1',
`description` varchar(255) NOT NULL,
`points` int NOT NULL DEFAULT '10',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_shop_logs` (
`id` int NOT NULL AUTO_INCREMENT,
`account_id` int NOT NULL,
`player_id` int NOT NULL,
`type` int NOT NULL,
`itemid` int NOT NULL,
`count` int NOT NULL,
`points` int NOT NULL,
`time` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_shop_orders` (
`id` int NOT NULL AUTO_INCREMENT,
`account_id` int NOT NULL,
`type` int NOT NULL,
`itemid` int NOT NULL,
`count` int NOT NULL,
`time` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_visitors` (
`id` int NOT NULL AUTO_INCREMENT,
`ip` bigint NOT NULL,
`value` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_visitors_details` (
`id` int NOT NULL AUTO_INCREMENT,
`ip` bigint NOT NULL,
`time` int NOT NULL,
`type` tinyint NOT NULL,
`account_id` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
-- Forum 1/3 (boards)
CREATE TABLE IF NOT EXISTS `znote_forum` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`access` tinyint NOT NULL,
`closed` tinyint NOT NULL,
`hidden` tinyint NOT NULL,
`guild_id` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
-- Forum 2/3 (threads)
CREATE TABLE IF NOT EXISTS `znote_forum_threads` (
`id` int NOT NULL AUTO_INCREMENT,
`forum_id` int NOT NULL,
`player_id` int NOT NULL,
`player_name` varchar(50) NOT NULL,
`title` varchar(50) NOT NULL,
`text` text NOT NULL,
`created` int NOT NULL,
`updated` int NOT NULL,
`sticky` tinyint NOT NULL,
`hidden` tinyint NOT NULL,
`closed` tinyint NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
-- Forum 3/3 (posts)
CREATE TABLE IF NOT EXISTS `znote_forum_posts` (
`id` int NOT NULL AUTO_INCREMENT,
`thread_id` int NOT NULL,
`player_id` int NOT NULL,
`player_name` varchar(50) NOT NULL,
`text` text NOT NULL,
`created` int NOT NULL,
`updated` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
-- Pending characters for deletion
CREATE TABLE IF NOT EXISTS `znote_deleted_characters` (
`id` int NOT NULL AUTO_INCREMENT,
`original_account_id` int NOT NULL,
`character_name` varchar(255) NOT NULL,
`time` datetime NOT NULL,
`done` tinyint NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_guild_wars` (
`id` int NOT NULL AUTO_INCREMENT,
`limit` int NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
-- Helpdesk system
CREATE TABLE IF NOT EXISTS `znote_tickets` (
`id` int NOT NULL AUTO_INCREMENT,
`owner` int NOT NULL,
`username` varchar(32) CHARACTER SET latin1 NOT NULL,
`subject` text CHARACTER SET latin1 NOT NULL,
`message` text CHARACTER SET latin1 NOT NULL,
`ip` bigint NOT NULL,
`creation` int NOT NULL,
`status` varchar(20) CHARACTER SET latin1 NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_tickets_replies` (
`id` int NOT NULL AUTO_INCREMENT,
`tid` int NOT NULL,
`username` varchar(32) CHARACTER SET latin1 NOT NULL,
`message` text CHARACTER SET latin1 NOT NULL,
`created` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `znote_global_storage` (
`key` varchar(32) NOT NULL,
`value` TEXT NOT NULL,
UNIQUE (`key`)
) ENGINE=InnoDB;
-- Character auction system
CREATE TABLE IF NOT EXISTS `znote_auction_player` (
`id` int NOT NULL AUTO_INCREMENT,
`player_id` int NOT NULL,
`original_account_id` int NOT NULL,
`bidder_account_id` int NOT NULL,
`time_begin` int NOT NULL,
`time_end` int NOT NULL,
`price` int NOT NULL,
`bid` int NOT NULL,
`deposit` int NOT NULL,
`sold` tinyint NOT NULL,
`claimed` tinyint NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
-- Populate basic info
INSERT INTO `znote` (`version`, `installed`) VALUES
(@znote_version, UNIX_TIMESTAMP(CURDATE()));
-- Add default forum boards
INSERT INTO `znote_forum` (`name`, `access`, `closed`, `hidden`, `guild_id`) VALUES
('Staff Board', '4', '0', '0', '0'),
('Tutors Board', '2', '0', '0', '0'),
('Discussion', '1', '0', '0', '0'),
('Feedback', '1', '0', '1', '0');
-- Convert existing accounts in database to be Znote AAC compatible
INSERT INTO `znote_accounts` (`account_id`, `ip`, `created`, `flag`)
SELECT
`a`.`id` AS `account_id`,
0 AS `ip`,
UNIX_TIMESTAMP(CURDATE()) AS `created`,
'' AS `flag`
FROM `accounts` AS `a`
LEFT JOIN `znote_accounts` AS `z`
ON `a`.`id` = `z`.`account_id`
WHERE `z`.`created` IS NULL;
-- Convert existing players in database to be Znote AAC compatible
INSERT INTO `znote_players` (`player_id`, `created`, `hide_char`, `comment`)
SELECT
`p`.`id` AS `player_id`,
UNIX_TIMESTAMP(CURDATE()) AS `created`,
0 AS `hide_char`,
'' AS `comment`
FROM `players` AS `p`
LEFT JOIN `znote_players` AS `z`
ON `p`.`id` = `z`.`player_id`
WHERE `z`.`created` IS NULL;
-- Delete duplicate account records
DELETE `d` FROM `znote_accounts` AS `d`
INNER JOIN (
SELECT `i`.`account_id`,
MAX(`i`.`id`) AS `retain`
FROM `znote_accounts` AS `i`
GROUP BY `i`.`account_id`
HAVING COUNT(`i`.`id`) > 1
) AS `x`
ON `d`.`account_id` = `x`.`account_id`
AND `d`.`id` != `x`.`retain`;
-- Delete duplicate player records
DELETE `d` FROM `znote_players` AS `d`
INNER JOIN (
SELECT `i`.`player_id`,
MAX(`i`.`id`) AS `retain`
FROM `znote_players` AS `i`
GROUP BY `i`.`player_id`
HAVING COUNT(`i`.`id`) > 1
) AS `x`
ON `d`.`player_id` = `x`.`player_id`
AND `d`.`id` != `x`.`retain`;
-- End of Znote AAC database schema

View File

@@ -0,0 +1,8 @@
<footer>
&copy; Znote AAC.
<?php
$finish = microtime(true);
$total_time = round(($finish - $start), 4);
echo 'Server date and clock is: '. getClock(false, true) .' Page generated in '. $total_time .' seconds. ';
?>
</footer>

View File

@@ -0,0 +1,167 @@
<?php
class Cache
{
protected $_file = false;
protected $_lifespan = 0;
protected $_content;
protected $_memory = false;
protected $_canMemory = false;
const EXT = '.cache.php';
/**
* @param string $file
* @access public
* @return void
**/
public function __construct($file) {
$cfg = config('cache');
$this->setExpiration($cfg['lifespan']);
if (function_exists('apcu_fetch')) {
$this->_canMemory = true;
$this->_memory = $cfg['memory'];
}
$this->_file = $file . self::EXT;
if (!$this->_memory && $cfg['memory']) die("
<p><strong>Configuration error!</strong>
<br>Cannot save cache to memory, but it is configured to do so.
<br>You need to enable PHP extension APCu to enable memory cache.
<br>Install it or set \$config['cache']['memory'] to false!
<br><strong>Ubuntu install:</strong> sudo apt install php-apcu</p>
");
}
/**
* Sets the cache expiration limit (IMPORTANT NOTE: seconds, NOT ms!).
*
* @param integer $span
* @access public
* @return void
**/
public function setExpiration($span) {
$this->_lifespan = $span;
}
/**
* Enable or disable memory RAM storage.
*
* @param bool $bool
* @access public
* @return bool $status
**/
public function useMemory($bool) {
if ($bool and $this->_canMemory) {
$this->_memory = true;
return true;
}
$this->_memory = false;
return false;
}
/**
* Set the content you'd like to cache.
*
* @param mixed $content
* @access public
* @return void
**/
public function setContent($content) {
$this->_content = (!$this->_memory && strtolower(gettype($content)) == 'array') ? json_encode($content) : $content;
}
/**
* Validates whether it is time to refresh the cache data or not.
*
* @access public
* @return boolean
**/
public function hasExpired() {
if ($this->_memory) {
return !apcu_exists($this->_file);
}
if (is_file($this->_file) && time() < filemtime($this->_file) + $this->_lifespan) {
return false;
}
return true;
}
/**
* Returns remaining time before scoreboard will update itself.
*
* @access public
* @return integer
**/
public function remainingTime() {
$remaining = 0;
if ($this->_memory) {
if (apcu_exists($this->_file)) {
$meta = apcu_cache_info();
foreach ($meta['cache_list'] AS $item) {
if ($item['info'] == $this->_file) {
$remaining = ($item['creation_time'] + $item['ttl']) - time();
return ($remaining > 0) ? $remaining : 0;
}
}
}
return $remaining;
}
if (!$this->hasExpired()) {
$remaining = (filemtime($this->_file) + $this->_lifespan) - time();
}
return $remaining;
}
/**
* Saves the content into its appropriate cache file.
*
* @access public
* @return void
**/
public function save() {
if ($this->_memory) {
return apcu_store($this->_file, $this->_content, $this->_lifespan);
}
$handle = fopen($this->_file, 'w');
fwrite($handle, $this->_content);
fclose($handle);
}
/**
* Loads the content from a specified cache file.
*
* @access public
* @return mixed
**/
public function load() {
if ($this->_memory) {
return apcu_fetch($this->_file);
}
if (!is_file($this->_file)) {
return false;
}
ob_start();
include_once($this->_file);
$content = ob_get_clean();
if (!isset($content) && strlen($content) == 0) {
return false;
}
if ($content = json_decode($content, true)) {
return (array) $content;
} else {
return $content;
}
}
}

View File

@@ -0,0 +1,613 @@
<?php
function setSession($key, $data) {
global $sessionPrefix;
$_SESSION[$sessionPrefix.$key] = $data;
}
function getSession($key) {
global $sessionPrefix;
return (isset($_SESSION[$sessionPrefix.$key])) ? $_SESSION[$sessionPrefix.$key] : false;
}
// Fetch and sanitize POST and GET values
function getValue($value) {
return (!empty($value)) ? sanitize($value) : false;
}
function SendGet($getArray, $location = 'error.php') {
$string = "";
$count = 0;
foreach ($getArray as $getKey => $getValue) {
if ($count > 0) $string .= '&';
$string .= "{$getKey}={$getValue}";
}
header("Location: {$location}?{$string}");
exit();
}
// Sweet error reporting
function data_dump($print = false, $var = false, $title = false) {
if ($title !== false) echo "<pre><font color='red' size='5'>$title</font><br>";
else echo '<pre>';
if ($print !== false) {
echo 'Print: - ';
print_r($print);
echo "<br>";
}
if ($var !== false) {
echo 'Var_dump: - ';
var_dump($var);
}
echo '</pre><br>';
}
function accountAccess($accountId, $TFS) {
$accountId = (int)$accountId;
$access = 0;
// TFS 0.3/4
$yourChars = mysql_select_multi("SELECT `name`, `group_id`, `account_id` FROM `players` WHERE `account_id`='$accountId';");
if ($yourChars !== false) {
foreach ($yourChars as $char) {
if ($TFS === 'TFS_03' || $TFS === 'OTHIRE') {
if ($char['group_id'] > $access) $access = $char['group_id'];
} else {
if ($char['group_id'] > 1) {
if ($access == 0) {
$acc = mysql_select_single("SELECT `type` FROM `accounts` WHERE `id`='". $char['account_id'] ."' LIMIT 1;");
$access = $acc['type'];
}
}
}
}
if ($access == 0) $access++;
return $access;
} else return false;
//
}
// Generate recovery key
function generate_recovery_key($lenght) {
$lenght = (int)$lenght;
$tmp = rand(1000, 9000);
$tmp += time();
$tmp = sha1($tmp);
$results = '';
for ($i = 0; $i < $lenght; $i++) $results = $results.''.$tmp[$i];
return $results;
}
// Calculate discount
function calculate_discount($orig, $new) {
$orig = (int)$orig;
$new = (int)$new;
$tmp = '';
if ($new >= $orig) {
if ($new != $orig) {
$calc = ($new/$orig) - 1;
$calc *= 100;
$tmp = '+'. floor($calc) .'%';
} else $tmp = '0%';
} else {
$calc = 1 - ($new/$orig);
$calc *= 100;
$tmp = '-'. floor($calc) .'%';
}
return $tmp;
}
// Proper URLs
function url($path = false) {
$folder = dirname($_SERVER['SCRIPT_NAME']);
return config('site_url') . '/' . $path;
}
function getCache() {
$results = mysql_select_single("SELECT `cached` FROM `znote`;");
return ($results !== false) ? $results['cached'] : false;
}
function setCache($time) {
$time = (int)$time;
mysql_update("UPDATE `znote` set `cached`='$time'");
}
// Get visitor basic data
function znote_visitors_get_data() {
return mysql_select_multi("SELECT `ip`, `value` FROM `znote_visitors` ORDER BY `id` DESC LIMIT 1000;");
}
// Set visitor basic data
function znote_visitor_set_data($visitor_data) {
$exist = false;
$ip = getIPLong();
foreach ((array)$visitor_data as $row) {
if ($ip == $row['ip']) {
$exist = true;
$value = $row['value'];
}
}
if ($exist && isset($value)) {
// Update the value
$value++;
mysql_update("UPDATE `znote_visitors` SET `value` = '$value' WHERE `ip` = '$ip'");
} else {
// Insert new row
mysql_insert("INSERT INTO `znote_visitors` (`ip`, `value`) VALUES ('$ip', '1')");
}
}
// Get visitor basic data
function znote_visitors_get_detailed_data($cache_time) {
$period = (int)time() - (int)$cache_time;
return mysql_select_multi("SELECT `ip`, `time`, `type`, `account_id` FROM `znote_visitors_details` WHERE `time` >= '$period' LIMIT 0, 50");
}
function znote_visitor_insert_detailed_data($type) {
$type = (int)$type;
/*
type 0 = normal visits
type 1 = register form
type 2 = character creation
type 3 = fetch highscores
type 4 = search character
*/
$time = time();
$ip = getIPLong();
if (user_logged_in()) {
$acc = (int)getSession('user_id');
mysql_insert("INSERT INTO `znote_visitors_details` (`ip`, `time`, `type`, `account_id`) VALUES ('$ip', '$time', '$type', '$acc')");
} else mysql_insert("INSERT INTO `znote_visitors_details` (`ip`, `time`, `type`, `account_id`) VALUES ('$ip', '$time', '$type', '0')");
}
function something () {
// Make acc data compatible:
$ip = getIPLong();
}
// Secret token
function create_token() {
echo 'Checking whether to create token or not<br />';
#if (empty($_SESSION['token'])) {
echo 'Creating token<br />';
$token = sha1(uniqid(time(), true));
$token2 = $token;
var_dump($token, $token2);
$_SESSION['token'] = $token2;
#}
echo "<input type=\"hidden\" name=\"token\" value=\"". $_SESSION['token'] ."\" />";
}
function reset_token() {
echo 'Reseting token<br />';
unset($_SESSION['token']);
}
// Time based functions
// 60 seconds to 1 minute
function second_to_minute($seconds) {
return ($seconds / 60);
}
// 1 minute to 60 seconds
function minute_to_seconds($minutes) {
return ($minutes * 60);
}
// 60 minutes to 1 hour
function minute_to_hour($minutes) {
return ($minutes / 60);
}
// 1 hour to 60 minutes
function hour_to_minute($hours) {
return ($hour * 60);
}
// seconds / 60 / 60 = hours.
function seconds_to_hours($seconds) {
$minutes = second_to_minute($seconds);
$hours = minute_to_hour($minutes);
return $hours;
}
function remaining_seconds_to_clock($seconds) {
return date("(H:i)",time() + $seconds);
}
/**
* Check if name contains more than configured max words
*
* @param string $string
* @return string|boolean
*/
function validate_name($string) {
return (str_word_count(trim($string)) > config('maxW')) ? false : trim($string);
}
// Checks if an IPv4(or localhost IPv6) address is valid
function validate_ip($ip) {
$ipL = safeIp2Long($ip);
$ipR = long2ip((int)$ipL);
if ($ip === $ipR) {
return true;
} elseif ($ip=='::1') {
return true;
} else {
return false;
}
}
// Fetch a config value. Etc config('vocations') will return vocation array from config.php.
function config($value) {
global $config;
return $config[$value];
}
// Some functions uses several configurations from config.php, so it sounds
// smarter to give them the whole array instead of calling the function all the time.
function fullConfig() {
global $config;
return $config;
}
// Capitalize Every Word In String.
function format_character_name($name) {
return ucwords(strtolower($name));
}
// Gets you the actual IP address even from users behind ISP proxies and so on.
function getIP() {
/*
$IP = '';
if (getenv('HTTP_CLIENT_IP')) {
$IP =getenv('HTTP_CLIENT_IP');
} elseif (getenv('HTTP_X_FORWARDED_FOR')) {
$IP =getenv('HTTP_X_FORWARDED_FOR');
} elseif (getenv('HTTP_X_FORWARDED')) {
$IP =getenv('HTTP_X_FORWARDED');
} elseif (getenv('HTTP_FORWARDED_FOR')) {
$IP =getenv('HTTP_FORWARDED_FOR');
} elseif (getenv('HTTP_FORWARDED')) {
$IP = getenv('HTTP_FORWARDED');
} else {
$IP = $_SERVER['REMOTE_ADDR'];
} */
return $_SERVER['REMOTE_ADDR'];
}
function safeIp2Long($ip) {
return sprintf('%u', ip2long($ip));
}
// Gets you the actual IP address even from users in long type
function getIPLong() {
return safeIp2Long(getIP());
}
// Deprecated, just use count($array) instead.
function array_length($ar) {
$r = 1;
foreach($ar as $a) {
$r++;
}
return $r;
}
// Parameter: level, returns experience for that level from an experience table.
function level_to_experience($level) {
return 50/3*(pow($level, 3) - 6*pow($level, 2) + 17*$level - 12);
}
// Parameter: players.hide_char returns: Status word inside a font with class identifier so it can be designed later on by CSS.
function hide_char_to_name($id) {
$id = (int)$id;
if ($id == 1) {
return 'hidden';
} else {
return 'visible';
}
}
// Parameter: players.online returns: Status word inside a font with class identifier so it can be designed later on by CSS.
function online_id_to_name($id) {
$id = (int)$id;
if ($id == 1) {
return '<font class="status_online">ONLINE</font>';
} else {
return '<font class="status_offline">offline</font>';
}
}
// Parameter: players.vocation_id. Returns: Configured vocation name.
function vocation_id_to_name($id) {
$vocations = config('vocations');
return (isset($vocations[$id]['name'])) ? $vocations[$id]['name'] : "{$id} - Unknown";
}
// Parameter: players.name. Returns: Configured vocation id.
function vocation_name_to_id($name) {
$vocations = config('vocations');
foreach ($vocations as $id => $vocation)
if ($vocation['name'] == $name)
return $id;
return false;
}
// Parameter: players.group_id. Returns: Configured group name.
function group_id_to_name($id) {
$positions = config('ingame_positions');
return ($positions[$id] >= 0) ? $positions[$id] : false;
}
function gender_exist($gender) {
// Range of allowed gender ids, fromid toid
if ($gender >= 0 && $gender <= 1) {
return true;
} else {
return false;
}
}
function skillid_to_name($skillid) {
$skillname = array(
0 => 'fist fighting',
1 => 'club fighting',
2 => 'sword fighting',
3 => 'axe fighting',
4 => 'distance fighting',
5 => 'shielding',
6 => 'fishing',
7 => 'experience', // Hardcoded, does not actually exist in database as a skillid.
8 => 'magic level' // Hardcoded, does not actually exist in database as a skillid.
);
return ($skillname[$skillid] >= 0) ? $skillname[$skillid] : false;
}
// Parameter: players.town_id. Returns: Configured town name.
function town_id_to_name($id) {
$towns = config('towns');
return (array_key_exists($id, $towns)) ? $towns[$id] : 'Missing Town';
}
// Unless you have an internal mail server then mail sending will not be supported in this version.
function email($to, $subject, $body) {
mail($to, $subject, $body, 'From: TEST');
}
function logged_in_redirect() {
if (user_logged_in() === true) {
header('Location: myaccount.php');
}
}
function protect_page() {
if (user_logged_in() === false) {
header('Location: protected.php');
exit();
}
}
// When function is called, you will be redirected to protect_page and deny access to rest of page, as long as you are not admin.
function admin_only($user_data) {
// Chris way
$gotAccess = is_admin($user_data);
if ($gotAccess == false) {
logged_in_redirect();
exit();
}
}
function is_admin($user_data) {
if (config('ServerEngine') === 'OTHIRE')
return in_array($user_data['id'], config('page_admin_access')) ? true : false;
else
return in_array($user_data['name'], config('page_admin_access')) ? true : false;
}
function array_sanitize(&$item) {
$item = htmlentities(strip_tags(mysql_znote_escape_string($item)));
}
function sanitize($data) {
return htmlentities(strip_tags(mysql_znote_escape_string($data)));
}
function output_errors($errors) {
return '<ul><li>'. implode('</li><li>', $errors) .'</li></ul>';
}
// Resize images
function resize_imagex($file, $width, $height) {
list($w, $h) = getimagesize($file['tmp']);
$ratio = max($width/$w, $height/$h);
$h = ceil($height / $ratio);
$x = ($w - $width / $ratio) / 2;
$w = ceil($width / $ratio);
$path = 'engine/guildimg/'.$file['new_name'];
$imgString = file_get_contents($file['tmp']);
$image = imagecreatefromstring($imgString);
$tmp = imagecreatetruecolor($width, $height);
imagecopyresampled($tmp, $image,
0, 0,
$x, 0,
$width, $height,
$w, $h);
imagegif($tmp, $path);
imagedestroy($image);
imagedestroy($tmp);
return true;
}
// Guild logo upload security
function check_image($image) {
$image_data = array('new_name' => $_GET['name'].'.gif', 'name' => $image['name'], 'tmp' => $image['tmp_name'], 'error' => $image['error'], 'size' => $image['size'], 'type' => $image['type']);
// First security check, quite useless but still do its job
if ($image_data['type'] === 'image/gif') {
// Second security check, lets go
$check = getimagesize($image_data['tmp']);
if ($check) {
// Third
if ($check['mime'] === 'image/gif') {
$path_info = pathinfo($image_data['name']);
// Last one
if ($path_info['extension'] === 'gif') {
// Resize image
$img = resize_imagex($image_data, 100, 100);
if ($img) {
header('Location: guilds.php?name='. $_GET['name']);
exit();
}
} else {
header('Location: guilds.php?error=Only gif images accepted, you uploaded:['.$path_info['extension'].'].&name='. $_GET['name']);
exit();
}
} else {
header('Location: guilds.php?error=Only gif images accepted, you uploaded:['.$check['mime'].'].&name='. $_GET['name']);
exit();
}
} else {
header('Location: guilds.php?error=Uploaded image is invalid.&name='. $_GET['name']);
exit();
}
} else {
header('Location: guilds.php?error=Only gif images are accepted, you uploaded:['.$image_data['type'].'].&name='. $_GET['name']);
exit();
}
}
// Check guild logo
function logo_exists($guild) {
$guild = sanitize($guild);
if (file_exists('engine/guildimg/'.$guild.'.gif')) {
echo'engine/guildimg/'.$guild.'.gif';
} else {
echo'engine/guildimg/default@logo.gif';
}
}
function generateRandomString($length = 16) {
$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
function verifyGoogleReCaptcha($postResponse = null) {
if(!isset($postResponse) || empty($postResponse)) {
return false;
}
$recaptcha_api_url = 'https://www.google.com/recaptcha/api/siteverify';
$secretKey = config('captcha_secret_key');
$ip = $_SERVER['REMOTE_ADDR'];
$params = 'secret='.$secretKey.'&response='.$postResponse.'&remoteip='.$ip;
$useCurl = config('captcha_use_curl');
if($useCurl) {
$curl_connection = curl_init($recaptcha_api_url);
curl_setopt($curl_connection, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($curl_connection, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl_connection, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl_connection, CURLOPT_FOLLOWLOCATION, 0);
curl_setopt($curl_connection, CURLOPT_POSTFIELDS, $params);
$response = curl_exec($curl_connection);
curl_close($curl_connection);
} else {
$response = file_get_contents($recaptcha_api_url . '?' . $params);
}
$json = json_decode($response);
return isset($json->success) && $json->success;
}
// html encoding function (encode any string to valid UTF-8 HTML)
function hhb_tohtml(/*string*/ $str)/*:string*/ {
return htmlentities($str, ENT_QUOTES | ENT_HTML401 | ENT_SUBSTITUTE | ENT_DISALLOWED, 'UTF-8', true);
}
// php5-compatibile version of php7's random_bytes()
// $crypto_strong: a boolean value that determines if the algorithm used was "cryptographically strong"
function random_bytes_compat($length, &$crypto_strong = null) {
$crypto_strong = false;
if (!is_int($length)) {
throw new \InvalidArgumentException("argument 1 must be an int, is " . gettype($length));
}
if ($length < 0) {
throw new \InvalidArgumentException("length must be >= 0");
}
if (is_callable("random_bytes")) {
$crypto_strong = true;
return random_bytes($length);
}
if (is_callable("openssl_random_pseudo_bytes")) {
return openssl_random_pseudo_bytes($length, $crypto_strong);
}
$ret = @file_get_contents("/dev/urandom", false, null, 0, $length);
if (is_string($ret) && strlen($ret) === $length) {
$crypto_strong = true;
return $ret;
}
// fallback to non-cryptographically-secure mt_rand() implementation...
$crypto_strong = false;
$ret = "";
for ($i = 0; $i < $length; ++$i) {
$ret .= chr(mt_rand(0, 255));
}
return $ret;
}
// hash_equals legacy support < 5.6
if(!function_exists('hash_equals')) {
function hash_equals($str1, $str2) {
if(strlen($str1) != strlen($str2)) {
return false;
}
$res = $str1 ^ $str2;
$ret = 0;
for($i = strlen($res) - 1; $i >= 0; $i--) {
$ret |= ord($res[$i]);
}
return !$ret;
}
}
?>

View File

@@ -0,0 +1,34 @@
<?php
/* Returns a PHP array $id => 'name'
$items = getItemList();
echo $items[2160]; // Returns 'Crystal Coin'
*/
function getItemList() {
return parseItems();
}
function getItemById($id) {
$items = parseItems();
if(isset($items[$id])) {
return $items[$id];
}
return false;
}
function parseItems() {
$file = Config('server_path') . '/data/items/items.xml';
if (file_exists($file)) {
$itemList = array();
$items = simplexml_load_file($file);
// Create our parsed item list
foreach ($items->children() as $item) {
if ($item['id'] && $item['name'] != NULL) {
$itemList[(int)$item['id']] = (string)$item['name'];
}
}
return $itemList;
}
return $file;
}
?>

View File

@@ -0,0 +1,99 @@
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
class Mail {
protected $_config = false;
/**
* @param array $config
* @access public
* @return void
**/
public function __construct($config) {
$this->_config = $config;
}
/**
* Sets the cache expiration limit (IMPORTANT NOTE: seconds, NOT ms!).
*
* @param string $to, string $title, string $text, string $accname
* @access public
* @return boolean
**/
public function sendMail($to, $title, $text, $accname = '') {
//SMTP needs accurate times, and the PHP time zone MUST be set
//This should be done in your php.ini, but this is how to do it if you don't have access to that
//date_default_timezone_set('Etc/UTC');
require_once __DIR__.'/../../PHPMailer/src/Exception.php';
require_once __DIR__.'/../../PHPMailer/src/PHPMailer.php';
require_once __DIR__.'/../../PHPMailer/src/SMTP.php';
//Create a new PHPMailer instance
$mail = new PHPMailer();
//Tell PHPMailer to use SMTP
$mail->isSMTP();
//Enable SMTP debugging
// 0 = off (for production use)
// 1 = client messages
// 2 = client and server messages
$mail->SMTPDebug = ($this->_config['debug']) ? 2 : 0;
//Ask for HTML-friendly debug output
$mail->Debugoutput = 'html';
//Set the hostname of the mail server
$mail->Host = $this->_config['host'];
//Set the SMTP port number - likely to be 25, 465 or 587
$mail->Port = $this->_config['port'];
//Whether to use SMTP authentication
$mail->SMTPAuth = true;
$mail->SMTPSecure = $this->_config['securityType'];
//Username to use for SMTP authentication
$mail->Username = $this->_config['username'];
//Password to use for SMTP authentication
$mail->Password = $this->_config['password'];
//Set who the message is to be sent from
$mail->setFrom($this->_config['email'], $this->_config['fromName']);
//Set who the message is to be sent to
$mail->addAddress($to, $accname);
//Set the subject line
$mail->Subject = $title;
// Body
$mail->Body = $text;
// Convert HTML -> plain for legacy mail recievers
// Create new lines instead of <br> html tags.
$text = str_replace("<br>", "\n", $text);
$text = str_replace("<br\>", "\n", $text);
$text = str_replace("<br \>", "\n", $text);
// Then get rid of the rest of the html tags.
$text = strip_tags($text);
//Replace the plain text body with one created manually
$mail->AltBody = $text;
//send the message, check for errors
$status = false;
if (!$mail->send()) {
echo "Mailer Error: " . $mail->ErrorInfo;
exit();
} else {
$status = true;
}
return $status;
}
}

View File

@@ -0,0 +1,285 @@
<?php
/** https://github.com/Voronenko/PHPOTP/blob/08cda9cb9c30b7242cf0b3a9100a6244a2874927/code/base32static.php
* Encode in Base32 based on RFC 4648.
* Requires 20% more space than base64
* Great for case-insensitive filesystems like Windows and URL's (except for = char which can be excluded using the pad option for urls)
*
* @package default
* @author Bryan Ruiz
**/
class Base32Static {
private static $map = array(
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 7
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
'=' // padding character
);
private static $flippedMap = array(
'A'=>'0', 'B'=>'1', 'C'=>'2', 'D'=>'3', 'E'=>'4', 'F'=>'5', 'G'=>'6', 'H'=>'7',
'I'=>'8', 'J'=>'9', 'K'=>'10', 'L'=>'11', 'M'=>'12', 'N'=>'13', 'O'=>'14', 'P'=>'15',
'Q'=>'16', 'R'=>'17', 'S'=>'18', 'T'=>'19', 'U'=>'20', 'V'=>'21', 'W'=>'22', 'X'=>'23',
'Y'=>'24', 'Z'=>'25', '2'=>'26', '3'=>'27', '4'=>'28', '5'=>'29', '6'=>'30', '7'=>'31'
);
/**
* Use padding false when encoding for urls
*
* @return base32 encoded string
* @author Bryan Ruiz
**/
public static function encode($input, $padding = true) {
if(empty($input)) return "";
$input = str_split($input);
$binaryString = "";
for($i = 0; $i < count($input); $i++) {
$binaryString .= str_pad(base_convert(ord($input[$i]), 10, 2), 8, '0', STR_PAD_LEFT);
}
$fiveBitBinaryArray = str_split($binaryString, 5);
$base32 = "";
$i=0;
while($i < count($fiveBitBinaryArray)) {
$base32 .= self::$map[base_convert(str_pad($fiveBitBinaryArray[$i], 5,'0'), 2, 10)];
$i++;
}
if($padding && ($x = strlen($binaryString) % 40) != 0) {
if($x == 8) $base32 .= str_repeat(self::$map[32], 6);
else if($x == 16) $base32 .= str_repeat(self::$map[32], 4);
else if($x == 24) $base32 .= str_repeat(self::$map[32], 3);
else if($x == 32) $base32 .= self::$map[32];
}
return $base32;
}
public static function decode($input) {
if(empty($input)) return;
$paddingCharCount = substr_count($input, self::$map[32]);
$allowedValues = array(6,4,3,1,0);
if(!in_array($paddingCharCount, $allowedValues)) return false;
for($i=0; $i<4; $i++){
if($paddingCharCount == $allowedValues[$i] &&
substr($input, -($allowedValues[$i])) != str_repeat(self::$map[32], $allowedValues[$i])) return false;
}
$input = str_replace('=','', $input);
$input = str_split($input);
$binaryString = "";
for($i=0; $i < count($input); $i = $i+8) {
$x = "";
if(!in_array($input[$i], self::$map)) return false;
for($j=0; $j < 8; $j++) {
$x .= str_pad(base_convert(@self::$flippedMap[@$input[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
}
$eightBits = str_split($x, 8);
for($z = 0; $z < count($eightBits); $z++) {
$binaryString .= ( ($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48 ) ? $y:"";
}
}
return $binaryString;
}
}
// http://www.faqs.org/rfcs/rfc6238.html
// https://github.com/Voronenko/PHPOTP/blob/08cda9cb9c30b7242cf0b3a9100a6244a2874927/code/rfc6238.php
// Local changes: http -> https, consistent indentation, 200x200 -> 300x300 QR image size, PHP end tag
class TokenAuth6238 {
/**
* verify
*
* @param string $secretkey Secret clue (base 32).
* @return bool True if success, false if failure
*/
public static function verify($secretkey, $code, $rangein30s = 3) {
$key = base32static::decode($secretkey);
$unixtimestamp = time()/30;
for($i=-($rangein30s); $i<=$rangein30s; $i++) {
$checktime = (int)($unixtimestamp+$i);
$thiskey = self::oath_hotp($key, $checktime);
if ((int)$code == self::oath_truncate($thiskey,6)) {
return true;
}
}
return false;
}
public static function getTokenCode($secretkey,$rangein30s = 3) {
$result = "";
$key = base32static::decode($secretkey);
$unixtimestamp = time()/30;
for($i=-($rangein30s); $i<=$rangein30s; $i++) {
$checktime = (int)($unixtimestamp+$i);
$thiskey = self::oath_hotp($key, $checktime);
$result = $result." # ".self::oath_truncate($thiskey,6);
}
return $result;
}
public static function getTokenCodeDebug($secretkey,$rangein30s = 3) {
$result = "";
print "<br/>SecretKey: $secretkey <br/>";
$key = base32static::decode($secretkey);
print "Key(base 32 decode): $key <br/>";
$unixtimestamp = time()/30;
print "UnixTimeStamp (time()/30): $unixtimestamp <br/>";
for($i=-($rangein30s); $i<=$rangein30s; $i++) {
$checktime = (int)($unixtimestamp+$i);
print "Calculating oath_hotp from (int)(unixtimestamp +- 30sec offset): $checktime basing on secret key<br/>";
$thiskey = self::oath_hotp($key, $checktime, true);
print "======================================================<br/>";
print "CheckTime: $checktime oath_hotp:".$thiskey."<br/>";
$result = $result." # ".self::oath_truncate($thiskey,6,true);
}
return $result;
}
public static function getBarCodeUrl($username, $domain, $secretkey, $issuer) {
$url = "https://chart.apis.google.com/chart";
$url = $url."?chs=300x300&chld=M|0&cht=qr&chl=otpauth://totp/";
$url = $url.$username . "@" . $domain . "%3Fsecret%3D" . $secretkey . '%26issuer%3D' . rawurlencode($issuer);
return $url;
}
public static function generateRandomClue($length = 16) {
$b32 = "234567QWERTYUIOPASDFGHJKLZXCVBNM";
$s = "";
for ($i = 0; $i < $length; $i++)
$s .= $b32[rand(0,31)];
return $s;
}
private static function hotp_tobytestream($key) {
$result = array();
$last = strlen($key);
for ($i = 0; $i < $last; $i = $i + 2) {
$x = $key[$i] + $key[$i + 1];
$x = strtoupper($x);
$x = hexdec($x);
$result = $result.chr($x);
}
return $result;
}
private static function oath_hotp ($key, $counter, $debug=false) {
$result = "";
$orgcounter = $counter;
$cur_counter = array(0,0,0,0,0,0,0,0);
if ($debug) {
print "Packing counter $counter (".dechex($counter).")into binary string - pay attention to hex representation of key and binary representation<br/>";
}
for($i=7;$i>=0;$i--) { // C for unsigned char, * for repeating to the end of the input data
$cur_counter[$i] = pack ('C*', $counter);
if ($debug) {
print $cur_counter[$i]."(".dechex(ord($cur_counter[$i])).")"." from $counter <br/>";
}
$counter = $counter >> 8;
}
if ($debug) {
foreach ($cur_counter as $char) {
print ord($char) . " ";
}
print "<br/>";
}
$binary = implode($cur_counter);
// Pad to 8 characters
str_pad($binary, 8, chr(0), STR_PAD_LEFT);
if ($debug) {
print "Prior to HMAC calculation pad with zero on the left until 8 characters.<br/>";
print "Calculate sha1 HMAC(Hash-based Message Authentication Code http://en.wikipedia.org/wiki/HMAC).<br/>";
print "hash_hmac ('sha1', $binary, $key)<br/>";
}
$result = hash_hmac ('sha1', $binary, $key);
if ($debug) {
print "Result: $result <br/>";
}
return $result;
}
private static function oath_truncate($hash, $length = 6, $debug=false) {
$result="";
// Convert to dec
if($debug) {
print "converting hex hash into characters<br/>";
}
$hashcharacters = str_split($hash,2);
if($debug) {
print_r($hashcharacters);
print "<br/>and convert to decimals:<br/>";
}
for ($j=0; $j<count($hashcharacters); $j++) {
$hmac_result[]=hexdec($hashcharacters[$j]);
}
if($debug) {
print_r($hmac_result);
}
// http://php.net/manual/ru/function.hash-hmac.php
// adopted from brent at thebrent dot net 21-May-2009 08:17 comment
$offset = $hmac_result[19] & 0xf;
if($debug) {
print "Calculating offset as 19th element of hmac:".$hmac_result[19]."<br/>";
print "offset:".$offset;
}
$result = (
(($hmac_result[$offset+0] & 0x7f) << 24 ) |
(($hmac_result[$offset+1] & 0xff) << 16 ) |
(($hmac_result[$offset+2] & 0xff) << 8 ) |
($hmac_result[$offset+3] & 0xff)
) % pow(10,$length);
return $result;
}
}
?>

View File

@@ -0,0 +1,89 @@
<?php
// List of characters: $, {}, []
class Token {
public static function generate() {
$token = sha1(uniqid(time(), true));
$_SESSION['token'] = $token;
}
/**
* Displays a random token to prevent CSRF attacks.
*
* @access public
* @static true
* @return void
**/
public static function create() {
echo '<input type="hidden" name="token" value="' . self::get() . '" />';
}
/**
* Returns the active token, if there is one.
*
* @access public
* @static true
* @return mixed
**/
public static function get() {
return isset($_SESSION['token']) ? $_SESSION['token'] : false;
}
/**
* Validates whether the active token is valid or not.
*
* @param string $post
* @access public
* @static true
* @return boolean
**/
public static function isValid($post) {
if (config('use_token')) {
// Token doesn't exist yet, return false.
if (!self::get()) {
return false;
}
// Token was invalid, return false.
if ($post == $_SESSION['old_token'] || $post == $_SESSION['token']) {
//self::_reset();
return true;
} else {
return false;
}
} else {
return true;
}
}
/**
* Destroys the active token.
*
* @access protected
* @static true
* @return void
**/
protected static function _reset() {
unset($_SESSION['token']);
}
/**
* Displays information on both the post token and the session token.
*
* @param string $post
* @access public
* @static true
* @return void
**/
public static function debug($post) {
echo '<pre>', var_dump(array(
'post' => $post,
'old_token' => $_SESSION['old_token'],
'token' => self::get()
)), '</pre>';
}
}
?>

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@@ -0,0 +1,175 @@
<?php if (version_compare(phpversion(), '5.6', '<')) die('PHP version 5.6 or higher is required.');
$l_time = microtime(true);
$l_start = $l_time;
function elapsedTime($l_start = false, $l_time = false) {
if ($l_start === false) global $l_start;
if ($l_time === false) global $l_time;
$l_time = explode(' ', microtime());
$l_finish = $l_time[1] + $l_time[0];
return round(($l_finish - $l_start), 4);
}
$time = time();
$version = '1.6';
$aacQueries = 0;
$accQueriesData = array();
session_start();
ob_start();
require_once 'config.php';
$sessionPrefix = $config['session_prefix'];
if ($config['paypal']['enabled'] || $config['use_captcha']) {
$curlcheck = extension_loaded('curl');
if (!$curlcheck) die("php cURL is not enabled. It is required to for paypal or captcha services.<br>1. Find your php.ini file.<br>2. Uncomment extension=php_curl<br>Restart web server.<br><br><b>If you don't want this then disable paypal & use_captcha in config.php.</b>");
}
if ($config['use_captcha'] && !extension_loaded('openssl')) {
die("php openSSL is not enabled. It is required to for captcha services.<br>1. Find your php.ini file.<br>2. Uncomment extension=php_openssl<br>Restart web server.<br><br><b>If you don't want this then disable use_captcha in config.php.</b>");
}
// References ( & ) works as an alias for a variable,
// they point to the same memmory, instead of duplicating it.
if (!isset($config['TFSVersion'])) $config['TFSVersion'] = &$config['ServerEngine'];
if (!isset($config['ServerEngine'])) $config['ServerEngine'] = &$config['TFSVersion'];
require_once 'database/connect.php';
require_once 'function/general.php';
require_once 'function/users.php';
require_once 'function/cache.php';
require_once 'function/mail.php';
require_once 'function/token.php';
require_once 'function/itemparser/itemlistparser.php';
if (isset($_SESSION['token'])) {
$_SESSION['old_token'] = $_SESSION['token'];
}
Token::generate();
$tfs_10_hasPremDays = true; // https://github.com/otland/forgottenserver/pull/2813
if (user_logged_in() === true) {
$session_user_id = getSession('user_id');
if ($config['ServerEngine'] !== 'OTHIRE') {
if ($config['ServerEngine'] == 'TFS_10') {
$hasPremDays = mysql_select_single("SHOW COLUMNS from `accounts` WHERE `Field` = 'premdays'");
if ($hasPremDays === false) {
$tfs_10_hasPremDays = false;
$user_data = user_data($session_user_id, 'id', 'name', 'password', 'email', 'premium_ends_at');
$user_data['premdays'] = ($user_data['premium_ends_at'] - time() > 0) ? floor(($user_data['premium_ends_at'] - time()) / 86400) : 0;
} else {
$user_data = user_data($session_user_id, 'id', 'name', 'password', 'email', 'premdays');
}
} else {
$user_data = user_data($session_user_id, 'id', 'name', 'password', 'email', 'premdays');
}
} else
$user_data = user_data($session_user_id, 'id', 'password', 'email', 'premend');
$user_znote_data = user_znote_account_data($session_user_id, 'ip', 'created', 'points', 'cooldown', 'flag' ,'active_email');
}
$errors = array();
// Log IP
if ($config['log_ip']) {
$visitor_config = $config['ip_security'];
$flush = $config['flush_ip_logs'];
if ($flush != false) {
$timef = $time - $flush;
if (getCache() < $timef) {
$timef = $time - $visitor_config['time_period'];
mysql_delete("DELETE FROM znote_visitors_details WHERE time <= '$timef'");
setCache($time);
}
}
$visitor_data = znote_visitors_get_data();
znote_visitor_set_data($visitor_data); // update or insert data
znote_visitor_insert_detailed_data(0); // detailed data
$visitor_detailed = znote_visitors_get_detailed_data($visitor_config['time_period']);
// max activity
$v_activity = 0;
$v_register = 0;
$v_highscore = 0;
$v_c_char = 0;
$v_s_char = 0;
$v_form = 0;
foreach ((array)$visitor_detailed as $v_d) {
// Activity
if ($v_d['ip'] == getIPLong()) {
// count each type of visit
switch ($v_d['type']) {
case 0: // max activity
$v_activity++;
break;
case 1: // account registered
$v_register++;
$v_form++;
break;
case 2: // character creations
$v_c_char++;
$v_form++;
break;
case 3: // Highscore fetched
$v_highscore++;
$v_form++;
break;
case 4: // character searched
$v_s_char++;
$v_form++;
break;
case 5: // Other forms (login.?)
$v_form++;
break;
}
}
}
// Deny access if activity is too high
if ($v_activity > $visitor_config['max_activity']) die("Chill down. Your web activity is too big. max_activity");
if ($v_register > $visitor_config['max_account']) die("Chill down. You can't create multiple accounts that fast. max_account");
if ($v_c_char > $visitor_config['max_character']) die("Chill down. Your web activity is too big. max_character");
if ($v_form > $visitor_config['max_post']) die("Chill down. Your web activity is too big. max_post");
//var_dump($v_activity, $v_register, $v_highscore, $v_c_char, $v_s_char, $v_form);
//echo ' <--- IP logging activity past 10 seconds.';
}
// Sub page override system
$filename = explode('/', $_SERVER['SCRIPT_NAME']);
$filename = $filename[count($filename) - 1];
$page_filename = str_replace('.php', '', $filename);
if ($config['allowSubPages']) {
require_once 'layout/sub.php';
if (isset($subpages) && !empty($subpages)) {
foreach ($subpages as $page) {
if ($page['override'] && $page['file'] === $filename) {
require_once 'layout/overall/header.php';
require_once 'layout/sub/'.$page['file'];
require_once 'layout/overall/footer.php';
exit;
}
}
} else {
?>
<div style="background-color: white; padding: 20px; width: 100%; float:left;">
<h2 style="color: black;">Old layout!</h2>
<p style="color: black;">The layout is running an outdated sub system which is not compatible with this version of Znote AAC.</p>
<p style="color: black;">The file /layout/sub.php is outdated.
<br>Please update it to look like <a style="color: orange;" target="_BLANK" href="https://github.com/Znote/ZnoteAAC/blob/master/layout/sub.php">THIS.</a>
</p>
</div>
<?php
}
}
?>

Some files were not shown because too many files have changed in this diff Show More