diff --git a/data/XML/vocations.xml b/data/XML/vocations.xml index b681928..bc2c272 100644 --- a/data/XML/vocations.xml +++ b/data/XML/vocations.xml @@ -18,7 +18,7 @@ - + diff --git a/data/talkactions/scripts/melee_damage.lua b/data/talkactions/scripts/melee_damage.lua deleted file mode 100644 index 0b5bb47..0000000 --- a/data/talkactions/scripts/melee_damage.lua +++ /dev/null @@ -1,139 +0,0 @@ -local weaponSkillsConfig = { - [WEAPON_SWORD] = SKILL_SWORD, - [WEAPON_CLUB] = SKILL_CLUB, - [WEAPON_AXE] = SKILL_AXE, - [WEAPON_DISTANCE] = SKILL_DISTANCE -} - -local function getWeapon(player) - local itemLeft = player:getSlotItem(CONST_SLOT_LEFT) - if itemLeft and itemLeft:getType():getWeaponType() ~= WEAPON_NONE and itemLeft:getType():getWeaponType() ~= WEAPON_SHIELD and itemLeft:getType():getWeaponType() ~= WEAPON_AMMO then - return itemLeft - end - - local itemRight = player:getSlotItem(CONST_SLOT_RIGHT) - if itemRight and itemRight:getType():getWeaponType() ~= WEAPON_NONE and itemRight:getType():getWeaponType() ~= WEAPON_SHIELD and itemRight:getType():getWeaponType() ~= WEAPON_AMMO then - return itemRight - end -end - -local function getDamageFormula(attack, attackSkill, fightMode) - local damage = attack - if fightMode == FIGHTMODE_ATTACK then - damage = math.floor(damage + 2 * damage / 10) - elseif fightMode == FIGHTMODE_DEFENSE then - damage = math.floor(damage - 4 * damage / 10) - end - - local formula = math.floor((5 * (attackSkill) + 50) * damage) - return formula -end - -local function getDamage(attack, attackSkill, fightMode, random1, random2) - local formula = getDamageFormula(attack, attackSkill, fightMode) - local randResult = math.floor(random1 % 100); - local damage = -math.floor((math.ceil(formula * ((random2 % 100 + randResult) / 2) / 10000.))); - return damage -end - -local function getMinDamage(attack, attackSkill, fightMode) - local formula = getDamageFormula(attack, attackSkill, fightMode) - local damage = -math.floor((math.ceil(formula * (0 / 2) / 10000.))); - return damage -end - -local function getMaxDamage(attack, attackSkill, fightMode) - local formula = getDamageFormula(attack, attackSkill, fightMode) - local damage = -math.floor((math.ceil(formula * (198 / 2) / 10000.))); - return damage -end - -local function setVocationDamageIncrease(vocationId, damage) - local knightCloseAttackDamageIncreasePercent = configManager.getNumber(configKeys.KNIGHT_CLOSE_ATTACK_DAMAGE_INCREASE_PERCENT) - if knightCloseAttackDamageIncreasePercent ~= -1 then - damage = math.floor(damage + damage * knightCloseAttackDamageIncreasePercent / 100); - end - - return damage -end - -local function getDefense(creature, random1, random2) - local totalDefense = creature:getType():getDefense() + 1 - local defenseSkill = creature:getType():getSkill() - - local formula = math.floor((5 * (defenseSkill) + 50) * totalDefense) - local randresult = math.floor(random1 % 100) - - return math.floor(formula * ((random2 % 100 + randresult) / 2) / 10000.) -end - -local function rshift(x, by) - return math.floor(x / 2 ^ by) -end - -local function getArmor(creature, rand) - local armor = creature:getType():getArmor() - if armor > 1 then - return rand % rshift(armor, 1) + rshift(armor, 1); - end - - return armor -end - -local function setDamageBlock(creature, damage, random1, random2, randomArmor) - if bit.band(creature:getType():getCombatImmunities(), COMBAT_PHYSICALDAMAGE) == COMBAT_PHYSICALDAMAGE then - return 0 - end - - damage = damage + getDefense(creature, random1, random2) - - if damage >= 0 then - return 0 - end - - if damage < 0 then - print (getArmor(creature, randomArmor)) - damage = damage + getArmor(creature, randomArmor) - if damage >= 0 then - return 0 - end - end - - return damage -end - -function onSay(player, words, param) - local split = param:split(",") - if split[3] == nil then - --player:sendCancelMessage("Insufficient parameters [(exp,skill,loot,magic),percentage,hours].") - --return false - end - - local creature = Creature("Troll") - if creature == nil then - player:sendCancelMessage("The monster does not exist.") - end - - local skillType = SKILL_FIST - local attack = 7 - local weapon = getWeapon(player) - if weapon ~= nil then - attack = weapon:getType():getAttack() - skillType = weaponSkillsConfig[weapon:getType():getWeaponType()] - end - - local damage = setDamageBlock(creature, setVocationDamageIncrease(player:getVocation():getId(), getDamage(attack, player:getSkillLevel(skillType), FIGHTMODE_ATTACK, os.rand(), os.rand())), os.rand(), os.rand(), os.rand()) - local minDamage = setDamageBlock(creature, setVocationDamageIncrease(player:getVocation():getId(), getDamage(attack, player:getSkillLevel(skillType), FIGHTMODE_ATTACK, 0, 0)), 99, 99, rshift(creature:getType():getArmor(), 1) + 1) - local maxDamage = setDamageBlock(creature, setVocationDamageIncrease(player:getVocation():getId(), getDamage(attack, player:getSkillLevel(skillType), FIGHTMODE_ATTACK, 99, 99)), 0, 0, 0) - - print ("TotalDamage:" .. damage) - print ("MinDamage:" .. minDamage) - print ("MaxDamage:" .. maxDamage) - --print (weapon:getType():getAttack()) - --print (shield:getName()) - local message = "" - message = message .. "Offensive Fighting\n" - message = message .. "- Daily kills for red skull " .. 123 .. "\n" - --player:showTextDialog(1998, message, false) - return false -end \ No newline at end of file diff --git a/data/talkactions/scripts/physical_damage.lua b/data/talkactions/scripts/physical_damage.lua new file mode 100644 index 0000000..14dc775 --- /dev/null +++ b/data/talkactions/scripts/physical_damage.lua @@ -0,0 +1,284 @@ +local weaponSkillsConfig = { + [WEAPON_SWORD] = SKILL_SWORD, + [WEAPON_CLUB] = SKILL_CLUB, + [WEAPON_AXE] = SKILL_AXE, + [WEAPON_DISTANCE] = SKILL_DISTANCE +} + +local skillNameConfig = { + [SKILL_SWORD] = "Sword Fighting", + [SKILL_CLUB] = "Club Fighting", + [SKILL_AXE] = "Axe Fighting", + [SKILL_DISTANCE] = "Distance Fighting", + [SKILL_FIST] = "Fist Fighting" +} + +local function ltrim(s) + if s == nil then + return s + end + + return s:match'^%s*(.*)' +end + +local function getWeapon(player) + local itemLeft = player:getSlotItem(CONST_SLOT_LEFT) + if itemLeft and itemLeft:getType():getWeaponType() ~= WEAPON_NONE and itemLeft:getType():getWeaponType() ~= WEAPON_SHIELD and itemLeft:getType():getWeaponType() ~= WEAPON_AMMO and itemLeft:getType():getWeaponType() ~= WEAPON_WAND then + return itemLeft:getType() + end + + local itemRight = player:getSlotItem(CONST_SLOT_RIGHT) + if itemRight and itemRight:getType():getWeaponType() ~= WEAPON_NONE and itemRight:getType():getWeaponType() ~= WEAPON_SHIELD and itemRight:getType():getWeaponType() ~= WEAPON_AMMO and itemRight:getType():getWeaponType() ~= WEAPON_WAND then + return itemRight:getType() + end +end + +local function getAmmunition(player) + local item = player:getSlotItem(CONST_SLOT_AMMO) + if item and item:getType():getWeaponType() == WEAPON_AMMO then + return item:getType() + end +end + +local function getAttack(weapon, ammunition) + local attack = 7 + if weapon ~= nil then + attack = weapon:getAttack() + if weapon:getWeaponType() == WEAPON_DISTANCE and weapon:getAmmoType() ~= 0 then + if ammunition ~= nil and ammunition:getAmmoType() == weapon:getAmmoType() then + attack = attack + ammunition:getAttack() + end + end + end + + return attack +end + +local function getDamageFormula(attack, attackSkill, fightMode) + local damage = attack + if fightMode == FIGHTMODE_ATTACK then + damage = math.floor(damage + 2 * damage / 10) + elseif fightMode == FIGHTMODE_DEFENSE then + damage = math.floor(damage - 4 * damage / 10) + end + + local formula = math.floor((5 * (attackSkill) + 50) * damage) + return formula +end + +local function getDamage(attack, attackSkill, fightMode, random1, random2) + local formula = getDamageFormula(attack, attackSkill, fightMode) + local randResult = math.floor(random1 % 100); + local damage = -math.floor((math.ceil(formula * ((random2 % 100 + randResult) / 2) / 10000.))); + return damage +end + +local function setVocationDamageIncrease(vocationId, damage) + if vocationId == 4 or vocationId == 8 then + local knightCloseAttackDamageIncreasePercent = configManager.getNumber(configKeys.KNIGHT_CLOSE_ATTACK_DAMAGE_INCREASE_PERCENT) + if knightCloseAttackDamageIncreasePercent ~= -1 then + damage = math.floor(damage + damage * knightCloseAttackDamageIncreasePercent / 100); + end + elseif vocationId == 3 or vocationId == 7 then + local paladinRangeAttackDamageIncreasePercent = configManager.getNumber(configKeys.PALADIN_RANGE_ATTACK_DAMAGE_INCREASE_PERCENT) + if paladinRangeAttackDamageIncreasePercent ~= -1 then + damage = math.floor(damage + damage * paladinRangeAttackDamageIncreasePercent / 100); + end + end + + return damage +end + +local function getDefense(creature, random1, random2) + local totalDefense = creature:getType():getDefense() + 1 + local defenseSkill = creature:getType():getSkill() + + local formula = math.floor((5 * (defenseSkill) + 50) * totalDefense) + local randresult = math.floor(random1 % 100) + + return math.floor(formula * ((random2 % 100 + randresult) / 2) / 10000.) +end + +local function rshift(x, by) + return math.floor(x / 2 ^ by) +end + +local function getArmor(creature, rand) + local armor = creature:getType():getArmor() + if armor > 1 then + return rand % rshift(armor, 1) + rshift(armor, 1); + end + + return armor +end + +local function setPhysicalDamageBlock(creature, damage, random1, random2, randomArmor) + if bit.band(creature:getType():getCombatImmunities(), COMBAT_PHYSICALDAMAGE) == COMBAT_PHYSICALDAMAGE then + return 0 + end + + damage = damage + getDefense(creature, random1, random2) + + if damage >= 0 then + return 0 + end + + if damage < 0 then + damage = damage + getArmor(creature, randomArmor) + if damage >= 0 then + return 0 + end + end + + return damage +end + +local function isThrowableHit(weapon, ammunition, skillValue, rand) + local distance = 15 -- we consider distance is always the best + local hitChance = 75 -- throwables and such + + if weapon:getAmmoType() ~= 0 then + hitChance = 90 -- bows and crossbows + if ammunition == nil or ammunition:getAmmoType() ~= weapon:getAmmoType() then + hitChance = -1 -- no ammo or invalid ammo + end + end + + if rand % distance <= skillValue then + return rand % 100 <= hitChance + end + + return false +end + +local function getTotalDamage(creature, weapon, ammunition, vocation, attack, skillValue, fightMode) + local damage = setVocationDamageIncrease(vocation:getId(), getDamage(attack, skillValue, fightMode, os.rand(), os.rand())) + local minDamage = setVocationDamageIncrease(vocation:getId(), getDamage(attack, skillValue, fightMode, 0, 0)) + local maxDamage = setVocationDamageIncrease(vocation:getId(), getDamage(attack, skillValue, fightMode, 99, 99)) + + if weapon ~= nil and weapon:getWeaponType() == WEAPON_DISTANCE then + if isThrowableHit(weapon, ammunition, skillValue, os.rand()) then + damage = setPhysicalDamageBlock(creature, damage, os.rand(), os.rand(), os.rand()) + else + damage = 0 + end + minDamage = 0 + if isThrowableHit(weapon, ammunition, skillValue, 0) then + maxDamage = setPhysicalDamageBlock(creature, maxDamage, 0, 0, 0) + else + maxDamage = 0 + end + else + damage = setPhysicalDamageBlock(creature, damage, os.rand(), os.rand(), os.rand()) + minDamage = setPhysicalDamageBlock(creature, minDamage, 99, 99, rshift(creature:getType():getArmor(), 1) + 1) + maxDamage = setPhysicalDamageBlock(creature, maxDamage, 0, 0, 0) + end + + local container = {} + container[0] = damage + container[1] = minDamage + container[2] = maxDamage + return container +end + +function onSay(player, words, param) + local split = param:split(",") + local creatureName = ltrim(split[1]) + local skillValueNumber = ltrim(split[2]) + local vocationName = ltrim(split[3]) + local weaponName = ltrim(split[4]) + local ammunitionName = ltrim(split[5]) + + local creature = Creature("Troll") + if creatureName ~= nil then + creature = Creature(creatureName) + if creature == nil then + player:sendCancelMessage("The monster does not exist.") + return false + end + else + creatureName = creature:getName() + end + + local weapon = getWeapon(player) + if weaponName ~= nil then + weapon = ItemType(weaponName) + if weapon == nil or weapon:getWeaponType() == WEAPON_NONE or weapon:getWeaponType() == WEAPON_SHIELD or weapon:getWeaponType() == WEAPON_AMMO or weapon:getWeaponType() == WEAPON_WAND then + player:sendCancelMessage("The weapon does not exist.") + return false + end + else + if weapon ~= nil then + weaponName = weapon:getName() + end + end + + local ammunition = getAmmunition(player) + if ammunitionName ~= nil then + ammunition = ItemType(ammunitionName) + if ammunition == nil or ammunition:getWeaponType() ~= WEAPON_AMMO then + player:sendCancelMessage("The ammunition does not exist.") + return false + end + else + if ammunition ~= nil then + ammunitionName = ammunition:getName() + end + end + + local skillType = SKILL_FIST + local attack = getAttack(weapon, ammunition) + if weapon ~= nil then + skillType = weaponSkillsConfig[weapon:getWeaponType()] + end + + local skillValue = player:getSkillLevel(skillType) + if skillValueNumber ~= nil then + if tonumber(skillValueNumber) > 0 then + skillValue = tonumber(skillValueNumber) + else + player:sendCancelMessage("The skill value has to be a number and greater than zero.") + return false + end + end + + local vocation = player:getVocation() + if vocationName ~= nil then + vocation = Vocation(vocationName) + if vocation == nil then + player:sendCancelMessage("The vocation does not exist.") + return false + end + else + vocationName = vocation:getName() + end + + local commandStr = "Executing command: !physicaldamage " .. creatureName .. ", " .. skillValue .. ", " .. vocationName .. "" + + if weapon ~= nil then + commandStr = commandStr .. ", " .. weaponName + end + + if ammunition ~= nil and weapon ~= nil and weapon:getWeaponType() == WEAPON_DISTANCE then + commandStr = commandStr .. ", " .. ammunitionName + end + player:sendCancelMessage(commandStr) + + local offensiveDamageContainer = getTotalDamage(creature, weapon, ammunition, vocation, attack, skillValue, FIGHTMODE_ATTACK) + local balancedDamageContainer = getTotalDamage(creature, weapon, ammunition, vocation, attack, skillValue, FIGHTMODE_BALANCED) + local defensiveDamageContainer = getTotalDamage(creature, weapon, ammunition, vocation, attack, skillValue, FIGHTMODE_DEFENSE) + + local message = "" + message = message .. "Vocation: " .. vocation:getName() .. "\n" + message = message .. "Skill Name: " .. skillNameConfig[skillType] .. ", Skill Value: " .. skillValue .. "\n" + message = message .. "Weapon: " .. (weaponName or 'none') .. " (atk: " .. attack .. ")\n" + message = message .. "Creature: " .. creatureName .. " (arm: " .. creature:getType():getArmor() .. ", def: " .. creature:getType():getDefense() .. ", skill: " .. creature:getType():getSkill() .. ")\n" + message = message .. "\nOffensive Fighting Damage\n" + message = message .. "Min: " .. offensiveDamageContainer[1] .. ", Max: " .. offensiveDamageContainer[2] .. "\n" + message = message .. "\nBalanced Fighting Damage\n" + message = message .. "Min: " .. balancedDamageContainer[1] .. ", Max: " .. balancedDamageContainer[2] .. "\n" + message = message .. "\nDefensive Fighting Damage\n" + message = message .. "Min: " .. defensiveDamageContainer[1] .. ", Max: " .. defensiveDamageContainer[2] .. "\n" + player:showTextDialog(weapon and weapon:getId() or 2950, message, false) + return false +end \ No newline at end of file diff --git a/data/talkactions/talkactions.xml b/data/talkactions/talkactions.xml index 104f173..8407033 100644 --- a/data/talkactions/talkactions.xml +++ b/data/talkactions/talkactions.xml @@ -56,7 +56,7 @@ - + diff --git a/src/luascript.cpp b/src/luascript.cpp index 5b17983..51015e0 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -2267,6 +2267,7 @@ void LuaScriptInterface::registerFunctions() registerMethod("ItemType", "getDefense", LuaScriptInterface::luaItemTypeGetDefense); registerMethod("ItemType", "getArmor", LuaScriptInterface::luaItemTypeGetArmor); registerMethod("ItemType", "getWeaponType", LuaScriptInterface::luaItemTypeGetWeaponType); + registerMethod("ItemType", "getAmmoType", LuaScriptInterface::luaItemTypeGetAmmoType); registerMethod("ItemType", "getTransformEquipId", LuaScriptInterface::luaItemTypeGetTransformEquipId); registerMethod("ItemType", "getTransformDeEquipId", LuaScriptInterface::luaItemTypeGetTransformDeEquipId); @@ -10339,6 +10340,19 @@ int LuaScriptInterface::luaItemTypeGetWeaponType(lua_State* L) return 1; } +int LuaScriptInterface::luaItemTypeGetAmmoType(lua_State* L) +{ + // itemType:getAmmoType() + const ItemType* itemType = getUserdata(L, 1); + if (itemType) { + lua_pushnumber(L, itemType->ammoType); + } + else { + lua_pushnil(L); + } + return 1; +} + int LuaScriptInterface::luaItemTypeGetTransformEquipId(lua_State* L) { // itemType:getTransformEquipId() diff --git a/src/luascript.h b/src/luascript.h index 7a93938..3c8467f 100644 --- a/src/luascript.h +++ b/src/luascript.h @@ -1084,6 +1084,7 @@ class LuaScriptInterface static int luaItemTypeGetDefense(lua_State* L); static int luaItemTypeGetArmor(lua_State* L); static int luaItemTypeGetWeaponType(lua_State* L); + static int luaItemTypeGetAmmoType(lua_State* L); static int luaItemTypeGetTransformEquipId(lua_State* L); static int luaItemTypeGetTransformDeEquipId(lua_State* L); diff --git a/src/networkmessage.cpp b/src/networkmessage.cpp index 6ad238f..e503be0 100644 --- a/src/networkmessage.cpp +++ b/src/networkmessage.cpp @@ -95,7 +95,7 @@ void NetworkMessage::addPosition(const Position& pos) addByte(pos.z); } -void NetworkMessage::addItem(uint16_t id, uint8_t count) +void NetworkMessage::addItem(uint16_t id, uint8_t count, bool textWindow /* = false*/) { const ItemType& it = Item::items[id]; @@ -105,14 +105,17 @@ void NetworkMessage::addItem(uint16_t id, uint8_t count) add(it.id); } - if (it.stackable || it.isRune()) { - addByte(count); - } else if (it.isSplash() || it.isFluidContainer()) { - addByte(getLiquidColor(count)); + if (!textWindow) { + if (it.stackable || it.isRune()) { + addByte(count); + } + else if (it.isSplash() || it.isFluidContainer()) { + addByte(getLiquidColor(count)); + } } } -void NetworkMessage::addItem(const Item* item) +void NetworkMessage::addItem(const Item* item, bool textWindow /* = false*/) { const ItemType& it = Item::items[item->getID()]; @@ -122,12 +125,16 @@ void NetworkMessage::addItem(const Item* item) add(it.id); } - if (it.stackable) { - addByte(std::min(0xFF, item->getItemCount())); - } else if (it.isRune()) { - addByte(std::min(0xFF, item->getCharges())); - } else if (it.isSplash() || it.isFluidContainer()) { - addByte(getLiquidColor(item->getFluidType())); + if (!textWindow) { + if (it.stackable) { + addByte(std::min(0xFF, item->getItemCount())); + } + else if (it.isRune()) { + addByte(std::min(0xFF, item->getCharges())); + } + else if (it.isSplash() || it.isFluidContainer()) { + addByte(getLiquidColor(item->getFluidType())); + } } } diff --git a/src/networkmessage.h b/src/networkmessage.h index 7d11153..9ced1bb 100644 --- a/src/networkmessage.h +++ b/src/networkmessage.h @@ -110,8 +110,8 @@ class NetworkMessage // write functions for complex types void addPosition(const Position& pos); - void addItem(uint16_t id, uint8_t count); - void addItem(const Item* item); + void addItem(uint16_t id, uint8_t count, bool textWindow = false); + void addItem(const Item* item, bool textWindow = false); void addItemId(uint16_t itemId); MsgSize_t getLength() const { diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index 42c71f8..90b7388 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -1755,7 +1755,7 @@ void ProtocolGame::sendTextWindow(uint32_t windowTextId, Item* item, uint16_t ma NetworkMessage msg; msg.addByte(0x96); msg.add(windowTextId); - msg.addItem(item); + msg.addItem(item, true); if (canWrite) { msg.add(maxlen); @@ -1791,7 +1791,7 @@ void ProtocolGame::sendTextWindow(uint32_t windowTextId, uint32_t itemId, const NetworkMessage msg; msg.addByte(0x96); msg.add(windowTextId); - msg.addItem(itemId, 1); + msg.addItem(itemId, 1, true); msg.add(text.size()); msg.addString(text); msg.add(0x00);