• 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!

[TFS 1.0] Critical Hit % - Permanent

Eldin

Eldin Projects
Premium User
Joined
Jun 12, 2008
Messages
1,334
Reaction score
613
Location
Sweden
Greets!

Some servers have the Critical Hit Chance in Config, TFS 1.0 doesn't but its a great feature and should be possible via CreatureScripts if im not misstaken.

At this stage, im really glad for any Crit Hit script rather then none, the important thing wouls be that it affects Knights and not Magea, if the Physical damage includes Paladins, then its not a problem.

If you want the bigger challange, heres my real request:

A Critical Hit % from your Fist Skill. The idea is to make fist useable and make the people that take their time get something for it. You start out with 10 in all skills, 10 in fist fighting would mean a 1% crit chance while 20 means 2%. I don't know where you count the actual damage but otherwise a random + dmg (20-100?) could be set if the crit chance take place.
This can also include paladins as they also would have to train for it. (If vocation knight and paladin?) onhit or? :)

I am happy in any way I get the critical script and anyway you can make it, the fist would be awesome, im open for suggestions if you could make it in any other way.

Kind Regards,
Eldin.

A well known and acceptable Bump ^^

Kind Regards,
Eldin.
 
Last edited by a moderator:
Well, with onHealthChange() script event, you can easily accomplish this by doing simple calculations and damage output modifications
if math.random(100) < (get skill value * 0.1) then modify damage output to increase it
 
Well, with onHealthChange() script event, you can easily accomplish this by doing simple calculations and damage output modifications
if math.random(100) < (get skill value * 0.1) then modify damage output to increase it
Please show an example script of using onHealthChange() script, full script with parameters and stuff, just a small script, I want to try to use it for critical hits and for reducing damage from spells by the armor a player is wearing....
 
Okay, prepare to be educated!

First off, some notes:
  • onHealthChange() and onManaChange() events are only called when health and mana are changed, respectively!
    • Yes, that means if there is a poff/spark, onHealthChange()/onManaChange() will never be called!
  • All parameters MUST be used or it will not work!
    • Do not worry about trying to figure out what to put if you can't think of anything, they have default values, just need to use the names alone!
  • Be smart, please remember this is called every time when health/mana changes, so don't bloat it up with dirty and messy code.
Here is the creature event function:
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)

Here are what the parameters mean:
  • creature: the victim, the one whose health is changing
  • attacker: the one changing the health, it can be nil (fields, traps, etc)
  • primaryDamage: the primary damage
  • primaryType: the type of the primary damage
  • secondaryDamage: the secondary damage
  • secondaryType: the type of the secondary damage
  • origin: the type of attack
    • ORIGIN_NONE
    • ORIGIN_CONDITION
    • ORIGIN_SPELL
    • ORIGIN_MELEE
    • ORIGIN_RANGED
You can find information on our beloved Wiki

What is the difference between primary and secondary?
This is pretty self-explanatory, let's say you have a firesword or an earth-enchanged warhammer.
The primary type will be physical, the secondary type is the elemental one (fire and earth, respectively).
secondaryDamage/secondaryType is only applied when there is a secondary damage/type, so it's somewhat uncommon.

Let's get into some examples:

In this example, I'm just showing you who the attacker and the creature are.
Remember, the attacker is the one changing the health, the victim is the one whose health is being changed.

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    attacker:sendTextMessage(MESSAGE_EVENT_ORANGE, "Attacker: " .. attacker:getName() .. "\n")
    attacker:sendTextMessage(MESSAGE_EVENT_ORANGE, "Victim: " .. creature:getName() .. "\n")

    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

lxSkz5n.png


Now let's say you want to have a bonus added to your weapon.
So, you can have a plain club and a special club (both same id), except special club has actionid 1000.
The special club will do twice the damage that a plain club will do.
So, when we change someone's health, we need to double the primary damage.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)  
    if attacker:getSlotItem(CONST_SLOT_LEFT):getActionId() == 1000 then
        return primaryDamage * 2, primaryType, secondaryDamage, secondaryType
    end
  
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

As you can see, I multiplied primaryDamage just by doing:
primaryDamage * 2

Plain club:
CM3p1R9.gif


Special club (actionid 1000):
HkEAXxW.gif


Let's keep going, right? It's pretty fun!

Let's play around with primary types. We'll have a 50-50 chance of hitting fire damage or ice damage.
Pretty simple, just need to set the primaryType to their respective types when a random number falls in the right spot.

NOTE: You can see all the primary types here

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        return primaryDamage, COMBAT_FIREDAMAGE, secondaryDamage, secondaryType
    end  
  
    return primaryDamage, COMBAT_ICEDAMAGE, secondaryDamage, secondaryType
end

30hRquj.gif


