1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[Solved] Help with fire spread trigger

Discussion in 'Triggers & Scripts' started by wc3neverdies, Sep 3, 2019.

  1. wc3neverdies

    wc3neverdies

    Joined:
    Sep 20, 2015
    Messages:
    281
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Hello as title says i want to make fire spread on my map. The fire effect is a unit with immolation so it burns nearby enemies and negative HP regen so it destroy itslef.
    I used OE filed to achieve this becasue i think it's better than trigger these effects.

    The fire spread is made by creating a fire unit next to another if the terrain type is grass.

    The first tirgger is the one that start the effect, and is activated whenever a fire unit enters the map.

    Code (vJASS):


    globals

       group array FireUnitGroup
       effect treeburnfx
       integer spreadint
    endglobals

    function DestAoeDmgFireDOT takes nothing returns nothing
     
     

       if IsDestructableAlive(GetEnumDestructable()) and IsDestructableTree(GetEnumDestructable()) then
         //set treeburnfx = AddSpecialEffectLocBJ( GetDestructableLoc(GetEnumDestructable()), "Environment\\LargeBuildingFire\\LargeBuildingFire2.mdl" )
         //call BlzSetSpecialEffectZ( treeburnfx, ( BlzGetLocalSpecialEffectZ(treeburnfx) + 200.00 ) )  

        call SetDestructableAnimation( GetEnumDestructable(), " stand hit")  
     
       call SetDestructableLife( GetEnumDestructable(), ( GetDestructableLife(GetEnumDestructable()) - 10) )

       endif

    endfunction


    function fireunitactions takes nothing returns nothing

        local unit pfu = GetEnumUnit()
        local real frx = GetUnitX(GetEnumUnit())
        local real fry = GetUnitY(GetEnumUnit())
        local real frz = BlzGetUnitZ(GetEnumUnit())
     local integer i = spreadint //GetPlayerId(GetOwningPlayer(GetEnumUnit()))

      local real angle = 0
      local real nfrx
      local real nfry
      local group tfg

         

        loop
        exitwhen(angle > 360)



            if angle == 0 or angle == 90 or angle == 180 or angle == 270 then
              set nfrx= frx + 129*Cos(angle)
              set nfry= fry + 129*Sin(angle)
            endif
            if angle == 45 or angle == 135 or angle == 225 or angle == 315 then
              set nfrx= frx + 181*Cos(angle*bj_DEGTORAD)
              set nfry= fry + 181*Sin(angle*bj_DEGTORAD)
            endif

         
     
            set tfg = GetUnitsInRangeOfLocMatching(100, Location(nfrx,nfry), Condition( function fireunitbool)  )
             

               if CountUnitsInGroup(tfg) == 0 and GetTerrainType(nfrx, nfry) == 'Fgrd' then
                  call CreateUnitAtLoc( Player(i), 'n005', Location(nfrx, nfry), 0. )
               endif

               call SetUnitLifeBJ(pfu, ( GetUnitStateSwap(UNIT_STATE_LIFE, pfu) - 2 ) )
             
               call DestroyGroup(tfg)

        set angle = angle + 45
        endloop
       
           // D E S T R   F I R E   DOT   D A M A G E
        call EnumDestructablesInCircleBJ( 100.00, Location(frx,fry), function DestAoeDmgFireDOT  )



    endfunction

    function fireunitpick1 takes nothing returns boolean
       
        local integer i = spreadint

        return ( GetOwningPlayer(GetFilterUnit()) == Player(i) )
    endfunction


    function FireSpreadLoop takes nothing returns nothing

        local integer i = spreadint
     
        set FireUnitGroup[i] = GetUnitsInRectMatching(GetPlayableMapRect(), And(Condition(function fireunitpick1), Condition(function fireunitbool)) )  
     
         
         if CountUnitsInGroup(FireUnitGroup[i]) > 0 then  
         
           call ForGroupBJ(FireUnitGroup[i],function fireunitactions)

         endif


         if CountUnitsInGroup(FireUnitGroup[i]) == 0 then
          call PauseTimer(udg_FireSpreadTimer[i])
          call DisplayTextToForce( GetPlayersAll(), "SpreadFireOFF  " + I2S(i) )
          //call DestroyEffect(treeburnfx)
         endif

       call DestroyGroup(FireUnitGroup[i])

    endfunction



    function Trig_FirespreadJASS_Actions takes nothing returns nothing


        local integer i = GetPlayerId(GetOwningPlayer(GetEnteringUnit()))
        local unit eu = GetEnteringUnit()
     
        set spreadint = i

    if GetUnitTypeId(eu) == 'n005' and GetOwningPlayer(eu) == Player(i)  then
        call DisplayTextToForce( GetPlayersAll(), "SpreadFireON  " + I2S(i) )

        call GroupAddUnit(FireUnitGroup[i], eu)
     
        //call EnableTrigger( gg_trg_FireSpreadonYellowGrass )
        //call DisableTrigger( GetTriggeringTrigger() )
        call TimerStart( udg_FireSpreadTimer[i], 0.60, true,function FireSpreadLoop)
     
    endif

    endfunction

    //===========================================================================
    function InitTrig_FirespreadJASS takes nothing returns nothing
        set gg_trg_FirespreadJASS = CreateTrigger(  )
         
        //call DisableTrigger( gg_trg_FirespreadJASS )
        call TriggerRegisterEnterRectSimple( gg_trg_FirespreadJASS, GetPlayableMapRect() )
     
     
        call TriggerAddAction( gg_trg_FirespreadJASS,function Trig_FirespreadJASS_Actions )
    endfunction

     


    When a fire unit dies it changes the terrain to another type of grass(burned grass).

    Code (vJASS):



    function Trig_FireUnitDie_Actions takes nothing returns nothing

       local real dfux = GetUnitX(GetDyingUnit())
       local real dfuy = GetUnitY(GetDyingUnit())

        if GetUnitTypeId(GetDyingUnit()) == 'n005' then
     
            if GetTerrainType(dfux, dfuy) == 'Fgrd' or GetTerrainType(dfux, dfuy) == 'Lgrd' then
                 
                    call SetTerrainType(dfux, dfuy, 'Jgsb', -1, 1, 0)
             endif
        endif

    endfunction

    //===========================================================================
    function InitTrig_FireUnitDie takes nothing returns nothing
        set gg_trg_FireUnitDie = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( gg_trg_FireUnitDie, EVENT_PLAYER_UNIT_DEATH )
     
        call TriggerAddAction( gg_trg_FireUnitDie, function Trig_FireUnitDie_Actions )
    endfunction
     


    The problem is that the timer does not paused when there are no fire units in the map.

    Also i don't know why only 7 fire units are created instead of 8.


    Thanks in advance fo the help.

    EDIT: I fixed the deg to rad thing. Now my concern is that i keep getting the fire spread off message if i test it with player 2,3,4....
     
    Last edited: Sep 4, 2019
  2. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,905
    Resources:
    1
    Spells:
    1
    Resources:
    1
    • You're never initializing the unit groups. They need to be set = CreateGroup() on map init. You're also destroying them the first time the periodic function runs, not sure why you thought you needed to do that.
    • Where is function fireunitbool? I don't see it in your code.
    • The last time you posted about this fire spreading you had given these fire units locust and (presumably) learned that 'units in region' will not detect locusted units. Here you're using GetUnitsInRectMatching so it won't find them if they have locust....
    • There's no reason to do things like using local integer i = spreadint in FireSpreadLoop. Just use spreadint directly.
    • The way you are using spreadint, the spreading fire can only be owned by one player at a time. If a new player spawns fire while the first player's fire still exists, all newly created fire (around both fires) will belong to the new player.
    • You're still using a few BJ functions. Pretty much don't use them except things like TriggerRegisterAnyUnitEventBJ().
    • You can use BJDebugMsg("text here") to display messages for debugging instead of DisplayTextToForce(...)
    • There's no reason to do something like this:
      Code (vJASS):
           if CountUnitsInGroup(FireUnitGroup[i]) > 0 then  
           
             call ForGroupBJ(FireUnitGroup[i],function fireunitactions)

           endif

      If the group is empty nothing will be run in the ForGroup anyway. Additionally it may be worth it to do a FirstOfGroup loop instead of a ForGroup callback for some groups.
    • Your formatting is still a little weird but that's just my opinion.
    • You should use encapsulation (libraries and scopes with initializers) and global variables liberally instead of hardcoding things like 'Fgrd', 'n005', and 'Jgsb':
      Code (vJASS):
      library firespread initializer init
      globals
        private constant integer FIRE_ID = 'n005'
        private constant integer GRASS_1 = 'Fgrd'
        private constant integer GRASS_2=  'Lgrd'
        private constant real SPREAD_INTERVAL = 0.60
        //etc.
      endglobals
      //...
      private function init takes nothing returns nothing
        //sets up the global group array
      endfunction
      endlibrary

    • This line leaks a location: set tfg = GetUnitsInRangeOfLocMatching(100, Location(nfrx,nfry), Condition( function fireunitbool) ). You are leaking other locations in similar ways using Location(...).
    • You can and should use GetTriggerUnit() wherever you can instead of things like GetDyingUnit(), GetEnteringUnit(). Most of the time the only other unit event response I ever need is GetSpellTargetUnit().
    • This condition: CountUnitsInGroup(FireUnitGroup[i]) > 0 and this condition: CountUnitsInGroup(FireUnitGroup[i]) == 0 are opposites so you can just put the second one inside an else instead of checking the condition again for its inverse.
    • Stop using locations. Use XY coordinates directly. This removes possible leaks, are generally faster, and don't require the game engine to allocate resources for a handle.
    • Code (vJASS):
              if angle == 0 or angle == 90 or angle == 180 or angle == 270 then
                set nfrx= frx + 129*Cos(angle)
                set nfry= fry + 129*Sin(angle)
              endif
              if angle == 45 or angle == 135 or angle == 225 or angle == 315 then
                set nfrx= frx + 181*Cos(angle*bj_DEGTORAD)
                set nfry= fry + 181*Sin(angle*bj_DEGTORAD)
              endif

      You forgot a DEGTORAD but I think that's what your edit's about. This can be simplified using a trick with integer division since it truncates. If angle/90 == (angle + 45)/90 then it's a cardinal direction; if they're not equal it's a diagonal (think about it or do the math yourself).
      Code (vJASS):
      if angle/90 == (angle+45)/90 then
        set d = 128. //not 129.
      else
        set d = 181 //128*sqrt(2)
      endif
      set nfrx = frx + d*Cos(angle*bj_DEGTORAD)
      set nfry = fry + d*Sin(angle*bj_DEGTORAD)


    There's probably more but that's it for a first pass from me. I think you misusing the unit groups is the issue, so try fixing that first then tackle the other things I listed.
     
  3. wc3neverdies

    wc3neverdies

    Joined:
    Sep 20, 2015
    Messages:
    281
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Hello, first of all, thank you for the answer.


    1. Ahh, I see. Since this is a udg variable i thought it wasnt necessary. Will do.

    2. It's in another trigger that i made it before this one. I converted some GUI actions too see how i can add functions with argument to group function.
    Code (vJASS):

    function fireunitbool takes nothing returns boolean
        return ( GetUnitTypeId(GetFilterUnit()) == 'n005' )
    endfunction


    3.Yeah i removed the locust ability from the units.

    4. I know. I recently started to use jass, so instead of deleting a line that i might need to write back i just changed the value there.

    5. Yeah, that's my main problem, 'spreadint' variable reaches 27, i don't know how since the loop is supposed to stop at 23.

    6. I know but i find them useful in term of less copy paste. I use BJ's that are more lines of normal code that i would need to copy or write myself. How can i avoid using these? Like: PolledWait, two point distance or some boolean.

    7. I do when i can, the only problem with that is the duration of the text. It's set to 60, too long for me, since i have miltiple triggers debug messages.



    1. Ahh thank you. I am a JASS noob so i will check others toutorials about groups in JASS.

    2. I know, i program in the WE since i can run syntax checker and jass helper every time i finish to write a line :) . And | write a line - copy paste - run check | takes more time that write and check directly( also the checker speed slows down after every line written.) I make a lot of spaces and empty lines between blocks of codes so it's easier for me to navigate through. Also at teh beginning of a new line i use TAB key instead of space because it's difficult to select the very first character in the editor, it sometimes just scrolls down and make me go insane.

    3. I am not familiar with this. I tried to create a library once ( just after i downloaded the mouse snippet you suggested me) but i got lots of errors and problems and got scared. Also the mouse snippet is disabled because in one of my tests my Map Initialization trigger (i still have it in GUI, for now) didnt work, it was like it was disabled but it wasn't. Disabling the mouse snippet solved the issue.

    What if instead of create a library and globals i create udg variables? I already use plenty of those since some part fo the map script are still in GUI. Also for fast debugging i always use GUI and i can't have access to global variables with GUI.




    1. How can i destroy this? I was told the location was leaking but then when i tried to figure how to do so, the only way i found is to create a local location varialbe and destroy it.

    I hope you mean "try to avoid using location where you don't have to" because i can't imagine to get rid of locations completely. Also using X and Y feels more tricky than a location for me but I will try.

    2. I will. Even thouh i developed the habit of using those in GUI, it may takes a while.

    3. That's right.



    I am terrible at math and i tried to understand this but assuming angle is in degrees :


    angle = 0

    0/90 = (0+45)/90
    0 = 0.5

    angle = 45

    45/90 = (45+45)/90
    0.5 = 1

    angle = 90

    90/90 = (90+45)/90
    1 = 0.7


    For me this "angle/90 == (angle + 45)/90" it's the same of this "X == X +1" and it will be always false.
    So i think im too dumb to get it, would you like to explain more precisely?
     
  4. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,905
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Will give a detailed response later but as for the arithmetic the reason it works is that integer division truncates and never rounds. As long as all numbers/variables involved in a computation are integers then it will follow those rules. In int-land:

    1/2 = 1/10 = 7/8 = 17472/222043 = 0

    2/2 = 3/2 = 11/10 = 14/8 = 1

    So you have:
    (45+45)/90 = 90/90 = 1
    45/90 = 0
    1 != 0 so it’s a diagonal
    ——
    (90+45)/90 = 135/90 = 1
    90/90 = 1
    1 = 1 so it’s a cardinal

    This only works if angle is an integer variable, not a real. If angle is a real then the check will fail because as you said x+n != x for any value of x or n that isn't 0.
     
    Last edited: Sep 5, 2019
  5. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,905
    Resources:
    1
    Spells:
    1
    Resources:
    1
    To expand on my comment about the CountUnitsInGroup check: if you don't actually need to know the number of units in the group but just want to see if it has any units, you can check
    FirstOfGroup(g) != null
    .
    Fair enough, I won't comment on it again. Everyone has their own style.
    Yeah that shit drives me insane too. Here is the JASSHelper manual, which explains how to use pretty much everything it can do: JassHelper 0.A.0.0 Check out the libraries and global declaration freedom sections.
    That's the correct way to do it if you must use a location:
    Code (vJASS):
    local location l = Location(x,y)
    call DoSomethingWithLoc(l)
    call RemoveLocation(l)
    set l = null

    I do, however, mean to stop using locations entirely. If you are using GUI often then yeah it's pretty much impossible to eliminate locations, but in JASS nearly everything takes XY coordinates directly. The only function I am aware of that requires you to use a location is GetLocationZ(); everything else has both a location and xy variant of the native. It may be slightly more complex initially but the freedom it affords you is significant, and since coordinates are reals you don't ever have to clean them up! If you want to see how the coordinates work for polar projection (for example), you can always look at the function in Blizzard.j.
    I think you understand this but the globals created in GUI just have a udg_ text prefix; they aren't different in any way. The functional 'difference' is just that GUI can't see/interact with freely declared globals in your JASS code, so yes if you want a GUI trigger to be able to use it you must create it in the variable editor. The easiest way around this is to use custom script lines in your GUI triggers which should be able to see your declared globals, and you can do something like this to access their values by assigning them to other GUI-accessible variables.
    • Custom script: set udg_GUI_Int = SomeGlobalIntVariableInJASS
    • Game - Display to (All players) the text String(GUI_Int)
    • -------- --------
    • Custom script: set udg_GUI_Unit = SomeGlobalUnit
    • Unit - Kill GUI_Unit

    Now, as for how the 'size' value of GUI arrays works: say you give an int array called Test a size N and default value of V. This means that the first N indicies of Test will be assigned the value V upon map startup. Say N = 5 and V = -1, you will have this:
    Test[0] = 0 //GUI starts arrays at index 1 and ignores the 0th index
    Test[1] = -13
    Test[2] = -13
    Test[3] = -13
    Test[4] = -13
    Test[5] = -13
    Test[6] = 0
    Test[7] = 0
    ...
    Test[32767] = 0 //max array index currently

    For most array variables you will be setting the value of the array before you attempt to use it, or you are okay with using the 'base' value of 0, 0.0, false, "", null (depending on variable type). In this instance you can leave N = 1 and it doesn't matter what V is. For handle-type variables, the value of N sometimes matters (and V is usually set based on the variable type and can't be changed). It doesn't matter for units. It does matter for groups and timers. If you attempt to add units to an uninitialized group nothing will happen. If you attempt to start an uninitialized timer nothing will run. In these cases you either need to set the size variable appropriately so that the first N groups are allocated as 'Empty Group', or set them yourself before attempting to add units to them (if they have not yet been initialized).
    I don't see where spreadint is ever incremented. It's only assigned once in Trig_FirespreadJASS_Actions as
    GetPlayerId(GetOwningPlayer(GetEnteringUnit()))
    . How is it getting up to 27? Where is the loop that's supposed to stop at 23?
     
  6. wc3neverdies

    wc3neverdies

    Joined:
    Sep 20, 2015
    Messages:
    281
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Yeah, i am confused too.


    In this video i recorded you can see the string TimeSpreadOff 27 as soon as i used the fire element. I sincerely don't know how this can be possible
     
  7. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,905
    Resources:
    1
    Spells:
    1
    Resources:
    1
    There has to be another trigger that is setting the value of spreadint that you haven't posted. The reason I know this is the case is that "SpreadFireON 0" is visible at 0:14 and then the next time a fire message appears a second later it shows "SpreadFire OFF 27". Something changed the value from 0 (player # of player red, so that was correct) to 27. Can you post the map if you can't find the trigger that is changing it?
     
  8. wc3neverdies

    wc3neverdies

    Joined:
    Sep 20, 2015
    Messages:
    281
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Sure, but my code and triggers are quite messy. I think the issue is in the triggers that spawn a fire unit in the map, PointAttackMissileJASS and TargetAttackMissileJASS
     

    Attached Files:

  9. wc3neverdies

    wc3neverdies

    Joined:
    Sep 20, 2015
    Messages:
    281
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Okay i solved it, i remove the spreadint variable and managed to solve the player index issue.

    Thanks for the help, patience and time