From f5c6bdedd6b047bb83a698bb87ae6c733637e171 Mon Sep 17 00:00:00 2001 From: KrecikOnDexin Date: Sun, 24 Dec 2023 14:50:29 +0100 Subject: [PATCH 1/3] charms: initial commit --- data/events/events.xml | 2 +- data/lib/core/player.lua | 39 +++++++++++++++++++++++ data/lib/core/storages.lua | 5 ++- data/scripts/network/bestiary_classes.lua | 5 +++ src/const.h | 2 ++ src/luascript.cpp | 30 +++++++++++++++++ src/luascript.h | 1 + src/player.h | 6 ++++ src/protocolgame.cpp | 6 +++- 9 files changed, 93 insertions(+), 3 deletions(-) diff --git a/data/events/events.xml b/data/events/events.xml index 6af4594efd..d25b17e821 100644 --- a/data/events/events.xml +++ b/data/events/events.xml @@ -45,5 +45,5 @@ - + diff --git a/data/lib/core/player.lua b/data/lib/core/player.lua index 80d9c94e01..03b761c5fe 100644 --- a/data/lib/core/player.lua +++ b/data/lib/core/player.lua @@ -523,6 +523,11 @@ function Player.addBestiaryKills(self, raceId) break end end + + if kills < bestiaryInfo.mastery and newKills >= bestiaryInfo.mastery then + self:addCharmPoints(bestiaryInfo.charmPoints) + end + return self:setBestiaryKills(raceId, newKills) end @@ -737,3 +742,37 @@ function Player.disableLoginMusic(self) msg:delete() return true end + +function Player.isCharmUnlocked(self, charmId) + if self:getStorageValue(PlayerStorageKeys.charmsUnlocked + charmId) == 1 then + return 1 + end + return 0 +end + +function Player.addCharmPoints(self, value) + return self:setStorageValue(PlayerStorageKeys.charmPoints, self:getCharmPoints() + value) +end + +function Player.removeCharmPoints(self, value) + local points = self:getCharmPoints() + if points < value then + return false + end + return self:setStorageValue(PlayerStorageKeys.charmPoints, points - value) +end + +function Player.unlockCharm(self, charmId) + self:setStorageValue(PlayerStorageKeys.charmsUnlocked + charmId, 1) +end + +function Player.setCharmMonster(self, charmId, raceId) + return self:setStorageValue(PlayerStorageKeys.charmsMonster + charmId, raceId) +end + +function Player.getCharmPoints(self) + return math.max(0, self:getStorageValue(PlayerStorageKeys.charmPoints)) +end +function Player.getCharmMonster(self, charmId) + return math.max(0, self:getStorageValue(PlayerStorageKeys.charmsMonster + charmId)) +end diff --git a/data/lib/core/storages.lua b/data/lib/core/storages.lua index 601e5ee419..43762beb51 100644 --- a/data/lib/core/storages.lua +++ b/data/lib/core/storages.lua @@ -41,6 +41,9 @@ PlayerStorageKeys = { achievementsCounter = 20000, achievementsBase = 300000, - -- Bestiary: + -- Bestiary: 400000 - 410101 bestiaryKillsBase = 400000, + charmPoints = 410000, + charmsMonster = 410001, + charmsUnlocked = 410101, } diff --git a/data/scripts/network/bestiary_classes.lua b/data/scripts/network/bestiary_classes.lua index 62aab8b386..1a1361e544 100644 --- a/data/scripts/network/bestiary_classes.lua +++ b/data/scripts/network/bestiary_classes.lua @@ -12,6 +12,11 @@ end local handler = PacketHandler(0xE1) function handler.onReceive(player) + sendCharmData(player) + player:sendResourceBalance(RESOURCE_BANK_BALANCE, player:getBankBalance()) + player:sendResourceBalance(RESOURCE_GOLD_EQUIPPED, player:getMoney()) + player:sendResourceBalance(RESOURCE_CHARM_POINTS, player:getCharmPoints()) + local bestiaryClasses = Game.getBestiary() local msg = NetworkMessage() msg:addByte(0xD5) diff --git a/src/const.h b/src/const.h index 058da2a083..11008a531d 100644 --- a/src/const.h +++ b/src/const.h @@ -608,6 +608,8 @@ enum ResourceTypes_t : uint8_t RESOURCE_PREY_WILDCARDS = 0x0A, RESOURCE_DAILYREWARD_STREAK = 0x14, RESOURCE_DAILYREWARD_JOKERS = 0x15, + + RESOURCE_CHARM_POINTS = 0x1E, // u32 }; enum PlayerFlags : uint64_t diff --git a/src/luascript.cpp b/src/luascript.cpp index ed0bb6c8d5..508bb1435a 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -2084,6 +2084,13 @@ void LuaScriptInterface::registerFunctions() registerEnum(ZONE_NOLOGOUT); registerEnum(ZONE_NORMAL); + registerEnum(RESOURCE_BANK_BALANCE); + registerEnum(RESOURCE_GOLD_EQUIPPED); + registerEnum(RESOURCE_PREY_WILDCARDS); + registerEnum(RESOURCE_DAILYREWARD_STREAK); + registerEnum(RESOURCE_DAILYREWARD_JOKERS); + registerEnum(RESOURCE_CHARM_POINTS); + registerEnum(MAX_LOOTCHANCE); registerEnum(SPELL_INSTANT); @@ -2672,6 +2679,7 @@ void LuaScriptInterface::registerFunctions() registerMethod("Player", "getBankBalance", LuaScriptInterface::luaPlayerGetBankBalance); registerMethod("Player", "setBankBalance", LuaScriptInterface::luaPlayerSetBankBalance); + registerMethod("Player", "sendResourceBalance", LuaScriptInterface::luaPlayerSendResourceBalance); registerMethod("Player", "addItem", LuaScriptInterface::luaPlayerAddItem); registerMethod("Player", "addItemEx", LuaScriptInterface::luaPlayerAddItemEx); @@ -9824,6 +9832,28 @@ int LuaScriptInterface::luaPlayerSetBankBalance(lua_State* L) return 1; } +int LuaScriptInterface::luaPlayerSendResourceBalance(lua_State* L) +{ + // player:sendResourceBalance(resourceType, amount) + Player* player = getUserdata(L, 1); + if (!player) { + lua_pushnil(L); + return 1; + } + + if (!isNumber(L, 2) || !isNumber(L, 3)) { + lua_pushnil(L); + return 1; + } + + ResourceTypes_t resourceType = getNumber(L, 2); + uint64_t amount = getNumber(L, 3); + + player->sendResourceBalance(resourceType, amount); + pushBoolean(L, true); + return 1; +} + int LuaScriptInterface::luaPlayerAddItem(lua_State* L) { // player:addItem(itemId[, count = 1[, canDropOnMap = true[, subType = 1[, slot = CONST_SLOT_WHEREEVER]]]]) diff --git a/src/luascript.h b/src/luascript.h index f2637a590d..b7ddfe79f3 100644 --- a/src/luascript.h +++ b/src/luascript.h @@ -950,6 +950,7 @@ class LuaScriptInterface static int luaPlayerGetBankBalance(lua_State* L); static int luaPlayerSetBankBalance(lua_State* L); + static int luaPlayerSendResourceBalance(lua_State* L); static int luaPlayerAddItem(lua_State* L); static int luaPlayerAddItemEx(lua_State* L); diff --git a/src/player.h b/src/player.h index 949be363d3..2ba8146cd2 100644 --- a/src/player.h +++ b/src/player.h @@ -692,6 +692,12 @@ class Player final : public Creature, public Cylinder client->sendCreatureShield(creature); } } + void sendResourceBalance(ResourceTypes_t resourceType, uint64_t amount) + { + if (client) { + client->sendResourceBalance(resourceType, amount); + } + } void sendSpellCooldown(uint8_t spellId, uint32_t time) { if (client) { diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index 68d137f4ef..6829dfd53d 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -2094,7 +2094,11 @@ void ProtocolGame::sendResourceBalance(const ResourceTypes_t resourceType, uint6 NetworkMessage msg; msg.addByte(0xEE); msg.addByte(resourceType); - msg.add(amount); + if (resourceType == RESOURCE_CHARM_POINTS) { + msg.add(amount); + } else { + msg.add(amount); + } writeToOutputBuffer(msg); } From 7ca651f6d8f3b199868f8cd01eb39c5c12050229 Mon Sep 17 00:00:00 2001 From: KrecikOnDexin Date: Sun, 24 Dec 2023 15:02:02 +0100 Subject: [PATCH 2/3] charms: initial commit - part2 --- data/scripts/network/charms.lua | 407 ++++++++++++++++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 data/scripts/network/charms.lua diff --git a/data/scripts/network/charms.lua b/data/scripts/network/charms.lua new file mode 100644 index 0000000000..708e1131b9 --- /dev/null +++ b/data/scripts/network/charms.lua @@ -0,0 +1,407 @@ +local config = { + faccLimit = 2, + premiumLimit = 6, + premiumDiscount = 0.75, -- percent multipler + charmDataSize = 0, + charmCostPerLevel = 100 +} + +local ACTION_TYPE = { + UNLOCK_RUNE = 0, + SET_CREATURE = 1, + REMOVE_CREATURE = 2 +} + +CHARMS_TYPE = { + WOUND = 0, + ENFLAME = 1, + POISON = 2, + FREEZE = 3, + ZAP = 4, + CURSE = 5, + CRIPPLE = 6, + PARRY = 7, + DODGE = 8, + ADRENALINE_BOOST = 9, + NUMB = 10, + CLEANS = 11, + BLSEE = 12, + SCAVANGE = 13, + GUT = 14, + LOW_BLOW = 15, + DIVINE_WRATH = 16, + VAMPIRIC_EMBRACE = 17, + VOID_CALL = 18, + + LAST = 18 +} + +CHARMS_DATA = { + [CHARMS_TYPE.WOUND] = { + id = CHARMS_TYPE.WOUND, + name = "Wound", + description = "Triggers on a creature with a certain chance and deals 5% of its initial hit points as Physical Damage once.", + percentage = 5, + chance = 10, + effect = CONST_ME_HITAREA, + cost = 600, + damageType = COMBAT_PHYSICALDAMAGE + }, + [CHARMS_TYPE.ENFLAME] = { + id = CHARMS_TYPE.ENFLAME, + name = "Enflame", + description = "Triggers on a creature with a certain chance and deals 5% of its initial hit points as Fire Damage once.", + percentage = 5, + chance = 10, + effect = CONST_ME_HITBYFIRE, + cost = 1000, + damageType = COMBAT_FIREDAMAGE + }, + [CHARMS_TYPE.POISON] = { + id = CHARMS_TYPE.POISON, + name = "Poison", + description = "Triggers on a creature with a certain chance and deals 5% of its initial hit points as Earth Damage once.", + percentage = 5, + chance = 10, + effect = CONST_ME_GREEN_RINGS, + cost = 600, + damageType = COMBAT_EARTHDAMAGE + }, + [CHARMS_TYPE.FREEZE] = { + id = CHARMS_TYPE.FREEZE, + name = "Freez", + description = "Triggers on a creature with a certain chance and deals 5% of its initial hit points as Ice Damage once.", + percentage = 5, + chance = 10, + effect = CONST_ME_ICEATTACK, + cost = 800, + damageType = COMBAT_ICEDAMAGE + }, + [CHARMS_TYPE.ZAP] = { + id = CHARMS_TYPE.ZAP, + name = "Zap", + description = "Triggers on a creature with a certain chance and deals 5% of its initial hit points as Energy Damage once.", + percentage = 5, + chance = 10, + effect = CONST_ME_ENERGYHIT, + cost = 800, + damageType = COMBAT_ENERGYDAMAGE + }, + [CHARMS_TYPE.CURSE] = { + id = CHARMS_TYPE.CURSE, + name = "Curse", + description = "Triggers on a creature with a certain chance and deals 5% of its initial hit points as Death Damage once.", + percentage = 5, + chance = 10, + effect = CONST_ME_SMALLCLOUDS, + cost = 900, + damageType = COMBAT_DEATHDAMAGE + }, + [CHARMS_TYPE.DIVINE_WRATH] = { + id = CHARMS_TYPE.DIVINE_WRATH, + name = "Divine Wrath", + description = "Triggers on a creature with a certain chance and deals 5% of its initial hit points as Holy Damage once.", + percentage = 5, + chance = 10, + effect = CONST_ME_HOLYDAMAGE, + cost = 1500, + damageType = COMBAT_HOLYDAMAGE + }, + [CHARMS_TYPE.DODGE] = { + id = CHARMS_TYPE.DODGE, + name = "Dodge", + description = "Dodges an attack with a certain chance without taking any damage at all.", + chance = 10, + effect = 231, + cost = 1000 + }, +} + +local function getCharmMonsterRemoveCost(player) + if player:isPremium() then + return config.charmCostPerLevel * player:getLevel() * config.premiumDiscount + end + return config.charmCostPerLevel * player:getLevel() +end + +local function getCompletedBestiary(player) + local bestiaryClasses = Game.getBestiary() + local unlockedRaces = {} + for _, class in pairs(bestiaryClasses) do + for _, monsterType in ipairs(class.monsterTypes) do + local info = monsterType:getBestiaryInfo() + if info then + if info.raceId and player:getStorageValue(PlayerStorageKeys.bestiaryKillsBase + info.raceId) >= + info.mastery then + table.insert(unlockedRaces, info.raceId) + end + end + end + end + return unlockedRaces +end + +local function getTableLength(tbl) + local getN = 0 + for n in pairs(tbl) do + getN = getN + 1 + end + return getN +end + +do + config.charmDataSize = getTableLength(CHARMS_DATA) +end + +local function getMaxCharmCreatures(player) + if player:isPremium() then + return config.premiumLimit + else + return config.faccLimit + end +end + +function sendCharmData(player) + local msg = NetworkMessage() + msg:addByte(0xD8) + msg:addU32(player:getCharmPoints()) -- charm points + + msg:addByte(config.charmDataSize) -- charm count + -- charm block + for i = 0, CHARMS_TYPE.LAST, 1 do + if CHARMS_DATA[i] then + msg:addByte(CHARMS_DATA[i].id) -- charmId + msg:addString(CHARMS_DATA[i].name) + msg:addString(CHARMS_DATA[i].description) + msg:addByte(2) -- charm level (0-2) + msg:addU16(CHARMS_DATA[i].cost) -- cost in charm points + msg:addByte(player:isCharmUnlocked(CHARMS_DATA[i].id)) -- is unlocked + local raceIdAssigned = player:getCharmMonster(CHARMS_DATA[i].id) + if raceIdAssigned == 0 then + msg:addByte(0) -- is monster assigned + else + msg:addByte(1) -- is monster assigned + msg:addU16(raceIdAssigned) -- raceId + msg:addU32(getCharmMonsterRemoveCost(player)) -- remove cost + end + end + end + + msg:addByte(getMaxCharmCreatures(player)) -- amount of charms you can assign (0-254), 255 - unlimited + + -- list of monsters you can assign your charms to + local typeIds = getCompletedBestiary(player) + msg:addU16(#typeIds) -- creatures count + for _, value in ipairs(typeIds) do + msg:addU16(value) -- raceId (shows on the list) + end + msg:sendToPlayer(player) + msg:delete() +end + +local function buyCharmRune(player, runeId) + if not CHARMS_DATA[runeId] then + return false + end + + if CHARMS_DATA[runeId].cost <= player:getCharmPoints() then + player:unlockCharm(runeId) + player:popupFYI("You successfully unlocked '" .. CHARMS_DATA[runeId].name .. "' for " .. + CHARMS_DATA[runeId].cost .. " charm points.") + player:removeCharmPoints(CHARMS_DATA[runeId].cost) + else + player:popupFYI("You don't have enough charm points to unlock this rune") + end + + return true +end + +local function isCharmedCreaturesLimitReached(player, count) + if getMaxCharmCreatures(player) > count then + return false + end + + return true +end + +local function isCharmedMonsterAlreadyCharmed(player, raceId) + for i = 0, CHARMS_TYPE.LAST, 1 do + if CHARMS_DATA[i] then + if player:getCharmMonster(CHARMS_DATA[i].id) == raceId then + return true + end + end + end + + return false +end + +local function setCharmCreature(player, runeId, raceId) + local count = 0 + for i = 0, CHARMS_TYPE.LAST, 1 do + if CHARMS_DATA[i] then + if player:getCharmMonster(CHARMS_DATA[i].id) > 0 then + count = count + 1 + end + end + end + + if not CHARMS_DATA[runeId] or not MonsterType(raceId) then + print(">> setCharmCreature: invalid request") + return + end + + if CHARMS_DATA[runeId].id == CHARMS_TYPE.SCAVANGE then + player:popupFYI("This charm is always active, monster not required.") + return + end + + if isCharmedCreaturesLimitReached(player, count) then + player:popupFYI("You don't have any charm slots available.") + return + end + + if isCharmedMonsterAlreadyCharmed(player, raceId) then + player:popupFYI("Charm failed, " .. MonsterType(raceId):getName() .. " is already charmed.") + return + end + + player:setCharmMonster(runeId, raceId) + player:popupFYI("Charm '" .. CHARMS_DATA[runeId].name .. "' has been successfully set to " .. + MonsterType(raceId):getName()) +end + +local function removeCharmCreature(player, runeId) + if not CHARMS_DATA[runeId] then + print(">> removeCharmCreature: invalid request") + return + end + + if player:getTotalMoney() < getCharmMonsterRemoveCost(player) then + player:popupFYI("You don't have enough money.") + return + end + + player:popupFYI("Charm '" .. CHARMS_DATA[runeId].name .. "' has been successfully cleared.") + player:removeTotalMoney(getCharmMonsterRemoveCost(player)) + player:setCharmMonster(runeId, 0) +end + +local handlerBuy = PacketHandler(0xE4) +function handlerBuy.onReceive(player, msg) + local runeId = msg:getByte() + local action = msg:getByte() + local raceId = msg:getU16() + + if action == ACTION_TYPE.UNLOCK_RUNE then + buyCharmRune(player, runeId) + elseif action == ACTION_TYPE.SET_CREATURE then + setCharmCreature(player, runeId, raceId) + elseif action == ACTION_TYPE.REMOVE_CREATURE then + removeCharmCreature(player, runeId) + end + sendCharmData(player) + player:sendResourceBalance(RESOURCE_BANK_BALANCE, player:getBankBalance()) + player:sendResourceBalance(RESOURCE_GOLD_EQUIPPED, player:getMoney()) + player:sendResourceBalance(RESOURCE_CHARM_POINTS, player:getCharmPoints()) +end +handlerBuy:register() + +local combatCharms = {CHARMS_TYPE.WOUND, CHARMS_TYPE.ENFLAME, CHARMS_TYPE.POISON, CHARMS_TYPE.FREEZE, CHARMS_TYPE.ZAP, + CHARMS_TYPE.CURSE, CHARMS_TYPE.DIVINE_WRATH} + +local charmCombat = Combat() +charmCombat:setParameter(COMBAT_PARAM_USECHARGES, 1) + +local isCharmTrigerred = false + +local charmDamage = CreatureEvent("CharmMonsterDamageTrigger") +function charmDamage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, + origin) + + if isCharmTrigerred == true then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + + if not attacker or not creature then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + + if not (creature:isMonster() and attacker:isPlayer()) then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + + local bestiaryInfo = creature:getType():getBestiaryInfo() + if not bestiaryInfo then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + + if bestiaryInfo.raceId == 0 then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + + local charm = nil + for index, value in ipairs(combatCharms) do + if attacker:getCharmMonster(CHARMS_DATA[value].id) == bestiaryInfo.raceId then + charm = CHARMS_DATA[value] + break + end + end + + if not charm then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + + if math.random(0, 100) <= charm.chance then + isCharmTrigerred = true + local damage = math.floor(creature:getMaxHealth() / 100 * charm.percentage) + charmCombat:setParameter(COMBAT_PARAM_TYPE, charm.damageType) + charmCombat:setParameter(COMBAT_PARAM_EFFECT, charm.effect) + charmCombat:setFormula(COMBAT_FORMULA_DAMAGE, -damage, 0, -damage, 0) + charmCombat:execute(attacker, Variant(creature:getId())) + isCharmTrigerred = false + end + return primaryDamage, primaryType, secondaryDamage, secondaryType +end +charmDamage:register() + +local charmDamage = CreatureEvent("CharmPlayerDamageReceived") +function charmDamage.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) + if not attacker then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + + if not (creature:isPlayer() and attacker:isMonster()) then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + + local bestiaryInfo = attacker:getType():getBestiaryInfo() + if not bestiaryInfo then + return primaryDamage, primaryType, secondaryDamage, secondaryType + end + + local charm = CHARMS_DATA[CHARMS_TYPE.DODGE] + + if math.random(0, 100) <= charm.chance and creature:getCharmMonster(charm.id) == bestiaryInfo.raceId and bestiaryInfo.raceId ~= 0 then + creature:getPosition():sendMagicEffect(charm.effect) + creature:sendTextMessage(MESSAGE_DAMAGE_RECEIVED, "You blocked damage from " .. attacker:getDescription()) + return 0, primaryType, 0, secondaryType + end + + return primaryDamage, primaryType, secondaryDamage, secondaryType +end +charmDamage:register() + +local playerCharm = CreatureEvent("CharmPlayerDamageReceivedLogin") +function playerCharm.onLogin(player) + player:registerEvent("CharmPlayerDamageReceived") + return true +end +playerCharm:register() + +local ec = EventCallback +ec.onSpawn = function(monster, position, startup, artificial) + monster:registerEvent("CharmMonsterDamageTrigger") + return true +end +ec:register() \ No newline at end of file From a085b03ffafb341a01ab8f768771f8c3ecf525ce Mon Sep 17 00:00:00 2001 From: KrecikOnDexin Date: Wed, 27 Dec 2023 10:40:10 +0100 Subject: [PATCH 3/3] update --- data/lib/core/player.lua | 30 ++++++++++++++++++++++-------- data/scripts/network/charms.lua | 23 ++++++++++++----------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/data/lib/core/player.lua b/data/lib/core/player.lua index 03b761c5fe..c8b6d4a53e 100644 --- a/data/lib/core/player.lua +++ b/data/lib/core/player.lua @@ -743,19 +743,26 @@ function Player.disableLoginMusic(self) return true end +local charmStatus = { + UNLOCKED = 1, + LOCKED = 0, +} + function Player.isCharmUnlocked(self, charmId) - if self:getStorageValue(PlayerStorageKeys.charmsUnlocked + charmId) == 1 then - return 1 + if (self:getStorageValue(PlayerStorageKeys.charmsUnlocked + charmId) or 0) == charmStatus.UNLOCKED then + return charmStatus.UNLOCKED end - return 0 + return charmStatus.LOCKED end function Player.addCharmPoints(self, value) - return self:setStorageValue(PlayerStorageKeys.charmPoints, self:getCharmPoints() + value) + local points = self:getCharmPoints() or 0 + return self:setStorageValue(PlayerStorageKeys.charmPoints, points + value) end function Player.removeCharmPoints(self, value) - local points = self:getCharmPoints() + local points = self:getCharmPoints() or 0 + if points < value then return false end @@ -763,16 +770,23 @@ function Player.removeCharmPoints(self, value) end function Player.unlockCharm(self, charmId) - self:setStorageValue(PlayerStorageKeys.charmsUnlocked + charmId, 1) + self:setStorageValue(PlayerStorageKeys.charmsUnlocked + charmId, charmStatus.UNLOCKED) +end + +function Player.removeCharm(self, charmId) + self:setStorageValue(PlayerStorageKeys.charmsUnlocked + charmId, charmStatus.LOCKED) end function Player.setCharmMonster(self, charmId, raceId) + if self:isCharmUnlocked(charmId) == charmStatus.LOCKED then + return false + end return self:setStorageValue(PlayerStorageKeys.charmsMonster + charmId, raceId) end function Player.getCharmPoints(self) - return math.max(0, self:getStorageValue(PlayerStorageKeys.charmPoints)) + return math.max(0, self:getStorageValue(PlayerStorageKeys.charmPoints) or 0) end function Player.getCharmMonster(self, charmId) - return math.max(0, self:getStorageValue(PlayerStorageKeys.charmsMonster + charmId)) + return math.max(0, self:getStorageValue(PlayerStorageKeys.charmsMonster + charmId) or 0) end diff --git a/data/scripts/network/charms.lua b/data/scripts/network/charms.lua index 708e1131b9..b2774539ec 100644 --- a/data/scripts/network/charms.lua +++ b/data/scripts/network/charms.lua @@ -117,7 +117,7 @@ CHARMS_DATA = { }, } -local function getCharmMonsterRemoveCost(player) +local function getCharmRemoveCost(player) if player:isPremium() then return config.charmCostPerLevel * player:getLevel() * config.premiumDiscount end @@ -169,20 +169,21 @@ function sendCharmData(player) msg:addByte(config.charmDataSize) -- charm count -- charm block for i = 0, CHARMS_TYPE.LAST, 1 do - if CHARMS_DATA[i] then - msg:addByte(CHARMS_DATA[i].id) -- charmId - msg:addString(CHARMS_DATA[i].name) - msg:addString(CHARMS_DATA[i].description) + local charmData = CHARMS_DATA[i] + if charmData then + msg:addByte(charmData.id) -- charmId + msg:addString(charmData.name) + msg:addString(charmData.description) msg:addByte(2) -- charm level (0-2) - msg:addU16(CHARMS_DATA[i].cost) -- cost in charm points - msg:addByte(player:isCharmUnlocked(CHARMS_DATA[i].id)) -- is unlocked - local raceIdAssigned = player:getCharmMonster(CHARMS_DATA[i].id) + msg:addU16(charmData.cost) -- cost in charm points + msg:addByte(player:isCharmUnlocked(charmData.id)) -- is unlocked + local raceIdAssigned = player:getCharmMonster(charmData.id) if raceIdAssigned == 0 then msg:addByte(0) -- is monster assigned else msg:addByte(1) -- is monster assigned msg:addU16(raceIdAssigned) -- raceId - msg:addU32(getCharmMonsterRemoveCost(player)) -- remove cost + msg:addU32(getCharmRemoveCost(player)) -- remove cost end end end @@ -277,13 +278,13 @@ local function removeCharmCreature(player, runeId) return end - if player:getTotalMoney() < getCharmMonsterRemoveCost(player) then + if player:getTotalMoney() < getCharmRemoveCost(player) then player:popupFYI("You don't have enough money.") return end player:popupFYI("Charm '" .. CHARMS_DATA[runeId].name .. "' has been successfully cleared.") - player:removeTotalMoney(getCharmMonsterRemoveCost(player)) + player:removeTotalMoney(getCharmRemoveCost(player)) player:setCharmMonster(runeId, 0) end