Pretty simple? Let's go deeper, shall we? We'll use a firesword for this example.
We want to modify the types for the firesword. Remember, a firesword is one of those weapons
that have both a primary and secondary damage/type. It attacks with physical and fire.
Let's change them, again on 50-50 chance, to earth and energy?
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        return primaryDamage, COMBAT_ENERGYDAMAGE, secondaryDamage, COMBAT_EARTHDAMAGE
    end  
  
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

Note that the primaryType and secondaryType inside the if statement are changed to earth and energy.
The fire and physical damage are defaulted from the firesword. So you don't need to change the primaryType and secondaryType in the end (outside the if statement).

ybNXpWL.gif


Super simple, right? Now, I know this thread is about critical hits, so let's do that then.
From my understanding of critical hits, each time a player hits someone, there's a chance of dealing X additional damage.
As we've learned up to now, we can multiply damage and we can apply a chance (ofcourse...). So, let's mix the two.

But wait, let's make it more customized. Let's have a message appear above the creatures head when a critical hit happens. And let's make it even more unique, let's have different messages depending on the type of attack.
In this example, melee attacks will have the message "SKULLBASH!" and distance attacks will have the message "HEADSHOT!". Also, they both will deal 10 times the original damage.
Remember, the origin parameter will tell us what type of attack has occurred. You can see their types above.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        if origin == ORIGIN_MELEE then
            creature:say("SKULLBASH!", TALKTYPE_MONSTER_SAY)
        elseif origin == ORIGIN_RANGED then
            creature:say("HEADSHOT!", TALKTYPE_MONSTER_SAY)
        end
        return primaryDamage * 10, primaryType, secondaryDamage, secondaryType
    end
  
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

qLN1Ee5.gif


NOTE: I did not include the ORIGIN_RANGED example because it is bugged. No matter what distance weapon you use the server will always respond with ORIGIN_MELEE. I've reported this to @Dalkon and a fix should be on the way soon.

Last example I have, let's use the ORIGIN_SPELL type. Let's say you have a super ability that absorbs all
spell damages. That means, when someone uses a damage spell on you, it will heal you instead.
Pretty simple, just use COMBAT_HEALING for the primaryType. COMBAT_HEALING is technically just healing damage, so you don't need to modify primaryDamage to make it heal, in fact, it doesn't work this way, you have to use COMBAT_HEALING.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if origin == ORIGIN_SPELL then
        return primaryDamage, COMBAT_HEALING, secondaryDamage, COMBAT_HEALING
    end
  
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

GgUwo6K.png


There's definitely THOUSANDS of different things you can do, all you need to do is have a lot of creativity.
 
Okay, prepare to be educated!

First off, some notes:
  • onHealthChange() and onManaChange() events are only called when health and mana are changed, respectively!
    • Yes, that means if there is a poff/spark, onHealthChange()/onManaChange() will never be called!
  • All parameters MUST be used or it will not work!
    • Do not worry about trying to figure out what to put if you can't think of anything, they have default values, just need to use the names alone!
  • Be smart, please remember this is called every time when health/mana changes, so don't bloat it up with dirty and messy code.
Here is the creature event function:
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)

Here are what the parameters mean:
  • creature: the victim, the one whose health is changing
  • attacker: the one changing the health, it can be nil (fields, traps, etc)
  • primaryDamage: the primary damage
  • primaryType: the type of the primary damage
  • secondaryDamage: the secondary damage
  • secondaryType: the type of the secondary damage
  • origin: the type of attack
    • ORIGIN_NONE
    • ORIGIN_CONDITION
    • ORIGIN_SPELL
    • ORIGIN_MELEE
    • ORIGIN_RANGED
You can find information on our beloved Wiki

What is the difference between primary and secondary?
This is pretty self-explanatory, let's say you have a firesword or an earth-enchanged warhammer.
The primary type will be physical, the secondary type is the elemental one (fire and earth, respectively).
secondaryDamage/secondaryType is only applied when there is a secondary damage/type, so it's somewhat uncommon.

Let's get into some examples:

In this example, I'm just showing you who the attacker and the creature are.
Remember, the attacker is the one changing the health, the victim is the one whose health is being changed.

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    attacker:sendTextMessage(MESSAGE_EVENT_ORANGE, "Attacker: " .. attacker:getName() .. "\n")
    attacker:sendTextMessage(MESSAGE_EVENT_ORANGE, "Victim: " .. creature:getName() .. "\n")

    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

lxSkz5n.png


Now let's say you want to have a bonus added to your weapon.
So, you can have a plain club and a special club (both same id), except special club has actionid 1000.
The special club will do twice the damage that a plain club will do.
So, when we change someone's health, we need to double the primary damage.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) 
    if attacker:getSlotItem(CONST_SLOT_LEFT):getActionId() == 1000 then
        return primaryDamage * 2, primaryType, secondaryDamage, secondaryType
    end
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

