Interesting Ability Trouble

phyrex1an

Staff Member and irregular helper
Reaction score
447
JASS:

// ...
function Prism_End takes nothing returns boolean
    local trigger trig = GetTriggeringTrigger()
    local Prism prism = GetData(trig)
    call prism.stopChannelPrism()
    call DestroyTrigger(trig)
    set trig = null
    return false
endfunction

// The attach method here is currently global.
// In my "maps" I use the same struct id for all structs that a unit has attached
// so I can use the unit custom value without any problems.
// Note: I don't really know if this code works, it is supposed to make sure that
// a unit have one and never more Prism.
// Note2: The Prism are NEVER destroyed in this code, this has to be fixed. I suggest that you destroy them when a unit dies.
function Unit2Prism takes unit u returns Prism
    local Prism p = Prism( GetData(u) )
    if p == 0 then
         set p = Prism.new(u)
         call SetData(u, p)
    endif
   return p    
endfunction

function Trig_Prism_Actions takes nothing returns nothing
    local unit cast = GetTriggerUnit()
    local unit targ = GetSpellTargetUnit() 
    local trigger trig = CreateTrigger()
    local Prism prism = Unit2Prism(cast)
    call prism.castPrismOn(Unit2Prism(targ))
    call TriggerRegisterUnitEvent(trig,cast,EVENT_UNIT_SPELL_ENDCAST)
    call TriggerAddCondition(trig,Condition(function Prism_End))
    call SetData(trig,prism)        
    set cast = null
    set targ = null
    set trig = null
endfunction
// ...


JASS:

struct Prism
     // ...
     public method addBonus takes integer bonus returns nothing
           set this.selfBonus = this.selfBonus + bonus
           if this.selfBonus==1 then
              call UnitAddAbility(this.prismUnit,get_abil(this.prismUnit))
          else
              call SetUnitAbilityLevel(this.prismUnit,get_abil(this.prismUnit),this.selfBonus)
          endif
           if this.target != 0 then
               call this.target.addBonus(bonus)
          endif    
     endmethod

     public method removeBonus takes integer bonus returns nothing
           set this.selfBonus = this.selfBonus - bonus
           if this.selfBonus==0 then
              call UnitRemoveAbility(this.prismUnit,get_abil(this.prismUnit))
          else
              call SetUnitAbilityLevel(this.prismUnit,get_abil(this.prismUnit),this.selfBonus)
          endif
           if this.target != 0 then
               call this.target.removeBonus(bonus)
           endif
     endmethod
     // ...
endstruct

It can work... Maybe...

>we need it updated, and I still dont get how to do it, do transfer bonuses around as links grow, to the last tower in a series, and to transfer the bonuses back to the last in a series if a part of the current series is broken

Is it important that just the last unit in the chain has the bonus? For a damage bonus that wouldn't matter but for a aura it is important. If it does matter then Prism.addBonus and Prism.removeBonus need to be somewhat changed.
 

emjlr3

Change can be a good thing
Reaction score
395
ill we read over, but let me just reinterate what exactly is going on

towers can channel a "Prism" ability, on allied towers of the same type

when a tower channels onto another, it adds a bonus damage ability to it, with each increasing tower channeled onto that same tower, this dummy ability increases in level

say 3 towers are channeled onto this 1, it would have level 3 of the dummy bonus ability

now say that tower, with the level 3 bonues dummy ability, channels onto another tower, its bonues is lost(no point to keeping it snce its channelig), and the next tower, the one it just channeled onto gets level 4 of the dummy ability

now say one of those first 3 towers that channeled, stops channeling, the last tower in the series will drop to level 3 for the dummy ability, since there are now only two towers channeled to the second tower, which is channeled to it

hopefully this picture will hlep to illustrate it better then my words can

as you can see, it would also need to detect when a tower is try to initiated a connection to a tower it already belongs to a series with, and not allow it to do so

ty
 

phyrex1an

