vJASS & Zinc Documentation
For the latest documentation about how it works vJASS and Zinc language layers for Warcraft III, please follow these links:
Jasshelper documentation - Zinc documentation - WC3 Optimizer documentation

RVons SpellQuestions

rvonsonsnadtz · 64898

0 Members and 1 Guest are viewing this topic.

RVons SpellQuestions
on: October 14, 2012, 09:45:56 AM

Well, it haven't been much time since my last post, and syntax-writing have started to take over my GUI-usage more and more. But I got a question I want answered; How do I pick units in a line (for creating a trigger-based spell like shockwave or similar). I have before used a number of rects to simulate the line-effect, but is there a better way, then I am interested in hearing it.

And a little of-topic question; a quite long time ago, I read a topic about vJass and its strengths against normal GUI, and one particular thing I remembered was that someone mentioned another way to fire of triggers rather than the way triggers runs of in the GUI that was more efficient, but I can't recall how nor where I read it. Someone who knows anything about it?



Re: RVons SpellQuestions
Reply #1 on: October 14, 2012, 11:27:45 AM

Well, it haven't been much time since my last post, and syntax-writing have started to take over my GUI-usage more and more. But I got a question I want answered; How do I pick units in a line (for creating a trigger-based spell like shockwave or similar). I have before used a number of rects to simulate the line-effect, but is there a better way, then I am interested in hearing it.
Well, you seem to read my mind :) Right now I'm working in a code to group units in a line. And yes you can avoid rects, in fact rects is not the proper way. You can do this process in code as follows:
  • define the cast point and final point with X,Y coordinates
  • set the middle point (with average formula 0.5*(x1+x2) )
  • group units in range with center in the middle point
  • Measure the angle between the cast point and the unit, if it doesn't differ much from the original angle (you set that parameter) then add it to the destination group
  • Repeat until covers all thew units in the initail range[/i]
If you're a little patient I'm gonna finish this code very quickly (I have the algorithm almost done)

Quote
And a little of-topic question; a quite long time ago, I read a topic about vJass and its strengths against normal GUI, and one particular thing I remembered was that someone mentioned another way to fire of triggers rather than the way triggers runs of in the GUI that was more efficient, but I can't recall how nor where I read it. Someone who knows anything about it?
Definitely!!!

the problem with GUI is that you generate one trigger and every conditional create a new function which trigger ANOTHER function. Check this example:

Trigger: Test 2025402050
   
           
  • Untitled Trigger 001
  •     Events
  •         Unit - A unit Finishes casting an ability
  •     Conditions
  •         (Ability being cast) Equal to Animate Dead
  •     Actions
  •         Unit - Create 1 Footman for Player 1 (Red) at (Position of (Triggering unit)) facing Default building facing degrees
  •         -------- Etc, etc, etc... --------
           

This GUI viewed as jass looks like this:
Code: jass
  1. function Trig_Untitled_Trigger_001_Conditions takes nothing returns boolean
  2.     if ( not ( GetSpellAbilityId() == 'AUan' ) ) then
  3.         return false
  4.     endif
  5.     return true
  6. endfunction
  7.  
  8. function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
  9.     call CreateNUnitsAtLoc( 1, 'hfoo', Player(0), GetUnitLoc(GetTriggerUnit()), bj_UNIT_FACING )
  10.     // Etc, etc, etc...
  11. endfunction
  12.  
  13. //===========================================================================
  14. function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
  15.     set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
  16.     call TriggerRegisterAnyUnitEventBJ( gg_trg_Untitled_Trigger_001, EVENT_PLAYER_UNIT_SPELL_FINISH )
  17.     call TriggerAddCondition( gg_trg_Untitled_Trigger_001, Condition( function Trig_Untitled_Trigger_001_Conditions ) )
  18.     call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
  19. endfunction
As you can see, this code is too long in all the terms possible and that's because GUI puts a lot "just in case" code which in most of the situations is inefficient as hell. In the triggering part is the same: you register the event (Unit finish casting an ability) then you set a WHOLE FUNCTION just to check the spell type and then it runs another function to run the script.

In fact the question is: couldn't we do this in only one function? and the answer is YES: Let's optimize.

Code: jass
  1. function Trig_Untitled_Trigger_001_Conditions takes nothing returns boolean
  2.     local location L // I define a local variable so this code doesn't leak a location
  3.     if GetSpellAbilityId() == 'AUan' then
  4.         set L = GetUnitLoc(GetTriggerUnit())
  5.         call CreateNUnitsAtLoc( 1, 'hfoo', Player(0), L, bj_UNIT_FACING )
  6.         call RemoveLocation(L) // removes the location
  7.     endif
  8.     set L = null //
  9.     return false // set it so nothing will run after this function, we3 don't need it :)
  10. endfunction
  11.  
  12. //===========================================================================
  13. function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
  14.     set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
  15.     call TriggerRegisterAnyUnitEventBJ( gg_trg_Untitled_Trigger_001, EVENT_PLAYER_UNIT_SPELL_FINISH )
  16.     call TriggerAddCondition( gg_trg_Untitled_Trigger_001, Condition( function Trig_Untitled_Trigger_001_Conditions ) )
  17. endfunction

Here I merged the action inside the condition, doing this gives you an advantage: conditions are faster than actions. I added some code to remove the location leak in the original code. Let's optimize more, doing some vJASS:

Code: jass
  1. scope TestTrigger initializer init
  2.  
  3. private function Conditions takes nothing returns boolean
  4.     if GetSpellAbilityId() == 'AUan' then
  5.         call CreateUnit( Player(0), 'hfoo', GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()), bj_UNIT_FACING )
  6.         // Add more things to do...
  7.     endif
  8.     return false // set it so nothing will run after this function, we3 don't need it :)
  9. endfunction
  10.  
  11. //===========================================================================
  12. private function init takes nothing returns nothing
  13.     local trigger t = CreateTrigger(  )
  14.     call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_FINISH )
  15.     call TriggerAddCondition( t, Condition( function Conditions ) )
  16.     set t = null // to free handle counter
  17. endfunction
  18.  
  19. endscope
Now this code is totally efficient, it DOES NOT use locations anymore and it has a nicer presentation and readability. I've set the trigger as a local variable so it doesn't interfere with other code making it totally MUI (multi unit instanceable).

I hope with this live example you can see the advantage of jass and vJASS in triggering terms.


Re: RVons SpellQuestions
Reply #2 on: October 14, 2012, 12:51:01 PM

Ok, nice! If I could use your algorithms then I would be very thankfull. And since the things I read from your code-exemple, I belive I have to rewrite some of the codes I have done. Another question that I really need answered; are a local variable uniq per function and use or just a function? For example

Code: jass
  1. function exemple takes unit attacker, unit attacked returns nothing
  2. local DamageC
  3.   set DamageC = 50 //Just taking a number for example, the real code is a bit more complicated, and seldom the same
  4.   call TriggerSleepAction( 0.50 )
  5.   call UnitDamageTargetBJ(attacker, attacked, DamageC, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL)
  6. endfunction

Would a multiple uses of the function nearly at the same time mess upp the variable, or are the variable uniq per function and use?
« Last Edit: October 14, 2012, 01:21:39 PM by moyack »



Re: RVons SpellQuestions
Reply #3 on: October 14, 2012, 01:18:46 PM

That's right, they're only valid inside the function you define.

Some small fixes:

Code: jass
  1. function exemple takes unit attacker, unit attacked returns nothing
  2. local real DamageC // don't forget to add the type of the variable :)
  3.   set DamageC = 50 //Just taking a number for example, the real code is a bit more complicated, and seldom the same
  4.   call TriggerSleepAction( 0.50 )
  5.   call UnitDamageTargetBJ(attacker, attacked, DamageC, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL)
  6. endfunction

So no matters how many times or how simultaneously you call the function, those values are unique per call, that guarantee that your code won't mess up with other events in the game :)


Re: RVons SpellQuestions
Reply #4 on: October 14, 2012, 02:44:07 PM

Ok, do I need to nullify real-locals?

Btw, is there a way to store a integer in a ability or similar (or even better, store a number of different integers in certain unit-types) so I could instead of using a large number of if/then/else after eachothers, I could just get the numbers directly? (A little like a Hashtable, but backwards or something).
Since I want to have complete control over all damage done, I don't want to use the already made- attacktypes and armortypes to declare certain strengths and weaknesses.