As you can see, I multiplied primaryDamage just by doing:
primaryDamage * 2

Plain club:
CM3p1R9.gif


Special club (actionid 1000):
HkEAXxW.gif


Let's keep going, right? It's pretty fun!

Let's play around with primary types. We'll have a 50-50 chance of hitting fire damage or ice damage.
Pretty simple, just need to set the primaryType to their respective types when a random number falls in the right spot.

NOTE: You can see all the primary types here

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        return primaryDamage, COMBAT_FIREDAMAGE, secondaryDamage, secondaryType
    end 
 
    return primaryDamage, COMBAT_ICEDAMAGE, secondaryDamage, secondaryType
end

30hRquj.gif


Pretty simple? Let's go deeper, shall we? We'll use a firesword for this example.
We want to modify the types for the firesword. Remember, a firesword is one of those weapons
that have both a primary and secondary damage/type. It attacks with physical and fire.
Let's change them, again on 50-50 chance, to earth and energy?
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        return primaryDamage, COMBAT_ENERGYDAMAGE, secondaryDamage, COMBAT_EARTHDAMAGE
    end 
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

Note that the primaryType and secondaryType inside the if statement are changed to earth and energy.
The fire and physical damage are defaulted from the firesword. So you don't need to change the primaryType and secondaryType in the end (outside the if statement).

ybNXpWL.gif


Super simple, right? Now, I know this thread is about critical hits, so let's do that then.
From my understanding of critical hits, each time a player hits someone, there's a chance of dealing X additional damage.
As we've learned up to now, we can multiply damage and we can apply a chance (ofcourse...). So, let's mix the two.

But wait, let's make it more customized. Let's have a message appear above the creatures head when a critical hit happens. And let's make it even more unique, let's have different messages depending on the type of attack.
In this example, melee attacks will have the message "SKULLBASH!" and distance attacks will have the message "HEADSHOT!". Also, they both will deal 10 times the original damage.
Remember, the origin parameter will tell us what type of attack has occurred. You can see their types above.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        if origin == ORIGIN_MELEE then
            creature:say("SKULLBASH!", TALKTYPE_MONSTER_SAY)
        elseif origin == ORIGIN_RANGED then
            creature:say("HEADSHOT!", TALKTYPE_MONSTER_SAY)
        end
        return primaryDamage * 10, primaryType, secondaryDamage, secondaryType
    end
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

qLN1Ee5.gif


NOTE: I did not include the ORIGIN_RANGED example because it is bugged. No matter what distance weapon you use the server will always respond with ORIGIN_MELEE. I've reported this to @Dalkon and a fix should be on the way soon.

Last example I have, let's use the ORIGIN_SPELL type. Let's say you have a super ability that absorbs all
spell damages. That means, when someone uses a damage spell on you, it will heal you instead.
Pretty simple, just use COMBAT_HEALING for the primaryType. COMBAT_HEALING is technically just healing damage, so you don't need to modify primaryDamage to make it heal, in fact, it doesn't work this way, you have to use COMBAT_HEALING.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if origin == ORIGIN_SPELL then
        return primaryDamage, COMBAT_HEALING, secondaryDamage, COMBAT_HEALING
    end
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

GgUwo6K.png


There's definitely THOUSANDS of different things you can do, all you need to do is have a lot of creativity.

OMG MAN AMAZING! Just seeing you do this has given me some ideas, and thanks to storage values I am already working on implementing them... Thanks man you rox! I think this should be in tutorials, and first page, if not stickied! Thanks man, you helped out ALOT.
 
OMG MAN AMAZING! Just seeing you do this has given me some ideas, and thanks to storage values I am already working on implementing them... Thanks man you rox! I think this should be in tutorials, and first page, if not stickied! Thanks man, you helped out ALOT.

I forgot to mention that onHealthChange() is a creature event, therefore creatures have to be registered with this event.
You can easily use player:registerEvent("eventName") in login.lua to register it for players, however unfortunately, there isn't a simple way to register it to every monster because there is no way to register it to some that didn't even log into the world. Kind of tough to explain, however, this does not mean it can never work for monsters.

You can do it the old-fashioned monsters XML way by adding the tags below.
Code:
    <script>
        <event name="eventName"/>
    </script>

Yes, I know there's a lot of monster files to add this to, heck you probably don't need them all to have it.
Still, you can add it to every file with a click of a button using Notepad++.
 
About to read it now, this is far more then I could ever imagine as an explanation, almost tought you left us hanging! :D
Thank You alot!

EDIT:
I now read it all once, dinner guests makes it hard to try it out more at this very second.
Once I can, I will directly try to create the critical hit chance by calculations of fist fighting, the request I mentioned at the first post.

