HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

[Spell: Chain Frost] Multi-Instance Problem

03-15-2008, 05:34 AM#1
omegaboi
Hi, I am making the spell 'Chain Frost', which is exactly the same as the one in DotA. For those who have not seen the spell before, it is similar to Chain Lightning, but it actually slows the target for each bounce (the missle speed of Chain Frost is slow too). In total, the Chain Frost can bounce and hit up to 6 units. Anyway, here is my code:

Collapse JASS:
globals
    unit ChainFrostPreviousUnit
    location ChainFrostTempPoint
endglobals

function Trig_Chain_Frost_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A01V' ) ) then
        return false
    endif
    return true
endfunction

function ChainFrost_PickNewTargetCondition takes nothing returns boolean
    if ( IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == true ) then
        return false
    endif
    if ( IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == true ) then
        return false
    endif
    if ( udg_DummyUnit_Condition == true ) then
        return false
    endif
    if ( GetFilterUnit() == ChainFrostPreviousUnit ) then
        return false
    endif
    if ( IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) != true ) then
        return false
    endif
    if ( IsUnitDeadBJ(GetFilterUnit()) == true ) then
        return false
    endif
    if ( IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == true ) then
        return false
    endif
    return true
endfunction

function ChainFrost_ChainNextTarget takes nothing returns nothing
    call CreateNUnitsAtLocFacingLocBJ( 1, 'h000', GetOwningPlayer(GetTriggerUnit()), ChainFrostTempPoint, ChainFrostTempPoint )
    call UnitApplyTimedLifeBJ( 1.00, 'BTLF', GetLastCreatedUnit() )
    call UnitAddAbilityBJ( 'A01U', GetLastCreatedUnit() )
    call SetUnitAbilityLevelSwapped( 'A01U', GetLastCreatedUnit(), GetUnitAbilityLevelSwapped('A01V', GetTriggerUnit()) )
    call IssueTargetOrderBJ( GetLastCreatedUnit(), "thunderbolt", GetEnumUnit() )
    set ChainFrostPreviousUnit = GetEnumUnit()
endfunction

function Trig_Chain_Frost_Actions takes nothing returns nothing
    
    // Local Variables
    local group ChainFrostUnitGroup
    local integer ChainFrostCounter
    local unit ChainFrostTimingUnit
    
    set ChainFrostPreviousUnit = GetSpellTargetUnit()
    set ChainFrostCounter = 1

    loop
    exitwhen ChainFrostCounter > 6
        
        // Create a caster dummy for timing purpose
        set ChainFrostTempPoint = GetUnitLoc(ChainFrostPreviousUnit)
        call CreateNUnitsAtLocFacingLocBJ( 1, 'h000', GetOwningPlayer(GetTriggerUnit()), ChainFrostTempPoint, ChainFrostTempPoint )
        call UnitApplyTimedLifeBJ( ( ( DistanceBetweenPoints(GetUnitLoc(GetTriggerUnit()), ChainFrostTempPoint) / 475.00 ) + 1.00 ), 'BTLF', GetLastCreatedUnit() )
        set ChainFrostTimingUnit = GetLastCreatedUnit()
        call RemoveLocation ( ChainFrostTempPoint )
        
        // Wait for target unit to get hit
        loop
            exitwhen ( IsUnitDeadBJ(ChainFrostTimingUnit) == true or IsUnitDeadBJ(ChainFrostPreviousUnit) == true or UnitHasBuffBJ(ChainFrostPreviousUnit, 'B00M') == true )
            call TriggerSleepAction(RMaxBJ(bj_WAIT_FOR_COND_MIN_INTERVAL, 0.10))
        endloop
        
        // Slow target unit
        call CreateNUnitsAtLocFacingLocBJ( 1, 'h000', GetOwningPlayer(GetTriggerUnit()), ChainFrostTempPoint, ChainFrostTempPoint )
        call UnitApplyTimedLifeBJ( 1.00, 'BTLF', GetLastCreatedUnit() )
        call UnitAddAbilityBJ( 'A02W', GetLastCreatedUnit() )
        call IssueTargetOrderBJ( GetLastCreatedUnit(), "frostnova", ChainFrostPreviousUnit )
        
        // Special Effect
        set ChainFrostTempPoint = GetUnitLoc(ChainFrostPreviousUnit)
        call AddSpecialEffectLocBJ( ChainFrostTempPoint, "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl" )
        call DestroyEffectBJ( GetLastCreatedEffectBJ() )
        
        // Pick new target to bounce
        set ChainFrostUnitGroup = GetRandomSubGroup(1, GetUnitsInRangeOfLocMatching(475.00, ChainFrostTempPoint, Condition(function ChainFrost_PickNewTargetCondition)))
        if ( CountUnitsInGroup(ChainFrostUnitGroup) != 0 and ChainFrostCounter < 6 ) then
            call ForGroupBJ( ChainFrostUnitGroup, function ChainFrost_ChainNextTarget )
        else
            set ChainFrostCounter = 10
        endif
        
        call RemoveLocation ( ChainFrostTempPoint )
        call DestroyGroup ( ChainFrostUnitGroup )
        
        set ChainFrostCounter = ( ChainFrostCounter + 1 )
    endloop
    
    set ChainFrostTempPoint = null
    set ChainFrostUnitGroup = null
    set ChainFrostPreviousUnit = null
    
