[Spell] Crit System

Level 11
Joined
May 16, 2020
Messages
657
Hi guys,

I want to create a system with which I can trigger all crits in my map.

So far, I created all my crits in this fashion:
  • The Crit system so far
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.10
    • Conditions
      • (Level of ABILITY for DamageEventSource) Greater than 0
      • (DamageEventTarget is A structure) Equal to False
      • DamageEventAttackT Equal to ATTACK_TYPE_HERO
      • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Random percentage) Less than or equal to 15.00
        • Then - Actions
          • Set VariableSet Temp_Point = (Position of DamageEventSource)
          • Set VariableSet DamageEventAmount = (DamageEventAmount x (0.75 + (1.25 x (Real((Level of Coup de Grace for DamageEventSource))))))
          • -------- --------
          • Floating Text - Create floating text that reads ((String((Integer((DamageEventAmount + 0.50))))) + !) at Temp_Point with Z offset -25.00, using font size 10.00, color (100.00%, 0.00%, 0.00%), and 0.00% transparency
          • Set VariableSet Global_FloatingText = (Last created floating text)
          • -------- --------
          • Floating Text - Hide Global_FloatingText for (All players)
          • Set VariableSet Global_PlayerGroup = (All players matching ((DamageEventSource is visible to (Matching player).) Equal to True).)
          • Floating Text - Show Global_FloatingText for Global_PlayerGroup
          • Custom script: call DestroyForce(udg_Global_PlayerGroup)
          • -------- --------
          • Floating Text - Change Global_FloatingText: Disable permanence
          • Floating Text - Set the velocity of Global_FloatingText to 72.00 towards 90.00 degrees
          • Floating Text - Change the lifespan of Global_FloatingText to 5.00 seconds
          • Floating Text - Change the fading age of Global_FloatingText to 2.00 seconds
          • -------- --------
          • Custom script: call RemoveLocation (udg_Temp_Point)
        • Else - Actions

The problem with this: If crits happen at the same time (for example because a unit has a crit ability and crit item), the crits overlap and happen both at once.
Instead of overlapping, I want the crit with the higher multiplier amount to have priority over the other (i.e. so only 1 crit happens).

@Bribe helped get started:

The most efficient method is to put these all in one trigger as someone suggested.

The logic flow in your case is to create a variable called CritAmount (or whatever you want it to be called) and do:

Set CritAmount = 1.00
——check your crit conditions and change CritAmount to the multiplier ONLY if the multiplier is greater than CritAmount ——
Multiply the damageeventamount by CritAmount.

...but I'm still struggling with the implementation. To create a crit system which stores everything, I think I will need two things:

1) A trigger to define the crit chance + multiplier per unit:

  • Events
    • Unit - A unit Learns a skill
    • Unit - A unit Acquires an item
    • Unit - A unit Loses an item
  • Conditions
  • Actions
    • Set VariableSet Crit_Chance[unit custom value] = 25.0
    • Set VariableSet Crit_Multiplier[unit custom value] = 1.75

2) The system to trigger the crits. But here is were I'm stuck:

  • Events
    • Game - DamageModifierEvent becomes Equal to 1.10
  • Conditions
    • (DamageEventTarget is A structure) Equal to False
    • DamageEventAttackT Equal to ATTACK_TYPE_HERO
    • DamageEventDamageT Equal to DAMAGE_TYPE_NORMAL
  • Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • (Random percentage) Less than or equal to Crit_Chance[unit custom value]
      • Then - Actions
        • .... this will not work, the crit chance/multiplier are overwritten if the unit has an ability + item...
        • So the suggested loop will not work.
      • Else - Actions

But this will not work, as the chances/multipliers[custom value] will overwrite each others.

Can anyone help please?
 
Level 5
Joined
Jul 2, 2013
Messages
141
Well, make sure you control all criticals with triggers. Turn them off. Activate the 2nd crit and after an attack, turn the 2nd crit off, then turn the first crit on. Tedious to code, but surely would work.
TIP: You can use normal crit ability without any other systems. Just replace the 2 crit abilities. Hope I helped.
 