Thanks again @Evan.

Kind Regards,
Eldin.
 
Last edited:
Okay, prepare to be educated!

First off, some notes:
  • onHealthChange() and onManaChange() events are only called when health and mana are changed, respectively!
    • Yes, that means if there is a poff/spark, onHealthChange()/onManaChange() will never be called!
  • All parameters MUST be used or it will not work!
    • Do not worry about trying to figure out what to put if you can't think of anything, they have default values, just need to use the names alone!
  • Be smart, please remember this is called every time when health/mana changes, so don't bloat it up with dirty and messy code.
Here is the creature event function:
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)

Here are what the parameters mean:
  • creature: the victim, the one whose health is changing
  • attacker: the one changing the health, it can be nil (fields, traps, etc)
  • primaryDamage: the primary damage
  • primaryType: the type of the primary damage
  • secondaryDamage: the secondary damage
  • secondaryType: the type of the secondary damage
  • origin: the type of attack
    • ORIGIN_NONE
    • ORIGIN_CONDITION
    • ORIGIN_SPELL
    • ORIGIN_MELEE
    • ORIGIN_RANGED
You can find information on our beloved Wiki

What is the difference between primary and secondary?
This is pretty self-explanatory, let's say you have a firesword or an earth-enchanged warhammer.
The primary type will be physical, the secondary type is the elemental one (fire and earth, respectively).
secondaryDamage/secondaryType is only applied when there is a secondary damage/type, so it's somewhat uncommon.

Let's get into some examples:

In this example, I'm just showing you who the attacker and the creature are.
Remember, the attacker is the one changing the health, the victim is the one whose health is being changed.

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    attacker:sendTextMessage(MESSAGE_EVENT_ORANGE, "Attacker: " .. attacker:getName() .. "\n")
    attacker:sendTextMessage(MESSAGE_EVENT_ORANGE, "Victim: " .. creature:getName() .. "\n")

    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

lxSkz5n.png


Now let's say you want to have a bonus added to your weapon.
So, you can have a plain club and a special club (both same id), except special club has actionid 1000.
The special club will do twice the damage that a plain club will do.
So, when we change someone's health, we need to double the primary damage.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) 
    if attacker:getSlotItem(CONST_SLOT_LEFT):getActionId() == 1000 then
        return primaryDamage * 2, primaryType, secondaryDamage, secondaryType
    end
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

As you can see, I multiplied primaryDamage just by doing:
primaryDamage * 2

Plain club:
CM3p1R9.gif


Special club (actionid 1000):
HkEAXxW.gif


Let's keep going, right? It's pretty fun!

Let's play around with primary types. We'll have a 50-50 chance of hitting fire damage or ice damage.
Pretty simple, just need to set the primaryType to their respective types when a random number falls in the right spot.

NOTE: You can see all the primary types here

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        return primaryDamage, COMBAT_FIREDAMAGE, secondaryDamage, secondaryType
    end 
 
    return primaryDamage, COMBAT_ICEDAMAGE, secondaryDamage, secondaryType
end

30hRquj.gif


Pretty simple? Let's go deeper, shall we? We'll use a firesword for this example.
We want to modify the types for the firesword. Remember, a firesword is one of those weapons
that have both a primary and secondary damage/type. It attacks with physical and fire.
Let's change them, again on 50-50 chance, to earth and energy?
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        return primaryDamage, COMBAT_ENERGYDAMAGE, secondaryDamage, COMBAT_EARTHDAMAGE
    end 
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

Note that the primaryType and secondaryType inside the if statement are changed to earth and energy.
The fire and physical damage are defaulted from the firesword. So you don't need to change the primaryType and secondaryType in the end (outside the if statement).

ybNXpWL.gif


Super simple, right? Now, I know this thread is about critical hits, so let's do that then.
From my understanding of critical hits, each time a player hits someone, there's a chance of dealing X additional damage.
As we've learned up to now, we can multiply damage and we can apply a chance (ofcourse...). So, let's mix the two.

But wait, let's make it more customized. Let's have a message appear above the creatures head when a critical hit happens. And let's make it even more unique, let's have different messages depending on the type of attack.
In this example, melee attacks will have the message "SKULLBASH!" and distance attacks will have the message "HEADSHOT!". Also, they both will deal 10 times the original damage.
Remember, the origin parameter will tell us what type of attack has occurred. You can see their types above.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        if origin == ORIGIN_MELEE then
            creature:say("SKULLBASH!", TALKTYPE_MONSTER_SAY)
        elseif origin == ORIGIN_RANGED then
            creature:say("HEADSHOT!", TALKTYPE_MONSTER_SAY)
        end
        return primaryDamage * 10, primaryType, secondaryDamage, secondaryType
    end
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