endfunction

//===========================================================================
function InitTrig_Chain_Frost takes nothing returns nothing
    set gg_trg_Chain_Frost = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Chain_Frost, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Chain_Frost, Condition( function Trig_Chain_Frost_Conditions ) )
    call TriggerAddAction( gg_trg_Chain_Frost, function Trig_Chain_Frost_Actions )
endfunction

I have tried it in GUI using the array with the index value '[GetConvertedPlayerId(GetOwningPlayer(GetTriggerUnit()))]' and it worked well; it is multi-instancable. However, when the skill is recast the second time by the SAME player, the first cast will end when it hit the next target and the second cast will continue to bounce and hit a total of 6 units. Even though I have converted it to JASS and used local variables, whenever the Chain Frost is cast the second time, the first cast will end when it hit the next unit too and it is not multi-instancable anymore.

Q1: Therefore, is there a way to fix this problem as mentioned above?

Q2: Is there a way to make the global variables 'ChainFrostPreviousUnit' and 'ChainFrostTempPoint' to become local variables in function 'Trig_Chain_Frost_Actions' and still be used by functions like 'ChainFrost_ChainNextTarget' ? I can't find a way to pass in an argument to functions which are called by ForGroupBJ.

Here is my GUI code just in case it is needed for reference.

Trigger:
Chain Frost GUI
Collapse Events
Unit - A unit Starts the effect of an ability
Collapse Conditions
(Ability being cast) Equal to Chain Frost (Lich King)
Collapse Actions
Set ChainFrostPreviousUnit[(Player number of (Owner of (Triggering unit)))] = (Target unit of ability being cast)
Set ChainFrostCounter[(Player number of (Owner of (Triggering unit)))] = 1
Custom script: loop
Custom script: exitwhen udg_ChainFrostCounter[GetConvertedPlayerId(GetOwningPlayer(GetTriggerUnit()))] > 6
-------- Create a caster dummy for timing purpose --------
Set ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))] = (Position of ChainFrostPreviousUnit[(Player number of (Owner of (Triggering unit)))])
Unit - Create 1 Caster Dummy for (Owner of (Triggering unit)) at ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))] facing ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))]
Unit - Add a (((Distance between (Position of (Triggering unit)) and ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))]) / 475.00) + 1.10) second Generic expiration timer to (Last created unit)
Custom script: call RemoveLocation (udg_ChainFrostTempPoint[GetConvertedPlayerId(GetOwningPlayer(GetTriggerUnit()))])
Wait until (((ChainFrostPreviousUnit[(Player number of (Owner of (Triggering unit)))] has buff Chain Frost ) Equal to True) or (((ChainFrostPreviousUnit[(Player number of (Owner of (Triggering unit)))] is dead) Equal to True) or (((Last created unit) is dead) Equal to T, checking every 0.10 seconds
-------- Slow target unit --------
Unit - Create 1 Caster Dummy for (Owner of (Triggering unit)) at ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))] facing ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))]
Unit - Add a 1.00 second Generic expiration timer to (Last created unit)
Unit - Add Chain Frost Slow (Caster Dummy) to (Last created unit)
Unit - Order (Last created unit) to Undead Lich - Frost Nova ChainFrostPreviousUnit[(Player number of (Owner of (Triggering unit)))]
-------- Special Effect --------
Set ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))] = (Position of ChainFrostPreviousUnit[(Player number of (Owner of (Triggering unit)))])
Special Effect - Create a special effect at ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))] using Abilities\Spells\Undead\FrostNova\FrostNovaTarget.mdl
Special Effect - Destroy (Last created special effect)
-------- Pick new target to bounce --------
Set ChainFrostUnitGroup[(Player number of (Owner of (Triggering unit)))] = (Random 1 units from (Units within 475.00 of ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))] matching ((((Matching unit) is A structure) Not equal to True) and ((((Matching unit) is dead) Not equal to True) and ((((Matching unit) belongs
Collapse If (All Conditions are True) then do (Then Actions) else do (Else Actions)
Collapse If - Conditions
(Number of units in ChainFrostUnitGroup[(Player number of (Owner of (Triggering unit)))]) Not equal to 0
ChainFrostCounter[(Player number of (Owner of (Triggering unit)))] Less than 6
Collapse Then - Actions
Collapse Unit Group - Pick every unit in ChainFrostUnitGroup[(Player number of (Owner of (Triggering unit)))] and do (Actions)
Collapse Loop - Actions
Unit - Create 1 Caster Dummy for (Owner of (Triggering unit)) at ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))] facing ChainFrostTempPoint[(Player number of (Owner of (Triggering unit)))]
Unit - Add a 1.00 second Generic expiration timer to (Last created unit)
Unit - Add Chain Frost Stun and Damage (Caster Dummy) to (Last created unit)
Unit - Set level of Chain Frost Stun and Damage (Caster Dummy) for (Last created unit) to (Level of Chain Frost (Lich King) for (Triggering unit))
Unit - Order (Last created unit) to Human Mountain King - Storm Bolt (Picked unit)
Set ChainFrostPreviousUnit[(Player number of (Owner of (Triggering unit)))] = (Picked unit)
Collapse Else - Actions
Set ChainFrostCounter[(Player number of (Owner of (Triggering unit)))] = 10
Custom script: call RemoveLocation (udg_ChainFrostTempPoint[GetConvertedPlayerId(GetOwningPlayer(GetTriggerUnit()))])
Custom script: call DestroyGroup (udg_ChainFrostUnitGroup[GetConvertedPlayerId(GetOwningPlayer(GetTriggerUnit()))])
Set ChainFrostCounter[(Player number of (Owner of (Triggering unit)))] = (ChainFrostCounter[(Player number of (Owner of (Triggering unit)))] + 1)
Custom script: endloop
Set ChainFrostPreviousUnit[(Player number of (Owner of (Triggering unit)))] = No unit

Thanks a lot, in advance!
03-15-2008, 10:12 AM#2
RolePlaynGamer
Quote:
Originally Posted by omegaboi
Is there a way to make the globals into locals?

Make them locals instead and store them in a gamecache
03-15-2008, 02:29 PM#3
omegaboi
Quote:
Originally Posted by RolePlaynGamer
Make them locals instead and store them in a gamecache

By making 'ChainFrostPreviousUnit' or 'ChainFrostTempPoint' local in function 'Trig_Chain_Frost_Actions', it cannot be used in other functions like 'ChainFrost_ChainNextTarget' and 'ChainFrost_PickNewTargetCondition'.

Anyway, can game cache be used through the battle.net/local area network? From this tutorial (http://www.wc3campaigns.net/showthread.php?t=81478), it says 'Remember that Game Caches can not be used on Battle Net, and are almost only ever used for campaigns.'

By the way, I have tried the game caches and I can't seem to 'refer' to a unit without restoring it at a location in the map, unlike other types like real, where I can use the value like this (for example):

Trigger:
Example
Events
Collapse Conditions
(Life of (Triggering unit)) Greater than or equal to (Load life of Example from ExampleGameCache)
Actions

If possible, can you show me how to store local variables (especially units) in game caches and use them -or- are there any workarounds for the problems mentioned above?
03-15-2008, 04:53 PM#4
RolePlaynGamer
Game Caches CAN be used in b.net. Most people dont think so, because gamecaches was meant to store files from one game to another. But that's not true. Storing files between games is the primary objective of gamecaches. But they are also used for MUI spellmaking. I will look at the spell and post a code later.

Could you tell more about which dummy and buff etc. you use?
03-15-2008, 05:24 PM#5
BestZero
Gamecahce can be used on bnet, At least on my maps.

You can take a look at my(IceFrog's) OLD Chain Frost script (based on DotA, I just changed special effect to dummy projectile). There is no need for global nor handing variables.

Expand JASS:
03-15-2008, 05:42 PM#6
RolePlaynGamer
Also you could take a look at this tutorial, which will learn you the basics of game caches and MUI.

http://wc3campaigns.net/showthread.php?t=83337
03-16-2008, 05:25 AM#7
omegaboi
Quote:
Originally Posted by RolePlaynGamer
Could you tell more about which dummy and buff etc. you use?

I have attached the map which I used to test out the spell as I find it hard to describe. Thank you for your time on helping me with the code.

Quote:
Originally Posted by BestZero
You can take a look at my(IceFrog's) OLD Chain Frost script (based on DotA, I just changed special effect to dummy projectile). There is no need for global nor handing variables.

Thanks, I will take this as a reference later.

Quote:
Originally Posted by RolePlaynGamer
Also you could take a look at this tutorial, which will learn you the basics of game caches and MUI.

http://wc3campaigns.net/showthread.php?t=83337

Thanks for this link too, I think I have missed out this tutorial. Even though I have tried using the game caches, I have yet to succeed in using them properly. However, I have learnt some effective ways of using JASS by learning to shorten some codes (I am new to JASS but learning it now).
Attached Files
File type: w3xChain Frost.w3x (17.8 KB)