Level 13
Joined
Feb 27, 2019
Messages
330
I thought about using a hashtable but imagined it would be a nightmare so Id rather store each individual crit item/ability seperately as an integer that is updated with +/-1 when item/ability is acquired or lost. Then run the loops in the damage event something like this.

  • Events
  • Conditions
  • Actions
    • For each (Integer A) from 1 to CritItem1(custom value of unit), do (Actions)
      • Loop - Actions
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Random real number between 0.01 and 1.00) Less than 0.16
          • Then - Actions
            • Set VariableSet TempBln = True
            • Set VariableSet TempReal = 1.75
            • Custom script: exitwhen true
          • Else - Actions
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • TempBln Equal to False
      • Then - Actions
        • For each (Integer A) from 1 to CritItem2(custom value of unit), do (Actions)
          • Loop - Actions
            • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
              • If - Conditions
                • (Random real number between 0.01 and 1.00) Less than 0.16
              • Then - Actions
                • Set VariableSet TempBln = True
                • Set VariableSet TempReal = 1.50
                • Custom script: exitwhen true
              • Else - Actions
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • TempBln Equal to False
          • Then - Actions
            • For each (Integer A) from 1 to CritItem3(custom value of unit), do (Actions)
              • Loop - Actions
                • Set VariableSet TempReal = 1.25
                • Custom script: exitwhen true
          • Else - Actions
      • Else - Actions
    • Set VariableSet TempBln = False
 
Level 13
Joined
Feb 27, 2019
Messages
330
I decided to face my hashtable nightmare.

Initialize Hashtable and variables. I did it like this because changing default values of a variable later will disable every condition/action they are used in. I imagined that the max amount of Critical Strike items/abilities/buffs one unit can have is 10 but it can easily be increased by changing these variables. The variables are basically used as a kind of index where CritIndex is the index between 1-10. So CriChance + CritIndex can be anything from 11 up to 20.

  • Create Hashtable
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Hashtable - Create a hashtable
      • Set VariableSet Hashtable = (Last created hashtable)
      • Set VariableSet CritIndex = 0
      • Set VariableSet CritType = 0
      • Set VariableSet CritChance = 10
      • Set VariableSet CritMultiplier = 20

This is what is put in the damage event trigger. The Key will be unique for each unit. The loop will go through all active Critical Strikes the unit has and apply the best activated multiplier value to TempReal.

  • Damage Event
    • Events
    • Conditions
    • Actions
      • Set VariableSet TempReal = 1.00
      • Set VariableSet Key = (Custom value of (Triggering unit))
      • For each (Integer CritL) from 1 to (Load CritIndex of Key from Hashtable.), do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Random real number between 0.01 and 1.00) Less than (Load (CritL + CritChance) of Key from Hashtable.)
              • (Load (CritL + CritMultiplier) of Key from Hashtable.) Greater than TempReal
            • Then - Actions
              • Set VariableSet TempReal = (Load (CritL + CritMultiplier) of Key from Hashtable.)
            • Else - Actions
      • Set VariableSet DamageEventAmount = (DamageEventAmount x TempReal)

Here is an example of the Critical Strike being added to the hashtable and unique Key(Index) of that unit. First which item is checked and later Int will become our CritType. All these Critical Strikes will stack, no matter if we have more of the same item. Then TempReal is set to CritChance and TempReal2 is set to CritMultiplier.

  • Acquire Crit Item
    • Events
      • Unit - A unit Acquires an item
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Item-type of (Item being manipulated)) Equal to Claws of Attack +15
        • Then - Actions
          • Set VariableSet Int = 1
          • Set VariableSet TempReal = 0.16
          • Set VariableSet TempReal2 = 1.25
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Item-type of (Item being manipulated)) Equal to Crown of Kings +5
            • Then - Actions
              • Set VariableSet Int = 2
              • Set VariableSet TempReal = 0.21
              • Set VariableSet TempReal2 = 1.40
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Item-type of (Item being manipulated)) Equal to Kelen's Dagger of Escape
                • Then - Actions
                  • Set VariableSet Int = 3
                  • Set VariableSet TempReal = 0.16
                  • Set VariableSet TempReal2 = 1.75
                • Else - Actions
                  • Custom script: return
      • Set VariableSet Key = (Custom value of (Triggering unit))
      • Hashtable - Save ((Key (Load CritIndex of Key in Hashtable.).) + 1) as CritIndex of Key in Hashtable.
      • Hashtable - Save Int as (CritType + (Load CritIndex of Key from Hashtable.)) of Key in Hashtable.
      • Hashtable - Save TempReal as (CritChance + (Load CritIndex of Key from Hashtable.)) of Key in Hashtable.
      • Hashtable - Save TempReal2 as (CritMultiplier + (Load CritIndex of Key from Hashtable.)) of Key in Hashtable.

