introduce physical damage calculator for paladins and knights

This commit is contained in:
ErikasKontenis 2020-10-15 13:52:34 +03:00
parent b0b93edcb4
commit 6904342e06
9 changed files with 324 additions and 157 deletions

View File

@ -18,7 +18,7 @@
<skill id="5" multiplier="1.5"/>
<skill id="6" multiplier="1.1"/>
</vocation>
<vocation id="2" flagid="2" name="Druid" description="a druid" gaincap="10" gainhp="5" gainmana="30" gainhpticks="6" gainhpamount="1" gainmanaticks="3" gainmanaamount="2" manamultiplier="1.1" attackspeed="2000" basespeed="70" soulmax="100" gainsoulticks="120" fromvoc="2">
<vocation id="2" flagid="2" name="Druid" description="a druid" gaincap="10" gainhp="5" gainmana="30" gainhpticks="6" gainhpamount="1" gainmanaticks="3" gainmanaamount="2" manamultiplier="1.1" attackspeed="1" basespeed="70" soulmax="100" gainsoulticks="120" fromvoc="2">
<skill id="0" multiplier="1.5"/>
<skill id="1" multiplier="1.8"/>
<skill id="2" multiplier="1.8"/>

View File

@ -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

View File

@ -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

View File

@ -56,7 +56,7 @@
<talkaction words="!serverinfo" script="serverinfo.lua"/>
<talkaction words="!share" script="experienceshare.lua"/>
<talkaction words="!shop" script="znoteshop.lua"/>
<talkaction words="!meleedamage" script="melee_damage.lua"/>
<talkaction words="!physicaldamage" separator=" " script="physical_damage.lua"/>
<talkaction words="469" script="469.lua"/>
<!-- test talkactions -->

View File

@ -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<const ItemType>(L, 1);
if (itemType) {
lua_pushnumber(L, itemType->ammoType);
}
else {
lua_pushnil(L);
}
return 1;
}
int LuaScriptInterface::luaItemTypeGetTransformEquipId(lua_State* L)
{
// itemType:getTransformEquipId()

View File

@ -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);

View File

@ -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<uint16_t>(it.id);
}
if (!textWindow) {
if (it.stackable || it.isRune()) {
addByte(count);
} else if (it.isSplash() || it.isFluidContainer()) {
}
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,14 +125,18 @@ void NetworkMessage::addItem(const Item* item)
add<uint16_t>(it.id);
}
if (!textWindow) {
if (it.stackable) {
addByte(std::min<uint16_t>(0xFF, item->getItemCount()));
} else if (it.isRune()) {
}
else if (it.isRune()) {
addByte(std::min<uint16_t>(0xFF, item->getCharges()));
} else if (it.isSplash() || it.isFluidContainer()) {
}
else if (it.isSplash() || it.isFluidContainer()) {
addByte(getLiquidColor(item->getFluidType()));
}
}
}
void NetworkMessage::addItemId(uint16_t itemId)
{

View File

@ -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 {

View File

@ -1755,7 +1755,7 @@ void ProtocolGame::sendTextWindow(uint32_t windowTextId, Item* item, uint16_t ma
NetworkMessage msg;
msg.addByte(0x96);
msg.add<uint32_t>(windowTextId);
msg.addItem(item);
msg.addItem(item, true);
if (canWrite) {
msg.add<uint16_t>(maxlen);
@ -1791,7 +1791,7 @@ void ProtocolGame::sendTextWindow(uint32_t windowTextId, uint32_t itemId, const
NetworkMessage msg;
msg.addByte(0x96);
msg.add<uint32_t>(windowTextId);
msg.addItem(itemId, 1);
msg.addItem(itemId, 1, true);
msg.add<uint16_t>(text.size());
msg.addString(text);
msg.add<uint16_t>(0x00);