qLN1Ee5.gif


NOTE: I did not include the ORIGIN_RANGED example because it is bugged. No matter what distance weapon you use the server will always respond with ORIGIN_MELEE. I've reported this to @Dalkon and a fix should be on the way soon.

Last example I have, let's use the ORIGIN_SPELL type. Let's say you have a super ability that absorbs all
spell damages. That means, when someone uses a damage spell on you, it will heal you instead.
Pretty simple, just use COMBAT_HEALING for the primaryType. COMBAT_HEALING is technically just healing damage, so you don't need to modify primaryDamage to make it heal, in fact, it doesn't work this way, you have to use COMBAT_HEALING.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if origin == ORIGIN_SPELL then
        return primaryDamage, COMBAT_HEALING, secondaryDamage, COMBAT_HEALING
    end
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

GgUwo6K.png


There's definitely THOUSANDS of different things you can do, all you need to do is have a lot of creativity.
This needs to be like in resources :p
 
Okay, prepare to be educated!

First off, some notes:
  • onHealthChange() and onManaChange() events are only called when health and mana are changed, respectively!
    • Yes, that means if there is a poff/spark, onHealthChange()/onManaChange() will never be called!
  • All parameters MUST be used or it will not work!
    • Do not worry about trying to figure out what to put if you can't think of anything, they have default values, just need to use the names alone!
  • Be smart, please remember this is called every time when health/mana changes, so don't bloat it up with dirty and messy code.
Here is the creature event function:
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)

Here are what the parameters mean:
  • creature: the victim, the one whose health is changing
  • attacker: the one changing the health, it can be nil (fields, traps, etc)
  • primaryDamage: the primary damage
  • primaryType: the type of the primary damage
  • secondaryDamage: the secondary damage
  • secondaryType: the type of the secondary damage
  • origin: the type of attack
    • ORIGIN_NONE
    • ORIGIN_CONDITION
    • ORIGIN_SPELL
    • ORIGIN_MELEE
    • ORIGIN_RANGED
You can find information on our beloved Wiki

What is the difference between primary and secondary?
This is pretty self-explanatory, let's say you have a firesword or an earth-enchanged warhammer.
The primary type will be physical, the secondary type is the elemental one (fire and earth, respectively).
secondaryDamage/secondaryType is only applied when there is a secondary damage/type, so it's somewhat uncommon.

Let's get into some examples:

In this example, I'm just showing you who the attacker and the creature are.
Remember, the attacker is the one changing the health, the victim is the one whose health is being changed.

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    attacker:sendTextMessage(MESSAGE_EVENT_ORANGE, "Attacker: " .. attacker:getName() .. "\n")
    attacker:sendTextMessage(MESSAGE_EVENT_ORANGE, "Victim: " .. creature:getName() .. "\n")

    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

lxSkz5n.png


Now let's say you want to have a bonus added to your weapon.
So, you can have a plain club and a special club (both same id), except special club has actionid 1000.
The special club will do twice the damage that a plain club will do.
So, when we change someone's health, we need to double the primary damage.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) 
    if attacker:getSlotItem(CONST_SLOT_LEFT):getActionId() == 1000 then
        return primaryDamage * 2, primaryType, secondaryDamage, secondaryType
    end
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

As you can see, I multiplied primaryDamage just by doing:
primaryDamage * 2

Plain club:
CM3p1R9.gif


Special club (actionid 1000):
HkEAXxW.gif


Let's keep going, right? It's pretty fun!

Let's play around with primary types. We'll have a 50-50 chance of hitting fire damage or ice damage.
Pretty simple, just need to set the primaryType to their respective types when a random number falls in the right spot.

NOTE: You can see all the primary types here

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        return primaryDamage, COMBAT_FIREDAMAGE, secondaryDamage, secondaryType
    end 
 
    return primaryDamage, COMBAT_ICEDAMAGE, secondaryDamage, secondaryType
end

30hRquj.gif


Pretty simple? Let's go deeper, shall we? We'll use a firesword for this example.
We want to modify the types for the firesword. Remember, a firesword is one of those weapons
that have both a primary and secondary damage/type. It attacks with physical and fire.
Let's change them, again on 50-50 chance, to earth and energy?
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        return primaryDamage, COMBAT_ENERGYDAMAGE, secondaryDamage, COMBAT_EARTHDAMAGE
    end 
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

Note that the primaryType and secondaryType inside the if statement are changed to earth and energy.
The fire and physical damage are defaulted from the firesword. So you don't need to change the primaryType and secondaryType in the end (outside the if statement).

ybNXpWL.gif


Super simple, right? Now, I know this thread is about critical hits, so let's do that then.
From my understanding of critical hits, each time a player hits someone, there's a chance of dealing X additional damage.
As we've learned up to now, we can multiply damage and we can apply a chance (ofcourse...). So, let's mix the two.

