fix rune charges display when using hotkey. Add minimap scan talkaction. Add znoteshop talkaction.

This commit is contained in:
ErikasKontenis 2020-01-19 14:18:08 +02:00
parent 544a6b34a4
commit 80a7d99866
6 changed files with 289 additions and 4 deletions

View File

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

View File

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

View File

@ -36,6 +36,7 @@
<talkaction words="/ghost" script="ghost.lua" /> <talkaction words="/ghost" script="ghost.lua" />
<talkaction words="/clean" script="clean.lua" /> <talkaction words="/clean" script="clean.lua" />
<talkaction words="/storagevalue" separator=" " script="storagevalue.lua" /> <talkaction words="/storagevalue" separator=" " script="storagevalue.lua" />
<talkaction words="/minimap" separator=" " script="minimap_scan.lua" />
<!-- player talkactions --> <!-- player talkactions -->
<talkaction words="!buypremium" script="buyprem.lua"/> <talkaction words="!buypremium" script="buyprem.lua"/>
@ -49,6 +50,7 @@
<talkaction words="!online" script="online.lua"/> <talkaction words="!online" script="online.lua"/>
<talkaction words="!serverinfo" script="serverinfo.lua"/> <talkaction words="!serverinfo" script="serverinfo.lua"/>
<talkaction words="!share" script="experienceshare.lua"/> <talkaction words="!share" script="experienceshare.lua"/>
<talkaction words="!shop" script="znoteshop.lua"/>
<!-- test talkactions --> <!-- test talkactions -->
<talkaction words="!z" separator=" " script="magiceffect.lua"/> <talkaction words="!z" separator=" " script="magiceffect.lua"/>

View File

@ -324,7 +324,14 @@ bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item*
player->stopWalk(); player->stopWalk();
if (isHotkey) { if (isHotkey) {
showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), (!item->getFluidType() ? -1 : item->getSubType()))); uint32_t count = 0;
if (item->isRune()) {
count = player->getRuneCount(item->getID());
} else {
count = player->getItemTypeCount(item->getID(), (!item->getFluidType() ? -1 : item->getSubType()));
}
showUseHotkeyMessage(player, item, count);
} }
ReturnValue ret = internalUseItem(player, pos, index, item, isHotkey); ReturnValue ret = internalUseItem(player, pos, index, item, isHotkey);
@ -354,7 +361,15 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position&
} }
if (isHotkey) { if (isHotkey) {
showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), (!item->getFluidType() ? -1 : item->getSubType()))); uint32_t count = 0;
if (item->isRune()) {
count = player->getRuneCount(item->getID());
}
else {
count = player->getItemTypeCount(item->getID(), (!item->getFluidType() ? -1 : item->getSubType()));
}
showUseHotkeyMessage(player, item, count);
} }
if (!action->executeUse(player, item, fromPos, action->getTarget(player, creature, toPos, toStackPos), toPos, isHotkey)) { if (!action->executeUse(player, item, fromPos, action->getTarget(player, creature, toPos, toStackPos), toPos, isHotkey)) {

View File

@ -2540,6 +2540,31 @@ uint32_t Player::getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) con
return count; return count;
} }
// 7.8 mechanics to count runes by charges requires different logic to be implemented
uint32_t Player::getRuneCount(uint16_t itemId) const
{
uint32_t count = 0;
for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
Item* item = inventory[i];
if (!item) {
continue;
}
if (item->getID() == itemId) {
count += item->getCharges();
}
if (Container* container = item->getContainer()) {
for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
if ((*it)->getID() == itemId) {
count += (*it)->getCharges();
}
}
}
}
return count;
}
bool Player::removeItemOfType(uint16_t itemId, uint32_t amount, int32_t subType, bool ignoreEquipped/* = false*/) const bool Player::removeItemOfType(uint16_t itemId, uint32_t amount, int32_t subType, bool ignoreEquipped/* = false*/) const
{ {
if (amount == 0) { if (amount == 0) {

View File

@ -972,6 +972,7 @@ class Player final : public Creature, public Cylinder
size_t getFirstIndex() const final; size_t getFirstIndex() const final;
size_t getLastIndex() const final; size_t getLastIndex() const final;
uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const final; uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const final;
uint32_t getRuneCount(uint16_t itemId) const;
std::map<uint32_t, uint32_t>& getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const final; std::map<uint32_t, uint32_t>& getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const final;
Thing* getThing(size_t index) const final; Thing* getThing(size_t index) const final;