Edit: I ran across a annoying Compile-error.. The following code returns a "unexpected "("?"-error..
Code: jass
  1. call GroupEnumUnitsInRange(unitsinrange, GetUnitX(attackedR), GetUnitY(attackedR), 300, Filter(function IsUnitEnemy(GetFilterUnit(), Player(attackerid)) == true and (GetFilterUnit() == attackedR) == false))
I need to store a unitgroup (a group I call "unitsinrange)
« Last Edit: October 15, 2012, 01:15:29 AM by moyack »



Re: RVons SpellQuestions
Reply #5 on: October 15, 2012, 01:29:34 AM

Ok, do I need to nullify real-locals?
No, real, integers, booleans and code dont' need to be nullified, other variables depending of agent type must be destroyed and nullified if they offer the option.

Quote
Btw, is there a way to store a integer in a ability or similar (or even better, store a number of different integers in certain unit-types) so I could instead of using a large number of if/then/else after eachothers, I could just get the numbers directly? (A little like a Hashtable, but backwards or something).
In fact the id of abilities and buff are integers. In fact a code like 'AUan' is actually an integer expressed in a special way. You can link the ability to other data using hashtables.
Quote
Since I want to have complete control over all damage done, I don't want to use the already made- attacktypes and armortypes to declare certain strengths and weaknesses.
There's some libraries which offers what you want. You can do this in JASS but it's easier in vJASS because you can define a struct or object which manages the new damage type you want. If you need more information of a possible way to do it just ask me, but first try to cover the manage of structs.

Quote
Edit: I ran across a annoying Compile-error.. The following code returns a "unexpected "("?"-error..
Code: jass
  1. call GroupEnumUnitsInRange(unitsinrange, GetUnitX(attackedR), GetUnitY(attackedR), 300, Filter(function IsUnitEnemy(GetFilterUnit(), Player(attackerid)) == true and (GetFilterUnit() == attackedR) == false))
I need to store a unitgroup (a group I call "unitsinrange)
Hmmm, unfortunately you can't define this in that way. You need to define a function and call it here GroupEnum function.

Code: jass
  1. function Cond1 takes nothing returns boolean
  2.     return IsUnitEnemy(GetFilterUnit(), Player(attackerid)) == true and (GetFilterUnit() == attackedR) == false
  3. endfunction
  4.  
  5.  
  6. //...The function should be over this expresion, outside of the function. attackedR MUST be a global variable in order to make it work
  7. function bla....
  8. // code goes here...
  9.     call GroupEnumUnitsInRange(unitsinrange, GetUnitX(attackedR), GetUnitY(attackedR), 300, Filter(function Cond1))
  10. // ther's more code... blablabla...
  11. endfunction


Re: RVons SpellQuestions
Reply #6 on: October 15, 2012, 10:33:03 AM

Just null everything which is not real, boolean, integer, string or code even if it extends handle without the agent type.

vJass is already a jass preprocessor, no need to (ab)use vJass features in order to make some inferior vJass preprocessor coded "by hand".


Re: RVons SpellQuestions
Reply #7 on: October 15, 2012, 02:48:56 PM

Thanx again, but ye, I would like some extra information to do a struct that could manage the damage-types and armor-types for the units (since every player will just have one hero each in my map, I will use a hashtable for them where I could give any % I want). I was thinking if I should use a large if/then/else function to determine the types which returns integer attacktype or armortype depending on the units that is, but by some reason it feels like a inefficient way of doing it (and I would probably need 2 functions, one for armor and one for attack).

Edit: I read about structs and an example-use for it. But in the example it stored value for every single hero-unit, not just the hero-types. It feels like it if there isn't a way to store a struct for unit-types instead of units, this way would be a very memory-demanding-way since it would need a struct for all units on the map.
And on speak off that, a recycle-way of units would perhaps be usefull, since I will both have a part in the game that uses some DotA- or AoS-elements, as well as certain armies for every players that are used in certain events.
« Last Edit: October 16, 2012, 10:37:30 AM by rvonsonsnadtz »



Re: RVons SpellQuestions
Reply #8 on: October 16, 2012, 10:57:10 AM

Code: jass
  1. library UnitIdData initializer init requires Table // hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
  2.  
  3.     globals
  4.         // you could make the tables not private but encapsulation is a pretty good practice
  5.         private Table Damage_table
  6.         private Table Armor_table
  7.     endglobals
  8.  
  9.     private function SetData takes integer unit_id , integer damage, integer armor // will be used only inside this library to make the config part less verbose
  10.         set Damage_table[unit_id] = damage
  11.         set Armor_table[unit_id] = armor
  12.     endfunction
  13.  
  14.     private function Config takes nothing returns nothing
  15.         /*
  16.             here you will define Armor and Damage values according to the unit rawcode
  17.             let's define it for the footman, note that you can see an unit rawcode in the object editor by using the shortcut "Ctrl + D"
  18.         */
  19.  
  20.          call SetData( 'hfoo' , 42 , 666 )
  21.     endfunction
  22.  
  23.     function GetUnitDamageBase takes unit which_unit returns integer
  24.         return Damage_table[ GetUnitTypeId(which_unit) ]
  25.     endfunction
  26.  
  27.     function GetUnitArmorBase takes unit which_unit returns integer
  28.         return Armor_table[ GetUnitTypeId(which_unit) ]
  29.     endfunction
  30.  
  31.     private function init takes nothing returns nothing
  32.         set Damage_table = Table.create()
  33.         set Armor_table = Table.create()
  34.         call Config()
  35.     endfunction
  36.  
  37. endlibrary

Note that there i use integers, you could use reals instead with some code editing, but is it really needed ?

vJass is already a jass preprocessor, no need to (ab)use vJass features in order to make some inferior vJass preprocessor coded "by hand".


Re: RVons SpellQuestions
Reply #9 on: October 17, 2012, 03:27:15 PM

Ok, but perhaps I'll stick with the ability-things for the moment (I use a "Vulnerable to"/"Resistant to"-system instead of the ordinarie fixed armor-type-system, which gives me the ability to hand-tailor the damage-modifiers for every unit I would use, and the abilities serves as a teller for the player of what the unit is vulnerable to)
However, since Heroes uses alot of more values than in the vanilla wc3 (and the damage-values is very missleading) I would like to use a multiboard (or similar) to show this values. However.. It seems impossible to just show a specific multiboard for just one player, while I show another multiboard for another one.. I could use a chattext-triggered function to show all the stats, but I would like to hear if anyone has another idea. (I have calculated that I am using 26custom-values for heroes right now (with a small chance of reduction, I don't think I would be using anything more), so a text-message which shows 26lines of text is maybe not the best way)

Edit: I have encountered a very annoying glitch (which doesn't seem to be very unusual); the TriggerSleepAction is completely bugged.. It seems to do the same as SkipRemainingActions in many cases... Here is the the last part of the code;
Code: jass
  1.     //other code above here
  2.     call TriggerSleepAction( 0.50 )
  3.     set i = 0 //i is just a integer used for loops here
  4.     loop
  5.         exitwhen i > ( 4 + ( udg_herolevelmodifier[casterOId] + casterlvl ) )
  6.         call UnitDamageTarget( caster, targets[i], damageCend[i], FALSE, TRUE, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS )
  7.         set targets[i] = null
  8.         call DisplayTextToForce( GetPlayersAll(), I2S(i))
  9.         set i = i + 1
  10.     endloop
  11.     set targetpoint = null
  12.     call DestroyGroup(ug)
  13.     set ug = null
  14.     set caster = null
  15.     set target = null
  16.     set dummy = null
  17.     set targetO = null
  18.     set casterO = null
  19.     return true
  20.     else
  21.     endif
  22. return false
  23. endfunction
  24.  
The code doesn't generate any error-messages, and works completely fine when TriggerSleepAction is deactivated or removed. But active, nothing after it will do anything at all. This wait is used to get a visual sync between the fireballs created by dummies earlier in the code, and isn't REALLY necessary for gameplay, but still, the bug is annoying. Heard about PolledWaits, but after both read it's very slow and by taking a quick look at the function and thinking the bug should not be fixed since that function also uses TriggerSleepAction, it feels like it really isn't a good idea at all. Now, the remaining option of what I can see, is a timer, but this trigger have a great chance of running multiple times, so I can't use globals for the trigger that would run when the timer runs out. Is it at this point that I should learn about some other kinds of variables (I don't know how privates work..)?
« Last Edit: October 17, 2012, 05:47:30 PM by rvonsonsnadtz »



Re: RVons SpellQuestions
Reply #10 on: October 18, 2012, 04:58:48 AM

Ok, but perhaps I'll stick with the ability-things for the moment (I use a "Vulnerable to"/"Resistant to"-system instead of the ordinarie fixed armor-type-system, which gives me the ability to hand-tailor the damage-modifiers for every unit I would use, and the abilities serves as a teller for the player of what the unit is vulnerable to)
However, since Heroes uses alot of more values than in the vanilla wc3 (and the damage-values is very missleading) I would like to use a multiboard (or similar) to show this values. However.. It seems impossible to just show a specific multiboard for just one player, while I show another multiboard for another one.. I could use a chattext-triggered function to show all the stats, but I would like to hear if anyone has another idea. (I have calculated that I am using 26custom-values for heroes right now (with a small chance of reduction, I don't think I would be using anything more), so a text-message which shows 26lines of text is maybe not the best way)
Yes you can show a multiboard per player, but you must use jass to code that part. Using the awesome GetLocalPlayer() function (Be aware about this one because it can cause desyncs if not used properly)

Quote
Edit: I have encountered a very annoying glitch (which doesn't seem to be very unusual); the TriggerSleepAction is completely bugged.. It seems to do the same as SkipRemainingActions in many cases... Here is the the last part of the code;
Code: jass
  1.     //other code above here
  2.     call TriggerSleepAction( 0.50 )
  3.     set i = 0 //i is just a integer used for loops here
  4.     loop
  5.         exitwhen i > ( 4 + ( udg_herolevelmodifier[casterOId] + casterlvl ) )
  6.         call UnitDamageTarget( caster, targets[i], damageCend[i], FALSE, TRUE, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS )
  7.         set targets[i] = null
  8.         call DisplayTextToForce( GetPlayersAll(), I2S(i))
  9.         set i = i + 1
  10.     endloop
  11.     set targetpoint = null
  12.     call DestroyGroup(ug)
  13.     set ug = null
  14.     set caster = null
  15.     set target = null
  16.     set dummy = null
  17.     set targetO = null
  18.     set casterO = null
  19.     return true
  20.     else
  21.     endif
  22. return false
  23. endfunction
  24.  
The code doesn't generate any error-messages, and works completely fine when TriggerSleepAction is deactivated or removed. But active, nothing after it will do anything at all. This wait is used to get a visual sync between the fireballs created by dummies earlier in the code, and isn't REALLY necessary for gameplay, but still, the bug is annoying. Heard about PolledWaits, but after both read it's very slow and by taking a quick look at the function and thinking the bug should not be fixed since that function also uses TriggerSleepAction, it feels like it really isn't a good idea at all. Now, the remaining option of what I can see, is a timer, but this trigger have a great chance of running multiple times, so I can't use globals for the trigger that would run when the timer runs out. Is it at this point that I should learn about some other kinds of variables (I don't know how privates work..)?
Hmmm, triggersleepaction can only be used in an action function. It can not be used in loops or in conditional functions. And seeing your situation yes, you're reaching the point where you need to explore the usage of timers. This feature used properly will make your map accurate and well developed.



Re: RVons SpellQuestions
Reply #11 on: October 19, 2012, 05:48:11 AM

Ok, thanks! (:
But I really have no clue of how to do a very MUI-trigger using a timer? An array was a idea, but the size would need to be quite big... Cause, I don't know a safe, MUI, good way of using the data from one function to the function that starts after the timer..

EDIT: I fixed it. The only thing worring me is that I am using a loop to detect which array the timer is using, and since the size is on 1050 before reseting to 0, I am worried about the process-part.
« Last Edit: October 19, 2012, 09:29:20 AM by rvonsonsnadtz »



Re: RVons SpellQuestions
Reply #12 on: October 20, 2012, 10:52:35 AM

Moyack,if u haven't already figured it out, here is the angle-calculation of the line-spell;
(((Area/2) / Dist) * (180 / pii)) + ((180/pii) * Atan2(TPY - EPY, TPX - EPX))  >
(180/pii) * Atan2 (TPY- CPY, TPX - CPX) >
(((Area/2) / Dist) * (180 / pii)) - ((180/pii) * Atan2(TPY - EPY, TPX - EPX))

Area = Area of the spell
Dist = the distance from the unit to the caster(calc.: ((Target X - Caster X)^2) + ((Target Y - Caster Y)^2)
TPX/TPY = Target Unit X or Y
CPX/CPY = Casterunits X or Y
EPX/EPY =End X or Y of the line-group. However, this point doesn't necessary point out the range of the grouping, it is just a reference-point for the spells angle.
« Last Edit: October 20, 2012, 02:36:13 PM by rvonsonsnadtz »



Re: RVons SpellQuestions
Reply #13 on: October 20, 2012, 11:57:42 AM

First of all, sorry for my delayed response, real life has been a bitsh with me ;)


Ok, thanks! (:
But I really have no clue of how to do a very MUI-trigger using a timer? An array was a idea, but the size would need to be quite big... Cause, I don't know a safe, MUI, good way of using the data from one function to the function that starts after the timer..
You have to use hashtables or a timer utils or manage one timer that loops through an array. Personally I love the last option.

Quote
EDIT: I fixed it. The only thing worring me is that I am using a loop to detect which array the timer is using, and since the size is on 1050 before reseting to 0, I am worried about the process-part.
You don't have to worry, the idea is to make it in such way that the data can recycle elements. You normally don't need to run a code for 1050 units at the same time, in fact if it happens, you're doing a bad approach to the problem.

Moyack,if u haven't already figured it out, here is the angle-calculation of the line-spell;
((Area / Dist) * (180 / pii)) + ((180/pii) * Atan2(TPY - CPY, TPX - CPX))  >
(180/pii) * Atan2 (TPY- CPY, TPX - CPX) >
((Area / Dist) * (180 / pii)) - ((180/pii) * Atan2(TPY - CPY, TPX - CPX))

Area = Area of the spell
Dist = the distance from the unit to the caster(calc.: ((Target X - Caster X)^2) + ((Target Y - Caster Y)^2)
TPX/TPY = Target Unit X or Y
CPX/CPY = Casterunits X or Y
My worry implies in that there's a few chance to catch a unit because it should be exactly at the same angle, and that situation normally doesn't happen :(

You need to have a kind of "range" so it can cast more units in a more or less lined way.





Re: RVons SpellQuestions
Reply #14 on: October 20, 2012, 02:30:12 PM


My worry implies in that there's a few chance to catch a unit because it should be exactly at the same angle, and that situation normally doesn't happen :(

You need to have a kind of "range" so it can cast more units in a more or less lined way.


It works now, sure, the math wasn't completely right, but I shall change the calculation. Btw, is there a place where I can upload a map to show my succes? (x
Code: [Select]
function LineKillFilter takes nothing returns boolean
local unit u = GetFilterUnit()
local real unitx = GetUnitX(u)
local real unity = GetUnitY(u)
local real startx = GetUnitX(gg_unit_hfoo_0001)
local real starty = GetUnitY(gg_unit_hfoo_0001)
local real endx = GetUnitX(gg_unit_hfoo_0002)
local real endy = GetUnitY(gg_unit_hfoo_0002)
local real a1 = 57.2958 * Atan2(unity - starty, unitx - startx)
local real a2 = 57.2958 * Atan2(endy - starty, endx - startx)
local real distance = SquareRoot(((unitx - startx) * (unitx - startx)) + ((unity - starty) * (unity - starty)))
    if (a1) < (((100/distance) * 57.2958) + (a2)) then //The number 100 is the area of the line divided by 2, and could be changed to any value.
        if (a1) > ((a2) - ((200/distance) * 57.2958) ) then
            call KillUnit(GetFilterUnit()) // 57.2958 is the round off from 180/pii
            set u = null
            return TRUE
        else
        endif
    else
    endif
    return FALSE
endfunction

function Trig_linekill_Actions takes nothing returns nothing
local group g = CreateGroup()
local real x1 = GetUnitX(gg_unit_hfoo_0001)
local real x2 = GetUnitX(gg_unit_hfoo_0002)
local real y1 = GetUnitY(gg_unit_hfoo_0001)
local real y2 = GetUnitY(gg_unit_hfoo_0002)
local real dx = x1 + 500 * Cos(Atan2(y2 - y1, x2 - x1))
local real dy = y1 + 500 * Sin(Atan2(y2 - y1, x2 - x1))
call GroupEnumUnitsInRange(g, dx, dy, 1000.00 , Condition(function LineKillFilter))
endfunction

//===========================================================================
function InitTrig_linekill takes nothing returns nothing
local trigger t = CreateTrigger()
    call TriggerRegisterPlayerChatEvent( t, Player(0), "-kill", false )
    call TriggerAddAction( t, function Trig_linekill_Actions )
    set t = null
endfunction

This code will kill everyone between the 2 footmen on my map (right now including the second footman) in an area of 200 and a range of 1000

EDIT: The prior error is fixed
« Last Edit: October 20, 2012, 02:58:22 PM by rvonsonsnadtz »



Re: RVons SpellQuestions
Reply #15 on: October 20, 2012, 10:23:07 PM

It works now, sure, the math wasn't completely right, but I shall change the calculation. Btw, is there a place where I can upload a map to show my succes? (x
Sure, in "Attachments and other options" you will see an option to attach a file or you can create your own private album of files in the media section.
Quote
Code: [Select]
function LineKillFilter takes nothing returns boolean
local unit u = GetFilterUnit()
local real unitx = GetUnitX(u)
local real unity = GetUnitY(u)
local real startx = GetUnitX(gg_unit_hfoo_0001)
local real starty = GetUnitY(gg_unit_hfoo_0001)
local real endx = GetUnitX(gg_unit_hfoo_0002)
local real endy = GetUnitY(gg_unit_hfoo_0002)
local real a1 = 57.2958 * Atan2(unity - starty, unitx - startx)
local real a2 = 57.2958 * Atan2(endy - starty, endx - startx)
local real distance = SquareRoot(((unitx - startx) * (unitx - startx)) + ((unity - starty) * (unity - starty)))
    if (a1) < (((100/distance) * 57.2958) + (a2)) then //The number 100 is the area of the line divided by 2, and could be changed to any value.
        if (a1) > ((a2) - ((200/distance) * 57.2958) ) then
            call KillUnit(GetFilterUnit()) // 57.2958 is the round off from 180/pii
            set u = null
            return TRUE
        else
        endif
    else
    endif
    return FALSE
endfunction

function Trig_linekill_Actions takes nothing returns nothing
local group g = CreateGroup()
local real x1 = GetUnitX(gg_unit_hfoo_0001)
local real x2 = GetUnitX(gg_unit_hfoo_0002)
local real y1 = GetUnitY(gg_unit_hfoo_0001)
local real y2 = GetUnitY(gg_unit_hfoo_0002)
local real dx = x1 + 500 * Cos(Atan2(y2 - y1, x2 - x1))
local real dy = y1 + 500 * Sin(Atan2(y2 - y1, x2 - x1))
call GroupEnumUnitsInRange(g, dx, dy, 1000.00 , Condition(function LineKillFilter))
endfunction

//===========================================================================
function InitTrig_linekill takes nothing returns nothing
local trigger t = CreateTrigger()
    call TriggerRegisterPlayerChatEvent( t, Player(0), "-kill", false )
    call TriggerAddAction( t, function Trig_linekill_Actions )
    set t = null
endfunction

This code will kill everyone between the 2 footmen on my map (right now including the second footman) in an area of 200 and a range of 1000

EDIT: The prior error is fixed
Some suggestions:

 * use the [code=jass][/code] so it can highlight properly to jass.
 * You can use bj_DEGTORAD and bj_RADTODEG to convert from degrees to radians and back.
 * For those constant values, you can create a constant global variable:

Code: jass
  1. globals
  2.     constant real VALUE = 100.
  3. endglobals
  4.  
  5. ...
  6.  
  7. if (a1) < (((VALUE/distance) * 57.2958) + (a2)) then //The number 100 is the area of the line divided by 2, and could be changed to any value.
* After the endif, set the unit nullification
 * Destroy the group and set the variable to null after it.
« Last Edit: October 21, 2012, 12:59:45 AM by moyack »



Re: RVons SpellQuestions
Reply #16 on: October 22, 2012, 12:51:06 PM

seen the bj_RADTODEG and bj_DEGTORAD? They will return the number that I used, it is just that I have heard that I should let the computer work as little as possible to gain maximum speed, and so instead of using the bj_RADTODEG which uses the formula of 180/pii, I decided to use a calculator and take the number directly. Sure, the speed I gain from that is minimal to say the least, but yet, I wanted to make the math-part of the code as efficient as possible. However, I do belive there is an even better way of making an line-grouping; the theory is to make 2 lines, one on each side of the center-line, and group them inside, instead of checking the degrees between the startpoint and the filterunits. I might come back with the math if someone is interested.



Re: RVons SpellQuestions
Reply #17 on: October 22, 2012, 09:36:30 PM

Hmmm, Please check this code ;)

Code: jass
  1. /******************************************************************
  2. *                     GROUP UNITS IN LINE v1.0                    *
  3. *                           By moyack                             *
  4. *                              2012                               *
  5. *              ===================================                *
  6. *              Exclusive resource from wc3jass.com                *
  7. *              ===================================                *
  8. ******************************************************************/
  9. library GetUnitsInLine requires GeometryLibBase
  10. /* This library catch all the units in a line. This line however,
  11. can be defined in several ways, each one will offer a function for
  12. that purpose:
  13.  * Between 2 points
  14.  * Between 2 widgets
  15. */
  16. globals
  17.     private constant real SPREAD = 30. // this value defines how wide is the line of units selected
  18.     private group G = CreateGroup()
  19. endglobals
  20.  
  21. public function byPoints takes group g, real x1, real y1, real x2, real y2 returns nothing
  22.     local unit u
  23.     local real a
  24.     local real b
  25.     local real x = 0.5 * (x1 + x2)
  26.     local real y = 0.5 * (y1 + y2)
  27.     if g == null then
  28.         return
  29.     endif
  30.     call GroupEnumUnitsInRange(G, x, y, GetDistance(x, y, x1, y1) + SPREAD, null)
  31.     for u in G
  32.         if x2 - x1 == 0. then // manages vertical lines
  33.             set x = x1
  34.             set y = GetUnitY(u)
  35.         else
  36.             set x = GetUnitX(u)
  37.             set a = (y2 - y1) / (x2 - x1)
  38.             set b = -a * x1 + y1
  39.             set y = a * x + b
  40.         endif
  41.         if IsUnitInRangeXY(u, x, y, SPREAD) then
  42.             call GroupAddUnit(g, u)
  43.         endif
  44.     endfor
  45. endfunction
  46.  
  47. public function byWidgets takes group g, widget A, widget B returns nothing
  48.     call byPoints(g, GetWidgetX(A), GetWidgetY(A), GetWidgetX(B), GetWidgetY(B))
  49. endfunction
  50.  
  51. endlibrary


Re: RVons SpellQuestions
Reply #18 on: October 23, 2012, 03:16:12 PM

It looks a whole lot simpler than the one I do ( ^^''' ), and I see the main-thought of creating a middle-line and check the distance between the unit and the line. However (this problem is although easy solved with some math and coding), if I read your code right, your distance for the "grouping" is only based on the two points (unless you use some additional math in the functoin that calls your code). As I wrote, it is easily fixed. But I shall test my second thought and let you see that one too (If I realise that I give the computer a Bible of code this time too, then I will just say I was wrong and not post the headache here).

Edit: Ok, my math seems to be alot more complicated, so i guess your solution was a better way xD

I shall test your code now. If it works, then it really seems to be a bit easier for the computer to process than my "Cos(Atan((blah-blah)/(blah-blah))) * 0.01745" that needed to be calculated for every unit.
« Last Edit: October 23, 2012, 05:27:26 PM by rvonsonsnadtz »



Re: RVons SpellQuestions
Reply #19 on: October 24, 2012, 05:03:20 AM

If you need a test map I can provide one for you ;)



Re: RVons SpellQuestions
Reply #20 on: October 26, 2012, 02:53:45 PM

Ok, it seems to work nicely. But I got one question; what is the "for u in g"-code? Never seen it and didn't know it was possible to do so. O.o
But, if it is, then I belive that is something I might start to use

EDIT: Ok, I tested the "for u in g" and it works, so no need to fill me in on that. However, why is this code completely hidden? I mean, it doesn't highlight like other "similar" things (like loop, if, endif and so on). No matter, your code works and big creds for that, and the way your ability works have let me understand why to create the middle-point between the casters location and the target location. However, I have myself a similar solution too now, and I do belive that the speed and efficiency is quite equal to yours (not certain which one is fastest though) and I know exactly what everything in it does, so I will stick with my version. (Could post the code and a testmap if any1 is curious)
« Last Edit: October 26, 2012, 05:13:14 PM by rvonsonsnadtz »



Re: RVons SpellQuestions
Reply #21 on: October 27, 2012, 06:18:28 AM

Ok, it seems to work nicely. But I got one question; what is the "for u in g"-code? Never seen it and didn't know it was possible to do so. O.o
But, if it is, then I belive that is something I might start to use

EDIT: Ok, I tested the "for u in g" and it works, so no need to fill me in on that. However, why is this code completely hidden? I mean, it doesn't highlight like other "similar" things (like loop, if, endif and so on). No matter, your code works and big creds for that, and the way your ability works have let me understand why to create the middle-point between the casters location and the target location. However, I have myself a similar solution too now, and I do belive that the speed and efficiency is quite equal to yours (not certain which one is fastest though) and I know exactly what everything in it does, so I will stick with my version. (Could post the code and a testmap if any1 is curious)

Ohh yes, I'm interested :)

The keywords
Code: [Select]
for u in gis a new feature made in cohadar's jasshelper, it simplifies this:
Code: jass
  1. loop
  2.     set u = FirstOfGroup(g)
  3.     exitwhen u == null
  4.     // blablabla...
  5. endloop
The lines resalted are the ones simplified.


Re: RVons SpellQuestions
Reply #22 on: October 27, 2012, 08:51:18 AM

Ok, I will post it, but I will probably use it as a function, that takes some values (like area, distance, start and end x and y, and eventually a spread-value, which will give the possibility to use the function to create a cone-shaped version) so that is my reason for not using constants.
I do personally belive that our methods are very similar, the 2 biggest differences is what I can see that I am using a formula to detect if the unit is close to the line, while you use a native function, and secondly, your "mid"-point is between the casterspoint and the target point, while mine is between the castpoint and the "end"-point (determined by the distance). Otherwise, it seems to be very similiar.

So far:
Code: jass
  1. library LineChecks
  2. globals
  3.     private group g = CreateGroup()
  4.     private group gr = CreateGroup()
  5. endglobals
  6.  
  7. function GroupLine takes real startx, real starty, real endx, real endy, real distance, real area returns group
  8. local real dx
  9. local real dy
  10. local unit u
  11. local real unitx
  12. local real unity
  13. local real a
  14. local real abase = (endy - starty) / (endx - startx)
  15. local rect vr
  16. call GroupClear(g)
  17. call GroupClear(gr)
  18. if (startx - endx) == 0 then
  19.     set vr = Rect(startx, starty - (area / 2), endx + distance, endy + (area / 2))
  20.     call GroupEnumUnitsInRect(gr, vr, null)
  21. else
  22.     set a = (area/2) * Sin(Atan(abase))
  23.     set dy = starty + (distance/2) * Sin(Atan(abase))
  24.     set dx = startx + (distance/2) * Cos(Atan(abase))
  25.     call GroupEnumUnitsInRange(g, dx, dy, distance , null)
  26.     for u in g
  27.         set unitx = GetUnitX(u)
  28.         set unity = GetUnitY(u)
  29.         if (abase) > 0 then
  30.             if ((unity-starty) < ((unitx-startx) * abase + a)) and ((unity-starty) > ((unitx-startx) * abase - a)) then
  31.                 call GroupAddUnit(gr, u)
  32.             endif
  33.             else
  34.             if ((unity-starty) > ((unitx-startx) * abase + a)) and ((unity-starty) < ((unitx-startx) * abase - a)) then
  35.                 call GroupAddUnit(gr, u)
  36.             endif
  37.         endif
  38.     endfor
  39. endif
  40. return gr
  41. endfunction
  42.  
  43. endlibrary
  44.  

With the cone-function: (The "spread"-value is a real value determined in degrees.)
Code: jass
  1. library LineChecks
  2. globals
  3.     private group g = CreateGroup()
  4.     private group gr = CreateGroup()
  5. endglobals
  6.  
  7. function GroupLine takes real startx, real starty, real endx, real endy, real distance, real area, real spread returns group
  8. local real dx
  9. local real dy
  10. local unit u
  11. local real unitx
  12. local real unity
  13. local real a
  14. local real abase = (endy - starty) / (endx - startx)
  15. local rect vr
  16. set spread = spread / 2
  17. call GroupClear(g)
  18. call GroupClear(gr)
  19. if (startx - endx) == 0 then
  20.     set vr = Rect(startx, starty - (area / 2), endx + distance, endy + (area / 2))
  21.     call GroupEnumUnitsInRect(gr, vr, null)
  22. else
  23.     set a = (area/2) * Sin(Atan(abase))
  24.     set dy = starty + (distance/2) * Sin(Atan(abase))
  25.     set dx = startx + (distance/2) * Cos(Atan(abase))
  26.     call GroupEnumUnitsInRange(g, dx, dy, distance , null)
  27.     for u in g
  28.         set unitx = GetUnitX(u)
  29.         set unity = GetUnitY(u)
  30.         call BJDebugMsg("UnitX = "+R2S(unitx)+"  UnitY = "+R2S(unity)+"  Spreadr = "+R2S(spreadr))
  31.         if (abase) > 0 then
  32.             if ((unity-starty) < ((unitx-startx) * Tan(Atan(abase) + (spread * 0.01745)) + a)) and ((unity-starty) > ((unitx-startx) * Tan(Atan(abase) - (spread * 0.01745)) - a)) then
  33.                 call GroupAddUnit(gr, u)
  34.             endif
  35.         else
  36.             if ((unity-starty) > ((unitx-startx) * Tan(Atan(abase) + (spread * 0.01745)) + a)) and ((unity-starty) < ((unitx-startx) * Tan(Atan(abase) + (spread * 0.01745)) + a)) then
  37.                 call GroupAddUnit(gr, u)
  38.             endif
  39.         endif
  40.     endfor
  41. endif
  42. return gr
  43. endfunction
  44.  
  45. endlibrary
  46.  

Edit: I uploaded another version right now because the former used double the degree-value that you wrote. However, I found one interesting glitch; if you gave the spread a higher value than 90, it would take things behind in a 180 - your degree. I shall look into this, but it certainly was interesting.
« Last Edit: October 28, 2012, 06:52:11 AM by rvonsonsnadtz »



Re: RVons SpellQuestions
Reply #23 on: October 28, 2012, 07:34:53 AM

Hmmm... you're leaking a rect. you create a rect, you use it but you don't destroy it and null it.

Code: jass
  1. library LineChecks
  2. globals
  3.     private group g = CreateGroup()
  4.     private group gr = CreateGroup()
  5. endglobals
  6.  
  7. function GroupLine takes real startx, real starty, real endx, real endy, real distance, real area, real spread returns group
  8. local real dx
  9. local real dy
  10. local unit u
  11. local real unitx
  12. local real unity
  13. local real a
  14. local real abase = (endy - starty) / (endx - startx)
  15. local rect vr
  16. local real spreadr
  17. call GroupClear(g)
  18. call GroupClear(gr)
  19. if (startx - endx) == 0 then
  20.     set vr = Rect(startx, starty - (area / 2), endx + distance, endy + (area / 2))
  21.     call GroupEnumUnitsInRect(gr, vr, null)
  22.     call RemoveRect(vr) // yo don't need anymore the rect, you can remove it ;)
  23.     set vr = null // and nullify it to free this space for another handle...
  24. else
  25.     set a = (area/2) * Sin(Atan(abase))
  26.     set dy = starty + (distance/2) * Sin(Atan(abase))
  27.     set dx = startx + (distance/2) * Cos(Atan(abase))
  28.     call GroupEnumUnitsInRange(g, dx, dy, distance , null)
  29.     for u in g
  30.         set unitx = GetUnitX(u)
  31.         set unity = GetUnitY(u)
  32.         set spreadr = spread * SquareRoot((unitx-startx)*(unitx-startx) + (unity-starty)*(unity-starty)) / distance
  33.         call BJDebugMsg("UnitX = "+R2S(unitx)+"  UnitY = "+R2S(unity)+"  Spreadr = "+R2S(spreadr))
  34.         if (abase) > 0 then
  35.             if ((unity-starty) < ((unitx-startx) * Tan(Atan(abase) + (spread * 0.01745)) + a)) and ((unity-starty) > ((unitx-startx) * Tan(Atan(abase) - (spread * 0.01745)) - a)) then
  36.                 call GroupAddUnit(gr, u)
  37.             endif
  38.         else
  39.             if ((unity-starty) > ((unitx-startx) * Tan(Atan(abase) + (spread * 0.01745)) + a)) and ((unity-starty) < ((unitx-startx) * Tan(Atan(abase) + (spread * 0.01745)) + a)) then
  40.                 call GroupAddUnit(gr, u)
  41.             endif
  42.         endif
  43.     endfor
  44. endif
  45. return gr
  46. endfunction
  47.  
  48. endlibrary

BTW, I'm glad you're evolving fast with coding in jass, you're using properly the structures. You're reaching the point where you'll manage the jass handles & objects properlyand then you'll need to move into the part of using handles as minimum as possible. For example, you use 2 groups and 1 rect, I use one group. As less handles you use, the better.

But meanwhile, enjoy coding ;)
« Last Edit: October 28, 2012, 07:51:34 AM by moyack »



Re: RVons SpellQuestions
Reply #24 on: October 28, 2012, 11:24:23 AM

So, a relcalculation for (unitx-startx)=0 is better than using a rect?
I could easily remove the second unit-group by placing a "not" infront of the "if"-functions and using the RemoveUnitGroup (or what the function is called, the function that removes a unit from a group rather than adding it). Would that be better?

The rect-leak was a little mistake done by adding that part a little to quickly after realising that a division with y/0 won't work and I wanted to fix it fast, sry 'bout that ^^'.

But thanks for the complements (:



 

Chaos Realm - The world of Game modders and wc3 addicts     WC3JASS.com - The JASS Vault   Jetcraft - A Starcraft II mod