But wait, let's make it more customized. Let's have a message appear above the creatures head when a critical hit happens. And let's make it even more unique, let's have different messages depending on the type of attack.
In this example, melee attacks will have the message "SKULLBASH!" and distance attacks will have the message "HEADSHOT!". Also, they both will deal 10 times the original damage.
Remember, the origin parameter will tell us what type of attack has occurred. You can see their types above.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        if origin == ORIGIN_MELEE then
            creature:say("SKULLBASH!", TALKTYPE_MONSTER_SAY)
        elseif origin == ORIGIN_RANGED then
            creature:say("HEADSHOT!", TALKTYPE_MONSTER_SAY)
        end
        return primaryDamage * 10, primaryType, secondaryDamage, secondaryType
    end
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

qLN1Ee5.gif


NOTE: I did not include the ORIGIN_RANGED example because it is bugged. No matter what distance weapon you use the server will always respond with ORIGIN_MELEE. I've reported this to @Dalkon and a fix should be on the way soon.

Last example I have, let's use the ORIGIN_SPELL type. Let's say you have a super ability that absorbs all
spell damages. That means, when someone uses a damage spell on you, it will heal you instead.
Pretty simple, just use COMBAT_HEALING for the primaryType. COMBAT_HEALING is technically just healing damage, so you don't need to modify primaryDamage to make it heal, in fact, it doesn't work this way, you have to use COMBAT_HEALING.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if origin == ORIGIN_SPELL then
        return primaryDamage, COMBAT_HEALING, secondaryDamage, COMBAT_HEALING
    end
 
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

GgUwo6K.png


There's definitely THOUSANDS of different things you can do, all you need to do is have a lot of creativity.
can you edit this: https://github.com/otland/forgottenserver/wiki/Interface:Creature-Events#onHealthChange
I mean ur explanation is better than that post <3
 
LMAO! Hell yeah it is!

@Evan
Tried to make a spell that sets a storage value, and while its going on, all damage from hits you take add up, until you have taken three hits or more then it deals the added up damage to the attacker... It's not working though, no console error just not working here is my script...

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
    local dtsd = 0
    if Player(creature):getStorageValue(40107) == 3 then
    doTargetCombatHealth(creature, attacker, primaryType, -dtsd, -dtsd, CONST_ME_BLOCKHIT)
    elseif (Player(creature):getStorageValue(40107) <= 3 and Player(creature):getStorageValue(40107) > 0) then
    dtsd = dtsd + primaryDamage
    Player(creature):setStorageValue(40107,(Player(creature):getStorageValue(40107))+1)
    end
    return primaryDamage, primaryType, -secondaryDamage, secondaryType
end

and here is my spell....


Code:
local combat = createCombatObject()
setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE)
setCombatParam(combat, COMBAT_PARAM_AGGRESSIVE, false)

function onCastSpell(cid, var)

local function resetStorage(cid)
Player(cid):setStorageValue(40107, 0)
end

    if Player(cid):getStorageValue(40107) == 0 then
        Player(cid):setStorageValue(40107, 1)
        doCombat(cid, combat, var)
        addEvent(resetStorage, 30000, cid)
        else
        doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "You are already charging")
        return false
        end
    return
end

Any ideas why it's not working?

Storage values are working perfectly fine... They show up in database and disappear when told to... I don't know if they are adding up though...

Tried it like this too....

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
    local dtsd = 0
    if creature:getStorageValue(40107) == 3 then
    -- doTargetCombatHealth(creature, attacker, primaryType, -dtsd, -dtsd, CONST_ME_BLOCKHIT)  
    attacker:addHealth(-dtsd)
    elseif (creature:getStorageValue(40107) <= 3 and creature:getStorageValue(40107) > 0) then
    dtsd = dtsd + primaryDamage
    creature:setStorageValue(40107,(creature:getStorageValue(40107))+1)
    end
    return primaryDamage, primaryType, -secondaryDamage, secondaryType
end
 
Last edited by a moderator:
I didn't tried it... But why are you using additional functions (doTargetCombath~) instead of using the params of the onhealthchange?
Code:
return -dtsd, primaryType, -dtsd, secondaryType and creature:getPosition():sendMagicEffect(CONST_ME_BLOCKHIT)

@Edit: Nvm, I misunderstood what you said.
 
