2016-12-28 15:02:54 +01:00
--[[
Illarion Server
This program is free software : you can redistribute it and / or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation , either version 3 of the License , or ( at your option ) any
later version .
This program is distributed in the hope that it will be useful , but WITHOUT ANY
WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE . See the GNU Affero General Public License for more
details .
You should have received a copy of the GNU Affero General Public License along
with this program . If not , see < http : // www.gnu . org / licenses /> .
] ]
--[[
Reasoning of this lua - script : The standardfighting script is messy .
With features like ranged physical fighting , melee and upcoming magic fighting
there is a huge difference in how attacks are evaluated but only one entrypoint .
To reduce confusion it is advised to split every attack - type into its own script .
However shared functions must be accessible and not be copied .
This script is only a first step ( as it contains fighting logic which it shouldnt )
Better state ( imo ) would be :
2020-12-06 23:34:30 +01:00
- standardfighting as entrypoint
2016-12-28 15:02:54 +01:00
- melee.lua , ranged.lua , staff.lua ( spells should be handeled separatly )
2020-12-06 23:34:30 +01:00
- standardfighting owning all shared scripts
2016-12-28 15:02:54 +01:00
-- with no internal fighting-logic (like special cases for difference fightforms)
-- with smallest functions possible:
--- e.g.: checkRange checking also blocking is a confusing behavior
--- better: change function name to match behavior better
--- even better: split function into checkRange and checkBlocked (or so)
] ]
2021-03-28 11:11:40 +02:00
local common = require ( " base.common " )
2016-12-28 15:02:54 +01:00
local M = { }
--[[ Check the range from the attacker to the defender and ensure that it is
---- within weapon distance.
---- @param attackerStruct The table that stores the data of the attacker
---- @param defender The character who is attacked
---- @return true in case the range is fine, else false
2025-01-25 16:54:03 +01:00
---- @return distance between attacker and defender
] ]
2016-12-28 15:02:54 +01:00
function M . checkRangeAndView ( attackerStruct , defender )
2025-01-25 16:54:03 +01:00
if not isValidChar ( defender ) then
return false
end
2016-12-28 15:02:54 +01:00
local distance = attackerStruct.Char : distanceMetric ( defender )
2020-12-06 23:34:30 +01:00
2025-01-25 16:54:03 +01:00
if not isValidChar ( attackerStruct.Char ) or not isValidChar ( defender ) then --For some reason the error only happened below when doing world:LoS, so checking defender again just to be safe
return false
end
2016-12-28 15:02:54 +01:00
if distance > 1 then
local blockList = world : LoS ( attackerStruct.Char . pos , defender.pos )
local next = next
2020-12-06 23:34:30 +01:00
2016-12-28 15:02:54 +01:00
-- see if there is a "next" (first) object in blockList!
if ( next ( blockList ) ~= nil ) then
return false , distance
end
end
if distance <= 2 and attackerStruct.AttackKind == 4 then
return false , distance
end
2020-12-06 23:34:30 +01:00
2016-12-28 15:02:54 +01:00
if attackerStruct.IsWeapon then
return distance <= attackerStruct.Weapon . Range , distance
end
2020-12-06 23:34:30 +01:00
2016-12-28 15:02:54 +01:00
return distance <= 1 , distance
end
-- Load all weapon data for a character.
-- The data is added to the given parameter table
-- @param charStruct The table of the character the weapons are supposed to be load for
-- @returns the modified charStruct
function M . loadWeapons ( charStruct )
local rItem = charStruct.Char : getItemAt ( Character.right_tool )
local lItem = charStruct.Char : getItemAt ( Character.left_tool )
2022-06-19 18:56:58 +02:00
local items = { rItem , lItem }
2016-12-28 15:02:54 +01:00
2021-03-28 11:11:40 +02:00
-- Unequip broken weapons
2022-06-19 18:56:58 +02:00
for _ , theItem in pairs ( items ) do -- Both items in hand are checked one after the other
if theItem and theItem.id ~= 0 and common.isBroken ( theItem ) then -- Makes sure the item actually exists then checks if it is broken
local itemIsWeapon , theWeaponStruct = world : getWeaponStruct ( theItem.id )
if itemIsWeapon then -- Makes sure the item is a weapon, not a tool or anything else
local weaponType = theWeaponStruct.WeaponType
if not ( weaponType == WeaponStruct.arrow or weaponType == WeaponStruct.bolt or weaponType == WeaponStruct.stone ) then -- Ammunition counts as a weapon, so this excludes that from the check if there is somehow a broken arrow or something
if not common.moveItemToBackpack ( charStruct.Char , theItem ) then
world : erase ( theItem , theItem.number )
world : createItemFromItem ( theItem , charStruct.Char . pos , true )
end
end
end
2021-03-28 11:11:40 +02:00
end
end
2022-06-19 18:56:58 +02:00
-- items are fetched again in case they were unequipped above
rItem = charStruct.Char : getItemAt ( Character.right_tool )
lItem = charStruct.Char : getItemAt ( Character.left_tool )
local rAttFound , rAttWeapon = world : getWeaponStruct ( rItem.id )
local lAttFound , lAttWeapon = world : getWeaponStruct ( lItem.id )
2016-12-28 15:02:54 +01:00
-- the right item is ALWAYS used as the weapon now!
local isRWp = 1
local isLWp = 1
if rAttFound then
local WType = rAttWeapon.WeaponType
if WType == 10 or WType == 11 or WType == 12 or WType == 14 then -- Ammo or shield in right hand: switch r and l hand!
isRWp = 0
end
else
isRWp = 0
end
if lAttFound then
local WType = lAttWeapon.WeaponType
if WType == 10 or WType == 11 or WType == 12 or WType == 14 then -- Ammo or shield in right hand: switch r and l hand!
isLWp = 0
end
else
isLWp = 0
end
if isRWp == 0 and isLWp == 1 then -- switch weapons
rItem , lItem = lItem , rItem
rAttFound , lAttFound = lAttFound , rAttFound
rAttWeapon , lAttWeapon = lAttWeapon , rAttWeapon
end
charStruct [ " WeaponItem " ] = rItem
charStruct [ " IsWeapon " ] = rAttFound
charStruct [ " Weapon " ] = rAttWeapon
charStruct [ " SecWeaponItem " ] = lItem
charStruct [ " SecIsWeapon " ] = lAttFound
charStruct [ " SecWeapon " ] = lAttWeapon
2020-12-06 23:34:30 +01:00
2016-12-28 15:02:54 +01:00
return charStruct
end
function M . isMagicUser ( Attacker )
2016-12-28 15:57:59 +01:00
if Attacker : getMagicType ( ) == 0 and ( Attacker : getQuestProgress ( 37 ) ~= 0 or Attacker : getMagicFlags ( 0 ) > 0 ) then
2016-12-28 15:02:54 +01:00
return true
end
2020-02-16 18:39:58 +01:00
2016-12-28 15:02:54 +01:00
return false
end
2026-04-13 21:58:39 +02:00
function M . isDruid ( Attacker )
if Attacker : getMagicType ( ) == 3 then
return true
end
return false
end
2019-08-09 10:46:48 +02:00
local selectedEnemies = { }
-- Stores the current attacked enemy's id as the selected target of the character
function M . setSelectedEnemyId ( theCharId , selectedEnemyId )
2019-12-30 11:06:20 +01:00
selectedEnemies [ theCharId ] = { enemyId = selectedEnemyId , timestamp = world : getTime ( " unix " ) }
2019-08-09 10:46:48 +02:00
end
-- Returns the id of the currently selected enemy
-- Currently selected means that onAttack has been called at least within the last two seconds for that target
function M . getSelectedEnemyId ( theCharId )
2019-12-30 11:06:20 +01:00
if selectedEnemies [ theCharId ] then
if world : getTime ( " unix " ) - 2 <= selectedEnemies [ theCharId ] . timestamp then
return selectedEnemies [ theCharId ] . enemyId
end
end
2020-12-06 23:34:30 +01:00
2019-12-30 11:06:20 +01:00
return false
2019-08-09 10:46:48 +02:00
end
2021-03-28 11:11:40 +02:00
return M