• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!
  • 2026 staff recruitment is open! Check it out and consider applying!
  • New resources must be posted under Resources tab. A discussion thread will be created automatically, you can't open threads manually anymore.

CreatureEvent [TFS 1.3 / 1.4] Upgrade System

new attributes inside if creature:isPlayer() then in function us_onDamaged:
Absorb Health, Absorb Mana, reflect physical,fire,energy,earth.

LUA:
    -- Absorb Health / Mana on being hit Logic
        if primaryDamage > 0 then
            -- Absorb Health
            local absorbHealthPercent = creature:getAttribute(ATTR_ABSORBHEALTH)
            if absorbHealthPercent and absorbHealthPercent > 0 then
                if math.random(100) < 20 then
                    local healAmount = math.floor(primaryDamage * absorbHealthPercent / 100)
                    if healAmount > 0 then
                        creature:addHealth(healAmount)
                    end
                end
            end
            -- Absorb Mana
            local absorbManaPercent = creature:getAttribute(ATTR_ABSORBMANA)
            if absorbManaPercent and absorbManaPercent > 0 then
                if math.random(100) < 20 then
                    local manaAmount = math.floor(primaryDamage * absorbManaPercent / 100)
                    if manaAmount > 0 then
                        creature:addMana(manaAmount)
                    end
                end
            end

    -- Reflect damage back to attacker (true damage)
            if attacker and attacker ~= creature and attacker:isCreature() then
                local totalPercent = 0
                local function include(attrId)
                    local percent = creature:getAttribute(attrId)
                    if percent and percent > 0 then
                        totalPercent = totalPercent + percent
                    end
                end

                -- Sum applicable reflect attributes for this damage type
                if primaryType == COMBAT_PHYSICALDAMAGE then
                    include(ATTR_REFLECTPHYSICAL)
                    include(ATTR_REFLECTELEMENTS)
                elseif primaryType == COMBAT_FIREDAMAGE then
                    include(ATTR_REFLECTFIRE)
                    include(ATTR_REFLECTELEMENTS)
                elseif primaryType == COMBAT_ENERGYDAMAGE then
                    include(ATTR_REFLECTENERGY)
                    include(ATTR_REFLECTELEMENTS)
                elseif primaryType == COMBAT_EARTHDAMAGE then
                    include(ATTR_REFLECTPOISON)
                    include(ATTR_REFLECTELEMENTS)
                end

                if totalPercent > 0 then
                    if math.random(100) < 20 then
                        local reflectAmount = math.floor(primaryDamage * totalPercent / 100)
                        if reflectAmount > 0 then
                            doTargetCombatHealth(creature:getId(), attacker, COMBAT_PHYSICALDAMAGE, -reflectAmount, -reflectAmount, CONST_ME_DRAWBLOOD, ORIGIN_CONDITION)
                            creature:getPosition():sendMagicEffect(73)
                            local pos = attacker:getPosition()
                            addEvent(function()
                                Game.sendAnimatedText("Reflect!", pos, TEXTCOLOR_PASTELRED)
                            end, 200)
                        end
                    end
                end


