| @@ -0,0 +1,122 @@ | |||
| 1 | + | local deathListEnabled = true | |
| 2 | + | local maxDeathRecords = 5 | |
| 3 | + | ||
| 4 | + | local function sendWarStatus(guildId, enemyGuildId, warId, playerName, killerName) | |
| 5 | + | local guild, enemyGuild = Guild(guildId), Guild(enemyGuildId) | |
| 6 | + | if not guild or not enemyGuild then | |
| 7 | + | return | |
| 8 | + | end | |
| 9 | + | ||
| 10 | + | 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) | |
| 11 | + | if resultId then | |
| 12 | + | ||
| 13 | + | local guild1_kills = result.getNumber(resultId, "guild1_kills") | |
| 14 | + | local guild2_kills = result.getNumber(resultId, "guild2_kills") | |
| 15 | + | local limit = result.getNumber(resultId, "limit") | |
| 16 | + | result.free(resultId) | |
| 17 | + | ||
| 18 | + | local members = guild:getMembersOnline() | |
| 19 | + | for i = 1, #members do | |
| 20 | + | 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) | |
| 21 | + | end | |
| 22 | + | ||
| 23 | + | local enemyMembers = enemyGuild:getMembersOnline() | |
| 24 | + | for i = 1, #enemyMembers do | |
| 25 | + | 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) | |
| 26 | + | end | |
| 27 | + | ||
| 28 | + | if guild1_kills >= limit or guild2_kills >= limit then | |
| 29 | + | db.query("UPDATE `guild_wars` SET `status` = 4, `ended` = " .. os.time() .. " WHERE `status` = 1 AND `id` = " .. warId) | |
| 30 | + | Game.broadcastMessage(string.format("%s has just won the war against %s.", guild:getName(), enemyGuild:getName()), MESSAGE_EVENT_ADVANCE) | |
| 31 | + | end | |
| 32 | + | end | |
| 33 | + | end | |
| 34 | + | ||
| 35 | + | function onDeath(player, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified) | |
| 36 | + | local playerId = player:getId() | |
| 37 | + | if nextUseStaminaTime[playerId] then | |
| 38 | + | nextUseStaminaTime[playerId] = nil | |
| 39 | + | end | |
| 40 | + | ||
| 41 | + | player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are dead.") | |
| 42 | + | if not deathListEnabled then | |
| 43 | + | return | |
| 44 | + | end | |
| 45 | + | ||
| 46 | + | local byPlayer = 0 | |
| 47 | + | local killerName | |
| 48 | + | if killer then | |
| 49 | + | if killer:isPlayer() then | |
| 50 | + | byPlayer = 1 | |
| 51 | + | else | |
| 52 | + | local master = killer:getMaster() | |
| 53 | + | if master and master ~= killer and master:isPlayer() then | |
| 54 | + | killer = master | |
| 55 | + | byPlayer = 1 | |
| 56 | + | end | |
| 57 | + | end | |
| 58 | + | killerName = killer:getName() | |
| 59 | + | else | |
| 60 | + | killerName = "field item" | |
| 61 | + | end | |
| 62 | + | ||
| 63 | + | local byPlayerMostDamage = 0 | |
| 64 | + | local mostDamageKillerName | |
| 65 | + | if mostDamageKiller then | |
| 66 | + | if mostDamageKiller:isPlayer() then | |
| 67 | + | byPlayerMostDamage = 1 | |
| 68 | + | else | |
| 69 | + | local master = mostDamageKiller:getMaster() | |
| 70 | + | if master and master ~= mostDamageKiller and master:isPlayer() then | |
| 71 | + | mostDamageKiller = master | |
| 72 | + | byPlayerMostDamage = 1 | |
| 73 | + | end | |
| 74 | + | end | |
| 75 | + | mostDamageName = mostDamageKiller:getName() | |
| 76 | + | else | |
| 77 | + | mostDamageName = "field item" | |
| 78 | + | end | |
| 79 | + | ||
| 80 | + | local playerGuid = player:getGuid() | |
| 81 | + | 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) .. ")") | |
| 82 | + | local resultId = db.storeQuery("SELECT `player_id` FROM `player_deaths` WHERE `player_id` = " .. playerGuid) | |
| 83 | + | ||
| 84 | + | local deathRecords = 0 | |
| 85 | + | local tmpResultId = resultId | |
| 86 | + | while tmpResultId ~= false do | |
| 87 | + | tmpResultId = result.next(resultId) | |
| 88 | + | deathRecords = deathRecords + 1 | |
| 89 | + | end | |
| 90 | + | ||
| 91 | + | if resultId ~= false then | |
| 92 | + | result.free(resultId) | |
| 93 | + | end | |
| 94 | + | ||
| 95 | + | local limit = deathRecords - maxDeathRecords | |
| 96 | + | if limit > 0 then | |
| 97 | + | db.asyncQuery("DELETE FROM `player_deaths` WHERE `player_id` = " .. playerGuid .. " ORDER BY `time` LIMIT " .. limit) | |
| 98 | + | end | |
| 99 | + | ||
| 100 | + | if byPlayer == 1 then | |
| 101 | + | local targetGuild = player:getGuild() | |
| 102 | + | targetGuild = targetGuild and targetGuild:getId() or 0 | |
| 103 | + | if targetGuild ~= 0 then | |
| 104 | + | local killerGuild = killer:getGuild() | |
| 105 | + | killerGuild = killerGuild and killerGuild:getId() or 0 | |
| 106 | + | if killerGuild ~= 0 and targetGuild ~= killerGuild and isInWar(playerId, killer:getId()) then | |
| 107 | + | local warId = false | |
| 108 | + | resultId = db.storeQuery("SELECT `id` FROM `guild_wars` WHERE `status` = 1 AND ((`guild1` = " .. killerGuild .. " AND `guild2` = " .. targetGuild .. ") OR (`guild1` = " .. targetGuild .. " AND `guild2` = " .. killerGuild .. "))") | |
| 109 | + | if resultId ~= false then | |
| 110 | + | warId = result.getNumber(resultId, "id") | |
| 111 | + | result.free(resultId) | |
| 112 | + | end | |
| 113 | + | ||
| 114 | + | if warId ~= false then | |
| 115 | + | local playerName = player:getName() | |
| 116 | + | db.asyncQuery("INSERT INTO `guildwar_kills` (`killer`, `target`, `killerguild`, `targetguild`, `time`, `warid`) VALUES (" .. db.escapeString(killerName) .. ", " .. db.escapeString(playerName) .. ", " .. killerGuild .. ", " .. targetGuild .. ", " .. os.time() .. ", " .. warId .. ")") | |
| 117 | + | addEvent(sendWarStatus, 1000, killerGuild, targetGuild, warId, playerName, killerName) | |
| 118 | + | end | |
| 119 | + | end | |
| 120 | + | end | |
| 121 | + | end | |
| 122 | + | end |
| @@ -0,0 +1,4 @@ | |||
| 1 | + | 1. Add below line to XML file: data/creaturescripts/creaturescripts.xml | |
| 2 | + | <event type="login" name="znote_syncoutfits" script="syncoutfit.lua" /> | |
| 3 | + | ||
| 4 | + | 2. Place Lua file syncoutfit.lua in folder: data/creaturescripts/scripts/ |
| @@ -0,0 +1,46 @@ | |||
| 1 | + | -- Sync outfits that player own with Znote AAC | |
| 2 | + | -- So its possible to see which full sets player | |
| 3 | + | -- has in characterprofile.php | |
| 4 | + | ||
| 5 | + | znote_outfit_list = { | |
| 6 | + | { -- Female outfits | |
| 7 | + | 136, 137, 138, 139, 140, 141, 142, 147, 148, | |
| 8 | + | 149, 150, 155, 156, 157, 158, 252, 269, 270, | |
| 9 | + | 279, 288, 324, 329, 336, 366, 431, 433, 464, | |
| 10 | + | 466, 471, 513, 514, 542, 575, 578, 618, 620, | |
| 11 | + | 632, 635, 636, 664, 666, 683, 694, 696, 698, | |
| 12 | + | 724, 732, 745, 749, 759, 845, 852, 874, 885, | |
| 13 | + | 900, 973, 975, 1020, 1024, 1043, 1050, 1057, | |
| 14 | + | 1070, 1095, 1103, 1128, 1147, 1162, 1174, | |
| 15 | + | 1187, 1203, 1205, 1207, 1211, 1246, 1244, | |
| 16 | + | 1252, 1271, 1280, 1283, 1289, 1293, 1332 | |
| 17 | + | }, | |
| 18 | + | { -- Male outfits | |
| 19 | + | 128, 129, 130, 131, 132, 133, 134, 143, 144, | |
| 20 | + | 145, 146, 151, 152, 153, 154, 251, 268, 273, | |
| 21 | + | 278, 289, 325, 328, 335, 367, 430, 432, 463, | |
| 22 | + | 465, 472, 512, 516, 541, 574, 577, 610, 619, | |
| 23 | + | 633, 634, 637, 665, 667, 684, 695, 697, 699, | |
| 24 | + | 725, 733, 746, 750, 760, 846, 853, 873, 884, | |
| 25 | + | 899, 908, 931, 955, 957, 962, 964, 966, 968, | |
| 26 | + | 970, 972, 974, 1021, 1023, 1042, 1051, 1056, | |
| 27 | + | 1069, 1094, 1102, 1127, 1146, 1161, 1173, | |
| 28 | + | 1186, 1202, 1204, 1206, 1210, 1245, 1243, | |
| 29 | + | 1251, 1270, 1279, 1282, 1288, 1292, 1331 | |
| 30 | + | } | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | function onLogin(player) | |
| 34 | + | -- storage_value + 1000 storages (highest outfit id) must not be used in other script. | |
| 35 | + | -- Must be identical to Znote AAC config.php: $config['EQ_shower'] -> storage_value | |
| 36 | + | local storage_value = 10000 | |
| 37 | + | -- Loop through outfits | |
| 38 | + | for _, outfit in pairs(znote_outfit_list[player:getSex() + 1]) do | |
| 39 | + | if player:hasOutfit(outfit,3) then | |
| 40 | + | if player:getStorageValue(storage_value + outfit) ~= 3 then | |
| 41 | + | player:setStorageValue(storage_value + outfit, 3) | |
| 42 | + | end | |
| 43 | + | end | |
| 44 | + | end | |
| 45 | + | return true | |
| 46 | + | end |
| @@ -0,0 +1,64 @@ | |||
| 1 | + | -- getEternalStorage and setEternalStorage | |
| 2 | + | -- can be added to data/global.lua if you want to use eternal storage for another purpose than this. | |
| 3 | + | -- Regular TFS global storage values get reset every time server reboots. This does not. | |
| 4 | + | local function getEternalStorage(key, parser) | |
| 5 | + | local value = result.getString(db.storeQuery("SELECT `value` FROM `znote_global_storage` WHERE `key` = ".. key .. ";"), "value") | |
| 6 | + | if not value then | |
| 7 | + | if parser then | |
| 8 | + | return false | |
| 9 | + | else | |
| 10 | + | return -1 | |
| 11 | + | end | |
| 12 | + | end | |
| 13 | + | result.free(value) | |
| 14 | + | return tonumber(value) or value | |
| 15 | + | end | |
| 16 | + | ||
| 17 | + | local function setEternalStorage(key, value) | |
| 18 | + | if getEternalStorage(key, true) then | |
| 19 | + | db.query("UPDATE `znote_global_storage` SET `value` = '".. value .. "' WHERE `key` = ".. key .. ";") | |
| 20 | + | else | |
| 21 | + | db.query("INSERT INTO `znote_global_storage` (`key`, `value`) VALUES (".. key ..", ".. value ..");") | |
| 22 | + | end | |
| 23 | + | return true | |
| 24 | + | end | |
| 25 | + | ||
| 26 | + | -- SQL Query to execute: -- | |
| 27 | + | --[[ | |
| 28 | + | ALTER TABLE `znote_players` ADD `exphist_lastexp` BIGINT NOT NULL DEFAULT '0', | |
| 29 | + | ADD `exphist1` BIGINT NOT NULL DEFAULT '0', | |
| 30 | + | ADD `exphist2` BIGINT NOT NULL DEFAULT '0', | |
| 31 | + | ADD `exphist3` BIGINT NOT NULL DEFAULT '0', | |
| 32 | + | ADD `exphist4` BIGINT NOT NULL DEFAULT '0', | |
| 33 | + | ADD `exphist5` BIGINT NOT NULL DEFAULT '0', | |
| 34 | + | ADD `exphist6` BIGINT NOT NULL DEFAULT '0', | |
| 35 | + | ADD `exphist7` BIGINT NOT NULL DEFAULT '0', | |
| 36 | + | ADD `onlinetimetoday` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 37 | + | ADD `onlinetime1` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 38 | + | ADD `onlinetime2` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 39 | + | ADD `onlinetime3` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 40 | + | ADD `onlinetime4` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 41 | + | ADD `onlinetime5` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 42 | + | ADD `onlinetime6` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 43 | + | ADD `onlinetime7` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 44 | + | ADD `onlinetimeall` INT UNSIGNED NOT NULL DEFAULT '0'; | |
| 45 | + | ]]-- | |
| 46 | + | ||
| 47 | + | -- after that execute: -- | |
| 48 | + | --[[ | |
| 49 | + | UPDATE `znote_players` AS `z` INNER JOIN `players` AS `p` ON `p`.`id`=`z`.`player_id` SET `z`.`exphist_lastexp`=`p`.`experience`; | |
| 50 | + | ]]-- | |
| 51 | + | ||
| 52 | + | -- TFS 1.X (data/globalevents.xml) | |
| 53 | + | -- <!-- Power Gamers --> | |
| 54 | + | -- <globalevent name="PowerGamers" interval="60000" script="powergamers.lua"/> | |
| 55 | + | ||
| 56 | + | function onThink(interval, lastExecution, thinkInterval) | |
| 57 | + | if tonumber(os.date("%d")) ~= getEternalStorage(23856) then | |
| 58 | + | setEternalStorage(23856, (tonumber(os.date("%d")))) | |
| 59 | + | db.query("UPDATE `znote_players` SET `onlinetime7`=`onlinetime6`, `onlinetime6`=`onlinetime5`, `onlinetime5`=`onlinetime4`, `onlinetime4`=`onlinetime3`, `onlinetime3`=`onlinetime2`, `onlinetime2`=`onlinetime1`, `onlinetime1`=`onlinetimetoday`, `onlinetimetoday`=0;") | |
| 60 | + | 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`;") | |
| 61 | + | end | |
| 62 | + | 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`)") | |
| 63 | + | return true | |
| 64 | + | end |
| @@ -0,0 +1,156 @@ | |||
| 1 | + | -- <globalevent name="Znote Shop" interval="30000" script="znoteshop.lua"/> | |
| 2 | + | -- Znote Auto Shop v2.1 for Znote AAC on TFS 1.2+ | |
| 3 | + | function onThink(interval, lastExecution) | |
| 4 | + | local shopTypes = {1,5,6,7} | |
| 5 | + | local orderQuery = db.storeQuery([[ | |
| 6 | + | SELECT | |
| 7 | + | MIN(`po`.`player_id`) AS `player_id`, | |
| 8 | + | `shop`.`id`, | |
| 9 | + | `shop`.`type`, | |
| 10 | + | `shop`.`itemid`, | |
| 11 | + | `shop`.`count` | |
| 12 | + | FROM `players_online` AS `po` | |
| 13 | + | INNER JOIN `players` AS `p` | |
| 14 | + | ON `po`.`player_id` = `p`.`id` | |
| 15 | + | INNER JOIN `znote_shop_orders` AS `shop` | |
| 16 | + | ON `p`.`account_id` = `shop`.`account_id` | |
| 17 | + | WHERE `shop`.`type` IN(]] .. table.concat(shopTypes, ",") .. [[) | |
| 18 | + | GROUP BY `shop`.`id` | |
| 19 | + | ]]) | |
| 20 | + | -- Detect if we got any results | |
| 21 | + | if orderQuery ~= false then | |
| 22 | + | local type_desc = { | |
| 23 | + | "itemids", | |
| 24 | + | "pending premium (skip)", | |
| 25 | + | "pending gender change (skip)", | |
| 26 | + | "pending character name change (skip)", | |
| 27 | + | "Outfit and addons", | |
| 28 | + | "Mounts", | |
| 29 | + | "Instant house purchase" | |
| 30 | + | } | |
| 31 | + | repeat | |
| 32 | + | local player_id = result.getNumber(orderQuery, 'player_id') | |
| 33 | + | local orderId = result.getNumber(orderQuery, 'id') | |
| 34 | + | local orderType = result.getNumber(orderQuery, 'type') | |
| 35 | + | local orderItemId = result.getNumber(orderQuery, 'itemid') | |
| 36 | + | local orderCount = result.getNumber(orderQuery, 'count') | |
| 37 | + | local served = false | |
| 38 | + | ||
| 39 | + | local player = Player(player_id) | |
| 40 | + | if player ~= nil then | |
| 41 | + | ||
| 42 | + | local description = "Unknown or custom type" | |
| 43 | + | if type_desc[orderType] ~= nil then | |
| 44 | + | description = type_desc[orderType] | |
| 45 | + | end | |
| 46 | + | print("Processing type "..orderType..": ".. description) | |
| 47 | + | print("Processing shop order for: [".. player:getName() .."] type "..orderType..": ".. description) | |
| 48 | + | ||
| 49 | + | local tile = Tile(player:getPosition()) | |
| 50 | + | if tile ~= nil and tile:hasFlag(TILESTATE_PROTECTIONZONE) then | |
| 51 | + | -- ORDER TYPE 1 (Regular item shop products) | |
| 52 | + | if orderType == 1 then | |
| 53 | + | served = true | |
| 54 | + | local itemType = ItemType(orderItemId) | |
| 55 | + | -- Get weight | |
| 56 | + | if player:getFreeCapacity() >= itemType:getWeight(orderCount) then | |
| 57 | + | local backpack = player:getSlotItem(CONST_SLOT_BACKPACK) | |
| 58 | + | -- variable = (condition) and (return if true) or (return if false) | |
| 59 | + | local needslots = itemType:isStackable() and math.floor(orderCount / 100) + 1 or orderCount | |
| 60 | + | if backpack ~= nil and backpack:getEmptySlots(false) >= needslots then | |
| 61 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";") | |
| 62 | + | player:addItem(orderItemId, orderCount) | |
| 63 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. "!") | |
| 64 | + | print("Process complete. [".. player:getName() .."] has received " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".") | |
| 65 | + | else -- not enough slots | |
| 66 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "Your main backpack is full. You need to free up "..needslots.." available slots to get " .. orderCount .. " " .. ItemType(orderItemId):getName() .. "!") | |
| 67 | + | print("Process canceled. [".. player:getName() .."] need more space in his backpack to get " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".") | |
| 68 | + | end | |
| 69 | + | else -- not enough cap | |
| 70 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You need more CAP to carry this order!") | |
| 71 | + | print("Process canceled. [".. player:getName() .."] need more cap to carry " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".") | |
| 72 | + | end | |
| 73 | + | end | |
| 74 | + | ||
| 75 | + | -- ORDER TYPE 5 (Outfit and addon) | |
| 76 | + | if orderType == 5 then | |
| 77 | + | served = true | |
| 78 | + | ||
| 79 | + | local itemid = orderItemId | |
| 80 | + | local outfits = {} | |
| 81 | + | ||
| 82 | + | if itemid > 1000 then | |
| 83 | + | local first = math.floor(itemid/1000) | |
| 84 | + | table.insert(outfits, first) | |
| 85 | + | itemid = itemid - (first * 1000) | |
| 86 | + | end | |
| 87 | + | table.insert(outfits, itemid) | |
| 88 | + | ||
| 89 | + | for _, outfitId in pairs(outfits) do | |
| 90 | + | -- Make sure player don't already have this outfit and addon | |
| 91 | + | if not player:hasOutfit(outfitId, orderCount) then | |
| 92 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";") | |
| 93 | + | player:addOutfit(outfitId) | |
| 94 | + | player:addOutfitAddon(outfitId, orderCount) | |
| 95 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new outfit!") | |
| 96 | + | print("Process complete. [".. player:getName() .."] has received outfit: ["..outfitId.."] with addon: ["..orderCount.."]") | |
| 97 | + | else -- Already has outfit | |
| 98 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this outfit and addon!") | |
| 99 | + | print("Process canceled. [".. player:getName() .."] already have outfit: ["..outfitId.."] with addon: ["..orderCount.."].") | |
| 100 | + | end | |
| 101 | + | end | |
| 102 | + | end | |
| 103 | + | ||
| 104 | + | -- ORDER TYPE 6 (Mounts) | |
| 105 | + | if orderType == 6 then | |
| 106 | + | served = true | |
| 107 | + | -- Make sure player don't already have this outfit and addon | |
| 108 | + | if not player:hasMount(orderItemId) then | |
| 109 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";") | |
| 110 | + | player:addMount(orderItemId) | |
| 111 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new mount!") | |
| 112 | + | print("Process complete. [".. player:getName() .."] has received mount: ["..orderItemId.."]") | |
| 113 | + | else -- Already has mount | |
| 114 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this mount!") | |
| 115 | + | print("Process canceled. [".. player:getName() .."] already have mount: ["..orderItemId.."].") | |
| 116 | + | end | |
| 117 | + | end | |
| 118 | + | ||
| 119 | + | -- ORDER TYPE 7 (Direct house purchase) | |
| 120 | + | if orderType == 7 then | |
| 121 | + | served = true | |
| 122 | + | local house = House(orderItemId) | |
| 123 | + | -- Logged in player is not necessarily the player that bough the house. So we need to load player from db. | |
| 124 | + | local buyerQuery = db.storeQuery("SELECT `name` FROM `players` WHERE `id` = "..orderCount.." LIMIT 1") | |
| 125 | + | if buyerQuery ~= false then | |
| 126 | + | local buyerName = result.getString(buyerQuery, "name") | |
| 127 | + | result.free(buyerQuery) | |
| 128 | + | if house then | |
| 129 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";") | |
| 130 | + | house:setOwnerGuid(orderCount) | |
| 131 | + | 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.") | |
| 132 | + | print("Process complete. [".. buyerName .."] has received house: ["..house:getName().."]") | |
| 133 | + | else | |
| 134 | + | print("Process canceled. Failed to load house with ID: "..orderItemId) | |
| 135 | + | end | |
| 136 | + | else | |
| 137 | + | print("Process canceled. Failed to load player with ID: "..orderCount) | |
| 138 | + | end | |
| 139 | + | end | |
| 140 | + | ||
| 141 | + | if not served then -- If this order hasn't been processed yet (missing type handling?) | |
| 142 | + | print("Znote shop: Type ["..orderType.."] not properly processed. Missing Lua code?") | |
| 143 | + | end | |
| 144 | + | else -- Not in protection zone | |
| 145 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, 'You have a pending shop order, please enter protection zone.') | |
| 146 | + | print("Skipped one shop order. Reason: Player: [".. player:getName() .."] is not inside protection zone.") | |
| 147 | + | end | |
| 148 | + | else -- player not logged in | |
| 149 | + | print("Skipped one shop order. Reason: Player with id [".. player_id .."] is not online.") | |
| 150 | + | end | |
| 151 | + | ||
| 152 | + | until not result.next(orderQuery) | |
| 153 | + | result.free(orderQuery) | |
| 154 | + | end | |
| 155 | + | return true | |
| 156 | + | end |
| @@ -0,0 +1,99 @@ | |||
| 1 | + | local creatureevent = CreatureEvent("FirstItems") | |
| 2 | + | ||
| 3 | + | local config = { | |
| 4 | + | [1] = { -- Sorcerer | |
| 5 | + | items = { | |
| 6 | + | {2175, 1}, -- spellbook | |
| 7 | + | {2190, 1}, -- wand of vortex | |
| 8 | + | {8819, 1}, -- magician's robe | |
| 9 | + | {8820, 1}, -- mage hat | |
| 10 | + | {2468, 1}, -- studded legs | |
| 11 | + | {2643, 1}, -- leather boots | |
| 12 | + | {2661, 1} -- scarf | |
| 13 | + | }, | |
| 14 | + | container = { | |
| 15 | + | {2120, 1}, -- rope | |
| 16 | + | {2554, 1}, -- shovel | |
| 17 | + | {7620, 1} -- mana potion | |
| 18 | + | } | |
| 19 | + | }, | |
| 20 | + | [2] = { -- Druid | |
| 21 | + | items = { | |
| 22 | + | {2175, 1}, -- spellbook | |
| 23 | + | {2182, 1}, -- snakebite rod | |
| 24 | + | {8819, 1}, -- magician's robe | |
| 25 | + | {8820, 1}, -- mage hat | |
| 26 | + | {2468, 1}, -- studded legs | |
| 27 | + | {2643, 1}, -- leather boots | |
| 28 | + | {2661, 1} -- scarf | |
| 29 | + | }, | |
| 30 | + | container = { | |
| 31 | + | {2120, 1}, -- rope | |
| 32 | + | {2554, 1}, -- shovel | |
| 33 | + | {7620, 1} -- mana potion | |
| 34 | + | } | |
| 35 | + | }, | |
| 36 | + | [3] = { -- Paladin | |
| 37 | + | items = { | |
| 38 | + | {2525, 1}, -- dwarven shield | |
| 39 | + | {2389, 5}, -- 5 spears | |
| 40 | + | {2660, 1}, -- ranger's cloak | |
| 41 | + | {8923, 1}, -- ranger legs | |
| 42 | + | {2643, 1}, -- leather boots | |
| 43 | + | {2661, 1}, -- scarf | |
| 44 | + | {2480, 1} -- legion helmet | |
| 45 | + | }, | |
| 46 | + | container = { | |
| 47 | + | {2120, 1}, -- rope | |
| 48 | + | {2554, 1}, -- shovel | |
| 49 | + | {7618, 1}, -- health potion | |
| 50 | + | {2456, 1}, -- bow | |
| 51 | + | {2544, 50} -- 50 arrows | |
| 52 | + | } | |
| 53 | + | }, | |
| 54 | + | [4] = { -- Knight | |
| 55 | + | items = { | |
| 56 | + | {2525, 1}, -- dwarven shield | |
| 57 | + | {8601, 1}, -- steel axe | |
| 58 | + | {2465, 1}, -- brass armor | |
| 59 | + | {2460, 1}, -- brass helmet | |
| 60 | + | {2478, 1}, -- brass legs | |
| 61 | + | {2643, 1}, -- leather boots | |
| 62 | + | {2661, 1} -- scarf | |
| 63 | + | }, | |
| 64 | + | container = { | |
| 65 | + | {8602, 1}, -- jagged sword | |
| 66 | + | {2439, 1}, -- daramanian mace | |
| 67 | + | {2120, 1}, -- rope | |
| 68 | + | {2554, 1}, -- shovel | |
| 69 | + | {7618, 1} -- health potion | |
| 70 | + | } | |
| 71 | + | } | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | function creatureevent.onLogin(player) | |
| 75 | + | local targetVocation = config[player:getVocation():getId()] | |
| 76 | + | if not targetVocation then | |
| 77 | + | return true | |
| 78 | + | end | |
| 79 | + | ||
| 80 | + | if player:getLastLoginSaved() ~= 0 then | |
| 81 | + | return true | |
| 82 | + | end | |
| 83 | + | ||
| 84 | + | for i = 1, #targetVocation.items do | |
| 85 | + | player:addItem(targetVocation.items[i][1], targetVocation.items[i][2]) | |
| 86 | + | end | |
| 87 | + | ||
| 88 | + | local backpack = player:addItem(1988) -- backpack | |
| 89 | + | if not backpack then | |
| 90 | + | return true | |
| 91 | + | end | |
| 92 | + | ||
| 93 | + | for i = 1, #targetVocation.container do | |
| 94 | + | backpack:addItem(targetVocation.container[i][1], targetVocation.container[i][2]) | |
| 95 | + | end | |
| 96 | + | return true | |
| 97 | + | end | |
| 98 | + | ||
| 99 | + | creatureevent:register() |
| @@ -0,0 +1,17 @@ | |||
| 1 | + | local creatureevent = CreatureEvent("FirstItemsRook") | |
| 2 | + | ||
| 3 | + | local firstItems = {2050, 2382} -- torch and club | |
| 4 | + | ||
| 5 | + | function creatureevent.onLogin(player) | |
| 6 | + | if player:getLastLoginSaved() <= 0 then | |
| 7 | + | for i = 1, #firstItems do | |
| 8 | + | player:addItem(firstItems[i], 1) | |
| 9 | + | end | |
| 10 | + | player:addItem(player:getSex() == 0 and 2651 or 2650, 1) -- coat | |
| 11 | + | player:addItem(ITEM_BAG, 1) | |
| 12 | + | player:addItem(2674, 1) -- red apple | |
| 13 | + | end | |
| 14 | + | return true | |
| 15 | + | end | |
| 16 | + | ||
| 17 | + | creatureevent:register() |
| @@ -0,0 +1,3 @@ | |||
| 1 | + | Step 1: Put script on data/script folder (edit content if necessary) | |
| 2 | + | ||
| 3 | + | Step 2: Restart OT server, and it should work. :) |
| @@ -0,0 +1,135 @@ | |||
| 1 | + | local deathListEnabled = true | |
| 2 | + | local maxDeathRecords = 5 | |
| 3 | + | ||
| 4 | + | local function sendWarStatus(guildId, enemyGuildId, warId, playerName, killerName) | |
| 5 | + | local guild, enemyGuild = Guild(guildId), Guild(enemyGuildId) | |
| 6 | + | if not guild or not enemyGuild then | |
| 7 | + | return | |
| 8 | + | end | |
| 9 | + | ||
| 10 | + | 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) | |
| 11 | + | if resultId then | |
| 12 | + | ||
| 13 | + | local guild1_kills = result.getNumber(resultId, "guild1_kills") | |
| 14 | + | local guild2_kills = result.getNumber(resultId, "guild2_kills") | |
| 15 | + | local limit = result.getNumber(resultId, "limit") | |
| 16 | + | result.free(resultId) | |
| 17 | + | ||
| 18 | + | local members = guild:getMembersOnline() | |
| 19 | + | for i = 1, #members do | |
| 20 | + | 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) | |
| 21 | + | end | |
| 22 | + | ||
| 23 | + | local enemyMembers = enemyGuild:getMembersOnline() | |
| 24 | + | for i = 1, #enemyMembers do | |
| 25 | + | 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) | |
| 26 | + | end | |
| 27 | + | ||
| 28 | + | if guild1_kills >= limit or guild2_kills >= limit then | |
| 29 | + | db.query("UPDATE `guild_wars` SET `status` = 4, `ended` = " .. os.time() .. " WHERE `status` = 1 AND `id` = " .. warId) | |
| 30 | + | Game.broadcastMessage(string.format("%s has just won the war against %s.", guild:getName(), enemyGuild:getName()), MESSAGE_EVENT_ADVANCE) | |
| 31 | + | end | |
| 32 | + | end | |
| 33 | + | end | |
| 34 | + | ||
| 35 | + | local creatureevent = CreatureEvent("PlayerDeath") | |
| 36 | + | ||
| 37 | + | function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified) | |
| 38 | + | local playerId = player:getId() | |
| 39 | + | if nextUseStaminaTime[playerId] then | |
| 40 | + | nextUseStaminaTime[playerId] = nil | |
| 41 | + | end | |
| 42 | + | ||
| 43 | + | player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are dead.") | |
| 44 | + | if not deathListEnabled then | |
| 45 | + | return | |
| 46 | + | end | |
| 47 | + | ||
| 48 | + | local byPlayer = 0 | |
| 49 | + | local killerName | |
| 50 | + | if killer then | |
| 51 | + | if killer:isPlayer() then | |
| 52 | + | byPlayer = 1 | |
| 53 | + | else | |
| 54 | + | local master = killer:getMaster() | |
| 55 | + | if master and master ~= killer and master:isPlayer() then | |
| 56 | + | killer = master | |
| 57 | + | byPlayer = 1 | |
| 58 | + | end | |
| 59 | + | end | |
| 60 | + | killerName = killer:getName() | |
| 61 | + | else | |
| 62 | + | killerName = "field item" | |
| 63 | + | end | |
| 64 | + | ||
| 65 | + | local byPlayerMostDamage = 0 | |
| 66 | + | local mostDamageKillerName | |
| 67 | + | if mostDamageKiller then | |
| 68 | + | if mostDamageKiller:isPlayer() then | |
| 69 | + | byPlayerMostDamage = 1 | |
| 70 | + | else | |
| 71 | + | local master = mostDamageKiller:getMaster() | |
| 72 | + | if master and master ~= mostDamageKiller and master:isPlayer() then | |
| 73 | + | mostDamageKiller = master | |
| 74 | + | byPlayerMostDamage = 1 | |
| 75 | + | end | |
| 76 | + | end | |
| 77 | + | mostDamageName = mostDamageKiller:getName() | |
| 78 | + | else | |
| 79 | + | mostDamageName = "field item" | |
| 80 | + | end | |
| 81 | + | ||
| 82 | + | local playerGuid = player:getGuid() | |
| 83 | + | 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) .. ")") | |
| 84 | + | local resultId = db.storeQuery("SELECT `player_id` FROM `player_deaths` WHERE `player_id` = " .. playerGuid) | |
| 85 | + | ||
| 86 | + | local deathRecords = 0 | |
| 87 | + | local tmpResultId = resultId | |
| 88 | + | while tmpResultId ~= false do | |
| 89 | + | tmpResultId = result.next(resultId) | |
| 90 | + | deathRecords = deathRecords + 1 | |
| 91 | + | end | |
| 92 | + | ||
| 93 | + | if resultId ~= false then | |
| 94 | + | result.free(resultId) | |
| 95 | + | end | |
| 96 | + | ||
| 97 | + | local limit = deathRecords - maxDeathRecords | |
| 98 | + | if limit > 0 then | |
| 99 | + | db.asyncQuery("DELETE FROM `player_deaths` WHERE `player_id` = " .. playerGuid .. " ORDER BY `time` LIMIT " .. limit) | |
| 100 | + | end | |
| 101 | + | ||
| 102 | + | if byPlayer == 1 then | |
| 103 | + | local targetGuild = player:getGuild() | |
| 104 | + | targetGuild = targetGuild and targetGuild:getId() or 0 | |
| 105 | + | if targetGuild ~= 0 then | |
| 106 | + | local killerGuild = killer:getGuild() | |
| 107 | + | killerGuild = killerGuild and killerGuild:getId() or 0 | |
| 108 | + | if killerGuild ~= 0 and targetGuild ~= killerGuild and isInWar(playerId, killer:getId()) then | |
| 109 | + | local warId = false | |
| 110 | + | resultId = db.storeQuery("SELECT `id` FROM `guild_wars` WHERE `status` = 1 AND ((`guild1` = " .. killerGuild .. " AND `guild2` = " .. targetGuild .. ") OR (`guild1` = " .. targetGuild .. " AND `guild2` = " .. killerGuild .. "))") | |
| 111 | + | if resultId ~= false then | |
| 112 | + | warId = result.getNumber(resultId, "id") | |
| 113 | + | result.free(resultId) | |
| 114 | + | end | |
| 115 | + | ||
| 116 | + | if warId ~= false then | |
| 117 | + | local playerName = player:getName() | |
| 118 | + | db.asyncQuery("INSERT INTO `guildwar_kills` (`killer`, `target`, `killerguild`, `targetguild`, `time`, `warid`) VALUES (" .. db.escapeString(killerName) .. ", " .. db.escapeString(playerName) .. ", " .. killerGuild .. ", " .. targetGuild .. ", " .. os.time() .. ", " .. warId .. ")") | |
| 119 | + | addEvent(sendWarStatus, 1000, killerGuild, targetGuild, warId, playerName, killerName) | |
| 120 | + | end | |
| 121 | + | end | |
| 122 | + | end | |
| 123 | + | end | |
| 124 | + | end | |
| 125 | + | ||
| 126 | + | creatureevent:register() | |
| 127 | + | ||
| 128 | + | local creatureeventLogin = CreatureEvent("creatureeventLogin") | |
| 129 | + | ||
| 130 | + | function creatureeventLogin.onLogin(player) | |
| 131 | + | player:registerEvent("PlayerDeath") | |
| 132 | + | return true | |
| 133 | + | end | |
| 134 | + | ||
| 135 | + | creatureeventLogin:register() |
| @@ -0,0 +1,229 @@ | |||
| 1 | + | -- Auto install tables if we dont got them yet (first install) | |
| 2 | + | db.query([[ | |
| 3 | + | CREATE TABLE IF NOT EXISTS `player_history_skill` ( | |
| 4 | + | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, | |
| 5 | + | `player_id` int(11) NOT NULL, | |
| 6 | + | `lastlogin` bigint(20) unsigned NOT NULL, | |
| 7 | + | `lastlogout` bigint(20) unsigned NOT NULL, | |
| 8 | + | `town_id` int(11) NOT NULL, | |
| 9 | + | `lastip` int(10) unsigned NOT NULL, | |
| 10 | + | `skull` tinyint(1) NOT NULL, | |
| 11 | + | `blessings` tinyint(2) NOT NULL, | |
| 12 | + | `onlinetime` int(11) NOT NULL, | |
| 13 | + | `balance` bigint(20) unsigned NOT NULL, | |
| 14 | + | `level` int(11) NOT NULL, | |
| 15 | + | `experience` bigint(20) NOT NULL, | |
| 16 | + | `maglevel` int(11) NOT NULL, | |
| 17 | + | `skill_fist` int(10) unsigned NOT NULL, | |
| 18 | + | `skill_club` int(10) unsigned NOT NULL, | |
| 19 | + | `skill_sword` int(10) unsigned NOT NULL, | |
| 20 | + | `skill_axe` int(10) unsigned NOT NULL, | |
| 21 | + | `skill_dist` int(10) unsigned NOT NULL, | |
| 22 | + | `skill_shielding` int(10) unsigned NOT NULL, | |
| 23 | + | `skill_fishing` int(10) unsigned NOT NULL, | |
| 24 | + | PRIMARY KEY (`id`), | |
| 25 | + | FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE | |
| 26 | + | ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; | |
| 27 | + | ]]) | |
| 28 | + | ||
| 29 | + | ||
| 30 | + | -- Auto populate table if it is empty | |
| 31 | + | local resultId = db.storeQuery("SELECT `id` FROM `player_history_skill` LIMIT 1;") | |
| 32 | + | if resultId == false then | |
| 33 | + | db.asyncQuery([[ | |
| 34 | + | INSERT INTO `player_history_skill` ( | |
| 35 | + | `player_id`, | |
| 36 | + | `lastlogin`, | |
| 37 | + | `lastlogout`, | |
| 38 | + | `town_id`, | |
| 39 | + | `lastip`, | |
| 40 | + | `skull`, | |
| 41 | + | `blessings`, | |
| 42 | + | `onlinetime`, | |
| 43 | + | `balance`, | |
| 44 | + | `level`, | |
| 45 | + | `experience`, | |
| 46 | + | `maglevel`, | |
| 47 | + | `skill_fist`, | |
| 48 | + | `skill_club`, | |
| 49 | + | `skill_sword`, | |
| 50 | + | `skill_axe`, | |
| 51 | + | `skill_dist`, | |
| 52 | + | `skill_shielding`, | |
| 53 | + | `skill_fishing` | |
| 54 | + | ) | |
| 55 | + | SELECT | |
| 56 | + | `p`.`id` AS `player_id`, | |
| 57 | + | `zp`.`created` AS `lastlogin`, | |
| 58 | + | CASE WHEN `p`.`lastlogout` > 0 | |
| 59 | + | THEN `p`.`lastlogout` | |
| 60 | + | ELSE `zp`.`created` | |
| 61 | + | END AS `lastlogout`, | |
| 62 | + | `p`.`town_id`, | |
| 63 | + | `p`.`lastip`, | |
| 64 | + | `p`.`skull`, | |
| 65 | + | `p`.`blessings`, | |
| 66 | + | `p`.`onlinetime`, | |
| 67 | + | `p`.`balance`, | |
| 68 | + | `p`.`level`, | |
| 69 | + | `p`.`experience`, | |
| 70 | + | `p`.`maglevel`, | |
| 71 | + | `p`.`skill_fist`, | |
| 72 | + | `p`.`skill_club`, | |
| 73 | + | `p`.`skill_sword`, | |
| 74 | + | `p`.`skill_axe`, | |
| 75 | + | `p`.`skill_dist`, | |
| 76 | + | `p`.`skill_shielding`, | |
| 77 | + | `p`.`skill_fishing` | |
| 78 | + | FROM `players` AS `p` | |
| 79 | + | INNER JOIN `znote_players` AS `zp` | |
| 80 | + | ON `p`.`id` = `zp`.`player_id` | |
| 81 | + | ORDER BY `zp`.`created` | |
| 82 | + | ]]) | |
| 83 | + | else | |
| 84 | + | result.free(resultId) | |
| 85 | + | end | |
| 86 | + | ||
| 87 | + | ||
| 88 | + | -- Logout event, triggered by logout, and death | |
| 89 | + | function historyLogoutEvent(player) | |
| 90 | + | local blessdec = 0 | |
| 91 | + | local i = 0 | |
| 92 | + | while player:hasBlessing(i+1) do | |
| 93 | + | blessdec = blessdec+2^i | |
| 94 | + | i = i+1 | |
| 95 | + | end | |
| 96 | + | ||
| 97 | + | local playerGuid = player:getGuid() | |
| 98 | + | db.query([[ | |
| 99 | + | INSERT INTO `player_history_skill` ( | |
| 100 | + | `player_id`, | |
| 101 | + | `lastlogin`, | |
| 102 | + | `lastlogout`, | |
| 103 | + | `town_id`, | |
| 104 | + | `lastip`, | |
| 105 | + | `skull`, | |
| 106 | + | `blessings`, | |
| 107 | + | `onlinetime`, | |
| 108 | + | `balance`, | |
| 109 | + | `level`, | |
| 110 | + | `experience`, | |
| 111 | + | `maglevel`, | |
| 112 | + | `skill_fist`, | |
| 113 | + | `skill_club`, | |
| 114 | + | `skill_sword`, | |
| 115 | + | `skill_axe`, | |
| 116 | + | `skill_dist`, | |
| 117 | + | `skill_shielding`, | |
| 118 | + | `skill_fishing` | |
| 119 | + | ) VALUES ( | |
| 120 | + | ]]..table.concat({ | |
| 121 | + | playerGuid, | |
| 122 | + | player:getLastLoginSaved(), | |
| 123 | + | os.time(), | |
| 124 | + | player:getTown():getId(), | |
| 125 | + | player:getIp(), | |
| 126 | + | player:getSkull(), | |
| 127 | + | blessdec, | |
| 128 | + | "(SELECT `onlinetime` FROM `players` WHERE `id`='"..playerGuid.."') + ".. os.time() - player:getLastLoginSaved(), | |
| 129 | + | player:getBankBalance(), | |
| 130 | + | player:getLevel(), | |
| 131 | + | player:getExperience(), | |
| 132 | + | player:getMagicLevel(), | |
| 133 | + | player:getSkillLevel(SKILL_FIST), | |
| 134 | + | player:getSkillLevel(SKILL_CLUB), | |
| 135 | + | player:getSkillLevel(SKILL_SWORD), | |
| 136 | + | player:getSkillLevel(SKILL_AXE), | |
| 137 | + | player:getSkillLevel(SKILL_DISTANCE), | |
| 138 | + | player:getSkillLevel(SKILL_SHIELD), | |
| 139 | + | player:getSkillLevel(SKILL_FISHING) | |
| 140 | + | }, ",")..[[ | |
| 141 | + | ); | |
| 142 | + | ]]) | |
| 143 | + | end | |
| 144 | + | ||
| 145 | + | ||
| 146 | + | -- Log player state on logout | |
| 147 | + | local player_history_skill = CreatureEvent("player_history_skill") | |
| 148 | + | function player_history_skill.onLogout(player) | |
| 149 | + | --print("2-logout["..player:getName().."]") | |
| 150 | + | historyLogoutEvent(player) | |
| 151 | + | return true | |
| 152 | + | end | |
| 153 | + | player_history_skill:register() | |
| 154 | + | ||
| 155 | + | ||
| 156 | + | -- And on death | |
| 157 | + | local player_history_skill_death = CreatureEvent("player_history_skill_death") | |
| 158 | + | function player_history_skill_death.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified) | |
| 159 | + | --print("3-death["..creature:getName().."]") | |
| 160 | + | historyLogoutEvent(Player(creature)) | |
| 161 | + | end | |
| 162 | + | player_history_skill_death:register() | |
| 163 | + | ||
| 164 | + | ||
| 165 | + | -- If this is first login, insert current progress | |
| 166 | + | local player_history_skill_login = CreatureEvent("player_history_skill_login") | |
| 167 | + | function player_history_skill_login.onLogin(player) | |
| 168 | + | --print("1-login["..player:getName().."]") | |
| 169 | + | player:registerEvent("player_history_skill_death") | |
| 170 | + | ||
| 171 | + | local playerGuid = player:getGuid() | |
| 172 | + | local resultId = db.storeQuery("SELECT `id` FROM `player_history_skill` WHERE `player_id`="..playerGuid.." LIMIT 1;") | |
| 173 | + | if resultId == false then | |
| 174 | + | db.query([[ | |
| 175 | + | INSERT INTO `player_history_skill` ( | |
| 176 | + | `player_id`, | |
| 177 | + | `lastlogin`, | |
| 178 | + | `lastlogout`, | |
| 179 | + | `town_id`, | |
| 180 | + | `lastip`, | |
| 181 | + | `skull`, | |
| 182 | + | `blessings`, | |
| 183 | + | `onlinetime`, | |
| 184 | + | `balance`, | |
| 185 | + | `level`, | |
| 186 | + | `experience`, | |
| 187 | + | `maglevel`, | |
| 188 | + | `skill_fist`, | |
| 189 | + | `skill_club`, | |
| 190 | + | `skill_sword`, | |
| 191 | + | `skill_axe`, | |
| 192 | + | `skill_dist`, | |
| 193 | + | `skill_shielding`, | |
| 194 | + | `skill_fishing` | |
| 195 | + | ) | |
| 196 | + | SELECT | |
| 197 | + | `p`.`id` AS `player_id`, | |
| 198 | + | `zp`.`created` AS `lastlogin`, | |
| 199 | + | CASE WHEN `p`.`lastlogout` > 0 | |
| 200 | + | THEN `p`.`lastlogout` | |
| 201 | + | ELSE `zp`.`created` | |
| 202 | + | END AS `lastlogout`, | |
| 203 | + | `p`.`town_id`, | |
| 204 | + | `p`.`lastip`, | |
| 205 | + | `p`.`skull`, | |
| 206 | + | `p`.`blessings`, | |
| 207 | + | `p`.`onlinetime`, | |
| 208 | + | `p`.`balance`, | |
| 209 | + | `p`.`level`, | |
| 210 | + | `p`.`experience`, | |
| 211 | + | `p`.`maglevel`, | |
| 212 | + | `p`.`skill_fist`, | |
| 213 | + | `p`.`skill_club`, | |
| 214 | + | `p`.`skill_sword`, | |
| 215 | + | `p`.`skill_axe`, | |
| 216 | + | `p`.`skill_dist`, | |
| 217 | + | `p`.`skill_shielding`, | |
| 218 | + | `p`.`skill_fishing` | |
| 219 | + | FROM `players` AS `p` | |
| 220 | + | INNER JOIN `znote_players` AS `zp` | |
| 221 | + | ON `p`.`id` = `zp`.`player_id` | |
| 222 | + | WHERE `p`.`id` = ]]..playerGuid..[[ | |
| 223 | + | ]]) | |
| 224 | + | else | |
| 225 | + | result.free(resultId) | |
| 226 | + | end | |
| 227 | + | return true | |
| 228 | + | end | |
| 229 | + | player_history_skill_login:register() |
| @@ -0,0 +1,65 @@ | |||
| 1 | + | -- getEternalStorage and setEternalStorage | |
| 2 | + | -- can be added to data/global.lua if you want to use eternal storage for another purpose than this. | |
| 3 | + | -- Regular TFS global storage values get reset every time server reboots. This does not. | |
| 4 | + | local function getEternalStorage(key, parser) | |
| 5 | + | local value = result.getString(db.storeQuery("SELECT `value` FROM `znote_global_storage` WHERE `key` = ".. key .. ";"), "value") | |
| 6 | + | if not value then | |
| 7 | + | if parser then | |
| 8 | + | return false | |
| 9 | + | else | |
| 10 | + | return -1 | |
| 11 | + | end | |
| 12 | + | end | |
| 13 | + | result.free(value) | |
| 14 | + | return tonumber(value) or value | |
| 15 | + | end | |
| 16 | + | ||
| 17 | + | local function setEternalStorage(key, value) | |
| 18 | + | if getEternalStorage(key, true) then | |
| 19 | + | db.query("UPDATE `znote_global_storage` SET `value` = '".. value .. "' WHERE `key` = ".. key .. ";") | |
| 20 | + | else | |
| 21 | + | db.query("INSERT INTO `znote_global_storage` (`key`, `value`) VALUES (".. key ..", ".. value ..");") | |
| 22 | + | end | |
| 23 | + | return true | |
| 24 | + | end | |
| 25 | + | ||
| 26 | + | -- SQL Query to execute: -- | |
| 27 | + | --[[ | |
| 28 | + | ALTER TABLE `znote_players` ADD `exphist_lastexp` BIGINT NOT NULL DEFAULT '0', | |
| 29 | + | ADD `exphist1` BIGINT NOT NULL DEFAULT '0', | |
| 30 | + | ADD `exphist2` BIGINT NOT NULL DEFAULT '0', | |
| 31 | + | ADD `exphist3` BIGINT NOT NULL DEFAULT '0', | |
| 32 | + | ADD `exphist4` BIGINT NOT NULL DEFAULT '0', | |
| 33 | + | ADD `exphist5` BIGINT NOT NULL DEFAULT '0', | |
| 34 | + | ADD `exphist6` BIGINT NOT NULL DEFAULT '0', | |
| 35 | + | ADD `exphist7` BIGINT NOT NULL DEFAULT '0', | |
| 36 | + | ADD `onlinetimetoday` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 37 | + | ADD `onlinetime1` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 38 | + | ADD `onlinetime2` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 39 | + | ADD `onlinetime3` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 40 | + | ADD `onlinetime4` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 41 | + | ADD `onlinetime5` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 42 | + | ADD `onlinetime6` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 43 | + | ADD `onlinetime7` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 44 | + | ADD `onlinetimeall` INT UNSIGNED NOT NULL DEFAULT '0'; | |
| 45 | + | ]]-- | |
| 46 | + | ||
| 47 | + | -- after that execute: -- | |
| 48 | + | --[[ | |
| 49 | + | UPDATE `znote_players` AS `z` INNER JOIN `players` AS `p` ON `p`.`id`=`z`.`player_id` SET `z`.`exphist_lastexp`=`p`.`experience`; | |
| 50 | + | ]]-- | |
| 51 | + | ||
| 52 | + | local globalevent = GlobalEvent("PowerGamers") | |
| 53 | + | ||
| 54 | + | function globalevent.onThink(...) | |
| 55 | + | if tonumber(os.date("%d")) ~= getEternalStorage(23856) then | |
| 56 | + | setEternalStorage(23856, (tonumber(os.date("%d")))) | |
| 57 | + | db.query("UPDATE `znote_players` SET `onlinetime7`=`onlinetime6`, `onlinetime6`=`onlinetime5`, `onlinetime5`=`onlinetime4`, `onlinetime4`=`onlinetime3`, `onlinetime3`=`onlinetime2`, `onlinetime2`=`onlinetime1`, `onlinetime1`=`onlinetimetoday`, `onlinetimetoday`=0;") | |
| 58 | + | 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`;") | |
| 59 | + | end | |
| 60 | + | 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`)") | |
| 61 | + | return true | |
| 62 | + | end | |
| 63 | + | ||
| 64 | + | globalevent:interval(60000) | |
| 65 | + | globalevent:register() |
| @@ -0,0 +1,21 @@ | |||
| 1 | + | local talkaction = TalkAction("!report") | |
| 2 | + | ||
| 3 | + | function talkaction.onSay(player) | |
| 4 | + | local storage = 6708 -- You can change the storage if its already in use | |
| 5 | + | local delaytime = 30 -- Exhaust In Seconds. | |
| 6 | + | if param == '' then | |
| 7 | + | player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Command param required.") | |
| 8 | + | return true | |
| 9 | + | end | |
| 10 | + | if player:getStorageValue(storage) <= os.time() then | |
| 11 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Your report has been received successfully!") | |
| 12 | + | 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() .. "')") | |
| 13 | + | player:setStorageValue(storage, os.time() + delaytime) | |
| 14 | + | else | |
| 15 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You have to wait " .. player:getStorageValue(storage) - os.time() .. " seconds to report again.") | |
| 16 | + | end | |
| 17 | + | return true | |
| 18 | + | end | |
| 19 | + | ||
| 20 | + | talkaction:separator(" ") | |
| 21 | + | talkaction:register() |
| @@ -0,0 +1,159 @@ | |||
| 1 | + | local globalevent = GlobalEvent("ShopSystemGlobal") | |
| 2 | + | ||
| 3 | + | function globalevent.onThink(...) | |
| 4 | + | local shopTypes = {1,5,6,7} | |
| 5 | + | local orderQuery = db.storeQuery([[ | |
| 6 | + | SELECT | |
| 7 | + | MIN(`po`.`player_id`) AS `player_id`, | |
| 8 | + | `shop`.`id`, | |
| 9 | + | `shop`.`type`, | |
| 10 | + | `shop`.`itemid`, | |
| 11 | + | `shop`.`count` | |
| 12 | + | FROM `players_online` AS `po` | |
| 13 | + | INNER JOIN `players` AS `p` | |
| 14 | + | ON `po`.`player_id` = `p`.`id` | |
| 15 | + | INNER JOIN `znote_shop_orders` AS `shop` | |
| 16 | + | ON `p`.`account_id` = `shop`.`account_id` | |
| 17 | + | WHERE `shop`.`type` IN(]] .. table.concat(shopTypes, ",") .. [[) | |
| 18 | + | GROUP BY `shop`.`id` | |
| 19 | + | ]]) | |
| 20 | + | -- Detect if we got any results | |
| 21 | + | if orderQuery ~= false then | |
| 22 | + | local type_desc = { | |
| 23 | + | "itemids", | |
| 24 | + | "pending premium (skip)", | |
| 25 | + | "pending gender change (skip)", | |
| 26 | + | "pending character name change (skip)", | |
| 27 | + | "Outfit and addons", | |
| 28 | + | "Mounts", | |
| 29 | + | "Instant house purchase" | |
| 30 | + | } | |
| 31 | + | repeat | |
| 32 | + | local player_id = result.getNumber(orderQuery, 'player_id') | |
| 33 | + | local orderId = result.getNumber(orderQuery, 'id') | |
| 34 | + | local orderType = result.getNumber(orderQuery, 'type') | |
| 35 | + | local orderItemId = result.getNumber(orderQuery, 'itemid') | |
| 36 | + | local orderCount = result.getNumber(orderQuery, 'count') | |
| 37 | + | local served = false | |
| 38 | + | ||
| 39 | + | local player = Player(player_id) | |
| 40 | + | if player ~= nil then | |
| 41 | + | ||
| 42 | + | local description = "Unknown or custom type" | |
| 43 | + | if type_desc[orderType] ~= nil then | |
| 44 | + | description = type_desc[orderType] | |
| 45 | + | end | |
| 46 | + | print("Processing type "..orderType..": ".. description) | |
| 47 | + | print("Processing shop order for: [".. player:getName() .."] type "..orderType..": ".. description) | |
| 48 | + | ||
| 49 | + | local tile = Tile(player:getPosition()) | |
| 50 | + | if tile ~= nil and tile:hasFlag(TILESTATE_PROTECTIONZONE) then | |
| 51 | + | -- ORDER TYPE 1 (Regular item shop products) | |
| 52 | + | if orderType == 1 then | |
| 53 | + | served = true | |
| 54 | + | local itemType = ItemType(orderItemId) | |
| 55 | + | -- Get weight | |
| 56 | + | if player:getFreeCapacity() >= itemType:getWeight(orderCount) then | |
| 57 | + | local backpack = player:getSlotItem(CONST_SLOT_BACKPACK) | |
| 58 | + | -- variable = (condition) and (return if true) or (return if false) | |
| 59 | + | local needslots = itemType:isStackable() and math.floor(orderCount / 100) + 1 or orderCount | |
| 60 | + | if backpack ~= nil and backpack:getEmptySlots(false) >= needslots then | |
| 61 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";") | |
| 62 | + | player:addItem(orderItemId, orderCount) | |
| 63 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. "!") | |
| 64 | + | print("Process complete. [".. player:getName() .."] has received " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".") | |
| 65 | + | else -- not enough slots | |
| 66 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "Your main backpack is full. You need to free up "..needslots.." available slots to get " .. orderCount .. " " .. ItemType(orderItemId):getName() .. "!") | |
| 67 | + | print("Process canceled. [".. player:getName() .."] need more space in his backpack to get " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".") | |
| 68 | + | end | |
| 69 | + | else -- not enough cap | |
| 70 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You need more CAP to carry this order!") | |
| 71 | + | print("Process canceled. [".. player:getName() .."] need more cap to carry " .. orderCount .. "x " .. ItemType(orderItemId):getName() .. ".") | |
| 72 | + | end | |
| 73 | + | end | |
| 74 | + | ||
| 75 | + | -- ORDER TYPE 5 (Outfit and addon) | |
| 76 | + | if orderType == 5 then | |
| 77 | + | served = true | |
| 78 | + | ||
| 79 | + | local itemid = orderItemId | |
| 80 | + | local outfits = {} | |
| 81 | + | ||
| 82 | + | if itemid > 1000 then | |
| 83 | + | local first = math.floor(itemid/1000) | |
| 84 | + | table.insert(outfits, first) | |
| 85 | + | itemid = itemid - (first * 1000) | |
| 86 | + | end | |
| 87 | + | table.insert(outfits, itemid) | |
| 88 | + | ||
| 89 | + | for _, outfitId in pairs(outfits) do | |
| 90 | + | -- Make sure player don't already have this outfit and addon | |
| 91 | + | if not player:hasOutfit(outfitId, orderCount) then | |
| 92 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";") | |
| 93 | + | player:addOutfit(outfitId) | |
| 94 | + | player:addOutfitAddon(outfitId, orderCount) | |
| 95 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new outfit!") | |
| 96 | + | print("Process complete. [".. player:getName() .."] has received outfit: ["..outfitId.."] with addon: ["..orderCount.."]") | |
| 97 | + | else -- Already has outfit | |
| 98 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this outfit and addon!") | |
| 99 | + | print("Process canceled. [".. player:getName() .."] already have outfit: ["..outfitId.."] with addon: ["..orderCount.."].") | |
| 100 | + | end | |
| 101 | + | end | |
| 102 | + | end | |
| 103 | + | ||
| 104 | + | -- ORDER TYPE 6 (Mounts) | |
| 105 | + | if orderType == 6 then | |
| 106 | + | served = true | |
| 107 | + | -- Make sure player don't already have this outfit and addon | |
| 108 | + | if not player:hasMount(orderItemId) then | |
| 109 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";") | |
| 110 | + | player:addMount(orderItemId) | |
| 111 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new mount!") | |
| 112 | + | print("Process complete. [".. player:getName() .."] has received mount: ["..orderItemId.."]") | |
| 113 | + | else -- Already has mount | |
| 114 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this mount!") | |
| 115 | + | print("Process canceled. [".. player:getName() .."] already have mount: ["..orderItemId.."].") | |
| 116 | + | end | |
| 117 | + | end | |
| 118 | + | ||
| 119 | + | -- ORDER TYPE 7 (Direct house purchase) | |
| 120 | + | if orderType == 7 then | |
| 121 | + | served = true | |
| 122 | + | local house = House(orderItemId) | |
| 123 | + | -- Logged in player is not necessarily the player that bough the house. So we need to load player from db. | |
| 124 | + | local buyerQuery = db.storeQuery("SELECT `name` FROM `players` WHERE `id` = "..orderCount.." LIMIT 1") | |
| 125 | + | if buyerQuery ~= false then | |
| 126 | + | local buyerName = result.getString(buyerQuery, "name") | |
| 127 | + | result.free(buyerQuery) | |
| 128 | + | if house then | |
| 129 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. orderId .. ";") | |
| 130 | + | house:setOwnerGuid(orderCount) | |
| 131 | + | 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.") | |
| 132 | + | print("Process complete. [".. buyerName .."] has received house: ["..house:getName().."]") | |
| 133 | + | else | |
| 134 | + | print("Process canceled. Failed to load house with ID: "..orderItemId) | |
| 135 | + | end | |
| 136 | + | else | |
| 137 | + | print("Process canceled. Failed to load player with ID: "..orderCount) | |
| 138 | + | end | |
| 139 | + | end | |
| 140 | + | ||
| 141 | + | if not served then -- If this order hasn't been processed yet (missing type handling?) | |
| 142 | + | print("Znote shop: Type ["..orderType.."] not properly processed. Missing Lua code?") | |
| 143 | + | end | |
| 144 | + | else -- Not in protection zone | |
| 145 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, 'You have a pending shop order, please enter protection zone.') | |
| 146 | + | print("Skipped one shop order. Reason: Player: [".. player:getName() .."] is not inside protection zone.") | |
| 147 | + | end | |
| 148 | + | else -- player not logged in | |
| 149 | + | print("Skipped one shop order. Reason: Player with id [".. player_id .."] is not online.") | |
| 150 | + | end | |
| 151 | + | ||
| 152 | + | until not result.next(orderQuery) | |
| 153 | + | result.free(orderQuery) | |
| 154 | + | end | |
| 155 | + | return true | |
| 156 | + | end | |
| 157 | + | ||
| 158 | + | globalevent:interval(30000) | |
| 159 | + | globalevent:register() |
| @@ -0,0 +1,135 @@ | |||
| 1 | + | local talkaction = TalkAction("!shop") | |
| 2 | + | ||
| 3 | + | function talkaction.onSay(player) | |
| 4 | + | local storage = 54073 -- Make sure to select non-used storage. This is used to prevent SQL load attacks. | |
| 5 | + | local cooldown = 15 -- in seconds. | |
| 6 | + | ||
| 7 | + | if player:getStorageValue(storage) <= os.time() then | |
| 8 | + | player:setStorageValue(storage, os.time() + cooldown) | |
| 9 | + | ||
| 10 | + | local type_desc = { | |
| 11 | + | "itemids", | |
| 12 | + | "pending premium (skip)", | |
| 13 | + | "pending gender change (skip)", | |
| 14 | + | "pending character name change (skip)", | |
| 15 | + | "Outfit and addons", | |
| 16 | + | "Mounts", | |
| 17 | + | "Instant house purchase" | |
| 18 | + | } | |
| 19 | + | print("Player: " .. player:getName() .. " triggered !shop talkaction.") | |
| 20 | + | -- Create the query | |
| 21 | + | local orderQuery = db.storeQuery("SELECT `id`, `type`, `itemid`, `count` FROM `znote_shop_orders` WHERE `account_id` = " .. player:getAccountId() .. ";") | |
| 22 | + | local served = false | |
| 23 | + | ||
| 24 | + | -- Detect if we got any results | |
| 25 | + | if orderQuery ~= false then | |
| 26 | + | repeat | |
| 27 | + | -- Fetch order values | |
| 28 | + | local q_id = result.getNumber(orderQuery, "id") | |
| 29 | + | local q_type = result.getNumber(orderQuery, "type") | |
| 30 | + | local q_itemid = result.getNumber(orderQuery, "itemid") | |
| 31 | + | local q_count = result.getNumber(orderQuery, "count") | |
| 32 | + | ||
| 33 | + | local description = "Unknown or custom type" | |
| 34 | + | if type_desc[q_type] ~= nil then | |
| 35 | + | description = type_desc[q_type] | |
| 36 | + | end | |
| 37 | + | print("Processing type "..q_type..": ".. description) | |
| 38 | + | ||
| 39 | + | -- ORDER TYPE 1 (Regular item shop products) | |
| 40 | + | if q_type == 1 then | |
| 41 | + | served = true | |
| 42 | + | -- Get weight | |
| 43 | + | if player:getFreeCapacity() >= ItemType(q_itemid):getWeight(q_count) then | |
| 44 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";") | |
| 45 | + | player:addItem(q_itemid, q_count) | |
| 46 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received " .. q_count .. " x " .. ItemType(q_itemid):getName() .. "!") | |
| 47 | + | else | |
| 48 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "Need more CAP!") | |
| 49 | + | end | |
| 50 | + | end | |
| 51 | + | ||
| 52 | + | -- ORDER TYPE 5 (Outfit and addon) | |
| 53 | + | if q_type == 5 then | |
| 54 | + | served = true | |
| 55 | + | ||
| 56 | + | local itemid = q_itemid | |
| 57 | + | local outfits = {} | |
| 58 | + | ||
| 59 | + | if itemid > 1000 then | |
| 60 | + | local first = math.floor(itemid/1000) | |
| 61 | + | table.insert(outfits, first) | |
| 62 | + | itemid = itemid - (first * 1000) | |
| 63 | + | end | |
| 64 | + | table.insert(outfits, itemid) | |
| 65 | + | ||
| 66 | + | for _, outfitId in pairs(outfits) do | |
| 67 | + | -- Make sure player don't already have this outfit and addon | |
| 68 | + | if not player:hasOutfit(outfitId, q_count) then | |
| 69 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";") | |
| 70 | + | player:addOutfit(outfitId) | |
| 71 | + | player:addOutfitAddon(outfitId, q_count) | |
| 72 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new outfit!") | |
| 73 | + | else | |
| 74 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this outfit and addon!") | |
| 75 | + | end | |
| 76 | + | end | |
| 77 | + | end | |
| 78 | + | ||
| 79 | + | -- ORDER TYPE 6 (Mounts). itemid holds the mount id. | |
| 80 | + | if q_type == 6 then | |
| 81 | + | served = true | |
| 82 | + | -- Make sure player don't already have this mount | |
| 83 | + | if not player:hasMount(q_itemid) then | |
| 84 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";") | |
| 85 | + | player:addMount(q_itemid) | |
| 86 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new mount!") | |
| 87 | + | else | |
| 88 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this mount!") | |
| 89 | + | end | |
| 90 | + | end | |
| 91 | + | ||
| 92 | + | -- ORDER TYPE 7 (Direct house purchase) | |
| 93 | + | if q_type == 7 then | |
| 94 | + | served = true | |
| 95 | + | local house = House(q_itemid) | |
| 96 | + | -- Logged in player is not necessarily the player that bough the house. So we need to load player from db. | |
| 97 | + | local buyerQuery = db.storeQuery("SELECT `name` FROM `players` WHERE `id` = "..q_count.." LIMIT 1") | |
| 98 | + | if buyerQuery ~= false then | |
| 99 | + | local buyerName = result.getString(buyerQuery, "name") | |
| 100 | + | result.free(buyerQuery) | |
| 101 | + | if house then | |
| 102 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";") | |
| 103 | + | house:setOwnerGuid(q_count) | |
| 104 | + | 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.") | |
| 105 | + | print("Process complete. [".. buyerName .."] has received house: ["..house:getName().."]") | |
| 106 | + | end | |
| 107 | + | end | |
| 108 | + | end | |
| 109 | + | ||
| 110 | + | -- Add custom order types here | |
| 111 | + | -- Type 1 is for itemids (Already coded here) | |
| 112 | + | -- Type 2 is for premium (Coded on web) | |
| 113 | + | -- Type 3 is for gender change (Coded on web) | |
| 114 | + | -- Type 4 is for character name change (Coded on web) | |
| 115 | + | -- Type 5 is for character outfit and addon (Already coded here) | |
| 116 | + | -- Type 6 is for mounts (Already coded here) | |
| 117 | + | -- Type 7 is for Instant house purchase (Already coded here) | |
| 118 | + | -- So use type 8+ for custom stuff, like etc packages. | |
| 119 | + | -- if q_type == 8 then | |
| 120 | + | -- end | |
| 121 | + | until not result.next(orderQuery) | |
| 122 | + | result.free(orderQuery) | |
| 123 | + | if not served then | |
| 124 | + | player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You have no orders to process in-game.") | |
| 125 | + | end | |
| 126 | + | else | |
| 127 | + | player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You have no orders.") | |
| 128 | + | end | |
| 129 | + | else | |
| 130 | + | player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Can only be executed once every " .. cooldown .. " seconds. Remaining cooldown: " .. player:getStorageValue(storage) - os.time()) | |
| 131 | + | end | |
| 132 | + | return false | |
| 133 | + | end | |
| 134 | + | ||
| 135 | + | talkaction:register() |
| @@ -0,0 +1,50 @@ | |||
| 1 | + | local creatureevent = CreatureEvent("SincOutfit") | |
| 2 | + | ||
| 3 | + | -- Sync outfits that player own with Znote AAC | |
| 4 | + | -- So its possible to see which full sets player | |
| 5 | + | -- has in characterprofile.php | |
| 6 | + | ||
| 7 | + | znote_outfit_list = { | |
| 8 | + | { -- Female outfits | |
| 9 | + | 136, 137, 138, 139, 140, 141, 142, 147, 148, | |
| 10 | + | 149, 150, 155, 156, 157, 158, 252, 269, 270, | |
| 11 | + | 279, 288, 324, 329, 336, 366, 431, 433, 464, | |
| 12 | + | 466, 471, 513, 514, 542, 575, 578, 618, 620, | |
| 13 | + | 632, 635, 636, 664, 666, 683, 694, 696, 698, | |
| 14 | + | 724, 732, 745, 749, 759, 845, 852, 874, 885, | |
| 15 | + | 900, 973, 975, 1020, 1024, 1043, 1050, 1057, | |
| 16 | + | 1070, 1095, 1103, 1128, 1147, 1162, 1174, | |
| 17 | + | 1187, 1203, 1205, 1207, 1211, 1246, 1244, | |
| 18 | + | 1252, 1271, 1280, 1283, 1289, 1293, 1332 | |
| 19 | + | }, | |
| 20 | + | { -- Male outfits | |
| 21 | + | 128, 129, 130, 131, 132, 133, 134, 143, 144, | |
| 22 | + | 145, 146, 151, 152, 153, 154, 251, 268, 273, | |
| 23 | + | 278, 289, 325, 328, 335, 367, 430, 432, 463, | |
| 24 | + | 465, 472, 512, 516, 541, 574, 577, 610, 619, | |
| 25 | + | 633, 634, 637, 665, 667, 684, 695, 697, 699, | |
| 26 | + | 725, 733, 746, 750, 760, 846, 853, 873, 884, | |
| 27 | + | 899, 908, 931, 955, 957, 962, 964, 966, 968, | |
| 28 | + | 970, 972, 974, 1021, 1023, 1042, 1051, 1056, | |
| 29 | + | 1069, 1094, 1102, 1127, 1146, 1161, 1173, | |
| 30 | + | 1186, 1202, 1204, 1206, 1210, 1245, 1243, | |
| 31 | + | 1251, 1270, 1279, 1282, 1288, 1292, 1331 | |
| 32 | + | } | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | function creatureevent.onLogin(player) | |
| 36 | + | -- storage_value + 1000 storages (highest outfit id) must not be used in other script. | |
| 37 | + | -- Must be identical to Znote AAC config.php: $config['EQ_shower'] -> storage_value | |
| 38 | + | local storage_value = 10000 | |
| 39 | + | -- Loop through outfits | |
| 40 | + | for _, outfit in pairs(znote_outfit_list[player:getSex() + 1]) do | |
| 41 | + | if player:hasOutfit(outfit,3) then | |
| 42 | + | if player:getStorageValue(storage_value + outfit) ~= 3 then | |
| 43 | + | player:setStorageValue(storage_value + outfit, 3) | |
| 44 | + | end | |
| 45 | + | end | |
| 46 | + | end | |
| 47 | + | return true | |
| 48 | + | end | |
| 49 | + | ||
| 50 | + | creatureevent:register() |
| @@ -0,0 +1,68 @@ | |||
| 1 | + | -- Znote LoginWebService (version 1) for protocol 11, 12+ | |
| 2 | + | -- Move file to this location: data/scripts/znote_login.lua | |
| 3 | + | -- And restart OT server, it should auto load script. | |
| 4 | + | -- Requires updated version of Znote AAC. (18. June 2020) | |
| 5 | + | -- This script will help Znote AAC connect players to this game server. | |
| 6 | + | ||
| 7 | + | local znote_loginWebService = GlobalEvent("znote_loginWebService") | |
| 8 | + | function znote_loginWebService.onStartup() | |
| 9 | + | print(" ") | |
| 10 | + | print("=============================") | |
| 11 | + | print("= Znote AAC loginWebService =") | |
| 12 | + | print("=============================") | |
| 13 | + | local configLua = { | |
| 14 | + | ["SERVER_NAME"] = configManager.getString(configKeys.SERVER_NAME), | |
| 15 | + | ["IP"] = configManager.getString(configKeys.IP), | |
| 16 | + | ["GAME_PORT"] = configManager.getNumber(configKeys.GAME_PORT) | |
| 17 | + | } | |
| 18 | + | local configSQL = { | |
| 19 | + | ["SERVER_NAME"] = false, | |
| 20 | + | ["IP"] = false, | |
| 21 | + | ["GAME_PORT"] = false | |
| 22 | + | } | |
| 23 | + | local webStorage = db.storeQuery([[ | |
| 24 | + | SELECT | |
| 25 | + | `key`, | |
| 26 | + | `value` | |
| 27 | + | FROM `znote_global_storage` | |
| 28 | + | WHERE `key` IN('SERVER_NAME', 'IP', 'GAME_PORT') | |
| 29 | + | ]]) | |
| 30 | + | if webStorage ~= false then | |
| 31 | + | repeat | |
| 32 | + | local key = result.getString(webStorage, 'key') | |
| 33 | + | local value = result.getString(webStorage, 'value') | |
| 34 | + | configSQL[key] = value | |
| 35 | + | until not result.next(webStorage) | |
| 36 | + | result.free(webStorage) | |
| 37 | + | end | |
| 38 | + | local inserts = {} | |
| 39 | + | if configSQL.SERVER_NAME == false then | |
| 40 | + | table.insert(inserts, "('SERVER_NAME',".. db.escapeString(configLua.SERVER_NAME) ..")") | |
| 41 | + | elseif configSQL.SERVER_NAME ~= configLua.SERVER_NAME then | |
| 42 | + | db.query("UPDATE `znote_global_storage` SET `value`=".. db.escapeString(configLua.SERVER_NAME) .." WHERE `key`='SERVER_NAME';") | |
| 43 | + | print("= Updated [SERVER_NAME] FROM [" .. configSQL.SERVER_NAME .. "] to [" .. configLua.SERVER_NAME .. "]") | |
| 44 | + | end | |
| 45 | + | if configSQL.IP == false then | |
| 46 | + | table.insert(inserts, "('IP',".. db.escapeString(configLua.IP) ..")") | |
| 47 | + | elseif configSQL.IP ~= configLua.IP then | |
| 48 | + | db.query("UPDATE `znote_global_storage` SET `value`=".. db.escapeString(configLua.IP) .." WHERE `key`='IP';") | |
| 49 | + | print("= Updated [IP] FROM [" .. configSQL.IP .. "] to [" .. configLua.IP .. "]") | |
| 50 | + | end | |
| 51 | + | if configSQL.GAME_PORT == false then | |
| 52 | + | table.insert(inserts, "('GAME_PORT',".. db.escapeString(configLua.GAME_PORT) ..")") | |
| 53 | + | elseif configSQL.GAME_PORT ~= tostring(configLua.GAME_PORT) then | |
| 54 | + | db.query("UPDATE `znote_global_storage` SET `value`=".. db.escapeString(configLua.GAME_PORT) .." WHERE `key`='GAME_PORT';") | |
| 55 | + | print("= Updated [GAME_PORT] FROM [" .. configSQL.GAME_PORT .. "] to [" .. configLua.GAME_PORT .. "]") | |
| 56 | + | end | |
| 57 | + | if #inserts > 0 then | |
| 58 | + | db.query("INSERT INTO `znote_global_storage` (`key`,`value`) VALUES "..table.concat(inserts,',')..";") | |
| 59 | + | print("= Fixed " .. #inserts .. " missing configurations.") | |
| 60 | + | end | |
| 61 | + | print("=============================") | |
| 62 | + | print("= SERVER_NAME: " .. configLua.SERVER_NAME) | |
| 63 | + | print("= IP: " .. configLua.IP) | |
| 64 | + | print("= GAME_PORT: " .. configLua.GAME_PORT) | |
| 65 | + | print("=============================") | |
| 66 | + | print(" ") | |
| 67 | + | end | |
| 68 | + | znote_loginWebService:register() |
| @@ -0,0 +1,18 @@ | |||
| 1 | + | -- <talkaction words="!report" separator=" " script="adminreport.lua"/> | |
| 2 | + | -- Coded by Dark ShaoOz, modified by Znote | |
| 3 | + | function onSay(player, words, param) | |
| 4 | + | local storage = 6708 -- You can change the storage if its already in use | |
| 5 | + | local delaytime = 30 -- Exhaust In Seconds. | |
| 6 | + | if param == '' then | |
| 7 | + | player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Command param required.") | |
| 8 | + | return true | |
| 9 | + | end | |
| 10 | + | if player:getStorageValue(storage) <= os.time() then | |
| 11 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Your report has been received successfully!") | |
| 12 | + | 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() .. "')") | |
| 13 | + | player:setStorageValue(storage, os.time() + delaytime) | |
| 14 | + | else | |
| 15 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You have to wait " .. player:getStorageValue(storage) - os.time() .. " seconds to report again.") | |
| 16 | + | end | |
| 17 | + | return true | |
| 18 | + | end |
| @@ -0,0 +1,133 @@ | |||
| 1 | + | -- <talkaction words="!shop" script="znoteshop.lua"/> | |
| 2 | + | -- Znote Shop v1.1 for Znote AAC on TFS 1.2+ | |
| 3 | + | function onSay(player, words, param) | |
| 4 | + | local storage = 54073 -- Make sure to select non-used storage. This is used to prevent SQL load attacks. | |
| 5 | + | local cooldown = 15 -- in seconds. | |
| 6 | + | ||
| 7 | + | if player:getStorageValue(storage) <= os.time() then | |
| 8 | + | player:setStorageValue(storage, os.time() + cooldown) | |
| 9 | + | ||
| 10 | + | local type_desc = { | |
| 11 | + | "itemids", | |
| 12 | + | "pending premium (skip)", | |
| 13 | + | "pending gender change (skip)", | |
| 14 | + | "pending character name change (skip)", | |
| 15 | + | "Outfit and addons", | |
| 16 | + | "Mounts", | |
| 17 | + | "Instant house purchase" | |
| 18 | + | } | |
| 19 | + | print("Player: " .. player:getName() .. " triggered !shop talkaction.") | |
| 20 | + | -- Create the query | |
| 21 | + | local orderQuery = db.storeQuery("SELECT `id`, `type`, `itemid`, `count` FROM `znote_shop_orders` WHERE `account_id` = " .. player:getAccountId() .. ";") | |
| 22 | + | local served = false | |
| 23 | + | ||
| 24 | + | -- Detect if we got any results | |
| 25 | + | if orderQuery ~= false then | |
| 26 | + | repeat | |
| 27 | + | -- Fetch order values | |
| 28 | + | local q_id = result.getNumber(orderQuery, "id") | |
| 29 | + | local q_type = result.getNumber(orderQuery, "type") | |
| 30 | + | local q_itemid = result.getNumber(orderQuery, "itemid") | |
| 31 | + | local q_count = result.getNumber(orderQuery, "count") | |
| 32 | + | ||
| 33 | + | local description = "Unknown or custom type" | |
| 34 | + | if type_desc[q_type] ~= nil then | |
| 35 | + | description = type_desc[q_type] | |
| 36 | + | end | |
| 37 | + | print("Processing type "..q_type..": ".. description) | |
| 38 | + | ||
| 39 | + | -- ORDER TYPE 1 (Regular item shop products) | |
| 40 | + | if q_type == 1 then | |
| 41 | + | served = true | |
| 42 | + | -- Get weight | |
| 43 | + | if player:getFreeCapacity() >= ItemType(q_itemid):getWeight(q_count) then | |
| 44 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";") | |
| 45 | + | player:addItem(q_itemid, q_count) | |
| 46 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received " .. q_count .. " x " .. ItemType(q_itemid):getName() .. "!") | |
| 47 | + | else | |
| 48 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "Need more CAP!") | |
| 49 | + | end | |
| 50 | + | end | |
| 51 | + | ||
| 52 | + | -- ORDER TYPE 5 (Outfit and addon) | |
| 53 | + | if q_type == 5 then | |
| 54 | + | served = true | |
| 55 | + | ||
| 56 | + | local itemid = q_itemid | |
| 57 | + | local outfits = {} | |
| 58 | + | ||
| 59 | + | if itemid > 1000 then | |
| 60 | + | local first = math.floor(itemid/1000) | |
| 61 | + | table.insert(outfits, first) | |
| 62 | + | itemid = itemid - (first * 1000) | |
| 63 | + | end | |
| 64 | + | table.insert(outfits, itemid) | |
| 65 | + | ||
| 66 | + | for _, outfitId in pairs(outfits) do | |
| 67 | + | -- Make sure player don't already have this outfit and addon | |
| 68 | + | if not player:hasOutfit(outfitId, q_count) then | |
| 69 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";") | |
| 70 | + | player:addOutfit(outfitId) | |
| 71 | + | player:addOutfitAddon(outfitId, q_count) | |
| 72 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new outfit!") | |
| 73 | + | else | |
| 74 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this outfit and addon!") | |
| 75 | + | end | |
| 76 | + | end | |
| 77 | + | end | |
| 78 | + | ||
| 79 | + | -- ORDER TYPE 6 (Mounts). itemid holds the mount id. | |
| 80 | + | if q_type == 6 then | |
| 81 | + | served = true | |
| 82 | + | -- Make sure player don't already have this mount | |
| 83 | + | if not player:hasMount(q_itemid) then | |
| 84 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";") | |
| 85 | + | player:addMount(q_itemid) | |
| 86 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Congratulations! You have received a new mount!") | |
| 87 | + | else | |
| 88 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You already have this mount!") | |
| 89 | + | end | |
| 90 | + | end | |
| 91 | + | ||
| 92 | + | -- ORDER TYPE 7 (Direct house purchase) | |
| 93 | + | if q_type == 7 then | |
| 94 | + | served = true | |
| 95 | + | local house = House(q_itemid) | |
| 96 | + | -- Logged in player is not necessarily the player that bough the house. So we need to load player from db. | |
| 97 | + | local buyerQuery = db.storeQuery("SELECT `name` FROM `players` WHERE `id` = "..q_count.." LIMIT 1") | |
| 98 | + | if buyerQuery ~= false then | |
| 99 | + | local buyerName = result.getString(buyerQuery, "name") | |
| 100 | + | result.free(buyerQuery) | |
| 101 | + | if house then | |
| 102 | + | db.query("DELETE FROM `znote_shop_orders` WHERE `id` = " .. q_id .. ";") | |
| 103 | + | house:setOwnerGuid(q_count) | |
| 104 | + | 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.") | |
| 105 | + | print("Process complete. [".. buyerName .."] has received house: ["..house:getName().."]") | |
| 106 | + | end | |
| 107 | + | end | |
| 108 | + | end | |
| 109 | + | ||
| 110 | + | -- Add custom order types here | |
| 111 | + | -- Type 1 is for itemids (Already coded here) | |
| 112 | + | -- Type 2 is for premium (Coded on web) | |
| 113 | + | -- Type 3 is for gender change (Coded on web) | |
| 114 | + | -- Type 4 is for character name change (Coded on web) | |
| 115 | + | -- Type 5 is for character outfit and addon (Already coded here) | |
| 116 | + | -- Type 6 is for mounts (Already coded here) | |
| 117 | + | -- Type 7 is for Instant house purchase (Already coded here) | |
| 118 | + | -- So use type 8+ for custom stuff, like etc packages. | |
| 119 | + | -- if q_type == 8 then | |
| 120 | + | -- end | |
| 121 | + | until not result.next(orderQuery) | |
| 122 | + | result.free(orderQuery) | |
| 123 | + | if not served then | |
| 124 | + | player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You have no orders to process in-game.") | |
| 125 | + | end | |
| 126 | + | else | |
| 127 | + | player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "You have no orders.") | |
| 128 | + | end | |
| 129 | + | else | |
| 130 | + | player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Can only be executed once every " .. cooldown .. " seconds. Remaining cooldown: " .. player:getStorageValue(storage) - os.time()) | |
| 131 | + | end | |
| 132 | + | return false | |
| 133 | + | end |
| @@ -0,0 +1,99 @@ | |||
| 1 | + | local creatureevent = CreatureEvent("FirstItems") | |
| 2 | + | ||
| 3 | + | local config = { | |
| 4 | + | [1] = { -- Sorcerer | |
| 5 | + | items = { | |
| 6 | + | {2175, 1}, -- spellbook | |
| 7 | + | {2190, 1}, -- wand of vortex | |
| 8 | + | {8819, 1}, -- magician's robe | |
| 9 | + | {8820, 1}, -- mage hat | |
| 10 | + | {2468, 1}, -- studded legs | |
| 11 | + | {2643, 1}, -- leather boots | |
| 12 | + | {2661, 1} -- scarf | |
| 13 | + | }, | |
| 14 | + | container = { | |
| 15 | + | {2120, 1}, -- rope | |
| 16 | + | {2554, 1}, -- shovel | |
| 17 | + | {7620, 1} -- mana potion | |
| 18 | + | } | |
| 19 | + | }, | |
| 20 | + | [2] = { -- Druid | |
| 21 | + | items = { | |
| 22 | + | {2175, 1}, -- spellbook | |
| 23 | + | {2182, 1}, -- snakebite rod | |
| 24 | + | {8819, 1}, -- magician's robe | |
| 25 | + | {8820, 1}, -- mage hat | |
| 26 | + | {2468, 1}, -- studded legs | |
| 27 | + | {2643, 1}, -- leather boots | |
| 28 | + | {2661, 1} -- scarf | |
| 29 | + | }, | |
| 30 | + | container = { | |
| 31 | + | {2120, 1}, -- rope | |
| 32 | + | {2554, 1}, -- shovel | |
| 33 | + | {7620, 1} -- mana potion | |
| 34 | + | } | |
| 35 | + | }, | |
| 36 | + | [3] = { -- Paladin | |
| 37 | + | items = { | |
| 38 | + | {2525, 1}, -- dwarven shield | |
| 39 | + | {2389, 5}, -- 5 spears | |
| 40 | + | {2660, 1}, -- ranger's cloak | |
| 41 | + | {8923, 1}, -- ranger legs | |
| 42 | + | {2643, 1}, -- leather boots | |
| 43 | + | {2661, 1}, -- scarf | |
| 44 | + | {2480, 1} -- legion helmet | |
| 45 | + | }, | |
| 46 | + | container = { | |
| 47 | + | {2120, 1}, -- rope | |
| 48 | + | {2554, 1}, -- shovel | |
| 49 | + | {7618, 1}, -- health potion | |
| 50 | + | {2456, 1}, -- bow | |
| 51 | + | {2544, 50} -- 50 arrows | |
| 52 | + | } | |
| 53 | + | }, | |
| 54 | + | [4] = { -- Knight | |
| 55 | + | items = { | |
| 56 | + | {2525, 1}, -- dwarven shield | |
| 57 | + | {8601, 1}, -- steel axe | |
| 58 | + | {2465, 1}, -- brass armor | |
| 59 | + | {2460, 1}, -- brass helmet | |
| 60 | + | {2478, 1}, -- brass legs | |
| 61 | + | {2643, 1}, -- leather boots | |
| 62 | + | {2661, 1} -- scarf | |
| 63 | + | }, | |
| 64 | + | container = { | |
| 65 | + | {8602, 1}, -- jagged sword | |
| 66 | + | {2439, 1}, -- daramanian mace | |
| 67 | + | {2120, 1}, -- rope | |
| 68 | + | {2554, 1}, -- shovel | |
| 69 | + | {7618, 1} -- health potion | |
| 70 | + | } | |
| 71 | + | } | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | function creatureevent.onLogin(player) | |
| 75 | + | local targetVocation = config[player:getVocation():getId()] | |
| 76 | + | if not targetVocation then | |
| 77 | + | return true | |
| 78 | + | end | |
| 79 | + | ||
| 80 | + | if player:getLastLoginSaved() ~= 0 then | |
| 81 | + | return true | |
| 82 | + | end | |
| 83 | + | ||
| 84 | + | for i = 1, #targetVocation.items do | |
| 85 | + | player:addItem(targetVocation.items[i][1], targetVocation.items[i][2]) | |
| 86 | + | end | |
| 87 | + | ||
| 88 | + | local backpack = player:addItem(1988) -- backpack | |
| 89 | + | if not backpack then | |
| 90 | + | return true | |
| 91 | + | end | |
| 92 | + | ||
| 93 | + | for i = 1, #targetVocation.container do | |
| 94 | + | backpack:addItem(targetVocation.container[i][1], targetVocation.container[i][2]) | |
| 95 | + | end | |
| 96 | + | return true | |
| 97 | + | end | |
| 98 | + | ||
| 99 | + | creatureevent:register() |
| @@ -0,0 +1,17 @@ | |||
| 1 | + | local creatureevent = CreatureEvent("FirstItemsRook") | |
| 2 | + | ||
| 3 | + | local firstItems = {2050, 2382} -- torch and club | |
| 4 | + | ||
| 5 | + | function creatureevent.onLogin(player) | |
| 6 | + | if player:getLastLoginSaved() <= 0 then | |
| 7 | + | for i = 1, #firstItems do | |
| 8 | + | player:addItem(firstItems[i], 1) | |
| 9 | + | end | |
| 10 | + | player:addItem(player:getSex() == 0 and 2651 or 2650, 1) -- coat | |
| 11 | + | player:addItem(ITEM_BAG, 1) | |
| 12 | + | player:addItem(2674, 1) -- red apple | |
| 13 | + | end | |
| 14 | + | return true | |
| 15 | + | end | |
| 16 | + | ||
| 17 | + | creatureevent:register() |
| @@ -0,0 +1,20 @@ | |||
| 1 | + | TFS 1.6 uses the same revscriptsys API as TFS 1.0 / 1.4, so these files are | |
| 2 | + | identical to the ones in Lua/TFS_10/revscriptsys. | |
| 3 | + | ||
| 4 | + | Step 1: Copy the .lua files you need into data/scripts/ (any sub-folder works). | |
| 5 | + | The shop system needs both: | |
| 6 | + | - shopsystem_talkaction.lua (players type !shop to receive orders) | |
| 7 | + | - shopsystem_globalevent.lua (delivers orders to online players every | |
| 8 | + | 30s, only while they stand in a PZ) | |
| 9 | + | You can run just the talkaction if you prefer manual !shop delivery. | |
| 10 | + | ||
| 11 | + | Step 2: Restart the server (or /reload scripts). | |
| 12 | + | ||
| 13 | + | Notes: | |
| 14 | + | - shopsystem: znote_shop_orders.itemid holds the item id (type 1), the packed | |
| 15 | + | outfit pair male*1000 + female (type 5), the mount id (type 6), or the house | |
| 16 | + | id (type 7, with the buyer player id in `count`). | |
| 17 | + | - znote_login.lua keeps SERVER_NAME / IP / GAME_PORT in znote_global_storage in | |
| 18 | + | sync with config.lua for the AAC login web service. | |
| 19 | + | - powergamers.lua and player_history.lua need the SQL columns / tables listed | |
| 20 | + | at the top of each file before first use. |
| @@ -0,0 +1,135 @@ | |||
| 1 | + | local deathListEnabled = true | |
| 2 | + | local maxDeathRecords = 5 | |
| 3 | + | ||
| 4 | + | local function sendWarStatus(guildId, enemyGuildId, warId, playerName, killerName) | |
| 5 | + | local guild, enemyGuild = Guild(guildId), Guild(enemyGuildId) | |
| 6 | + | if not guild or not enemyGuild then | |
| 7 | + | return | |
| 8 | + | end | |
| 9 | + | ||
| 10 | + | 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) | |
| 11 | + | if resultId then | |
| 12 | + | ||
| 13 | + | local guild1_kills = result.getNumber(resultId, "guild1_kills") | |
| 14 | + | local guild2_kills = result.getNumber(resultId, "guild2_kills") | |
| 15 | + | local limit = result.getNumber(resultId, "limit") | |
| 16 | + | result.free(resultId) | |
| 17 | + | ||
| 18 | + | local members = guild:getMembersOnline() | |
| 19 | + | for i = 1, #members do | |
| 20 | + | 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) | |
| 21 | + | end | |
| 22 | + | ||
| 23 | + | local enemyMembers = enemyGuild:getMembersOnline() | |
| 24 | + | for i = 1, #enemyMembers do | |
| 25 | + | 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) | |
| 26 | + | end | |
| 27 | + | ||
| 28 | + | if guild1_kills >= limit or guild2_kills >= limit then | |
| 29 | + | db.query("UPDATE `guild_wars` SET `status` = 4, `ended` = " .. os.time() .. " WHERE `status` = 1 AND `id` = " .. warId) | |
| 30 | + | Game.broadcastMessage(string.format("%s has just won the war against %s.", guild:getName(), enemyGuild:getName()), MESSAGE_EVENT_ADVANCE) | |
| 31 | + | end | |
| 32 | + | end | |
| 33 | + | end | |
| 34 | + | ||
| 35 | + | local creatureevent = CreatureEvent("PlayerDeath") | |
| 36 | + | ||
| 37 | + | function creatureevent.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified) | |
| 38 | + | local playerId = player:getId() | |
| 39 | + | if nextUseStaminaTime[playerId] then | |
| 40 | + | nextUseStaminaTime[playerId] = nil | |
| 41 | + | end | |
| 42 | + | ||
| 43 | + | player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You are dead.") | |
| 44 | + | if not deathListEnabled then | |
| 45 | + | return | |
| 46 | + | end | |
| 47 | + | ||
| 48 | + | local byPlayer = 0 | |
| 49 | + | local killerName | |
| 50 | + | if killer then | |
| 51 | + | if killer:isPlayer() then | |
| 52 | + | byPlayer = 1 | |
| 53 | + | else | |
| 54 | + | local master = killer:getMaster() | |
| 55 | + | if master and master ~= killer and master:isPlayer() then | |
| 56 | + | killer = master | |
| 57 | + | byPlayer = 1 | |
| 58 | + | end | |
| 59 | + | end | |
| 60 | + | killerName = killer:getName() | |
| 61 | + | else | |
| 62 | + | killerName = "field item" | |
| 63 | + | end | |
| 64 | + | ||
| 65 | + | local byPlayerMostDamage = 0 | |
| 66 | + | local mostDamageKillerName | |
| 67 | + | if mostDamageKiller then | |
| 68 | + | if mostDamageKiller:isPlayer() then | |
| 69 | + | byPlayerMostDamage = 1 | |
| 70 | + | else | |
| 71 | + | local master = mostDamageKiller:getMaster() | |
| 72 | + | if master and master ~= mostDamageKiller and master:isPlayer() then | |
| 73 | + | mostDamageKiller = master | |
| 74 | + | byPlayerMostDamage = 1 | |
| 75 | + | end | |
| 76 | + | end | |
| 77 | + | mostDamageName = mostDamageKiller:getName() | |
| 78 | + | else | |
| 79 | + | mostDamageName = "field item" | |
| 80 | + | end | |
| 81 | + | ||
| 82 | + | local playerGuid = player:getGuid() | |
| 83 | + | 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) .. ")") | |
| 84 | + | local resultId = db.storeQuery("SELECT `player_id` FROM `player_deaths` WHERE `player_id` = " .. playerGuid) | |
| 85 | + | ||
| 86 | + | local deathRecords = 0 | |
| 87 | + | local tmpResultId = resultId | |
| 88 | + | while tmpResultId ~= false do | |
| 89 | + | tmpResultId = result.next(resultId) | |
| 90 | + | deathRecords = deathRecords + 1 | |
| 91 | + | end | |
| 92 | + | ||
| 93 | + | if resultId ~= false then | |
| 94 | + | result.free(resultId) | |
| 95 | + | end | |
| 96 | + | ||
| 97 | + | local limit = deathRecords - maxDeathRecords | |
| 98 | + | if limit > 0 then | |
| 99 | + | db.asyncQuery("DELETE FROM `player_deaths` WHERE `player_id` = " .. playerGuid .. " ORDER BY `time` LIMIT " .. limit) | |
| 100 | + | end | |
| 101 | + | ||
| 102 | + | if byPlayer == 1 then | |
| 103 | + | local targetGuild = player:getGuild() | |
| 104 | + | targetGuild = targetGuild and targetGuild:getId() or 0 | |
| 105 | + | if targetGuild ~= 0 then | |
| 106 | + | local killerGuild = killer:getGuild() | |
| 107 | + | killerGuild = killerGuild and killerGuild:getId() or 0 | |
| 108 | + | if killerGuild ~= 0 and targetGuild ~= killerGuild and isInWar(playerId, killer:getId()) then | |
| 109 | + | local warId = false | |
| 110 | + | resultId = db.storeQuery("SELECT `id` FROM `guild_wars` WHERE `status` = 1 AND ((`guild1` = " .. killerGuild .. " AND `guild2` = " .. targetGuild .. ") OR (`guild1` = " .. targetGuild .. " AND `guild2` = " .. killerGuild .. "))") | |
| 111 | + | if resultId ~= false then | |
| 112 | + | warId = result.getNumber(resultId, "id") | |
| 113 | + | result.free(resultId) | |
| 114 | + | end | |
| 115 | + | ||
| 116 | + | if warId ~= false then | |
| 117 | + | local playerName = player:getName() | |
| 118 | + | db.asyncQuery("INSERT INTO `guildwar_kills` (`killer`, `target`, `killerguild`, `targetguild`, `time`, `warid`) VALUES (" .. db.escapeString(killerName) .. ", " .. db.escapeString(playerName) .. ", " .. killerGuild .. ", " .. targetGuild .. ", " .. os.time() .. ", " .. warId .. ")") | |
| 119 | + | addEvent(sendWarStatus, 1000, killerGuild, targetGuild, warId, playerName, killerName) | |
| 120 | + | end | |
| 121 | + | end | |
| 122 | + | end | |
| 123 | + | end | |
| 124 | + | end | |
| 125 | + | ||
| 126 | + | creatureevent:register() | |
| 127 | + | ||
| 128 | + | local creatureeventLogin = CreatureEvent("creatureeventLogin") | |
| 129 | + | ||
| 130 | + | function creatureeventLogin.onLogin(player) | |
| 131 | + | player:registerEvent("PlayerDeath") | |
| 132 | + | return true | |
| 133 | + | end | |
| 134 | + | ||
| 135 | + | creatureeventLogin:register() |
| @@ -0,0 +1,229 @@ | |||
| 1 | + | -- Auto install tables if we dont got them yet (first install) | |
| 2 | + | db.query([[ | |
| 3 | + | CREATE TABLE IF NOT EXISTS `player_history_skill` ( | |
| 4 | + | `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, | |
| 5 | + | `player_id` int(11) NOT NULL, | |
| 6 | + | `lastlogin` bigint(20) unsigned NOT NULL, | |
| 7 | + | `lastlogout` bigint(20) unsigned NOT NULL, | |
| 8 | + | `town_id` int(11) NOT NULL, | |
| 9 | + | `lastip` int(10) unsigned NOT NULL, | |
| 10 | + | `skull` tinyint(1) NOT NULL, | |
| 11 | + | `blessings` tinyint(2) NOT NULL, | |
| 12 | + | `onlinetime` int(11) NOT NULL, | |
| 13 | + | `balance` bigint(20) unsigned NOT NULL, | |
| 14 | + | `level` int(11) NOT NULL, | |
| 15 | + | `experience` bigint(20) NOT NULL, | |
| 16 | + | `maglevel` int(11) NOT NULL, | |
| 17 | + | `skill_fist` int(10) unsigned NOT NULL, | |
| 18 | + | `skill_club` int(10) unsigned NOT NULL, | |
| 19 | + | `skill_sword` int(10) unsigned NOT NULL, | |
| 20 | + | `skill_axe` int(10) unsigned NOT NULL, | |
| 21 | + | `skill_dist` int(10) unsigned NOT NULL, | |
| 22 | + | `skill_shielding` int(10) unsigned NOT NULL, | |
| 23 | + | `skill_fishing` int(10) unsigned NOT NULL, | |
| 24 | + | PRIMARY KEY (`id`), | |
| 25 | + | FOREIGN KEY (`player_id`) REFERENCES `players` (`id`) ON DELETE CASCADE | |
| 26 | + | ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; | |
| 27 | + | ]]) | |
| 28 | + | ||
| 29 | + | ||
| 30 | + | -- Auto populate table if it is empty | |
| 31 | + | local resultId = db.storeQuery("SELECT `id` FROM `player_history_skill` LIMIT 1;") | |
| 32 | + | if resultId == false then | |
| 33 | + | db.asyncQuery([[ | |
| 34 | + | INSERT INTO `player_history_skill` ( | |
| 35 | + | `player_id`, | |
| 36 | + | `lastlogin`, | |
| 37 | + | `lastlogout`, | |
| 38 | + | `town_id`, | |
| 39 | + | `lastip`, | |
| 40 | + | `skull`, | |
| 41 | + | `blessings`, | |
| 42 | + | `onlinetime`, | |
| 43 | + | `balance`, | |
| 44 | + | `level`, | |
| 45 | + | `experience`, | |
| 46 | + | `maglevel`, | |
| 47 | + | `skill_fist`, | |
| 48 | + | `skill_club`, | |
| 49 | + | `skill_sword`, | |
| 50 | + | `skill_axe`, | |
| 51 | + | `skill_dist`, | |
| 52 | + | `skill_shielding`, | |
| 53 | + | `skill_fishing` | |
| 54 | + | ) | |
| 55 | + | SELECT | |
| 56 | + | `p`.`id` AS `player_id`, | |
| 57 | + | `zp`.`created` AS `lastlogin`, | |
| 58 | + | CASE WHEN `p`.`lastlogout` > 0 | |
| 59 | + | THEN `p`.`lastlogout` | |
| 60 | + | ELSE `zp`.`created` | |
| 61 | + | END AS `lastlogout`, | |
| 62 | + | `p`.`town_id`, | |
| 63 | + | `p`.`lastip`, | |
| 64 | + | `p`.`skull`, | |
| 65 | + | `p`.`blessings`, | |
| 66 | + | `p`.`onlinetime`, | |
| 67 | + | `p`.`balance`, | |
| 68 | + | `p`.`level`, | |
| 69 | + | `p`.`experience`, | |
| 70 | + | `p`.`maglevel`, | |
| 71 | + | `p`.`skill_fist`, | |
| 72 | + | `p`.`skill_club`, | |
| 73 | + | `p`.`skill_sword`, | |
| 74 | + | `p`.`skill_axe`, | |
| 75 | + | `p`.`skill_dist`, | |
| 76 | + | `p`.`skill_shielding`, | |
| 77 | + | `p`.`skill_fishing` | |
| 78 | + | FROM `players` AS `p` | |
| 79 | + | INNER JOIN `znote_players` AS `zp` | |
| 80 | + | ON `p`.`id` = `zp`.`player_id` | |
| 81 | + | ORDER BY `zp`.`created` | |
| 82 | + | ]]) | |
| 83 | + | else | |
| 84 | + | result.free(resultId) | |
| 85 | + | end | |
| 86 | + | ||
| 87 | + | ||
| 88 | + | -- Logout event, triggered by logout, and death | |
| 89 | + | function historyLogoutEvent(player) | |
| 90 | + | local blessdec = 0 | |
| 91 | + | local i = 0 | |
| 92 | + | while player:hasBlessing(i+1) do | |
| 93 | + | blessdec = blessdec+2^i | |
| 94 | + | i = i+1 | |
| 95 | + | end | |
| 96 | + | ||
| 97 | + | local playerGuid = player:getGuid() | |
| 98 | + | db.query([[ | |
| 99 | + | INSERT INTO `player_history_skill` ( | |
| 100 | + | `player_id`, | |
| 101 | + | `lastlogin`, | |
| 102 | + | `lastlogout`, | |
| 103 | + | `town_id`, | |
| 104 | + | `lastip`, | |
| 105 | + | `skull`, | |
| 106 | + | `blessings`, | |
| 107 | + | `onlinetime`, | |
| 108 | + | `balance`, | |
| 109 | + | `level`, | |
| 110 | + | `experience`, | |
| 111 | + | `maglevel`, | |
| 112 | + | `skill_fist`, | |
| 113 | + | `skill_club`, | |
| 114 | + | `skill_sword`, | |
| 115 | + | `skill_axe`, | |
| 116 | + | `skill_dist`, | |
| 117 | + | `skill_shielding`, | |
| 118 | + | `skill_fishing` | |
| 119 | + | ) VALUES ( | |
| 120 | + | ]]..table.concat({ | |
| 121 | + | playerGuid, | |
| 122 | + | player:getLastLoginSaved(), | |
| 123 | + | os.time(), | |
| 124 | + | player:getTown():getId(), | |
| 125 | + | player:getIp(), | |
| 126 | + | player:getSkull(), | |
| 127 | + | blessdec, | |
| 128 | + | "(SELECT `onlinetime` FROM `players` WHERE `id`='"..playerGuid.."') + ".. os.time() - player:getLastLoginSaved(), | |
| 129 | + | player:getBankBalance(), | |
| 130 | + | player:getLevel(), | |
| 131 | + | player:getExperience(), | |
| 132 | + | player:getMagicLevel(), | |
| 133 | + | player:getSkillLevel(SKILL_FIST), | |
| 134 | + | player:getSkillLevel(SKILL_CLUB), | |
| 135 | + | player:getSkillLevel(SKILL_SWORD), | |
| 136 | + | player:getSkillLevel(SKILL_AXE), | |
| 137 | + | player:getSkillLevel(SKILL_DISTANCE), | |
| 138 | + | player:getSkillLevel(SKILL_SHIELD), | |
| 139 | + | player:getSkillLevel(SKILL_FISHING) | |
| 140 | + | }, ",")..[[ | |
| 141 | + | ); | |
| 142 | + | ]]) | |
| 143 | + | end | |
| 144 | + | ||
| 145 | + | ||
| 146 | + | -- Log player state on logout | |
| 147 | + | local player_history_skill = CreatureEvent("player_history_skill") | |
| 148 | + | function player_history_skill.onLogout(player) | |
| 149 | + | --print("2-logout["..player:getName().."]") | |
| 150 | + | historyLogoutEvent(player) | |
| 151 | + | return true | |
| 152 | + | end | |
| 153 | + | player_history_skill:register() | |
| 154 | + | ||
| 155 | + | ||
| 156 | + | -- And on death | |
| 157 | + | local player_history_skill_death = CreatureEvent("player_history_skill_death") | |
| 158 | + | function player_history_skill_death.onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified) | |
| 159 | + | --print("3-death["..creature:getName().."]") | |
| 160 | + | historyLogoutEvent(Player(creature)) | |
| 161 | + | end | |
| 162 | + | player_history_skill_death:register() | |
| 163 | + | ||
| 164 | + | ||
| 165 | + | -- If this is first login, insert current progress | |
| 166 | + | local player_history_skill_login = CreatureEvent("player_history_skill_login") | |
| 167 | + | function player_history_skill_login.onLogin(player) | |
| 168 | + | --print("1-login["..player:getName().."]") | |
| 169 | + | player:registerEvent("player_history_skill_death") | |
| 170 | + | ||
| 171 | + | local playerGuid = player:getGuid() | |
| 172 | + | local resultId = db.storeQuery("SELECT `id` FROM `player_history_skill` WHERE `player_id`="..playerGuid.." LIMIT 1;") | |
| 173 | + | if resultId == false then | |
| 174 | + | db.query([[ | |
| 175 | + | INSERT INTO `player_history_skill` ( | |
| 176 | + | `player_id`, | |
| 177 | + | `lastlogin`, | |
| 178 | + | `lastlogout`, | |
| 179 | + | `town_id`, | |
| 180 | + | `lastip`, | |
| 181 | + | `skull`, | |
| 182 | + | `blessings`, | |
| 183 | + | `onlinetime`, | |
| 184 | + | `balance`, | |
| 185 | + | `level`, | |
| 186 | + | `experience`, | |
| 187 | + | `maglevel`, | |
| 188 | + | `skill_fist`, | |
| 189 | + | `skill_club`, | |
| 190 | + | `skill_sword`, | |
| 191 | + | `skill_axe`, | |
| 192 | + | `skill_dist`, | |
| 193 | + | `skill_shielding`, | |
| 194 | + | `skill_fishing` | |
| 195 | + | ) | |
| 196 | + | SELECT | |
| 197 | + | `p`.`id` AS `player_id`, | |
| 198 | + | `zp`.`created` AS `lastlogin`, | |
| 199 | + | CASE WHEN `p`.`lastlogout` > 0 | |
| 200 | + | THEN `p`.`lastlogout` | |
| 201 | + | ELSE `zp`.`created` | |
| 202 | + | END AS `lastlogout`, | |
| 203 | + | `p`.`town_id`, | |
| 204 | + | `p`.`lastip`, | |
| 205 | + | `p`.`skull`, | |
| 206 | + | `p`.`blessings`, | |
| 207 | + | `p`.`onlinetime`, | |
| 208 | + | `p`.`balance`, | |
| 209 | + | `p`.`level`, | |
| 210 | + | `p`.`experience`, | |
| 211 | + | `p`.`maglevel`, | |
| 212 | + | `p`.`skill_fist`, | |
| 213 | + | `p`.`skill_club`, | |
| 214 | + | `p`.`skill_sword`, | |
| 215 | + | `p`.`skill_axe`, | |
| 216 | + | `p`.`skill_dist`, | |
| 217 | + | `p`.`skill_shielding`, | |
| 218 | + | `p`.`skill_fishing` | |
| 219 | + | FROM `players` AS `p` | |
| 220 | + | INNER JOIN `znote_players` AS `zp` | |
| 221 | + | ON `p`.`id` = `zp`.`player_id` | |
| 222 | + | WHERE `p`.`id` = ]]..playerGuid..[[ | |
| 223 | + | ]]) | |
| 224 | + | else | |
| 225 | + | result.free(resultId) | |
| 226 | + | end | |
| 227 | + | return true | |
| 228 | + | end | |
| 229 | + | player_history_skill_login:register() |
| @@ -0,0 +1,65 @@ | |||
| 1 | + | -- getEternalStorage and setEternalStorage | |
| 2 | + | -- can be added to data/global.lua if you want to use eternal storage for another purpose than this. | |
| 3 | + | -- Regular TFS global storage values get reset every time server reboots. This does not. | |
| 4 | + | local function getEternalStorage(key, parser) | |
| 5 | + | local value = result.getString(db.storeQuery("SELECT `value` FROM `znote_global_storage` WHERE `key` = ".. key .. ";"), "value") | |
| 6 | + | if not value then | |
| 7 | + | if parser then | |
| 8 | + | return false | |
| 9 | + | else | |
| 10 | + | return -1 | |
| 11 | + | end | |
| 12 | + | end | |
| 13 | + | result.free(value) | |
| 14 | + | return tonumber(value) or value | |
| 15 | + | end | |
| 16 | + | ||
| 17 | + | local function setEternalStorage(key, value) | |
| 18 | + | if getEternalStorage(key, true) then | |
| 19 | + | db.query("UPDATE `znote_global_storage` SET `value` = '".. value .. "' WHERE `key` = ".. key .. ";") | |
| 20 | + | else | |
| 21 | + | db.query("INSERT INTO `znote_global_storage` (`key`, `value`) VALUES (".. key ..", ".. value ..");") | |
| 22 | + | end | |
| 23 | + | return true | |
| 24 | + | end | |
| 25 | + | ||
| 26 | + | -- SQL Query to execute: -- | |
| 27 | + | --[[ | |
| 28 | + | ALTER TABLE `znote_players` ADD `exphist_lastexp` BIGINT NOT NULL DEFAULT '0', | |
| 29 | + | ADD `exphist1` BIGINT NOT NULL DEFAULT '0', | |
| 30 | + | ADD `exphist2` BIGINT NOT NULL DEFAULT '0', | |
| 31 | + | ADD `exphist3` BIGINT NOT NULL DEFAULT '0', | |
| 32 | + | ADD `exphist4` BIGINT NOT NULL DEFAULT '0', | |
| 33 | + | ADD `exphist5` BIGINT NOT NULL DEFAULT '0', | |
| 34 | + | ADD `exphist6` BIGINT NOT NULL DEFAULT '0', | |
| 35 | + | ADD `exphist7` BIGINT NOT NULL DEFAULT '0', | |
| 36 | + | ADD `onlinetimetoday` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 37 | + | ADD `onlinetime1` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 38 | + | ADD `onlinetime2` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 39 | + | ADD `onlinetime3` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 40 | + | ADD `onlinetime4` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 41 | + | ADD `onlinetime5` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 42 | + | ADD `onlinetime6` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 43 | + | ADD `onlinetime7` MEDIUMINT UNSIGNED NOT NULL DEFAULT '0', | |
| 44 | + | ADD `onlinetimeall` INT UNSIGNED NOT NULL DEFAULT '0'; | |
| 45 | + | ]]-- | |
| 46 | + | ||
| 47 | + | -- after that execute: -- | |
| 48 | + | --[[ | |
| 49 | + | UPDATE `znote_players` AS `z` INNER JOIN `players` AS `p` ON `p`.`id`=`z`.`player_id` SET `z`.`exphist_lastexp`=`p`.`experience`; | |
| 50 | + | ]]-- | |
| 51 | + | ||
| 52 | + | local globalevent = GlobalEvent("PowerGamers") | |
| 53 | + | ||
| 54 | + | function globalevent.onThink(...) | |
| 55 | + | if tonumber(os.date("%d")) ~= getEternalStorage(23856) then | |
| 56 | + | setEternalStorage(23856, (tonumber(os.date("%d")))) | |
| 57 | + | db.query("UPDATE `znote_players` SET `onlinetime7`=`onlinetime6`, `onlinetime6`=`onlinetime5`, `onlinetime5`=`onlinetime4`, `onlinetime4`=`onlinetime3`, `onlinetime3`=`onlinetime2`, `onlinetime2`=`onlinetime1`, `onlinetime1`=`onlinetimetoday`, `onlinetimetoday`=0;") | |
| 58 | + | 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`;") | |
| 59 | + | end | |
| 60 | + | 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`)") | |
| 61 | + | return true | |
| 62 | + | end | |
| 63 | + | ||
| 64 | + | globalevent:interval(60000) | |
| 65 | + | globalevent:register() |
| @@ -0,0 +1,21 @@ | |||
| 1 | + | local talkaction = TalkAction("!report") | |
| 2 | + | ||
| 3 | + | function talkaction.onSay(player) | |
| 4 | + | local storage = 6708 -- You can change the storage if its already in use | |
| 5 | + | local delaytime = 30 -- Exhaust In Seconds. | |
| 6 | + | if param == '' then | |
| 7 | + | player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "Command param required.") | |
| 8 | + | return true | |
| 9 | + | end | |
| 10 | + | if player:getStorageValue(storage) <= os.time() then | |
| 11 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, "Your report has been received successfully!") | |
| 12 | + | 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() .. "')") | |
| 13 | + | player:setStorageValue(storage, os.time() + delaytime) | |
| 14 | + | else | |
| 15 | + | player:sendTextMessage(MESSAGE_STATUS_WARNING, "You have to wait " .. player:getStorageValue(storage) - os.time() .. " seconds to report again.") | |
| 16 | + | end | |
| 17 | + | return true | |
| 18 | + | end | |
| 19 | + | ||
| 20 | + | talkaction:separator(" ") | |
| 21 | + | talkaction:register() |