We also want to remove the Critical Strike from the hashtable if the item is lost. This is done through dynamic indexing.

  • Lose Crit Item
    • Events
      • Unit - A unit Loses an item
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Item-type of (Item being manipulated)) Equal to Claws of Attack +15
        • Then - Actions
          • Set VariableSet Int = 1
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Item-type of (Item being manipulated)) Equal to Crown of Kings +5
            • Then - Actions
              • Set VariableSet Int = 2
            • Else - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Item-type of (Item being manipulated)) Equal to Kelen's Dagger of Escape
                • Then - Actions
                  • Set VariableSet Int = 3
                • Else - Actions
                  • Custom script: return
      • Set VariableSet Key = (Custom value of (Triggering unit))
      • For each (Integer CritL) from 1 to (Load CritIndex of Key from Hashtable.), do (Actions)
        • Loop - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Load (CritL + CritType) of Key from Hashtable.) Equal to 1
            • Then - Actions
              • Hashtable - Save (Load (CritType + (Load CritIndex of Key from Hashtable.)) of Key from Hashtable.) as (CritL + CritType) of Key in Hashtable.
              • Hashtable - Save (Load (CritChance + (Load CritIndex of Key from Hashtable.)) of Key from Hashtable.) as (CritL + CritChance) of Key in Hashtable.
              • Hashtable - Save (Load (CritMultiplier + (Load CritIndex of Key from Hashtable.)) of Key from Hashtable.) as (CritL + CritMultiplier) of Key in Hashtable.
              • Hashtable - Save ((Key (Load CritIndex of Key in Hashtable.).) - 1) as CritIndex of Key in Hashtable.
              • Custom script: exitwhen true
            • Else - Actions

This
  • Hashtable - Save (Load (CritChance + (Load CritIndex of Key from Hashtable.)) of Key from Hashtable.) as (CritL + CritChance) of Key in Hashtable.
Equals this
  • Hashtable - Save (Load (10 + (Load 0 of Key from Hashtable.)) of Key from Hashtable.) as (CritL + 10) of Key in Hashtable.

The CritIndex of Key from Hashtable will probably need to be set to 0 when a unit is removed from the game to avoid another unit gaining the Crits.

I havnt tested it but it should work. You can test it if you like.
 

Attachments

  • hashtable.w3m
    19.8 KB · Views: 16
Level 11
Joined
May 16, 2020
Messages
657
Thanks a lot for the extended answer an the test map! Let me test it more throughly before I give feedback.

But tbh I was hoping that there would be a solution which doesn't involve cycling through all specific integers of a units when an attack happens in the map. I will take what I can if there's not a better solution, but since this system will run hundreds of times every second in my map, I want to ensure that's truly the best/only option.

I checked available systems, and potentially Chopinski already found a solution in his New Bonus system. The Problem is that I don't really understand how he's running the crits or handling multiple crits running at the same time...

@chopinski @ThompZon Would you be able to help here please?
 
Thanks a lot for the extended answer an the test map! Let me test it more throughly before I give feedback.

But tbh I was hoping that there would be a solution which doesn't involve cycling through all specific integers of a units when an attack happens in the map. I will take what I can if there's not a better solution, but since this system will run hundreds of times every second in my map, I want to ensure that's truly the best/only option.

I checked available systems, and potentially Chopinski already found a solution in his New Bonus system. The Problem is that I don't really understand how he's running the crits or handling multiple crits running at the same time...

@chopinski @ThompZon Would you be able to help here please?
I have an indexer and two arrays.
On index, set crit-chance-array to 0.0, crit-damage to 2.0.
When getting critical hit, I add or set whatever to crit-chance and crit-damage.
On damage dealt, some ifs to check that it's a regular attack that should be able to crit, roll a random real, if it's less that crit-chance, multiply the damage by the crit-damage amount.

That's how I do it and Chopinskis crit-system is basically the same thing.
 
Level 13
Joined
Feb 27, 2019
Messages
330
I think new bonus and using something like a hashtable works differently. New bonus must be supposed to be used with much lower values, like increasing crit chance with +0.05(5%). If 5 items increase it with 0.15 and 1.5 crit that would be 75% crit chance to do 3.5 times damage, while using the hashtable will be 0.15/1 + 0.15/1 + 0.15/1 or however it is calculated to do 1.5 times damage. It all depends how youd like the system. I actually like the method with single increased values a bit more, but it scales very differently aswell. It becomes a straight and clean bonus instead of seperate crit abilities that take priority over each other.
 
Level 11
Joined
May 16, 2020
Messages
657
I have an indexer and two arrays.
On index, set crit-chance-array to 0.0, crit-damage to 2.0.
When getting critical hit, I add or set whatever to crit-chance and crit-damage.
On damage dealt, some ifs to check that it's a regular attack that should be able to crit, roll a random real, if it's less that crit-chance, multiply the damage by the crit-damage amount.

