I won't have enough time to complete this lib, it was supposed to replace the Combat() and createCombatArea() functions as you can only use them at the start of the script, with this lib you would be able to modify the area and have more flexibility to damages, effects, area etc.
Well this works for somethings but it have bugs like:
Its iterating over all the tiles not having in mind if its reachable from the center or not.)
getCombatDamage is incomplete and its not using the parameter values, only the callback, it should look like this: https://github.com/otland/forgottenserver/blob/master/src/combat.cpp#L42-L92
So this is mainly for people to look/learn and maybe get something out of it:
Talkaction example:
In this case i'm forcing variant to be targeted but in onCastSpell the variant would come in the callback and you would use that instead of that table.
Well this works for somethings but it have bugs like:
Its iterating over all the tiles not having in mind if its reachable from the center or not.)
getCombatDamage is incomplete and its not using the parameter values, only the callback, it should look like this: https://github.com/otland/forgottenserver/blob/master/src/combat.cpp#L42-L92
So this is mainly for people to look/learn and maybe get something out of it:
Code:
function table.copy(t, out)
out = out or {}
if type(t) ~= "table" then
return false
end
for i, x in pairs(t) do
if type(x) == "table" then
out[i] = {}
table.copy(t[i], out[i])
else
out[i] = x
end
end
return out
end
VARIANT_NONE = 0
VARIANT_NUMBER = 1
VARIANT_POSITION = 2
VARIANT_TARGETPOSITION = 3
VARIANT_STRING = 4
DynamicCombatArea = {}
DynamicCombat = {}
setmetatable(DynamicCombat,
{__call =
function()
return setmetatable({parameters = {COMBAT_PARAM_TYPE = COMBAT_NONE}}, {__index = DynamicCombat})
end
})
setmetatable(DynamicCombatArea,
{__call =
function(_, area)
if type(area) ~= "table" then
return false
end
return setmetatable({area = table.copy(area)}, {__index = DynamicCombatArea})
end
})
function DynamicCombatArea:getCenter()
if self.centerx and self.centery then
return self.centerx, self.centery
end
for y = 1, #self.area do
for x = 1, #self.area[y] do
if self.area[y][x] == 3 then
self.centerx = x
self.centery = y
return x, y
end
end
end
return false
end
function DynamicCombatArea:iterateArea(mapcenter, func, combat, caster, damage)
local centerx, centery = self:getCenter()
if not centerx then
print("[Error - iterateArea] Can't find area center.")
return false
end
for y = 1, #self.area do
for x = 1, #self.area[y] do
if self.area[y][x] == 1 or self.area[y][x] == 3 then
local offsetx, offsety = x-centerx, y-centery
func(combat, caster, Tile(mapcenter.x+offsetx, mapcenter.y+offsety, mapcenter.z), damage)
end
end
end
end
function DynamicCombat:setParameter(param, value)
self.parameters[param] = value
return true
end
function DynamicCombat:setDamageCallback(callback)
self.damagecallback = callback
return true
end
function DynamicCombat:setArea(area)
self.area = area
return true
end
function DynamicCombat:getCombatDamage(caster, target)
if self.damagecallback then
return self.damagecallback(self, caster, target)
end
return -100, -200 -- This is incomplete only handled by callback
end
function DynamicCombat:doCombat(caster, param, damage)
if not param then
return false
end
if param.isCreature and param:isCreature() then
local target = param
local min, max
if damage then
min, max = unpack(damage)
else
min, max = self:getCombatDamage()
end
local func = doTargetCombatHealth
if self.parameters[COMBAT_PARAM_TYPE] == COMBAT_MANADRAIN then
func = doTargetCombatMana
end
if self.parameters[COMBAT_PARAM_DISTANCEEFFECT] then
caster:getPosition():sendDistanceEffect(target:getPosition(), self.parameters[COMBAT_PARAM_DISTANCEEFFECT])
end
func(caster, target, self.parameters[COMBAT_PARAM_TYPE], min, max, self.parameters[COMBAT_PARAM_EFFECT] or CONST_ME_NONE)
elseif type(param) == "table" and param.x and param.y and param.z then
local min, max = self:getCombatDamage()
self.area:iterateArea(param, DynamicCombat.doCombat, self, caster, {min, max})
elseif param.isTile and param:isTile() then
local tile = param
local creatures = tile:getCreatures()
if creatures and #creatures > 0 then
for i = 1, #creatures do
self:doCombat(caster, creatures[i], damage)
end
elseif self.parameters[COMBAT_PARAM_EFFECT] then
tile:getPosition():sendMagicEffect(self.parameters[COMBAT_PARAM_EFFECT])
end
end
end
function DynamicCombat:execute(creature, variant)
if variant.type == VARIANT_NUMBER then
local target = Creature(variant.number)
if not target then
return false
end
if self.area then
self:doCombat(creature, target:getPosition())
else
self:doCombat(creature, target)
end
end
end
Talkaction example:
Code:
local damageCallback = function(combat, caster, target)
local v = math.random(100, 200)
return -v, -v
end
local combat = DynamicCombat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_ICEDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ICEAREA)
combat:setParameter(COMBAT_PARAM_DISTANCEEFFECT, CONST_ANI_ICE)
combat:setDamageCallback(damageCallback)
local area = DynamicCombatArea(AREA_CIRCLE3X3)
combat:setArea(area)
function onSay(player)
local target = player:getTarget()
if target then
combat:execute(player, {type=VARIANT_NUMBER, number=target:getId()})
end
end
In this case i'm forcing variant to be targeted but in onCastSpell the variant would come in the callback and you would use that instead of that table.