Last edited:
Code:
local dtsd = 0
This means each time the function (onHealthChange in this case) is called, dtsd gets set to 0. You need to store it in a way that it is not reset. You can either use an additional storagevalue or define dtsd as a table in the main chunk and do something like this:
Code:
local dtsd = {}
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
    local dtsd[creature:getId()] = dtsd[creature:getId()] or 0
    if creature:getStorageValue(40107) >= 3 then
        -- I'm no expert but why don't you just set primaryDamage = dtsd here?
        -- doTargetCombatHealth(creature, attacker, primaryType, -dtsd[creature:getId()], -dtsd[creature:getId()], CONST_ME_BLOCKHIT)  
        attacker:addHealth(-dtsd[creature:getId()])
        dtsd[creature:getId()] = nil -- reset now that we're done
    elseif (creature:getStorageValue(40107) <= 3 and creature:getStorageValue(40107) > 0) then
        dtsd[creature:getId()] = dtsd[creature:getId()] + primaryDamage
        creature:setStorageValue(40107,(creature:getStorageValue(40107))+1)
    end
    return primaryDamage, primaryType, -secondaryDamage, secondaryType
end
 
Code:
local dtsd = 0
This means each time the function (onHealthChange in this case) is called, dtsd gets set to 0. You need to store it in a way that it is not reset. You can either use an additional storagevalue or define dtsd as a table in the main chunk and do something like this:
Code:
local dtsd = {}
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
    local dtsd[creature:getId()] = dtsd[creature:getId()] or 0
    if creature:getStorageValue(40107) >= 3 then
        -- I'm no expert but why don't you just set primaryDamage = dtsd here?
        -- doTargetCombatHealth(creature, attacker, primaryType, -dtsd[creature:getId()], -dtsd[creature:getId()], CONST_ME_BLOCKHIT) 
        attacker:addHealth(-dtsd[creature:getId()])
        dtsd[creature:getId()] = nil -- reset now that we're done
    elseif (creature:getStorageValue(40107) <= 3 and creature:getStorageValue(40107) > 0) then
        dtsd[creature:getId()] = dtsd[creature:getId()] + primaryDamage
        creature:setStorageValue(40107,(creature:getStorageValue(40107))+1)
    end
    return primaryDamage, primaryType, -secondaryDamage, secondaryType
end

I tried it just like that, and it says


[Warning - Event::checkScript] Can not load script: scripts/onhealthgain.lua
data/creaturescripts/scripts/onhealthgain.lua:3: unexpected symbol near '['

I don't understand why you are using the creature:getId ? It already knows who the attack and who creature are, I just want the spell to start it and make it last for thirty seconds because the storage resets back to 0 at 30 seconds.. while the storage is 1 and 2 it should be adding up, on three it should hit whoever is attacking him for the damage previously done to him while charging (only while storage value is 1 or 2)...

Could you explain to me why you are using the creature:getId()?
 
I think the error is because I forgot to remove 'local' in front of dtsd on the first line in the function (oops).

I just use :getId() in order to get something to reference the creature in the table. You can't use a userdata object as an index, so I used the ID instead.
dtsd[creature:getId()] will actually be something like dtsd[8147694]. The reason I used a table is because it's declared in the main chunk of the script and will be the same for every player/monster. By storing the damage under the ID of the creature makes sure each creature has it's own value. I realize now that it might be better to use the attacker's ID than the creature's though.

Feel free to throw me a PM if you'd like me to explain something better, so we don't fill up the thread with worthless junk. :)
 
Can you make a skill like that?View attachment 28440

You can create that skill through lua as you can see here. You can create the skill in the source (through c++) as well. Both ways would require one of two options, either using an event (like say a talkaction) to have that skill shown to the player, or you will have to use otclient or another client and write it in there as well, I'm not entirely sure (since I haven't even used Otclient) but I believe in Otclient you can easily add new skills.
 
Okay, prepare to be educated!

First off, some notes:
  • onHealthChange() and onManaChange() events are only called when health and mana are changed, respectively!
    • Yes, that means if there is a poff/spark, onHealthChange()/onManaChange() will never be called!
  • All parameters MUST be used or it will not work!
    • Do not worry about trying to figure out what to put if you can't think of anything, they have default values, just need to use the names alone!
  • Be smart, please remember this is called every time when health/mana changes, so don't bloat it up with dirty and messy code.
Here is the creature event function:
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)

Here are what the parameters mean:
  • creature: the victim, the one whose health is changing
  • attacker: the one changing the health, it can be nil (fields, traps, etc)
  • primaryDamage: the primary damage
  • primaryType: the type of the primary damage
  • secondaryDamage: the secondary damage
  • secondaryType: the type of the secondary damage
  • origin: the type of attack
    • ORIGIN_NONE
    • ORIGIN_CONDITION
    • ORIGIN_SPELL
    • ORIGIN_MELEE
    • ORIGIN_RANGED
You can find information on our beloved Wiki

What is the difference between primary and secondary?
This is pretty self-explanatory, let's say you have a firesword or an earth-enchanged warhammer.
The primary type will be physical, the secondary type is the elemental one (fire and earth, respectively).
secondaryDamage/secondaryType is only applied when there is a secondary damage/type, so it's somewhat uncommon.