config.lua - based on oen attr's:
LUA:
  [ATTR_ABSORBHEALTH] = {
    name = "Absorb Health",
    combatType = US_TYPES.DEFENSIVE,
    combatDamage = COMBAT_ENERGYDAMAGE + COMBAT_EARTHDAMAGE + COMBAT_FIREDAMAGE,
    maxValue = 10,
    format = function(value)
      return "[Absorb Health +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.BOOTS + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  },
  [ATTR_ABSORBMANA] = {
    name = "Absorb Mana",
    combatType = US_TYPES.DEFENSIVE,
    combatDamage = COMBAT_ENERGYDAMAGE + COMBAT_EARTHDAMAGE + COMBAT_FIREDAMAGE,
    maxValue = 10,
    format = function(value)
      return "[Absorb Mana +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.BOOTS + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  },
  [ATTR_REFLECTPHYSICAL] = {
    name = "Reflect Physical",
    combatType = US_TYPES.DEFENSIVE,
    maxValue = 10,
    format = function(value)
      return "[Reflect Physical +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  },
  [ATTR_REFLECTFIRE] = {
    name = "Reflect Fire",
    combatType = US_TYPES.DEFENSIVE,
    maxValue = 10,
    format = function(value)
      return "[Reflect Fire +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  },
  [ATTR_REFLECTENERGY] = {
    name = "Reflect Energy",
    combatType = US_TYPES.DEFENSIVE,
    maxValue = 10,
    format = function(value)
      return "[Reflect Energy +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  },
  [ATTR_REFLECTPOISON] = {
    name = "Reflect Poison",
    combatType = US_TYPES.DEFENSIVE,
    maxValue = 10,
    format = function(value)
      return "[Reflect Poison +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  },
  [ATTR_REFLECTELEMENTS] = {
    name = "Reflect Elements",
    combatType = US_TYPES.DEFENSIVE,
    maxValue = 10,
    format = function(value)
      return "[Reflect Elements +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  }

You code ralke looks really great :) didn't had much time to analyze it but it looks great!

Thanks, appreciate it! Be aware with ATTR_REFLECTPHYSICAL and others because in older protocols I think that reflection attributes aren't enabled, that's why I only kept drown as reflection damage (without calling attribute, if i'm not wrong I used a formula from income damage). Regards!
 
Thanks, appreciate it! Be aware with ATTR_REFLECTPHYSICAL and others because in older protocols I think that reflection attributes aren't enabled, that's why I only kept drown as reflection damage (without calling attribute, if i'm not wrong I used a formula from income damage). Regards!
this ATTR_REFLECTPHYSICAL is just friendly name for id's. Defined at top of config.lua

ATTR_ABSORBHEALTH = 37
ATTR_ABSORBMANA = 38
ATTR_REFLECTPHYSICAL = 39
ATTR_REFLECTFIRE = 40
ATTR_REFLECTENERGY = 41
ATTR_REFLECTPOISON = 42
ATTR_REFLECTELEMENTS = 43
 
New attributes. Burning (apply fire damage condition), poison (apply earth damagecondition), electrify (apply energy damagecondition), bleeding (apply physical damagecondition), freeze (apply paralyze effect). It should include target reductions from previous attributes. When target is a monster it checks if target is immune for specific damage type (it would be stupid to apply fire condition to dragon).


function us_onDamaged(creature, attacker, primaryDamage, primaryType, origin):

LUA:
    -- Apply status conditions (Burning, Poison, Electrify, Bleeding, Freeze)
        if creature:isMonster() or creature:isPlayer() then
            -- Burning (Fire damage over time)
            local burningChance = attacker:getAttribute(ATTR_BURNING)
            if burningChance > 0 and math.random(100) <= burningChance then
                -- Check immunity for monsters
                local isImmune = false
                if creature:isMonster() then
                    local immunity = creature:getType():getCombatImmunities()
                    if bit.band(immunity, COMBAT_FIREDAMAGE) == COMBAT_FIREDAMAGE then
                        isImmune = true
                    end
                end
                
                if not isImmune then
                    -- Calculate damage reduction for fire
                    local damageReduction = 0
                    if creature:isPlayer() then
                        damageReduction = creature:getAttribute(ATTR_FIRE_REDUCTION) + creature:getAttribute(ATTR_ELEMENTAL_REDUCTION)
                    end
                    
                    local baseDamage = 10
                    local finalDamage = math.ceil(baseDamage * (1 - damageReduction / 100))
                    
                    -- Apply condition
                    local condition = Condition(CONDITION_FIRE)
                    condition:setParameter(CONDITION_PARAM_DELAYED, true)
                    condition:addDamage(6, 8000, -finalDamage)
                    creature:addCondition(condition)
                    
                    -- Apply initial damage with 200ms delay
                    local creatureId = creature:getId()
                    local pos = creature:getPosition()
                    addEvent(function()
                        local target = Creature(creatureId)
                        if target then
                            doTargetCombat(0, target, COMBAT_FIREDAMAGE, -finalDamage, -finalDamage, CONST_ME_HITBYFIRE, true, false, false)
                        end
                    end, 200)
                    
                    -- Apply text animation with 400ms delay
                    addEvent(function()
                        Game.sendAnimatedText("Burning!", pos, TEXTCOLOR_ORANGE)
                    end, 400)
                end
            end

            -- Poison (Earth damage over time)
            local poisonChance = attacker:getAttribute(ATTR_POISON)
            if poisonChance > 0 and math.random(100) <= poisonChance then
                -- Check immunity for monsters
                local isImmune = false
                if creature:isMonster() then
                    local immunity = creature:getType():getCombatImmunities()
                    if bit.band(immunity, COMBAT_EARTHDAMAGE) == COMBAT_EARTHDAMAGE then
                        isImmune = true
                    end
                end
                
                if not isImmune then
                    -- Calculate damage reduction for poison
                    local damageReduction = 0
                    if creature:isPlayer() then
                        damageReduction = creature:getAttribute(ATTR_EARTH_REDUCTION) + creature:getAttribute(ATTR_ELEMENTAL_REDUCTION)
                    end
                    
                    local baseDamage = 5
                    local finalStartVal = math.ceil(baseDamage * (1 - damageReduction / 100))
                    
                    -- Apply initial damage
                    creature:getPosition():sendMagicEffect(CONST_ME_GREEN_RINGS)
                    
                    -- Apply condition
                    local condition = Condition(CONDITION_POISON)
                    condition:setParameter(CONDITION_PARAM_DELAYED, true)
                    
                    -- Always add the first damage tick with the full damage
                    condition:addDamage(0, 4000, -finalStartVal)
                    -- Add subsequent ticks only if they would do at least 1 damage
                    local damage = finalStartVal - 1
                    if damage >= 1 then
                        condition:addDamage(3, 4000, -damage)
                    end
                    damage = damage - 1
                    if damage >= 1 then
                        condition:addDamage(6, 4000, -damage)
                    end
                    damage = damage - 1
                    if damage >= 1 then
                        condition:addDamage(9, 4000, -damage)
                    end
                    damage = damage - 1
                    if damage >= 1 then
                        condition:addDamage(37, 4000, -damage)
                    end
                    creature:addCondition(condition)
                    
                    local pos = creature:getPosition()
                    addEvent(function()
                        Game.sendAnimatedText("Poisoned!", pos, TEXTCOLOR_LIGHTGREEN)
                    end, 200)
                end
            end

            -- Electrify (Energy damage over time)
            local electrifyChance = attacker:getAttribute(ATTR_ELECTRIFY)
            if electrifyChance > 0 and math.random(100) <= electrifyChance then
                -- Check immunity for monsters
                local isImmune = false
                if creature:isMonster() then
                    local immunity = creature:getType():getCombatImmunities()
                    if bit.band(immunity, COMBAT_ENERGYDAMAGE) == COMBAT_ENERGYDAMAGE then
                        isImmune = true
                    end
                end
                
                if not isImmune then
                    -- Calculate damage reduction for energy
                    local damageReduction = 0
                    if creature:isPlayer() then
                        damageReduction = creature:getAttribute(ATTR_ENERGY_REDUCTION) + creature:getAttribute(ATTR_ELEMENTAL_REDUCTION)
                    end
                    
                    local baseDamage = 25
                    local finalDamage = math.ceil(baseDamage * (1 - damageReduction / 100))
                    
                    -- Apply condition
                    local condition = Condition(CONDITION_ENERGY)
                    condition:setParameter(CONDITION_PARAM_DELAYED, true)
                    condition:addDamage(0, 10000, -finalDamage)
                    creature:addCondition(condition)
                    
                    -- Apply initial damage with 200ms delay
                    local creatureId = creature:getId()
                    local pos = creature:getPosition()
                    addEvent(function()
                        local target = Creature(creatureId)
                        if target then
                            doTargetCombat(0, target, COMBAT_ENERGYDAMAGE, -finalDamage, -finalDamage, CONST_ME_ENERGYHIT, true, false, false)
                        end
                    end, 200)
                    
                    -- Apply text animation with 400ms delay
                    addEvent(function()
                        Game.sendAnimatedText("Electrified!", pos, TEXTCOLOR_PURPLE)
                    end, 400)
                end
            end

            -- Bleeding (Physical damage over time)
            local bleedingChance = attacker:getAttribute(ATTR_BLEEDING)
            if bleedingChance > 0 and math.random(100) <= bleedingChance then
                -- Check immunity for monsters
                local isImmune = false
                if creature:isMonster() then
                    local immunity = creature:getType():getCombatImmunities()
                    if bit.band(immunity, COMBAT_PHYSICALDAMAGE) == COMBAT_PHYSICALDAMAGE then
                        isImmune = true
                    end
                end
                
                if not isImmune then
                    -- Calculate damage reduction for physical (bleeding doesn't use elemental reduction)
                    local damageReduction = 0
                    if creature:isPlayer() then
                        damageReduction = creature:getAttribute(ATTR_PHYSICAL_REDUCTION)
                    end
                    
                    local baseDamage = math.max(4, math.floor(primaryDamage * 0.12))
                    local finalDamage = math.ceil(baseDamage * (1 - damageReduction / 100))
                    
                    -- Apply initial damage (bleeding doesn't have initial damage, just the DoT)
                    creature:getPosition():sendMagicEffect(CONST_ME_DRAWBLOOD)
                    
                    -- Apply condition
                    local condition = Condition(CONDITION_BLEEDING)
                    condition:setParameter(CONDITION_PARAM_DELAYED, true)
                    condition:addDamage(5, 2000, -finalDamage)
                    creature:addCondition(condition)
                    
                    local pos = creature:getPosition()
                    addEvent(function()
                        Game.sendAnimatedText("Bleeding!", pos, TEXTCOLOR_RED)
                    end, 200)
                end
            end

            -- Freeze (Paralyze target)
            local freezeChance = attacker:getAttribute(ATTR_FREEZE)
            if freezeChance > 0 and math.random(100) <= freezeChance then
                local condition = Condition(CONDITION_PARALYZE)
                condition:setParameter(CONDITION_PARAM_TICKS, 3000) -- 3 seconds paralyze
                condition:setParameter(CONDITION_PARAM_SPEED, -800) -- Reduce speed significantly
                creature:addCondition(condition)
                creature:getPosition():sendMagicEffect(CONST_ME_MAGIC_GREEN)
                local pos = creature:getPosition()
                addEvent(function()
                    Game.sendAnimatedText("Frozen!", pos, TEXTCOLOR_LIGHTBLUE)
                end, 200)
            end
        end
    end

Basic config:
ATTR_BURNING = 44
ATTR_POISON = 45
ATTR_ELECTRIFY = 46
ATTR_BLEEDING = 47
ATTR_FREEZE = 48

LUA:
  [ATTR_BURNING] = {
    name = "Burning",
    combatType = US_TYPES.OFFENSIVE,
    maxValue = 10,
    format = function(value)
      return "[Burn target +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  },
  [ATTR_POISON] = {
    name = "Poison",
    combatType = US_TYPES.OFFENSIVE,
    maxValue = 10,
    format = function(value)
      return "[Infect target +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  },
  [ATTR_ELECTRIFY] = {
    name = "Electrify",
    combatType = US_TYPES.OFFENSIVE,
    maxValue = 10,
    format = function(value)
      return "[Electrocute target +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  },
  [ATTR_BLEEDING] = {
    name = "Bleeding",
    combatType = US_TYPES.OFFENSIVE,
    maxValue = 10,
    format = function(value)
      return "[Bleed target +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  },
  [ATTR_FREEZE] = {
    name = "Freeze",
    combatType = US_TYPES.OFFENSIVE,
    maxValue = 10,
    format = function(value)
      return "[Freeze target +" .. value .. "%]"
    end,
    itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
  }

Peace!
 
Last edited:
I want to introduce a new type of rare item, specifically a Mythic one. How do I set its border and name to red after the Legendary tier, which is yellow?
@Gover @oen432
 
Last edited:
I want to introduce a new type of rare item, specifically a Mythic one. How do I set its border and name to red after the Legendary tier, which is yellow?
@Gover @oen432
I believe this topic does not include rarity frames scripts. But if you want to just add new rarity to oen script you can just add a new item in RARITY table, add new rarity constant like:

[MYTHIC] = {
name = "mythic",
maxBonus = 5, -- max amount of bonus attributes
chance = X -- 1:X chance that item will be common (1 = 100%)
}

COMMON = 1
RARE = 2
EPIC = 3
LEGENDARY = 4
MYTHIC = 5

and it should work in my opinion. I did not find any limits in functions which would not handle the fifth rarity. It usually scans the entire RARITY table.
 
New attributes:
-Double Bash (Chance to attack twice in one turn, with the second attack dealing 50% damage.)
-Mitigate (Chance to reduce some amount of damage)

inside us_onDamaged inside if attacker:isPlayer() then:
LUA:
    -- Double Bash - chance to attack twice with second attack dealing 50% damage
        local doubleBashChance = attacker:getAttribute(ATTR_DOUBLEBASH)
        if doubleBashChance > 0 and math.random(100) < doubleBashChance then
            local secondAttackDamage= math.floor(primaryDamage * 0.5)
            local creatureId = creature:getId()
            local pos = creature:getPosition()
            
            -- Apply second attack with 300ms delay
            addEvent(function()
                local target = Creature(creatureId)
                if target then
                    doTargetCombatHealth(attacker:getId(), target, primaryType, -secondAttackDamage * 2, -secondAttackDamage * 2, CONST_ME_DRAWBLOOD, ORIGIN_CONDITION)
                end
            end, 300)
            
            -- Show visual effect and text
            addEvent(function()
                pos:sendMagicEffect(CONST_ME_DRAWBLOOD)
                Game.sendAnimatedText("x2!", pos, TEXTCOLOR_ORANGE)
            end, 500)
        end


inside us_onDamaged inside if creature:isPlayer() then:
LUA:
    -- Mitigate damage
    if primaryDamage > 0 then
        local mitigatePercent = creature:getAttribute(ATTR_MITIGATE)
        if mitigatePercent and mitigatePercent > 0 then
            if math.random(100) < 20 then
                local mitigateAmount = math.floor(primaryDamage * mitigatePercent / 100)
                if mitigateAmount > 0 then
                    primaryDamage = primaryDamage - mitigateAmount
                    creature:getPosition():sendMagicEffect(73)
                    local pos = attacker:getPosition()
                    addEvent(function()
                        Game.sendAnimatedText("Mitigate!", pos, TEXTCOLOR_PASTELRED)
                    end, 200)
                end
            end
        end
    end

Example config:
ATTR_DOUBLEBASH = 49
ATTR_MITIGATE = 50

[ATTR_DOUBLEBASH] = {
name = "Double Bash",
combatType = US_TYPES.OFFENSIVE,
maxValue = 10,
format = function(value)
return "[Double Bash +" .. value .. "%]"
end,
itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
},
[ATTR_MITIGATE] = {
name = "Mitigate",
combatType = US_TYPES.OFFENSIVE,
maxValue = 10,
format = function(value)
return "[Mitigate +" .. value .. "%]"
end,
itemType = US_ITEM_TYPES.ARMOR + US_ITEM_TYPES.SHIELD + US_ITEM_TYPES.HELMET + US_ITEM_TYPES.LEGS
}
 
upgraded reflect system. Now it checks the shooteffect and hiteffect from every attack and returns it with correct animations:

core.lua:
LUA:
       -- Reflect damage back to attacker (with matching projectile and effect)
                if attacker and attacker ~= creature and attacker:isCreature() then
                    local totalPercent = 0
                    local function include(attrId)
                        local percent = creature:getAttribute(attrId)
                        if percent and percent > 0 then
                            totalPercent = totalPercent + percent
                        end
                    end

                    -- Sum applicable reflect attributes for this damage type
                    if primaryType == COMBAT_PHYSICALDAMAGE then
                        include(ATTR_REFLECTPHYSICAL)
                    elseif primaryType == COMBAT_FIREDAMAGE then
                        include(ATTR_REFLECTFIRE)
                        include(ATTR_REFLECTELEMENTS)
                    elseif primaryType == COMBAT_ENERGYDAMAGE then
                        include(ATTR_REFLECTENERGY)
                        include(ATTR_REFLECTELEMENTS)
                    elseif primaryType == COMBAT_EARTHDAMAGE then
                        include(ATTR_REFLECTPOISON)
                        include(ATTR_REFLECTELEMENTS)
                    end

                    if totalPercent > 0 then
                        if math.random(100) < 99 then
                            local reflectAmount = math.floor(primaryDamage * totalPercent / 100)
                            if reflectAmount > 0 then
                                -- Determine projectile and impact effects
                                local projectile = shootEffect or CONST_ANI_NONE
                                local hitEffect = impactEffect or CONST_ME_DRAWBLOOD
                             
                                -- Apply default effects if none were provided by the combat system
                                if (not shootEffect or shootEffect == 0) and (not impactEffect or impactEffect == 0) then
                                    if primaryType == COMBAT_PHYSICALDAMAGE then
                                        projectile = CONST_ANI_NONE
                                        hitEffect = CONST_ME_DRAWBLOOD
                                    elseif primaryType == COMBAT_FIREDAMAGE then
                                        projectile = CONST_ANI_FIRE
                                        hitEffect = CONST_ME_HITBYFIRE
                                    elseif primaryType == COMBAT_ENERGYDAMAGE then
                                        projectile = CONST_ANI_ENERGY
                                        hitEffect = CONST_ME_ENERGYHIT
                                    elseif primaryType == COMBAT_EARTHDAMAGE then
                                        projectile = CONST_ANI_POISON
                                        hitEffect = CONST_ME_HITBYPOISON
                                    end
                                end
                             
                                -- Show "Reflect!" text with slight delay for better visual flow
                                local defenderPos = creature:getPosition()
                                addEvent(function()
                                    Game.sendAnimatedText("Reflect!", defenderPos, TEXTCOLOR_PASTELRED)
                                end, 200)
                             
                                -- Store IDs for safe delayed execution
                                local defenderId = creature:getId()
                                local attackerId = attacker:getId()
                             
                                -- Execute reflect with delay to ensure proper visual sequence
                                addEvent(function()
                                    local defender = Creature(defenderId)
                                    local reflectTarget = Creature(attackerId)
                                 
                                    if not defender or not reflectTarget then
                                        return
                                    end
                                 
                                    -- Send projectile back to attacker
                                    if projectile and projectile ~= CONST_ANI_NONE and projectile ~= 0 then
                                        defender:getPosition():sendDistanceEffect(reflectTarget:getPosition(), projectile)
                                    end
                                 
                                    -- Deal reflect damage (ORIGIN_CONDITION prevents infinite reflection)
                                    doTargetCombatHealth(defenderId, reflectTarget, primaryType, -reflectAmount, -reflectAmount, hitEffect, ORIGIN_CONDITION)
                                 
                                    -- Ensure impact effect is displayed
                                    if hitEffect and hitEffect ~= CONST_ME_NONE and hitEffect ~= 0 then
                                        reflectTarget:getPosition():sendMagicEffect(hitEffect)
                                    end
                                end, 450)
                            end
                        end
                    end
                end

also you have to edit onManaChange and onHealthChange:
function ManaChangeEvent.onManaChange(creature, attacker, primaryDamage, primaryType, origin, shootEffect, impactEffect)
function HealthChangeEvent.onHealthChange(creature, attacker, primaryDamage, primaryType, origin, shootEffect, impactEffect)

and return it with additional parameters in this functions
return us_onDamaged(creature, attacker, primaryDamage, primaryType, origin, shootEffect, impactEffect)

combat.cpp at top inside the functions:
void Combat::doTargetCombat(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params)
void Combat::doAreaCombat(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params, bool angleSpell)

add
C++:
    // Store shoot and impact effects in damage structure for Lua access
    damage.shootEffect = params.distanceEffect;
    damage.impactEffect = params.impactEffect;


then extend the functions executeHealthChange and executeManaChange

from if (scriptInterface->protectedCall(L, 5, 2) != 0) { to if (scriptInterface->protectedCall(L, 7, 2) != 0) {

add enums.h
shootEffect = 0;
impactEffect = 0;

and
uint8_t shootEffect;
uint8_t impactEffect;


then edit luascript.cpp to pass this in function:

void LuaScriptInterface::pushCombatDamage(lua_State* L, const CombatDamage& damage)

add

lua_pushnumber(L, damage.shootEffect);
lua_pushnumber(L, damage.impactEffect);


tested with some runes, beholder (casting physical sudden death rune) and few others.

Regards,
Gover
 
Back
Top