• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Modulo - How to use it to detect a multiple of a real

Status
Not open for further replies.

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,515
Hello, I'm having trouble using Modulo. I read some posts on here and tried to recreate what they did but nothing seems to work. When I thought I had it set up correctly the trigger completely stopped working, which was weird because the modulo was just a small calculation that shouldn't have interfered with the other actions.

Anyway, here's what I have in my trigger. I have a Real that increases by 0.03 every 0.03 seconds, and I want to detect when this real is a multiple of 0.15. This should run 15 times because the timer only lasts 2.10 seconds, but it only runs 4 times the way it's set up now.

  • Poseidon Tidal Command Run
    • Events
    • Conditions
    • Actions
      • If - Conditions
        • Or - Any (Conditions) are true
          • Conditions
            • PoseidonTCDuration[TempInt] Equal to 0.00
            • (PoseidonTCDuration[TempInt] mod 0.15) Equal to 0.00
      • Then - Actions
 
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,515
Sorry for not being clear enough, I didn't want to post a big messy trigger. I use a timer that runs every 0.03 seconds because it is in charge of ALL of my indexed spells.

Anyway, I actually managed to get it to work using an integer instead of a real. So I suppose this is solved, although I would like to know why the real didn't work. It's not a big deal, but it would save me from having to use an extra variable :p

This is what I used to get the multiple of 15 that WORKS:
(PoseidonTCInteger[TempInt] mod 15) Equal to 0

And here is the WORKING trigger (note that it runs every 0.03 seconds):

  • Poseidon Tidal Command Run
    • Events
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • PoseidonTCDuration[TempInt] Less than 2.10
        • Then - Actions
          • -------- Damage --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Or - Any (Conditions) are true
                • Conditions
                  • (PoseidonTCInteger[TempInt] mod 15) Equal to 0
                  • PoseidonTCInteger[TempInt] Equal to 0
            • Then - Actions
              • Set TempPoint1 = (Position of PoseidonTCDummy[TempInt])
              • Set TempPlayer1 = (Owner of PoseidonTCDummy[TempInt])
              • Custom script: set bj_wantDestroyGroup=true
              • Unit Group - Pick every unit in (Units within 100.00 of TempPoint1) and do (Actions)
                • Loop - Actions
                  • Set TempUnit2 = (Picked unit)
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • (TempUnit2 is alive) Equal to True
                      • (TempUnit2 is A structure) Equal to False
                      • (TempUnit2 is Undead) Equal to False
                      • (TempUnit2 is invulnerable) Equal to False
                      • (TempUnit2 belongs to an enemy of TempPlayer1) Equal to True
                    • Then - Actions
                      • Unit - Cause PlayerHero[(Player number of TempPlayer1)] to damage TempUnit2, dealing PoseidonTCDamage1[TempInt] damage of attack type Spells and damage type Normal
                    • Else - Actions
              • Custom script: call RemoveLocation (udg_TempPoint1)
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • PoseidonTCDamage2[TempInt] Greater than 0.00
                • Then - Actions
                  • Set TempPoint1 = (PoseidonTCPoint[TempInt] offset by (Random real number between 0.00 and 150.00) towards (Random angle) degrees)
                  • Special Effect - Create a special effect at TempPoint1 using Abilities\Spells\Other\Monsoon\MonsoonBoltTarget.mdl
                  • Special Effect - Destroy (Last created special effect)
                  • Custom script: set bj_wantDestroyGroup=true
                  • Unit Group - Pick every unit in (Units within 100.00 of TempPoint1) and do (Actions)
                    • Loop - Actions
                      • Set TempUnit2 = (Picked unit)
                      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                        • If - Conditions
                          • (TempUnit2 is alive) Equal to True
                          • (TempUnit2 is A structure) Equal to False
                          • (TempUnit2 is Undead) Equal to False
                          • (TempUnit2 is invulnerable) Equal to False
                          • (TempUnit2 belongs to an enemy of TempPlayer1) Equal to True
                        • Then - Actions
                          • Unit - Cause PlayerHero[(Player number of TempPlayer1)] to damage TempUnit2, dealing PoseidonTCDamage2[TempInt] damage of attack type Spells and damage type Normal
                        • Else - Actions
                  • Custom script: call RemoveLocation (udg_TempPoint1)
                • Else - Actions
            • Else - Actions
          • -------- Dummy Movement --------
          • Set PoseidonTCDuration[TempInt] = (PoseidonTCDuration[TempInt] + 0.03)
          • Set PoseidonTCInteger[TempInt] = (PoseidonTCInteger[TempInt] + 3)
          • Set PoseidonTCAngle[TempInt] = (PoseidonTCAngle[TempInt] + (Random real number between -10.00 and 10.00))
          • Set TempPoint1 = ((Position of PoseidonTCDummy[TempInt]) offset by 6.00 towards PoseidonTCAngle[TempInt] degrees)
          • Unit - Move PoseidonTCDummy[TempInt] instantly to TempPoint1
          • Custom script: call RemoveLocation (udg_TempPoint1)
          • -------- End --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • PoseidonTCDuration[TempInt] Equal to 2.10
            • Then - Actions
              • Unit - Kill PoseidonTCDummy[TempInt]
              • Set SpellLoopIsOn[TempInt] = False
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • PoseidonTCDamage2[TempInt] Greater than 0.00
                • Then - Actions
                  • Set PoseidonTCDamage2[TempInt] = 0.00
                  • Custom script: call RemoveLocation (udg_PoseidonTCPoint[udg_TempInt])
                • Else - Actions
            • Else - Actions
        • Else - Actions
 