Let's get into some examples:

In this example, I'm just showing you who the attacker and the creature are.
Remember, the attacker is the one changing the health, the victim is the one whose health is being changed.

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    attacker:sendTextMessage(MESSAGE_EVENT_ORANGE, "Attacker: " .. attacker:getName() .. "\n")
    attacker:sendTextMessage(MESSAGE_EVENT_ORANGE, "Victim: " .. creature:getName() .. "\n")

    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

lxSkz5n.png


Now let's say you want to have a bonus added to your weapon.
So, you can have a plain club and a special club (both same id), except special club has actionid 1000.
The special club will do twice the damage that a plain club will do.
So, when we change someone's health, we need to double the primary damage.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if attacker:getSlotItem(CONST_SLOT_LEFT):getActionId() == 1000 then
        return primaryDamage * 2, primaryType, secondaryDamage, secondaryType
    end

    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

As you can see, I multiplied primaryDamage just by doing:
primaryDamage * 2

Plain club:
CM3p1R9.gif


Special club (actionid 1000):
HkEAXxW.gif


Let's keep going, right? It's pretty fun!

Let's play around with primary types. We'll have a 50-50 chance of hitting fire damage or ice damage.
Pretty simple, just need to set the primaryType to their respective types when a random number falls in the right spot.

NOTE: You can see all the primary types here

Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        return primaryDamage, COMBAT_FIREDAMAGE, secondaryDamage, secondaryType
    end

    return primaryDamage, COMBAT_ICEDAMAGE, secondaryDamage, secondaryType
end

30hRquj.gif


Pretty simple? Let's go deeper, shall we? We'll use a firesword for this example.
We want to modify the types for the firesword. Remember, a firesword is one of those weapons
that have both a primary and secondary damage/type. It attacks with physical and fire.
Let's change them, again on 50-50 chance, to earth and energy?
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        return primaryDamage, COMBAT_ENERGYDAMAGE, secondaryDamage, COMBAT_EARTHDAMAGE
    end

    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

Note that the primaryType and secondaryType inside the if statement are changed to earth and energy.
The fire and physical damage are defaulted from the firesword. So you don't need to change the primaryType and secondaryType in the end (outside the if statement).

ybNXpWL.gif


Super simple, right? Now, I know this thread is about critical hits, so let's do that then.
From my understanding of critical hits, each time a player hits someone, there's a chance of dealing X additional damage.
As we've learned up to now, we can multiply damage and we can apply a chance (ofcourse...). So, let's mix the two.

But wait, let's make it more customized. Let's have a message appear above the creatures head when a critical hit happens. And let's make it even more unique, let's have different messages depending on the type of attack.
In this example, melee attacks will have the message "SKULLBASH!" and distance attacks will have the message "HEADSHOT!". Also, they both will deal 10 times the original damage.
Remember, the origin parameter will tell us what type of attack has occurred. You can see their types above.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if math.random(100) <= 50 then
        if origin == ORIGIN_MELEE then
            creature:say("SKULLBASH!", TALKTYPE_MONSTER_SAY)
        elseif origin == ORIGIN_RANGED then
            creature:say("HEADSHOT!", TALKTYPE_MONSTER_SAY)
        end
        return primaryDamage * 10, primaryType, secondaryDamage, secondaryType
    end

    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

qLN1Ee5.gif


NOTE: I did not include the ORIGIN_RANGED example because it is bugged. No matter what distance weapon you use the server will always respond with ORIGIN_MELEE. I've reported this to @Dalkon and a fix should be on the way soon.

Last example I have, let's use the ORIGIN_SPELL type. Let's say you have a super ability that absorbs all
spell damages. That means, when someone uses a damage spell on you, it will heal you instead.
Pretty simple, just use COMBAT_HEALING for the primaryType. COMBAT_HEALING is technically just healing damage, so you don't need to modify primaryDamage to make it heal, in fact, it doesn't work this way, you have to use COMBAT_HEALING.
Code:
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if origin == ORIGIN_SPELL then
        return primaryDamage, COMBAT_HEALING, secondaryDamage, COMBAT_HEALING
    end

    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

GgUwo6K.png


There's definitely THOUSANDS of different things you can do, all you need to do is have a lot of creativity.

This is amazing, there's no feature like this for 0.4 right? Maybe similiar things could be done with onStatsChange I guess?
 
Last edited:
This is amazing, there's no feature like this for 0.4 right? Maybe similiar things could be done with onStatsChange I guess?


Yes, I don't use that server, however I do recall seeing that function and event, if I remember correctly, the parameters should have what you need to make this, however I believe it would be more complex than what evan has showed us with this function/event
 
Back
Top