Staff Member and irregular helper
Reaction score
447
as you can see, it would also need to detect when a tower is try to initiated a connection to a tower it already belongs to a series with, and not allow it to do so
:(

Code addition:

JASS:

//...
     public method isPrismInChain takes Prism whatPrism returns boolean
        if this == whatPrism then
            return true
        elseif this.target == 0 then
            return false
        endif
        return this.target.isPrismInChain(whatPrism)
     endmethod

     // There was one error in this method :/
     public method addBonus takes integer bonus returns nothing
           set this.selfBonus = this.selfBonus + bonus
           call UnitAddAbility(this.prismUnit,get_abil(this.prismUnit))
           call SetUnitAbilityLevel(this.prismUnit,get_abil(this.prismUnit),this.selfBonus)
           if this.target != 0 then
               call this.target.addBonus(bonus)
          endif    
     endmethod
//...

Not the most efficient code in the world since you have to "traverse" the list twice, once to see if the target is in the list and once to add bonuses. Technically you could merge those two but then you have to wait to EVENT_PLAYER_UNIT_SPELL_EFFECT before you can stop the unit.

Anyway, use
JASS:

elseif Unit2Prism(u).isPrismInChain(Unit2Prism(GetSpellTargetUnit())) then

in the Prism_Check code to make sure that the target isn't in the series.
 

emjlr3

Change can be a good thing
Reaction score
395
alright so finally got to try this out Phyrex, and it almost worked like a charm, there is one small problem, and 1 larger problem

1. - bonuses are not removed from towers who channel onto another, say this tower has 1 bonus, and it channels onto another, it correctly passes the bonus of 2 to the next tower, however, it keeps its bonus of 1, not sure how to stop that, if is too big a deal to fix, then forgot about it, the towers cannot attack, and it actually makes it nice to be able to see how the bonuses are going around, so you can decide, if you want to, where to cut your loop

2. - the
JASS:
Unit2Prism(u).isPrismInChain(Unit2Prism(targ))
does not seem to work, I have it implemented correctly, however, towers an still channel onto one already in its loop, the result of which is double the bonuses all the way around(at least it doesn't crash :) )

here is the code that i am using, basically a hybrid of what you posted and what I had-what you posted that made my stuff obsolete + 2 lines to create and destroy a lightning of effect from caster to target
JASS:
//Implementation: *create and active unit ally targetable ability to be detected when cast, based off channel
//                *copy over my damage dummy ability for the towers and this trigger
//                *configure options given and check it up! 
//Documentation: Works well, neat ability I think.  You may want to create an effect on casting tower or something 
//               when cast in the object data.

scope Prism 

globals
    //config. options:
    private constant integer tower1_id = 'h001' //rwcode of level 1 tower
    private constant integer tower2_id = 'h000' //rawcode of level 2 tower
    private constant integer abil_id = 'A004' //rawcode of ability
    private constant integer dum1_id = 'A002' //rawcode of dummy damage level 1 ability
    private constant integer dum2_id = 'A003' //rawcode of dummy damage level 2 ability 
    private constant string lightnin = "DRAB" //string of wanted lightnign between towers
    private constant string wrong1 = "You may only cast this on Level 1 Reflection Towers." //simerror for level 1 towers
    private constant string wrong2 = "You may only cast this on Level 2 Reflection Towers." //simerror for level 2 towers
    private constant string wrong3 = "You may not cast this on towers you are in series with." //simerror for casting on towers already in your series
    
    //needed variables:
    private sound SimError = null
endglobals

//Get correct dummy damage ability    
private function get_abil takes unit tower returns integer
    if GetUnitTypeId(tower)==tower1_id then
        return dum1_id
    else
        return dum2_id
    endif
endfunction
//Needed struct
struct Prism
    private Prism target  = 0
    private unit prismUnit // Isn't used atm, you want to use this in add/removeBonus
    private integer selfBonus = 0 // What bonus this Prism has
    private integer giveBonus = 1 // What bonus it gives, this should probably change with level...
    lightning L = null
     
     //returns the new struct
     public static method new takes unit whichUnit returns Prism
            local Prism p = Prism.create()
            set p.prismUnit = whichUnit
            return p
     endmethod
     // 
     public method castPrismOn takes Prism whatPrism returns nothing
           call whatPrism.addBonus(this.selfBonus + this.giveBonus)
           set this.target  = whatPrism            
     endmethod 
     // 
     public method stopChannelPrism takes nothing returns nothing
            call this.target.removeBonus(this.selfBonus + this.giveBonus)
            set this.target = 0
     endmethod
     //add bonuses to tower
     public method addBonus takes integer bonus returns nothing
           set this.selfBonus = this.selfBonus + bonus
           call UnitAddAbility(this.prismUnit,get_abil(this.prismUnit))
           call SetUnitAbilityLevel(this.prismUnit,get_abil(this.prismUnit),this.selfBonus)
           if this.target != 0 then
               call this.target.addBonus(bonus)
          endif    
     endmethod
     //remove bonuses from towers
     public method removeBonus takes integer bonus returns nothing
           set this.selfBonus = this.selfBonus - bonus
           if this.selfBonus==0 then
              call UnitRemoveAbility(this.prismUnit,get_abil(this.prismUnit))
          else
              call SetUnitAbilityLevel(this.prismUnit,get_abil(this.prismUnit),this.selfBonus)
          endif
           if this.target != 0 then
               call this.target.removeBonus(bonus)
           endif
     endmethod 
     //check....something....
     public method isPrismInChain takes Prism whatPrism returns boolean
        if this == whatPrism then
            return true
        elseif this.target == 0 then
            return false
        endif
        return this.target.isPrismInChain(whatPrism)
     endmethod    
endstruct     

//filter for casting correct ability
function Trig_Prism_Conditions takes nothing returns boolean     
    return GetSpellAbilityId()==abil_id 
endfunction
//end the prism for the tower when it stops channeling
private function Prism_End takes nothing returns boolean
    local trigger trig = GetTriggeringTrigger()
    
    local Prism prism = GetData(trig)
    call DestroyLightning(prism.L)
    call prism.stopChannelPrism()
    call DestroyTrigger(trig)
    
    set trig = null
    return false
endfunction 
//takes the unit and returns a struct for it
private function Unit2Prism takes unit u returns Prism
    local Prism p = Prism( GetData(u) )
    if p == 0 then
         set p = Prism.new(u)
         call SetData(u, p)
    endif
   return p    
endfunction     
//start effects for caster and target tower
function Trig_Prism_Actions takes nothing returns nothing
    local unit cast = GetTriggerUnit()
    local unit targ = GetSpellTargetUnit() 
    local trigger trig = CreateTrigger()
    
    local Prism prism = Unit2Prism(cast)
    call prism.castPrismOn(Unit2Prism(targ))
    call TriggerRegisterUnitEvent(trig,cast,EVENT_UNIT_SPELL_ENDCAST)
    call TriggerAddCondition(trig,Condition(function Prism_End))
    call SetData(trig,prism)       
    
    set prism.L = AddLightningEx(lightnin,true,GetUnitX(targ),GetUnitY(targ),150.,GetUnitX(cast),GetUnitY(cast),150.)
       
    set cast = null
    set targ = null
    set trig = null
endfunction   

//Simrerror message creator for illegal casts 
private function Prism_SimError takes player ForPlayer, string msg returns nothing
    if SimError==null then
        set SimError=CreateSoundFromLabel( "InterfaceError",false,false,false,10,10)
    endif
    if (GetLocalPlayer() == ForPlayer) then
        call ClearTextMessages()
        call DisplayTimedTextToPlayer( ForPlayer, 0.52, -1.00, 2.00, "|cffffcc00"+msg+"|r" )
        call StartSound( SimError )
    endif
endfunction
//Stop tower if it cannot cast it where it is trying to
function Prism_Check takes nothing returns boolean
    local unit u = GetTriggerUnit()
    local unit targ = GetSpellTargetUnit()
    
    if Unit2Prism(u).isPrismInChain(Unit2Prism(targ)) or u==targ then
        call PauseUnit(u,true)
        call IssueImmediateOrder(u,"stop")
        call PauseUnit(u,false)
        call Prism_SimError(GetOwningPlayer(u),wrong3)
        set u = null
        set targ = null
        return false
    endif
    if GetUnitTypeId(u)==tower1_id and GetUnitTypeId(targ)!=tower1_id then
        call PauseUnit(u,true)
        call IssueImmediateOrder(u,"stop")
        call PauseUnit(u,false)
        call Prism_SimError(GetOwningPlayer(u),wrong1)
        set u = null
        set targ = null
        return false
    elseif GetUnitTypeId(u)==tower2_id and GetUnitTypeId(targ)!=tower2_id then
        call PauseUnit(u,true)
        call IssueImmediateOrder(u,"stop")
        call PauseUnit(u,false)
        call Prism_SimError(GetOwningPlayer(u),wrong2)
        set u = null
        set targ = null
        return false
    endif

    set u = null
    set targ = null
    return false
endfunction 

endscope

//==== Init Trigger Prism ====
function InitTrig_Prism takes nothing returns nothing
    local trigger trig = CreateTrigger()
    
    call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition(trig, Condition(function Prism_Check))
    
    set gg_trg_Prism = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(gg_trg_Prism,EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition(gg_trg_Prism, Condition(function Trig_Prism_Conditions))
    call TriggerAddAction(gg_trg_Prism, function Trig_Prism_Actions)
    
    set trig = null
endfunction


lastly, and probably least important (since many of your vJASS code methods are foreign to me), it is hard for me to understand many of the things you do, therefore, where you mentioned destroying the struct, I do not really know how, and it would be beneficial if you could add those few lines in there

thnx again a bunch

btw, here is the map with it implemented, in case you would like to test yourself
 

phyrex1an

Staff Member and irregular helper
Reaction score
447
1 and 3: Better destroy solution that the one I tried to explain before (assumes that there are no negative "bonuses"):
JASS:

     private method stopBeingAPrism takes nothing returns nothing
         // If it stops channel and don't have any bonus then it's safe to destroy,
         // NOTE: you need to flush the SetData thing (RemoveData?) for this.prismUnit here.
         // call RemoveData(this.prismUnit)
         call this.destroy()
     endmethod
 
     public method removeBonus takes integer bonus returns nothing
          set this.selfBonus = this.selfBonus - bonus
          if this.selfBonus==0 then
              call UnitRemoveAbility(this.prismUnit,get_abil(this.prismUnit))
              if this.target == 0 then
                  call this.stopBeingAPrism()
              endif
          else
              call SetUnitAbilityLevel(this.prismUnit,get_abil(this.prismUnit),this.selfBonus)
          endif
           if this.target != 0 then
               call this.target.removeBonus(bonus)
           endif
     endmethod 

     public method castPrismOn takes Prism whatPrism returns nothing
           call UnitRemoveAbility(this.prismUnit, get_abil(this.prismUnit))
           call whatPrism.addBonus(this.selfBonus + this.giveBonus)
           set this.target  = whatPrism            
     endmethod

     public method stopChannelPrism takes nothing returns nothing
            call this.target.removeBonus(this.selfBonus + this.giveBonus)
            set this.target = 0
            if this.bonus != 0 then
                call UnitAddAbility(this.prismUnit, get_abil(this.prismUnit))
                call SetUnitAbilityLevel(this.prismUnit, get_abil(this.prismUnit), this.selfBonus)
            else
                 call this.stopBeingAPrism()
            endif
     endmethod



2. Weird logic by me,
JASS:
Unit2Prism(targ).isPrismInChain(Unit2Prism(u))

should work.

I don't have a (working) installation of the world editor here so I can't test it. :(
 

emjlr3

Change can be a good thing
Reaction score
395
I assume, in the method stopChannelPrism

this.bonus was a typo? and is supposed to be this.selfBonus..

anyhow, with the updated code, looks like we got more problems :(

1. say we have a tower with 1 bonus, which then channels onto another tower, so that it gets its bonus removed, and the next tower has bonus 2, if were to take a third tower, and channel onto the second tower, a bonus of 2 would be shown(even though it can't attack, still weird), however the final tower is updated accordingly and gets 3 bonus, as it should (not always the case, happens randomly is seems)

2. every so often, there is a double free of type Prism, not too sure why, seems to be when parts of a series are cut off at different times

3. if I destroy the towers, rather then stop them from channeling, my lightning does not always go away, which makes me think the rest of the struct, etc. is not either/alternatively, if I destroy a tower being channeled on, the same thing tends to happen, the lightning to that tower remains

4. if a tower tries to channel onto another tower in its series, it displays my message saying no you can't do that, however it tosses a lightning random towards the lower right of the map, suggesting to me that out trigger is still running somehow

let you know if i encounter anything else
 

phyrex1an

Staff Member and irregular helper
Reaction score
447
1.Hurray for untested code :D

JASS:
public method addBonus takes integer bonus returns nothing
           set this.selfBonus = this.selfBonus + bonus
           if this.target == 0 then 
               call UnitAddAbility(this.prismUnit, get_abil(this.prismUnit))
               call SetUnitAbilityLevel(this.prismUnit, get_abil(this.prismUnit), this.selfBonus)
           else
               call this.target.addBonus(bonus)
          endif    
     endmethod

The random parts seems to be related to no 2 so skip that for now.

2. Weird, I blame the other bugs for this one. I'm not really sure but it's easier to do that :)

3. I guess EVENT_UNIT_SPELL_ENDCAST can't be trusted, my preferred way for channelling spells is to have some kind of loop (timer, trigger, loop) that checks the current order of the unit but my channel spells usually have a periodic effect.

4. This one is strange... I guess your stop code doesn't always work :nuts:

I'll have a closer look tomorrow, getting kinda late here so my response maybe doesn't help very much.
 

emjlr3

Change can be a good thing
Reaction score
395
alrighty then, I solved the issue with casting even though i told it not too, I changed the event to _SPELL_CHANNEL, and it works great now

your new code works perfect, so all in all, the spell works perfect aside from 1 thing

when a tower is sold, or a tower is upgraded, i had to add some code in to get all towers channeling onto that tower, and had to force them to stop, else they would be channeling onto either air, or a tower or the wrong type

this seems to bug the shit out like no other

I seem to get double frees of Prism, towers randomly(ones that were there, and new ones) are a part of each others "series", when they clearly are not, and the bonuses stop being passed around correctly

does this stem from forcing the unit to "stop", as opposed from you stopping it in game, I think so, but why is the question

this fires when units finish and upgrade, or when they die, and should stop towers who were channeling onto them, which it does, but from this stems all remaining bugs

JASS:

globals
    private unit U = null
endglobals
//needed filter
private function Prism_Filter takes nothing returns boolean
    return GetWidgetLife(GetFilterUnit())>.405 and GetOwningPlayer(GetFilterUnit())==GetOwningPlayer(U) and GetFilterUnit()!=U
endfunction
//in case a tower is sold or is upgraded
function Prism_Death takes nothing returns boolean
    local unit dead = GetTriggerUnit()
    local unit u
    local group g
    local Prism p
    
if GetUnitTypeId(dead)==tower1_id or GetUnitTypeId(dead)==tower2_id then
    set g = CreateGroup()
    set U = dead
    call GroupEnumUnitsInRect(g,bj_mapInitialPlayableArea,Condition(function Prism_Filter))
    //call BJDebugMsg(I2S(CountUnitsInGroup(g)))
    loop
        set u = FirstOfGroup(g)
        exitwhen u==null
        call GroupRemoveUnit(g,u)
                    
        set p = Prism( GetData(u) )
        if p.targ==dead then
            //call BJDebugMsg("Found one")
            call PauseUnit(u,true)
            call IssueImmediateOrder(u,"stop")
            call PauseUnit(u,false)
        endif
    endloop
    
    call DestroyGroup(g)
    set g = null
endif
    
    set dead = null
    return false
endfunction


here is the whole code, incase anyone wants to see
JASS:

//Implementation: *create and active unit ally targetable ability to be detected when cast, based off channel
//                *copy over my damage dummy ability for the towers and this trigger
//                *configure options given and check it up! 
//Documentation: Works well, neat ability I think.  You may want to create an effect on casting tower or something 
//               when cast in the object data.

scope Prism 

globals
    //config. options:
    private constant integer tower1_id = 'h001' //rawcode of level 1 tower
    private constant integer tower2_id = 'h000' //rawcode of level 2 tower
    private constant integer abil_id = 'A004' //rawcode of ability
    private constant integer dum1_id = 'A002' //rawcode of dummy damage level 1 ability
    private constant integer dum2_id = 'A003' //rawcode of dummy damage level 2 ability 
    private constant string lightnin = "DRAB" //string of wanted lightnign between towers
    private constant string wrong1 = "You may only cast this on Level 1 Reflection Towers." //simerror for level 1 towers
    private constant string wrong2 = "You may only cast this on Level 2 Reflection Towers." //simerror for level 2 towers
    private constant string wrong3 = "You may not cast this on towers you are in series with." //simerror for casting on towers already in your series
    
    //needed variables:
    private sound SimError = null
    private unit U = null
endglobals

//Get correct dummy damage ability    
private function get_abil takes unit tower returns integer
    if GetUnitTypeId(tower)==tower1_id then
        return dum1_id
    else
        return dum2_id
    endif
endfunction
//needed filter
private function Prism_Filter takes nothing returns boolean
    return GetWidgetLife(GetFilterUnit())>.405 and GetOwningPlayer(GetFilterUnit())==GetOwningPlayer(U) and GetFilterUnit()!=U
endfunction

//Needed struct
struct Prism
    private Prism target  = 0
    private unit prismUnit // Isn't used atm, you want to use this in add/removeBonus
    private integer selfBonus = 0 // What bonus this Prism has
    private integer giveBonus = 1 // What bonus it gives, this should probably change with level...
    lightning L = null
    unit targ
     
     //destroy the struct
     private method stopBeingAPrism takes nothing returns nothing
         if this!=0 then
             call this.destroy()
         endif
     endmethod
     //returns the new struct
     public static method new takes unit whichUnit returns Prism
            local Prism p = Prism.create()

            set p.prismUnit = whichUnit
            return p
     endmethod
     //...... 
     public method castPrismOn takes Prism whatPrism returns nothing
           call UnitRemoveAbility(this.prismUnit, get_abil(this.prismUnit))
           call whatPrism.addBonus(this.selfBonus + this.giveBonus)
           set this.target  = whatPrism            
     endmethod 
     //unit stops, do some stuff 
     public method stopChannelPrism takes nothing returns nothing
            call this.target.removeBonus(this.selfBonus + this.giveBonus)
            set this.target = 0
            if this.selfBonus != 0 then
                call UnitAddAbility(this.prismUnit, get_abil(this.prismUnit))
                call SetUnitAbilityLevel(this.prismUnit, get_abil(this.prismUnit), this.selfBonus)
            else
                call this.stopBeingAPrism()
            endif
     endmethod
     //add bonuses to tower
     public method addBonus takes integer bonus returns nothing
           set this.selfBonus = this.selfBonus + bonus
           if this.target == 0 then 
               call UnitAddAbility(this.prismUnit, get_abil(this.prismUnit))
               call SetUnitAbilityLevel(this.prismUnit, get_abil(this.prismUnit), this.selfBonus)
           else
               call this.target.addBonus(bonus)
          endif    
     endmethod
     //remove bonuses from towers
     public method removeBonus takes integer bonus returns nothing
          set this.selfBonus = this.selfBonus - bonus
          if this.selfBonus==0 then
              call UnitRemoveAbility(this.prismUnit,get_abil(this.prismUnit))
              if this.target == 0 then
                  call this.stopBeingAPrism()
              endif
          else
              call SetUnitAbilityLevel(this.prismUnit,get_abil(this.prismUnit),this.selfBonus)
          endif
           if this.target != 0 then
               call this.target.removeBonus(bonus)
           endif
     endmethod
     //checks if two unts are in the same series
     public method isPrismInChain takes Prism whatPrism returns boolean
        if this == whatPrism then
            return true
        elseif this.target == 0 then
            return false
        endif
        return this.target.isPrismInChain(whatPrism)
     endmethod    
endstruct     

//filter for casting correct ability
function Trig_Prism_Conditions takes nothing returns boolean     
    return GetSpellAbilityId()==abil_id 
endfunction
//end the prism for the tower when it stops channeling
private function Prism_End takes nothing returns boolean
    local trigger trig = GetTriggeringTrigger()    
    local Prism prism = GetData(trig)

    call DestroyLightning(prism.L)
    call prism.stopChannelPrism()
    call DestroyTrigger(trig)
    
    set trig = null
    return false
endfunction 
//takes the unit and returns a struct for it
private function Unit2Prism takes unit u returns Prism
    local Prism p = Prism( GetData(u) )

    if p == 0 then
         set p = Prism.new(u)
         call SetData(u, p)
    endif
   return p    
endfunction     
//start effects for caster and target tower
function Trig_Prism_Actions takes nothing returns nothing
    local unit cast = GetTriggerUnit()
    local unit targ = GetSpellTargetUnit() 
    local trigger trig = CreateTrigger()
    
    local Prism prism = Unit2Prism(cast)
    call prism.castPrismOn(Unit2Prism(targ))
    call TriggerRegisterUnitEvent(trig,cast,EVENT_UNIT_SPELL_ENDCAST)
    call TriggerRegisterUnitEvent(trig,cast,EVENT_UNIT_DEATH)
    call TriggerRegisterUnitEvent(trig,cast,EVENT_UNIT_UPGRADE_FINISH)
    call TriggerAddCondition(trig,Condition(function Prism_End))
    call SetData(trig,prism)       
    
    set prism.L = AddLightningEx(lightnin,true,GetUnitX(targ),GetUnitY(targ),150.,GetUnitX(cast),GetUnitY(cast),150.)
    set prism.targ = targ
       
    set cast = null
    set targ = null
    set trig = null
endfunction   

//Simrerror message creator for illegal casts 
private function Prism_SimError takes player ForPlayer, string msg returns nothing
    if SimError==null then
        set SimError=CreateSoundFromLabel( "InterfaceError",false,false,false,10,10)
    endif
    if (GetLocalPlayer() == ForPlayer) then
        call ClearTextMessages()
        call DisplayTimedTextToPlayer( ForPlayer, 0.52, -1.00, 2.00, "|cffffcc00"+msg+"|r" )
        call StartSound( SimError )
    endif
endfunction
//Stop tower if it cannot cast it where it is trying to
function Prism_Check takes nothing returns boolean
    local unit u = GetTriggerUnit()
    local unit targ = GetSpellTargetUnit()
    
    if GetSpellAbilityId()!=abil_id then
        set u = null
        set targ = null
        return false
    endif
    if Unit2Prism(targ).isPrismInChain(Unit2Prism(u)) or u==targ or OrderId2String(GetUnitCurrentOrder(u))=="channel" then
        call PauseUnit(u,true)
        call IssueImmediateOrder(u,"stop")
        call PauseUnit(u,false)
        call Prism_SimError(GetOwningPlayer(u),wrong3)
        set u = null
        set targ = null
        return false
    endif
    if GetUnitTypeId(u)==tower1_id and GetUnitTypeId(targ)!=tower1_id then
        call PauseUnit(u,true)
        call IssueImmediateOrder(u,"stop")
        call PauseUnit(u,false)
        call Prism_SimError(GetOwningPlayer(u),wrong1)
        set u = null
        set targ = null
        return false
    elseif GetUnitTypeId(u)==tower2_id and GetUnitTypeId(targ)!=tower2_id then
        call PauseUnit(u,true)
        call IssueImmediateOrder(u,"stop")
        call PauseUnit(u,false)
        call Prism_SimError(GetOwningPlayer(u),wrong2)
        set u = null
        set targ = null
        return false
    endif

    set u = null
    set targ = null
    return false
endfunction 

//in case a tower is sold or is upgraded
function Prism_Death takes nothing returns boolean
    local unit dead = GetTriggerUnit()
    local unit u
    local group g
    local Prism p
    
if GetUnitTypeId(dead)==tower1_id or GetUnitTypeId(dead)==tower2_id then
    set g = CreateGroup()
    set U = dead
    call GroupEnumUnitsInRect(g,bj_mapInitialPlayableArea,Condition(function Prism_Filter))
    //call BJDebugMsg(I2S(CountUnitsInGroup(g)))
    loop
        set u = FirstOfGroup(g)
        exitwhen u==null
        call GroupRemoveUnit(g,u)
                    
        set p = Prism( GetData(u) )
        if p.targ==dead then
            //call BJDebugMsg("Found one")
            call PauseUnit(u,true)
            call IssueImmediateOrder(u,"stop")
            call PauseUnit(u,false)
        endif
    endloop
    
    call DestroyGroup(g)
    set g = null
endif
    
    set dead = null
    return false
endfunction

endscope

//==== Init Trigger Prism ====
function InitTrig_Prism takes nothing returns nothing
    local trigger trig = CreateTrigger()
    
    call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_CHANNEL )
    call TriggerAddCondition(trig, Condition(function Prism_Check))
    
    set trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_UPGRADE_FINISH)
    call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddCondition(trig,Condition(function Prism_Death))
    
    set gg_trg_Prism = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(gg_trg_Prism,EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition(gg_trg_Prism, Condition(function Trig_Prism_Conditions))
    call TriggerAddAction(gg_trg_Prism, function Trig_Prism_Actions)
    
    set trig = null
endfunction
 

phyrex1an

Staff Member and irregular helper
Reaction score
447
JASS:

     //destroy the struct
     private method stopBeingAPrism takes nothing returns nothing
         if this!=0 then
             call this.destroy()
         endif
     endmethod

In a fully working code this!=0 check isn't needed. If this ever can be 0 then it's something that is very wrong somewhere.

However, here you need to clean the SetData() call made in Unit2Prism. I guess
JASS:
call SetData(this.prismUnit, 0)

will do it.

The mixed struct/functions code makes me kinda confused atm :p
 

emjlr3

Change can be a good thing
Reaction score
395
do not think I need to do that, setdata and getdata are simply my CSData variants, never thought they needed to be cleaned.....

in anycase, no ideas why it would bug when forcing units to stop?? that seems to be the only glitch

also reguardless of that check of !=0, we are still getting double frees somehow...
 

phyrex1an

Staff Member and irregular helper
Reaction score
447
do not think I need to do that, setdata and getdata are simply my CSData variants, never thought they needed to be cleaned....
It's not about memory leaks here, it's worse :)

Consider this code:
JASS:
local unit u = <whateverunit>
local MyStruct m1 = MyStruct.create()
call SetData(u, m1)
call m1.destroy()
set m1 = MyStruct.create()
if MyStruct(GetDate(u)) == m1 then
    // Yep, this will happen.
endif

Structs aren't like handles. Destroying doesn't really destroy them, it just marks them for recycle so if you have any reference to them anywhere you can still access the "old" struct. Since we store such a reference on our prism unit (with Unit2Prism) we must remove that reference when the struct is destroyed. Else Unit2Prism will return that (destroyed) struct when it's called next, and since the struct is marked for recycle it's fully possible that another unit also get that struct. Two units for one struct, bad.
 

emjlr3

Change can be a good thing
Reaction score
395
then what to do, you think could be the problem, not the order a unit to stop, but because there are double frees...or something...

this has never happened before, and I have never cleared data I have stored this way(though i have never stored stuff to a unit before, only local handles.....though I do use a timer stack....hmmmm...)

what do you suggest?
 

emjlr3

Change can be a good thing
Reaction score
395
alrighty then, I will give that a try when I can, and see if it aleviates our current (and only), issue
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top