Last edited:
Level 6
Joined
Jan 9, 2019
Messages
102
I'm quite sure these lines should be located above --Damage--.
  • Set PoseidonTCDuration[TempInt] = (PoseidonTCDuration[TempInt] + 0.03)
  • Set PoseidonTCInteger[TempInt] = (PoseidonTCInteger[TempInt] + 3)
Then if the real version still work improperly, try:
  • (PoseidonTCDuration[TempInt] mod 0.15) Less than 0.03
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,515
I'm quite sure these lines should be located above --Damage--.
  • Set PoseidonTCDuration[TempInt] = (PoseidonTCDuration[TempInt] + 0.03)
  • Set PoseidonTCInteger[TempInt] = (PoseidonTCInteger[TempInt] + 3)
Then if the real version still work improperly, try:
  • (PoseidonTCDuration[TempInt] mod 0.15) Less than 0.03

I intentionally have those lines below damage so that the spell deals damage at 0.00 duration in addition to every 0.15 seconds.

And that real version doesn't work for some reason. It works at 0.15 and 0.30, but doesn't work at 0.45 and other increments of 0.15 beyond that.
 
Level 6
Joined
Jan 9, 2019
Messages
102
I intentionally have those lines below damage so that the spell deals damage at 0.00 duration in addition to every 0.15 seconds.
For this trigger, changing the order doesn't change that mate. Whether it's run when 0.00 duration hits or not is not up to itself. Plus, when it does so, it still do everything below --Damage--, and according to things in --End--, it'd mean that the entire thing would run for 2.07 seconds, ending 1 tick prematurely.

See this.
  • -------- Damage --------
  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
  • -------- Dummy Movement --------
  • Set PoseidonTCDuration[TempInt] = (PoseidonTCDuration[TempInt] + 0.03)
  • Set PoseidonTCInteger[TempInt] = (PoseidonTCInteger[TempInt] + 3)
Should I say more mate?

And that real version doesn't work for some reason. It works at 0.15 and 0.30, but doesn't work at 0.45 and other increments of 0.15 beyond that.
I actually just tested this. For 0.15, starting from 0.6 the resultant shifted from lim x->0 x to lim x->0.15 x. The following is the source of the operation.

JASS:
// this is GUI's mod function
function ModuloReal takes real dividend, real divisor returns real
    // apparently this line itself can return lim x->divisor x instead of lim x->0 x
    local real modulus = dividend - I2R(R2I(dividend / divisor)) * divisor

    if (modulus < 0) then
        set modulus = modulus + divisor
    endif

    return modulus
endfunction

// tested working solution, don't know if anyone else has this already covered
function RealMod takes real left, real right returns real
	set left = left - R2I(left/right)*right

	if (left == right) then
		return 0.
	elseif (left < 0) then
		return left + right
	endif

	return left
endfunction
It has to do with technical stuffs relating to real operations, which I do not know. I read somewhere that JASS' == operator is inaccurate for easiness purpose (like 0.0000013 == 0.00 is still true even though it should be false), but the other relational operators don't have this inaccuracy and can lead to these complications.

Test result (of GUI's mod function):
RealModuloTestResult.png

Unfortunately replicating the above solution in pure GUI is too much to do, JASS + custom script action is the way for GUI.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,515
Oh, I see now. I was too hung up on this old condition "if PoseidonTCInteger[TempInt] Equal to 0", and was under the impression that it wouldn't fire unless I had it setup the way I did. I didn't understand how modulo really worked when I originally put it in there hehe. Thanks for letting me know, just fixed it all up.

And cool solution, maybe i'll give it a shot. Thanks!
 
Status
Not open for further replies.
Top