| 06-14-2008, 04:51 PM | #1 |
Okay, I've finally decided to really learn how to use vJass, so I started easy: Structs, scopes, libraries, and free globals. Structs, libraries, and free globals work fine. No problems there. (I do have a question regarding structs, but that's later) But for some reason that I still cannot figure out, and no matter how many times I've tried, (and I've tried lots of ways, too), I cannot get scopes to work. Period. Whenever I use them, the trigger never ever fires, whether the functions are public, private, or neither. But once I remove the scope from the trigger, (and everything associated), the trigger magically fires and works. My code is as follows: JASS:scope EMPPulse //Configuration Globals globals private integer SpellID = 'A000' //ID of the Spell private integer BuffID = 'B000' //ID of the Spell's Buff (Required!!) private integer DummyID = 'e000' //ID of the dummy caster you use private integer DummySpellID = 'A001' //ID of the dummy stun spell private integer StunID = 'B001' //ID of the dummy stun spell's buff (not required, and only used for indentification if you want) private integer Strikes = 5 //Number of targets private real Damage = 0. //Damage dealt to each target private real Radius = 700. //Radius you're checking for units around the initial target private real Interval = .85 //Time delay between each bolt private string Graphic = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //Path of the effect used on targets endglobals //End Configuration Globals //If you have any more filter booleans to add, feel free to do so. private function filter takes nothing returns boolean local unit u = GetFilterUnit() local boolean b1 = GetWidgetLife(u) > .405 local boolean b2 = IsUnitType(u, UNIT_TYPE_MECHANICAL) == true local boolean b3 = IsUnitType(u, UNIT_TYPE_STRUCTURE) == false local boolean b4 = GetUnitAbilityLevel(u, 'avul') < 1 local boolean b5 = IsUnitEnemy(u, GetOwningPlayer(GetTriggerUnit())) == true set u = null return b1 and b2 and b3 and b4 and b5 endfunction //Don't touch anything beyond this point! globals private timer Timer = CreateTimer() private group Targets = CreateGroup() private unit array BoltTargets endglobals private struct EMP_Data static unit caster = null static unit target = null static unit previous = null static integer hit = 0 endstruct private function Callback takes nothing returns nothing local unit u = EMP_Data.caster local unit u2 = EMP_Data.target local unit prev = EMP_Data.previous local unit u3 local integer hit = EMP_Data.hit if hit != Strikes and u2 != null then call UnitDamageTarget(u, u2, Damage, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null) if prev == null then set u3 = CreateDummyUnit(GetOwningPlayer(u), DummyID, GetUnitX(u), GetUnitY(u), DummySpellID, hit + 1, true) call IssueTargetOrder(u3, "thunderbolt", u2) else set u3 = CreateDummyUnit(GetOwningPlayer(u), DummyID, GetUnitX(prev), GetUnitY(prev), DummySpellID, hit + 1, true) call IssueTargetOrder(u3, "thunderbolt", u2) call DestroyEffect(AddSpecialEffectTarget(Graphic, prev, "origin")) endif set EMP_Data.hit = hit + 1 set EMP_Data.target = BoltTargets[hit] set EMP_Data.previous = u2 else call PauseTimer(GetExpiredTimer()) call DestroyTimer(GetExpiredTimer()) endif set u = null set u2 = null set u3 = null set prev = null endfunction private function Conditions takes nothing returns boolean return ( GetSpellAbilityId() == SpellID ) endfunction private function Actions takes nothing returns nothing local unit u = GetSpellTargetUnit() local EMP_Data EMP = EMP_Data.create() local unit f local integer i set EMP.caster = GetTriggerUnit() set EMP.target = u loop exitwhen GetUnitAbilityLevel(u, BuffID) > 0 call PolledWait(.2) endloop call GroupEnumUnitsInRange(Targets, GetUnitX(u), GetUnitY(u), Radius, Condition(function filter)) loop set f = FirstOfGroup(Targets) exitwhen f == null set BoltTargets[i] = f call GroupRemoveUnit(Targets, f) set i = i + 1 endloop call TimerStart(Timer, Interval, true, function Callback) call PolledWait(4.) call EMP.destroy() set u = null set f = null endfunction //=========================================================================== public function InitTrig_EMPPulse takes nothing returns nothing local trigger t = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(t, Condition( function Conditions ) ) call TriggerAddAction(t, function Actions ) set t = null endfunction endscope Also, my question about structs: I can't use struct members at all under any circumstance unless I put "static" before it. But in every other code I've seen that uses structs the same way as me, (I think), there's no "static" before it. Am I some weird, rare exception? PS: If you spot ways to better the code, be it normal jass or vJass, let me know. And I wouldn't be surprised if the code just sucks =/... or is done entirely wrong. |
| 06-14-2008, 05:09 PM | #2 |
You can use JASS:scope myScope initializer myInit private function myInit takes nothing returns nothing // The stuff you need here endfunction endscope JASS:scope anotherScope public function InitTrig takes nothing returns nothing // Your stuff here. endfunction endscope |
| 06-14-2008, 05:22 PM | #3 |
Theone problem is that you are using wrong the structs... you must remove the static word on the struct variables. JASS:private struct EMP_Data unit caster unit target unit previous integer hit = 0 endstruct |
| 06-14-2008, 06:24 PM | #4 |
I should add a syntax error when 'InitTrig_' is a prefix of a public scope function, it is becoming a common mistake. |
| 06-14-2008, 06:38 PM | #5 | ||
Quote:
Quote:
@Vex: So, what I should do is remove the "InitTrig_" and that will help something? @Gwypaas: What would that do, exactly? |
| 06-14-2008, 07:14 PM | #6 |
Bump/Update: Okay, I removed the InitTrig_ and gave my scope an initializer (do all scopes require one?). The spell now works.... once. I still can't make my structs accept non-static members, and that might have something to do with it. Here's the new code. JASS:scope EMPPulse initializer EMPPulse //Configuration Globals globals private integer SpellID = 'A000' //ID of the Spell private integer BuffID = 'B000' //ID of the Spell's Buff (Required!!) private integer DummyID = 'e000' //ID of the dummy caster you use private integer DummySpellID = 'A001' //ID of the dummy stun spell private integer StunID = 'B001' //ID of the dummy stun spell's buff (not required, and only used for indentification if you want) private integer Strikes = 5 //Number of targets private real Damage = 0. //Damage dealt to each target private real Radius = 700. //Radius you're checking for units around the initial target private real Interval = .85 //Time delay between each bolt private string Graphic = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //Path of the effect used on targets endglobals //End Configuration Globals //If you have any more filter booleans to add, feel free to do so. private function filter takes nothing returns boolean local unit u = GetFilterUnit() local boolean b1 = GetWidgetLife(u) > .405 local boolean b2 = IsUnitType(u, UNIT_TYPE_MECHANICAL) == true local boolean b3 = IsUnitType(u, UNIT_TYPE_STRUCTURE) == false local boolean b4 = GetUnitAbilityLevel(u, 'avul') < 1 local boolean b5 = IsUnitEnemy(u, GetOwningPlayer(GetTriggerUnit())) == true set u = null return b1 and b2 and b3 and b4 and b5 endfunction //Don't touch anything beyond this point! globals private timer Timer = CreateTimer() private group Targets = CreateGroup() private unit array BoltTargets endglobals private struct EMP_Data unit caster = null unit target = null unit previous = null integer hit = 0 endstruct private function Callback takes nothing returns nothing local unit u = EMP_Data.caster local unit u2 = EMP_Data.target local unit prev = EMP_Data.previous local unit u3 local integer hit = EMP_Data.hit if hit != Strikes and u2 != null then call UnitDamageTarget(u, u2, Damage, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null) if prev == null then set u3 = CreateDummyUnit(GetOwningPlayer(u), DummyID, GetUnitX(u), GetUnitY(u), DummySpellID, hit + 1, true) call IssueTargetOrder(u3, "thunderbolt", u2) else set u3 = CreateDummyUnit(GetOwningPlayer(u), DummyID, GetUnitX(prev), GetUnitY(prev), DummySpellID, hit + 1, true) call IssueTargetOrder(u3, "thunderbolt", u2) call DestroyEffect(AddSpecialEffectTarget(Graphic, prev, "origin")) endif set EMP_Data.hit = hit + 1 set EMP_Data.target = BoltTargets[hit] set EMP_Data.previous = u2 else call PauseTimer(GetExpiredTimer()) endif set u = null set u2 = null set u3 = null set prev = null endfunction private function Conditions takes nothing returns boolean return ( GetSpellAbilityId() == SpellID ) endfunction private function Actions takes nothing returns nothing local unit u = GetSpellTargetUnit() local EMP_Data EMP = EMP_Data.create() local unit f local integer i = 0 set EMP.caster = GetTriggerUnit() set EMP.target = u loop exitwhen GetUnitAbilityLevel(u, BuffID) > 0 call PolledWait(.2) endloop call GroupEnumUnitsInRange(Targets, GetUnitX(u), GetUnitY(u), Radius, Condition(function filter)) loop set f = FirstOfGroup(Targets) exitwhen f == null set BoltTargets[i] = f call GroupRemoveUnit(Targets, f) set i = i + 1 endloop call TimerStart(Timer, Interval, true, function Callback) call PolledWait(4.) call EMP.destroy() set u = null set f = null endfunction //=========================================================================== public function EMPPulse takes nothing returns nothing local trigger t = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(t, Condition( function Conditions ) ) call TriggerAddAction(t, function Actions ) set t = null endfunction endscope |
| 06-14-2008, 07:25 PM | #7 |
JASS:public function EMPPulse takes nothing returns nothing JASS:public function InitTrig takes nothing returns nothing JASS:function InitTrig_EMPPulse takes nothing returns nothing The reason it's not working is because newgen named the function "InitTrig_EMPPulse" to "InitTrig_EMPPulse_EMPPulse" Also create a global struct so you could access it again on the callback function, don't destroy it immediately after just storing to it: JASS:globals private EMP_Data Emp=0 endglobals Dude you also need to attach the EMP_Data to the timer so you could access it again in the callback function, "Structs" return integers. ---> First, allocate an index/instance for the struct data using .create() method. structs are arrays (globals) having the same index e.g Array1[1], Array2[1]. Store data. Don't destroy the struct just yet, you're still using it. ---> Then attach it to something that remains constant when the data is needed to be accessed, I'm assuming that you need to attach the timer, I'm not sure though. ---> On the callback function, process data, then destroy the struct so the index/instance will be recycled. Note: I may be wrong lol! You don't need to null t because t is never destroyed, handle id's only leak when a local variable points to a handle after it gets destroyed. JASS:public function EMPPulse takes nothing returns nothing local trigger t = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(t, Condition( function Conditions ) ) call TriggerAddAction(t, function Actions ) set t = null//<---- !! endfunction |
| 06-14-2008, 07:41 PM | #8 |
Okay, I'll make that change. And I don't destroy the struct until well after the spell would be completed, but I'll make a global anyways for the sake of doing so. Nulling "t" is a habit I've formed. And what you said doesn't sound right... JASS:function lol takes nothing returns nothing local unit u = GetTriggerUnit() call KillUnit(u) endfunction And I still can't get the damn spell to work more than once. After the first cast, nothing happens! EDIT: I got the struct to work w/o the "static" prefix. No idea how. EDIT/Off topic: It just occurred to me that "EMP Pulse" breaks down into "Electromagnetic Pulse Pulse". Heh. |
| 06-14-2008, 07:51 PM | #9 |
It's actually InitTrig_EMPPulse_EMPPulse. EDIT: I stand corrected. |
| 06-14-2008, 07:58 PM | #10 |
Yes, after the spell would end. (note the polledwait of 4 seconds above it) So, if I make a global struct, why would I need to attach it to a timer? Isn't a global... globally accessable? The way I have it now, it's just a global struct. I add things to it, and it works. One time. And no, I never destroy it anywhere. (New code doesn't kill it) (If you can't tell, I'm pathetically new to this) I've gotten structs to work before on a similar process (struct used to access data for a timer callback) and I had no trouble, nor did I need to make a global struct and/or attach it to the timer. |
| 06-14-2008, 08:03 PM | #11 |
Dude you don't need the polled wait, just destroy the struct in the callback. You're using 2 timers instead of 1 per instance, which will waste processing power lol. Hey I'll review you're spell thouroughly I was mistaken in my statement that you destroyed it immediately. I'm sorry dude hope you understand, I am lazy lol! EDIT: The difference in nulling a unit handle to a trigger handle you just used as an example is that, when the unit dies the variable points to nothing, hence a handle id leaks, likewise, in an InitTrig function, the trigger isn't destroyed at all, so if the variable points to id 0x10000F then after 30 minutes, it still points there, the handle is intact, no leaks. I also null everything at the end of each function to ensure that no handle id's would leak, but I think you wanted to know that info so I shared it to you. |
| 06-14-2008, 08:08 PM | #12 |
It's okay, people make mistakes. I went back to the aforementioned spell that worked with structs and re-designed my spell to match the way it worked. ... The spell works fine. ONCE. I still cannot figure out why in blazing hell it's not working more than once. Here is the latest code: Note: The other spell didn't use scopes, if that means anything at all. UPDATE: The spell does fire a second time. I can tell because the targeted unit gets hit by the dummy spell. However, the rest of the spell does nothing, and after that nothing happens at all when casted. Yes, I'm using "static", because the damn syntax checker attacks me if I don't. JASS:scope EMPPulse //Configuration Globals globals private integer SpellID = 'A000' //ID of the Spell private integer BuffID = 'B000' //ID of the Spell's Buff (Required!!) private integer DummyID = 'e000' //ID of the dummy caster you use private integer DummySpellID = 'A001' //ID of the dummy stun spell private integer StunID = 'B001' //ID of the dummy stun spell's buff (not required, and only used for indentification if you want) private integer Strikes = 5 //Number of targets private real Damage = 0. //Damage dealt to each target private real Radius = 700. //Radius you're checking for units around the initial target private real Interval = .85 //Time delay between each bolt private string Graphic = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //Path of the effect used on targets endglobals //End Configuration Globals //If you have any more filter booleans to add, feel free to do so. private function filter takes nothing returns boolean local unit u = GetFilterUnit() local boolean b1 = GetWidgetLife(u) > .405 local boolean b2 = IsUnitType(u, UNIT_TYPE_MECHANICAL) == true local boolean b3 = IsUnitType(u, UNIT_TYPE_STRUCTURE) == false local boolean b4 = GetUnitAbilityLevel(u, 'avul') < 1 local boolean b5 = IsUnitEnemy(u, GetOwningPlayer(GetTriggerUnit())) == true set u = null return b1 and b2 and b3 and b4 and b5 endfunction //Don't touch anything beyond this point! private struct EMP_Data static unit caster = null static unit target = null static unit previous = null static integer hit = 0 endstruct globals private timer Timer = CreateTimer() private group Targets = CreateGroup() private unit array BoltTargets // private EMP_Data EMP = 0 endglobals private function Callback takes nothing returns nothing local unit u = EMP_Data.caster local unit u2 = EMP_Data.target local unit prev = EMP_Data.previous local unit u3 local integer hit = EMP_Data.hit if hit != Strikes and u2 != null then call UnitDamageTarget(u, u2, Damage, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null) if prev == null then set u3 = CreateDummyUnit(GetOwningPlayer(u), DummyID, GetUnitX(u), GetUnitY(u), DummySpellID, hit + 1, true) call IssueTargetOrder(u3, "thunderbolt", u2) else set u3 = CreateDummyUnit(GetOwningPlayer(u), DummyID, GetUnitX(prev), GetUnitY(prev), DummySpellID, hit + 1, true) call IssueTargetOrder(u3, "thunderbolt", u2) call DestroyEffect(AddSpecialEffectTarget(Graphic, prev, "origin")) endif set EMP_Data.hit = hit + 1 set EMP_Data.target = BoltTargets[hit] set EMP_Data.previous = u2 else call PauseTimer(Timer) call DestroyTimer(Timer) set Timer = CreateTimer() endif set u = null set u2 = null set u3 = null set prev = null endfunction private function Conditions takes nothing returns boolean return ( GetSpellAbilityId() == SpellID ) endfunction private function Actions takes nothing returns nothing local unit u = GetSpellTargetUnit() local unit f local EMP_Data EMP = EMP_Data.create() local integer i = 0 set EMP.caster = GetTriggerUnit() set EMP.target = u loop exitwhen GetUnitAbilityLevel(u, BuffID) > 0 call PolledWait(.2) endloop call GroupEnumUnitsInRange(Targets, GetUnitX(u), GetUnitY(u), Radius, Condition(function filter)) call GroupRemoveUnit(Targets, u) loop set f = FirstOfGroup(Targets) exitwhen f == null set BoltTargets[i] = f call GroupRemoveUnit(Targets, f) set i = i + 1 endloop call TimerStart(Timer, Interval, true, function Callback) call PolledWait(6.) call EMP.destroy() set u = null set f = null endfunction //=========================================================================== public function InitTrig takes nothing returns nothing local trigger t = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(t, Condition( function Conditions ) ) call TriggerAddAction(t, function Actions ) set t = null endfunction endscope |
| 06-14-2008, 08:14 PM | #13 |
May I edit your code dude? |
| 06-14-2008, 08:15 PM | #14 |
Yeah sure, just point out what you change so I know. (Helps me learn) |
| 06-14-2008, 08:22 PM | #15 |
Ok dude thanks. I'll explain more about structs: JASS:private struct EMP_Data static unit caster = null static unit target = null static unit previous = null static integer hit = 0 endstruct JASS:globals unit array caster unit array target unit array previous integer array hit endglobals now when you call the method .create() an index is generated for the parallel arrays. allocators "may" look like this(I hope you know what textmacros are if not i'll post a non-text macro version just say it): JASS:library IndexGenerator //! textmacro IndexGenerator takes ArrayName globals //##IndexGenerator for $ArrayName$ integer $ArrayName$_StackN=0 integer $ArrayName$_StackMax=0 integer array $ArrayName$_IndexStack //##End of IndexGenerator for $ArrayName$ endglobals function Get$ArrayName$Index takes nothing returns integer //<----- .allocate()/.create() method if $ArrayName$_StackN==0 then set $ArrayName$_StackMax=$ArrayName$_StackMax+1 debug call BJDebugMsg("Increasing Index... new index is: "+I2S($ArrayName$_StackMax)) return $ArrayName$_StackMax endif set $ArrayName$_StackN=$ArrayName$_StackN-1 debug call BJDebugMsg("Re-using Index: "+I2S($ArrayName$_IndexStack[$ArrayName$_StackN])) return $ArrayName$_IndexStack[$ArrayName$_StackN] endfunction function Recycle$ArrayName$Index takes integer n returns nothing //<----- Index Recycler/ .ondestroy() method set $ArrayName$_IndexStack[$ArrayName$_StackN]=n debug call BJDebugMsg("Recycling Index: "+I2S(n)) set $ArrayName$_StackN=$ArrayName$_StackN+1 endfunction //! endtextmacro endlibrary EDIT: NON-Textmacro version: JASS:library IndexGenerator globals integer [structname]_StackN=0 integer [structname]_StackMax=0 integer array [structname]_IndexStack endglobals function Get[structname]Index takes nothing returns integer if [structname]_StackN==0 then set [structname]_StackMax=[structname]_StackMax+1 debug call BJDebugMsg("Increasing Index... new index is: "+I2S([structname]_StackMax)) return [structname]_StackMax endif set [structname]_StackN=[structname]_StackN-1 debug call BJDebugMsg("Re-using Index: "+I2S([structname]_IndexStack[[structname]_StackN])) return [structname]_IndexStack[[structname]_StackN] endfunction function Recycle[structname]Index takes integer n returns nothing set [structname]_IndexStack[[structname]_StackN]=n debug call BJDebugMsg("Recycling Index: "+I2S(n)) set [structname]_StackN=[structname]_StackN+1 endfunction endlibrary Umm Darkwulfv can you describe what the spell does? |