That's how I do it and Chopinskis crit-system is basically the same thing.

Sorry what do you mean with "I have an indexer and two arrays."?
Is the code available in your map "The Defence of the Castle" to check?

I think new bonus and using something like a hashtable works differently. New bonus must be supposed to be used with much lower values, like increasing crit chance with +0.05(5%). If 5 items increase it with 0.15 and 1.5 crit that would be 75% crit chance to do 3.5 times damage, while using the hashtable will be 0.15/1 + 0.15/1 + 0.15/1 or however it is calculated to do 1.5 times damage. It all depends how youd like the system. I actually like the method with single increased values a bit more, but it scales very differently aswell. It becomes a straight and clean bonus instead of seperate crit abilities that take priority over each other.

Ah, that is a good point. I THINK for my map I will only use fixed crit % values, for example in abilities or items. I don't plan to have buffs or attacks which add 5% stacking crit chance. But let me think a bit more on this...
 
Sorry what do you mean with "I have an indexer and two arrays."?
Is the code available in your map "The Defence of the Castle" to check?
It uses a really bad way of doing it I made like 3-4 years ago.
I remade it earlier this week to what I said, but I have not uploaded that version yet.

The system I suggest is flexible and MUI, but you'll have to set, add, or subtract the critical hit yourself.

What I meant will result in a system that :
  1. Have Damage Engine 5.7.1.2 and GUI Unit Indexer 1.4.0.0 (The 2nd one is an indexer. Note that it sets "Custom Value" of units so you cannot use it yourself!)
  2. Create 2 "real" array variables. One will represent "Critical Hit Chance" and the other "Critical Hit Damage".
  3. Create triggers
  • OnUnitIndex
    • Events
      • Game - UnitIndexEvent becomes Equal to 1.00
    • Conditions
    • Actions
      • Set crit_chance[UDex] = 0.00
      • Set crit_damage[UDex] = 2.00
This will "initialize" the crit chance to 0 and critial hit damage to x2. These can be any values.

  • CritCore
    • Events
      • Game - DamageModifierEvent becomes Less than 3.50
    • Conditions
      • IsDamageSpell Equal to False
    • Actions
      • Set temp_integer1 = (Custom value of DamageEventSource)
      • Set temp_real1 = crit_chance[temp_integer1]
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • DamageEventAmount Greater than 0.00
          • (Random real number between 0.00 and 1.00) Less than temp_real1
        • Then - Actions
          • Set hit_is_crit = True
          • Set DamageEventAmount = (DamageEventAmount x crit_damage[temp_integer1])
          • Trigger - Run CritCoreFloatingText <gen> (ignoring conditions)
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (((Max life of DamageEventTarget) / 3.00) + (Life of DamageEventTarget)) Less than or equal to DamageEventAmount
            • Then - Actions
              • -------- damageDone >= (maxhp/3) + currhp --------
              • Unit - Make DamageEventTarget Explode on death
            • Else - Actions
        • Else - Actions
          • Set hit_is_crit = False
I use "hit_is_crit" to allow items/abilities to have special effects if the hit is a critical hit.
The if-statement with the "Explode on death" is only a reference to "Baldurs Gate" when critical hit, it "splatts" targets. It's not really needed.

  • CritCoreFloatingText
    • Events
    • Conditions
    • Actions
      • Set temp_point = (Position of DamageEventTarget)
      • Floating Text - Create floating text that reads ((String((Integer(DamageEventAmount)))) + !) at temp_point with Z offset 125.00, using font size 12.00, color (100.00%, 5.00%, 5.00%), and 0.00% transparency
      • Floating Text - Change (Last created floating text): Disable permanence
      • Floating Text - Change the lifespan of (Last created floating text) to 0.50 seconds
      • Floating Text - Change the fading age of (Last created floating text) to 0.40 seconds
      • Floating Text - Set the velocity of (Last created floating text) to (128.00 + (Random real number between 0.00 and 32.00)) towards (75.00 + (Random real number between 0.00 and 30.00)) degrees
      • Custom script: call RemoveLocation(udg_temp_point)
The floating text is only cosmetic.

You set the critical hit chance of triggering unit to 25% to deal 4x damage by doing this.
  • Set crit_chance[Custom value of (Triggering Unit)] = 0.25
  • Set crit_damage[Custom value of (Triggering Unit)] = 4.00
Or you could add 5% by doing:
  • Set crit_chance[Custom value of (Triggering Unit)] = crit_chance[Custom value of (Triggering Unit)] + 0.05
 
Top